温度に比例した電圧を出力する温度センサー(MCP9700)を使って、測定した温度をLCDに表示、さらに、PushSWを押すと温度を音声で知らせるプログラムです。省電力にするために温度の更新は4秒に一度としほとんどの時間は、SLEEPモードになっています。
EEPROMには、言葉を細分化しその要素ごとに無音を0.1秒間挟んで記録してあります。各要素の開始アドレスと終了アドレスは、配列に記録してあるため、測定した温度の値は、この配列を参照しながらPWMで再生し温度を音声で知らせています。
main.c プログラムを示します。
必要となるほかのプログラムは、まとめて圧縮してあります。
音声温度計ソースファイル b07TempVoice.zip |
main.c
/*************************************
* File name: EEPROM 温度を音声再生
* EEPROM に記録された 8kHz 8bit PCMデータを再生
* System ClockはConfigで内部32MHzに設定
* LEDも連動した点灯
* PWM(LED) RC5
* PushSW RC4
* 温度入力 RC2
*
* Language: MPLABX XC8 v2.10
* PIC16F18346 on 20pin demoboard
* Created on 2020-6-16
**************************************/
#include <xc.h>
#include <stdio.h>
#include "I2C_EEPROM.h"
#include "words123.h"
#include "i2cLCD.h"
#include "config_sys.h"
//#define _XTAL_FREQ 32000000 // delay_ms(x) のための定義
void fadeIn(void);
void fadeOut(void);
void speekSin(int);
void speekWords(char*);
void speekTemp(int);
void speekTen(char,char);
void speekOne(char,char);
void speekDot(char);
void speekBlw(char);
// ******************* main *************************************
void main() {
int temp = 0;
i2c_MasterInit(400000); // i2c 関連初期化 400KHz クロック
LCD_int(); // LCDを初期化
sys_int(); // PWM SW ADC Timerを初期化
printf("Temp");
TMR1IE = 1; // Timer1割込み許可
PEIE = 1; // ペリフェラル割込み許可
INTE = 1; // Pin割込み許可
// -------------------------------------------
while(1){
// ----- SLEEP状態にする ------------------
SLEEP();
NOP();
// ----- ウェイクアップ --------------------
TMR1IF=0; // Timer1割込みフラッグクリア
temp = 0;
for(char i=0;i<32;i++){ // 32回測定し平均する
__delay_ms(5); // ADチャージ時間
GO = 1; // AD変換開始
while(GO); // AD変換終了待ち
temp += (ADRES - 500);
}
temp /= 32;
LCD_posyx(1,0); // 下段にカーソル移動
printf("%3d.%d C",temp/10,temp%10); // 結果表示
// SWが押されたなら ------------------------
if(INTF){
speekTemp(temp); // 温度を発声
while(!PushSW); // SWが離れるのを待つ
__delay_ms(10); // チャタリング防止
INTF = 0; // Pin割込みフラグクリア
}
}
// -------------------------------------------
} //main
// PWM出力を0vから無音状態(0x80)にゆっくり上げる
void fadeIn(void){
TRISC5 = 1; // PWM5 RC5 Output OFF
PWM5EN = 1; // PWM ON
PWM5DCH = 0; // 出力0vにを設定
TMR2IF = 0; // フラッグをクリア
while(! TMR2IF); // Timerカウントアップ待つ
TMR2IF = 0; // フラッグをクリア
TRISC5 = 0; // PWM5 RC5 Output ON
for(char i=0; i<0x80; i++){ // 無音状態(0x80)にゆっくり設定
while(! TMR2IF); // Timerカウントアップ待つ
TMR2IF = 0; // フラッグをクリア
PWM5DCH = i; // デュティサイクルを設定
}
}
// PWM出力を無音状態(0x80)から0vにゆっくり下げる
void fadeOut(void){
for(char i=0x80; i>0; i--){ // ovにゆっくり設定
while(! TMR2IF); // Timerカウントアップ待つ
TMR2IF = 0; // フラッグをクリア
PWM5DCH = i; // デュティサイクルを設定
}
PWM5EN = 0; // PWM OFF
}
// 音声で読み上げ ------------------------------------
void speekWords(char*step){
char wd,sp,data,mBlock;
unsigned long addr = 0; // EEPROM memory address
while(*step < 50){
wd=*step++; sp=*step++;
addr = StartAdd[wd]; // PCM 開始アドレス
mBlock = EEPROM_AddressSet(addr); // 内部アドレス設定
while(addr++ < EndAdd[wd]){ // データ終了まで
while(! TMR2IF); // Timerカウントアップ待つ
TMR2IF = 0; // フラッグをクリア
data = EEPROM_ReadCurrent(mBlock); // デュティサイクルを設定
PWM5DCH = data; // デュティサイクルを設定
if (addr == 0x10000) // ブロック境界なら
mBlock = EEPROM_AddressSet(addr); // 内部アドレス書換え
} //while
delay_ms(sp);
}
}
void speekTemp(int temp){
fadeIn();
speekSin(temp); // 符号を読み上げ
temp = abs(temp); // 絶対値をとる
char tempTen = temp / 100; // 10の位を抽出
char tempOne = (temp / 10) % 10; // 1の位を抽出
char tempBlw = temp % 10; // 小数点以下を抽出
speekTen(tempTen,tempOne); // 10の位を読み上げ
speekOne(tempTen,tempOne); // 1の位を読み上げ
speekDot(tempBlw); // 小数点を読み上げ
speekBlw(tempBlw); // 小数点以下を読み上げ
fadeOut();
}
// 符号を読み上げ ----------------------------------------
void speekSin(int temp){
if(temp < 0){
char words[] = {24,80,99};
speekWords(words);
}
}
// 10の位を読み上げ ----------------------------------------
void speekTen(char tempTen,char tempOne){
// 10の位が 0 なら、すぐにReturn
if(tempTen == 0) return;
if(tempOne == 0){
// 1の位が 0 なら、
char words[] = {20,0,21,110,99};
if(tempTen == 1){ words[1] = 110; words[2] = 99;}
else if(tempTen == 2) words[0] = 5;
else if(tempTen == 3) words[0] = 6;
else if(tempTen == 4) words[0] = 8;
else if(tempTen == 5) words[0] = 11;
speekWords(words);
}else{
// 1の位が 0 以外なら、
char words[] = {tempTen * 2,20,23,20,99};
if(tempTen == 1){ words[0] = 22; words[2] = 99;}
else if(tempTen == 3) words[3] = 70;
else if(tempTen == 5) words[0] = 11;
speekWords(words);
}
}
// 1の位を読み上げ ----------------------------------------
void speekOne(char tempTen,char tempOne){
if((tempTen != 0)&(tempOne == 0)) return;
char words[] = {tempOne * 2,50,99};
if(tempOne == 6) words[1] = 150;
else if(tempOne == 8) words[1] = 100;
else if(tempOne == 1) words[1] = 120;
speekWords(words);
}
// 小数点を読み上げ ----------------------------------------
void speekDot(char tempBlw){
char words[] = {25,0,99};
if(tempBlw == 3) words[1] = 80;
else if(tempBlw == 9) words[1] = 80;
speekWords(words);
}
// 小数点以下を読み上げ ----------------------------------------
void speekBlw(char tempBlw){
char words[] = {tempBlw * 2 + 1,0,26,0,99};
speekWords(words);
}