目次
前
次
Renesas対策再考
試走を続けながら、部屋を片付けているとトランジスタ技術の
付録になったR8C/Tinyを実装した基板が出てきました。
他にETで配布されていたオークス電子のR8C/Tiny基板も発掘されました。
同じR8C/Tinyでも、オークス電子の基板は11/12シリーズで
I/Oピンが多く、8ビットのタイマーもひとつ多くなって
います。
正方形の基板にマイコンチップが実装されているので
手半田の基板を作ったとしても使いやすい基板を実現
できそうです。
センサーは、Game Boy Cameraに接続したArduinoが担当しているので
4ビットで与えられる路面情報を利用するなら、R8C/Tinyを利用して
マシンを移動させてもよいのではないのかと思うようになりました。
R8C/Tinyはピン数が少ないので、MCR_VCマシンを動かすためには
どのくらいのピン数があればよいかを見直しします。
- 路面情報取得 4ビット
- 路面情報表示 8ビット程度
- モータドライバ 3ビット(DCモータx2、サーボモータx1)
- スタートトリガー 1ビット
- 内部情報表示 8ビット程度
32ピンのR8C/Tinyでも対応できそうなので、もう少し考えます。
R8C/Tinyの11/12シリーズには、8ビットタイマーはX、Y、Zの3種。
16ビットタイマーはCで1種。これらのタイマーをすべて利用すると
モータに関係するPWM波形生成は、簡単でしょう。
GPIOを利用したいとすれば、次のピンを利用できます。
- ポート0 8ビット
- ポート1 8ビット
- ポート3 3ビット
- ポート4 スタートトリガー 1ビット
データシートにあるピン配置を眺めてみます。
ピン配置から、次のように考えました。
路面情報は、ポート1の下位4ビットで入力。
ポート3は2、3ビットをDCモータ制御出力。
ポート3の7ビットをサーボモータ制御出力。
ポート1の7ビットをスタートゲートの状態入力。
路面情報は、ポート0をデータバスとして外部レジスタに渡す。
路面情報表示に、ポート3の0ビットを出力で利用。
内部情報は、ポート0をデータバスとして外部レジスタに渡す。
内部情報表示に、ポート3の1ビットを出力で利用。
制御基板は以下のものがあるので、ワイヤーコネクションを
変えて対応。
R8C/Tinyは、MITSUBISHIのM16CをCPUコアにもつので
I/O部分の修正程度で、ファームウエアを完成させる
ことが可能でした。
C言語を動かす前に必要なスタートアップルーチンでは
I/Oの入出力と初期値およびタイマー関係の設定を記述
すれば、H8、ArduinoのCソースを活用できます。
メインプロセッサにR8C/Tinyを利用するので、Renesas
が後援する大会に出しても、規定に抵触することはない
と判断しました。
ファームウエア
R8C/Tinyを使う場合、最も頭を悩ませたのは、クロック
処理でした。3つのクロックソースがあるのはよいです
が、関連するレジスタが散在し、設定するビットがバラ
けていて、日立のデータシートに勝るとも劣らないほど
わかりにくかったです。
クロックは、次のように指定しました。
INIT_CLK:
; disable PROTECT
bset #0,PRCR0
; change Xin,Xout
bset #3,CM1
; enable Xin,Xout
bclr #5,CM0
; no divide
bclr #6,CM0
; wait stable
nop
nop
nop
nop
; select main clock
bclr #2,OCD
; enable PROTECT
bclr #0,PRCR0
;
rts
ポートの入出力と初期値を指定。
(ポート1の下位ニブルは、プルアップ。)
INIT_IO:
; port values
mov.b #00h,P0 ; port_0 value
mov.b #00h,P1 ; port_1 value
mov.b #00h,P3 ; port_3 value
; port directions
mov.b #0FFh,PD0 ; port_0 direction
mov.b #050h,PD1 ; port_1 direction
mov.b #0FFh,PD3 ; port_3 direction
; P1 lower nibble pull up
bset #2,PUR0
;
rts
システムタイマーを1ms周期として、タイマーZを利用。
タイマーZのブロック図は、以下。
8ビットプリスケーラと8ビットカウンタを使い、次の
4モードが可能となっています。
- タイマー
- プログラマブル波形生成
- プログラマブルワンショット
- プログラマブルウエイトワンショット
システムタイマーをインクリメントするだけなので
タイマーモードを使うことに。
プリスケーラに入力するクロックソースは、以下。
- f1(20MHz)
- f8(2.5MHz)
- Timer Y underflow
- f2(10MHz)
1msは1kHzなので、プリスケーラとカウンタの値を
組み合わせてみると、以下。
40x250=10,000
80x250=20,000
プリスケーラで250分周し、カウンタで40か80分周すると
f1、f2を利用できるとわかります。
利用するレジスタは、TYZMR、TZPR、PREZとなります。
初期化は以下。
INIT_TZ:
; select clock f1
and.b #11001111b,TCSS
; select timer Z mode
and.b #10001111b,TYZMR
; prescalar
mov.b #39,PREZ
; divider
mov.b #79,TZPR
; enable interrupt
mov.b #00001001b,TZIC
; start timer Z
bset #7,TYZMR
;
rts
1msごとにイベントフラグのセットとカウンタを
インクリメントします。
INT_TZ:
; set flag
bset #0,FLAGS
; increment
inc.w TIMCNT
;
reit
割込みを使う場合、ヴェクター方式なので次のように
ROMエリアの一部に展開していきます。
dummy_int:
reit
.section VVECTOR,ROMDATA
.org 0FF00h
VVECTOR_ADR:
.lword dummy_int ; vector 0 (BRK)
.lword dummy_int ; vector 1 (reserved)
.lword dummy_int ; vector 2 (reserved)
.lword dummy_int ; vector 3 (reserved)
.lword dummy_int ; vector 4 (reserved)
.lword dummy_int ; vector 5 (reserved)
.lword dummy_int ; vector 6 (reserved)
.lword dummy_int ; vector 7 (reserved)
.lword dummy_int ; vector 8 (reserved)
.lword dummy_int ; vector 9 (reserved)
.lword dummy_int ; vector 10(reserved)
.lword dummy_int ; vector 11(reserved)
.lword dummy_int ; vector 12(reserved)
.lword dummy_int ; vector 13(Key Input)
.lword dummy_int ; vector 14(A/D converter)
.lword dummy_int ; vector 15(reserved)
.lword dummy_int ; vector 16(Timer C compare 1)
.lword dummy_int ; vector 17(UART0 transmitt)
.lword dummy_int ; vector 18(UART0 receive)
.lword dummy_int ; vector 19(UART1 transmitt)
.lword dummy_int ; vector 20(UART0 receive)
.lword dummy_int ; vector 21(INT2 interrupt)
.lword dummy_int ; vector 22(Timer X interrupt)
.lword dummy_int ; vector 23(Timer Y interrupt)
.lword INT_TZ ; vector 24(Timer Z interrupt)
.lword dummy_int ; vector 25(INT1 interrupt)
.lword dummy_int ; vector 26(INT3 interrupt)
.lword dummy_int ; vector 27(Timer C overflow)
.lword dummy_int ; vector 28(Timer C compare 0)
.lword dummy_int ; vector 29(INT0 interrupt)
.lword dummy_int ; vector 30(reserved)
.lword dummy_int ; vector 31(reserved)
.lword dummy_int ; vector 32
.lword dummy_int ; vector 33
.lword dummy_int ; vector 34
.lword dummy_int ; vector 35
.lword dummy_int ; vector 36
.lword dummy_int ; vector 37
.lword dummy_int ; vector 38
.lword dummy_int ; vector 39
.lword dummy_int ; vector 40
.lword dummy_int ; vector 41
.lword dummy_int ; vector 42
.lword dummy_int ; vector 43
.lword dummy_int ; vector 44
.lword dummy_int ; vector 45
.lword dummy_int ; vector 46
.lword dummy_int ; vector 47
.lword dummy_int ; vector 48
.lword dummy_int ; vector 49
.lword dummy_int ; vector 50
.lword dummy_int ; vector 51
.lword dummy_int ; vector 52
.lword dummy_int ; vector 53
.lword dummy_int ; vector 54
MCR_VCマシンでは、DCモータとサーボモータを利用するので
1msか100us周期のタイマー割込みでカウンタをインクリメント
して、DUTY比かパルス幅を指定します。
DCモータでは、カウンタを0から100の範囲で動かし、指定した
DUTY比(0から99)と比較して、論理値の0か1を出力します。
DCモータを制御するためのPWM波形を、タイマーXを利用して
生成することを考えました。
カウンタをDCNTとして、タイマーのアンダーフローごとに+1し
100になったときに、0に戻るカラクリで、回します。
このカラクリを記述すると、以下。
INT_X:
; store
pushm R0,R1
; impress (copy counter values)
mov.b DCNT,R1L
mov.b R1L,R1H
mov.b RXDUTY,R0L
mov.b LXDUTY,R0H
; impress ( RXDUTY > DCNT )
cmp.b R0L,R1L
jlt INT_X0
bclr #2,P3
jmp INT_X1
INT_X0:
bset #2,P3
INT_X1:
; impress ( LXDUTY > DCNT )
cmp.b R0H,R1H
jlt INT_X2
bclr #3,P3
jmp INT_X3
INT_X2:
bset #3,P3
INT_X3:
; increment
inc.b DCNT
; get value
mov.b DCNT,R1H
; compare
cmp.b #100,R1H
; if R1H = 100 , clear
jne INT_X4
mov.b #0,DCNT
; copy
mov.b RDUTY,RXDUTY
mov.b LDUTY,LXDUTY
INT_X4:
; resume
popm R0,R1
; return
reit
タイマーXを利用すると仮定して、コードを書いたので
割込み周期を1msとしてみます。
タイマーXのブロック図を見て、初期化処理を考えます。
1msを生成するには、タイマーXのプリスケーラ、カウンタを
利用してf1(20MHz)あるいはf2(10MHz)を分周。またモードは
タイマーモードで利用すれば、充分でしょう。
この仕様を、アセンブリ言語コードにすると、以下。
INIT_TX:
; select clock f1
and.b #11111100b,TCSS
; select timer X mode
and.b #11111100b,TXMR
; prescalar
mov.b #39,PREX
; divider
mov.b #79,TX
; enable interrupt
mov.b #00001001b,TXIC
; start timer X
bset #3,TXMR
;
rts
タイマーXの割込みを使うので、ヴェクターにアドレスを指定。
.section VVECTOR,ROMDATA
.org 0FF00h
VVECTOR_ADR:
.lword dummy_int ; vector 0 (BRK)
.lword dummy_int ; vector 1 (reserved)
.lword dummy_int ; vector 2 (reserved)
.lword dummy_int ; vector 3 (reserved)
.lword dummy_int ; vector 4 (reserved)
.lword dummy_int ; vector 5 (reserved)
.lword dummy_int ; vector 6 (reserved)
.lword dummy_int ; vector 7 (reserved)
.lword dummy_int ; vector 8 (reserved)
.lword dummy_int ; vector 9 (reserved)
.lword dummy_int ; vector 10(reserved)
.lword dummy_int ; vector 11(reserved)
.lword dummy_int ; vector 12(reserved)
.lword dummy_int ; vector 13(Key Input)
.lword dummy_int ; vector 14(A/D converter)
.lword dummy_int ; vector 15(reserved)
.lword dummy_int ; vector 16(Timer C compare 1)
.lword dummy_int ; vector 17(UART0 transmitt)
.lword dummy_int ; vector 18(UART0 receive)
.lword dummy_int ; vector 19(UART1 transmitt)
.lword dummy_int ; vector 20(UART0 receive)
.lword dummy_int ; vector 21(INT2 interrupt)
.lword INT_TX ; vector 22(Timer X interrupt)
.lword dummy_int ; vector 23(Timer Y interrupt)
.lword INT_TZ ; vector 24(Timer Z interrupt)
.lword dummy_int ; vector 25(INT1 interrupt)
.lword dummy_int ; vector 26(INT3 interrupt)
.lword dummy_int ; vector 27(Timer C overflow)
.lword dummy_int ; vector 28(Timer C compare 0)
.lword dummy_int ; vector 29(INT0 interrupt)
.lword dummy_int ; vector 30(reserved)
.lword dummy_int ; vector 31(reserved)
サーボモータの角度をタイマーYを使って、制御します。
サーボモータは、20mの周期で、パルス幅を1msから2msに
変化させると、角度が追従する仕様となっています。
カウンタを0から2000まで変化させて、2000になったなら
0に戻し、カウンタを10usごとにインクリメントすること
を考えます。
カウンタをSCNTとして、タイマーのアンダーフローごとに+1し
2000になったときに、0に戻るカラクリで、回します。
このカラクリを記述すると、以下。
INT_Y:
; store
pushm R0,R1
; impress (copy counter values)
mov.w SCNT,R1
mov.w SXVAL,R0
; impress ( SXVAL > SCNT )
cmp.w R0,R1
jlt INT_Y0
bclr #7,P3
jmp INT_Y1
INT_Y0:
bset #7,P3
INT_Y1:
; increment
inc.w SCNT
; get value
mov.w SCNT,R1
; compare
cmp.b #2000,R1
; if R1 = 2000 , clear
jne INT_Y2
mov.w #0,SCNT
; copy
mov.w SVAL,SXVAL
INT_Y2:
; resume
popm R0,R1
; return
reit
タイマーYを利用すると仮定して、コードを書いたので
割込み周期を1msとしてみます。
タイマーYのブロック図を見て、初期化処理を考えます。
10usは100kHzなので、f1(20MHz)を使うとすれば、プリスケーラ
とカウンタの値の組み合わせは、以下。
200 = 100x2 = 50x4 = 25x8
プリスケーラで25分周し、カウンタで8分周すると
f1を利用できるとわかります。
利用するレジスタは、TYZMR、TYPR、PREYとなります。
初期化は以下。
INIT_TY:
; select clock f1
and.b #11110011b,TCSS
; select timer Y mode
and.b #11111000b,TYZMR
; prescalar
mov.b #24,PREY
; divider
mov.b #7,TYPR
; enable interrupt
mov.b #00001001b,TYIC
; start timer Y
bset #3,TYZMR
;
rts
タイマーYの割込みを使うので、ヴェクターにアドレスを指定。
.section VVECTOR,ROMDATA
.org 0FF00h
VVECTOR_ADR:
.lword dummy_int ; vector 0 (BRK)
.lword dummy_int ; vector 1 (reserved)
.lword dummy_int ; vector 2 (reserved)
.lword dummy_int ; vector 3 (reserved)
.lword dummy_int ; vector 4 (reserved)
.lword dummy_int ; vector 5 (reserved)
.lword dummy_int ; vector 6 (reserved)
.lword dummy_int ; vector 7 (reserved)
.lword dummy_int ; vector 8 (reserved)
.lword dummy_int ; vector 9 (reserved)
.lword dummy_int ; vector 10(reserved)
.lword dummy_int ; vector 11(reserved)
.lword dummy_int ; vector 12(reserved)
.lword dummy_int ; vector 13(Key Input)
.lword dummy_int ; vector 14(A/D converter)
.lword dummy_int ; vector 15(reserved)
.lword dummy_int ; vector 16(Timer C compare 1)
.lword dummy_int ; vector 17(UART0 transmitt)
.lword dummy_int ; vector 18(UART0 receive)
.lword dummy_int ; vector 19(UART1 transmitt)
.lword dummy_int ; vector 20(UART0 receive)
.lword dummy_int ; vector 21(INT2 interrupt)
.lword INT_TX ; vector 22(Timer X interrupt)
.lword INT_TY ; vector 23(Timer Y interrupt)
.lword INT_TZ ; vector 24(Timer Z interrupt)
.lword dummy_int ; vector 25(INT1 interrupt)
.lword dummy_int ; vector 26(INT3 interrupt)
.lword dummy_int ; vector 27(Timer C overflow)
.lword dummy_int ; vector 28(Timer C compare 0)
.lword dummy_int ; vector 29(INT0 interrupt)
.lword dummy_int ; vector 30(reserved)
.lword dummy_int ; vector 31(reserved)
2種のモータのために、値の初期化が必要。
DCモータのDUTY比は0%として、回転なしにしておき
サーボモータへは、1.5msのパルス幅を与えるように
指定します。
INIT_VAL:
; clear
mov.w #0,R0
mov.w #0,R1
; DC motor
mov.b R0L,DCNT
mov.b R0H,LDUTY
mov.b R1L,RDUTY
mov.b R1H,LXDUTY
mov.b R0L,RXDUTY
; servo motor
mov.w R0,SCNT
mov.w #150,SVAL
mov.w #150,SXVAL
;
rts
モータを使うには、パラメータを変更できるように
サブルーチンを用意。APIを設計しているので、2種
のモータのパラメータ変更用サブルーチン名は、以下
のように決めます。
SEND_DC
SEND_SERVO
アセンブリ言語では、パラメータを渡すときにレジスタ
を利用するのが、お約束なので2種のAPIは次のように
レジスタR0を使うことに。
SEND_DC:
; store
push.w R0
; put left duty ratio
mov.b R0H,LDUTY
; put right duty ratio
mov.b R0L,RDUTY
; resume
pop.w R0
;
rts
SEND_SERVO:
; store
push.w R0
; put parameter
mov.w R0,SVAL
;
rts
DCモータのDUTY比は、左右で各8ビットなので
レジスタR0が16ビットという特徴を利用。
モータの制御に関係する階層構造は、以下。
スタートゲートの状態を監視するためのサブルーチンを
用意して、処理を単純にします。ポート1の7ビットを
スタートゲートのOPENED、CLOSEDに割り当てると以下の
処理でよいはず。
GET_START:
; store
push.w R1
; default (clear flag)
bclr #2,FLAGS
; get start gate state
mov.b P1,R1H
; judge
btst #7,R1H
jeq GET_START1
bset #2,FLAGS
GET_START1:
; resume
pop.w R1
;
rts
スタートゲートの状態を監視して、OPENEDになった
ならば、フラグをセット。状態フラグを使い、移動
処理を実行するのは、サブルーチンを使う側の問題
に変換してしまいます。
APIには時間待ちもあるので、これをサブルーチンで用意。
DELAY_MS:
; store
pushm R2,R3
; get now system timer count
mov.w TIMCNT,R3
; calculate
add.w R2,R3
; compare
DELAY_MS1:
cmp.w TIMCNT,R3
jlt DELAY_MS1
; resume
popm R2,R3
;
rts
レジスタR2に遅延したい時間のカウント値を格納して
サブルーチンコールします。
ライントレースするには、路面状態を取得しなければ
なりません。ポート1の下位ニブルからセンサー出力
の路面状態を取得。
GET_SENSOR:
; store
push.w R0
; set pattern
mov.b #00001111b,R0H
; get nibble from port 1
mov.b R1,R0L
; mask
add.b R0H,R0L
; store
mov.b R0L,ROAD
; resume
pop.w R0
;
rts
ここまでで、利用する変数が確定したので
まとめて、表形式に。
.section VARIABLE,DATA
.org 0400h
FLAGS: .blkb 1
ROAD: .blkb 1
TIMCNT: .blkw 1
DCNT: .blkb 1
LDUTY: .blkb 1
RDUTY: .blkb 1
LXDUTY: .blkb 1
RXDUTY: .blkb 1
SCNT: .blkw 1
SVAL: .blkw 1
SXVAL: .blkw 1
STATE: .blkb 1
内部状態を持っておいた方が、ファームウエアを
作成しやすいので、1バイトのSTATEを用意。
内部状態を指定するために、STATEを用意しますが
代入する値(状態値)を、次のように割当てます。
- STATE=0 WAIT start trigger
- STATE=1 NORMAL
- STATE=2 CRANK
- STATE=3 ROTATE
- STATE=4 LANE
- STATE=5 CHANGE
- STATE=6 BLIND
- STATE=7 TIME_UP
マイコンのファームウエアでは、状態値をみて
状態遷移をするか否かを決定します。
走行時は、以下の状態遷移に。
NORMAL -> CRANK -> ROTATE -> NORMAL
NORMAL -> LANE -> CHANGE -> BLIND -> NORMAL
スタートトリガーを使い、STATE=0からSTATE=1に
遷移させます。また、タイムアップしたときには
STATE=10として、即座にSTATE=0に戻します。
路面と内部の状態をLEDで表示します。
8ビットのレジスタICを利用して、LEDを点灯、消灯
すれば簡単に実現できると判断。
回路図は以下。
路面と内部の状態は、ひとつのサブルーチンで一度に
表示できるようにします。
表に値を格納しておき、表から値を引き出して設定する
方法を採用。
DISPLAY:
; store
pushm A0,R0
; set pointer
mova RDTBL,A0
; calculate address
mov.b #0,R0H
mov.b ROAD,R0L
add.w R0,A0
; impress code
mov.b [A0],P0
; set trigger
bset #0,P3
; clear trigger
bclr #0,P3
; set pointer
mova STTBL,A0
; calculate address
mov.b #0,R0H
mov.b STATE,R0L
add.w R0,A0
; impress code
mov.b [A0],P0
; set trigger
bset #1,P3
; clear trigger
bclr #1,P3
; resume
popm A0,R0
;
rts
表に値を格納するサブルーチンは、以下。
RDTBL: .blkb 13
STTBL: .blkb 8
INIT_TABLE:
; store
push.w A0
; set pointer
mova RDTBL,A0
; ALL_BLACK
mov.b #0,[A0]
; ALL_WHITE
mov.b #0FFh,1:8[A0]
; LEFT_WHITE
mov.b #0F0h,2:8[A0]
; RIGHT_WHITE
mov.b #00Fh,3:8[A0]
; CENTER
mov.b #38h,4:8[A0]
; TINY_RIGHT
mov.b #0Ch,5:8[A0]
; RIGHT
mov.b #06h,6:8[A0]
; BIG_RIGHT
mov.b #03h,7:8[A0]
; TINY_LEFT
mov.b #30h,8:8[A0]
; LEFT
mov.b #60h,9:8[A0]
; BIG_LEFT
mov.b #0C0h,10:8[A0]
; BOTH_WHITE
mov.b #81h,11:8[A0]
; ILLEAGAL
mov.b #0A5h,12:8[A0]
; set pointer
mova STTBL,A0
; STATE = 0
mov.b #01h,[A0]
; STATE = 1
mov.b #02h,1:8[A0]
; STATE = 2
mov.b #04h,2:8[A0]
; STATE = 3
mov.b #08h,3:8[A0]
; STATE = 4
mov.b #10h,4:8[A0]
; STATE = 5
mov.b #20h,5:8[A0]
; STATE = 6
mov.b #40h,6:8[A0]
; STATE = 7
mov.b #80h,7:8[A0]
; resume
pop.w A0
;
rts
必要なサブルーチンを定義したので、移動処理を考えます。
実際の移動に関わらない部分だけを定義。
WST .EQU 0
NORMAL .EQU 1
CRANK .EQU 2
ROTATE .EQU 3
LANE .EQU 4
CHANGE .EQU 5
BLIND .EQU 6
TIME_UP .EQU 7
MAIN:
; get sensor state
jsr GET_SENSOR
mov.b ROAD,R0H
; show internal state and road state
jsr DISPLAY
; judge STATE
mov.b STATE,R0L
cmp.b WST,R0L
jgt MAIN1
; get start gate
jsr GET_START
btst #0,FLAGS
jz MAIN1
; 1 -> STATE
inc.b STATE
MAIN1:
; get system timer count
mov.w TIMCNT,R3
cmp.w #60000,R3
jlt MAIN2
mov.b #TIME_UP,STATE
jmp MAIN10
MAIN2:
; ???
; ???
; ???
MAIN10:
mov.b #WST,STATE
;
MAINL:
;
jmp MAIN
R8C/Tinyはピン数も少ないので、スタートゲートの状態と
タイムアップの情報を、同一のピンで共有することに。
回路は、以下。
スタートゲートセンサーの開閉情報をシフトレジスタで
受けておき、タイムアップの情報とデータセレクタで
区別して対応。
パワーオンリセットで、出力CLRに'1'を出力しておき
スタートゲートが開いたなら、CLRを'0'にします。
シフトレジスタもカウンタも1Hzのクロックを使い
スタートゲートの状態とタイムアップを出力。どちら
の情報を使うのかは、マイコン次第に。
マイコンの2ピンを使うだけで、スタートゲートと
タイムアップの情報を指定できます。タイムアップ
を内蔵タイマーで扱わないだけ、ファームウエアの
テストが楽です。
スタートゲートセンサーの開閉情報は、センサーにより
論理値が異なるので、シフトレジスタの出力を反転する
か否かをデバイス出力で選択できるようにと考えました。
1Hzの精度は、それほどいらないので、8ピンのPICで
生成するだけです。
試走で利用するカウンタに使っているので、手持ちの
PIC12F1501にファームウエアを転送するだけ。
1Hzと10Hzのクロックを生成するファームウエアは、以下。
/* redefine data type */
typedef unsigned char UBYTE ;
typedef unsigned int UWORD ;
#define OFF 0
#define ON OFF+1
#define MASK0F 0x0f
#define OUT10 GPIO.B0
#define OUT1 GPIO.B1
#define OUTM GPIO.B2
#define OUTX GPIO.B4
#define CNTBEGIN 6
#define XCNTMAX 25
#define YCNTMAX 250
volatile UBYTE eflag ;
volatile UBYTE tmp ;
volatile UBYTE xcnt ;
volatile UBYTE ycnt ;
volatile UBYTE zcnt ;
volatile UBYTE cnt10 ;
volatile UBYTE cnt ;
/* function prototype */
void init_usr(void);
/* interrupt handler */
void interrupt(void)
{
/* generate 500Hz */
if ( INTCON.T0IF == ON ) {
/* clear flag */
INTCON.T0IF = OFF ;
/* initialize (/250) */
TMR0 = CNTBEGIN ;
/* set flag */
eflag = ON ;
}
}
void main(void)
{
/* user initialize */
init_usr();
/* endless loop */
while ( ON ) {
/* generate 10Hz and 1Hz */
if ( eflag == ON ) {
/* clear flag */
eflag = OFF ;
/* counter increment */
xcnt++ ;
ycnt++ ;
zcnt++ ;
/* generate 20Hz */
if ( xcnt == XCNTMAX ) {
xcnt = 0 ;
cnt10 = cnt10 + 1 ;
}
/* generate 2Hz */
if ( ycnt == YCNTMAX ) {
ycnt = 0 ;
cnt++ ;
}
/* impress */
OUT10 = cnt10 & ON ;
OUT1 = cnt & ON ;
OUTX = zcnt & ON ;
}
/* manual clock generator */
{
/* get and mask */
tmp = GPIO & (1 << 5);
/* shift */
tmp >>= 5 ;
/* impress */
OUTM = !(tmp & ON) ;
}
}
}
/* define function body */
void init_usr(void)
{
/* I/O state */
GPIO = 0x00 ;
/* I/O directions */
TRISIO = 0x28 ; /* bit0,1,2,4 as output , others as input */
/* disable compare module */
CMCON = 0x07 ;
/* pull-up */
WPU = 0x20 ;
/* initialize Timer 0 */
{
/*
4MHz/4 = 1MHz -> 1MHz/8 = 125kHz prescaler = 1:8
*/
OPTION_REG = 0x02 ;
/* 256 - 6 = 250 */
TMR0 = CNTBEGIN ;
/* enable timer0 overflow interrupt */
INTCON.T0IE = ON ;
}
/* enable general interrupt */
INTCON.GIE = ON ;
/* clear flag */
eflag = OFF ;
/* others */
xcnt = 0 ;
ycnt = 0 ;
zcnt = 0 ;
cnt10 = 0 ;
cnt = 0 ;
}
目次
前
次