PICで電飾 PWM調光 その4 ボタンの受付 | 波動砲口形状研究

PICで電飾 PWM調光 その4 ボタンの受付

PWM調光しながらボタン入力を受け付ける

タイマ割り込みを使った調光は、以前説明したタイマ割り込みを使ったボタンの受付と両立させることが出来ます。

 

ここではRB6とRB7が航行灯として交互点灯し、ボタンを押すと波動砲(RA1)が光るというのを、調光をすべてPWMにした例でお見せします。

 

 

これらは一点を除いてすべてすでに説明してきた機能で実現しています。

異なるタイマによる割り込みの両立

PWM調光に関しては、前々回とほぼ同じです。これをタイマ2を使った割り込みで実現していることはすでに説明しました。下掲のブログラムでタイマ2に関するところを水色の背景色で示しています。

 

ボタンの受付はタイマ1を使っています。タイマ1に関するところを紫の背景色で示しています。

 

このプログラムを走らせるとPICは

 

1) 20ミリ秒に1回、タイマ1の割り込み関数のところへ行ってボタンが押されていないかチェックし、

 

2) 5ミリ秒に1回、タイマ2の割り込み関数のところへ行ってPWM調光のためのカウンタを回し、LEDの点灯・消灯の作業をします。

 

どちらも同じ割り込み関数void __interrupt() xxx(void)に囲われていることに注目してください。割り込み関数の中には異なるタイマによる割り込みを同居させることができるのです。

 

breakで中断させる

一点だけ、新しく説明が必要になるのがbreakです。breakが出て来る部分のプログラムを抜書きすると

 

            for(DC1=0; DC1<100; DC1=DC1+1)
            {
                DutyRB6=100-DC1;
                DutyRB7=DC1;
                if (Event==1)
break;//Event==1ならfor文脱出
                __delay_ms(3);
            }

 

こうなっています。

 

PICはこのfor文に入ると、DC1というカウンターを0から100まで増やしながらそれをDutyRB7に代入し、DutyRB6には100-DC1を代入する仕事をはじめます。Dutyが増えてRB7のLEDはだんだん明るく、RB6はだんだん暗くなります。

 

それをしながらもタイマ1にせっつかれて20ミリ秒に1回、割り込み関数のところへ行ってボタンが押されていないか確認もしています。もしボタンが押されていたらイベントフラグを立てます(Event=1)。

 

フラグを立てた以上、波動砲に見立てたRA1を光らせる仕事にすぐ移って欲しいところです。

 

ところがPICは

 

「まだfor文でDC1を100まで数えてないから」

 

とその作業に戻ってしまうのです。数え終わるまで次に行きません。そこで、

 

「フラグ立ったらその仕事いいから次行ってよ」

 

と指示する必要があります(オレンジ背景の文)。この「その仕事いいから次行ってよ」を意味するのがbreakなのです。

 

このbreakでここのfor文を抜けるのですが、その次にもRB7をだんだん暗くするforが待っているので、次のfor文の中にもEvent==1ならbreakだよ、と入れておきます。

 

            for(DC1=100; DC1>0; DC1=DC1-1)
            {

                if (Event==1) break;//Event==1ならfor文脱出
                DutyRB6=100-DC1;
                DutyRB7=DC1;        
                __delay_ms(3);
            }

 

こうすることでフラグが立ってるのにfor文に捕まっている、ということにならずに済み、すみやかにRA1の発光に仕事が移るのです。

 

プログラムは以下の通りです。動画を見て、点滅や発光のタイミングが釈然としないという方も多いと思います。ぜひ自分で色々いじって、納得の演出をつけてやってください。

 

 

#include <xc.h>
#define _XTAL_FREQ  8000000   

//コンフィグ設定
#pragma config FOSC = INTOSC
#pragma config WDTE = OFF
#pragma config PWRTE = ON
#pragma config MCLRE = ON
#pragma config CP = OFF
#pragma config CPD = OFF
#pragma config BOREN = ON
#pragma config CLKOUTEN = OFF
#pragma config IESO = OFF
#pragma config FCMEN = OFF
#pragma config WRT = OFF
#pragma config PLLEN = OFF
#pragma config STVREN = ON
#pragma config BORV = HI
#pragma config LVP = OFF

int Counter,DutyRB6,DutyRB7,DutyRA1,DC1,Event;//変数の設定

//メイン関数ここから
void main(void)
{
     //特殊レジスタの設定
     OSCCON = 0b01110010 ;//内部クロック8MHz   
     ANSELA = 0b00000000 ;//RAの足をすべてデジタルI/Oとして使う     
     ANSELB = 0b00000000 ;//RBの足をすべてデジタルI/Oとして使う     
     OPTION_REG = 0b00000000;//内部プルアップ抵抗を使う
     TRISA  = 0b00000000 ;//RAの足はすべて出力に使う  
     TRISB  = 0b00001100 ;//RBの足のうちRB2,RB3を入力に使う
     WPUB   = 0b00001100 ;//RB2,3に内部プルアップ抵抗をつなぐ
     PORTA  = 0b00000000 ;//RAの初期値はすべてゼロ
     PORTB  = 0b00000000 ;//RBの初期値はすべてゼロ

 
     Counter=0;
     DutyRA1=0;
     Event=0;

 

         // タイマ1設定
        T1CON=0b00110000;//②プリスケーラ値8
        TMR1H=236;//③タイマ1初期値1
        TMR1L=120;//③タイマ1初期値2
        TMR1IF=0;//タイマ1割り込みフラグ0にする    
        TMR1IE=1;//タイマ1割り込み許可
        TMR1ON=1;//タイマ1スタート
        
        // タイマ2設定
        T2CON=0b00000100;//②プリスケール1、③ポストスケール1
        PR2=99;//④PR2初期値99に
        TMR2IF = 0; //タイマ2割り込みフラグゼロに
        TMR2IE=1;//タイマ2割り込み許可
        TMR2ON=1;//タイマ2スタート

 

        PEIE=1;//周辺割り込み許可
        GIE=1;//全体割り込み許可

   
    while(1)
    {
        while (Event==0)//イベントなしの平常時
        {
            for(DC1=0; DC1<100; DC1=DC1+1)
            {

                if (Event==1) break;//Event==1ならfor文脱出

                DutyRB6=100-DC1;
                DutyRB7=DC1;
                __delay_ms(3);
            }

            for(DC1=100; DC1>0; DC1=DC1-1)
            {

                if (Event==1) break;//Event==1ならfor文脱出
                DutyRB6=100-DC1;
                DutyRB7=DC1;        
                __delay_ms(3);
            }

        }
        
        while (Event==1)//イベント時
        {                   
            DutyRB6=0;//RB6消灯
            DutyRB7=0;//RB7消灯
            
            for(DC1=0; DC1<100; DC1=DC1+1)//RA1を徐々に明るく
            {
                DutyRA1=DC1;
                __delay_ms(10);
            }
            for(DC1=0; DC1<5; DC1=DC1+1)//RA1を高速点滅
            {
                DutyRA1=100;
                __delay_ms(30);
                DutyRA1=0;      
                __delay_ms(30);
            }
            __delay_ms(300);
            Event=0;//イベント解除
        }

    }
}//メイン関数ここまで


//割り込み関数ここから
void __interrupt() xxx(void)
{

    if(TMR1IF)//タイマ1による割り込み(ボタン受付)
    {
        TMR1H=236;//タイマ1初期値にもどす
        TMR1L=120;//タイマ1初期値にもどす
        
        if(RB3==0 && Event==0)//もしRB3が押されていて、イベントが起きていなかったら
        {
            Event=1;//イベントフラグ立てる
        }
        TMR1IF=0;//タイマ1による割り込みリセット
    }//タイマ1による割り込みここまで
    
    if(TMR2IF)//タイマ2による割り込み
    {

        PR2=99;//PR2初期値99にもどす

    Counter=Counter+1;//①カウントアップ

    if (Counter==100)//②カウント100になったら
    {
        Counter=0;//②リセットしてカウントを0に
        if (DutyRB6>0) RB6=1;//③カウント0と同時にRB6点灯
        if (DutyRB7>0) RB7=1;//③カウント0と同時にRB7点灯
        if (DutyRA1>0) RA1=1;//③カウント0と同時にRA1点灯
    }

        if (Counter>DutyRB6) RB6=0;//④カウント数がDutyRB6を超えたらRB6消灯
        if (Counter>DutyRB7) RB7=0;//④カウント数がDutyRB7を超えたらRB7消灯
        if (Counter>DutyRA1) RA1=0;//④カウント数がDutyRA1を超えたらRA1消灯

       TMR2IF=0;//タイマ2による割り込みリセット
    }//タイマ2による割り込みここまで
       
}//割り込み関数ここまで