PIC16F88 割り込みでサーボを動かすの巻き

去年の10月くらい(2015/10)から一念発起して、私のようなもんがPICマイコンなんぞに手をだして、こともあろうかPID制御でACモーターをスピード制御させようと思い立って早一年が過ぎようとしています。 今年2016年に入って1月くらいまでは順調にPICマイコンについて勉強してましたが、3月くらいからすっげー忙しくなって本業の”臨床工学技士”の仕事に汗水たらしてましたら、もう9月になってました・・・ ここいらで少し余裕がでてきたので、猛スピードで続きをして、結果をださなければ何をしてたんだ?ってなります。  

まずはPICについてこれまでのおさらいです。

  なぜPICマイコンなのか?ですが・・ 透析装置のモーターを利用してミニ人工心肺をつくるが位相制御だけではスピードコントロールに限界を感じる ↓ PICマイコンをはじめてみる PICの奥深さに感銘と無限大の可能性を感じる ↓ PICマイコンの練習 いけそうな気がしてくる ↓ MPLABXでプログラミング前編 ・ 後編 で皆んなにPICの素晴らしさを知って欲しいと思ってみる ↓ PIC16F88のCCP機能でサーボモータを動かす事に成功 (2015/01) この後PICの世界から一時離れる。   まぁこういった具合にすすんできました。 あとPID制御要はフィードバック制御をできるようになるには ①割り込みを覚えること➡️今回やる事!! ②LCDパネルを扱えるようになること ③アナログデジタル変換ができるようになる です。   んで、今回は割り込みをやってみます。 先に結論から言うとPICで割り込みするのは結構簡単でした。 こちらが実際の動いているところです。CCP同様に滑らかに動いていますが、なんかギギギッとかいう音が気になるります。 CCP機能でPWMモードを使ったほうが無難かもしれませんね。 でも割り込みという機能の習得には大いに役立ちました。    

割り込みの概念

今回も内部クロックで動いてくれるPIC16F88を使用しました。 実は定番マイコンのPIC16F84Aでも割り込みのプログラムは同様です。 pic16f88 これがPIC16F88です。 PIC16F88のほうが値段も安いし、いろいろできるし、内部クロック使えるし最高なんです。 前回のサーボ制御はCCP機能といってまさにサーボを動かす為の機能といっても過言ではないPWMというパルス信号を作ってくれる便利なものでした。 今回はほぼ同様のPWMのパルス信号を割り込みという機能を使って作ります。 ちなみにパルスは約20mSecごとに発生して、パルス幅は0.6mSec~2.3mSec程度とします。 まず割り込みとはからですが、簡単に言うと任意に決めたタイミングで自動的に割り込みプログラムを駆動させる事を言います。 なぜ割り込みというかですが、メインプログラムに対する割り込みだからです。 通常電源を入れるとメインプログラムが駆動されるわけですが、メインプログラムが走っている時に任意のタイミングがくるとメインプログラムが止まり、割り込みプログラムが駆動されるので”割り込み”と言うのです。  

PIC16F88の割り込みの設定

まずはOPTION_REGの設定です。

option%e3%83%ac%e3%82%b7%e3%82%99%e3%82%b9%e3%82%bf OPTION_REGのbit5 TOCS 0の時タイマーモード 1の時にカウンターモード(外部トリガー) だから今回は0で設定   OPTION_REGのbit3 PSA ここの設定でプリスケーラーをウォッチドッグタイマーかTMR0どちらに割り当てるか決定する。 今回割り込みを利用するのでTMR0に設定するので、PSAビットに0を設定する   OPTION_REGのbit2-0 PS ここでプルスケーラーの比率の設定をします。 あとで計算しますが今回PWMのパルスを20msecごとに発生させるようにするので、プリスケーラーはMAXの256にしています。  

INTOCONレススタの設定

intocon%e3%83%ac%e3%82%b7%e3%82%99%e3%82%b9%e3%82%bf もう一つ重要なのが上記のINTOCONレジスタ設定です。 bit7 GIE 1で全ての割り込みを許可する だから今回は1を設定   bit5 TMR0IE GIEとかぶりますが、TMR0の割り込みの許可です。 1で割り込みを許可します。 ですので今回は1を設定。   bit2 TMR0IF このビットは特殊で割り込みはクロック周波数と8ビット(256)のTMR0とOPTION_REGのbit2-0 PSで設定したプリスケーラーで時間が決まります。 TMR0は1命令ごとにカウントアップされていき255から0に変わる時に割り込みが発生しTMR0IFがオーバーフローします。 だから割り込みプログラムではTMR0IFをプログラム上でクリアしないといけません。  

TMR0レジスタ

  8ビットのカウンタで、1命令サイクル毎に+1されます。 プルスケーラーが設定されていればその比率でカウントが伸びます。 最初に設定した値からカウントアップが始まり最大値の255から0に戻る時にTMR0割り込みが発生します。 たとえばTMR0に100がセットされていると (255-100+1)×(プリスケーラーの値)でTMR0割り込みが発生するのです。 内部クロックを8M㎐で駆動させていれば1サイクル0.5μsecなので プリスケーラを256倍とすれば、 156×256×0.5μsec=19968μsec  約20msecとなります。  

回路図

pic16f88warikomiserbo

プログラム

// PIC16F88 Configuration Bit Settings // ‘C’ source line config statements #include <xc.h> // #pragma config statements should precede project file includes. // Use project enums instead of #define for ON and OFF. // CONFIG1 #pragma config FOSC = INTOSCIO //内部クロックをつかいますよー #pragma config WDTE = OFF // #pragma config PWRTE = OFF // #pragma config MCLRE = OFF // #pragma config BOREN = OFF // #pragma config LVP = OFF // #pragma config CPD = OFF // #pragma config WRT = OFF // #pragma config CCPMX = RB3 // #pragma config CP = OFF // // CONFIG2 #pragma config FCMEN = OFF // #pragma config IESO = OFF // //////////delay関数を使うために発振周波数を定義 8Mhz//////////// #define _XTAL_FREQ 8000000 void arg_delay(unsigned int x);   //グローバル変数の定義 unsigned int angle; //サーボの振れ角度     //割り込みが入った時のプログラムです。 void interrupt myIsr(void) { if (INTCONbits.TMR0IE && INTCONbits.TMR0IF) { PORTAbits.RA0 = 1; //パルスON arg_delay(100 + angle);//angleは0-230の値で可変させるとうまくいきました。 //TMR0IETMR0IFが両方とも1ならRA0を1にします //パルスON時間はangleを変更して決定して継続させますよ。 PORTAbits.RA0 = 0; //パルスOFFさせます INTCONbits.TMR0IF = 0;  //次の割り込みの為にTMR0IFをクリアしておきます } }   //ここからがメインで設定 void main(void) { OSCCON = 0b01110000;//内部周波数8Mhzにします OPTION_REG = 0b00000111; //プリスケーラを256に設定 INTCONbits.TMR0IE = 1; //割り込みを許可 INTCONbits.GIE = 1;//全体の割り込みを許可 INTCONbits.TMR0IF=0;//TMR0IF最初にクリアしときます。 TMR0 = 100; //256-100= 156*256*0.5us=19968us ほぼ20msec ANSEL = 0b00000000;//すべてデジタルに設定しときますよ TRISA = 0b00000000;//portAはすべて出力に設定 TRISB=0b00000000;//portBはすへて出力に設定 PORTA = 0;//きれいにクリアしときます。 PORTB=0;//きれいにクリアしときます。 //メインの処理 PORTAbits.RA1 = 1; //RA2:パワーオンを示すLED点灯 angle = 115; //サーボの触れ角を0度に __delay_ms(2000); while (1) { //基準角度確認 angle = 115; //パルス幅 __delay_ms(1000); angle = 230; //パルス幅 __delay_ms(1000); angle = 115; //パルス幅 __delay_ms(1000); angle = 0; //パルス幅 __delay_ms(1000); } return; } //待ち時間が変数設定できるarg_delay()関数を定義 //_delay()は引数に変数設定が出来ない為) void arg_delay(unsigned int x) { while (x) { __delay_us(1); x–; } }
スポンサードリンク
カテゴリー: 制作事例 パーマリンク

コメントを残す

メールアドレスが公開されることはありません。

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください