/************************************************************************************ Side Tone Osc By PWM v1.1 2015/9 By JA3OOK T.nakamura PWMモードを使用     ・タイマー割込み毎にCCPR値(パルス幅設定レジスタ値)を周期的に変化させ、      パルス幅を連続的に変更し、MCUの外につけたRCフィルターで波形を整えている。   参考 1 オーディオ周波数はMCUのクロック周波数に依存する。 2 LCDは無くても使用には差し支えない。あれば開発やプログラム変更に便利。 ------------------------------------------------------------------------------------- 開発環境 MPLAB X IDL ver 3.05 MPLAB XC8 C Compiler Free mode v1.34 CPU: PIC16F1936 1:MCLR:1 2:RA0:0:LCD ctrl 3:RA1:0:LCD ctrl 4:RA2:0:LCD ctrl 5:RA3:0:LCD ctrl 6:RA4:0:LCD ctrl 7:RA5:0:LCD ctrl 8:VSS 9: RA7: 10:RA6: 11:RC0: 12:RC1: 13:RC2/CCP1:PWM出力 14:RC3: 15:RC4: 16:RC5: 17:RC6: 18:RC7: 19:VSS 20:VDD 21:RB0/AN12:1:出力周波数変更指示受け用電圧測定(可変抵抗器) 22:RB1:0:動作開始確認および出力中表示用LED 23:RB2:1:CW-key入力 24:RB3:0: 25:RB4:0: 26:RB5:0: 27:RB6:0: 28:RB7:0: *************************************************************************************/ // refer \c:Program Files\Microchip\xc8\v1.34\docs\chips\16f1936.html #pragma config CLKOUTEN =OFF//CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin #pragma config WDTE =OFF //WDT disabled #pragma config PWRTE =OFF //Power-up Timer disable #pragma config CP =OFF //Code protection off #pragma config BOREN =OFF //BOR disabled #pragma config FCMEN =OFF //Fail-Safe Clock Monitor disabled #pragma config MCLRE =OFF //MCLR/VPP pin function is digital input #pragma config CPD = OFF //Code protection off #pragma config IESO =OFF //Internal External Switchover mode disabled #pragma config FOSC =INTOSC //INTRC oscillator; port I/O function on both RA6/OSC2/CLKO pin and RA7/OSC1/CLKI pin #pragma config PLLEN =OFF //4x PLL disabled #pragma config STVREN =OFF //OFF Stack Overflow or Underflow will not cause a Reset #pragma config LVP =OFF //High-voltage on MCLR/VPP must be used for programming #pragma config WRT =OFF //Write protection off #include //#include //#include #include #include "lcd.h" #define MHz 000000 #define _XTAL_FREQ 16MHz //PIC_OSC_FREQ #define pwm_port CCP1 //PWM out #define led_port LATB1 //LED out #define cw_key RB2 //CW-KEY input internal weak pull-up signed int pr2_cur; // PR2に設定すべき値 signed int t2countH; // タイマー2用カウント値 Hレジスター用 signed int t2countL; // タイマー2用カウント値 Lレジスター用 unsigned int tm2_count; // タイマーの割込み発生回数をカウントする変数 #define pulse_max 36 // 1サイクル当たりのパルス数 #define wave_style_bunbo 100 //wave_styleの分母の値 unsigned char wave_style_sin[pulse_max] = {50,59,67,75,82,88,93,97,99,100,99,97,93,88,82,75,67,59,50,41,33,25,18,12,7,3,1,0,1,3,7,12,18,25,33,41}; //上記各値はExcelで計算→csvで出力→エディタ→コピーペースト signed int wave_style_ccpr[pulse_max]; //最新のタイマー値でのccpr値を格納 unsigned int ad_val; unsigned int timer; //時間稼ぎ用(符号無し16ビット長) unsigned char i; //汎用char unsigned int i_uint; //汎用int unsigned int j_uint; //汎用int unsigned char hexch[] ="0123456789ABCDEF";//16進数表示変換テーブル unsigned char str[10]; //汎用 主に数値の十進数表示変換に使用 void //intやchar正数をASCII文字に変換し*bufferに格納されるので文字として送信したり液晶に表示できる Bin2str(unsigned int data, char *buffer, unsigned int digit) { // 引数:intやchar正数データ, 文字データ, 期待する文字データの文字数 char i; buffer += digit; // 最後の数字位置 += : Buffer=Buffer+digit *buffer = 0; // 文字列終端(文字数+1の位置)へ0X00 for(i=digit; i>0; i--) { // 変換は下位から上位へ buffer--; // ポインター1 *buffer = (data % 10) + 0x30;// ASCIIへ  % : 割り算の余り data = data / 10; // 次の桁へ } } void //int正数を16進のASCII文字に変換しstrに格納されるので文字として送信したり液晶に表示できる DispHex(unsigned int j){ char i; i = j >> 12; str[0] = hexch[i]; //上位4bit表示 max:03FF i = (j >> 8) & 0x000F; str[1] = hexch[i]; //下位4bit表示 max:03FF i = (j >> 4) & 0x000F; str[2] = hexch[i]; //上位4bit表示 max:03FF i = j & 0x000F; str[3] = hexch[i]; //下位4bit表示 max:03FF str[4] = 0; //set NULL } void interrupt InterTimer( void ){ // タイマー割込み //GIE = 0 ; //全割込みをマスクする ← これはPICのHWが自動的に行う if (TMR2IF == 1) { // タイマー割込み発生か? TMR2IF = 0 ; // タイマー割込フラグをリセット tm2_count ++; if(tm2_count >= pulse_max) { tm2_count = 0; } CCPR1H = (wave_style_ccpr[tm2_count] >> 8); // パルス幅の設定 CCPR1L = (wave_style_ccpr[tm2_count] & 0x00FF) ; // パルス幅の設定 } } void Analize_voltage(void){ Lcd_goto(0x0D); //debug Bin2str(wave_style_bunbo, str,3);//debug Lcd_puts(str); //debug while(1){ //無限loop if(cw_key == 0){ //CW電鍵押下(ON) TMR2IE = 1 ; // タイマー割込をマスクしない led_port=1; } else { TMR2IE = 0 ; // タイマー割込をマスク led_port=0; } Lcd_goto(0x06); //debug Bin2str(pr2_cur, str,5); //debug Lcd_puts(str); //debug Lcd_goto(0x40); //debug Bin2str(wave_style_ccpr[9], str,5);//debug Lcd_puts(str); //debug Lcd_goto(0x46); //debug Bin2str(wave_style_ccpr[27], str,5);//debug Lcd_puts(str); //debug //電圧測定 ADON = 1; //AD変換を開始せよ __delay_ms(5); //ソースプログラム v1.1 追加 //アクイジションAcquisition時間(取得=充電時間) See Data Sheet 165P GO_nDONE = 1; //AD変換開始 while(GO_nDONE); //変換完了待ち ad_val = (ADRESH << 8) | ADRESL; // 0 =< ad_val =<1023 //測定電圧生データの表示 //debug Lcd_goto(0x00); //debug Bin2str(ad_val, str, 4);//debug Lcd_puts(str); //debug //電圧測定終了 //PWM mode用タイマー割込み間隔を pr2_cur にセットし、同じ値をPR2にもセット。 // pr2_cur = 255 - 0.24 * ad_val (つまりセットする値の範囲は 255から9) // Fosc16MHzでの実験結果  出力周波数は 434Hz(255) から 約2000Hz(45)、 // それ以上は割込みが多く電圧を計れない(周波数を変えられない)。 //    MPUにより違いが出るだろう。 //i_uint = ad_val * 24; //i_uint = i_uint / 100; //pr2_cur = 255-i_uint; // pr2_cur = 255 - 0.01 * ad_val (つまりセットする値の範囲は 255から102) // Fosc16MHzでの実験結果  出力周波数は 434Hz(255) から 約1070Hz(102) //    MPUにより違いが出るだろう。 i_uint = ad_val * 15; i_uint = i_uint / 100; pr2_cur = 255-i_uint; PR2 = pr2_cur; //wave_style_ccprを計算 //cur_ccpr = wave_style[] * PR2 / wave_style_bunbo for(i=0; i