電子工作 13 - PICの割込み機能を試す

 

 概要

   BasicコンパイラであるPICBasic Pro (PBP)の割込み(interrupt)機能を PIC microcontroller で使うのは易しくない。割込みの概念が飲み込みにくいのと、プログラム作成そのものが結構難しい。とにかく、身近にある資料を参考に、回路を組み実験してみることにした。

 

16F84Aでの単純割込み

 今となってはいささか旧型になってしまったPICではあるが、割り込み機能がちゃんと備わっていて、それを実行させるプログラムも比較的分かりやすい。

 

 回路

以下のような回路を組んでみた。割込みを起こす入力としてPORTB.0 (RB0/INT)にSWを取付け、PORTB.1とPORTB.2に、それぞれLED(赤)とLED(緑)を取付け、出力とした。PORTBはプログラムで内部的に pullupした。

 


 


                   

 

プログラム

   以下に示すように、このプログラムは メインルーチン(Main)で LEDred(赤)を点灯させておいて、SWを押すと割込みがかかって、別の LEDgreen(緑)を点灯させる部分(myinterrupt以下)に飛び、LEDgreenを2回点灯させて、再びメインルーチンに戻る、というものである。割込みにはPBPの中のON INTERRUPT GOTO Label、DISABLE、RESUME、ENABLE というコマンドを使う。また、割込みの許可を与えたり、フラグの操作のため、INTCON レジスタもいじくる必要がある。ここで使った INTCON=$90(=%10010000)は, GIE (bit7) (全体割込み許可ビット)や RB0/INT (bit4)の割込み許可ビットを立てることを意味し、以後割込み待ちになる。もう一つのレジスタ OPTION_REG=$3f(=%00111111)では、bit7の"0"はPORTBのpullupを許可し、bit6の"0"は、SWからの 入力が falling edge(H -> L) の時、割込みが起こることを意味する(Interrupt on falling edge of RB0/INT pin)。

 

'****************** プログラミング備忘録 ******************** 

'Int16F84m.bas  Interrupt demo program for 16F84A 2006.11.28

'This program starts to blink LEDred. When an interrupt occurs by pressing

'SW1 the LEDgreen blinks twice and the program returns to the continuous

' blinking routine for LEDred.

 

 

'*************** ここからがプログラム本体 ***************

SW var PORTB.0       'SW is connected to PORTB.0 (input)

LEDred var PORTB.1    'LEDred is connected to PORTB.1 (output)

LEDgreen var PORTB.2  'LEDgreen is connected to PORTB.2 (output)

 

OPTION_REG=$3f   '=%00111111 - Enable PORTB pullups/falling edge interrupt

TRISB=%11110001  '"1" input while "0" output

 

On Interrupt Goto myinterrupt  'Jump to myinterrupt if interrupted

INTCON=$90    '=%10010000 - GIE and RB0/INT bits Enable

 

Low LEDgreen  'Erase LEDgreen

 

Main:                'Main routine to blink LED (Red)

High LEDred

Pause 500

Low LEDred

Pause 500

Goto Main

 

'Interrupt handler

DISABLE

 

myinterrupt:    ‘Interrupt routine to blink LED (Green)

High LEDgreen

Pause 500

Low LEDgreen

Pause 500

High LEDgreen

Pause 500

Low LEDgreen

Pause 500

 

INTCON.1=0  'INTF: RB0/INT External Interrupt Flag : This needs to be cleared

         ‘for the next interrupt.

 

RESUME

ENABLE

 

‘Note) AKI-Programmer's configuration:

      'FOSC - XT; WDTE - Enable; PWRTE - Disable

      'CP – Not Protected

 

 

 

16F84Aでの割込み、超低電流消費スリープモードへ(1)

  普段は消費電流ほぼゼロで眠っていて、割込みを行った時だけ必要な電流を消費して作業をこなすように、割込みをセットアップすることが出来る。このモードでは電池による駆動も可能である。

 

回路

  16F84AのPORTBの6本のPORTを使う。すなわち、PB0,PB1,PB2にLED1,LED2,LED3をそれぞれ取付け、また、PB4,PB5,PB6にSW1,SW2,SW3を取付ける。そして、各SWを押すと、対応するLEDが点灯する。それ以外の時間は、スリープモードに入り、電流消費が極端に低くなる。回路図の下に実験中の写真を示す。左下方に見えるのがSW1,SW2,SW3である。

 


      


      

プログラム

以下のようなプログラムで動作する。何といっても要の部分はラベル main 以下である。3つのSW1,SW2、SW3の入力待ちをしながら、入力があれば対応するLEDを点灯させ、無い場合にはNAPコマンド(NAP 7: 2.304 seconds 休止at 4 MHz)で、ハードウエアの一時休止モード(Sleep mode)に入り、main routine を繰返し続ける。この部分が、電流消費を極端に低くする。

 

 

'****************** プログラミング備忘録 ******************** 

'Int16F84a.bas (based on wakex2.bas from Melabs)

'This program demonstrates the waking on interrupt.

 

'*************** ここからがプログラム本体 ***************

SW1 var PORTB.4

SW2 var PORTB.5

SW3 var PORTB.6

 

LED1 var PORTB.0

LED2 var PORTB.1

LED3 var PORTB.2

 

INTCON.3=1  'RBIE: Enable RB4-RB7 interrupt bits whose change causes

                    'an interrupt (interrupt-on-change)

OPTION_REG=$7f  '=%01111111: bit 7 "0" means PORTB pullups

TRISB=%11111000 'PB7-PB3=input, PB2-PB0=output

 

main:

    PORTB=0   'Turn off all LEDs

 

     'Check the SW entry.    ‘Wait for SW entry

     IF SW1=0 Then LED1=1   'SW "0" means H -> L while LED "1" means LED ON..

     IF SW2=0 Then LED2=1 

     IF SW3=0 Then LED3=1

    

     INTCON.0=0  'Clear RBIF (RB4-RB7 state change interrupt flag) for the next interrupt

 

     NAP 7       'Go to sleep. When the watchdog timer is disabled, NAP won't wake up until an ‘interrupt occurs.

 

      Goto main

     

End

 

Note) 今回、AKI-Programmer で焼く時は、WDTはDisableでないといけない。

 

(興味深い発見)

SWを押すと、対応するLEDが点灯する。同時に2個 、3個のSWも押せ、対応する数だけ点灯させることができる。 実験ボードの電源部にテスター(METEX M-3850D)を直列に入れ、電流消費を測定してみた。以下がその結果である。

 

SWを押した時の電流消費

Mode

SW ON/OFF

Vdd=2.05V

Vdd=3.00V

Vdd=5.00V

1. SW1, 2 or 3

ON

   0.467 mA

1.709 mA

5.27 mA

2. SW1and SW2

ON

0.860

3.028

10.51

3. SW1, 2 and 3

ON

1.172

3.95

15.68

4. SW1, 2 and 3

OFF

<1 uA

<1 uA

<1 uA

 

上表より明らかなように、LED点灯時は、相応に電流を消費するが、非点灯時はその消費量が、1uA以下のように極端に低下することが分かる。それでもSW入力待ちルーチンが動作しているから驚きである。単三2本といった電池による駆動も、容易に可能のようだ。

 

16F84Aでの割込み、超低電流消費スリープモードへ(2)

    別の割込み方法が見つかった。回路は(1)と全く同じものを使う。SW1,SW2,SW3のいずれかを押すと、LED1,LED2,LED3が0.5秒間隔で連続点灯して終わり、その後 スリープモード (今回はアッセンブラ命令の @sleepを使用)に入り、次ぎのSW1,SW2,SW3からの状態変化入力(PB port change interrupt)待ちになる。この状態では、(1)の場合同様、消費電流は極端に低くなる (SW-OFFの状態で待つ時1mA以下、SW-ONの状態で待つ時180mA位。ちなみに、液晶付きリモコンFUNAI RC-7では待機時220mA、キーを押した時最大40mA位流れていた)。SWの一つを水銀SWに換えて試したところ、水銀玉がSWを導通させると、割込みがかかって、LEDs が点灯して消え、そして、水銀玉がSWを導通させている状態から導通を断つと、割込みがかかって、LEDs が点灯して消える。つまり、ポートの二つの状態変化(H -> L or L -> H)で割込みがかかるのである。

 

プログラム

'****************** プログラミング備忘録 ******************** 

'Int16F84d.bas New Interrupt Exp. 2006.12.22 using 16F84A

'Using exactly the same circuit as used in Int16F84a.bas

'Another PicBasic Pro program to demonstrate wake on interrupt.

'An assembler "@sleep" command was used.

'Power consumption is extremely low.

'Important! - DISABLE WDT (WATCHDOG TIMER) in Programming.

 

'In the program shown below, any change in any of SW1-3 (OFF->ON or ON->OFF)

'causes an interrupt and executes "My demo routine". After that, the program

'enters into the sleep mode, consuming an extremley low current, until another

'interrupt from any of SW1-3 wakes the system.

'Hg switch was tested. It worked in two ways (SW contact:OFF->ON or ON->OFF).

 

' The switches must be connected to PORTB, pins 4,5,6,

' or 7 in order to use the RB port change interrupt.

 

'Pin connection to SW1-3

'sw1 - PORTB.4

'sw2 - PORTB.5

'sw3 - PORTB.6

 

'Pin connection to LED1-3

'led1 - PORTB.0

'led2 - PORTB.1

'led3 - PORTB.2

 

''*************** ここからがプログラム本体 ***************

OPTION_REG=%01111111 'bit 7 "0" means PORTB pullups

TRISB=%11111000        'PB7-PB3=input, PB2-PB0=output

 

PORTB=0                 'Turn off all LEDs

 

INTCON.3 = 1               'Enable the RB port change interrupt

SleepLoop:

INTCON.0 = 0              'Clear RBIF (RB4-RB7 state change inter for the next interrupt

@Sleep                      'This is a key command

 

'*** My demo routine ***

High PORTB.0

Pause 500

Low PORTB.0

Pause 500

High PORTB.1

Pause 500

Low PORTB.1

Pause 500

High PORTB.2

Pause 500

Low PORTB.2

Pause 500

 

Goto SleepLoop

 

'Note: WDT must be DISABLED when burnt by Aki-Programmer.

 

 

12F629での割込み

 8ピンのPICの場合の割込みも試みた。ピンの数が少ないだけ各ピンが複数の目的に使われているので、16F84Aの場合に比べ、設定が複雑である。何しろ12F629/12F675の英文データシートは118ページもある。

 

回路

 以下の回路で、電源が入ると赤のLEDが点滅を始める。SWを押すと、割込みがかかり、別の割込みプログラムが動作を開始し、緑のLEDが明るさを変えながら点灯する。内部クロックを使ったので、外付けのセルロックはない。

 


 


プログラム

  このプルログラムでは、先ず、led、LEDmain に ポートGPIO.1、GPIO.4を割当て、iをbyteの型の変数と定義づける。 先ず、ラベル loop 以下のルーチンを繰返します。そして、割込みが入ると myint以下の割込みルーチンが実行される。 ここでは PWM (Pulse Width Modulation) 出力をDuty比を変えてLEDに出して、ゼロから次第に明るくなり、ピークに達したら、次第に暗くなり再びゼロになるように明るさを変化させます。これを12回繰返します。そして、また loop に戻ります。

 

'****************** プログラミング備忘録 ******************** 

'PWMF629.bas 2004.1.20 - 2006.11.29

'PWM and On Interrupt - EXPERIMENT in PICBasic Pro

'Let LEDmain (GPIO.4) blink. When interrupt occurred

'to GPIO.2 pin (or to GND), stop the above LEDmain blinking

'sequence and let start PWM sequence (brightness change

'in LED at GPIO.1) .

 

'Succeeded at last after many trial-error attempts.

 

'*************** ここからがプログラム本体 ***************

led         var  GPIO.1   'LEDint (Interrupt) on GPIO.1

LEDmain var  GPIO.4   'LEDmain on GPIO.4

i            var   byte        'i is variable

 

        TRISIO=%000100      'GPIO.2 (Interrupt) - input

        CMCON=7               'Disable Comparators.

        WPU=%00000100       'Weak Pull-Up on GPIO.2 only

        OPTION_REG.7=0      'Enable Weak Pull-Up's

        OPTION_REG.6=0      'Interrupt GPIO.2 on Falling Edge

       

        On Interrupt Goto myint    'Define interrupt handler

        INTCON = %10010000      'Enable GIE and INTE bits

        GPIO.1=0                       'Turn off LED at GPIO.1.       

        Enable

                               

loop:   High LEDmain       'Start of Main routine

        Pause   500           'Wait .5 seconds

        Low ledmain

        Pause   500

        High ledmain

        Pause   500

        Low ledmain

        Pause   500

        Goto loop          'Repeat the loop forever.

 

' Interrupt handler

        Disable             'No interrupts past this point       

                                

myint:  For i=1 to 10      ' Repeat 1 to 10

              

        PWM led, 5, 255      ' Turn on led with Duty Ratio 5 and

                                    ' Cycle 255 (repeat of 255 times)

                                    ' Brightness gradually increses at GPIO.1.      

        PWM led, 25, 255

        PWM led, 50, 255       

        PWM led, 75, 255

        PWM led, 100, 255

        PWM led, 125, 255

        PWM led, 150, 255

        PWM led, 175, 255

        PWM led, 200, 255

        PWM led, 225, 255

        PWM led, 255, 255       ' Highest LED's brightness

       

        PWM led, 225, 255       ' LED gradually becomes darker.

        PWM led, 200, 255

        PWM led, 175, 255

        PWM led, 150, 255

        PWM led, 125, 255

        PWM led, 100, 255

        PWM led, 75, 255

        PWM led, 50, 255

        PWM led, 25, 255

        PWM led, 5, 255         ' Least LED's brightness

        Next i

       

        INTCON.1=0      'Clear interrupt flag (INTF flag -

                               'this changes to "1" when an interrupt

                               'occurred. So needs to be cleared for

                               'succeeding interrupts).

        Resume           'Return to main program

        Enable

        End                                                      

 

'Note): AKI-Programmer's configuration:

 'FOSC – INTOSC (GP4:I/O); WDTE - Enable; PWRTE – Enable;

       'MCLR - Internal (so no MCLR-resistor-+Vdd connection

       'required!! ); BODEN - Enable; CD - Disable; CDP - Disable.

 

12F629のコンパレータを試してみる

割込みには関係ないが、12F629のコンパレータの使い方を試してみた。

実験は以下のような回路で行った。


    

12F629の中にはコンパレーラが1個入っている。入力電圧CIN- (GP1) を、プログラム設定できる内部標準電圧CVref と比較して、高いか、低いかによって、COUT (GP2) に、High(LED ON)またはLow (LED OFF) の信号を出すことができる。したがって、入力電圧のレベル検知に利用できる。今回は、サーミスタを半田ごてのヒータ部にグラスファイバー片を介して結びつけ、 ヒータの温度が上がると、ある点でLEDが点灯する回路を組んだ。このサーミスタは18°Cで3KW、180°Cで100W程度に抵抗値を変える。また、VRが入っているのでLEDが点灯するレベルも変えられる。

 

プログラム

このプログラムは簡単であるが、CMCONとVRCONレジスタの設定がポイントである。先ず、GPIO.1とGPIO.2をそれぞれ、Thermister 入力のThermINと、コンパレータ出力のLEDcmpOUTに割り当てる。 TRISIOはbit1(GPIO.1)を入力、他はすべて出力に設定する。CMCONのbit2-0の011は “CIN- ピンとコンパレータ基準電圧 CVref を比較し、コンパレータ出力結果COUTをGP2から出す”ということを意味し、bit4 の1 でコンパレータからの出力の反転させている。一方、VRCONレジスタはコンパレータ基準電圧を設定するもので、例えば、低電圧設定の場合、 (基準電圧)=(電源電圧)X(VRCONのVbit3-0の値)¸ 24 で計算する。 ここで電源電圧3V、VRCONのbit3-0の値として0111 (=7 in 15) を入れると、基準電圧は3X7¸24=0.625Vとなり、可変電圧範囲は0 - 0.625Vとなる。Bit5の1は, 低電圧設定に設定することを意味し、bit6はコンパレータ基準電圧の電源をONにすることを意味する。

 

'****************** プログラミング備忘録 ********************   

'CMPF629.bas 2006.12.4  OK!

'Exp. for comparator use. When the temp. exceeds a set value (adjustable by VR),

'LED lights up.

 

'*************** ここからがプログラム本体 ***************

ThermIN       var     GPIO.1   'Thermister INPUT

LEDcmpOUT  var     GPIO.2   'Comparator OUTPUT on GPIO.1

 

 

        TRISIO=%000010      'GP1=Thermister INPUT; GP2=Comparater OUTPUT

        CMCON=%00010011   'GP1/CN-pin=therm input; GP2/COUT=Comp.output

                                       'bit4=1 is Comp.output reversed.

        VRCON=%10100111   'Low voltage; VR3:VR0=0111 (5 in 15; CVref=0-0.63V)

       

   End                                                      

 

'Note:

   'Aki Programmer's configuration:

   'FOSC - INTOSC(GP4:I/O); WDTE - Enable; PWRTE - Disable

   'MCLR - Internal (so no MCLR-resistor-+Vdd connection

'required!! ); BODEN - Enable; CD - Disable; CDP - Disable.

 

半田ゴテの温度変化の状態を2個のLEDで表示する回路

今度は、同じ8ピンでADCの付いた12F675 を使って、いつも気になっていた半田ゴテの温度をLEDで大まかに表示する回路を組んでみることにした。尚、ここでは「割込み」は使っていない。

 

回路

  下図のように、先ず、サーミスタを半田ゴテのヒータ部に、ガラスファイバー布片を介して固定し、温度変化を抵抗変化に変換する。この変化を分圧回路で電圧変化に変える。それを12F675 の GP0 ピンにアナログ値として導入し、プログラムでアナログからデジタル値に変換し、GP1 (緑のLED) とGP2 (赤のLED) の両ピンから出力し表示する。アナログ入力値のレンジは、VRである程度変えられる。半田ゴテ(大洋電機産業製TQ-80)は、その先端部が約6分で300°C、約10分で310°C程度の最高温度に達し、その時、検温部のサーミスタは175-180°Cになる。

 

使用したサーミスタ: 3K at 18°C <--> 100 ohm at 175 - 180°C

 


写真1は使用したサーミスタ(秋葉原の鈴商で入手)の外観を示す。写真2では、このサーミスタをグラスファイバー布片に包んで半田ゴテのヒータ部に固定されている様子、さらに、コテの把手の先端に、コテの焼け具合、つまり温度の高低を表示する緑と赤のLEDが取付てあるのが分かる。写真3はそれらの発光を制御するPICボードである。VR(可変抵抗、20T)一個、固定抵抗2本、PIC12F675一個のみからなる。電源は手元にあったDC4Vを出力するACアダプタを使ってみた。電源Vddをアナログーデジタル変換の基準電圧に設定してあるので、5Vの代りに4Vを電源にすると、LEDの点灯、点滅の境界電圧(温度境界)が多少変化するが、VRで調整できる。

写真1                            写真2


 写真3


 


プログラム

  下にフローチャートとプログラムリストを示す。両者から分かるように、アナログーデジタル 変換を行うので初期設定が結構複雑であるが、ラベルStartから始まる本体プログラム部分はいたって明快単純である。フローチャートでは If 800>Vin>650 Then xxxとしてあるが、実際のプログラムではIF (800>Vin) and (Vin>650) Then xxx と書いた。

 


 

 


'****************** プログラミング備忘録 *******************

'SLD675.bas (SLD=soldering iron) Soldering iron temp. indicator 2006.12.8

'Pin 1: Vdd (5.0V)

'Pin 8: Vss (GND)

'Pin 7: GPIO.0: input - connected to (3K-thermistor + 2K-VR (0-5V))

                   'between Vdd and Vss

'Pin 6: GPIO.1: output - connected to Green LED  via 560 ohm resistor                       

'Pin 5: GPIO.2: output - connected to Red LED via 560 ohm resistor

 

'*************** ここからがプログラム本体 ***************

ADCON0=%10000011  '=131 (decimal)

'bit7=1(right justified), bit6=0 (Vdd used as volt reference for ADC),

'bit5-4=0 (unimplemented), bit3-2=00 (channel0-An0),

'bit1=1(starts right away), bit0=1(on operation?)

 

ANSEL=%00010001   '=17 (decimal), sets Fosc/8 and GPIO.0 as ADC input

'bit7=0 (unimplemented), bit6-4=001 (Fosc/8?),

'bit3-2-1=0 (Digtal I/O pins), bit0=1(Analog input)

 

TRISIO=%00000001  'GPIO.0 (thermistor input), GPIO.1 and 2 (LEDGreen and LEDRed)

CMCON=7         'turns off comparator function

 

Define ADC_BITS 10     '10-bit ADC used

Define ADC_CLOCK 3     'Internal clock used

Define ADC_SAMPLEUS 50 'Polling is unnecessary due to use of ADCIN command

 

Vin      var word

LEDGreen var GPIO.1

LEDRed   var GPIO.2

 

Start:

LEDGreen=0

LEDRed=0

 

 

ADCIN 0, Vin 'Read Vin from GPIO.0 (AN0).        

 

If Vin>800 Then Goto LEDGstatic            

If (800>Vin) and (Vin>650) Then Goto LEDGblink

If (650>Vin) and (Vin>450) Then Goto LEDRblink

If Vin<450 then Goto LEDRstatic

 

LEDGstatic:

High LEDGreen

Pause 200

Goto Start

 

LEDGblink:

High LEDGreen

Pause 200

Low LEDGreen

Pause 200

Goto Start

 

LEDRblink:

High LEDRed

Pause 200

Low LEDRed

Pause 200

Goto Start

 

LEDRstatic:

High LEDRed

pause 200

Goto Start

 

End

 

‘Note) Aki-Programmer’s configuration:

      ‘FOSC – INTSC (GP4:I/O), WDTE - Enable, PWRTE – Disable, MCLRE – Internal, ‘CP/CPD – Not_Protect

 

半田ゴテに電源を入れてから、PICのGP0に入るサーミスタからの電圧、経過時間、それと、緑と赤2つLEDの変化の関係を見てみた。下表のようになった。ここではVddとして5V、VRは適当に調整して室温約18℃で、GP0に4.18Vが加わるようにした。

 

経過時間 (m : sec)

GP0への入力電圧(V)

LEDの色、モード変化*

0:0

¯

4.18

¯

Green static

¯

1:20

¯

3.80

¯

Green blink

¯

2:10

¯

3.13

¯

Red blink

¯

3:32

¯

2.18

¯

Red static

¯

5:00

1:68

¯

8:00

1.04

¯

12:00

0.83

Red static

* static – 点灯、blink – 点滅

 

考察

1)     LEDの点灯、点滅の境界値 800, 650, 450は適当に決めたが、大体予想した通りの表示をすることが分かった。設定は自由にできる。

2)     もともと、火傷をしないように、半田ゴテが熱いのか、冷たいのか、を知るのが目的だったので、その機能は果たせると思う。

3)     やっばり、PIC用の電源のACアダプタが邪魔。「割込み」を使い、無しで済ませるようにするのが次ぎの課題である。

 

割込みの応用例

目下、検討中です。

 

Menu Pageへ戻る    

Topへ戻る