ここでは、モーターを動かします。
以下のプログラムをコピーして、貼り付けて使用してください。
「PWM」プログラム |
---|
// ファイル名 PWM.c // 16ms周期のPWM波形を作り、赤のLEDの明るさをコントロールする #include <htc.h> __CONFIG(PWRTEN&HS&WDTDIS&UNPROTECT&MCLRDIS&BORDIS&IESODIS&FCMDIS); #define _XTAL_FREQ 20000000 #define PERIOD 100 /* 20MHz/4 = 5MHz TMR0 * prescale*counter/5MHZ = (256-PERIOD) * 256*2/5MHZ = (256-100) * 256*2/5MHZ = 16m秒 */ unsigned char counter=0; void interrupt Intr(void) { // 割り込みがtimer0から来る counter++; TMR0 = PERIOD; T0IF = 0; // 割り込みフラグクリア return; } main() { unsigned int i, j; PORTA = 0; // PORTAを0にする TRISA = 0; // PORTAを出力に設定する ANSEL = 0; // アナログ入力をOFF // Timer0 の設定 OPTION = 0xd0; // FOSC/4 プリスケーラ1/2 T0IE=1; // Timer0 の割り込みON T0IF=0; // 割り込みフラグクリア GIE=1; // CPUへの割り込みON while(1) { for(i=0; i<255; i++) { for(j=0; j<100; j++) { if(counter <= i) RA0 = 1; else RA0 = 0; } } for(i=255; i>0; i--) { for(j=0; j<100; j++) { if(counter <= i) RA0 = 1; else RA0 = 0; } } } } |
if(counter <= i) RA0 = 1; else RA0 = 0; | 1周期(16ms)を256分割して、0から255の時間に分けます。例えば190よりも小さいときに1、 それ以外の場合には0とすれば下図のようになります。(下図の場合発振器の都合で周期が約17msになっています。1目盛が5msです) |
#define SLOW 35 #define LOW 40 #define MIDDLE 80 #define MIDDLE2 120 #define HIGH 160
以下のプログラムをコピーして、貼り付けてください。
「CdSPWM」プログラム |
---|
// ファイル名 CdSPWM.c // CdSに手をかざすとそれに対応してモーターが動く // 左前のCdSだけに手をかざすと押される感じに車体が右に旋回する(赤のLED点灯) // 右前のCdSだけに手をかざすと押される感じに車体が左に旋回する(緑のLED点灯) // 後ろのCdS場合はバックする(青のLED点灯) // 全部かざすと前進する。(白のLED点灯) // これをタイマー0割り込みを用いて実現する // 関数にして機能分担化させる // 自動閾値設定(初期値95%)を電源投入直後に行うのでCdSの上には障害物 // (手など)はあってはいけない。 #include <htc.h> __CONFIG(PWRTEN&HS&WDTDIS&UNPROTECT&MCLRDIS&BORDIS&IESODIS&FCMDIS); // マクロ宣言 #define true 1 #define false 0 #define _XTAL_FREQ 20000000 #define ANS4 0x10 #define ANS5 0x20 #define ANS6 0x40 #define AN4 0x11 #define AN5 0x15 #define AN6 0x19 #define PERIOD 100 /*********************************************/ /* モーターをPWMで動かす時に利用するグローバル変数の counter変数のupdate間隔の計算 20MHz/4 = 5MHz TMR0 * prescale*counter/5MHZ = (256-PERIOD) * 2*256/5MHZ = (256-100) * 2*256/5MHZ = 16m秒 */ //*************************** #define SLOW 35 #define LOW 40 #define MIDDLE 80 #define MIDDLE2 120 #define HIGH 160 //************************** typedef unsigned char byte; // プロトタイプ宣言 void initialize(); byte checkCdS(); void setThreshold(byte th); void overHand(); //****************** // グローバル変数の宣言 byte counter=0; byte LeftCdS, CenterCdS, RightCdS; byte ADch=0; byte ConvEndFlag; byte ADC[3]={AN4,AN5,AN6}; byte threshold[4]; //************************ byte Leftspeed=208; byte Centerspeed=208; byte Rightspeed=208; //************************ // 割り込み処理ルーチン void interrupt Intr(void) { // (256-PERIOD)*256/5MHz // =(256-100)*2/5MHz = 62.4μs // 62.4μs毎に割り込む if(T0IF) { // 割り込みがtimer0からか? counter++; TMR0 = PERIOD; T0IF = 0; // 割り込みフラグクリア return; } if(ADIF) { // 割り込みがADコンバーターからか? if(ADch==0) LeftCdS = ADRESH; else if(ADch==1) CenterCdS = ADRESH; else if(ADch==2) RightCdS = ADRESH; ConvEndFlag=true; // データの更新あり ADIF = 0; // 割り込みフラグクリア } } main() { initialize(); // 各種初期設定 // 永久ループ while(true) { overHand(); // CdSでモーターを動かす //checkCdS(); } } void initialize() { PORTA = 0; // PORTAを0にする TRISA = 0; // PORTAを出力に設定する ANSEL = ANS4 | ANS5 | ANS6; // PORTAアナログ入力をAN4-6をON ADCON0 = AN4; // ADFM=0(左詰) ADon OPTION = 0; // FOSC/4 プリスケーラ1/2 // 20MHz/4/2 = 2.5MHz PORTC = 0; // PORTCを0にする TRISC = 0x0f; // PORTC下4bit入力、上4bit出力 ANSELH = 0; // PORTCアナログ入力をOFF // Timer0 の設定 T0IE=1; // Timer0 の割り込みON T0IF=0; // 割り込みフラグクリア setThreshold(65); // ADコンバーターの設定 ADIE = 1; // ADコンバータの割り込み許可 ADIF = 0; // 割り込みフラグクリア PEIE = 1; // 周辺装置の割り込み許可 GIE = 1; // CPUへの割り込み許可 GODONE = 1; // AD変換スタート } byte checkCdS() { byte status; if(ConvEndFlag) { // AD変換が終わったらすぐに次のチャンネルの変換を行う ConvEndFlag = false; if(++ADch==3) ADch=0; ADCON0 = ADC[ADch]; // ADチャンネル変更 GODONE = 1; // AD変換スタート } PORTA=0; if(RightCdS<threshold[2]) RA2=1; // 赤 if(CenterCdS<threshold[1]) RA1=1; // 緑 if(LeftCdS<threshold[0]) RA0=1; // 青 status = RightCdS<threshold[2]; status += (CenterCdS<threshold[1])?2:0; status += (LeftCdS<threshold[0])?4:0; return status; } void setThreshold(byte th) // 平均の指定%を閾値にする { byte i, j, x, g; int average[3]={0,0,0}; g = GIE; // 割り込み状態の保存 GIE=0; // 割り込みは使わない // 明るいときの値 明るいと大きい値 ADC[0]=AN4; ADC[1]=AN5; ADC[2]=AN6; for(j=0; j<40; j++) { // 平均の明るさを求める for(i=0; i<3; i++) { ADCON0 = ADC[i]; GODONE = 1; while(GODONE); x = ADRESH; average[i] = (average[i]*9+x)/10; } } for(i=0; i<3; i++) threshold[i]=average[i]*th/100; GIE = g; // CPUへの割り込み状態を戻す } void overHand() { while(RA3) { switch(checkCdS()) { case 7: Rightspeed=Leftspeed=MIDDLE; // 直進 RC5 = RC7 = 0; break; case 6: Rightspeed = LOW; Leftspeed = MIDDLE; RC5 = 0; RC7 = 0; break; case 4: Rightspeed = ~MIDDLE; Leftspeed = MIDDLE; RC5 = 1; RC7 = 0; break; case 3: Rightspeed = MIDDLE; Leftspeed = LOW; RC5 = 0; RC7 = 0; break; case 1: Rightspeed = MIDDLE; Leftspeed = ~MIDDLE; RC5 = 0; RC7 = 1; break; case 2: Rightspeed=Leftspeed=~MIDDLE; RC5 = RC7 = 1; break; default: case 0: Rightspeed=Leftspeed=255; RC5 = RC7 = 1; break; } RC4 = counter<Rightspeed; RC6 = counter<Leftspeed; } } |
byte checkCdS()
| ADコンバータの値、つまりCdSの手のかざし加減を見てどのCdSの上に手があるかを知らせます。 |
void overHand()
| 上記checkCdS()から知らされた値でモーターを指定通りに動かします。 |
RC4 = counter<Rightspeed; RC6 = counter<Leftspeed; |
ここで左右のモーターをPWM制御をしています。 counter<Rightspeedが正しかったらRC4に1、そうでなかったら0が代入されます。 |
Rightspeed = ~MIDDLE; RC5 = 1; | モーターを逆転させるには、RC5を1にしなければいけませんが、同時にRC4の波形を逆転させなければいけません。例えば正転で100の期間Highだった場合はLowは156になります(8bit, 全部で256だから)。この逆転と言うことは156Highにして100Lowにすることです。問題は156をどうやって高速に求めるかです。256-100でいいのですが、非力のCPUでは時間とメモリーを使ってしまいます。以下に簡単でメモリーを使わないでプログラムをするかが問題です。これは簡単で、100のbit反転すればいいだけです。誤差が1出ますが(155になる)モーターを動かすな使用法では全く問題はありません。~MIDDLEはMIDDLEのbit反転を意味します。 |