//-------------------------------------------------------------------------- // LEDの表示制御プログラム 16F876 //-------------------------------------------------------------------------- // 2016-05-27 Copyright@VividHobby //------------------------------------------------------------------------- // SW操作による点滅の始動、停止を制御 // PWM出力追加。LOOP遅延を10msecに短縮 v012 // PWMを割り込み処理で実施、RCサーボを制御 v020 // 最終版(SW0のON/OFFでサーボを動作) v022 // サーボの動作速度を可変にする v023 // サーボの制御方法の改良(分解能アップ) v024 // プログラムリストの整理、コメントの見直し v026 // AD変換でRCサーボの動作を決める v030 //------------------------------------------------------------------------- //------------------------------------------------------------------------- // プログラム初期設定 //------------------------------------------------------------------------- #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) //------------------------------------- // スイッチ用ポートの設定 //------------------------------------- #define sw0 RC0 // RC0にsw0を設定 #define sw1 RC1 // RC1にsw1を設定 //----------------------------------------------------------------------- // 点滅設定定義エリア //----------------------------------------------------------------------- // 各ポートごとに点滅するLEDの点滅方法を設定する // //(通常の点滅モード) //[x, y, z]の指定パラメータは、 //配列は、LEDを接続するポートの数あって、それぞれ、{x、y、z}三つの //パラメータを指示するだけです。 //xは、以下の様な意味を持ちます。 // //0=ランダムの点滅 //1から12=順次点滅の順番 //13=定期点滅 // //zは、点滅周期の長さで、今回のプログラムでは、1=100msecに指定しています。 //ランダムで点滅する際は、この値が最大値となるように、周期が変わります。 // //yは、定数で、変更してはいけません。 //初めの、1,2,4,8,16は、ポートAのポート0,1,2,3,4に対応します。 //次の、1,2,4,8,16,32,64,128は、ポートBの0,1,2,3,4,5,6,7に対応します。 // // //------------------------------ PWM設定 ------------------------------------- //(自動モード) //x=14:PWMの制御を指定 //y=0 :PWMのデューティー比を0から100%の間で、自動的に増減させる。 //z=n :デューティー比を増減させる速度を指定。1%あたりnx5msecで増減。 //例:2とすると、2x5msecx100=1000msec、つまり1秒で消灯から最大輝度へ、 //また次の1秒で、消灯へ。ただし、最大値は255。 // //(手動モード) //x=14:同上 //y=1?255:周期を1から255までの間で指定可能。 //現在は、255固定(約0.819ミリ秒=約1220Hz)で使用する。 //デューティー比0?100%制御のため固定とする。 //z=n ;デューティー比を指定。0?100(%)として指定可能。 //プログラム内で、上記の周期に合わせてデューティー0?100%で制御。 // //(注意)使用しないポートにも、何か数値を書き込んでください。 //----------------------------------------------------------------------- // RCサーボの制御値の変更: // 0-100%で動作範囲を指定する方法から、分解能向上の為、0-250の範囲で // 指定する方法に変更。 // //----------------------------------------------------------------------- unsigned char parameter[14][3] = {// {0, 1, 20}, // ポートRA0用の設定==>パスされて利用されない(ADのchのため) {1, 2, 20}, // ポートRA1用の設定。以下、RA2-RA4とRB0-RB7と続く {2, 4, 20}, // RA2 {13, 8, 10}, // RA3 {13, 16, 15}, // RA4 {13, 1, 20}, // RB0 {13, 2, 25}, // RB1 {0, 4, 30}, // RB2 {0, 8, 30}, // RB3 {0, 16, 30}, // RB4 {0, 32, 30}, // RB5 {0, 64, 30}, // RB6 {15, 128, 0}, // RB7 サーボに設定、0に初期化 {14, 0, 3} // PWM : PWM周期=255、 Dutyは自動変化 }; // ここまで //------------------------------------------------------------------------ // 変数初期設定 //------------------------------------------------------------------------ unsigned char portcnt; unsigned char smax; unsigned char scnt; unsigned char led_cnt[14] = {0}; //ポート毎のLED点滅周期のカウント値を記憶初期値0 unsigned char sw0v, sw1v; //swで操作するポートの初期値を記憶する unsigned char updw = 0; int i; int duty, freq; //デューティー比と周期を格納 int servo; //サーボ設定パラメータ unsigned char svv; signed int m, n; //----------------------- 変更分 ------------------------------------------- // 新しく変数を定義し、初期値を設定 float target = 0; //サーボ動作目標値の保存 float step = 1, current = 0; //サーボ動作のステップ値の定義、現在のサーボ動作数 // このstepを加減することでサーボの動作速度を調整できる //-------------------------ここまで------------------------------------------ //------------------------------------------------------------------------ // 関数定義 //------------------------------------------------------------------------ void serial_max(void); //シリアル点滅のポート数を計算する static void interrupt tmr0_int(void); //割り込み処理ルーチン void rndm(void); //ランダム点滅ルーチン void regular(void); //定期点滅ルーチン void seri(void); //順次点滅ルーチン void port_out(void); //指定ポートの点滅 void port_off(void); void port_on(void); void port_pwm(void); void PWM_setup(void); void ad_conv(void); static void interrupt tmr0_int(void); //TMR0割り込み処理 void sv_on(void); //サーボ出力ポートをONにし、TMR0割り込みを設定する //------------------------------------------------------------------------- // メインプログラム //------------------------------------------------------------------------- main() { //--------------------------------------------- // PICのPORT設定の初期化 AD変換RA0ポートに設定 //--------------------------------------------- //PICのポート設定 ADCON0 = 0b10000001; //アナログ使用AD0 //ADはRA0 結果はADRESHに左寄せ8ビット ADCON1 = 0b00001110; //For A/D0 & degital I/O TRISA = 0b00000001; // A/D変換はRA0で行うのでRA0を入力に設定 TRISB = 0b00000000; TRISC = 0b00000011; // スイッチsw0とsw1を接続 PORTA = 0b11111111; // ポートの初期値を設定する(Hで通常、LでON) PORTB = 0b11111111; PORTC = 0b11111111; //---------------------------------------------------------------- // TMR0設定変更 // TRM0 setting 割り込み使用、プリスケーラ1/32設定@20MHz // 割り込みは約1.6384 msec毎に発生する //---------------------------------------------------------------- OPTION_REG = 0b10000100; /// Prescaller 1/32 (1.6384 msec full count) INTCON = 0b00100000; GIE = 1; // 全体割り込み許可 T0IE = 0; // TMR0 中断 T0IF = 0; // TMR0 割り込みフラッグクリア //---------------------------------------------------------------- // 変数初期値設定 //---------------------------------------------------------------- portcnt = 1; scnt = 1; smax = 0; PWM_setup(); //PWM初期設定 RA0 = 0; __delay_ms(2000); //PIC動作確認用 RA0 = 1; __delay_ms(2000); //---------------------------------------------------------------- // メイン処理開始 //---------------------------------------------------------------- serial_max(); //シリアル設定の最大個数計算 while (1) { //--------------------------------------------- //スイッチの状態をチェックして配列データを変更する //---------------------------------------------- //---------------------------- //SWでサーボの角度を切り替える // サーボの動作は0から250にする //---------------------------- //注意---------------------------------------------------------------- // TMR0はカウントアップ->オーバーフロー型のタイマーなので // 設定するパラメータ(0-255)が小さければ、オーバーフローまでの // 時間が長く、割り込みまで時間がかかり、逆にパラメータが大きいと // 割り込みは短時間で発生します。 // 試行錯誤の結果、パルス立ち上がり時のDelay関数の値は、660マイクロ秒 // TMR0のパラメータは、0-250でサーボが、ほぼフルスイングする様に // なりました。 //--------------------------------------------------------------------- //--------------------------------------------- // サーボの制御はAD変換で行うのでSWは使用しない //--------------------------------------------- // if (sw0 == 0)parameter[12][2] = 0; //SWがON(0)ならサーボパラメータを20に // else { // parameter[12][2] = 250; //そうでなければ、250にセット // } //----------------------------------------------------------- // 配列の設定データにしたがって、LEDを接続したポートをON/OFFする //----------------------------------------------------------- // ADにRA0を使用するためportcntは、1から13とする //----------------------------------------------------------- for (portcnt = 1; portcnt <= 13; portcnt++) { if (parameter[portcnt][0] == scnt) seri(); if (parameter[portcnt][0] == 0) rndm(); if (parameter[portcnt][0] == 13) regular(); if (parameter[portcnt][0] == 14) port_pwm(); if (parameter[portcnt][0] == 15) sv_on(); } __delay_ms(20); //処理全体の繰り返し時の待ち時間設定 20msec } } //------------------------------------------------------------------- // RA0のchでAD変換を実行し結果をservoに代入する // AD変換結果は0-256となるので、250以上は250として処理 //------------------------------------------------------------------- void ad_conv(void) { ADCON0 = 0b10000001; // AD変換準備RA0チャンネル __delay_us(20); // 20usecの準備待ち GO = 1; // AD変換開始 while (GO) { // GOビットが1の間待つ(0になるまで待つ) } servo = ADRESH; if (servo >= 250) servo = 250; } //-------------------------------------------------------------------- // シリアル点滅のポート数を計算する //-------------------------------------------------------------------- void serial_max(void) { m = 0; smax = 0; for (m = 0; m <= 12; m++) { if ((parameter[m][0] >= 1)&(parameter[m][0] <= 12)) { if (smax < parameter[m][0]) smax = parameter[m][0]; } } } //-------------------------------------------------------------------- // ランダム点滅ルーチン //-------------------------------------------------------------------- void rndm(void) { if (led_cnt[portcnt] <= 0) { port_out(); //LED出力を反転 n = parameter[portcnt][2]; m = rand() / 5000 * n; led_cnt[portcnt] = m; } else { led_cnt[portcnt]--; } } //-------------------------------------------------------------------- // 定期点滅ルーチン //-------------------------------------------------------------------- void regular(void) { if (led_cnt[portcnt] <= 0) { port_out(); //LED出力を反転 led_cnt[portcnt] = parameter[portcnt][2]; //ポート毎の点滅時間をセット } else { led_cnt[portcnt]--; } } //-------------------------------------------------------------------- // 順次点滅ルーチン //-------------------------------------------------------------------- void seri(void) { if (led_cnt[portcnt] == 0) { led_cnt[portcnt] = parameter[portcnt][2]; port_on(); } led_cnt[portcnt]--; if (led_cnt[portcnt] == 0) { port_off(); scnt++; if (scnt > smax)scnt = 1; } } //-------------------------------------------------------------------- // 指定ポート出力の反転(配列の最大数を超えない様に) //------------------------------------------------------------------- void port_out(void) { if ((portcnt >= 0)&(portcnt <= 4)) { PORTA = parameter[portcnt][1]^PORTA; //portcntが1-4ならPORTA } else { PORTB = parameter[portcnt][1]^PORTB; //portcntが5-12ならPORTB } // __delay_ms(1); } //-------------------------------------------------------------------- // 指定ポート出力をOFF(配列の最大数を超えない様に) //------------------------------------------------------------------- void port_off(void) { if ((portcnt >= 0)&(portcnt <= 4)) { PORTA = parameter[portcnt][1] | PORTA; //portcntが1-4ならPORTA } else { PORTB = parameter[portcnt][1] | PORTB; //portcntが5-12ならPORTB } // __delay_ms(1); } //-------------------------------------------------------------------- // 指定ポート出力をON(配列の最大数を超えない様に) //------------------------------------------------------------------- void port_on(void) { if ((portcnt >= 0)&(portcnt <= 4)) { PORTA = ~(parameter[portcnt][1]) & PORTA; //portcntが1-4ならPORTA } else { PORTB = ~(parameter[portcnt][1]) & PORTB; //portcntが5-12ならPORTB } // __delay_ms(1); } //----------------------------------------------------------------------- // PWM処理(自動・手動) //----------------------------------------------------------------------- void port_pwm(void) { if (parameter[portcnt][1] == 0) { //AutoPWM if (updw == 0) { //up led_cnt[portcnt]++; if (led_cnt[portcnt] >= 100) { updw = 1; } } else { led_cnt[portcnt]--; if (led_cnt[portcnt] <= 0) { updw = 0; } } freq = 255; // 自動モードの際は周期は固定(255) PR2 = freq; duty = led_cnt[portcnt]; duty = duty * 255 / 100; CCPR1L = duty; } else { //Manual mode freq = parameter[portcnt][1]; PR2 = freq; duty = parameter[portcnt][2]; duty = duty * 255 / 100; CCPR1L = duty; } } //-------------------------------------------------------------------------- // PWM 初期設定 // 変数設定 freq = 周期、 duty = パルス幅 // 出力 CCP1 = RC2 //-------------------------------------------------------------------------- void PWM_setup(void) { PORTCbits.RC2 = 0; // PWMの出力を設定 PR2 = 0; // PWMの周期をセット CCPR1L = 0; // PWMのパルス幅をセット、ただし初期値はゼロ 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スタート( 0=STOP, 1=START) T2CONbits.T2CKPS1 = 1; // プリスケーラ T2CONbits.T2CKPS0 = 1; // 00=1/1, 01=1/4, 10=1/16, 11=1/16 } //---------------------------------------------------------------- // TMR0割り込み処理 // サーボ制御のポートをLに戻す //---------------------------------------------------------------- static void interrupt tmr0_int(void) { port_on(); //サーボ接続ポートをOFF(負論理) T0IE = 0; // タイマー割り込み一時禁止として、すぐに戻る } //------------------------------------------------------------------ // TMR0に割り込み時間をセットして、サーボ信号出力をONに // 660マイクロ秒をDelay関数で待って、 // TMR0に時間をセットして割り込み処理を許可するが、そのまま割り込み // 発生をLOOPで待ち、割り込み検出後に、ポートをOFFにして次の処理にすすむ //------------------------------------------------------------------ void sv_on(void) { //------- 変更分 ------------------------------------------- // target = parameter[portcnt][2]; //サーボ設定値を読み取り(値=0-255) // // if (target != current) { // if (target <= current) current = current - step; //目標値が現在値よりちいさければstepを減算 // else current = current + step; //そうでなければ加算 // if (current <= 0) current = 0; //下限値チェック // if (current >= 250) current = 250; //上限値チェック // } // servo = current; //値(0-255)をservoに渡す //--------ここまで-------------------------------------------- ad_conv(); // AD変換実行。結果はservoに格納 port_off(); //サーボ接続ポートをON(負論理) __delay_us(660); //サーボ制御パルスの最初の基本部分750マイクロ秒をここで指定する TMR0 = servo; T0IF = 0; // TMR0 割り込みフラッグクリア T0IE = 1; // タイマー割り込み解除 while (T0IF == 0) { // TMR0割り込みが発生するまで待つ } } // -------------------------End of Program --------------------------------