ラズパイ3でベアメタル - その3:シリアル通信(UART)でデータ送信(割り込みなし)

今回は、UARTを使用してシリアル通信で「ラズパイ3からPCへデータ送信」を作ってみます。
まずは割り込みは使用せずに作ってみます。


なお、Raspberry Pi 3で64bitベアメタル(bare metal)プログラミングを試してみる
本シリーズの目次はコチラです。


0. PCとRaspberry Pi 3のシリアル通信の準備

PCとRaspberry Pi 3のシリアル通信での接続方法は、「Raspberry Pi シリアル通信」辺りで検索すると色々と出てきます。
(Raspberry Pi 3の記事も既に色々なサイトで書かれている様ですが、Raspberry Pi側のピン番号等、
Raspberry Pi 2と同じなので、2の記事を参考にしても良いと思います。)


例えば、以下の記事が参考になるかと思います。

1. Mini UARTについて

UARTはシリアル通信を実現するために、送信・受信それぞれ1本ずつの信号線上に、
通信データフォーマットに合わせた形で1ビットずつ送信したり、その逆(受信)を行ったりする機能です。


BCM2837、というより、少なくともBCM2835からのBroadcomのSoCには、
以下の2つのUARTが存在します。(データシート(*1)を参照)

  • UART1: Mini UART
  • UART0: PL011


今回は、「最小限のコードで簡単に使える方を」という判断で、UART1のMini UARTを使います。
(VideoCoreにより初期化されているため、初期設定なしで使えます。)


(*1): Peripheral specification

(BCM2837のPeripheral specificationは公開されていないので、これが一番参考になります。)

2. UARTでのデータ送信について

UARTには送信・受信それぞれにFIFOがあり、
データ送信の際は、送信FIFOへデータを追加すると、
UARTにより送信の信号線上に1ビットずつ送出されます。


送信は1バイトずつで、手順は以下のとおりです。
1. UARTの送信FIFOの状態を確認
2. UARTの送信FIFOへ1バイト追加


なお、以降の説明で登場するレジスタのアドレスは、
(*1)で説明されているBCM2835のアドレスをRaspberry Pi 3用(BCM2837)に読み替えたものです。
(読み替えについてはその2:GPIO制御のブログ記事の通りです。)

2.1. UARTの送信FIFOの状態を確認

UARTの送受信のFIFO状態を確認するには、LSR(Line Status Register)というレジスタを使用します。


LSRのアドレスは0x3f215054で、送信FIFOの状態確認には、以下の2つのビットを確認します。

  • ビット6: Transmitter idle(以降、TX_IDLE)
    • 送信FIFOが空であり、かつトランスミッターがアイドル状態(最後のビットの送り出し(シフト)が完了した)場合に1がセットされる
  • ビット5: Transmitter empty(以降、TX_EMPTY)
    • 送信FIFOが少なくとも1バイト受け付けられる場合に1がセットされる


この2つのビットの組み合わせに対し、UARTのTxFIFOが1バイト受付可能か否かをまとめると以下のとおりです。

TX_IDLE TX_EMPTY 送信FIFOは1バイト受付可能か
0 0 受付不可
0 1 受付可
1 0 受付可、ただしTX_IDLE=1の時点でTX_EMPTY=1であるはずなので、この組み合わせは無い
1 1 受付可

この表から、TX_IDLEとTX_EMPTYが共に0ではない事を確認すれば、送信FIFOへ1バイト追加しても良いことがわかります。

2.2. UARTの送信FIFOへ1バイト追加

Mini UARTの場合、送信FIFOへのデータ追加と受信FIFOからのデータ取り出しは
共にIOレジスタで行います。
(レジスタへの書き込みで送信FIFOへデータ追加、
レジスタ読み出しで受信FIFOからのデータ取り出し)


ここではラズパイ3からPCへデータを送信してみたいので、
IOレジスタへデータを書き込んでみます。
なお、IOレジスタのアドレスは0x3f215040です。

3. ソースコード('A'を送信し続ける)

以上を元に、「無限ループで文字'A'を送信し続ける」ソースコードは以下のとおりです。

#define MU_IO		(*(volatile unsigned int *)0x3f215040)
#define MU_LSR		(*(volatile unsigned int *)0x3f215054)
#define MU_LSR_TX_IDLE	(1U << 6)
#define MU_LSR_TX_EMPTY	(1U << 5)

int main(void)
{
	while (1) {
		while (!(MU_LSR & MU_LSR_TX_IDLE) && !(MU_LSR & MU_LSR_TX_EMPTY));
		MU_IO = (unsigned int)'A';
	}

	return 0;
}


ソースコードのビルドとRaspberry Pi 3での実行方法は以下のブログ記事を参照して下さい。


また、Makefileを加えた必要なファイル一式は
以下のGitHubリポジトリの「uart_tx_char_simple」ディレクトリにあります。


上記のリポジトリをcloneしてビルドするまでの手順は以下のとおりです。

[PC]$ git clone https://github.com/cupnes/bare_metal_aarch64.git
[PC]$ cd bare_metal_aarch64/uart_tx_char_simple/
[PC]$ make
[PC]$ ls kernel8.img
kernel8.img


加えて、UARTの初期設定をVideoCoreで実行されるファームウェアに任せたいので、
ファームウェアが参照する設定ファイル「config.txt」もmicroSDに配置する必要があります。


以下の「config.txt」をkernel8.img同様にmicroSDFAT32パーティションに配置しておいて下さい。

enable_uart=1

4. 動作確認

PCとRaspberry Pi 3を接続し、Raspberry Pi 3を起動すると、
PC側でシリアルポートを開いている端末アプリの画面に「AAA...」と'A'が立て続けに表示されます。

5. その他、参考になる記事