總結一次為R329開啟uart的經歷


sipeed出的maix sense開發板雖然宣稱有5個串口,但是uart0用作了debug,uart1用作的Bluetooth,uart2沒引出來,uart3只引出了RX和CTS,需要完整使用串口的話,只能使用ruart,並且maixsense上面預留的4Pin uart也接的也是這個。
image
好巧不巧的是,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 |

總結


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM