皆さん こんにちは。
今回は UARTの割り込みを使って送受信してみます。
環境構築やプロジェクトのつくり方
環境構築やプロジェクトのつくり方、VSCodeの起動については、このブログの一番最初の記事に書きました。
環境構築してみたい方は こちら をご覧になってください。
プロジェクトの作成
以下の設定でプロジェクトを作成します。
Project Name: picoUartRxTxInt
Console Options の Console over UART のチェックをはずす
IDE Options の Create VSCode project をチェックする
Debugger: は PicoProbe を選択する

VSCodeを起動する
起動後、何かファイルが開いてたら File – Close Folder で閉じた後、File – Open Folder から picoUartRxTxInt のフォルダーを辿って選択します。
こちらの環境では C:\Users\m3925\Documents\Pico\pico-project-generator\picoUartRxTxInt です。
m3925の部分は皆さんのユーザー名に置き換えてください。
vscode関連の json ファイルは前回作成済のプロジェクトのものをドラッグ&ドロップでコピー&ペーストすると楽です。
ソースコードを書く
今回はファイルを分割してみました。
まずは picoUartRxTxInt.c を以下の通りに編集します。
picoUartRxTxInt.c
VSCodeのツリーから picoUartTxInt.c を選択して次のように編集し、保存します。
#include "pico/stdlib.h"
#include "uart_ex.h"
int main() {
stdio_init_all();
uart_init(uart0, 115200);
uart_ex_init();
// 7bit + パリティだと 8bit を指定するらしい
uart_set_format(uart0, 8, 1, UART_PARITY_EVEN);
// 7bit + パリティはそのまま設定すれば OK です
// PC側の設定に合わせれば OK です。
uart_set_format(uart0, 7, 1, UART_PARITY_EVEN);
uart_set_fifo_enabled(uart0, false); // FiFoなしでやってみる
gpio_set_function(0, GPIO_FUNC_UART);
gpio_set_function(1, GPIO_FUNC_UART);
irq_set_exclusive_handler(UART0_IRQ, on_uart_rx_tx_int);
irq_set_enabled(UART0_IRQ, true);
uart_set_irq_enables(uart0, true, true); // 送信と受信 割り込みのみ
while (true) {
uart_ex_send_first_byte();
sleep_ms(1000);
}
return 0;
}
続いて uart_ex.c を作成します。
uart_ex.cの作成と編集
まず File – New File から入力ボックスに uart_ex.c と入力し Enterキーを押します。
picoUartTxInt のディレクトリーが出るので、そのまま Create File ボタンを押します。
続いて uart_ex.c を以下の通りに編集します。
#include "uart_ex.h"
uart_t ut;
void on_uart_rx_tx_int() {
if (uart_is_writable(uart0) && uart_ex_is_txim(uart0)) {
ut.index++;
uart_ex_non_blocking_putc(uart0, ut.buf[ut.index]);
if (ut.index == ut.length) {
hw_clear_bits(&uart_get_hw(uart0)->imsc,
1 << UART_UARTIMSC_TXIM_LSB);
ut.index = 0;
}
}
if (uart_is_readable(uart0)) {
uart_ex_non_blocking_putc(uart0, uart_getc(uart0));
}
}
void uart_ex_send_first_byte() {
if (uart_is_writable(uart0)) {
uart_ex_non_blocking_putc(uart0, ut.buf[0]);
hw_set_bits(&uart_get_hw(uart0)->imsc, 1 << UART_UARTIMSC_TXIM_LSB);
}
}
void uart_ex_init() {
const char *str = "1234567890\r\n";
ut.index = 0;
ut.length = 12;
for (int i = 0; i < 12; i++) {
ut.buf[i] = str[i];
}
}
bool uart_ex_is_txim(uart_inst_t *uart) {
return uart_get_hw(uart)->imsc & UART_UARTIMSC_TXIM_BITS;
}
void uart_ex_non_blocking_putc(uart_inst_t *uart, uint8_t data) {
while (!uart_is_writable(uart)) { // ここは待たなければならない
tight_loop_contents();
}
uart_get_hw(uart)->dr = data;
}
続いて uart_ex.h を作成します。
uart_ex.hの作成と編集
File – New File から入力ボックスに uart_ex.h と入力し Enterキーを押します。
picoUartTxInt のディレクトリーが出るので、そのまま Create File ボタンを押します。
続いて uart_ex.h を以下の通りに編集します。
#ifndef __UARTEX_H
#define __UARTEX_H
#include "pico/stdlib.h"
typedef struct {
uint8_t buf[1024];
int index;
int length;
} uart_t;
void on_uart_rx_tx_int();
void uart_ex_send_first_byte();
void uart_ex_init();
bool uart_ex_is_txim(uart_inst_t *);
void uart_ex_non_blocking_putc(uart_inst_t *, uint8_t);
#endif /* __UARTEX_H */
CMakeLists.txtの編集
ファイルを追加したので、CMakeLists.txt を編集する必要があります。
add_executable() に uart_ex.c を追加します。
add_executable(picoUartRxTxInt picoUartRxTxInt.c uart_ex.c)
これで追加したファイルもビルドしてもらえるようになります。
PC側の準備
通信の確認用として Tera Term を使います。
お持ちでない方は こちら からダウンロードしてお使いください。
インストール後、 Tera Term を起動し、シリアルポートでPicoprobeのCOMポートを選択します。
もしCOMポートが複数ある場合には、Picoprobe以外の通信ケーブルをはずしてデバイスマネージャーでCOMポート番号を確認しておきます。

起動後、設定メニューのシリアルポートから以下の設定を行います。
スピート : 115200
データ : 7 bit
パリティ : even
ストップビット : 1
フロー制御 : none
これらの値は送受信する相手と合わせる必要があります。

次に設定メニューの端末から以下の設定を行います。
改行コードをCR+LFに設定します。

それから設定メニューの端末から以下の設定を行います。
ローカルエコーにチェックを入れます。

プログラムの説明
まずノンブロッキングの話です。
ノンブロッキングとは
関数(処理)にはブロッキングとノンブロッキングがあります。
少し前の記事で紹介した以下の関数は、その名の通りでブロッキングの関数です。
uart_write_blocking(uart0, (const uint8_t *)buf, 12);
これは配列bufの中にあるデータを12バイト送信する関数です。
関数の中で1バイトを送っては、次のデータを送って良いか確認しています。
というわけでデータをほぼ全て送り終えたら関数から戻ってきます。
このように処理が終わるまで関数から戻ってこないものをブロッキングと呼びます。
ノンブロッキングはその反対で、処理が終わる前に関数から戻ってきます。
UART、送信の場合には送るデータが多いほど、そして通信速度が遅ければ遅いほど、その関数で待たされることになり次の処理に進めません。
これでは効率が悪いので待たされないようにノンブロッキングを考える必要が出てきます。
その反面割り込みを使う場合には、そのオーバーヘッドが出てくることや処理が複雑になるデメリットがあります。
コードの説明
uart_set_format(uart0, 8, 1, UART_PARITY_EVEN) でフォーマットの指定を行います。
第2引数の 8 ですが、パリティ付きの場合にはパリティを含めた値を指定する必要があるようです。
(今回の仕様は、データ:ビット、偶数パリティとしている)
uart_set_fifo_enabled(uart0, false) でFiFoを使わない設定にします。
irq_set_exclusive_handler(UART0_IRQ, on_uart_rx_tx_int) でUARTの割り込みハンドラに on_uart_rx_tx_int() を指定します。
irq_set_enabled(UART0_IRQ, true) で割り込みを許可します。
uart_set_irq_enables(uart0, true, true) で受信割り込み及び、送信割り込みを許可します。
uart_ex_send_first_byte() で最初の1バイトを送信し、送信の割り込みマスクビットを有効にします。
これで送信バッファが空になると割り込みが入ります。
on_uart_rx_tx_int() はUARTの割り込みハンドラ関数です。
送信バッファが空になるか、受信バッファにデータを取り込むとここに来ます。
送信データを書き込み可能でかつ、送信のマスクビットが有効ならば次のデータを送ります。
uart_is_writable() は送信データが書き込み可能なら true が返ります。
uart_ex_is_txim() はマスクビットが有効なら true が返ります。
uart_ex_non_blocking_putc() はノンブロッキングで1バイトを送信します。データレジスタに送信データを書いてすぐ戻ってきます。
全数(12バイト)送り終えたら、送信の割り込みマスクビットを無効にします。
uart_is_readable() は受信バッファにデータが存在していたら true が返ります。
ここでは uart_ex_non_blocking_putc() を使って読んだ値をそのまま返信しています。
割り込みで処理しているため、取りこぼしは起きにくいはずです。
RP2040(ラズピコのマイコン)の場合、UARTの要因別の割り込みベクターはないので、割り込みハンドラ関数内で要因を判断する必要があります。
動作させてみる
それでは F5キーを押してプログラムを動作させてみます。
1秒周期で Tera Term に 1234567890 が表示されれば成功です。
それとは別に Tera Term から送信した文字がエコーバック(押したキーを2文字表示)すれば成功です。
GitHub に picoUartRxTxInt のコードをアップしましたのでご自由にお使いください。
(ただし当方では動作の保証は行っていません)
お疲れさまでした。