---
---
PWM を使用してオルゴール調の音色で曲を演奏します。 PIC16F1459のオルゴールプログラムをPIC12F1822に書きなおしたものです。このオルゴールは、基本的な動作を確認するために1つの音しか発生させていませんが、この発音方法を応用し、4音同時に演奏するプログラムも次のページに掲載しました。また、このプログラムでは、PICの特徴を生かし、演奏をしていないときはスリープ状態にし消費電力を抑えたため乾電池2本で、電池寿命を忘れるほど長く持ちます。小さな箱に組み込んで使用するのに最適です。
*Note*
このプログラムで使用する楽譜データは、 [オルゴールデーター作成 Windows アプリ] を利用して、作ることができます。
オルガン プログラム CB03_Orgel.zip |
<プログラム> main.c
/* File: main.c 1 * gakufu は、16 bit 単位。 * 最初にテンポ(1分間に演奏する四分音符の数)が記録されている。 * それ以降は音符 MSB 8 bitが音の高さ。 半音単位で、中央のドが 60(0x3C) * LSB 8 bitが音の長さ * 2分音符:10、 4分音符:08、 8分音符:04、 16分音符:02 * 付点2分音符:18、付点4分音符:0C、付点8分音符:06、付点16分音符:03 * | 7 6 5 4 3 2 1 0| 7 6 5 4 3 2 1 0 | * | 高さ | 長さ | * Language: MPLABX XC8 * Target: PIC12F1822 */ // 音が減衰する程度 指定値(ms)で1/16減少 #define EnvelopeConst 50 // 音符と音符の間の無音時間(ms) #define Silent 30 #include "xc.h" #include "gakufu.h" #define _XTAL_FREQ 8000000 #define SW RA5 #define SPK LATA2 #define TRIS_SPK TRISA2 #define TRIS_LED TRISA4 #define LED LATA4 #define waitStart 0 #define getGakufu 1 #define getOnpu 2 #define onpuStart 3 #define onpuPlay 4 #define onpuEnd 5 #define gkfEnd 6 #pragma config FOSC = INTOSC, WDTE = OFF, PWRTE = OFF, MCLRE = OFF, CP = OFF #pragma config CPD = OFF, BOREN = ON, CLKOUTEN = OFF, IESO = OFF, FCMEN = OFF #pragma config WRT = OFF, PLLEN = OFF, STVREN = ON, BORV = LO, LVP = OFF // 音の高さを決める定数 for 8MHz Fosc PWM Freq 15.625KHz 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) が中心のド(60 0x3E) ,0xD9B はラ (440Hz) unsigned int accWave,incWave; unsigned char amp,wave; void main(void) { char SWdown=0; // SW が押下のフラグ char SWnotRdy=0; // SW チャタリング防止 char state = 0; // 制御ステート unsigned int pOnp = 0; // 音符位置 unsigned int Onp; // 音符 unsigned char tempo; // テンポ unsigned char tPlay; // 演奏長さ char AmpCnt = 25; // 振幅調整 char TmpCnt = 65; // テンポ調整 char TmpFlg = 0; // テンポフラグ char SltCnt = 20; // 無音時間 char SltFlg = 0; // 無音フラグ char ampNext; // 次音符音量 OSCCON = 0b01110000; // 内部クロック 8Mhz ANSELA = 0; // 全てデジタルIO LATA = 0; TRIS_SPK = 0; // SPK RA2 音声出力 SPK = 0; // SPK 出力「0] TRIS_LED = 0; // LED RC2 出力 LED = 0; // LED 消灯 nWPUEN = 0; // 弱プルアップ 有効 // Timer1 の設定 ------------------------------------ T1CON = 0b01000001; // Fosc ps 1/1 TMR1ON TMR1 = 0xE100; // +7936 (0.992ms) で Carry TMR1IF = 0; // Timer1フラグクリア // PWM Timer2 の設定 ------------------------------------ T2CON = 0b00000100; // Timer2 ON PreS 1/1 PR2 = 0x7F; // 繰返し周波数 15.625KHz TMR2IF = 0; // Timer2フラグクリア TMR2IE = 1; // Timer2割込み有効 PEIE = 1; // ペリフェラル割込み有効 while(1){ //------------ 1ms 毎のタイミング処理 ---------------------- if(TMR1IF){ // 1ms 毎に実施 TMR1H = 0xE1; // 次の 1ms を設定 TMR1IF = 0; // フラグクリア //------- SWチャタリング防止 1msループ内 ---------------- if(SWnotRdy){ // SWupの確認中なら if(SW)SWnotRdy--; // SWupの確認をー1 else SWnotRdy = 5; // SWupの確認初期値 }else if(!SW){ // SWupの確認済みでDownなら SWdown=1; // SWが押されたマーク SWnotRdy = 5; // SWupの確認待ちに } //------- 振幅調整 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; // 消音フラグをセット } } //------------------- 主フロー制御 --------------- switch(state){ case waitStart: // ====== 演奏開始待ち ====== if(SWnotRdy)break; // SW がOFFのこと SWdown=0; // SWdown リセット CCP1CON = 0; // PWM OFF T1CON = 0; // Timer1 OFF GIE = 0; // 割込み禁止 IOCAN5 = 1; // SW ONを待つ IOCAF = 0; // SW フラグリセット IOCIE = 1; // SW SLEEP再起動 ON NOP(); // ------------------------------------- SLEEP(); // この間 スリープ NOP(); // ------------------------------------- IOCIE = 0; // SW SLEEP再起動禁止 CCP1CON = 0b00111100; // シングルPWM 正論理出力 T1CON = 0b01000001; // Fosc ps 1/1 TMR1ON SWdown = 0; // SW 初期化 SWnotRdy = 5; // GIE = 1; // 割込み有効 state = getGakufu; break; case getGakufu: // ====== 楽譜の設定 ====== if(p1gkf[pOnp]==0xFFFF)pOnp=0; // 楽譜終了なら先頭に tempo = 7500/p1gkf[pOnp++]; // 楽譜のテンポ取得 TmpCnt = tempo; // テンポセット TmpFlg = 0; // フラグりセット state = getOnpu; break; case getOnpu: // ====== 音符の設定 ====== Onp = p1gkf[pOnp++]; // 音符を取得 if(Onp == 0xFFFF){ // 曲の終了なら state = gkfEnd; // 抜ける break; } tPlay = Onp & 0x00FF; // 音符の長さ取得 Onp = Onp >> 8; // 音高を取り出す if(Onp){ // 音符なら incWave = dW_tbl[Onp - 23]; // 音高を繰返し時間に変換 ampNext = 0x7F; // 音量最大 }else{ // 休符なら incWave = 0; // 繰返し なし ampNext = 0; // 音量OFF } state = onpuStart; break; case onpuStart: // ====== 音符の演奏開始 ====== if(TmpFlg){ // テンポ待ち TmpFlg = 0; // テンポフラグリセット amp = ampNext; // 音量最大 if(amp)LED=1; // 休符でなければLED点灯 AmpCnt = EnvelopeConst; // 音量減少リセット state = onpuPlay; } break; case onpuPlay: // ====== 音符の演奏継続 ====== if(TmpFlg){ // テンポ待ち TmpFlg = 0; // テンポフラグリセット if(tPlay-- == 2){ // 音符終了なら SltCnt = tempo - Silent; // 消音時間をセットし SltFlg = 0; // 消音フラグをリセット LED=0; // LED消灯 state = onpuEnd; } if(SWdown){ // SWが押されたなら SWdown=0; // SW 処理済み明示 state = gkfEnd; // 終了処理へ } } break; case onpuEnd: // ====== 音符の演奏終了 ====== if(SltFlg){ // 時間を待つ amp = 0; // 音量OFF state = getOnpu; // 次の音符取得へ } break; case gkfEnd: // ====== 楽譜の演奏終了 ====== LED=0; // LED消灯 pOnp--; // 曲終了まで進め while(p1gkf[pOnp++] != 0xFFFF); state = waitStart; // 演奏開始待ちに break; } } // while } // 割込み処置 ----------------------------------------------------- void interrupt isr(void){ if(TMR2IF){ // Timer2 割込みを確認 TMR2IF = 0; // Timer2 フラグ リセット accWave += incWave; // 周期計算 if(STATUSbits.CARRY)wave ^= 1; // キャリが出れば、波形反転 if(wave)CCPR1L = amp; // 波形ONなら、 出力あり else CCPR1L = 0; // OFFなら、出力なし } }
<プログラム> gakufu.h
const unsigned int p1gkf[] = { 84, // 夕焼け小焼け 0x4304,0x4304,0x4304,0x4504,0x4304,0x4304,0x4304,0x4004, 0x3C04,0x3C04,0x3E04,0x4004,0x3E0C,0x0004,0x4008,0x4004, 0x4304,0x4504,0x4804,0x4804,0x4504,0x4304,0x4304,0x4504, 0x4304,0x480C,0x0004,0x4806,0x4A02,0x4804,0x4504,0x4804, 0x4804,0x4304,0x4304,0x4504,0x4304,0x4504,0x4304,0x400C, 0x0004,0x4304,0x4004,0x3E04,0x3C04,0x3E04,0x3E04,0x3C04, 0x3E04,0x4004,0x4304,0x4504,0x4304,0x480C,0x0004, 0xFFFF, 92, // フランスの古い歌 0x3E04,0x4304,0x4504,0x4604,0x4804,0x4A0C,0x4A04,0x4804, 0x4A04,0x4B04,0x4804,0x4A0C,0x4A04,0x4804,0x4A04,0x4B04, 0x4804,0x4A04,0x4B02,0x4A02,0x4804,0x4604,0x450E,0x4302, 0x430C,0x3E04,0x4304,0x4504,0x4604,0x4804,0x4A0C,0x4A04, 0x4804,0x4A04,0x4B04,0x4804,0x4A0C,0x4A04,0x4804,0x4A04, 0x4B04,0x4804,0x4A04,0x4B02,0x4A02,0x4804,0x4604,0x450E, 0x4302,0x4310,0x4308,0x4304,0x4504,0x460C,0x4604,0x4808, 0x4808,0x450C,0x4504,0x4A0C,0x4A04,0x4B04,0x4D02,0x4B02, 0x4A04,0x4804,0x4608,0x4504,0x4304,0x450C,0x3E04,0x4304, 0x4504,0x4604,0x4804,0x4A0C,0x4A04,0x4804,0x4A04,0x4B04, 0x4804,0x4A0C,0x4A04,0x4804,0x4A04,0x4B04,0x4804,0x4A04, 0x4B02,0x4A02,0x4804,0x4604,0x450E,0x4302,0x4310, 0xFFFF, 92, // かたつむり 0x4506,0x4502,0x4504,0x4204,0x3E06,0x3E02,0x3E04,0x4004, 0x4206,0x4202,0x4004,0x3E04,0x4008,0x0008,0x4206,0x4302, 0x4504,0x4704,0x4506,0x4502,0x4504,0x4204,0x4006,0x4002, 0x3E04,0x4004,0x4208,0x0008,0x4504,0x4A04,0x4A04,0x4504, 0x4204,0x4504,0x4504,0x4204,0x3E04,0x4204,0x4206,0x4002, 0x3E08,0x0008, 0xFFFF, 0xFFFF, };