LoginSignup
0
1

More than 1 year has passed since last update.

Raspi Pico PIO でロータリーエンコーダを読む

Last updated at Posted at 2022-11-14

はじめに

自分自身pioasmを勉強し始めたばかりでコード書き方が分からない。ということで人のコードを読んで勉強しよう。今回は以下のロータリーエンコーダを読むプログラムを読んでいく。

読んでみる

https://github.com/GitJer/Rotary_encoder/blob/master/pio_rotary_encoder.pio よりプログラムを引用する。コメントアウトは https://www.deepl.com/ja/translator にて翻訳した。

pio_rotary_encoder.pio
.program pio_rotary_encoder
.wrap_target
.origin 0 		; ジャンプテーブルは0から始まらなければならない。
				; A'B'ABの4ビットの組み合わせの16ビットのそれぞれに対して正しいジャンプを含む  
            	; A'B'ABで形成される4ビットの組み合わせ
            	; A = ロータリーエンコーダのピン_A の現在の読み取り値
            	; A' = ロータリーエンコーダのpin_Aの前回の読み値
            	; B = ロータリーエンコーダのpin_Bの現在の読み取り値
            	; B' = ロータリーエンコーダのpin_Bの前回の読み取り値
    jmp read	; 0000 = 00から00へ = 読み取り値の変化なし
    jmp CW 		; 0001 = 00から01 = 時計回り回転
    jmp CCW 	; 0010 = 00から10まで = 反時計回りの回転
    jmp read 	; 0011 = 00から11まで = エラー

    jmp CCW 	; 0100 = 01から00まで = 反時計回り回転
    jmp read 	; 0101 = 01から01 = 読み取り値の変化なし 
    jmp read 	; 0110 = 01から10 = エラー
    jmp CW 		; 0111 = 01から11 = 時計回り回転
 
    jmp CW 		; 1000 = 10から00まで = 時計回りに回転
    jmp read 	; 1001 = 10から01 = エラー
    jmp read 	; 1010 = 10から10 = 読み取り値の変化なし 
    jmp CCW 	; 1011 = 10から11 = 反時計回りの回転
 
    jmp read 	; 1100 = 11から00 = エラー
    jmp CCW 	; 1101 = 11から01 = 時計と反対方向の回転
    jmp CW 		; 1110 = 11から10 = 時計回りに回転
    jmp read 	; 1111 = 11から11 = 読み取り値に変化なし 

pc_start: 	;プログラムのエントリポイントです。
    in pins 2 	; AとBの現在値を読み出し、それを使って以前の値を初期化する。
                ; 前の値(A'B')を初期化するために使用する。
read:
    mov OSR ISR ; OSRは(次の命令の後に)前の値を持つ2つのビットをシフトするために使用されます。
                ; 前の値を持つ2ビットをISRにシフトする。
    out ISR 2 	; 前の値をISRにシフトする。これはまた
                ; ISRの他のすべてのビットを0にします。
    in pins 2 	; 現在の値をISRにシフトする。
                ; ISRの16個のLSBは現在、0000000000A'B'ABを含みます。
                ; これは、A'B'AB番地へのjmp命令を意味します。
    mov exec ISR ; ISRにエンコードされたjmpを実行する
CW: 		; 時計回りの回転が検出された
    irq 0 		; IRQで時計回りの回転を知らせる
    jmp read 	; AとBの現在値の読み出しにジャンプする
CCW: 		; 反時計回りの回転を検出した
    irq 1 		; IRQで逆時計回りの回転を知らせる
;    jmp read 	; AとBの現在値を読みに行く。
                ; jmpは.wrapのため必要ない。
                ; プログラムの最初の文がjmp readになる。
.wrap

PIOには8つの命令(しかない)がある。それぞれの命令について以下のページを参考にした。

read

概要

  • 現在の GPIO の状態を読み取り、0000 0000 0000 A'B'ABに並び変える。
  • 0 A'B'A Bにジャンプし、C言語で言うswitch文のように機能する。

mov OSR ISR: OSR(レジスタ) に ISR(レジスタ) の値をコピーする。
out ISR 2: ISR をクリア。同時に ISR に OSR の最下位 2bit をシフトする。
in pins 2: GPIO の状態を ISR に左シフトする。1
mov exec ISR: ISR の値を命令として実行する
このとき ISR の中は0000 0000 0000 A'B'ABであり、表より JMP 命令であることがわかる。また0 A'B'A Bのアドレスに JMP することもわかる。
image.png
引用元 : https://hanya-orz.hatenablog.com/entry/2021/07/28/191910#JMP

ここで./build/pio_rotary_encoder.pio.hを参照する。このファイルは一度プログラムをコンパイルすると生成されるファイルである。このファイルを参照することで、それぞれの命令のアドレスを確認できる。

pio_rotary_encoder.pio.h
// -------------------------------------------------- //
(中略)
static const uint16_t pio_rotary_encoder_program_instructions[] = {
            //     .wrap_target
    0x0011, //  0: jmp    17                         
    0x0015, //  1: jmp    21                         
    0x0017, //  2: jmp    23                         
    0x0011, //  3: jmp    17                         
    0x0017, //  4: jmp    23                         
    0x0011, //  5: jmp    17                         
    0x0011, //  6: jmp    17                         
    0x0015, //  7: jmp    21                         
    0x0015, //  8: jmp    21                         
    0x0011, //  9: jmp    17                         
    0x0011, // 10: jmp    17                         
    0x0017, // 11: jmp    23                         
    0x0011, // 12: jmp    17                         
    0x0017, // 13: jmp    23                         
    0x0015, // 14: jmp    21                         
    0x0011, // 15: jmp    17                         
    0x4002, // 16: in     pins, 2                    
    0xa0e6, // 17: mov    osr, isr                   
    0x60c2, // 18: out    isr, 2                     
    0x4002, // 19: in     pins, 2                    
    0xa086, // 20: mov    exec, isr                  
    0xc000, // 21: irq    nowait 0                   
    0x0011, // 22: jmp    17                         
    0xc001, // 23: irq    nowait 1                   
            //     .wrap
};
(以下略)

pio_rotary_encoder.pio内のラベルpc_startより上のjmp read jmp CWは、アドレスとして0 A'B'A Bの取る値を網羅しており、C言語で言うswitch文のように機能している。アドレスとそのアドレスの命令を下表にまとめる。

アドレス 命令 アドレス 命令 アドレス 命令 アドレス 命令
0 0000 jmp read 0 0100 jmp CCW 0 1000 jmp CW 0 1100 jmp read
0 0001 jmp CW 0 0101 jmp read 0 1001 jmp read 0 1101 jmp CCW
0 0010 jmp CCW 0 0110 jmp read 0 1010 jmp read 0 1110 jmp CW
0 0011 jmp read 0 0111 jmp CW 0 1011 jmp CCW 0 1111 jmp read

CW

irq 0 : IRQ 0 のフラグを立てる。
Cファイル内で IRQ 0 の割り込み処理をする。
jmp read : ラベルreadにジャンプする。

CCW

irq 1 : IRQ 1 のフラグを立てる。
Cファイル内で IRQ 1 の割り込み処理をする。
jmp read : ラベルreadにジャンプする。

感想

mov exec <レジスタ>で条件分岐ができることに感動した。条件分岐の方法としては JMP 命令に条件を追加する方法があるが、分岐の数によってはmov exec <レジスタ>を用いたほうが良いだろう。

  1. Cファイル内でsm_config_set_in_shift(&c, false, false, 0);より左シフトで入れる設定にされている。RaspberryPi SDK

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1