/*---------------------------------------------------------------------------- * File: JoyStick001.c * Copyright : VividHobby * * JoyStic Remote Controller * Starnew versionAD読み込み表示完了 v001 * JoyStickの稼動範囲の補正 v002 * その2 v003 * その3 x_centの読み込みがERR v004 * x, yの中央値を個別に指定するとAD変換は動作するが、読み取りにすると *  動作しない。 * * 問題解決の試み1ad_val直読みではOK v005 * 原因判明 float変数の使い方が間違い v006 * AD結果を10ビットで表示 v007 * ADや中立値の決定は10回読み込んで平均 v008 * RS232送信機能を追加 v010 * IRの変調のためにPWMを設定する v011 * * * * * * * 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 RC0 // RC0にsw0を設定 #define sw1 RC1 // RC1にsw1を設定 //------------------------------------------------------------------------ // 変数初期設定 //------------------------------------------------------------------------ int ad_val = 0; //AD変換結果格納 int ad_x = 0, ad_y = 0; //AD変換後、補正後の最終x,y格納場所 (0-1023) int ad_h = 0, ad_l = 0; //AD変換結果レジスタの上位下位の格納 int x_max = 0, x_cent = 0, x_min = 1023, y_max = 0, y_cent = 0, y_min = 1023; //JoyStickの位置の最大最小中央値初期化 int i, j, temp = 0; // Loop counter float x_h = 0, x_l = 0, y_h = 0, y_l = 0; //AD変換正規化の補正値(X,Yそれぞれ上位と下位) float calc = 0, calc2 = 0; unsigned char ad_ch = 0; //AD変換ch指定(0-5) unsigned char ad_para_temp = 0b10000001; //AD変換パラメータのテンプレート unsigned char rcv_1B, send_1B; //RS232送受信データ用 //------------------------------------------------------------------------ // 関数定義 //------------------------------------------------------------------------ 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は2ch分設定(PCFG=0100) // RS232_ini(); //RS232初期化 PWM_setup(); //PWM初期設定 //---------------------------------------------------------------- // 変数初期値設定 //--------------------------------------------------------------- //---------------------------------------------------------------- // メイン処理開始 //---------------------------------------------------------------- //----------------- // AD変換準備 //----------------- __delay_ms(1000); // ad_cent(); // ジョイスティックの中央値セット // PORTB = 0; // ジョイスティック中央値読み込み完了の表示 // __delay_ms(1000); // for (i = 1; i <= 500; i++) { // ad_limit(); // ジョイスティックのX,Yそれぞれの最大最小値を得る // __delay_ms(5); // } // correction(); // AD変換補正値を計算し、セット // while (1) { // __delay_ms(2000); // ad_actual(); // AD変換を実施し、値を補正。結果は、ad_x と ad_yにセット(10ビット) //AD結果10ビットを表示のために上位8ビットに if (sw0 == 0) { PWM_start(); // swがONでPWM開始 // PORTC = ~(ad_x >> 6); // PORTB = ~ad_x; //表示が01反転の為 } else { PWM_stop(); // PWM停止 // PORTC = ~(ad_y >> 6); // PORTB = ~ad_y; } //--------------------------------------- // AD変換した結果2バイトx2をRS232で送信 // 送信ERR防止の為、各データを二回ずつ送信 //--------------------------------------- // send_1B = ad_x; //xの下位バイト // RS232_write(send_1B); // RS232_write(send_1B); // send_1B = ad_x >> 8; //xの上位バイト // RS232_write(send_1B); // RS232_write(send_1B); // send_1B = ad_y; //yの下位バイト // RS232_write(send_1B); // RS232_write(send_1B); // send_1B = ad_y >> 8; //yの上位バイト // RS232_write(send_1B); // 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 = 0b10000100; //For A/D0 & degital I/O(PCFG=0100) //結果は右詰め(10ビット使用) TRISA = 0b00000011; // A/D変換はRA0で行うのでRA0を入力に設定 TRISB = 0b00000000; TRISC = 0b10000011; // スイッチsw0とsw1を接続。RX=RC7をINに設定, RC2=out for PWM PORTA = 0b11111111; // ポートの初期値を設定する(Hで通常、LでON) PORTB = 0b11111111; PORTC = 0b11111011; // RC0=sw0, RC1=sw1 //---------------------------------------------------------------- // ==== 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 >= x_max) x_max = ad_val; if (ad_val <= x_min) x_min = ad_val; ad_ch = 1; // AD ch1 ad_conv(); // Get AD value if (ad_val >= y_max) y_max = ad_val; if (ad_val <= y_min) y_min = ad_val; } //--- 注意 -------------------------------------------------------------- // 読み取ったX,Yの最大最小中央値を元に // X,Yの補正係数を計算する x_h等とcalcはfloat型変数 //----------------------------------------------------------------------- void correction(void) { calc = x_max - x_cent; x_h = 512 / calc; calc = x_cent - x_min; x_l = 512 / calc; calc = y_max - y_cent; y_h = 512 / calc; calc = y_cent - y_min; y_l = 512 / calc; } //------------------------------------------------------------------------ // 補正値を元にして // AD変換を事項して、結果を補正する 最終結果 ad_x と ad_y //------------------------------------------------------------------------ void ad_actual(void) { ad_ch = 0; // AD ch0 ad_conv(); calc = ad_val - x_cent; calc2 = ad_val - x_min; if (ad_val >= x_cent) { ad_x = x_cent + (calc * x_h); } else { ad_x = (calc2 * x_l); } if (ad_x <= 0) ad_x = 0; if (ad_x >= 1023) ad_x = 1023; ad_ch = 1; // AD ch1 ad_conv(); // Get AD value calc = ad_val - y_cent; calc2 = ad_val - y_min; if (ad_val >= y_cent) { ad_y = y_cent + (calc * y_h); } else { ad_y = (calc2 * y_l); } if (ad_y <= 0) ad_y = 0; if (ad_y >= 1023) ad_y = 1023; } //--------------------------------------------------------------------- // JoyStickの中立X,Y値のを求め、補正を行う // 指を離した状態のJSの位置を10回読み込んで平均する //--------------------------------------------------------------------- void ad_cent(void) { x_cent = 0; y_cent = 0; ad_ch = 0; // AD ch0 for (i = 1; i <= 10; i++) { ad_conv(); // Get AD value x_cent = x_cent + ad_val; } x_cent = x_cent / 10; ad_ch = 1; // AD ch1 for (i = 1; i <= 10; i++) { ad_conv(); // Get AD value y_cent = y_cent + ad_val; } y_cent = y_cent / 10; } //------------------------------------------------- //RS232 initialize //------------------------------------------------- void RS232_ini(void) { //送信レジスタ設定 TRISC7 = 1; //set input for RX TRISC6 = 0; //set output for TX TXSTA = 0b00100100; //非同期、8ビット、高速送信モード 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 ----------------------------------}