皆さんこんにちは、グレキチです。
そう言えば、11月1日は我がピースコードの開業日でした。気がついたら、フリーランスになって3年が経過してました!
何せ、IT業界未経験でフリーランスになっちゃったので、収入のことはちょっとそっちのけで、自分の目標達成のために、必要な知識と技術を早く身に付けたいという一心で、あれこれと取り組んできましたから・・・😅
その甲斐あってかなり出来ることも増えてきたので、そろそろ真剣に収入を得るようなことにも取り組まないといけないかなーと考えている次第です😆
さて、今回のベアメタルシリーズですが、ラズパイPicoでUART通信を使って、他の端末へデータを送信し、それを受信したものを表示するということをやってみましたので、その活動記録になります。それでは行ってみましょう。
マイコン通信方式
UARTとはUniversal Asynchrous Receiver Transmitterの略で、日本語訳では、汎用非同期送受信機ということになります。電子デバイス間でデータを転送するために、広く採用されているシリアル通信規格です。電子デバイスから受け取ったパラレルデータを送信機でシリアル変換して送信したり、送信されてきたシリアルデータを受信機でパラレルデータに変換して電子デバイスに受け渡すという処理が出来ます。UART通信を詳細に説明するにあたっては、まずは大まかに通信方式全体について説明したいと思います。
通信方式には、大きくアナログとデジタルがありますが、組み込み開発などで使用するマイコンの場合は、デジタル通信になります。そして、その中には、UART、I2C、SPI、CAN、USB、Ethernet、1-Wire、RS232Cなど色々な方式があります。それらを区分けできる単位で分類してまとめると、下図のようになります。

デジタル通信は、上表のように4段階で分類できます。それぞれの項目が何を表しているのかを、以下に説明します。
シリアルとパラレル
まず、シリアルとパラレルです。シリアルとは、データ送信信号1波形(上下動で1クロック)あたりのデータ量が1ビットで、そのための信号線が1本で済むので低コストであり、配線なども簡単なのが特徴です。対して、パラレルは、読んで字の如く並列処理という意味で、1クロックで複数ビットのデータを送ることが可能です。これは、シリアル通信に対して、通信速度を早くできるメリットがあります。しかし、並列でデータ送信するためには、信号線も複数必要になって配線が複雑になるとともに、コストも高くなります。さらに、並行して送信するデータの同期が難しいなどのデメリットもあります。そのため、現在組み込み開発の業界では、同期が簡単なシリアル通信の方が普及している状況のようです。
クロック同期/非同期
次は、データ送受信タイミングに関するクロック同期と非同期についてです。通信は、データの送信側(送信システム)と受信側(受信システム)が同時に存在して成り立っています。データ送信側の基準クロックに併せてデータを受信する場合をクロック同期と言い、データ受信側がデータ送信側の基準クロックではなく、自身が持つクロックに従って受信する場合を非同期(調歩同期)と言います。
クロック同期式の場合、同一クロック基準で通信が行われるため、送受信でのデータの整合性が保たれます。そのため、転送効率が良く、転送レートを上げられるというメリットがある反面、お互いのデータ送受信タイミングを合わせるために、リアルタイムでの通信が出来る状態でなければなりません。
対して、非同期式の場合ですが、リアルタイム性に縛られないため、各々の機器の都合の良いタイミングでデータの送受信が出来るというメリットがあります。反面デメリットは、通信開始と終了を表す追加のビット情報が必要となり、データ転送速度が遅くなってしまう傾向があります。また、通信中に何かしらのエラーが発生した場合、エラー原因の特定が難しくなってしまうことがあるようです。
全二重と半二重
次に、全二重と半二重ですが、これはデータの送信方向とタイミングに関することを表すもので、一般の人はほとんど知らない用語だと思います。私も、今回初めて知りました。組み込み開発の勉強をしていなかったら、一生お目に掛かることは無かったと思います😲
それで、まず全二重ですが、これは送受信しているデバイスが、各々送受信機能を持っている場合、お互いに同時に送信と受信をできることを表します。そのため、通信回線が最低2本は必要になりコストはかかりますが、伝送効率は良い方式になります。
一方、半二重とは、全二重同様に双方向の通信はできるのですが、お互いが同時に送受信をすることは出来ません。片方が送信していたらもう片方は受信しか出来ず、反対に送信したい場合は、現在通信が行われている処理が終るのを待ってからでなければ出来ないようになっています。例えるなら、トランシーバーのような感じです。この方式は、信号線が1本で済むので、コストは安く抑えられますが、伝送効率は良くないということが想像できると思います。
※ちなみに、送信側から受信側へ、常に一定方向にしかデータ送信しないものを単向というみたいです。(例えば、テレビやラジオなど)
シングルエンドと差動
最後にシングルエンドと差動です。これらも先ほどの全二重/半二重同様、今回初めて触れた用語です。
まずは、シングルエンドについて。シングルエンドは、1本の信号線を使って、グラウンドとの電位差によって信号を伝送する方式のことで、構造がシンプルなので低コストに出来ます。しかし、ノイズ耐性が低く、グラウンドや電源レールのノイズで信号が簡単に破損してしまう可能性があるようです。
一方、差動という方式は、プラスとマイナスの2本の信号線を使い、信号伝送時にお互いの信号線の差分をとってデータを(0か1か)表現するものです。プラス、マイナスどちらの信号線もグラウンドに接地しないためノイズ耐性が高く、高速で安定した伝送、消費電力が低いなどのメリットがあります。デメリットは、終端に抵抗が必要になるなどの技術的な手間が掛かることのようです。
以上、デジタル通信の各分類についての説明でした。
総じて言うとUARTは、好きなタイミングで送受信を行えるが、データ信頼性にちょっと不安がある、コストを抑えた通信方式ということになりますかね🤔 なので、リアルタイムで正確性を問われない用途になら、使えるものなのかなと思いました。
ラズパイPico UART仕様
UART通信の概要は、前セクションでの説明の通りですが、このセクションでは、ラズパイPicoのUART装置に関する特徴について簡単に説明します。
ラズパイPicoには、UARTは2組(UART0とUART1)設置されていて、使用できるGPIOポートは下図の通りです。(※GPIOポート番号とピン番号の混同に注意)

<UART0> TX側:GPIO0、GPIO12、GPIO16 RX側:GPIO1、GPIO13、GPIO17
<UART1> TX側:GPIO4G、PIO8 RX側:GPIO5、GPIO9
そして、1組につき、送信用のTXDと受信用のRXDと名付けられた信号線(ピン)2本を使用します。信号線の繋ぎ方は、一対の接続機器として例えば、下図のようにデバイスAとデバイスBがあったとすると、Aの送信線TXDとBの受信線RXDを繋ぎ、Bの送信線TXDとAの受信線RXDを繋ぐといった結線の仕方です。(※GNDはAとBで必ず共通にする)

実際の通信では、データ依頼側(マスター)から命令受託側(スレーブ)へデータ送信を依頼し、それに返答する形で、スレーブからマスターへデータが返信される流れです。
データ伝送速度は、各々のデバイスのシステムクロック周波数に依存しますが、ボーレート値の設定はマスターとスレーブで同じにしないといけないです。(※Picoでのボーレート最大値は、クロック周波数125MHzで7.8Mbaudとのこと)なお、よく用いられるボーレートの値は、9600とか115200のようでした。
なお、ラズパイPicoのUARTには、ハードウェアフロー制御という機能もあるようです。これは、UARTでは受信側が都合の良いタイミングでデータ受取が可能な分、高速なデータ通信を行う場合に、送信速度に対して受信側のデータ受取処理が追いつかずにデータが上書きされて消失してしまう現象が発生することがあるので、それを防ぐための機能のようです。今回の実装では取り扱わなかったので、ここでの詳しい説明は省きます。
その他の詳細については、この後のプログラム内容のところでも説明します。
回路設計
続いては回路図の内容です。 回路図は下図の通り計画しました。
左側がターゲットデバイスで、右側がデバッガー(Pico Probe)です。実物のマイコンと下図とは、DEBUG端子の位置が違うので注意して下さい。

ラズパイPicoがもう一機あれば、今回のようにデバッガー用Probeは自作出来ます。そして、Probe作成にあたっては、こちらの公式マニュアル(p17から)を参考にしました。作成手順の説明は本題から外れてしまうので、ここでは割愛します。
なお、この回路図は、この公式マニュアルに記載のあった配線図を元に作成しています。
ターゲットデバイスから出ている通信ケーブル(SW CLKとSW DIO)を、デバッガー側のGPIO2及び3ポートに繋ぎます。また、ProbeをPCとUSBケーブルで結線して電源供給するので、送信機側PicoのVSYSとGNDは、各々ProbeのVSYSとGNDに接続しないといけないです。特に、GNDはProbeとターゲットで共通になっていないと、ノイズ入りの原因などになり、データ送受信がうまくいかない確率が上がってしまうので注意が必要です。(“マイコン通信方式” のセクションで説明した通りです)
次に配線図ですが、以下のように計画しました。

回路図を元に、配線の取り回しがしやすいように結線します。なお、図には反映していませんが、電源供給とデータ受信結果表示のために、PCとDebuggerをUSBケーブルで接続します。
プログラム内容
今回のプログラムでは、UART制御に以下のレジスタを使用します。
(※UART用のレジスタは、下記以外にもいくつか設けられています)
- UART0_BASE_UARTDR_RW: データレジスタ(送受信データの書込み先)
- UART0_BASE_UARTFR_RW: 送受信状態の確認フラグレジスタ
- UART0_BASE_UARTIBRD_RW: 分周器設定レジスタ(ボーレート分周値の整数部分)
- UART0_BASE_UARTFBRD_RW: 分周器設定レジスタ(ボーレート分周値の小数部分)
- UART0_BASE_UARTLCR_H_RW: ラインコントロールレジスタ(送信パラメータなど)
- UART0_BASE_UARTCR_RW: コントロールレジスタ(各設定の有効無効を制御)
これらのレジスタを使いこなして、狙い通りのUART通信設定を行います。今回の通信にあたっての各項目の設定値は、下表のように計画しました。
| 項目 | 設定値 |
|---|---|
| 使用クロック | 水晶発振器(XOSC) |
| システムクロック周波数 | 12MHz |
| ボーレート | 115,200 |
| 1フレームあたりデータビット数 | 8bits |
| 送信データ型 | 文字列(char型) |
| FIFOs | 有効 |
| パリティビット | 無し |
| ストップビット | 1bit |
| ハードウェアフロー制御 | 無し |
ここで、設定値を各レジスタへ反映するにあたり、事前に計算が必要なボーレート分周値の求め方について詳しく説明しておきます。
ボーレート分周値の算出
狙ったボーレートで設定するにはシステムクロックの分周処理というものが必要です。分周とは言葉の通り、周波数を分ける(割る)という意味です。システムクロック周波数は非常に高い値(今回は12MHz)のため、周辺機器を扱う場合は、それらが扱える周波数帯域まで下げる必要があり、そのことを分周処理と言います。
それで、ラズパイPicoの場合は、分周計算に下記の方法を用いることになります。
(※詳しくは、ラズパイPico公式データシートのp421に説明があります)

要約すると、計算手順は次のようになります。
1. 以下の計算式(1)で、ボーレート分周値の整数部分(BRDI)と少数部分(BRDF)を求める
式(1): ボーレート分周値 = システムクロック周波数 /(16 ✖︎ ボーレート)
2.以下の計算式(2)で、ボーレート少数部分に6bit数(m)を求める
式(2): m = integer(BRDF ✖︎ 2n + 0.5)
※n はUARTFBRDレジスタの幅なので 6(bit)、よって、26 = 64
そして、計算結果の BRDI と m を各々該当するレジスタのUART0_BASE_UARTIBRD_RW、UART0_BASE_UARTFBRD_RW へセットするという流れになります。
以上をふまえて、今回のUART通信のボーレート分周値を計算すると、以下のようになります。
ボーレート分周値 = 12000000 / ( 16 × 115200 ) = 6.5104 = 6 + 0.5104
m = int( 0.5104 × 64 + 0.5 ) = int( 33.1656 ) = 33ということで、6 と 33 を該当レジスタに設定すればよいことになります。下記の通りです。
PUT32(UART0_BASE_UARTIBRD_RW, 6); // ボーレート分周値 整数部
PUT32(UART0_BASE_UARTFBRD_RW, 33); // ボーレート分周値 小数部割り込み処理の活用
UARTの送信結果は出力受信結果をモニタリングすれば分かりますが、加えてマイコンの動作状態を視覚的にも確認できるように、データ送信中は内蔵LED(GPIO25)が1秒感覚で点滅するようにも設定しています。内蔵LEDを点滅させる方法については以前記事を投稿したので、気になる方はそちらもご確認頂ければと思います。
但し、今回の方法は以前紹介した方法とはちょっと違っていて、Systickタイマーという汎用タイマーを使って、割り込み処理でLEDを点滅させるということをやってみました。割り込み処理には、下記の通り、systick_handlerという名前の関数で設定しました。
void systick_handler(void)
{
PUT32(SIO_GPIO_OUT_XOR, 1 << 25); // GPIO25出力を停止
GET32(SYST_CSR);
}systick_handler関数ではGPIO25出力の停止指示のみを行っています。常時LEDを点灯させておいて、この関数で一旦停止させることで、点滅を表現しています。
さらにLED点滅に加えて、何も設定しないと通常連続で送信されるUARTのデータを、きちんと1回の送信毎にスリープがかかるようにして、受信結果をわかりやすくすることにも活用しました。それには、ARM独自のWFI(Wait For Interrupt)命令というものを使っています。
このWFI命令は、呼び出されるとスリープ(低電力モード)状態に移行し、何か割り込みが発生するとスリープから復帰させるという動作をさせることが出来ます。ご察しの通り、今回はSystickタイマーを使って割り込み処理を行っているので、それをトリガーにして、スリープとウェイクをコントロールするという仕組みを使っています。なお、割り込み処理のカウント時間は約1秒に設定しているので、データ送信も1秒間隔で送信されるようになります。
この方法以外にも、DELAY関数を設定して、単純に必要な時間だけ待機させる方法などもあります。
プログラムに関しては、ざっとこんなところでしょうか😀
いつものように、プログラム全体はこちらにアップロードしています。
実行結果
それではいよいよ最後となりますが、実行結果です。
まずはマイコン動作状況の確認動画です。
ターゲットデバイスのLEDが点滅しているのがわかると思います。なお、Probe側のLEDは、電源供給されていると常に点灯しています。
次にデータ受信結果ですが、モニタリング方法にはいくつかあって、今回は2つの方法を紹介します。いずれの方法も、対象となるソフトウェアをインストールする必要があります。
1.Minicomを使った方法
まず1つ目は、Minicomです。Minicomはシリアル通信が可能なテキストベースのターミナルエミュレータで、CLIで操作します。今回PCとProbeの接続はUSBで行いますが、RS-232などの接続でも使用可能です。
LinuxOSへのインストールは、以下のコマンドで簡単に行えます。
sudo apt install minicomそれで、受信設定は至って簡単で、結線済みのマイコン達のProbe側とPCをUSBケーブルで繋いだ後、下記のコマンドをPCの端末で入力するだけです。なお、各々のオプションコマンドの意味なども記載しておきます。
minicom -b 115200 -o -D /dev/ttyACM0 -b : ボーレートの指定 ー> 続く’115200’がボーレート値
-o : 初期化コードのスキップ ※このオプションは設定無しでも問題ありません
-D : デバイスの指定 ー> 続く’/dev/ttyACM0’ が対象デバイス名
※デバイス名は、PCによって異なる場合があります。
これで実行できる訳ですが、Minicomは終了方法がちょっと変わっていて、“ctrl + A” → “x” で終了確認のポップアップが立ち上がるので、そこで “y” を押して終了します。
実際の受信状況は、以下の通りです。
2.VSCodeを使った方法
続いては、VSCodeを使った方法です。が、色々と事前設定が必要なので、ちょっと扱い方がややこしいです😓
VSCodeの拡張機能で、組み込みデバイスの開発用途にMicrosoft公式のSerial Monitorというものがあり、これを使うことで、シリアルポートへの出力表示とメッセージ送信が可能になります。なので、まずはVSCodeにこの拡張機能をインストールします。
次にソフトウェアですが、使用するソフトウェアはOpenOCDとGDBの2つを使います。
OpenOCD(Open On-Chip Debugger)は、デバッグアダプタを介して、組み込みプラットフォームのプログラミング、デバッグ、バウンダリスキャンを実行できるオープンソースソフトウェアのことです。OpenOCDを使用すると、ユーザーはデバッグ、プログラミング、テストのために組み込みターゲットシステムを制御および通信できます。但し、単独で使うものではなく、GDBなどとセットで使うことになります。その際、OpenOCDをサーバーモードで起動して、GDBの機能を使って、PC上のシリアルモニターに結果を出力するという流れになります。
一方、GDB(GNU Debugger)はその名の通りデバッグツールで、多くのプログラミング言語に対応しています。この機能としては色々とあるのですが、ここでの説明は割愛します。今回は、OpenOCDサーバーに流れてきたプログラムを実行させる用途でGDBを使います。
各コマンド実行の前に、Minicomの時と同様に、PCとProbeをUSB接続しておきます。そして、VSCodeも立ち上げておきます。さらに、2つのソフトウェアも以下の方法でインストール済みの状態になっていると仮定して進めます。
OpenOCD:
sudo apt install openocd
GDB:
sudo apt install gdb
※これでインストールできない端末の場合は、他のインストール方法を探してみて下さい。
それでは、実際の使い方の説明に入ります。
まずは以下のコマンドで、OpenOCDサーバーを起動します。
sudo openocd -f interface/cmsis-dap.cfg -f target/rp2040.cfg -c "adapter speed 5000"コマンド入力後、以下のように”starting gdb server on 3333″となっていれば成功です。

次に、2つ目のターミナルウィンドウを開いて、作成したプログラムのあるフォルダに移動し、今度は以下のGDBコマンドで、PCからOpenOCDサーバーに接続します。(上から一つずつ、順番に実行していきます)
※elfファイルは、私のプログラム内容でビルドしていれば、自動的に出来ているはずです。
$ gdb uart.elf
> target extended-remote :3333
> monitor reset init
> continueこれでデータ送信が始まっているので、今度は、VSCodeで受信できているかを確認します。
以下の通り、SERIAL MONITORタブで画面を開きます。

そして、以下の各項目を設定します。

Monitor Mode – -> Serial
View Mode – – > Text
Port – – > /dev/ttyACM0-Raspberry Pi
Baud rate – – > 115200
Line ending – – > None
以上の設定が終わったら、Start Monitoring ボタンを押して、モニタリングを開始します。
実際の実行結果は、以下の通りです。
無事に実行できました。めでたしめでたし🎉
以上、2つの方法で実行してみました。Minicomを使った方が、断然簡単でしたよね?🤗
これらの方法は、ラズパイPico Probeの使用方法として公式HPにも記載されていますので、気になったらこちらも確認してみて下さい。
まとめ
今回はUART通信のトライでした。ここまで読んで頂いてありがとうございました😀
ハードウェアフロー制御や双方向通信の確認なんかは今回やりませんでしたが、今後機会があったらトライしてみたいですね。
それにしても、VSCodeでの出力確認は実行するまでの手順が多すぎて、便利なのかどうかわからない感じでした😓 いや、Minicomが簡単すぎるのか?🤔
何にしても、組み込み開発で使うツールとして、各コマンドの機能や使い方を学ぶ良い機会となりました。
ツールとして使用したPicoデバッグProbeの作成に関する詳細や、OpenOCD、GDBなどのコマンド内容の説明については、今回しれっとすっ飛ばしましたが、詳しく知りたい方は頑張って勉強してみて下さい☺️
シリアル通信方式は他にもまだありますので、今後それらにも取り組む予定です。
それでは、今回はこの辺で🖐️
〆


