sipeed出的maix sense開發板雖然宣稱有5個串口,但是uart0用作了debug,uart1用作的Bluetooth,uart2沒引出來,uart3只引出了RX和CTS,需要完整使用串口的話,只能使用ruart,並且maixsense上面預留的4Pin uart也接的也是這個。
好巧不巧的是,sipeed發布的第一版armbian鏡像並沒有啟用其他串口,對ttyS2-ttyS5操作沒有任何用,只能手動編輯設備樹。
查看設備樹
maix sense的kernel官方已經上傳到github上,先clone下來
git clone -b r329-wip --depth 1 https://github.com/sipeed/linux.git
設備樹在/linux/arch/arm64/boot/dts/allwinner/
路徑下,和r329相關的設備樹有
sun50i-r329-maix-iia.dtsi
sun50i-r329-maixsense.dts
sun50i-r329.dtsi
三個。
其中uart的描述在中sun50i-r329.dtsi
,如下:
uart0: serial@2500000 {
compatible = "snps,dw-apb-uart";
reg = <0x02500000 0x400>;
interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>;
reg-shift = <2>;
reg-io-width = <4>;
clocks = <&ccu CLK_BUS_UART0>;
resets = <&ccu RST_BUS_UART0>;
status = "disabled";
};
uart1: serial@2500400 {
compatible = "snps,dw-apb-uart";
reg = <0x02500400 0x400>;
interrupts = <GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>;
reg-shift = <2>;
reg-io-width = <4>;
clocks = <&ccu CLK_BUS_UART1>;
resets = <&ccu RST_BUS_UART1>;
status = "disabled";
};
uart2: serial@2500800 {
compatible = "snps,dw-apb-uart";
reg = <0x02500800 0x400>;
interrupts = <GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>;
reg-shift = <2>;
reg-io-width = <4>;
clocks = <&ccu CLK_BUS_UART2>;
resets = <&ccu RST_BUS_UART2>;
status = "disabled";
};
uart3: serial@2500c00 {
compatible = "snps,dw-apb-uart";
reg = <0x02500c00 0x400>;
interrupts = <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>;
reg-shift = <2>;
reg-io-width = <4>;
clocks = <&ccu CLK_BUS_UART3>;
resets = <&ccu RST_BUS_UART3>;
status = "disabled";
};
引腳定義也在該文件中
uart0_pb_pins: uart0-pb-pins {
pins = "PB4", "PB5";
function = "uart0";
};
uart1_pg_pins: uart1-pg-pins {
pins = "PG6", "PG7";
function = "uart1";
};
uart fuction在另外兩個文件中,其中uart0被用做了stdout-path
,uart1被用作了bluetooth
。
aliases {
serial0 = &uart0;
mmc0 = &mmc0;
};
chosen {
stdout-path = "serial0:115200n8";
};
&uart0 {
pinctrl-names = "default";
pinctrl-0 = <&uart0_pb_pins>;
status = "okay";
};
&uart1 {
pinctrl-names = "default";
pinctrl-0 = <&uart1_pg_pins>, <&uart1_pg_rts_cts_pins>;
uart-has-rtscts;
status = "okay";
bluetooth {
compatible = "realtek,rtl8723ds-bt";
device-wake-gpios = <&r_pio 1 1 GPIO_ACTIVE_HIGH>; /* PM1 */
host-wake-gpios = <&r_pio 1 3 GPIO_ACTIVE_HIGH>; /* PM3 */
enable-gpios = <&r_pio 1 2 GPIO_ACTIVE_HIGH>; /* PM2 */
max-speed = <1500000>;
};
};
添加uart3節點
可以看到,只有uart0和uart1定義了pinctrl-0和function,
其他uart沒有定義,所以在linux中沒法使用。
uart3已經描述,要開啟uart3,只需要添加uart3的pinctrl-0和function即可。
照葫蘆畫瓢,在sun50i-r329.dtsi
文件中添加pinctrl-0
uart3_ph_pins: uart3-ph-pins {
pins = "PH4", "PH5";
function = "uart3";
};
在sun50i-r329-maixsense.dts
中添加uart3 function
&uart3 {
pinctrl-names = "default";
pinctrl-0 = <&uart3_ph_pins>;
status = "okay";
};
即可。
編譯dtb make dtbs
,放入maixsense的boot/dtb/allwinner/文件夾中,即可使用。
測試uart3
上電,PH5引腳接RXD。
查看ttyS*
maixsense:~:# ls /dev/ttyS*
/dev/ttyS0 /dev/ttyS2 /dev/ttyS3 /dev/ttyS4 /dev/ttyS5
沒有出現ttyS1,可能為某個設備保留,測試ttyS2
使用minicom接收數據 minicom -D /dev/ttyS2
Welcome to minicom 2.8
OPTIONS: I18n
Port /dev/ttyS2, 16:00:56
Press CTRL-A Z for help on special keys
hello sipeed
UART RXD正常。
但是發現lcd屏幕無法刷新了,查看原理圖發現PH4連接了LCD的DC,導致了功能沖突。
修改sun50i-r329.dtsi
,設置uart3_tx_pin為缺省值。
uart3_ph_pins: uart3-ph-pins {
pins = "", "PH5";
function = "uart3";
};
重新編譯,上電,一切正常。
添加s_uart
uart3雖然能用了,但是只能收不能發,若要使用全功能uart,還需要啟用s_uart。
設備樹中並沒有s_uart相關的描述,因此需要自行添加。
參考uart0的描述,其中reg,interrupts,clocks和resets我們都不知道,因此添加s_uart的重點就在於查找這些參數。
uart0: serial@2500000 {
compatible = "snps,dw-apb-uart";
reg = <0x02500000 0x400>;
interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>;
reg-shift = <2>;
reg-io-width = <4>;
clocks = <&ccu CLK_BUS_UART0>;
resets = <&ccu RST_BUS_UART0>;
status = "disabled";
};
查找技術手冊
翻開R329 User Manual,找到7.2 UART一章
在7.2.5 Register List小節中,提供了uart的reg基址。
Module Name | Base Address |
---|---|
UART0 | 0x02500000 |
UART1 | 0x02500400 |
UART2 | 0x02500800 |
UART3 | 0x02500C00 |
R_UART0 | 0x07080000 |
其中r_uart0就是我們需要的s_uart(為啥是?因為沒別的了。為啥兩個代號?我也不清楚) |
reg get!
r_uart: serial@7080000 {
compatible = "snps,dw-apb-uart";
reg = <0x0708000 0x400>;
reg-shift = <2>;
reg-io-width = <4>;
接下來需要查找的參數是interrupts,找到3.8 Generic Interrupt Controller (GIC) 一章。
在Table 3-10 Interrupt Sources 表格中,描述了所有的中斷號。
Module Name | Interrupt Number |
---|---|
34 | UART0 |
35 | UART1 |
36 | UART2 |
37 | UART3 |
143 | R_UART0 |
參考標准串口使用的GIC_SPI (SPI:shared processor interrupts 中斷號 32 ~32+224), |
|
得到r_uart的中斷號143-32=111 |
Interrupt get!
r_uart: serial@7080000 {
compatible = "snps,dw-apb-uart";
reg = <0x0708000 0x400>;
reg-shift = <2>;
reg-io-width = <4>;
接下來就是clocks和resets。
參考設備樹中其他信息,CPUS Domain Related
的資源,也就是中斷號120以后的資源,使用的是r_ccu
lradc: lradc@7030800 {
compatible = "allwinner,sun50i-r329-lradc";
reg = <0x07030800 0x400>;
interrupts = <GIC_SPI 115 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&r_ccu CLK_R_BUS_LRADC>;
resets = <&r_ccu RST_R_BUS_LRADC>;
status = "disabled";
};
其定義在
#include <dt-bindings/clock/sun50i-r329-r-ccu.h>
#include <dt-bindings/reset/sun50i-r329-r-ccu.h>
中。
All get!
r_uart: serial@7080000 {
compatible = "snps,dw-apb-uart";
reg = <0x07080000 0x400>;
interrupts = <GIC_SPI 111 IRQ_TYPE_LEVEL_HIGH>;
reg-shift = <2>;
reg-io-width = <4>;
clocks = <&r_ccu CLK_R_BUS_UART>;
resets = <&r_ccu RST_R_BUS_UART>;
};
將s_uart的描述插入sun50i-r329.dtsi中。
配置s_uart
s_uart由cpus控制,因此要把相關描述放入r_pio下
r_pio: pinctrl@7022000 {
compatible = "allwinner,sun50i-r329-r-pinctrl";
reg = <0x07022000 0x400>;
interrupts = <GIC_SPI 107 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 109 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&r_ccu CLK_R_APB1>, <&osc24M>, <&rtc 0>;
clock-names = "apb", "hosc", "losc";
gpio-controller;
#gpio-cells = <3>;
interrupt-controller;
#interrupt-cells = <3>;
r_uart_pins: r-uart-pins {
pins = "PL8", "PL9";
function = "s_uart";
};
然后在sun50i-r329-maixsense.dts中開啟r_uart
&r_uart{
status = "okay";
};
r_uart的設備樹配置就完成了。
但是實際上,在此時的版本中,r_uart還是無法使用。因為在pinctrl中,並沒有cpus和cpux下的控制器定義,在Icenowy提交了該次pr后,才能正常使用。
新增的相關引腳配置如下
SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 7),
SUNXI_FUNCTION(0x0, "gpio_in"),
SUNXI_FUNCTION(0x1, "gpio_out"),
SUNXI_FUNCTION(0x2, "s_ir"), /* RX */
SUNXI_FUNCTION(0x4, "clock"), /* X32KFOUT */
SUNXI_FUNCTION(0x5, "s_pwm"), /* PWM5 */
SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 7)), /* PL_EINT7 */
SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 8),
SUNXI_FUNCTION(0x0, "gpio_in"),
SUNXI_FUNCTION(0x1, "gpio_out"),
SUNXI_FUNCTION(0x2, "s_uart"), /* TX */
SUNXI_FUNCTION(0x3, "s_i2c"), /* SDA */
SUNXI_FUNCTION(0x4, "s_ir"), /* RX */
SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 8)), /* PL_EINT8 */
可以發現實際上function = "s_uart";
調用的function由SUNXI_FUNCTION(0x2, "s_uart")
定義。
測試s_uart
上電測試時,發現了一個新問題,啟動uboot時,log打印在uart0上,但是啟動kernel以后,數據飛了。開始以為是內核卡死了,但是lcd有輸出,於是使用ssh連接上,使用minicom操作串口后發現,ttyS0被連接到r_uart上了,uart0此時變成了ttyS2。
最后的解決辦法是在aliases中給r_uart設置別名
aliases {
serial0 = &uart0;
serial1 = &r_uart;
mmc0 = &mmc0;
};
經過測試,/dev/ttyS1只有經過aliases分配編號后才能使用,並且只有r_uart可以使用,並且r_uart只會使用/dev/ttyS0和/dev/ttyS1。
當未給r_uart分配編號時,r_uart會搶占/dev/ttyS0,因此所有信息由r_uart輸出。
此時uartx會被分配到/dev/ttyS2以后。並且aliases只能分配serial0-serial3之間的編號,即/dev/ttyS0到/dev/ttyS3之間。在給uart分配serial4及以后的編號時,ttyS*不會有任何反應。(目前原因未知)
重新編譯后,串口就能正常使用了。
maixsense:~:# ls /dev/ttyS*
/dev/ttyS0 /dev/ttyS1 /dev/ttyS3 /dev/ttyS4 /dev/ttyS5
|/dev/ttyS0| /dev/ttyS1 |/dev/ttyS3 |
| ---- | ---- |---- |---- |
| uart0|r_uart |uart3 |