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

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

PIC16F18346の内部モジュールの働きを確認する意味で、NCO,DSM,CLC,CCPを多様してオルゴールを作成します。

PICのペリフェラルは以下の用途に使用しています。

Timer4
SWのチャタリング防止のため1mSタイミングを作るのに使用
Fosc/4(2MHz)をPS:/16 PR4;/125するので、1mS毎にFlagをセット。
Timer6
NCOのクロックとなる250KHzを作るのに使用
Fosc/4(2MHz)をPS:/1 PR6;/8するので、250KHzをCLC1に出力。
CCP1 Timer2
音量(平均電圧レベル)を変化させるためPWM波形を作るのに使用
PWM周期はFosc/4(2MHz)をPS:/1 PR2;/64するので、31KHz。50mS毎に15/16(6%)ずつ減少させている。
NCO
音階に従った周波数の波形を発生させる中心のモジュール。
NCOのクロックはTimerから直接入力できないので、CLC1を経由している。
DSM
NCOとPWMの出力を組み合わせオルゴール出力とする。
CCP4 Timer5
音量(平均電圧レベル)を変化させるための50mSをLFINTOSCから作成している。
CCP2 CCP3 Timer3
CCP3で無音期間の30mSになるとCLC3に構成したSR FFをセットし、CCP2で音符演奏の長さになったらリセットする。
CCP2IFは、音符演奏の終了をプログラムに通知し、次の音符を読み込み演奏するフラグとなる。

オルゴールの波形

ラ音の八分音符を3連続で演奏した時の波形です。


プログラム

このプログラムは、main.c,config.h,config.c,gakufu.hの4つのファイルから構成されています。スペースの関係で、このページには、main.c,config.cのファイルだけ掲載します。他のファイルは、下のアイコンからダウンロードしてください。また、楽譜ファイル(gakufu.h)は、このホームページPIC16F18346のオルゴール項目にあるPCアプリで作成したものと同一の構成です。そのまま、使用することが可能です。

Orgel ソースファイル B02_orgel.zip

main.c

/*  File: オルゴール  main.c  8 MHz NCO PWM DSM
 * RC0 LED * RA4 SP * SW  RA5
 *
 *     Language: MPLABX XC8
 *     Target: PIC16F18346
 */

// 音が減衰する程度  指定値(ms)で1/16減少
#define EnvelopeConst 50
// 音符と音符の間の無音時間(ms)
#define Silent        30

#include <xc.h>
#include <stdint.h>
#include "config.h"
#include "gakufu.h"

#define _XTAL_FREQ 8000000

// 共通変数 ************************************************
uint8_t SWdown=0;           // SW が押下のフラグ

//----------------- 割込み処理 1ms 毎 ------------------------
void interrupt isr(void){
    static uint8_t SWnotRdy=0;       // SW チャタリング防止回数
    if(TMR4IF){                         // 1ms 毎に実施
        TMR4IF = 0;                     // フラグクリア
                                        //SWチャタリング防止 -------
        if(SWnotRdy){                         // SWupの確認中なら
            if(SW)SWnotRdy--;                 //   SWupの確認をー1
              else SWnotRdy = 5;              //   SWupの確認初期値
        }else if(!SW){                        // SWupの確認済みでDownなら
                SWdown=1;                     //   SWが押されたマーク
                SWnotRdy = 5;                 //   SWupの確認待ちに
        }
    }
}

//---------------------- main ------------------------
void main(void) {
    uint16_t cntOnpu;           // 音符再生時間
    uint16_t incWave;           // 音符周波数
    uint16_t pOnp = 0;          // 演奏音符位置
    uint16_t Onp;               // 音符
    uint8_t state = 0;          // 制御ステート
    uint8_t tempo;              // テンポ

    OSCFRQ  = 0b00000110;       // HFOSC  16MHz
    OSCCON1 = 0b01100001;       // HFINT1 DIV 2

    int_pins();
    int_modules();
    TMR4IF = 0;            // タイマー4 割込みフラッグをクリア
    TMR4IE = 1;            // タイマー4 割込みを使用許可
    PEIE = 1;              // 周辺機能割込みの使用許可
    GIE  = 1;              // 全割込みの使用許可

    while(1){
        //------------------- 主フロー制御 ---------------
        switch(state){
            case waitStart:             // ====== 演奏開始待ち ======
                if(SWdown){                     // SW押された なら
                    SWdown=0;                   // SWdown リセット
                    state = getGakufu;
                }
                break;
            case getGakufu:             // ====== 楽譜の設定 ======
                if(p1gkf[pOnp]==0xFFFF)pOnp=0;  // 楽譜終了なら先頭に
                tempo = 7500/p1gkf[pOnp++];     // 楽譜のテンポ取得
                playCnt = 0;                    // 演奏時間カウンタリセット
                state = getOnpu;
                break;
            case getOnpu:               // ====== 音符の設定 ======
                Onp = p1gkf[pOnp++];            // 音符を取得
                if(Onp == 0xFFFF){              // 曲の終了なら
                    state = gkfEnd;             // 抜ける
                    break;
                }
                cntOnpu = Onp & 0x00FF;         // 音符の長さ取得
                cntOnpu *= tempo * 4;           // 音符の長さ取得
                Onp >>= 8;                      // 音高を取り出す
                if(Onp){                        // 音符なら
                    incWave = dW_tbl[Onp - 23]; //  音高を繰返時間に変換
                    musicOn();                  //  出力をON
                    volume = 0xFF;              //  音量最大
                }else{                          // 休符なら
                    incWave = 0;                //  繰返し なし
                    musicOff();                 //  出力OFF
                    volume = 0;                 //  音量OFF
                }
                NCO1INCH = incWave >> 8;        // NCO1INCに結果セット
                NCO1INCL = incWave;
                playLen = cntOnpu;              // 演奏時間をセット
                playEnd = 0;                    // 演奏終了フラグをリセット
                state = onpuEnd;
                break;
            case onpuEnd:             // ====== 音符の演奏終了 ======
                if(reduceFlg){                  // 50mS毎に
                    reduceFlg = 0;              // 音量を -6% する
                    volume = (int)volume * 15/16;
                }
                if(playEnd){                    // 音符演奏終了を待つ
                    playCnt = 0;                // 演奏経過時間をリセット
                    state = getOnpu;            // 次の音符取得へ
                }
                if(SWdown){                     // SWが押されたなら
                    SWdown=0;                   //  SW 処理済み明示
                    state = gkfEnd;             //  終了処理へ
                }
                break;
            case gkfEnd:              // ====== 楽譜の演奏終了 ======
                musicOff();                     // 音量OFF
                pOnp--;                         // 曲終了まで進め
                while(p1gkf[pOnp++] != 0xFFFF);
                state = waitStart;              // 演奏開始待ちに
                break;
        }
    }   //  while
}

config.c

/********************************
 * config.c
 * コンフィグおよび各モジュールの設定
 *
 * ******************************
 */

#include <xc.h>
#include "config.h"

// Configuration bits *****************
#pragma config FEXTOSC = OFF,RSTOSC = HFINT1  // HFINTOSC (1MHz)
#pragma config CLKOUTEN = OFF,CSWEN = ON,FCMEN = OFF
#pragma config MCLRE = OFF,PWRTE = OFF,WDTE = OFF,LPBOREN = OFF
#pragma config BOREN = OFF,BORV = LOW,PPS1WAY = OFF,STVREN = ON
#pragma config DEBUG = OFF
#pragma config WRT = OFF,LVP = OFF,CP = OFF,CPD = OFF

void int_pins(){
    ANSELA = 0;                 // 全てデジタルIO
    ANSELC = 0;                 // 全てデジタルIO
    WPUA = 0b111111;            // 入力は全て弱プルアップ
    WPUC = 0b111111;            // 入力は全て弱プルアップ
    TRISA = 0b101111;           // RA4 Sp出力
    TRISC = 0b111110;           // RC0 LED出力
    LATA = 0;
    LATC = 0;                   // LED 消灯
}

void int_modules(){
    // CCP Timer 連携設定 ***********************************
    CCPTMRS = 0b11101001;       // CCP4-CCP3-CCP2-CCP1
                                // TMR5-TMR3-TMR3-TMR2
    // CCP1 PWM Timer2 設定 (音量制御) ***********************
    //   8MHz/4/64 = 31KHz
    // *****************************************************
    T2CON  = 0b00000100;        // Timer 2 PS1/1設定
    PR2    = 0x3F;              // Timer2 Period Register設定
    CCPR1H = 0;                 // PR2が0x3F duty右詰のため
    CCPR1L = 0;                 // CCPR1Lの8bitだけが有効
    CCP1CON = 0b10001111;       // PWN duty右詰め

    // Timer3 の設定 (音符OnOff制御) **************************
    // LFintOsc 31KHz PS:/8 = 3.9KHz (0.258mS毎)
    // *****************************************************
    T3CON = 0b11110001;          // LFintOsc PreS 1/8
    T3GCON = 0;

    // CCP2 Timer3連携 Compare設定 (無音時間) *****************
    //   無音時間 30mS = 116 (0x74)  -50mS = -194 (0xFF3E)
    // *****************************************************
    CCPR2H = 0;                 //
    CCPR2L = 116;               //
    CCP2CON = 0b10001010;       // Compare
    CCP2IF = 0;

    // CCP3 Timer3連携 Compare設定 (演奏時間) *****************
    //   音符継続時間 500mS = 1938 (0x0792)
    // *****************************************************
    CCPR3H = 0x07;              //
    CCPR3L = 0x92;              //
    CCP3CON = 0b10001010;       // Compare
    CCP3IF = 0;

    // CCP4 Timer5連携 Compare設定 (音量減衰時間) **************
    //   Timer5 : LFintOsc 31KHz PS:/8 = 3.9KHz (0.258mS)
    //   音符音量減衰時間 50mS = 194
    // *****************************************************
    T5CON = 0b11110001;         // LFintOsc PreS 1/8
    T5GCON = 0;
    CCPR4H = 0;                 //
    CCPR4L = 194;               //
    CCP4CON = 0b10001011;       // Compare with reset TMR5
    CCP4IF = 0;

    // Timer4 の設定 1mS毎の時間設定 **************************
    // 8MHz /4 PS:/16 PR4;/125 = 1KHz (1mS毎)
    // *****************************************************
    T4CON = 0b00000110;          // Timer ON PreS 1/16
    PR4      = 124;              // 繰返し周波数 1KHz
    TMR4IF = 0;                  // Timer4フラグクリア

    // Timer6 の設定 NCO用のクロック **************************
    // 8MHz /4 PS:/1 PR6;/8 = 250KHz
    // *****************************************************
    T6CON = 0b00000100;         // Timer ON PreS 1/1
    PR6   = 7;                  // 繰返し周波数 250KHz

    // CLC1 初期化 **************************************
    // AND-ORを使用 入力は、T6_match、そのまま出力、
    // NCOで利用する
    // *************************************************
    CLC1POL  = 0x02;    // Gate2出力反転 他はそのまま
    CLC1SEL0 = 0x23;    // 信号源1 35 = TMR6/PR6一致
    CLC1SEL1 = 0x00;    // 信号源2 CLCIN0PPS 利用なし
    CLC1SEL2 = 0x00;    // 信号源3 CLCIN0PPS 利用なし
    CLC1SEL3 = 0x00;    // 信号源4 CLCIN0PPS 利用なし
    CLC1GLS0 = 0x02;    // Gate1 Data1T ON 他はOFF
    CLC1GLS1 = 0x00;    // Gate2 全入力OFF
    CLC1GLS2 = 0x00;    // Gate3 全入力OFF
    CLC1GLS3 = 0x00;    // Gate4 全入力OFF
    CLC1CON  = 0x80;    // LC1 ON 割込OFF MODE AND-OR
    // CLC3 初期化 **************************************
    // SR latchを使用 S入力は、CCP2、R入力は、CCP3
    // CLC4で利用する
    // *************************************************
    CLC3POL = 0x00;         // 反転は無し
    CLC3SEL0 = 0x0D;        // 信号源1 CCP2 output;
    CLC3SEL1 = 0x0E;        // 信号源2 CCP3 output;
    CLC3SEL2 = 0x00;        // 信号源3
    CLC3SEL3 = 0x00;        // 信号源4
    CLC3GLS0 = 0x02;        // Gate1 Data1T ON 他はOFF
    CLC3GLS1 = 0x00;        // Gate2
    CLC3GLS2 = 0x08;        // Gate3 Data2T ON 他はOFF
    CLC3GLS3 = 0x00;        // Gate4
    CLC3CON = 0x83;         // LC3EN SR latch;
    RC0PPS = 6;             // RC0にCLC3出力

    // CLC4 初期化 **************************************
    // AND-ORを使用 入力は、DSM と CLC3
    // 出力はオルゴール出力
    // *************************************************
    CLC4POL = 0x00;         // 反転は無し
    CLC4SEL0 = 0x0A;        // LC4D1S DSM_OUT;
    CLC4SEL1 = 0x06;        // LC4D2S LC3_out;
    CLC4SEL2 = 0x00;        //
    CLC4SEL3 = 0x00;        //
    CLC4GLS0 = 0x02;        // LC4G1D1T enabled
    CLC4GLS1 = 0x08;        // LC4G2D2T enabled
    CLC4GLS2 = 0x00;        //
    CLC4GLS3 = 0x00;        //
    CLC4CON = 0x80;         // LC4EN  AND-OR;
    RA4PPS  = 7;            // RA4にCLC4出力

    // NCO 設定 ************************************************
    NCO1CLK = 0b00000010;       // CLC1-outがクロック
    NCO1CON = 0b10000000;       // 正論理、反転モード
    NCO1INC = 0;

    // DSM 設定 *********************************************
    //   PWM5搬送波信号とNCO情報信号を混合
    // *****************************************************
    MDCON = 0x80;               // DSMは『On/Off』モード
    MDSRC = 0x0B;               // 情報信号 正論理非同期 NOC1
    MDCARH = 0x04;              // 搬送波H 正論理非同期 CCP1
    MDCARL = 0;                 // 搬送波L VSS
}