前言
自筆者提出 serialX 串口驅動到今天近半年了,當初只在 STM32F4 NUC970 兩個系列芯片上做過理論驗證。一個是 ARM CM4 核心架構,一個是 ARM9。這兩款芯片能完美實現筆者的需求。
經過這半年的實踐考驗,筆者還是相信 serialX 的實力的,最近這幾天筆者嘗試在 N32 AB32 RA6M4 上適配 serialX,下面就向各位匯報一下適配結果。
芯片 | STM32F4 | NUC970 | N32 | AB32 | RA6M4 | GD32F4 |
架構 | CM4 | ARM9 | CM4 | RISC-V | CM33 | CM4 |
N32G45
因為這個也是 CM4,和 STM32F4 相較而言,可能差別很小。讓筆者感到欣慰的是用`DMA_GetFlagStatus(uart->dma_tx.dma_flag, uart->dma_tx.dma_module) == SET` 代替了 `uart->dmaTxing` 。這是一處小改進。
除此之外,沒啥可說的了。
已實現的功能有:
- 輪詢收發
- 中斷收發(可阻塞可非阻塞)
- DMA 收發(可阻塞可非阻塞)
AB32VG1
從芯片手冊我們可以看到,它的串口外設只有“接收一個字節完成”和“發送一個字節完成”兩個中斷。
在 serialX 的設計構想里,我們希望有個“發送寄存器空”中斷。因為這樣很容易啟動一次中斷,在中斷里判斷是否有數據需要發送,進而啟動一次發送過程。
假如沒有這個中斷,我們必須通過先寫一個字節引起一次“發送完成中斷”,然后借助這次中斷繼續判斷是否有數據需要發送。在數據所有數據發送完之前,我們還需要有個 flag 標識一下現在處于發送流程中。
因此,serialX 需要進行一些改動:
`_serial_int_tx` 函數
// TODO: start tx
#if defined (RT_SERIAL_NO_TXEIT)
if (serial->ops->is_int_txing != RT_NULL && serial->ops->is_int_txing(serial) == RT_FALSE) {
ch = _serial_fifo_pop_data(tx_fifo);
serial->ops->start_tx(serial, ch);
}
#else
serial->ops->start_tx(serial);
#endif
```
`struct rt_uart_ops`
```
#if defined (RT_SERIAL_NO_TXEIT)
rt_bool_t (*is_int_txing)(struct rt_serial_device *serial);
void (*start_tx)(struct rt_serial_device *serial, rt_uint8_t ch);
#else
void (*start_tx)(struct rt_serial_device *serial);
#endif
因為這些改動,AB32VG1 的底層驅動寫法也就不一樣了,多了一個判斷是否處于發送流程中的 api。start_tx stop_tx 也不僅僅是開關中斷那么簡單了,需要改變 intTxing 這個 flag 標識發送流程狀態。
rt_bool_t ab32_int_txing(struct rt_serial_device *serial)
{
struct ab32_uart *uart;
RT_ASSERT(serial != RT_NULL);
uart = rt_container_of(serial, struct ab32_uart, serial);
return uart->intTxing;
}
static void ab32_start_tx(struct rt_serial_device *serial, rt_uint8_t ch)
{
struct ab32_uart *uart;
RT_ASSERT(serial != RT_NULL);
uart = rt_container_of(serial, struct ab32_uart, serial);
uart->intTxing = RT_TRUE;
hal_uart_control(uart->handle.instance, UART_TXIT_ENABLE, HAL_ENABLE);
hal_uart_write(uart->handle.instance, ch);
}
static void ab32_stop_tx(struct rt_serial_device *serial)
{
struct ab32_uart *uart;
RT_ASSERT(serial != RT_NULL);
uart = rt_container_of(serial, struct ab32_uart, serial);
hal_uart_control(uart->handle.instance, UART_TXIT_ENABLE, HAL_DISABLE);
uart->intTxing = RT_FALSE;
}
為此,我們需要添加個新配置,components/drivers/Kconfig
config RT_SERIAL_NO_TXEIT
bool "No TX Empty interrupt"
default n
help
Useful only if the chip hasn't Transmit Register Empty interrupt
Such as: AB32 RA6M4
意思是說,當芯片沒有“發送寄存器空中斷”支持的時候,我們需要用 `intTxing` 代替實現控制發送過程。
另外,發送寄存器也沒有空狀態,`putc` 函數倒是可以判斷發送完成標志,但是這樣就不能在中斷里調用 `putc` 了;不加發送完成判斷,就不能在輪詢發送中調用它。總之,輪詢發送和中斷發送不用用一樣的 `putc` 函數了。
已實現的功能有:
- 中斷收發(可阻塞可非阻塞)
RA6M4
RA6M4 是一款 CM33 核 ARM 芯片,本以為它比 CM4 高級可以很容易實現 CM4 上實現的操作。
但是,筆者也沒有從手冊中找到“發送寄存器空中斷”。所以 RA6M4 和 AB32VG1 有一樣的補救處理。
但是,筆者還發現另外一個問題,**如果是中斷發送,每次寫完 TDR 寄存器后,必須重新使能發送中斷**。不這樣做,就不會出現發送完成中斷。
雖然如此,連續發送多個字節仍然會出現發送中斷不觸發(或丟失)的情況,導致發送功能完全癱瘓(這也是 `intTxing` 引入的隱患)。
已實現的功能有:
- 中斷接收(可阻塞可非阻塞)
- 中斷發送(未完),暫時可以用輪詢發送代替
多說兩句,RA6M4 的 SCI 好像可以啟用 FIFO ,這樣一來串口收發寄存器就是帶 FIFO 的。遺憾的是筆者不會用啊,有會用的大佬可以嘗試移植一下,用 FIFO 了就相當于用 DMA 了。
GD32F4
這個也可以做到和 STM32F4 一樣的程度,DMA 沒有發送標志,只能繼續用 `dmaTxing` 。
已實現的功能有:
- 輪詢收發
- 中斷收發(可阻塞可非阻塞)
- DMA收發(可阻塞可非阻塞)
注:只分配了 UART0 的 DMA 通道,如果其它的也需要開啟 DMA 請自行修改 `struct gd32_uart uarts` 數組變量分配 DMA 通道。
注:還有一點,rt-studio 里下載的 GD32F4 firmware 庫版本是很多年前的,現在已經改動過好幾次了。筆者使用的 `gd32f4xx_usart.h` 版本是 “2020-09-30, V2.1.0, firmware for GD32F4xx” 。如有編譯錯誤請升級 firmware 庫。
結束語
關于 serialX 理論的部分,之前的文章已經說的夠多了。這次是想在多種平臺上用實踐檢驗一下 serialX 理論的可行性。經過這幾天的投入,最終多多少少有些收獲,還是很欣慰的。
匯總一下,目前可以適配的芯片包括如下幾類
1. 沒有 DMA ,只有串口接收發送中斷
2. 沒有“發送寄存器空”狀態或沒有“發送寄存器空”中斷
3. 帶接收 IDLE 檢測,帶“發送寄存器空”中斷
4. 帶 DMA ,并且至少有 DMA 半傳輸中斷和全傳輸中斷
5. 串口外設自帶收發 FIFO (可認為是 DMA ,但是比 DMA 使用更簡單)
在此,特別感謝[嚜軒公告](https://club.rt-thread.org/u/7c37fff6229d1ccd)支援的開發板,最終完成了 serialX 在這些平臺上的實現。
下期預告,我們來扒一扒 serialX 的缺陷,對,它的缺陷。準確的講是在 RTOS 上引入的坑有哪些以及怎么避免。
附 [serialX](https://gitee.com/thewon/serialX) 倉庫地址,感興趣的可以下載最新版 serialX 源碼。本文提到的幾種芯片的驅動也都已提交。
審核編輯:湯梓紅
-
ARM
+關注
關注
134文章
9057瀏覽量
366873 -
N32
+關注
關注
0文章
18瀏覽量
7195 -
STM32F4
+關注
關注
3文章
194瀏覽量
28003 -
RT-Thread
+關注
關注
31文章
1274瀏覽量
39940 -
serialX
+關注
關注
0文章
7瀏覽量
803
發布評論請先 登錄
相關推薦
評論