/***************************************************************************************** * metronome_v2a * metronome_v2プロジェクトの、バージョンアップ版 * <改善点> * (1) BEAT 1, 2, 3, 4に、x2(1拍=8分2個)・x3(1拍=8分x3個)・x4(1拍=16分x4個)を追加 * * 液晶:AQM1602XA-RN-GBW * コントローラ:ST7032 * * MPU:PIC18F14K50 * <クロック・コンディション> * Fosc= 16MHz(外部水晶) * Fcy= 16MHz/4= 4MHz(Tcy= 0.25uS) * * 初期作成日:2019/6/19 N.Ishii * 更新:2019/7/27 N.Ishii /*****************************************************************************************/ #include #include "AQM1602XA_i2cLCD.h" //I2C接続LCD関数 //-------------- コンフィグレーション ------------------------ #pragma config FOSC = HS, PCLKEN = ON // 外部クロック #pragma config USBDIV = OFF, CPUDIV = NOCLKDIV #pragma config IESO = OFF, FCMEN = OFF, PLLEN = OFF #pragma config BORV = 30, BOREN = OFF, PWRTEN = OFF #pragma config WDTPS = 32768, WDTEN = OFF #pragma config HFOFST = OFF, MCLRE = ON, XINST = OFF #pragma config BBSIZ = OFF, LVP = OFF, STVREN = ON #pragma config CP1 = OFF, CP0 = OFF, CPD = OFF, CPB = OFF #pragma config WRT1 = OFF, WRT0 = OFF, WRTB = OFF, WRTC = OFF #pragma config EBTR1 = OFF, EBTR0 = OFF, EBTRB = OFF #define LED LATCbits.LATC2 /// Envelope Contorol Block Define #define CK1 LATCbits.LATC3 #define CK1_Lo LATCbits.LATC3 = 0 #define UP1_Hi LATCbits.LATC7 = 1 #define UP1_Lo LATCbits.LATC7 = 0 #define DWN1_Hi LATCbits.LATC6 = 1 #define DWN1_Lo LATCbits.LATC6 = 0 /// SW Define #define Start_Stop_SW PORTBbits.RB7 #define Beat_SW PORTBbits.RB5 /// Rotary ecoder #define RE_A PORTCbits.RC4 #define RE_B PORTCbits.RC5 //==================================================================== char msg1[]= "metronome_v2a"; char msg2[]= "BEAT :x "; char msg3[]= "TEMPO:xxx[bpm]"; char msg4[]= "BEAT :"; //==================================================================== unsigned int T2_count = 0; unsigned int beat_count = 0; //unsigned char BPM; unsigned int BPM; unsigned int Beat_Period; unsigned int T2_count_MAX = 2000; char Beat_No; char BeatSwCount; char start_flag = 0; char sw1_m0 = 0; // Start/Stop SW char sw1_m1 = 0; char sw1_m2 = 0; char sw2_m0 = 0; // Beat Sel SW char sw2_m1 = 0; char sw2_m2 = 0; char re_b_m0 = 0; // ROTARY ENCODER B-OUT:Clock char re_b_m1 = 0; char re_b_m2 = 0; //******************* プロトタイプ *********************************** void Start_Stop_SW_read(void); void Beat_No_Sel_SW_read(void); void Rotary_Encoder_read(void); void ltostring(char digit, unsigned long data, char *buffer); // ********** EEROM に設定する 初期値 *********** __EEPROM_DATA(2,60,0,0,0,0,0,0); // 0番地:BeatSwCount、1番地:テンポ:BPM /******************************************************************************* * T2割込み処理: 割り込み周期= 0.5mS ********************************************************************************/ void interrupt InterTimer( void ) { if (TMR2IF == 1) { // タイマー2の割込み発生か? ++T2_count; /// 頭の音だったら、発音=1kHz if (beat_count == 0) { if (T2_count <= 160) CK1 = !CK1; } else { /// 頭以外の音だったら、発音=500Hz if ((T2_count <= 160) && ((T2_count - 1) % 2) == 0) CK1 = !CK1; } /// エンベロープ:ADSR制御 switch (T2_count) { case 1: /// Attack区間:0.5mS x (21-1)= 10mS(最初のトリガ時のみ、case実行、その後はcase=20までbreak) LED = 1; // LED ON UP1_Hi; // 頭以外の音:UP1=Hi/DWN1=Lo if (beat_count == 0) DWN1_Hi; // 頭の音:UP1=Hi/DWN1=Hi→ アクセントになる。 break; case 21:/// Dacay区間:0.5mS x ((25-1)-(21-1))= 2mS UP1_Lo; if (beat_count == 0) DWN1_Lo; // 頭のタイミングで、DWN1_Loに戻す。 break; case 25:/// Sustain区間:0.5mS x ((125-1)-(25-1))= 50mS DWN1_Hi; break; case 125:/// Release:このタイミングで減衰 DWN1_Lo; LED = 0; // LED OFF break; default: break; } if (T2_count == T2_count_MAX) { // 161-xxx: Interval Block T2_count = 0; ++beat_count; if (beat_count == Beat_No) beat_count = 0; } TMR2IF= 0; // タイマー2割込フラグをリセット } } /********************************************* * メイン関数 **********************************************/ void main(void) { char i; OSCCON = 0b00000000; // 16MHz外部クロック(水晶) 190610 OSCCON2 = 0b00000100; // オシレータ駆動回路を、ON 190610 UCONbits.USBEN = 0 ; // USBは使用しない /// REピン(RB4,5)は、アナログ入力と併用なので /// ANSELxビットを'0'にリセットしデジタルピンとして使用するように設定 ANSELHbits.ANS10 = 0; // RB4 digital input ANSELHbits.ANS11 = 0; // RB5 digital input TRISA= 1; // RA is Input:OSC1,OSC2のみ使用 TRISB= 0b11110000; // RB4-7:START_SW, SDA, BEAT_SW, SCL is Input TRISC= 0b00110000; // RC7,6,3,2:UP1,DWN1,CK1,LED is Output/RC5,4:RE-B,RE-A is Input LED= 0; // LED 消灯 /// 内部プルアップ設定(RCポートには内部プルアップ無いので、ロータリーエンコーダは外部でプルアップ) INTCON2bits.RABPU= 0; // プルアップ許可 WPUB= 0b10100000; // RB7:START/STOP SW, RB5:BEAT SW is Pull-Up LCD_int(); // LCDを初期化 LCD_posyx(0,0); LCD_str(msg1); // LCD上段に"metronome_v2"表示 delay_ms(2000); LCD_clr(); // 全消去 LCD_cg_ram_user_set(); // ユーザーキャラクタ・セット LCD_cmd(DDRAM_START_ADDRESS_SET); //change CGRAM -> DDRAM /// EEPROMから設定値読込み BeatSwCount = eeprom_read(0); BPM = eeprom_read(1); /// 初期 Beat_Noセット if (BeatSwCount < 4) Beat_No = BeatSwCount+1; else Beat_No = BeatSwCount-2; ///// Timer2設定:T2周期= 0.5mS T2CON= 0b00000010; // 出力ポストスケール値:1:1・T2 OFF・プリスケーラ:PS= 1:16 PR2= 124; // 基準周期レジスタ値:124= N-1 // カウンタ入力クロック= (16MHz/4)/16= 250kHz= 4uS(= ワンカウントの時間) // これを、0.5mS(500uS)にするには、N= 500/4= 125回カウントアップさせればよい。 //// 割込み許可処理 TMR2= 0; // カウンタ(カウントアップレジスタ)初期化 TMR2IF= 0; // タイマー2割込フラグを0にする TMR2IE= 1; // タイマー2割り込みを許可する PEIE= 1; // 周辺装置割り込みを許可する GIE= 1; // 全割り込み処理を許可する //// MAIN LOOP while(1) { Start_Stop_SW_read(); Beat_No_Sel_SW_read(); Rotary_Encoder_read(); if (BeatSwCount < 4) Beat_Period = 60000/BPM; else { if (BeatSwCount == 4) Beat_Period = 60000/(BPM*2); if (BeatSwCount == 5) Beat_Period = 60000/(BPM*3); if (BeatSwCount == 6) Beat_Period = 60000/(BPM*4); } T2_count_MAX = Beat_Period * 2; /// BEAT表示 LCD_posyx(0,0); if (BeatSwCount < 4) { ltostring(1, BeatSwCount+1, msg2+6); LCD_str(msg2); } else { LCD_str(msg4); if (BeatSwCount == 4) for(i= 0; i < 2; ++i) LCD_dat(0x06); if (BeatSwCount == 5) for(i= 0; i < 3; ++i) LCD_dat(0x06); if (BeatSwCount == 6) for(i= 0; i < 4; ++i) LCD_dat(0x07); } /// TEMPO表示 LCD_posyx(1,0); ltostring(3, BPM, msg3+6); LCD_str(msg3); } } //======================================================================================= /***************************************************** * Start/Stop SW 読込み ******************************************************/ void Start_Stop_SW_read(void) { sw1_m0 = Start_Stop_SW; //New SW1 Data sw1_m2 = sw1_m1 ^ (sw1_m0 & sw1_m1); //Neg_Edge Sence SW1 sw1_m1 = sw1_m0; //Chenge New Data to Old Data m1 if (!start_flag) { if (sw1_m2) { // Start start_flag = 1; T2CONbits.TMR2ON = 1; // T2 ON } } else { if (sw1_m2) { // Stop start_flag = 0; T2_count = 0; beat_count = 0; CK1_Lo; UP1_Lo; DWN1_Lo; LED = 0; // LED OFF T2CONbits.TMR2ON = 0; // T2 OFF } } } /***************************************************** * Beat SW 読込み ******************************************************/ void Beat_No_Sel_SW_read(void) { sw2_m0 = Beat_SW; //New SW2 Data sw2_m2 = sw2_m1^(sw2_m0 & sw2_m1); //Neg_Edge Sence SW2 sw2_m1 = sw2_m0; //Chenge New Data to Old Data m1 if (sw2_m2) { ++BeatSwCount; if (BeatSwCount == 7) BeatSwCount = 0; if (BeatSwCount < 4) Beat_No = BeatSwCount+1; else Beat_No = BeatSwCount-2; eeprom_write(0, BeatSwCount); // EEPROM 0番地 書込み if (start_flag) { T2CONbits.TMR2ON = 0; // T2 OFF T2_count = 0; beat_count = 0; CK1_Lo; UP1_Lo; DWN1_Lo; T2CONbits.TMR2ON = 1; // T2 ON } } } /***************************************************** * Rotary_Encoder 読込み ******************************************************/ void Rotary_Encoder_read(void) { //Read ROTARY ENCODER B_Clock re_b_m0 = RE_B; re_b_m2 = re_b_m1^(re_b_m0 & re_b_m1); //Neg_Edge Sence CLOCK re_b_m1 = re_b_m0; //Chenge New Data to Old Data m1 if (re_b_m2 == 1) { //Neg_Edge Sence B_Clock ? if (RE_A == 0) { // A_Data = 0 ? ++BPM; if (BPM >= 201) BPM = 200; eeprom_write(1, BPM); // EEPROM 1番地 書込み } else { // B_Data = 0 --BPM; if (BPM <= 39) BPM = 40; eeprom_write(1, BPM); // EEPROM 1番地 書込み } if (start_flag) { T2CONbits.TMR2ON = 0; // T2 OFF T2_count = 0; beat_count = 0; CK1_Lo; UP1_Lo; DWN1_Lo; T2CONbits.TMR2ON = 1; // T2 ON } } } /******************************************************** * Numerical Value-> Ascci Convert *********************************************************/ void ltostring(char digit, unsigned long data, char *buffer) { char i; buffer += digit; // To Last of Strings for(i=digit; i>0; i--) { // From LS. Digit To MS. Digit buffer--; *buffer = (data % 10) + '0'; // This Digit Value-> Ascii Convert ('0'=0x30)-> Store Buffer data = data / 10; // Digit-1 } /// ブランキング処理 i = 0; while((i < digit-1)&&(*buffer == '0')) // 上位桁が0の間 { *buffer = ' '; // ブランクに変換 buffer++; i++; } }