〇main.txt→main.cへ 〇末尾にmain_sub と曲データがあります。 /******************************************************** * Version_17 連続間欠タイマー + 得点表示 25.2.22 * Pic:16F1827 MPLAB IDE Ver8.84 HI-TECH C v9.83 * 計測時間設定はVR(可変抵抗)、間欠時間はロータリーSW(VR) * 表示は大型手作り7segLED(interface:TM1637)アノード・コモン * および上面LCD(I2c)PCF8574T(1602) * メロディはピアノ(減衰)音を出力するプログラム(3連符可v)を利用 * メロディ部分は 12F1822(sub) に移動(プログラムメモリの不足) * RA0:AD変換(計測時間設定) RA1:AD変換(インターバル時間の設定) * RA2:AD変換(休憩時間設定) RA3:『休憩』ボタン * RA4:機能切換 RA5:リセット RA6、RA7:LCDのSDAとSCL * RB0〜3:カウンタ用 & RB0:開始/停止 * RB4,5:前面大型7segLED用 RB6,7:メロディ・サブへの信号 * タイマ0:1秒カウント用 タイマ1:メロディ用 タイマ2:PWM * 30秒以内は5秒、2分以内は10秒きざみ、越えると30秒きざみの時間設定 * インターバル時間をロータリーSWとRで設定するバージョン * インターバル設定は、最大20秒(0,5,10,15,20) * 休憩時間の設定は、2〜5分(ロータリーSW) * プログラムメモリ消費 90.5% 消費電流:50〜55mA(無音時) **********************************************************/ #include #include #define _XTAL_FREQ 4000000 // 4MHz #define REST RA3 // 休憩開始 #define SW RA4 // 機能切換 #define STSP RB0 // 開始/停止(start/stop) #define SDA RA6 // 上面LCD #define SCL RA7 // #define TRISSDA TRISA6 #define CLK RB4 // 前面大型7seg用 #define DIO RB5 // //#define ADDR1 0x4E // I2cアダプタPCF8574Tのアドレス #define ADDR1 0x4C #define R_melody 20 // 5分休憩の後のメロディ(秒数) /***** コンフィギュレーションの設定 ********/ __CONFIG(FOSC_INTOSC // INTOSCIO oscillator I/O function & WDTE_OFF // Watchdog Timer disabled & PWRTE_ON // Power-up Timer enabled & BOREN_ON // Brown Out enabled & MCLRE_ON // MCLR RA5はリセット & CP_OFF // Program memory code protection is disabled & CPD_OFF // データメモリーを保護しない(OFF) & CLKOUTEN_OFF // CLKOUTピンをRA6ピンで使用する(OFF) & IESO_OFF // Internal External Switchover mode is disabled & FCMEN_OFF // Fail-Safe Clock Monitor is disabled & WRT_OFF ); // Flashメモリーを保護しない(OFF) __CONFIG(PLLEN_OFF // 動作クロックを32MHzでは動作させない(OFF) & STVREN_ON // Stack Overflow/Underflow will cause a Reset & BORV_LO // 電源電圧降下常時監視電圧(2.5V)設定(LO) & LVP_OFF ); // 低電圧プログラミング機能使用しない(OFF) //大型の7seg側 abc〜g → TM1637側 seg7,seg6〜seg1(通常とは逆) char const LED_SEG[10] = {0x7E, 0x30, 0x6D, 0x79, 0x33, 0x5B, 0x5F, 0x70, 0x7F, 0x73 }; // 前面7seg用 // CGRAM(5x8ドット)に格納する内容(下側の5ビット) const char FontParts[][8] = { // 0x1F→0b11111 { 0x01, 0x02, 0x04, 0x0A, 0x02, 0x02, 0x02, 0x00 },// 0x01"イ" { 0x04, 0x04, 0x1F, 0x04, 0x0E, 0x15, 0x04, 0x00 },// 0x02"木" { 0x04, 0x0A, 0x11, 0x0E, 0x0A, 0x12, 0x04, 0x00 } // 0x03"分" }; // タイマー用 unsigned int Time,Time1,Time2; // 設定秒数(繰り返し,インターバル) unsigned int g_time,k_time,r_time; // ADCから得た計測時間 unsigned char min1,sec1,min2,sec2;// 分数、秒、休憩分、インターバル秒 unsigned char Mode; // 処理モード: 0〜3 unsigned char Count; // 20mSのカウント(50回で1秒) unsigned char s1,s2,s3,s4,s5; // 大型7seg用表示文字 // 前面大型7seg用 void S7_Start(void); void S7_Stop(void); void S7_init(); // 初期化 unsigned char S7_Out(unsigned char data); // LCD用関数 LCD1602は4ビットモードで稼働 void I2c_Start(void); void I2c_Stop(void); unsigned char I2c_Out(unsigned char data); void lcd_common(unsigned char data); void lcd_data(unsigned char data); void lcd_cmd(unsigned char data); void lcd_init(void); void lcd_str(char* ptr); // 表示 unsigned char Msg1[] = "xx クリカエシ = x:xx"; // 上面LCD(1行目) unsigned char Int1[] = "xフ メロディ = xx"; // タイマー用(1行目) unsigned char Counter1[] = "L R"; // カウンタ1行目 unsigned char Counter2[] = "* xx : * xx "; // カウンタ2行目 // 数値〜文字DATAへ void i_to_str(char digit, unsigned int data, char *buffer); void S7_Hyoji(); // 7seg表示 void Hyoji1(void); // LCD:タイマー時 void Hyoji2(void); // インターバル時 void Hyoji2b(void); // インターバル時節約表示 void Hyoji3(void); // カウンター時 void H_init(void); // 表示の初期化 // 表示する文字を設定 void keisan(void); // 秒→分:秒 void Set_7seg(); // カウンタ表示データへ // 各フラグ unsigned char BZF, chflg; // 開始ブザーと1秒経過のフラグ unsigned char Jump_flg,Rest_flg;// 休憩モードのフラグ unsigned char melody_on; // メロディonのフラグ // カウンター用 unsigned char RIGHT,LEFT; // 得点の変数(右,左) unsigned char SIDE; // サーブ側表示 R=>0, L=>1 unsigned char colon; // サーブ側変動時:点灯、連続得点:消灯 unsigned char Tact; // 得点用タクトスイッチ unsigned char set_interval(void);// インターバル時間(秒数)の取得 unsigned int set_restsec(void); // 休憩時間の設定(最大300秒) unsigned char melody(void); // Subへの信号出力"00" unsigned char melody_off(void); // Subへの停止信号出力"10" unsigned char bzr1(void); // 〃"01" unsigned char bzr2(void); // 〃"10" void delay_10us(unsigned char t1); // 10us遅延 void delay_10ms(unsigned char t2); // 10ms遅延 unsigned int GetData(); // ADCからデータ取得 unsigned char keysearch(); // どのキー? unsigned char getkey(); // 確認5回 // CGRAM用のデータ(custom)をメモリに格納する関数 void lcd_Def_Char(char adrs, char *pattern){ lcd_cmd(0x40 | (adrs << 3)); //CGRAMアドレス(adrs: 0-7を3,4,5ビットに) for(char i=0; i<8; i++){ lcd_data(pattern[i]); } } void main(void) { OSCCON = 0b01101000;// 内部クロック 4Mhz(右端は:10,00どちらも可) ANSELA = 0x07; // RA0,RA1,RA2をアナログに(ADC) ANSELB = 0x00; // 全てデジタルIO TRISA = 0x3F; // 基本は入力設定(RA3はその都度出力に) TRISB = 0x0F; // 0-3:入力、4,5 は 7seg(I2c)出力 // CM1CON0 = 0x00; // コンパレータ不使用 // ADCON0 = 0x00; デフォルト:RA0(0x04→RA1) 使う時=>0x05(有効化) ADCON1 = 0xA0; // Fosc/32,右詰,基準(Vdd-Vss) // タイマー0の設定 秒カウントダウン・タイマー用 OPTION_REG = 0x86; // 内部プルアップ無し、プリスケーラ 1/128 // TOCS=0,TOSE=0,PSA=0,PS2〜0=1:1:0 INTCON = 0xC0; // GIE=1:全割込み処理を許可する // PEIE=1ペリフェラル割込み有効, T0IE=0, T0IF=0 TMR0 = 0x64; // 初期値:100 0.5μS×(256-100)×256 => 約20mS T0IE = 0; // Timer0 始動はまだ /**** メインループ *****/ Rest_flg = 0; // 休憩フラグOFF Jump_flg = 0; LATB = 0xFF; // 表示器(7seg,LCD)の初期化 S7_init(); lcd_init(); // CGRAM0x01〜03へフォントデータ書込み for(char j=1; j<4; j++){ lcd_Def_Char(j, &FontParts[j-1][0]); } while(1){ // while_0 r_time = set_restsec(); // 休憩時間(秒)を取得 min2 = r_time / 60; // 休憩時間(分) H_init(); // 表示初期化 LATB = 0xFF; /*** 機能切替SW(RA4)により、タイマーとカウンターに動作分岐 ***/ if(SW == 1 || Jump_flg == 1){// 機能切替スイッチ=off(デフォルト:1) // か、カウンター動作中に『休憩』が押された。 // タイマーの場合 : 変数初期化 Count = 0; // 50回で1秒 BZF = 0; // 開始ブザーフラグ Mode = 0; // 動作モード(0:設定 1:開始 2:動作中 3:インターバル) while(1){ // while_1 if(REST == 0 || Jump_flg == 1){ // 「休憩ボタン」が押されたら while(REST == 0); delay_10ms(2); strcpy(Msg1,"xx キュウケイ = x:xx"); Msg1[0] = 0x01; // "イ"登録文字 Msg1[1] = 0x02; // "木" 〃 Time = r_time; // 休憩時間(秒)を取得 Rest_flg = 1; if(Jump_flg == 1)Jump_flg = 2; Mode = 1; // 計測開始 } if(STSP== 0){ // 開始/停止ボタン(RA7)が押されたら while(STSP== 0); // チャタリング回避 delay_10ms(2); if(Mode == 0 && Time > 0){ Mode = 1; // Timeが0でなければ開始 }else{ T0IE = 0; // タイマ0停止 melody_off(); // メロディ停止 Mode = 0; } } switch(Mode){ case 0: Rest_flg = 0; // 休憩モードOFF // 時間設定:AD変換で、VRから値(0-1023)を得る ADCON0 = 0x00; // AD変換:RA0を指定 g_time = GetData(); // VRから値(0-1023)を得る g_time = (long)g_time * 301 / 1023; // 最大300秒(5分) if(g_time > 120)g_time = g_time / 30 * 30;// 2分以上は30秒ごと else if(g_time > 30)g_time = g_time / 10 * 10;// 30秒−2分は10秒 else g_time = g_time / 5 * 5; // 30秒以下は5秒ごと Time = g_time; k_time = set_interval(); Time2 = k_time; Hyoji1(); // LCD表示 break; case 1: // 開始へ if(Rest_flg == 1)k_time = R_melody; Time2 = k_time; // 「5分休憩」時はメロディ20秒 T0IE = 1; // タイマ0始動 Mode = 2; BZF = 1; // ブザーフラグ break; case 2: // 設定秒の計測中 Hyoji1(); if(BZF == 1){ bzr2(); // 開始のブザーを鳴らす delay_10ms(10); // ピッピッ bzr2(); delay_10ms(3); BZF = 0; } if(0 < Time && Time < 4){ // 残り3秒からプッ if(chflg == 1){ // 1秒経過したら chflg = 0; bzr1(); } } break; case 3: // インターバル中 Hyoji2(); if(melody_on == 1){ melody_on = 0; // フラグをリセット melody(); // 12F1822 へメロディ開始信号 } break; } // end_switch keisan(); S7_Hyoji(); if(Jump_flg == 3)break;// カウンターへ戻るため } // end_while1 }else{ // SW=0(on)の場合 // カウンターの場合 Jump_flg = 0; LATB = 0xFF;// 信号初期化 SIDE = 2; // 最初はどちらでもない colon = 1; // コロン表示 while(1){ if(REST == 0){ // 『5分休憩ボタン』押下の時 while(REST == 0); delay_10ms(2); Jump_flg = 1; break; // whileを出てタイマーへ戻る } Tact = getkey(); // どのキーを押した? switch(Tact){ case 1: // 右得点 bzr1(); // ブザー RIGHT++; if(RIGHT > 99)RIGHT = 0; if(SIDE != 0){ SIDE = 0; // 右DOT点灯 colon = 1; // サイドが変わったら }else{ // コロン点灯 colon = 0; } while(RB0 == 0); delay_10ms(10); break; case 2: // 右減点 bzr2(); // ブザー if(RIGHT)RIGHT--; // 0なら減点無し while(RB1 == 0); delay_10ms(10); break; case 3: // 左減点 bzr2(); if(LEFT)LEFT--; // 0なら減点無し while(RB2 == 0); delay_10ms(10); break; case 4: // 左得点 bzr1(); LEFT++; if(LEFT > 99)LEFT = 0; if(SIDE != 1){ SIDE = 1; // 左DOT点灯 colon = 1; }else{ colon = 0; } while(RB3 == 0); delay_10ms(10); break; } // end_case Set_7seg(); S7_Hyoji(); Hyoji3(); } // end_while2 } // end_else } // end_whil_0 } // end_main // 割込み処置 ----------------------------------------------------- void interrupt isr(void){ // Timer0 は秒数カウント、Timer2はメロディ用 if (T0IF == 1){ // タイマー0の割込み発生か? TMR0 = 0x64; // タイマー0の初期化 Count++; // 割込み発生の回数をカウントする T0IF = 0; // タイマー0割込フラグをリセット if (Count >= 50){ // 割込みを50回カウントすると約1秒 Count = 0; Time--; chflg = 1; // 1秒経過フラグ } if(Time==0){ if(Mode == 2){ // 通常のタイムアップ? Time2 = k_time; // インターバル時間をセット if(Time2 == 0){ // インターバル無しなら Time = g_time; // 繰り返し時間をセット Mode = 1; }else{ Time1 = g_time; // 繰り返し時間をメモ Time = Time2; melody_on = 1; Mode = 3; // インターバル・モードへ } }else if(Mode == 3){ // インターバルの終了なら melody_off(); // メロディ中止(10送信) if(Rest_flg == 1){ Rest_flg = 0; //RESET(); H_init(); Mode = 0; if(Jump_flg == 2)Jump_flg = 3;// 1でも2でもない状態 }else{ Time = g_time; delay_10ms(30); // メロディとブザーが重ならないように Mode = 1; }// end_if(Rest_flg == 1) } // end_if(Mode == 2) } // End_if(Time==0) } //if (T0IF == 1) } // initialize void H_init(void){ strcpy(Msg1,"xx クリカエシ = x:xx"); Msg1[0] = 0x01; // 休の漢字 Msg1[1] = 0x02; Int1[1] = 0x03; // 分の漢字 } // RA1からインターバル時間を取得 unsigned char set_interval(){ unsigned int d_time; ADCON0 = 0x04; // AD変換:RA1を指定 d_time = GetData(); // VRから値(0-1023)を得る d_time = (long)d_time * 23 / 1023; // 一工夫 if(d_time > 19)d_time = 20; // 最大20秒 d_time = d_time / 5 * 5; // 5秒毎の数値に return(d_time); } // RA2から休憩時間を取得 unsigned int set_restsec(){ unsigned int d_time; ADCON0 = 0x08; // AD変換:RA2を指定 d_time = GetData(); // VRから値(0-1023)を得る d_time = (long)d_time * 330 / 1023; // 一工夫 // if(d_time > 290)d_time = 300; // 最大300秒 // if(d_time < 120)d_time = 120; // 最小120秒 d_time = d_time / 60 * 60; // 60秒毎の数値に return(d_time); } // 計算(秒→7segデータに) // s1〜s5は前面大型7seg、s1:右端 s5:左端(通常とは逆) // コロンは(大型)s3のa_seg(0x01)とb_seg(0x02) void keisan(void){ // s1 = LED_SEG[Time / 600];// 10分の桁の場合 s1 = 0x00; // 10分の桁は無 if(Time < 60){ s2 = 0x00; // 1分の桁が空ならコロンも無し s3 = 0x00; }else{ s2 = LED_SEG[Time / 60]; s3 = 0x03; // コロンあり } if(Time < 10){ s4 = 0x00; }else{ s4 = LED_SEG[Time % 60 / 10]; } s5 = LED_SEG[Time % 10]; } // カウンター用・表示データの設定(LEFT RIGHTの数値→7segデータに) void Set_7seg(){ // SIDE:0 右(s3のf:0x20) SIDE:1 左(s3のg:0x40) ::後ろから見て // コロン(s3のaとb) // 上側の7segの表示は左右が逆になる。 // 〃 では、サーブ側の表示はDP(最上位ビット:0x80)で代用する。 if((RIGHT / 10) == 0){ s1 = 0x00; } else{ s1 = LED_SEG[RIGHT / 10]; } s2 = LED_SEG[RIGHT % 10]; if((LEFT / 10) == 0){ s4 = 0x00; } else{ s4 = LED_SEG[LEFT / 10]; } s5 = LED_SEG[LEFT % 10]; // コロンの表示 if(colon == 0){ s3 = 0x00; }else{ s3 = 0x03; } // サーブ側の表示 if(SIDE == 0){ s3 |= 0x20; }else{ s3 |= 0x40; } } // 表示1( LCDで残り時間表示 ) void Hyoji1(void){ min1 = Time /60; // 総秒数→x分 sec1 = Time % 60; // 総秒数→x0秒 sec2 = Time2; // インターバル秒 i_to_str(1,min1, Msg1+12); // 分 表示 if(Time < 60){ // '0'サプレス処理 Msg1[12] = 0x20; Msg1[13] = 0x20; }else{ Msg1[13] = ':'; } i_to_str(2,sec1, Msg1+14); // 秒 if(Time < 10)Msg1[14] = 0x20; i_to_str(1,min2, Int1+0); // 休憩時間(分) i_to_str(2,sec2, Int1+14); // メロディ(秒) if(sec2 < 10)Int1[14] = 0x20; lcd_cmd(0x80); // 表示位置を1行目1列[00H]に設定 lcd_str(Msg1); // [00H]から書込まれる(表示する) lcd_cmd(0xC0); // 表示位置を2行目1列[40H]に設定 lcd_str(Int1); // [40H]から書込まれる(表示する) } // 表示2( LCDインターバル時間を表示 ) void Hyoji2(void){ min1 = Time1 /60; sec1 = Time1 % 60; sec2 = Time; i_to_str(1,min1, Msg1+12); if(Time1 < 60){ Msg1[12] = 0x20; Msg1[13] = 0x20; }else{ Msg1[13] = ':'; } i_to_str(2,sec1, Msg1+14); if(Time1 < 10)Msg1[14] = 0x20; i_to_str(1,min2, Int1+0); i_to_str(2,sec2, Int1+14); if(sec2 < 10)Int1[14] = 0x20; lcd_cmd(0x80); lcd_str(Msg1); lcd_cmd(0xC0); lcd_str(Int1); } // メロディ演奏時:表示時間節約のため(変化する部分のみ) void Hyoji2b(void){ sec2 = Time; // i_to_str(1,min2, Int1+0); i_to_str(2,sec2, Int1+14); if(sec2 < 10)Int1[14] = 0x20; lcd_cmd(0xC0); lcd_str(Int1); } // カウンター用・表示データの設定(LEFT RIGHTの数値→LCDのデータに) void Hyoji3(void){ // 上側LCD if(SIDE == 0){ Counter2[0] = ' '; Counter2[9] = '*'; }else if(SIDE == 1){ Counter2[0] = '*'; Counter2[9] = ' '; } i_to_str(2, RIGHT, Counter2+13);// 2行目右 if(RIGHT < 10)Counter2[13] = 0x20;// '0'抑制 i_to_str(2, LEFT, Counter2+4); // 2行目左 if(LEFT < 10)Counter2[4] = 0x20; if(colon == 1){ strcpy(Counter1,"L Over R"); }else{ strcpy(Counter1,"L R"); } lcd_cmd(0x80); lcd_str(Counter1); lcd_cmd(0xC0); lcd_str(Counter2); } // 大型7セグ初期化 void S7_init(){ S7_Start(); // スタート S7_Out(0x40); // 移動表示モード S7_Stop(); S7_Start(); S7_Out(0x8B); // 明るさ指定(12/16) S7_Stop(); } // 大型7セグに表示 void S7_Hyoji(){ S7_Start(); S7_Out(0xC0); // 開始位置左端 S7_Out(s5); // 左端 10分 S7_Out(s4); // 分 S7_Out(s3); // コロン S7_Out(s2); // 10秒 S7_Out(s1); // 右端 S7_Stop(); } // 通信 スタート・ストップ void S7_Start(void){ /* CLk,DIOは通常:High */ DIO = 0; // 先にDIO=Low } void S7_Stop(void){ CLK = 0; // CLK Low DIO = 0; // DIO Low CLK = 1; // 先にCLKをHigh DIO = 1; // 後からDIOをHigh } // int整数からASCII文字に変換 void i_to_str(char digit, unsigned int data, char *buffer){ char i; buffer += digit; // 最後の数字位置 for(i=digit; i>0; i--) { // 変換は下位から上位へ buffer--; // ポインター1 *buffer = (data % 10) + '0'; // ASCIIへ data = data / 10; // 次の桁へ } } // I2cスタート・ストップ void I2c_Start(void){ // SCL,SDAは通常:High SDA = 0; // 先に SDA=Low TRISSDA = 0; // SDA出力 } void I2c_Stop(void){ SCL = 0; // SCL Low SDA = 0; // SDA Low TRISSDA = 0; // 出力モードに戻す SCL = 1; // 先にSCLをHigh SDA = 1; // 後からSDAをHigh } // I2c_で1バイト出力(1ビットずつ) unsigned char I2c_Out(unsigned char data){ int i; unsigned char BitPos, ACK; TRISSDA = 0; // SDA出力モード BitPos = 0x80; // ビット位置初期値(左端) for(i=0; i<8; i++) // 8ビット繰り返し { SCL = 0; // SCL Low if((data & BitPos) != 0) // ビット出力 SDA = 1; // SDA High else SDA = 0; // SDA Low BitPos = BitPos >> 1; // ビット位置移動 SCL = 1; // SCL Highに戻す } // Ack チェック SCL = 0; // クロック1Low TRISSDA = 1; // 入力モードにしてACK入力 ACK = SDA; // ACKチェック for delay ACK = SDA; // ACKチェック SCL = 1; // クロックHighに戻す return(ACK); // ACKを戻り値とする } // アダプターへデータ送信(基本) void lcd_common(unsigned char data){ I2c_Start(); // スタート I2c_Out(ADDR1); // LCD1のアドレス I2c_Out(data); // 表示データ出力 I2c_Stop(); // ストップ delay_10us(5); // 30μsec待ち } // 液晶へ1文字表示データ出力 void lcd_data(unsigned char data){ unsigned char hydata; // 上位4ビット hydata = data & 0xf0 | 0x0D; // +Backlight,EN,RS lcd_common(hydata); delay_10us(2); lcd_common(hydata & ~0x04); // Enable_Pulse -> Low delay_10us(2); // 下位4ビット hydata = (data << 4) & 0xf0 | 0x0D; // +Backlight,EN,RS lcd_common(hydata); delay_10us(2); lcd_common(hydata & ~0x04); // Enable_Pulse -> Low delay_10us(5); } // 液晶へ1コマンド出力 void lcd_cmd(unsigned char data){ unsigned char command; // 上位4ビット command = data & 0xf0 | 0x0C; // +Backlight,EN,(RS:0) lcd_common(command); delay_10us(2); lcd_common(command & ~0x04); // Enable_Pulse -> Low delay_10us(2); // 下位4ビット command = (data << 4) & 0xf0 | 0x0C;// +Backlight,EN lcd_common(command); delay_10us(2); lcd_common(command & ~0x04); // Enable_Pulse -> Low delay_10us(5); } // I2Cで1バイト出力(要:9ビット目CLK ON/OFF) unsigned char S7_Out(unsigned char data){ char i; for(i=0; i<8; i++){ // 8回繰り返し CLK = 0; // CLK Low DIO = (data>>i) & 0x01; // ビット送信 CLK = 1; // CLK Highに戻す } CLK = 0; // ※要9ビット目 CLK = 1; // ACK:low } // 文字列表示関数 void lcd_str(char *ptr){ while(*ptr != 0) //文字取り出し lcd_data(*ptr++); //文字表示 } // 初期化関数 void lcd_init(void){ delay_10ms(10); // 0.1秒待つ(電源が安定するまで) // 8ビットモード lcd_common(0x34); delay_10us(1); lcd_common(0x34 & ~0x04);// Enable→0 lcd_common(0x34); delay_10us(1); lcd_common(0x34 & ~0x04); lcd_common(0x34); delay_10us(1); lcd_common(0x34 & ~0x04); delay_10us(1); // 4bit_mode & BackLight,EN lcd_common(0x2C); // '2'のデータと'1100' delay_10us(1); lcd_common(0x2C & ~0x04);// Enable を 0 に delay_10us(5); // ここから4ビット通常モード delay_10us(10); lcd_cmd(0x28); // 4ビット × 2, 2line delay_10us(10); lcd_cmd(0x08); // Display off,Cutsor off Blink off delay_10us(10); lcd_cmd(0x01); // Display clear delay_10ms(1); lcd_cmd(0x06); // Entry mode delay_10us(10); lcd_cmd(0x0c); // Display on,Cursor off delay_10us(10); } // 遅延タイマ void delay_10us(unsigned char t1){ while(t1){ __delay_us(10); // 10μsec t1--; // 10μsec x time } } void delay_10ms(unsigned char t2){ while(t2){ __delay_ms(10); // 10msec t2--; // 10msec x time } } // A/D変換:10回の平均値 unsigned int GetData(){ unsigned int hei, kei; unsigned char i; kei = 0; for(i=0;i<10;i++){ ADON = 1; delay_10us(1); ADGO = 1; while(ADGO); kei = kei + (ADRESH*256 + ADRESL); } hei = kei / 10; return(hei); } // 押されている(=0の)SWを探す unsigned char keysearch(){ unsigned char tsw; switch (PORTB & 0b00001111){// RB0〜RB3 case 0b00001110:// 押されたビットが0になる tsw = 1; break; case 0b00001101: tsw = 2; break; case 0b00001011: tsw = 3; break; case 0b00000111: tsw = 4; break; default: tsw = 0; } return (tsw); } // 5回連続で「押」を確認したら、その値を返す unsigned char getkey(){ unsigned char newval, oldval, cnt; oldval = 0x00; while(1){ for(cnt = 0; cnt < 5; cnt++){ newval = keysearch(); if(newval != oldval){ cnt = 0; oldval = newval; } delay_10us(10); } return(newval); } } // サブ(12F1822)への信号(RB6,7) // サブ側は、00:メロディ開始 10:メロディ停止 unsigned char melody(void){ LATB = 0x3F; // "00" delay_10us(10); PORTB = 0xFF; } unsigned char melody_off(void){ LATB = 0xBF; // "10"bzr2と同じ delay_10us(10); LATB = 0xFF; } unsigned char bzr1(void){ LATB = 0x7F; // "01" delay_10us(10); LATB = 0xFF; } unsigned char bzr2(void){ LATB = 0xBF; // "10" delay_10us(10); LATB = 0xFF; } // mainのEOF ここからはサブ /************************************************************ * タイマー&カウンターのサブ * PIC:12F1822 MPLAB_IDE v8.84 htc 9.83 * 駆動周波数を8MHzから4MHzに変更したバージョン * RA4、RA5 単独押はブザー1、2 * RA4,RA5:同時押:メロディ(メロディ中のRA4:停止) * タクトスイッチのRA4,RA5をメイン・プログラムに繋ぎ、信号による * ブザーやメロディ・コントロールを可能にした ************************************************************/ // 音が減衰する程度 指定値(ms)で1/16減少 #define EnvelopeConst 30 // 50→30 // 音符と音符の間の無音時間(ms) 変更30→10 #define Silent 10 #include /* 楽譜は const unsigned int Music[] = { , , }; の形式になっていること */ #include "ohkinakurino_k.h" // 『大きな栗の木の下で』 #define _XTAL_FREQ 4000000 // メロディとブザー音は RA2 から /***** コンフィギュレーションの設定 ********/ __CONFIG(FOSC_INTOSC // INTOSCIO oscillator I/O function & WDTE_OFF // Watchdog Timer disabled & PWRTE_OFF // Power-up Timer enabled & BOREN_ON // Brown Out enabled & MCLRE_ON // MCLR RA3はリセット & CP_OFF // Program memory code protection is disabled & CPD_OFF // データメモリーを保護しない(OFF) & CLKOUTEN_OFF // CLKOUTピンをRA6ピンで使用する(OFF) & IESO_OFF // Internal External Switchover mode is disabled & FCMEN_OFF // Fail-Safe Clock Monitor is disabled & WRT_OFF ); // Flashメモリーを保護しない(OFF) __CONFIG(PLLEN_OFF // 動作クロックを32MHzでは動作させない(OFF) & STVREN_ON // Stack Overflow/Underflow will cause a Reset & BORV_LO // 電源電圧降下常時監視電圧(2.5V)設定(LO) & LVP_OFF ); // 低電圧プログラミング機能使用しない(OFF) // 音の高さを決める定数 for 8MHz Fosc PWM Freq 16.525kHz const unsigned int dW_tbl[] = { // Do, Do#, Re, Re#, Mi, Fa, Fa#, So, So#, Ra, Ra#, Si, 0,0x112,0x122,0x133,0x146,0x159,0x16E,0x183,0x19B,0x1B3,0x1CD,0x1E8,0x205, //12 0x224,0x245,0x267,0x28C,0x2B3,0x2DC,0x307,0x336,0x366,0x39A,0x3D1,0x40B, 0x449,0x48A,0x4CF,0x518,0x566,0x5B8,0x60F,0x66C,0x6CD,0x735,0x7A3,0x817, 0x892,0x915,0x99F,0xA31,0xACD,0xB71,0xC1F,0xCD8,0xD9B,0xE6A,0xF46,0x102E, 0x1125,0x122A,0x133E,0x1463,0x159A,0x16E3,0x183F,0x19B0,0x1B37,0x1CD5,0x1E8C,0x205D, 0x224A,0x2454,0x267D,0x28C7,0x2B34,0x2DC6,0x307E,0x3361,0x366F,0x39AB,0x3D19,0x40BB }; // 0x892(index 37) が中心のド(楽譜ファイルでは +23 して、60:0x3Cと表記) // 0xE6A はラ (440Hz) unsigned int accWave,incWave; unsigned char amp,wave; void melody(); // オルゴール演奏 // メロディ用変数 unsigned int Track; // 音符位置 unsigned int Note; // 音符 unsigned char tempo; // テンポ unsigned char Len; // 演奏長さ char AmpCnt = 25; // 振幅調整 char TmpCnt = 65; // テンポ調整 char TmpFlg = 0; // テンポフラグ char SltCnt = 20; // 無音時間 char SltFlg = 0; // 無音フラグ char ampNext; // 次音符音量 unsigned char int_sw; unsigned char set_sw(void); void bzr1(void); // プ void bzr2(void); // ピ void delay_10us(unsigned char t1); // 10us遅延 void delay_10ms(unsigned char t2); // 10ms遅延 void main(void) { // 使用しないピンは出力にして、0設定 // OSCCON = 0b01110000; // 内部クロック 8MHz OSCCON = 0b01101000; // 内部クロック 4MHz ANSELA = 0; // 全てデジタルIO TRISA = 0b00111000; // RA2 音声出力 RA0 LED PORTA = 0b00111000; // nWPUEN = 1; // 弱プルアップ 有効 /* Timer1 の設定 */ T1CON = 0b01000000; // Fosc ps 1/1 TMR1ONはまだ // TMR1 = 0xE100; // +7936 (0.992ms) で Carry TMR1 = 0xEF00; TMR1IF = 0; // Timer1フラグクリア /* PWM Timer2 の設定 */ CCP1CON = 0b00111100; // シングルPWM 正論理出力 T2CON = 0b00000000; // Timer2 Off PreS 1/1 // PR2 = 0x7F; // 繰返し周波数 15.625KHz PR2 = 0x3F; TMR2IF = 0; // Timer2フラグクリア TMR2IE = 1; // Timer2割込み有効 PEIE = 1; // ペリフェラル割込み有効 GIE = 1; // 全割込み処理を許可する RA4 = 1; RA5 = 1; delay_10ms(50); // (SW受付を)一休み Track = 0; // メロディの進行シグナル while(1){ int_sw = set_sw(); // 値は、11(=7:信号なし)から00(=0:全ポートon)まで4種類 // メロディは、00で開始10で停止 switch(int_sw){ case 0: // =00 melody(); break; case 1: // =01 ブッ bzr1(); break; case 2: // =10 プッ bzr2(); break; case 3: // =11:デフォルト break; } } // end_while } // end_main unsigned char set_sw(void){ unsigned char sw; sw = PORTA; sw &= 0x30; // RA4〜RA5をDATAとしてinput sw = sw>>4; // シフトして数値に return(sw); } // 割込み処置 ----------------------------------------------------- void interrupt isr(void){ if(TMR2IF){ // Timer2 割込みを確認 TMR2IF = 0; // Timer2 フラグ リセット accWave += incWave; // 周期計算 if((STATUS & 0x01)==1)wave ^= 1;// キャリが出れば、波形反転 if(wave)CCPR1L = amp; // 波形ONなら、 出力あり else CCPR1L = 0; // OFFなら、出力なし } } /* メロディ関数 */ void melody(){ CCP1CON = 0b00111100; // シングルPWM 正論理出力 unsigned char Step; TRISA2 = 0; // 音源を出力に // Track = 0; // 「いつも最初から」の時 delay_10ms(10); // 開始信号が落ち着くための時間 if(Track == 0){ Step = 1; }else{ TMR2ON = 1; Step = 2; } TMR1ON = 1; while(1){ //------------ 1ms 毎のタイミング処理 ---------------------- if(TMR1IF){ // 1ms 毎に実施 // TMR1H = 0xE1; // 次の 1ms を設定 TMR1H = 0xEF; // 次の 1ms を設定 TMR1IF = 0; // フラグクリア //------- 振幅調整 50ms -------------------------------- if(AmpCnt-- == 1){ AmpCnt = EnvelopeConst; // 音量を 50ms毎に amp = (int)amp * 15/16; // 音量を -6% する } //------- テンポ調整 ----------------------------------- if(TmpCnt-- == 1){ TmpCnt = tempo; // テンポ毎に TmpFlg = 1; // テンポフラグをセット } //------- Silent時間 ----------------------------------- if(SltCnt-- == 1){ // 消音時間になれば SltFlg = 1; // 消音フラグをセット } } int_sw = set_sw(); if(int_sw == 2){ // 信号が"10"なら終了処理へ delay_10ms(10); // 信号安定時間 Step = 6; // 終了 } //----- 主フロー制御 -------------- switch(Step){ case 1: // ====== 楽譜の設定 ====== tempo = 2500/Music[Track++];// 楽譜のテンポ取得7500→2500 TMR2ON = 1; // Timer2 on TmpCnt = tempo; // テンポセット TmpFlg = 0; // フラグりセット Step = 2; break; case 2: // ====== 音符の設定 ====== Note = Music[Track++]; // 音符を取得 if(Note == 0xFFFF){ // 曲の終了なら Track = 0; Step = 1; // Step = 6; // 抜ける break; } Len = Note & 0x00FF; // 音符の長さ取得 Note = Note >> 8; // 音程を取り出す if(Note){ // 音符(休符でない)なら incWave = dW_tbl[Note - 23];// 音符60→テーブル37番目 ampNext = 0x7F; // 音量最大 }else{ // 休符なら incWave = 0; // 繰返し なし ampNext = 0; // 音量OFF } Step = 3; break; case 3: // ====== 音符の演奏開始 ====== if(TmpFlg){ // テンポ待ち TmpFlg = 0; // テンポフラグリセット amp = ampNext; // 音量最大 AmpCnt = EnvelopeConst; // 音量減少リセット Step = 4; } break; case 4: // ====== 音符の演奏継続 ====== if(TmpFlg){ // テンポ待ち TmpFlg = 0; // テンポフラグリセット if(Len-- == 2){ // 音符終了なら SltCnt = tempo - Silent;// 消音時間をセットし SltFlg = 0; // 消音フラグをリセット Step = 5; } } break; case 5: // ====== 音符の演奏終了 ====== if(SltFlg){ // 時間を待つ amp = 0; // 音量OFF Step = 2; // 次の音符取得へ } break; case 6: // ====== 楽譜の演奏終了 ====== TMR1ON = 0; // Timer1 Off TMR2ON = 0; // Timer2 Off break; } // end_switch if(Step == 6){ TRISA2 = 1; // 音源を入力に return; } } // end_while } // end_melody // 遅延タイマ void delay_10us(unsigned char t1){ while(t1){ __delay_us(10); // 10μsec t1--; // 10μsec x time } } void delay_10ms(unsigned char t2){ while(t2){ __delay_ms(10); // 10msec t2--; // 10msec x time } } /****** ブザー用ルーチン ********/ void bzr1(void){ CCP1CON = 0b00000000; // PWM設定を戻す TRISA2 = 0; // 音源を出力に設定 int i; for (i = 0; i < 80 ; i++) { RA2 = 1 ; delay_10us(60) ; RA2 = 0 ; delay_10us(60) ; } TRISA2 = 1; // RA2を入力に戻す } void bzr2(void){ CCP1CON = 0b00000000; TRISA2 = 0; int i; for (i = 0; i < 60 ; i++) { RA2 = 1 ; delay_10us(40) ; RA2 = 0 ; delay_10us(40) ; } TRISA2 = 1; } // プログラムEOF ここからは曲データ const unsigned int Music[] = { 130, // 大きな栗の木の下で 0x3C18,0x3C0C,0x3E0C,0x400C,0x400C,0x4318,0x400C,0x400C, 0x3E0C,0x3E0C,0x3C24,0x000C,0x4018,0x4118,0x4318,0x4818, 0x4518,0x4818,0x4324,0x000C,0x4818,0x4818,0x4718,0x4318, 0x450C,0x450C,0x450C,0x450C,0x4324,0x000C,0x3C18,0x3C0C, 0x3E0C,0x400C,0x400C,0x4318,0x400C,0x400C,0x3E0C,0x3E0C, 0x3C24,0x000C, 0xFFFF, 0xFFFF, };