/*---------------------------------------------------------------------------- * File: JoyStick020.c * Copyright : VividHobby * ジョイスティック型赤外線リモコン送信機 * 4ch アナログ入力、2ch プッシュスイッチ入力 * 2400bps 赤外線送信(38KHz変調方式) * * A/D 入力は、AN0, AN1, AN2, AN3  SW入力は、RB0, RB1 * PWM出力は、RC2:CCP1 RS232送信は、RC6:TX * * 基本プログラム製作 2016/06/14 v020 * 基本プログラムバグ取り完了(注意:モニタLEDの点滅は実際とは逆 v021 * * * * * * * * * * * * * Created on 2016/06/01, 17:31 */ //------------------------------------------------------------------------- // プログラム初期設定 //------------------------------------------------------------------------- #include #include #include #pragma config FOSC = HS //高速オシレーター(4MHz以上)使用 #pragma config WDTE = OFF //ウォッチドッグタイマーOFF #pragma config CP = OFF //コードプロテクションOFF #pragma config PWRTE = ON //パワーアップタイマーON #pragma config LVP = OFF//PIC16F84Aでは未サポート //PICのクロック設定 #define _XTAL_FREQ 20000000 //PICのクロックをHzで設定(20MHz) //------------------------------------- // SWスイッチ用ポートの設定 //------------------------------------- #define sw0 RB0 // RB0にsw0を設定(通常はHにプルアップ、Lでアクティブ) #define sw1 RB1 // RB1にsw1を設定(同上) //------------------------------------------------------------------------ // 変数初期設定 //------------------------------------------------------------------------ int ad_val = 0; //AD変換結果格納 int ad_x1 = 0, ad_y1 = 0; //AD変換後、補正後の最終x,y格納場所 (0-1023) int ad_x2 = 0, ad_y2 = 0; int ad_h = 0, ad_l = 0; //AD変換結果レジスタの上位下位の格納 int x1_max = 0, x1_cent = 0, x1_min = 1023, y1_max = 0, y1_cent = 0, y1_min = 1023; //JoyStickの位置の最大最小中央値初期化 int x2_max = 0, x2_cent = 0, x2_min = 1023, y2_max = 0, y2_cent = 0, y2_min = 1023; //JoyStickの位置の最大最小中央値初期化 int i, j, temp = 0; // Loop counter float x1_h = 0, x1_l = 0, y1_h = 0, y1_l = 0; //AD変換正規化の補正値(X,Yそれぞれ上位と下位) float x2_h = 0, x2_l = 0, y2_h = 0, y2_l = 0; //AD変換正規化の補正値(X,Yそれぞれ上位と下位) float calc = 0, calc2 = 0; unsigned char ad_ch = 0; //AD変換ch指定(0-3) unsigned char ad_para_temp = 0b10000001; //AD変換パラメータのテンプレート unsigned char rcv_1B, send_1B; //RS232送受信データ用 unsigned char sw_port; // プッシュSWを読み込む //------------------------------------------------------------------------ // 関数定義 //------------------------------------------------------------------------ void pic_init_16f876(void); void ad_conv(void); void ad_limit(void); void ad_cent(void); void correction(void); void ad_actual(void); void RS232_ini(void); void RS232_write(unsigned char send_1B); unsigned char RS232_read(void); void PWM_setup(void); void PWM_start(void); void PWM_stop(void); //------------------------------------------------------------------------- // メインプログラム //------------------------------------------------------------------------- main() { pic_init_16f876(); // PIC初期化。 ADは4ch分使用設定(PCFG=0000) // SW設定、RS232、PMW出力設定 //-------------------------------------------------- // PIC起動確認LED点灯 //-------------------------------------------------- __delay_ms(2000); // 2秒待つ RC2 = 0; // RC2:PWM出力(予定の)ポートをH RC6 = 0; // RC6:RS232 TX(予定の)ポートをL で、赤色LED点灯 //---------------------------------------------------------------- // 変数初期値設定 //--------------------------------------------------------------- //================================================================ // メイン処理開始 //================================================================ //----------------- // AD変換準備 //----------------- ad_cent(); // ジョイスティックの中央値セット __delay_ms(1000); for (i = 1; i <= 500; i++) { ad_limit(); // ジョイスティックのX,Yそれぞれの最大最小値を得る __delay_ms(5); } correction(); // AD変換補正値を計算し、セット //-------------------------------------------------- // ジョイスティック中央値読み込み完了確認LED 消灯 //-------------------------------------------------- RC2 = 1; // RC2:PWM出力(予定の)ポートをL RC6 = 0; // RC6:RS232 TX(予定の)ポートをL で、赤色LED 消灯 __delay_ms(2000); // 2秒間LED消灯 //-------------------------------------------------- // PWM 初期設定 波長27マイクロ秒(38KHz) //-------------------------------------------------- PWM_setup(); //PWM初期設定 //--------------------- // PWM起動開始 //--------------------- PWM_start(); // swがONでPWM開始 //-------------------------------------------------- // RS232 初期設定 2400bps //-------------------------------------------------- RS232_ini(); //RS232初期化 while (1) { __delay_ms(3000); // 動作確認用、ゆっくりデータ送信 ad_actual(); // AD変換を実施し、値を補正。結果は、ad_x と ad_yにセット(10ビット) //--------------------------------------- // AD変換した結果2バイトx2をRS232で送信 // 送信ERR防止の為、各データを送信 //--------------------------------------- // デバッグ用にAD値、スイッチ状態を3行にわけて送信 send_1B = ad_x1 >> 8; //xの上位バイト RS232_write(send_1B); send_1B = ad_x1; //xの下位バイト RS232_write(send_1B); send_1B = ad_y1 >> 8; //yの上位バイト RS232_write(send_1B); send_1B = ad_y1; //yの下位バイト RS232_write(send_1B); RS232_write(0x0d); // 改行 send_1B = ad_x2 >> 8; //xの上位バイト RS232_write(send_1B); send_1B = ad_x2; //xの下位バイト RS232_write(send_1B); send_1B = ad_y2 >> 8; //yの上位バイト RS232_write(send_1B); send_1B = ad_y2; //yの下位バイト RS232_write(send_1B); RS232_write(0x0d); // 改行 send_1B = sw_port; // スイッチポートのデータを送信 RS232_write(send_1B); RS232_write(0x0d); // 改行 } } //=============================== Main prog 終了 ============================== //================== 以下 関数定義 ============================================ //------------------------------------------------------------------------- // PIC 16F876 初期設定(ポート、AD、割り込み) //------------------------------------------------------------------------- //-------------------------------------------------------------------------- // PWM 初期設定(20MHzクロック) // 赤外線リモコンの変調パルスに設定を合わせる // 変数設定 周波数=38KHz、(周期=26.38usec) duty = 約30% // 周期設定 PR2=33、TMR1プリスケーラ=1/4、 // duty 30%として、CCPR1L = 0100 1110、CCP1X=0,CCP1Y=0 // RC2 = CCP1 PWM出力ポート //-------------------------------------------------------------------------- void PWM_setup(void) { PORTCbits.RC2 = 0; // PWMの出力を設定(念のためポートRC2をLにする) PR2 = 33; // PWMの周期をセット CCPR1L = 0b00000000; // CCPR1L + CCP1X + CCP1Y = 0 デューティー // PWMのパルス幅Dutyをセット、ただし初期値はゼロ CCP1CONbits.CCP1X = 0; // パルス幅設定10ビットの下位2ビットはゼロ CCP1CONbits.CCP1Y = 0; CCP1CONbits.CCP1M3 = 1; // PWMモード(M3-M0) CCP1CONbits.CCP1M2 = 1; // CCP1CONbits.CCP1M1 = 0; // CCP1CONbits.CCP1M0 = 0; // T2CONbits.TOUTPS3 = 0; // ポストスケーラはゼロ T2CONbits.TOUTPS2 = 0; T2CONbits.TOUTPS1 = 0; T2CONbits.TOUTPS0 = 0; T2CONbits.TMR2ON = 1; // タイマー2(PWM)スタート( 0=STOP, 1=START) T2CONbits.T2CKPS1 = 0; // プリスケーラ(01=1/4) T2CONbits.T2CKPS0 = 1; // 00=1/1, 01=1/4, 10=1/16, 11=1/16 } //------------------------------------------------------------------- // PWM 動作開始 タイマー2(PWM)は常時RUN //------------------------------------------------------------------- void PWM_start(void) { PR2 = 33; CCPR1L = 0b00001011; // CCPR1L = 0b01101110; // CCPR1L + CCP1X + CCP1Y = 44 CCP1X = 0; CCP1Y = 0; } //------------------------------------------------------------------- // PWM 動作停止 タイマー2(PWM)は常時RUNするが、Dutyをゼロにする  //------------------------------------------------------------------- void PWM_stop(void) { CCPR1L = 0; // Dutu比ゼロにして出力をLにする CCP1X = 0; CCP1Y = 0; } void pic_init_16f876(void) { //--------------------------------------------- // PICのPORT設定の初期化 AD変換RA0ポートに設定 //--------------------------------------------- //PICのポート設定 ADCON0 = 0b10000001; //アナログ使用AD on CHS0 = 0; // 以下の3ビットでADの使用chを設定(0-4ch) CHS1 = 0; CHS2 = 0; //ADはAN0, AN1, AN3 結果はADRESHに左寄せ8ビット ADCON1 = 0b10000000; //For Analog Port (PCFG=0000) //結果は右詰め(10ビット使用) TRISA = 0b00001111; // A/D変換はRA0-RA3で行うので入力に設定 TRISB = 0b11111111; // PortBを全て入力に設定。実際の使用は、RB0, RB1。他はプルアップ TRISC = 0b10000000; // RX=RC7をINに設定, RC6はTX送信。RC2= PWM出力 PORTA = 0b11111111; // ポートの初期値を設定する(Hで通常、LでON) PORTB = 0b11111111; // 初期化 PORTC = 0b10111111; // 初期化 //---------------------------------------------------------------- // ==== TMR は未使用 ==== // TMR0設定 // TRM0 setting 割り込み使用、プリスケーラ1/32設定@20MHz // 割り込みは約1.6384 msec毎に発生する //---------------------------------------------------------------- OPTION_REG = 0b10000100; /// Prescaller 1/32 (1.6384 msec full count) INTCON = 0b00100000; GIE = 0; // 全体割り込み禁止 T0IE = 0; // TMR0 中断 T0IF = 0; // TMR0 割り込みフラッグクリア } //------------------------------------------------------------------- // ad_chに変換するADのchをセットしAD変換を実行。結果をad_valに代入する // AD変換結果は0-1023 (補正はなし。純粋にADを実行) //------------------------------------------------------------------- void ad_conv(void) { ad_val = 0; for (j = 1; j <= 10; j++) { //AD変換50回を平均する ADCON0 = ad_para_temp | (ad_ch << 3); //ADのchを指定 __delay_us(20); // 20usecの準備待ち GO = 1; // AD変換開始 while (GO) { // GOビットが1の間待つ(0になるまで待つ) } temp = ADRESH; ad_h = temp << 8; //高位値は256倍する ad_l = ADRESL; ad_val = ad_val + ad_h + ad_l; //低位値と加算してAD結果10ビットを得る __delay_ms(1); } ad_val = ad_val / 10; } //..................................................................... // AD変換の考え方 // VRのAD変換値は、かならずしも0-1023(10bit)にならないので、X/Y値それぞれの // 最小値、最大値を読み取り、中間値とその差をフルスケール512で除して、係数とする //..................................................................... //--------------------------------------------------------------------- // AD変換後のX,Y値の最大最小を求め、正規化を行う //--------------------------------------------------------------------- void ad_limit(void) { ad_ch = 0; // AD ch0 ad_conv(); // Get AD value if (ad_val >= x1_max) x1_max = ad_val; if (ad_val <= x1_min) x1_min = ad_val; ad_ch = 1; // AD ch1 ad_conv(); // Get AD value if (ad_val >= y1_max) y1_max = ad_val; if (ad_val <= y1_min) y1_min = ad_val; ad_ch = 2; // AD ch2 ad_conv(); // Get AD value if (ad_val >= x2_max) x2_max = ad_val; if (ad_val <= x2_min) x2_min = ad_val; ad_ch = 3; // AD ch3 ad_conv(); // Get AD value if (ad_val >= y2_max) y2_max = ad_val; if (ad_val <= y2_min) y2_min = ad_val; } //--- 注意 -------------------------------------------------------------- // 読み取ったX,Yの最大最小中央値を元に // X,Yの補正係数を計算する x_h等とcalcはfloat型変数 //----------------------------------------------------------------------- void correction(void) { calc = x1_max - x1_cent; x1_h = 512 / calc; calc = x1_cent - x1_min; x1_l = 512 / calc; calc = y1_max - y1_cent; y1_h = 512 / calc; calc = y1_cent - y1_min; y1_l = 512 / calc; calc = x2_max - x2_cent; x2_h = 512 / calc; calc = x2_cent - x2_min; x2_l = 512 / calc; calc = y2_max - y2_cent; y2_h = 512 / calc; calc = y2_cent - y2_min; y2_l = 512 / calc; } //------------------------------------------------------------------------ // 補正値を元にして // AD変換を事項して、結果を補正する 最終結果 ad_x1, ad_y1, ad_x2, ad_y2 // 加えて、SW1、SW2接続のPortBをsw_portに読み込む //------------------------------------------------------------------------ void ad_actual(void) { ad_ch = 0; // AD ch0 ad_conv(); calc = ad_val - x1_cent; calc2 = ad_val - x1_min; if (ad_val >= x1_cent) { ad_x1 = x1_cent + (calc * x1_h); } else { ad_x1 = (calc2 * x1_l); } if (ad_x1 <= 0) ad_x1 = 0; if (ad_x1 >= 1023) ad_x1 = 1023; //-------------------------------------------- ad_ch = 1; // AD ch1 ad_conv(); // Get AD value calc = ad_val - y1_cent; calc2 = ad_val - y1_min; if (ad_val >= y1_cent) { ad_y1 = y1_cent + (calc * y1_h); } else { ad_y1 = (calc2 * y1_l); } if (ad_y1 <= 0) ad_y1 = 0; if (ad_y1 >= 1023) ad_y1 = 1023; //--------------------------------------------- ad_ch = 2; // AD ch0 ad_conv(); calc = ad_val - x2_cent; calc2 = ad_val - x2_min; if (ad_val >= x2_cent) { ad_x2 = x2_cent + (calc * x2_h); } else { ad_x2 = (calc2 * x2_l); } if (ad_x2 <= 0) ad_x2 = 0; if (ad_x2 >= 1023) ad_x2 = 1023; //--------------------------------------------------- ad_ch = 3; // AD ch1 ad_conv(); // Get AD value calc = ad_val - y2_cent; calc2 = ad_val - y2_min; if (ad_val >= y2_cent) { ad_y2 = y2_cent + (calc * y2_h); } else { ad_y2 = (calc2 * y2_l); } if (ad_y2 <= 0) ad_y2 = 0; if (ad_y2 >= 1023) ad_y2 = 1023; } //--------------------------------------------------------------------- // JoyStickの中立X,Y値のを求め、補正を行う // 指を離した状態のJSの位置を10回読み込んで平均する //--------------------------------------------------------------------- void ad_cent(void) { x1_cent = 0; y1_cent = 0; ad_ch = 0; // AD ch0 for (i = 1; i <= 10; i++) { ad_conv(); // Get AD value x1_cent = x1_cent + ad_val; } x1_cent = x1_cent / 10; ad_ch = 1; // AD ch1 for (i = 1; i <= 10; i++) { ad_conv(); // Get AD value y1_cent = y1_cent + ad_val; } y1_cent = y1_cent / 10; x2_cent = 0; y2_cent = 0; ad_ch = 2; // AD ch0 for (i = 1; i <= 10; i++) { ad_conv(); // Get AD value x2_cent = x2_cent + ad_val; } x2_cent = x2_cent / 10; ad_ch = 3; // AD ch1 for (i = 1; i <= 10; i++) { ad_conv(); // Get AD value y2_cent = y2_cent + ad_val; } y2_cent = y2_cent / 10; } //------------------------------------------------- //RS232 initialize IR通信の為低速モード2400bpsに設定 //------------------------------------------------- void RS232_ini(void) { //送信レジスタ設定 TRISC7 = 1; //set input for RX TRISC6 = 0; //set output for TX // TXSTA = 0b00100100; //非同期、8ビット、高速送信モード(bit2=1 ) TXSTA = 0b00100000; //非同期、8ビット、低速モード) SPBRG = 129; //2400 bpsモード(20MHz低速モード) //SPBRG = 129; //9600 bpsモード(20MHz高速モード) //SPBRG = 20; // 57600 bpsモード(20MHz高速モード) //SPBRG = 64; // 9600 bps モード(10MHz)高速モード) // SPBRG = 10; // 57600 bpsモード(10MHz高速モード) //受信レジスタ設定 RCSTA = 0b10010000; //シリアル通信、8ビット、連続受信モード PIR1bits.RCIF = 0; //RCIFクリア } //--------------------------------------------------- //RS232送信(1バイト) //--------------------------------------------------- void RS232_write(unsigned char send_1B) { while (TXSTAbits.TRMT == 0) { } //TRMTが1になるまで待つ TXREG = send_1B; //データを送信レジスタにセット } //---------------------------------------------------- //RS232受信(1バイト) //受信ERRORに関しては未対応 //---------------------------------------------------- unsigned char RS232_read(void) { PIR1bits.RCIF = 0; while (PIR1bits.RCIF == 0) { } // 下記のERROR処理は割愛しています // if (RCSTAbits.FERR == 1 ){ rcv_1B = 0xFF ; // RCSTAbits.CREN = 0; // RCSTAbits.CREN = 1; // return rcv_1B; // } // if (RCSTAbits.OERR == 1 ){ rcv_1B = 0xFF ; // RCSTAbits.CREN = 0; // RCSTAbits.CREN = 1; // } // return rcv_1B; rcv_1B = RCREG; return rcv_1B; } //--------------------------- End of Program ----------------------------------}