PIC16F18346の内部モジュールの働きを確認する意味で、NCO,DSM,CLC,CCPを多様してオルゴールを作成します。
PICのペリフェラルは以下の用途に使用しています。
ラ音の八分音符を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 }