PIC12F1822の基本動作から応用プログラムまでを学びます。

ホーム
16F18313
16F18325
16F18346
16F1619
Curiosity
---
---
    
12F1822
16F1455
16F1459
18F14K50
18F26J50
dsPIC
その他
オルゴール

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,
};