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
}