田舎の組み込みプログラマーがわざわざ趣味でも色々開発してみようとあがく様を綴るブログです。
DMM.makeのクリエイターズマーケットに出品しています。
実践!ロータリーエンコーダ ― 2015年09月17日
このところ

ずっとロータリーエンコーダ関連の処理に取り組んでいて、先日ようやくR8Cのタイマ機能を使って取り込むところまで進みました。
今回はいよいよアプリケーションの中でどう使うかを追求します。
その前に
先日は入力のCRフィルターが効き過ぎていたのを放置していましたが、やはりそれでは勉強にならないと考えて調整してみました。
と言っても、コンデンサを0.1uFから0.033uFに変更しただけですが。
その結果がこちら。
昨日とほぼ同じ速さですが連続検出中もほぼ5Vまで戻っています。
わずか60msec足らずの間に15カウントした結果ですので十分だと思います。
そして、指先一つで速度自在なロータリーエンコーダはやはり便利なデバイスですね。
システム化
まず、R8Cの位相計数モードの設定はこんな風にしてみました。
;-----------------------------------
; タイマRG
;-----------------------------------
TRG$_MDF_MODE 00010001b ; 位相計数モード[ 加算/減算条件 ]
TRG$_START ; カウント開始
マクロの中身は公開できませんが、先日ご紹介した位相計数モードの使用準備がこれだけでできるようにしてあります。
そして、いきなりですがロータリーエンコーダの処理部分がこちら。
;-----------------------------------
; メモリ定義部
;-----------------------------------
DIFFERENCE$_ALLOC rotary
char_num: .blkb 1
;-----------------------------------
; 初期化部
;-----------------------------------
DIFFERENCE$_INIT rotary, [trg].w
[ char_num ] = 'A'
LCD$_POS 0, 0
LCD$_WRITE [char_num]
;-----------------------------------
; メインループ
;-----------------------------------
MAIN_LOOP$
DIFFERENCE$_EXEC$_ rotary, [trg].w
[ char_num ] = R0L + [ char_num ]
LCD$_POS 0, 0
LCD$_WRITE [char_num]
_$
_END
本当にいきなりですが、LCDに1文字表示していて、その文字をロータリーエンコーダで切り換えていると書けば何となくお分かりいただけるのではないでしょうか。
"DIFFERENCE"は変化分を抽出するオブジェクトで、今回新設しました。
"[trg].w"はタイマRGのカウント値が入っているレジスタで、これを直に指定するのは隠蔽が不十分な感じですが、位相計数モードのためだけにこの手の処理を作るのも特化しすぎな気もするのでこれでよしとしておきます。
"DIFFERENCE$_EXEC$_"〜"_$"はメインループの中にありますので最短周期で実行されていて、"[trg].w"が変化した時だけブロックの中まで入っていくようになっています。
この時、R0には変化分がワードサイズで入っていますが、ブロック中ではR0Lで下位バイトだけ使っています。
このフリーダムさもアセンブラの魅力ですね。
とまあ、こんな風に構造化アセンブラにどっぷり浸っていますので他のマイコンが使えなくて困っていたりもします。
ちなみに、上記のソースは構造化アセンブラを使って筆者がこう書いているだけで、構造化アセンブラ自体に"MAIN_LOOP$"とかがあるわけではありません。
割り込みを使わなかったわけ
色々考えてみましたが、R8CのタイマRGの割り込みは位相計数モードでは実質アウトプットコンペアだけで、1カウント毎に割り込みをかけたかったら現在のカウント値の+1と-1の値で割り込みがかかるようにその都度セットする必要があり現実的ではありません。
それに、ロータリーエンコーダの処理はカウント毎に実行しないといけないわけでもなく、例えば100msec周期でチェックしてその間に30カウント増えていたとしたら30カウント分増えた処理をしてやればいいだけです。
カウントはタイマRGがキッチリやってくれていますので、周期はもっと長くても平気です。
とは言え、さすがにそれでは操作に違和感があるでしょうから、実際は10msecくらいが妥当でしょうか。
オシロで見たところ、相当速く回しても10msecだと4カウントまでいかないくらいでしたし。
というわけで、やっとロータリーエンコーダを使いこなすことができたと思います。
構造化アセンブラもカテゴリーに ― 2015年09月09日
なんだか
構造化アセンブラについてもっと語りたくなってしまったのでカテゴリーを追加してみました。
先日の"MEM$_BIT_RESET"マクロは、よく考えてみるとリロケータブル領域のビットには使えませんし、通常の2000hより上位アドレスにある SFRのビットは".btequ"で定義されているのでこれまた使えずと、どや顔で紹介した割にはほとんどCAN部分専用という感じでした。
そこで
今回はもう少し使えそうな記述をご紹介します。
; 定義部
CAN0_MODE .define "[ c0str ].b & 11b"
CAN_OPERATION .equ 00b
CAN_RESET .equ 01b
CAN_HALT .equ 10b
; 実体記述部
for CAN0_MODE != CAN_OPERATION
next
; 展開結果
..fr0009:
MOV.B c0str,R0L
AND.B #11b,R0L
CMP.B #CAN_OPERATION,R0L
JEQ ..fr000b
JMP ..fr0009
..fr000b:
CANモジュールのモードがオペレーションモードに切り替わるのをfor文で待つ処理です。
条件は"=="も使えますし、do文やif文もありです。
こんなのはCでは当たり前の記述じゃないかと思われるかもしれませんが、アセンブラにこの記述を織り交ぜられるところがいいんです。
とまぁ、こんな感じで時々構造化アセンブラをごり押ししていこうと思います。
2000h問題、部分的に解決 ― 2015年09月08日
2000h問題とは
寡聞にして知らなかったのですが、R8Cでは絶対ビット命令アドレッシングで使用できるメモリの上限は1FFFhまでとなっています。
R8CのCAN関連レジスタは2E00h以降に割り付けられており、この制限に引っかかっていることに遅ればせながら気付いて先日つまづいてしまったわけです。
そこで
部分的にですが、以下のような処方で解決を図ってみました。
ちなみに、言語は構造化アセンブラです。
; 定義部
slpm_c0ctlr .define 2,c0ctlr
MEM$_BIT_RESET .macro iBitNum, iBaseMem
.if iBaseMem < 2000h
bclr iBitNum, iBaseMem
.else
A0 = iBitNum
bclr iBaseMem[A0]
.endif
.endm
; 実体記述部
MEM$_BIT_RESET slpm_c0ctlr
; 展開結果
MEM$_BIT_RESET 2,c0ctlr
.if iBaseMem < 2000h
.else
A0 = 2
MOV.W #2,A0
bclr c0ctlr[A0]
.endif
ざっくり言うと、2000h以降のビット操作命令に関しては自動的にアドレスレジスタ相対アドレッシングにしてしまうと言うわけです。
定義部の".define"ではビット情報を文字列として定義しています。
ビット定義には".btequ"という専用の疑似命令があるんですが、これはアセンブル時にアドレスの決まっているメモリにしか使用できず、実質的にはSFR領域にしか使用できません。
そこで、単なる文字列として定義しておけばリロケータブル領域の変数に対してもビット定義をしている体を為すことができるのです。
構造化アセンブラについて語り出すと長くなるのでこの辺で。


最近のコメント