PIC88の活用

  1. はじめに

     ここでは、PIC88の機能を活用した、統合的応用回路を紹介します。個々の内容は他の記事と重複します。
    CDSの明るさをセンサで読み取り、AD変換後10段階のレベルに変換します。この値を1桁の7素子表示器で表示し、さらに、DCモータをPWM制御します。

  2. 数字表示

    1. 7素子数字表示素子

        7素子表示は、電卓などでおなじみの数字表示器です。棒状の発光体を7本を 日 の字型に組み合せ、適当に点滅させると、0から9の数字を表示することができます。下の図は5と9を表示した例です。5では、a,c,d,f,g の素子を点灯します。
      点灯する場合を 1 とし g,f, .., c,b,a  の順に表記すると、 0 のパタンを点灯するには 0111111 、1の場合は 0000110 となります。



    2. 発光パターン

       表示したい数字に対し、a,..,g の各素子の発光パターンを決めるにはどうしたらよいでしょうか?ここで、プログラムの出番です。まず、以下の配列パターンを用意します。各データは、16進定数で指定されています。

      int segment_data[] = {0x7E, 0x0C, 0xB6, 0x9E, 0xCC, 0xDA, 0xFA, 0xE, 0xFE, 0xCE};

      16進定数は、4ビットの2進数に対し、0,1,2、.. 、7,8,9、A,B,C,D,E,F の文字を対応させます。

      16進数 0 1 2 3 4 5 6 7 8 9 A B C D E F
      2進数 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111

      16進文字が2文字で、8ビットの2進数を表現できます。たとえば、0xB6 は 1101 0110 に相当します。最下位の0を無視すると、1101 011 は 数字 2 を表示するパターンです。したがって、

       segment_data[2]

      は数字 2 を表示するパターンとなります。一般に、segment_data[i] は数字 i のパターンを表示します。ここで、配列の最後(第0)のビットを無視したのは、PIC のBポート の B1、..,B7 に 7素子表示の a,..,g を接続するからです。B0 は特殊な目的に利用できますから、ここでは、この端子を使用しないで空けておきます。

       segment_data[2]

      をポートBに送ると、最後(第0)ビットは7素子表示器にはおくられず、第1から第7ビットが segment_data[2] に送られ数字の"2"を表示します。

  3. アナログ入力とAD変換

    1. 光センサー

       光センサーは光の強さにより、抵抗値が変わる物質(CdS:硫化カドミウム)を利用しています。光が殆どないときは、数十Kオームの高抵抗ですが、、室内照明程度の明るさで、数Kオームまで抵抗が低下します。



       このcdsの抵抗変化を電圧として取り出し、PICのAD変換機能を利用すると、光センサーからの電圧により、出力の制御を行うことができます。

    2. センサー回路

       図で楕円で囲まれた抵抗の記号がCdS光センサです。CDSの面への明るさが変わると、図の出力端子の電圧が変化します。



      テスターでcdsの面を室内光をあてた場合と、指で塞いだ場合の抵抗を測定してください。次に、図のように70kΩ程度の抵抗を直列に接続し、5Vの電圧をかけたときの出力電圧を測定してください。
       CDSの抵抗を r kΩ(Ω) としたとき、 出力電圧は 5 * ( r / ( r + 70) ) となることを確認してください。

    3. アナログ信号

       1,0 の2値でなく、電圧値などの物理量がそのまま値となるような信号をアナログ信号といいます。アナログ信号を入力として、それを、ディジタル値に変換する機能を A/D (アナログディジタル)変換といいます。PICの 16F88 にはこのAD変換機能が組み込まれています。
       AD変換を行うには、アナログ信号を入力する端子と使用するクロックを指定します。

       setup_adc_ports(AN0);//A0端子をアナログ入力AN0として利用する
       setup_adc(ADC_clock_div_32);//クロックは内部クロックの1/32

      16F88は4本のアナログ信号端子があります。まず、まず set_adc_channel(0) で読み込む端子(チャンネル)を指定し、少し時間を置いてから read_adc() で変換値を読み込みます。読み込む値は10bitですから、変数は long で宣言します。

       long vd;//16ビット整数
       set_adc_channel(0);//0チャンネルを選択
       delay_us(30);//変換時間待つ
       vd=read_adc();//変換結果を読む

    4. AD変換を利用する場合の注意

       PICでAD変換機能を利用する場合、PICの電源として十分安定化した電源を利用する必要があります。簡単なDCアダプタの場合、AD変換の出力が10以上ばらつきます。電源に、30μF程度のコンデンサを入れるとばらつきは5程度に抑えられます。良い電源がない場合、3端子の定電圧(安定化)素子を利用すると効果的です。この場合、ACアダプタは9V〜12Vを利用します。

  4. PWM制御

    1. 小型DCモータ

       ここでは、小型のDCモータの回転制御を行います。小型DCモータは玩具にもよ良く利用されています。ここで利用するモータは適正電圧1.5V〜3V、電流数百mA程度です。
       DCモータは負荷がない場合(回転軸に何も接続されていない場合)、電流は少ないですが非常に高回転になり、軸やブラシを傷めます。また、負荷が重すぎる場合回転数が落ち、高い電流が流れます。電流が適正値を越えると、発熱して故障の原因になります。



    2. DCモータの特性

       DCモータの回転力は通常トルク(g/cm)であらわします。標準の回転数のときのトルクを適正負荷といいます。ここで利用するモータは、標準回転数は約2000(rpm)、トルクは120g程度です。回転数の単位rpmは分あたりの回転数です。モータの回転数は高いので、通常、ギヤ(歯車)で回転数を落として使用します。回転数を 1/100 にすると、トルクは100倍になります。回転数*トルク が仕事量になります。ギヤなどの損失を無視すれば、モータに流し込む電流がモータの仕事量になります。仕事量が同じであれば、回転数が半分になればトルクは倍になります。

    3. モータの制御

       モータに流れる電流は電圧が一定であれば、回転数で定まり外部から制御することは困難です。電圧をかえると、直線的にトルクが増し、回転数も増加しますが、流れる電流が多い場合電圧の制御は簡単ではありません。
       そこで、PWM: Pulse Width Modulation 制御がよく利用されます。
      この方法は、一定区間の中で通電する割合(時間幅)を変更して平均的な電力を調整する方法です。下図は、25%、50%、75% の電力制御の説明図です。横方向が時間で、立ち上がっている間スイッチをオンにして通電します。



      通電幅を0にすればオフ、繰り返し期間と通電期間を同じにすれば、完全オン状態になります。PWM制御は少々荒っぽい手法なので、制御可能な対象は限定されますが モータの電力制御にはよく利用されます。

    4. PICのPWM制御

      PIC88にはPWM信号を生成する機能があります。まず、setup_ccp1(CCP_PWM); で、ccp1 機能を PWM に設定します。

       setup_ccp1(CCP_PWM);

       次に、タイマー2の周期を設定します。タイマー2はPWMの全体の周期を設定します。 タイマー2の元になるクロックは、PIC 本体のクロックの 1/4 の周波数です(周期は周波数の逆数です)。これを、さらに、1/prd にしてPWMの周期とします。これは、ccp1とccp2に共通です。prdは 255 以下とする必要があります

       setup_timer_2(T2_DIV_BY_1,prd,1);

       周期の中のパルスの幅(これを duty といいます)を定まるには、set_pwm1_duty(pwt) を利用します。pwt が1周期内部のパルス幅を定めます。これは、周期より小さな値に設定する必要があります。

       set_pwm1_duty(pwt1);

       ccw1のpwm信号は、B0/ccp1 端子(6ピン)から、から取り出すことができます。

    5. 出力回路

       DCモータは小型でも100mA、大きなものでは100A以上の電流を必要とします。オン/オフの制御の場合はリレーを利用しますが、細かい制御が必要な場合、トランジスタを用いた電流増幅を行います。基本は IID で紹介した回路ですが、モーターなど、大きな磁力を発生する回路には、特別な配慮が必要です。
       磁力を発生する回路(インダクタンス負荷)は、同じ状態を保とうとする性質があり、電流を流そうとするときと電流を切る場合に反撥します。ここでの問題は電流を切る場合で、発生する反撥(起電)力で、半導体が壊れてしまいます。そこで、反撥する起電力を吸収するため、図のようにコイルに並列にダイオードを接続し、この反撥起電圧を吸収します。

       

  5. 制作回路

    1. 目的

       cdsで検知した明るさにしたがい、7素子表示で10段階のレベル表示を行い、パルス幅制御で小型DCモータの回転制御を行います。
    2. 回路図

       CdSセンサーを抵抗と直列に接続し、明るさに応じた電圧を取り出します。これを、PIC(16F88)のアナログ入力(AN0)に接続します。PICのBポートの上位7本を 7セグLED に接続し1桁の数字表示を行います。16F88 のPWM(CCP1)を抵抗を通してトランジスタ(2SC1815)のベースに接続します。トランジスタのコレクタに小型モータを接続します。モータの端子には ダイオード を接続し モータ の電流切断時に発生する逆起電力を吸収します。





    3. 7素子表示に関する注意

       ここで利用している7素子表示器(A551UB)は、アノードコモンで電源端子(3,8)に5Vを接続し、各素子(a〜g)をL(GND)側にドライブすると発光します。一方、よく利用される C551 はカソードコモンで、電源端子はGNDに接続し、各素子(a〜g)をH側にドライブすると発光します。C551を利用する場合、上の回路で7素子表示器の電源端子をGND側に接続します。また、プログラムで output_b(~segment_data[vs]);//7セグ出力 の~ を削除します。~ はビットごとの反転演算で、これがあると 点灯したいとき L レベルを出力します。

  6. プログラム

    1. プログラム

      先頭の #include <16f88.h> は16F88 の定数を取り込みます。#device ADC=10 は AD変換 を10ビットで読み込む指定です。#fuses はプログラムでは設定できない 16F88 の動作モードを指定します。NOBROWNOUT は電圧低下に伴うリセットを停止する機能で、電池駆動では必要です。use delay(CLOCK=4000000) は内部クロックの周波数です。

      segment_data[] は、0〜9 までの数字に対応する発光パターンです。ここでは、B7を7セグの a 端子に接続したため、左右が逆転したパターンになります。
       main() 関数では、先頭で set_tris_a(0xFF); でAポートを入力とし、setup_adc_ports(sAN0); でA0端子をアナログ入力として利用し、setup_adc(ADC_clock_div_32); でAD 変換のクロックを設定し、set_adc_channel(0); でアナログ入力を 0チャンネル(AN0)とすることを指定します。
       次は、出力側の設定で、set_tris_b(0x01);  でPB0以外を出力とすること、output_b(0xFF); でBポートの初期値を1にすること、setup_ccp1(CCP_PWM); でPWM を利用することを指定します。PWM は PB0 から出力されます。次に繰り返しの値を100とし、setup_timer_2(T2_DIV_BY_1,prd,1); でPWMの繰り返し周期を指定します。この場合、PWMの基本クラックは1μ秒ですから、繰り返し周期は 100μ秒(10KHz)となります。
      while() から繰り返しになります。まず、read_adc() でCdSからの明るさ信号をAD変換しこの値の1/20 を vd とします。vd が prd/2 を超える場合は vd を prd/2 とします。この値を 1/3 すると、0〜9 の値になります。これを vs として、7セグ 素子に表示します。7セグ は端子を0 にすると発光しますから、~segment_data[vs] と 先頭に ~  で0,1 を反転します。
        set_pwm1_duty(prd-(int)vd) で、パルさの幅を vd だけ短くします。vd が大きいと、PWM の1となる幅が短くなるため、モータの回転が弱くなります。
       delay_ms(100); は100m秒休止することを指示します

    2. ソース

      #include <16f88.h>
      #device ADC=10
      #fuses INTRC_IO,NOWDT,NOLVP,NOMCLR,NOBROWNOUT//内部クロック、WDT,LVPなし
      #use delay(CLOCK=4000000) //クロック4MHz
      
      int prd;
      int vd,vs;
      
      //int segment_data[]={0x7E,0x0C,0xB6,0x9E,0xCC,0xDA,0xFA,0xE,0xFE,0xCE};
      int segment_data[]={0xFC,0x60,0xDA,0xF2,0x66,0xB6,0xBE,0xE0,0xFE,0xE6};
      
      void main(){
              
      //ポートA入力
       set_tris_a(0xFF);
        
       setup_adc_ports(sAN0);
       setup_adc(ADC_clock_div_32);
       set_adc_channel(0);
       
       set_tris_b(0x01); //ポートB7bit出力
       output_b(0xFF);
      
       setup_ccp1(CCP_PWM); //RB0にPWM信号生成
       
       //タイマー2の周期を設定
       //内部クロック 、最後の1は割り込み間隔
        prd=100; 
       setup_timer_2(T2_DIV_BY_1,prd,1);//pwm周期設定:100u
       
       while(1){
        
           vd = read_adc()/20; //明るさを取得、
           
           if(vd > prd/2 ) vd = prd/2;//
           
           vs = vd/3;//表示する値 0..9
           if(vs>9) vs=9;
           output_b(~segment_data[vs]);//7セグ出力
           
           set_pwm1_duty(prd-(int)vd);//pwmの幅設定
            
           delay_ms(100);
       
       }
      
      }