これで8/16ビットタイマーと割り込みの使い方はマスターしたが、実際に動作するサーボを目にできないのではつまらない。もちろんシミュレータ上でレジスタを書き換えればパルス幅が変わることを確認できるが、サーボが動くさまをみることができない。ボタンスイッチでパラメータを移動することもできるが、今ひとつ実感がわきそうもないので、アナログ入力を使うことにしよう。
PIC12F675には4つの10ビットアナログ入力が用意されている。ここでは入力装置として普通のボリュームを使う。
ボリュームは回転角と抵抗値の変化の度合いが複数規定されているが、ここではBカーブと呼ばれる「回転角と抵抗値が比例関係」のものを使う。両端にGNDと5ボルトを接続し、中間の煽動子をPICのアナログ入力に接続すれば回転角に応じたGNDから5ボルトの連続信号が得られるはずだ。PICのアナログ入力インピーダンスは10Kオームとなっているので、これが無視できる500オームのボリュームを千石電商で購入した(ツマミつきで100円)。
買ってきた抵抗。ブレッドボードに差すために抵抗の足を半田付けしてある
次はAD変換の使い方を確認しよう。(日本語ドキュメントのある)PIC12CE67XにもAD変換がついているのでこれを参照、と言いたいところだがこちらは8ビット入力。PIC12F675は10ビット入力なので仕様が異なる。
AD変換は色々と設定項目が多い。まず、アナログ4入力と言っても内部切替式なので、入力を切り替える設定が必要だ。次に基準電圧を内部(Vcc)か外部(Vref)にするか設定する。また出力は10ビットでレジスタ2つに出力されるが、これを右詰めにするか左詰めにするか指定できる。
これらはADCON0レジスタ($1F)で設定する。今回は8ビットで十分なので左詰めにして、上位のデータのみを読み出すことにする。また、I/Oピンをアナログ入力に設定するレジスタANSEL($9F)も設定が必要だ。ANSELレジスタはほかにAD変換の基準周波数の設定を行う。内部の4MHzクロックで動作させている場合8TOSCの設定にするのが適切だ。
さらにAD変換は1命令で結果が出ない。まずは内部で基準電圧を作って比較する作業を入力ビットの数だけ繰り返す必要がある。そして測定データを入力する時間も必要だ。具体的にはANSELでアナログ入力を行うピンと測定用周波数設定を行い、ADCON0で桁詰め、基準電圧、入力ピンの選択とAD変換機能のONを行ってから一定時間待ち(約20マイクロ秒)、その後ADCON0の第1ビット(GO)を1にする。
第1ビットが0になったらAD変換終了で、結果がADRESH($1E)とADRESL($9E)に入る。第1ビットが0になるまでプログラムで待たずに割り込みを使う方法もあるが、今回はプログラムでチェックを入れることにする。
その辺を加味して作ったのが以下のルーチンだ。AD変換を開始したら終了までループで待っている。本当はエラー処理をしないといけないがいまのところ省略している。また、精密な測定をおこなうためにはノイズ源となるデジタル動作はジャマになる。AD変換を開始したらCPUを休ませる、ということもできるが、今回は8ビット精度で十分ということもあってそのままループ命令で終了チェックすることにした。
これを2つの入力に対しておこなう。AD変換を頻繁に行っても意味がないのでサーボパルスを作ってからAD変換を行うことにした。データの入力から20ミリ秒遅れるが、気にしなくてよいだろう。
;
; RCサーボの制御テスト 16bit timer version
; AN0とAN1からデータを入力してその位置にサーボ移動
;
▼▼ |
LIST P=PIC12F675 |
▼▼ |
INCLUDE "P12F675.INC" |
__CONFIG _INTRC_OSC_NOCLKOUT & _WDT_OFF &_MCLRE_OFF |
;
;
;
▼▼ RC1POS |
EQU |
20H |
RC2POSEQU 21H |
▼▼ COUNT |
EQU |
|
22H |
▼▼ WBUF |
EQU |
|
23H |
SBUFEQU24H |
▼▼ |
ORG |
|
0 |
▼▼ |
GOTO |
MAIN |
ORG4 |
INTMAIN
▼▼ |
MOVWF |
WBUF |
|
; レジスタ類退避 |
▼▼ |
SWAPF |
STATUS,W |
▼▼ |
BCF |
|
STATUS,RP0 |
MOVWFSBUF |
;
▼▼ |
MOVLW |
0EBH |
|
; タイマー再セット |
▼▼ |
MOVWF |
TMR1L |
▼▼ |
MOVLW |
0B1H |
▼▼ |
MOVWF |
TMR1H |
BCFPIR1,TMR1IF |
;
RC1SET
▼▼ |
MOVF |
RC1POS,W |
|
; サーボ1のデータを送る |
▼▼ |
BTFSC |
STATUS,Z |
|
; データが0なら送らない |
▼▼ |
GOTO |
RC2SET |
|
|
; 中心値128 1-128-255 |
▼▼ |
BSF |
|
GPIO,GPIO5 |
▼▼ ; |
MOVLW |
0F6H |
|
|
; 01:0991 80:1499 FF:2007 F6 N1 N1 |
▼▼ ; |
MOVLW |
0D6H |
|
|
; 01:0864 80:1499 FF:2134 D6 N1 N2 |
▼▼ |
MOVLW |
0B6H |
|
|
; 01:0737 80:1499 FF:2261 B6 N1 N3 |
MOVWFCOUNT |
INTLP1
▼▼ |
NOP |
▼▼ |
DECFSZ |
COUNT,F |
▼▼ |
GOTO |
INTLP1 |
▼▼ |
MOVF |
RC1POS,W |
MOVWFCOUNT |
INTLP2
▼▼ |
NOP |
▼▼ |
NOP |
▼▼ |
NOP |
▼▼ |
DECFSZ |
COUNT,F |
▼▼ |
GOTO |
INTLP2 |
BCFGPIO,GPIO5 |
RC2SET
▼▼ |
MOVF |
RC2POS,W |
|
; サーボ2のデータを送る |
▼▼ |
BTFSC |
STATUS,Z |
|
; データが0なら送らない |
▼▼ |
GOTO |
RCSETE |
|
|
; 中心値128 1-128-255 |
▼▼ |
BSF |
|
GPIO,GPIO4 |
▼▼ ; |
MOVLW |
0F6H |
|
|
; 01:0991 80:1499 FF:2007 F6 N1 N1 |
▼▼ ; |
MOVLW |
0D6H |
|
|
; 01:0864 80:1499 FF:2134 D6 N1 N2 |
▼▼ |
MOVLW |
0B6H |
|
|
; 01:0737 80:1499 FF:2261 B6 N1 N3 |
MOVWFCOUNT |
INTLP3
▼▼ |
NOP |
▼▼ |
DECFSZ |
COUNT,F |
▼▼ |
GOTO |
INTLP3 |
▼▼ |
MOVF |
RC2POS,W |
MOVWFCOUNT |
INTLP4
▼▼ |
NOP |
▼▼ |
NOP |
▼▼ |
NOP |
▼▼ |
DECFSZ |
COUNT,F |
▼▼ |
GOTO |
INTLP4 |
BCFGPIO,GPIO4 |
RCSETE
;
; アナログ入力
▼▼ ; |
AN0 -> RC1POS |
;AN1 -> RC2POS |
; A/Dエラーの場合は前データを引き継ぎ
;
▼▼ |
MOVLW |
B'00000001' |
; AN0設定 |
▼▼ |
MOVWF |
ADCON0 |
▼▼ |
MOVLW |
6 |
MOVWFCOUNT |
LPAN01
▼▼ |
DECFSZ |
COUNT,F |
▼▼ |
GOTO |
LPAN01 |
|
; セットタイム待ち |
▼▼ |
BSF |
|
ADCON0,GO |
▼▼ |
MOVLW |
20H |
MOVWFCOUNT |
LPAN02
▼▼ |
BTFSS |
ADCON0,GO |
▼▼ |
GOTO |
LPAN03 |
▼▼ |
DECFSZ |
COUNT,F |
▼▼ |
GOTO |
LPAN02 |
▼▼ |
MOVF |
RC1POS,W |
GOTOLPAN04 |
LPAN03
LPAN04
;
▼▼ |
BSF |
|
ADCON0,CHS0 |
; AN1設定 |
▼▼ |
MOVLW |
6 |
MOVWFCOUNT |
LPAN11
▼▼ |
DECFSZ |
COUNT,F |
▼▼ |
GOTO |
LPAN11 |
|
; セットタイム待ち |
▼▼ |
BSF |
|
ADCON0,GO |
▼▼ |
MOVLW |
20H |
MOVWFCOUNT |
LPAN12
▼▼ |
BTFSS |
ADCON0,GO |
▼▼ |
GOTO |
LPAN13 |
▼▼ |
DECFSZ |
COUNT,F |
▼▼ |
GOTO |
LPAN12 |
▼▼ |
MOVF |
RC2POS,W |
GOTOLPAN14 |
LPAN13
LPAN14
;
;
▼▼ |
SWAPF |
SBUF,W |
|
; レジスタ類復帰 |
▼▼ |
MOVWF |
STATUS |
▼▼ |
SWAPF |
WBUF,F |
▼▼ |
SWAPF |
WBUF,W |
▼▼ |
BSF |
|
INTCON,GIE |
; 割り込み再許可 |
RETFIE |
;
;
;
MAIN
▼▼ |
CLRWDT |
|
|
|
; 割り込み関連初期化 |
CALL3FFh; 内蔵オシレーター調整 |
▼▼ |
MOVWF |
OSCCAL |
▼▼ |
BCF |
|
STATUS,RP0 |
▼▼ |
MOVLW |
B'00000001' |
; TMR1 1:1 |
▼▼ |
MOVWF |
T1CON |
▼▼ |
MOVLW |
B'11000000' |
; GIE & PEIE |
▼▼ |
MOVWF |
INTCON |
▼▼ |
BSF |
|
STATUS,RP0 |
▼▼ |
MOVLW |
B'10000001' |
; EEIE & TMR1IE |
▼▼ |
MOVWF |
PIE1 |
▼▼ |
BCF |
|
STATUS,RP0 |
▼▼ |
MOVLW |
0F0H |
|
; タイマ初期化 20ms |
▼▼ |
MOVWF |
TMR1L |
▼▼ |
MOVLW |
0B1H |
MOVWFTMR1H |
;
▼▼ |
CLRF |
GPIO |
▼▼ |
MOVLW |
07h |
▼▼ |
MOVWF |
CMCON |
▼▼ |
BSF |
|
STATUS,RP0 ; Bank 1 へ切替 |
▼▼ ; |
CLRF |
TRISIO |
|
; 非アナログ初期設定 |
▼▼ ; |
CLRF |
ANSEL |
▼▼ |
MOVLW |
B'00001011' |
; アナログ入力用初期設定 |
▼▼ |
MOVWF |
TRISIO |
▼▼ |
MOVLW |
B'00010011' |
▼▼ |
MOVWF |
ANSEL |
|
; ポート0/1はanalog入力 |
▼▼ |
CLRF |
WPU |
BCFSTATUS,RP0 ; Bank 0 へ戻る |
;
▼▼ |
MOVLW |
080H |
|
; フェイルセーフ |
▼▼ |
MOVWF |
RC1POS |
MOVWFRC2POS |
MAIN01
今回から割り込みルーチンとメインルーチンの順番を変えている。リセット時にはプログラムが0番地から実行され、割り込みは4番地から実行される。割り込みを使わないプログラムの場合は0番地からメインルーチンを書いていたが、割り込みを使う場合は4番地から割り込みルーチンを書き、メインルーチンをその後ろに書いたほうが分かりやすいだろう。
シミュレーションではAD変換のところまでチェックしきれないので、適当なところで切り上げて実際に回路を組んでみることにした。今回もブレッドボードを使い、電源も外部の5ボルト電源だ。前回シリーズのLEDでは手持ちの電源を使ったと書いたが、秋月電子で小型のスイッチングレギュレーターとDCジャックが安く売っているので、これを使うのもよいだろう。
例によってブレッドボードで実験回路を組んでみる
ボリュームを回すとサーボが動くだろうか? レスポンスがやや悪いのはサーボそのものの速度が遅い(ハイレスポンスのサーボは高い)のと、入力からサーボ出力まで時間差があるためだ(AD変換とサーボ出力のルーチンを逆にすれば解決する)。ちなみに、ブレッドボードで試しているとボリュームが抜けることがある。そうなると入力が不安定になり、サーボが勝手に動くことが分かる。
これでサーボは動いたが、ボリュームを使うのはあくまでテスト用だ。本来の野望を実現するためにはシーケンス動作してくれないと困る。
Copyright © ITmedia, Inc. All Rights Reserved.