温度に比例した電圧を出力する温度センサー(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); }