◆ はじめに
SOPINE(sun50i)でレジスタを直接操作してGPIOとUARTを使う方法について記載します。
本稿ではAllwinnerA64のUART0を設定する方法になりますが、他のUARTも同じだと思います(多分)。
GPIOもPortBの#2(PB2)を設定する方法になりますが、(おそらく)他も一緒だと思います。
◆ PIN SELECT
最初に、GPIOのPB2とUART0のTXとRXを使うようにPINの設定を行います。
GPIOのPB2は、そのままの意味ですがPB2(bank:1, pin:2)を使います。
UART0のTXはPB8(bank:1, pin:8)、UART0のRXはPB9(bank:1, pin:9)を使います。
それぞれの設定は該当するレジスタの値をセットすればOKです。
各ピンにどの機能が割り当てられているかは、AllwinnerA64のUserManualのP.376〜を見てください。
◆ Port Configure Register
PortControllerのBaseAddressは0x01c20800になります。
各PINでどの機能を使うかはPort n Configure Register 0-3で設定します。
詳細は下記表になります。
レジスタ | offset | 対応GPIO |
---|---|---|
PB_CFG0 | +0x24 | PB0〜PB7 |
PB_CFG1 | +0x28 | PB8〜PB9 |
今回はPortBに限定してますが、PortC、PortD、PortDの場合はOffisetに+0x24、+0x48、+0x6Cを加算してください。
つまり一般化すると、
Pnmのピン(nはB,C,D,・・・,H、 mは0〜24)のConfigure Registerのアドレスは、
BaseAddress(0x01c20800) + 0x24 * p + 0x4 * q
(pは、B=1, C=2, D=3・・・、 q=0:0≦m≦7、1:8≦m≦15、2:16≦m≦23、3:24≦m≦31)
細かいのはUserManual参照でお願いします。
また、各ピンの機能はLSBから4bitごとに分けて設定します。
PB_CFG0の場合、[3:0]がPB0、[7:4]がPB1となります。
そこで、PB2をGPIOのOutput、PB8をUART0_TX、PB9をUART0_RXに設定する場合、下記のようにします。
レジスタ | Offset | bit | 設定値 |
---|---|---|---|
PB_CFG0 | +0x24 | [11:8] | 0b0001(0x1) |
PB_CFG1 | +0x28 | [3:0] | 0b0100(0x4) |
PB_CFG0 | +0x28 | [7:4] | 0b0100(0x4) |
◆ UART
UARTの設定では、順番に以下を設定します。
- クロックの設定
- ボーレートの設定
- 一度の送信ビット数、パリティ、ストップビットの設定
◆ クロックの設定
BUS_CLK_GATING_REG3(0x01c2006c)の[16]がUART0のクロックゲーティング用bitですので、
このbitを1にセットすればOKです。
なお、UserManualによるとこのクロックは24MHzのようです。
また、BUS_SOFT_RST_REG4(0x01c202d8)の[16]に1をセットします。
これでソフトウェアリセットがかかります。
◆ ボーレートの設定
今回はFIFOを使わない方法を記載します。
まずボーレートをDivisor Latch Low Registor(UART0_DLL)とDivisor Latch High Register(UART0_DLH)で指定することを、
UART0 Line Control Register(UART0_LCR:0x01c28000)で指定します。
UART0_LCR[7]に1をセットすればOKです。
このあと、UART0_DLLとUART0_DLHを使いDivisorを設定しますが、この設定値は以下のように算出します。
ボーレート = クロック周波数 / (16 * Divisor)
AllwinnerのUART0のボーレートは115,200にすること通例のようです。
また、クロック周波数は前述のとおり24MHzです。
そうなるとDivisorは下記の通りになります
Divisor = 24 * 1000 * 1000 / 16 / 115200 = 0xD
(余りが出るけどいいのだろうか・・・。サンプルでもコレだし動くから目をつぶります)
そこで、UART0_DDHに0x0、UART_DDLに0xDを設定します。
◆ 一度の送信ビット数、パリティ、ストップビットの設定
ここでは、一度の送信ビット数、パリティ、ストップビットの設定を行います。
それぞれは下記の通りにUART0_LCR(0x01c28000)を設定します。
設定項目 | bit | 設定値 |
---|---|---|
パリティ | [3] | 1:enable 0:disable |
ストップビット | [2] | 1:2bit 0:1bit |
送信bit数 | [1:0] | 11:8bit 10:7bit 01:6bit 00:5bit |
今回は、一度の送信で8bit、パリティビットなし、ストップビットは1bitで設定します。
したがって、0x00000003をUART0_LCR(0x01c28000)に書き込みます。
◆ 使ってみる
それでは、GPIOやUARTを使ってみます。
まずGPIOにHigh/Lowを出力させてみます。
PB2の場合、PB Data Registerを使います。
具体的には、PB_DATA_REG(0x01c20834)[2]がそれに該当しますので、
ここに1をセットすればHigh、0をセットすればLowになります。
次にUART0を使います。
FIFOを使ってないので、UART Transmit Holding Register(送信)、UART Receive Buffer Register(受信)を使います。
両方ともアドレスは0x01c28000となります。
ここの[7:0]に書き込み/読み込みした値がそれぞれ、UART0の送信/受信となります。
送信時は、UART Line Status Register(0x01c28014)[6]が0の時はデータ送信中となるので、
1になるまでループなりで待つ必要があります。
受信時は、UART Line Status Register(0x01c28014)[0]が0の時はデータ受信中になるので、
1になるまでループなりで待つ必要があります。
◆ おわりに
以上が、GPIO、UARTの設定と使う方法になります。
これでBareMetalプログラミングをする際にデバッグメッセージを出力させたり、
簡単なLED点灯による動作確認などができるかと思います。
次は今のコードをRust化させるところですが、どうなるやら。
あと、sunxi-felのhex[dump]が32bitのLSBの8bitしか表示されないことにハマりました。
自分の環境が悪いのかどうかは調査してないので不明ですが、結果として動いたのでよしとします。