UARTなどのインタフェース不足や、実装されていない機能を
GPIOに対してプログラムできる機能だ。
PIOを調べてみたい。
PIOについて少し調べてみるか!
この記事では、「Raspberry Pi Pico/W」に実装されている「PIO(Programmable I/O」についてご紹介します。
まずは第一弾として、「PIO(Programmable I/O」とは何かを紹介し、次回以降の記事でUARTやSPIなどの機能を「PIO(Programmable I/O」を使って実装してみます。
microPythonやC言語でのプログラム例はあるものの、Arduino IDEを使った例はあまりないので、ぜひチェックしてくださいね!
Raspberry Pi の中でも、省電力なCPUを搭載したマイコン開発ボードです。
他のRaspberry Pi 製品とは異なり、Linux OSを搭載していません。
しかし省スペース、省電力の専用CPU「RP2040」を搭載していて、Arduino IDEや、MicroPythonで開発可能です。
しかも、Wi-FiとBluetoothが使用できるモジュールも搭載されているので、スタンドアロンでの稼働のみならず、他のマイコンと無線通信することが可能です。
「PIO(Programmable I/O」とは何か。どのような仕組みで動くのかについて確認します。
また、Arduino IDEで使用するために必要な準備についても確認します。
「Raspberry Pi Pico/W」の特徴でもある「PIO(Programmable I/O」について身に着けることで、「Raspberry Pi Pico/W」をもっと使いこなせるようになります。
ぜひ、皆さんも挑戦してみてくださいね!
この記事の前提となる「Raspberry Pi Pico W」のはじめかたは、次の記事で紹介しています。
すけろく ついに、国内でもRaspberry Pi Pico Wを手に入れたぞ! げんろく Wi-Fiモジュールがついているので、 国内の技適取得が必要だったからな。 すけろ[…]
あわせて読みたい!「Raspberry Pi Pico W」関連の便利記事
すけろく ん~。 このエラーの時の変数の値が知りたいな~ げんろく どうした? プログラムがうまく動かないのか? すけろく そうなのだ。 Raspberry Pi Pico[…]
今回の記事の対象となるマイコンはこちら!
Raspberry Pi Pico
Raspberry Pi Pico W
ピンヘッダ実装済み
ピンヘッダを自分ではんだ付けするもの
PIO(Programmable I/O)とは?
PIOとは Prgrammable I / O (Input / Output)の略で、端子の部分とマイコン中核部の仲介役をプログラマブルに行うことができるものです。
例えば SPI , I2C , UART などのペリフェラルの数が不足している場合に、PIOでプログラムを組むことで不足分を補うことができます。
基本的な内容は、Raspberry Pi Pico/Wに実装されている「RP2040」コントローラのデータシートに記載されています。
データシートの「3章 PIO」に記載されています。
以降、このデータシートを元に記載していきます。
「PIO(Programmable I/O」 は、多用途のハードウェア インターフェイスです。次のようなさまざまな IO 標準をサポートできます。
- 8080 and 6800 parallel bus
- I2C
- 3-pin I2S
- SDIO
- SPI, DSPI, QSPI
- UART
- DPI or VGA (via resistor DAC)
PIO はプロセッサと同じようにプログラム可能です。
それぞれ 4 つのステート マシン(State Machine)を備えた 2 つの PIO ブロックがあり、GPIO の操作やデータ転送のための逐次プログラムを独立させて実行できます。
PIO ステート マシンは 、汎用プロセッサとは異なり、IO に高度に特化しており、決定性、正確なタイミング、固定機能ハードウェアとの密接な統合に重点が置かれています。
プログラミング方法は、IOに特化した部分をアセンブラ言語で書いて、システム全体をC言語でとりまとめるといったイメージになります。
(言語にPython、C/C++を使う環境もあります。
当ブログでは、C言語環境をArduino IDEで使用するという内容を取り扱います。)
PIOのブロック図
PIOのハードウェア構成
PIOひとつは以下の要素で構成されています。
- 4つのState Machine :ステートマシン(以降はSMと記載します。)
- 8つのFIFO
- 命令用メモリ(Instruction Memory)
(32命令分を4つのSM共用で使う) - I/Oマッピング
- IREQ(割り込み要求)
SM
SMは命令用メモリに書かれたプログラムを順次実行します。
4 つのSMは共有命令メモリから実行されます。
プログラムをこのメモリにロードし、SMと IO マッピングを構成して、SMの実行を設定します。
SMはCPUとは独立したサブプロセッサのような働きをします。
CPUとの連携は、FIFOバッファを介したデータ送受信と割込みの手段を持ちます。
9種類の独自の命令セットを持ち、1命令1クロックで実行されます。
GPIOへの入出力命令も持ち、GPIOの制御が可能です。
SMのブロック図は次のように表されます。
ブロック図を見るとSMは、以下の要素から構成されています。
名称 | 使い方、役割 | |
Out Shift | 出力シフトレジスタ (OSR) | TX FIFO とピン (またはスクラッチ レジスタなどの他の宛先) の間で出力データを保持し、シフトします。 |
In Shift | 入力シフトレジスタ (ISR) | GPIOからのデータが格納されるシフトレジスタです。データ受信時にはこの領域からRX FIFOへデータを読み出します。 |
Scratch X | スクラッチレジスタ X | 命令の繰り返し数などを格納するレジスタです。 |
Scratch Y | スクラッチレジスタ Y | 命令の繰り返し数などを格納するレジスタです。 |
PC | プログラムカウンタ | 次に実行する命令のメモリー番地を管理するレジスター |
Clock Div | クロック分周器 | 入力信号を分周したクロック信号を出力します。 16bitの整数分周(n=1~65535)と、8bitの小数点分周(f=0~255)の設定が可能。 PIOの命令クロック周波数は、メイン・クロック周波数÷(n+f÷256)[Hz]になります。 ※小数点分周はディザリングで実現しているため多少ジッタ(主にデジタル信号の「タイミングの揺らぎ」)が表示ます。 |
Control Logic | コントロール ロジック |
実行制御を行う |
出力シフトレジスタ (OSR)
TX FIFO とピン (またはスクラッチ レジスタなどの他の宛先) の間で出力データを保持し、シフトします。
- PULL 命令: TX FIFO から 32 ビット ワードを削除し、OSR に置きます。
- OUT 命令は、OSR から他の宛先にデータを一度に 1 ~ 32 ビットシフトします。
- データがシフトアウトされると、OSR は「0」で埋められます。
- Auto Pullが有効である場合、SMは合計シフト カウントのしきい値に達すると、TX FIFO から OSR を自動的に補充します。
※空の OSR から OUT しようとするとステート マシンが停止します。
これには次の 2 つの利点があります。
① 適切なタイミングで FIFO からPullするための命令を記載する必要がない(命令を消費しない)
②より高いスループットを実現できる - シフト方向は左/右で、構成レジスタを介して構成可能です。
入力シフトレジスタ (ISR)
GPIOからのデータが格納されるシフトレジスタです。データ受信時にはこの領域からRX FIFOへデータを読み出します。
- IN 命令は、一度に 1 ~ 32 ビットをレジスタにシフトインします。
- PUSH 命令は、ISR の内容を RX FIFO に書き込みます。
- ISR からデータがPUSHされると、ISRはすべて「 0」 にクリアされます。
- Auto PUSHが有効になっている場合、シフトしきい値に達すると、ステート マシンは ISR を自動的にPUSHします。
- シフト方向は左/右で、構成レジスタを介して構成可能です。
UART などの一部のペリフェラルでは、配線順序が LSB ファーストであるため、左シフトします。
プロセッサは結果を右詰めであることを期待している場合があり、ヌル入力ソースによって解決されます。
これにより、プログラマはデータに続いて、いくつかのゼロを ISR にシフトすることができます。
SMは、合計何ビットが OUT 命令によって OSR からシフトアウトされ、IN 命令によって ISR にシフトされたかを記憶します。
この情報は、それぞれ 0 ~ 32 の値を保持できる 1 対のハードウェア カウンター( 出力シフト カウンター と入力シフト カウンター) によって常に追跡されます。
シフト操作が行われるたびに、関連するカウンタがシフト カウントによって最大値 32 (シフト レジスタの幅に等しい) まで増分されます。
SMは、カウンタが設定可能なしきい値に達したときに特定のアクションを実行するように設定できます。
- ある程度のビット数がシフトアウトされると、OSR は自動的に再充填されます。
- ある程度の数のビットがシフトインされると、ISR は自動的に空になります。
- CTRL_SM_RESTART がアサートされると、入力シフト カウンタは 0 (まだ何もシフトインされていない) にクリアされ、
出力シフト カウンタは 32 (シフトアウトされるものは何も残っていない、完全に使い果たされた) に初期化されます。
他の命令はシフト カウンタに影響します。 - PULL が成功すると、出力シフト カウンタが 0 にクリアされます。
- PUSH が成功すると、入力シフト カウンタが 0 にクリアされます。
- MOV OSR, … (つまり、OSR を書き込む MOV 命令) は、出力シフト カウンタを「0」にクリアします。
- MOV ISR, … (ISR を書き込む MOV 命令) は入力シフト カウンタを「0」 にクリアします。
- OUT ISR, count 入力シフト カウンタを count に設定します。
スクラッチレジスタ
各ステート マシンには、X および Y と呼ばれる 2 つの 32 ビット内部スクラッチ レジスタがあります。
これらは次のように使用されます。
- IN/OUT/SET/MOV のソース/デスティネーション
- 分岐条件のソース
FIFO
FIFO(First In – First Out の略)は、先入れ先出しのメモリーです。
出力用の「TX FIFO」と入力用の「RX FIFO」に分けられます。
ひとつのFIFOは4つの32ビットデータを格納することができます。
システム側から書き込まれたデータは「TX FIFO」に保持され、PIOの「PULL命令」で 出力シフトレジスタ(OSR)に格納されます。
システム側に読み込まれるデータはPIOの「PUSH命令」で入力シフトレジスタ(ISR)から 「RX FIFO」に保持されシステムに読み出されます。
命令メモリ
1つのPIOに対して4つのSMが命令を読み出す共通のメモリで、32命令を格納することができます。
1命令は16ビット長なので、全部で64バイトの命令用メモリーがあります
I/Oマッピング
I/Oマッピングは、PIOの命令と端子(IOピン)の対応づけを行います。
マッピング設定 | 内容 |
out pins | どの端子に対して出力するのかを設定する |
in pins | どの端子から入力するのかを設定する |
side-set pins | サイドセット・ピンをどの出力端子にするのかを設定する 各命令にオプションとして指定できる命令(サイドセット)に割り当てます。 |
IRQ(割込み処理)
ひとつのPIOからはCPUに対する割り込みを2つ出力することができます。(IRQ0 , IRQ1)
IRQ フラグは、SMまたはシステムによってセットまたはクリアできる状態ビットです。
合計 8 つあります。8 つすべてがすべてのステート マシンに表示され、下位 4 つは IRQ0_INTE および IRQ1_INTE 制御レジスタを介して PIO の割り込み要求ラインの 1 つにマスクすることもできます。
これらには主に 2 つの用途があります。
SMプログラムからシステム レベルの割り込みをアサートし、オプションで割り込みが承認されるのを待機します。
2 つのSM間で実行を同期します。 SMは、IRQ および WAIT 命令を介してフラグと対話します。
PIOプログラムの構成
アセンブリの記述について
- JMP
- WAIT
- IN
- OUT
- PUSH
- PULL
- MOV
- IRQ
- SET
アセンブリを記述する際のディレクティブ
まず、アセンブリのソースファイル(.pio)を記述する際の、ディレクティブについて押さえていきます。
ディレクティブ(directive)とは、コンピュータプログラムのソースコードに記述される要素の一つで、そのコードを解釈・変換するソフトウェア(コンパイラやプリプロセッサなど)への指示や指定などを与えるためのものです。
整数値の定義
.define ( PUBLIC ) <symbol> <value>
<symbol> という名前の整数シンボルを <value> の値で定義します。
この .define が入力ファイルの最初のプログラムの前にある場合、その定義はすべてのプログラムに対してグローバルになります。
それ以外の場合、定義は、記載されているプログラムに対してローカルになります。
PUBLIC が指定されている場合、シンボルはユーザー コードで使用するためにアセンブルされた出力に出力されます。
プログラムの定義
.program <name>
<name> という名前で新しいプログラムを開始します。
この名前はコードで使用されるので、命名規則として、数字で始まらない英数字、またはアンダースコアにする必要があります。
プログラムは、別の .program ディレクティブまたはソース ファイルの終わりまで続きます。
PIO 命令はプログラム内でのみ許可されます
PIO命令メモリのオフセット指定
.origin <offset>
プログラムをロードする必要がある PIO 命令メモリ オフセットを指定するオプションです。
最も一般的に、これはオフセット 0 でロードする必要があるプログラムに使用されます。
プログラムでは、(絶対的) jmp ターゲットがわずか数ビットに格納されているデータベースの JMP が使用されるためです。
このディレクティブはプログラムの外では無効です
SideSetの宣言
.side_set <count> (opt) (pindirs)
<count> は使用されるサイドセット のビット数を示します。
さらに、命令の「side <value>」 がオプションであることを示すために opt を指定することもできます。
(これには、命令遅延に使用可能なビットから <count> ビットに加えて、追加のビットが必要になります)
最後に、サイド セット値を PIN ではなく PINDIR( に適用する必要があることを示すために、pindir を指定できます。
このディレクティブは、プログラム内で最初の命令の前にのみ有効です。
折り返し実行命令の指定
.wrap_target
・・・
.wrap
.wrap_target … .wrap の間で自動的にゼロサイクルジャンプさせる機能です。
.wrap_targetは命令の前、.wrapは命令の後に配置されます。
プログラムの折り返しにより実行が継続される命令を指定します。
このディレクティブはプログラムの外では無効であり、プログラム内で 1 回のみ使用できます。
指定しない場合は、デフォルトでプログラムの先頭に設定されます。
※ラッピングの命令数はゼロのため、命令メモリを節約できます。
サイクル数はゼロ
レジスタで繰り返し範囲のアドレス設定を行うSMごとに1つだけ使用できます。
言語ジェネレーターのオプション指定
.lang_opt <lang> <name> <option>
特定の言語(Python、Cなど)ジェネレーターに関連するプログラムのオプションを指定します。
このディレクティブはプログラムの外では無効です
16ビット値の保存
.word <value>
16 ビット値を命令としてプログラムに保存します。
この指令はプログラム外では無効です。
アセンブリで使用できるValue値
次のデータ型を使用して、整数または分岐ターゲットを定義できます。
integer | (-)0-9 (10進数、符号付き) |
hex | 0x1 (16進数) |
binary | 0b1 (2進数) |
symbol | .define されたシンボル |
<label> | ラベル |
( <expression> ) | (式) (括弧が必要) |
使用できる式
式には、次の記述方法が使用可能です。
<expression> + <expression> | 加算 |
<expression> – <expression> | 減算 |
<expression> * <expression> | 掛け算 |
<expression> / <expression> | 割り算 |
– <expression> | 式の否定 |
:: <expression> | 式のビット反転 |
<value> | 値 |
コメントの記述方法
アセンブリ内では、次の記号を書くことでコメントを記述できます。
行コメント:「 // 」または「 ; 」でサポートされます。
C スタイルのブロック コメント:「/* 」および「 */」 によってサポートされます。
ラベルのつけ方
アセンブリ内でJMP命令などを使用する際に記述するラベルの定義は次のように行います。
<symbol>:
または、
PUBLIC <symbol>:
※ラベルは実際には、現在のプログラム命令のオフセットに値が設定された単なる自動 .define です。
PUBLIC ラベルは、PUBLIC .define と同じ方法です。
命令の記述方法
命令の記述方法は次のようにします。
<instruction> (side <side_set_value>) ([<delay_value>])
各記載内容は、次の通りです。
<instruction> | アセンブリ命令を記述します。命令の詳細は、後述します。 |
<side_set_value> | 命令の開始時にside_setピンに値を適用します。
.side_set が指定されていないと無効になります。 サイドセットがオプションの場合は指定が無くてもよいですが、オプションでない場合は必要。side_set_value は .side_set で指定したビット数に合っている必要があります。 <side_set_value> は、.side_set ディレクティブで指定されたサイドセット ビットの数内に収まる必要があります。 |
<delay_value> | 命令の完了後に遅延するサイクル数を指定します。
late_value は値として指定され、一般に 0 から 31 までの値 (5 ビット値) ですが、.side_set によってサイドセットが有効になっている場合、ビット数は減少します <遅延値> が存在しない場合、命令には遅延はありません。 |
※1 命令、キーワード、指令は大文字と小文字を区別しません。
※2 命令の記載説明のコンマはオプションです。命令の記載時にコンマは無くてもよいです。
現在、pioasm は便宜上、nop Assembles to mov y, y という 1 つの疑似命令も提供しています。
遅延とサイドセット・ピン
16ビットの命令のうち、遅延とサイドセット・ピンはビット12-8の5ビットに割り当てられています。
サイドセット・ピンを使わなければ最大で31サイクル(5ビット)を遅延させることができます。
例えば2つの端子をサイドセット・ピンに割り当てると最大で7サイクル(3ビット)までしか遅延できなくなります。
遅延とサイドセット・ピンを組み合わせて5ビットを使うことになります。
遅延の使い方
ピンの状態を 1 (High-Level) にした後、7サイクル遅延させる命令は次のように記載します。
set pins, 1 [7]
命令を実行した後に、[]内のサイクル数分だけ遅延します。
※[]内で指定できる数値は最大で31です。
サイドセット(side-set)の使い方
pullでFIFOからデータを受け取って、サイドセット指定ピンの状態を 1(High-Level)にする。
setでスクラッチレジスタ「y」に7をセットし、サイドセット指定ピンの状態を 0 (Low-Level)にします。
pull side 1 set y,7 side 0
コマンドの実行と一緒にピンの状態を設定することができますので、ピン操作をするためのオプションコマンドのような使い方ができます。
アセンブリで使用する命令
- JMP
- WAIT
- IN
- OUT
- PUSH
- PULL
- MOV
- IRQ
- SET
PIO命令は16ビット長で、各命令は以下のようなエンコーディングになっています。
すべての PIO 命令は 1 クロック サイクルで実行されます。
16ビットの命令のうち、5 ビットの「Delay/side-set」 フィールドの機能は、SMの SIDESET_COUNT 設定によって異なります。
サイドセット・ピンを使わなければ最大で31サイクル(5ビット)を遅延させることができます。
例えば2つの端子をサイドセット・ピンに割り当てると最大で7サイクル(3ビット)までしか遅延できなくなります。
遅延とサイドセット・ピンを組み合わせて5ビットを使うことになります。
JMP命令
条件(Condition)が 「true」 の場合はプログラム カウンタをAddressに設定します。
「false」の場合は操作は行われません。
JMP の遅延サイクルは、条件(Condition) が true か false に関係なく常に有効になります。
遅延サイクルは、条件(Condition) が評価され、プログラム カウンタが更新された後に発生します。
条件の指定方法
000: (no condition) | 無条件にジャンプ |
001: !X | スクラッチレジスタ(x)が「0」の場合にジャンプ |
010: X- | スクラッチレジスタ(x)が「0」でない場合にジャンプ |
011: !Y | スクラッチレジスタ(y)が「0」の場合にジャンプ |
100: Y- | スクラッチレジスタ(y)が「0」でない場合にジャンプ |
101: X!=Y | スクラッチレジスタ(x)と(y)が等しくない場合にジャンプ |
110: PIN | PIN の入力が 1 でジャンプ、ピンは JMP_PIN レジスタで指定
SMの他のGPIOのマッピングとは関係なく、構成フィールド「 EXECCTRL_JMP_PIN」で選択された GPIO 上を元に分岐します。GPIO が High の場合、分岐が行われます。 |
111: !OSRE | OSRが空でない場合にジャンプ 最後の PULL 以降にシフトアウトされたビットを、SHIFTCTRL_PULL_THRESH で設定されたシフト カウントしきい値と比較します。 |
Adress: ジャンプ先の命令アドレス。命令エンコーディングでは、これは PIO 命令メモリ内の絶対アドレスです。
JMP命令の書き方
jmp ( <cond> ) <target>
<cond> | 前述の条件(Condition)を指定 |
<target> | プログラム内の命令オフセット (最初の命令はオフセット 0) を表すプログラム ラベルまたは値です。
PIO JMP 命令は PIO 命令メモリ内の絶対アドレスを使用するため、実行時にプログラム ロード オフセットに基づいて JMP を調整する必要があります。 |
使用例:
スクラッチレジスタ(x)が「0」でない場合、ラベル「loop」が記載されている場所に移動するには、次のように記述できます。
loop: ... jmp x-- loop
WAIT命令
GPIO, PIN, 割り込みフラグのいずれかからソースを選び設定した状態に変化するまで待機(ブロック)します。
GPIOとPINはGPIOの番号で指定するかIN命令のIOマッピングの番号で指定するかの違いです。
PINを使うと同じプログラムで複数のSMに対してSM毎に異なるGPIOを対象として処理できます。
割り込みフラグのセットをソースに指定した場合、セットされた時にSMによってフラグがクリアされます。
命令の最後にrelを付加することで割り込みフラグ番号をirq命令と同じ計算方法で相対的に扱うことができます。
遅延サイクルは命令の完了後に始まります。
つまり、遅延サイクルが存在する場合、待機条件が満たされるまでカウントは開始されません。
条件の指定方法
wait 0 gpio 5 | GPIO5が”Low”になるまで待機する |
wait 1 gpio 5 | GPIO5が”High”になるまで待機する |
wait 0 pin 0 | 事前にIN_BASEで指定したGPIOが”Low”になるまで待機する |
wait 1 pin 2 | 事前にIN_BASE+2で指定したGPIOが”High”になるまで待機する |
wait 0 irq 0 | 割り込みフラグ0がクリアされるまで待機する |
wait 1 irq 7 | 割り込みフラグ7がセットされるまで待機し, フラグ7をクリアする |
wait 1 irq 4 rel | 割り込みフラグ4+xがセットされるまで待機しフラグ4+xをクリアする |
WAIT命令の書き方
wait <polarity> gpio <gpio_num> wait <polarity> pin <pin_num> wait <polarity> irq <irq_num> ( rel )
<polarity> | 1 or 0を設定 |
<gpio_num> | 実際の GPIO ピン番号を指定する値 |
<pin_num> | 入力ピン番号 (SM 入力ピン マッピングによってマッピングされる) を指定する値 |
<irq_num> | 待機する irq 番号 (0 ~ 7) を指定する値です。
「rel」 が存在する場合、実際に使用される irq 番号は、irq 番号の下位 2 ビット (irq_num10) を合計の下位 2 ビット (irq_num10 + sm_num10) で置き換えることによって計算されます。ここで、sm_num10 はステート マシン番号です。 |
使用例:
GPIO5が”Low”になるまで待機する場合は、次のように記述できます。
wait 0 gpio 5
IN命令
in命令はISRをシフト量分だけシフトし、入力元の値をISRの空いた領域に書きます。
入力元(シフト元)はIO端子だけに限りません。
シフトによってISRから吐き出された値をISRに取り込むこともできます。
入力元にNULLを指定すると0が取り込まれます。
さらに、入力シフト数をビット数ずつ増加させ、32 で飽和します。
シフト方向は、SHIFTCTRL_IN_SHIFTDIR によってSMごとに設定されます。
「SHIFTCTRL_IN_SHIFTDIR」の設定は、SM初期化時に、sm_config_set_in_shift()関数で行います。
設定値
右シフト: MSBからLSBに向かってシフト
左シフト: LSBからMSBに向かってシフト
入力ソースの指定方法
pins | あらかじめ設定しておいたIO端子 |
X | スクラッチレジスタX |
Y | スクラッチレジスタY |
OSR | 出力シフトレジスタ |
null | 値 0 |
ISR | 入力シフトレジスタX |
IN命令の書き方
in <source>, <bit_count>
<source> | 1 or 0を設定 |
<bit_count> | 実際の GPIO ピン番号を指定する値 |
使用例:
ISRを1ビットシフトし、あらかじめ指定されているピンから1ビット分の値を取り込む場合は、次のように記述できます。
in pins, 1
OUT命令
out命令はOSRをシフト量分だけシフトし、あふれた値を出力先に書きます。
出力先(シフト先)はIO端子だけに限りません。
シフト ビットは出力シフト レジスタ (OSR) からビットをカウントし、それらのビットを出力先に書き込みます。
さらに、出力シフト数をビット数ずつ増加させ、32 で飽和します。
ビット数: 1 ~ 32 ビット、32 は 00000 としてエンコードされます。
シフト方向は、SHIFTCTRL_OUT_SHIFTDIR によってSMごとに設定されます。
「SHIFTCTRL_OUT_SHIFTDIR 」の設定は、SM初期化時に、 sm_config_set_out_shift()関数で設定する。
Auto Pullが有効な場合は次のような動作をします。
・Pullのしきい値 がSHIFTCTRL_PULL_THRESH に達すると、OSR は TX FIFO から自動的に再充填されます。
・出力シフト カウントは同時に 0 にクリアされます。
この場合、TX FIFO が空の場合は OUT が停止しますが、そうでない場合は 1 サイクルで実行されます。
設定値
右シフト: MSBからLSBに向かってシフト
左シフト: LSBからMSBに向かってシフト
出力先の指定方法
pins | あらかじめ設定しておいたIO端子 |
pindir | あらかじめ設定しておいたIO端子の方向 |
X | スクラッチレジスタX |
Y | スクラッチレジスタY |
pc | プログラムカウンタ |
exec | 次に実行する命令レジスタ |
null | 値 0 |
ISR | 入力シフトレジスタ |
OUT命令の書き方
out <destination>, <bit_count>
<destination> | 出力先を指定 |
<bit_count> | 実際の GPIO ピン番号を指定する値 |
使用例:
OSRを1ビットシフトし、あらかじめ指定されているピンへ、1ビット分の値を出力する場合、次のように記述できます。
out pins, 1
PUSH命令
push命令はISRからRX FIFOに32ビットデータを書き込み、ISRとISRのシフトカウンタをクリアします。
ただしRX FIFOに空きがない場合には待機(ブロック)します。
出力先の指定方法
push block :
blockビットはデフォルトで1なので、RX FIFO がいっぱいの場合は実行を待機します。
※pushと同じ動作になります。
push nonblock :
RX FIFOに空きがあればpushし、満杯の場合には何もせず次の命令を実行します。
この時ISRの値はクリアされます。
push iffull :
入力シフト数の合計がしきい値(SHIFTCTRL_PUSH_THRESH)に達したら、ISRからRX FIFOに32ビットデータを書き込みます。
その際、ISRのシフトカウンタをクリアします。ただしRX FIFOに空きがなければ待機(ブロック)します。
※Auto push に設定しておくことで、この命令を自動で行ってくれます。
しきい値及び Auto pushの設定は、SM初期化設定部分で、sm_config_set_in_shift()関数で行います。
push iffull nonblock :
入力シフト数の合計がしきい値に達したら、ISRからRX FIFOに32ビットデータを書き込みます。
その際、ISRのシフトカウンタをクリアします。
ただしRX FIFOに空きがなければ何もせず次の命令を実行します。この時ISRの値はクリアされます。
PUSH命令の書き方
push ( iffull )
push ( iffull ) block
push ( iffull ) noblock
iffull | IfFull == 1 と同等です。 これが指定されていない場合のデフォルトは IfFull == 0 です。 |
block | Block == 1 と同等です。 block も noblock も指定されていない場合、これがデフォルトです。 |
noblock | Block == 0 と同等です。 |
使用例:
push命令はISRからRX FIFOに32ビットデータを書き込む場合、次のように記述できます。
push
PULL命令
pull命令はTX FIFOからOSRに32ビットデータを読み込み込みOSRのシフトカウンタをクリアします。
ただしTX FIFOが空の場合には待機(ブロック)します。
出力先の指定方法
pull block :
blockビットはデフォルトで1なので、pullと同じ動作になります。
pull nonblock :
pull命令はTX FIFOからOSRに32ビットデータを読み込み込みOSRのシフトカウンタをクリアします。
ただしTX FIFOが空の場合には何もせず次の命令を実行し、OSRにはレジスタXの値が入ります。
pull ifempty :
出力シフト数の合計がしきい値に達したら、TX FIFOからORSに32ビットデータを書き込みます。
OSRのシフトカウンタをクリアします。
ただしTX FIFOが空の場合には何もせず次の命令を実行します。
Auto pull に設定しておくことで、この命令を自動で行ってくれます。
しきい値及び Auto pullの設定は、SM初期化設定部分で、 sm_config_set_in_shift()関数で設定します。
pull ifempty nonblock :
出力シフト数の合計がしきい値に達したら、TX FIFOからORSに32ビットデータを書き込みます。
その際、OSRのシフトカウンタをクリアします。
ただしTX FIFOが空の場合には何もせず次の命令を実行し、OSRにはレジスタXの値が入ります。
PULL命令の書き方
pull ( ifempty )
pull ( ifempty ) block
pull ( ifempty ) noblock
ifempty | 上記の IfEmpty == 1 と同等です。 これが指定されていない場合のデフォルトは IfEmpty == 0 です。 |
block | Block == 1 と同等です。 block も noblock も指定されていない場合、これがデフォルトです。 |
noblock | Block == 0 と同等です。 |
使用例:
pull命令でTX FIFOからOSRに32ビットデータを読み込み込みOSRのシフトカウンタをクリアする場合、次のように記述できます。
pull noblock
MOV命令
mov命令はPIOのSM内でデータをコピーします。
オペレーションが含まれる場合もあります。
以下に転送先と転送元に指定できるものを示します。
(注)auto pullを使う場合には mov で OSR を使わないようにしてください。
auto pullの判定は全てのサイクルで行われるため、OSRに対してmovした場合、
pullしたデータを上書きするタイミングがあるからです。
転送先の指定方法
pins | pins |
X | X(スクラッチレジスタ) |
Y | Y(スクラッチレジスタ) |
pc | status |
ISR | ISR |
OSR | OSR |
実行 | null (0) |
転送元(ソース)の指定方法
pins | pins |
X | X(スクラッチレジスタ) |
Y | Y(スクラッチレジスタ) |
STATUS | status |
ISR | ISR |
OSR | OSR |
NULL | null (0) |
MOV命令の書き方
mov <destination>, ( op ) <source>
<destination> | 宛先を指定します。 |
<op> | 存在する場合 :! or ~ NOT (注: ビットごとの NOT です) :: ビット反転の場合 |
<source> | ソースを指定します。 |
使用例:
XにYを反転した値を代入する場合、次のように記述できます。
mov x, -y
IRQ命令
irq命令はIndex 引数で選択された IRQ (割込み)フラグのセット・クリアを行います。
ひとつのPIOは0~7番の割り込みフラグを持っています。
これらは各SMで共有しています。
0~3番はシステムレベルの割り込みとして出力されます。
4~7番はPIO内のSMからしか見えません。
waitと組み合わせて他のSMとタイミングを同期することができます。
割り込みフラグはwaitによってもクリアされます。
IRQ命令の書き方
irq <irq_num> ( rel ) irq set <irq_num> ( rel ) irq nowait <irq_num> ( rel ) irq wait <irq_num> ( rel ) irq clear <irq_num> ( rel )
<irq_num> ( rel ) | 待機する irq 番号 (0 ~ 7) を指定する値 です。
rel がある場合、実際に使用される IRQ 番号は、次のように計算されます。IRQ 番号 (irq_num) の下位 2 ビットを置き換えることによって計算されます。 |
irq | 待機なしのIRQフラグを設定します。 |
irq set | 待機なしのIRQフラグを設定します。 |
irq nowait | 待機なしのIRQフラグを設定します。 |
irq wait | IRQ を設定し、そのIRQフラグがクリアされるのを待ってから次に進みます。 |
irq clear | IRQフラグをクリアします。 |
使用例:
割り込みフラグ「0」をセットする場合
irq 0
割り込みフラグ「3」をセットしフラグ「3」がクリアされるまで待機する場合
irq wait 3
割り込みフラグ「3」をクリアする場合
irq 3 clear
また、4つのSMで異なる割り込みフラグを立てたい場合があります。
その場合は、命令の最後に 「_rel」 を付加することで、それぞれのSMで異なる割り込みフラグの操作ができます。
SM#(0-3) で #(0-3)番目に割込みフラグをセットする場合
irq_num & 4 + ((irq_num + sm_num) & 3)=sum_num & 3
irq 0 _rel
SM#(0-3) で #(0-3)+4番目の割込みフラグをクリアする場合
irq_num & 4 + ((irq_num + sm_num) & 3)=4 & 4 + (4 + sum_num) & 3
irq clear 4 _rel
SET命令
set命令は5ビットの値を書き込みます。
Dataで指定された値を Destination に書き込む。
制御信号のクロックやチップセレクト、ループカウンタの初期化などに使用できます。
Data は 5 ビットのデータをスクラッチレジスタに設定でき、0-31 の値は 32 回の繰り返しに使える。
書き込み先の指定方法
pins | pins |
X | X(スクラッチレジスタ) 下位5ビットに Data が書き込まれ、他のビットは 0 にクリアされます。 |
Y | Y(スクラッチレジスタ) 下位5ビットに Data が書き込まれ、他のビットは 0 にクリアされます。 |
PINDIR | PINDIRS: ピンの向きを設定、1 で出力、0 で入力 |
SET命令の書き方
set <destination>, <value>
<destination> | 書き込み先の指定方法から一つを指定 |
<value> | 書き込みを行うデータ値 |
使用例:
スクラッチレジスタ(y)に値「7」をセットする場合
set y, 7
編集後記
いかがだったでしょうか。
「Raspberry Pi Pico / W」の「PIO(Programmable I/O」について基本となる内容と、アセンブリの記述方法などを説明しました。
次回からは、「Raspberry Pi Pico W」を使って「PIO(Programmable I/O」のプログラミングを行っていきます。
使用する開発環境は「Arduino IDE」です。
記事の内容は以上です。
最後までご覧くださり、ありがとうございました。
次回もご期待ください。
今回の記事の対象となるマイコンはこちら!
Raspberry Pi Pico
Raspberry Pi Pico W
ピンヘッダ実装済み
ピンヘッダを自分ではんだ付けするもの
Raspberry Pi Pico Wの他の記事はこちらからどうぞ!
すけろく ウーム。 センサーなどの情報を記録したいな。 げんろく なに。Raspberry Pi Picoで記録媒体を 使いたいのか。 すけろく そうだ。 センサーやカメラのデータを保存したいのだ。 げんろく データの保管には、microSDカードが 使いやすいそ。使ってみるか! この記事では、「Raspberry Pi Pico/W」に「microSDカードスロット」を接続して読み書きしてみま […]
すけろく うーむ。 げんろく どうした?浮かない顔をして。 すけろく LEDを火のように光らせる方法を知りたくてな。 げんろく それなら、1/fの揺らぎを使って 再現してみるか。 この記事では、「Raspberry Pi Pico W」を使って、「LEDを揺らぎを加えて点灯させる」方法をご紹介します。 Raspberry Pi の中でも、省電力なCPUを搭載したマイコン開発ボードです。 他のRa […]
すけろく うーむ。 どうしたものか。 げんろく どうした。 暗い顔をして。 すけろく カーモデルのウィンカー点滅を再現したくてな。 明滅回路を作ろうか悩んでおる。 げんろく ん!? そんなときは、メインプロセッサと切り離して 動作するPIOの出番ではないのか! この記事では、「Raspberry Pi Pico/W」に実装されている「PIO(Programmable I/O」を実際に使ってみます […]
すけろく Raspberry Pi Pico/WのPIO機能を使ってUARTの送信には成功した。 しかし、まだ受信側ができていないのぉ。 げんろく そうだな。 今度は受信側を試してみるか。 すけろく どうせなら、パソコンから送った文字を返すような やり取りができるといいな。 げんろく よし。 UART送受信を実装してみよう。 この記事では、「Raspberry Pi Pico/W」に実装されてい […]
すけろく Raspberry Pi Pico/WのPIO機能については前回調べた。 今度は実際に使ってみたいの~ げんろく そうだな。 実際にプログラムする際の注意事項などを見ていこう。 すけろく Arduino IDEでも使用できるのか? げんろく ああ。 使えるぞ! Arduino IDEで使う場合を例にやっていこう。 この記事では、「Raspberry Pi Pico/W」に実装されている […]
すけろく Raspberry Pi Pico/Wには、PIOという機能があるらしいな。 げんろく そうだ。 UARTなどのインタフェース不足や、実装されていない機能を GPIOに対してプログラムできる機能だ。 すけろく Raspberry Pi Pico/Wをもっと知るために PIOを調べてみたい。 げんろく よし! PIOについて少し調べてみるか! この記事では、「Raspberry Pi P […]
すけろく うーむ。 げんろく どうした浮かない顔して。 すけろく これまで、Raspberry Pi Pico / Wをいろいろ確認してきたが 外付けのボタンの認識ってやったかぇ? げんろく たしかに!まだ取り上げていない基本的なものがあるな よし、今回はボタン押下時の状態を Raspberry Pi Pico / Wで認識してみよう。 この記事では、「Raspberry Pi Pico W」を […]
すけろく 宅内で使っているリモコンは、どういった仕組みで テレビなどをつけているのかな~? げんろく 赤外線を使って信号を送っているのだ。 すけろく しかし、たくさんある機器のリモコンが混信しないのは どういった仕組みなのかぇ? げんろく よし、今回は赤外線リモコンの通信を 調査してみるか! この記事では、「Raspberry Pi Pico W」を使って、「赤外線リモコンの通信内容を分析する」 […]
すけろく うーむ。 対応付けが難しいな。 げんろく どうした? Raspberry Pi Picoで悩んでいるようだな。 すけろく Raspberry Pi PicoやPico Wの GPIOピンの情報がボード上にプリント されていないから資料との対応付けがやりづらくてな。 げんろく そうか。そういう課題があるのだな。 よし、解決するグッツを作って提供してみよう! 皆さんは、「Raspberry […]
すけろく うーむ。 モーターを毎回同じくらい回転させることはできんのかな。 げんろく DCモーターや、サーボモーターだと、 回転量を正確に制御することは難しいぞ。 すけろく では、何か解決策はあるのかぇ? げんろく ステッピングモーターというものがある。 モーターの回転をステップとして制御できる。 今回はこのあたりを動かしてみるか。 この記事では、「Raspberry Pi Pico W」を使っ […]