UART介紹
UART(Universal Asynchronous Receiver/Transmitter,通用異步收發傳輸器)也常被稱為串口。UART作為異步串口通信協議的一種,工作原理是將傳輸數據的每個字符一位接一位地傳輸。UART是在應用程序開發過程中使用頻率最高的數據總線。在嵌入式設計中,UART常用于主機與輔助設備通信,如嵌入式設備與外接模塊(Wi-Fi、藍牙模塊等)的通信,嵌入式設備與PC監視器的通信,或用于兩個嵌入式設備之間的通信。
UART串口屬于字符設備的一種,它的硬件連接也比較簡單,只要兩根傳輸線就可以實現雙向通信:一根線(TX)發送數據,另一根線(RX)接收數據。
UART串口通信有幾個重要的參數,分別是波特率、起始位、數據位、停止位和奇偶檢驗位,對于兩個使用UART串口通信的端口,這些參數必須匹配,否則通信將無法正常完成。
數據格式包含起始位、數據位、奇偶校驗位、停止位。
起始位:表示數據傳輸的開始,電平邏輯為“0”。
數據位:數據位通常為8bit的數據(一個字節),但也可以是其他大小,例如5bit、6bit、7bit,表示傳輸數據的位數。
奇偶校驗位:用于接收方對接收到的數據進行校驗,校驗一個二進制數中“1”的個數為偶數(偶校驗)或奇數(奇校驗),以此來校驗數據傳送的正確性,使用時也可以不需要此位。
停止位:表示一幀數據的結束,電平邏輯為“1”。
波特率:串口通信時的速率,它用單位時間內傳輸的二進制代碼的有效位數來表示,其單位為bit/s。常見的波特率值有4800、9600、14400、38400、115200等,數值越大數據傳輸越快,波特率為115200表示每秒傳輸115200位數據。
UART v2.0版本的UART框架和驅動講解
UART層級結構
1)I/O設備管理層向應用層提供rt_device_read/write等標準接口,應用層可以通過這些標準接口訪問UART設備。
2)UART設備驅動框架源碼文件為serial_v2.c,位于RT-Thread源碼的componentsdriversserial文件夾中。抽象出的UART設備驅動框架和平臺無關,是一層通用的軟件層。UART設備驅動框架提供以下功能。
①對接上層的I/O設備管理層,以讓應用層調用I/O設備管理層提供的統一接口對UART進行操作。
②UART設備驅動框架向UART設備驅動層提供UART設備操作方法接口struct rt_uart_ops(如configure、control、putc、getc、transmit),驅動開發者需要實現這些接口。
③提供設備注冊管理接口rt_hw_serial_register和中斷處理接口rt_hw_serial_isr。4929.html
3)UART設備驅動源碼文件為drv_usartv2.c,放在具體bsp目錄下,v2表示對接在串口v2版本的設備驅動框架上。UART設備驅動的實現與平臺相關,它操作具體的MCU UART控制器。UART設備驅動需要實現UART設備的操作方法struct rt_uart_ops,以提供訪問和控制UART硬件的能力。這一層也負責調用rt_hw_serial_register函數將UART設備注冊到操作系統。最后還需調用中斷處理接口rt_hw_serial_isr,通知UART設備驅動框架層處理數據。
4)最下面一層是MCU外接的UART模塊,如UART通信模塊、RS-232芯片或者RS-485芯片電路模塊等,這樣MCU就可以與外接模塊進行數據通信了。
UART設備驅動開發的主要任務就是實現串口設備操作方法接口struct rt_uart_ops,然后注冊串口設備。
查看代碼,串口初始化:
int rt_hw_usart_init(void)
{
rt_size_t obj_num = sizeof(uart_obj) / sizeof(struct stm32_uart);
struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;
rt_err_t result = 0;
stm32_uart_get_dma_config();
for (int i = 0; i < obj_num; i++)
{
uart_obj[i].config = &uart_config[i];
uart_obj[i].serial.ops = &stm32_uart_ops;
uart_obj[i].serial.config = config;
/* register UART device */
result = rt_hw_serial_register(&uart_obj[i].serial, uart_obj[i].config->name,
RT_DEVICE_FLAG_RDWR
| RT_DEVICE_FLAG_INT_RX
| RT_DEVICE_FLAG_INT_TX
| uart_obj[i].uart_dma_flag
, NULL);
RT_ASSERT(result == RT_EOK);
}
return result;
}
創建UART設備。對UART設備來說,在驅動開發時需要先從struct rt_serial_device結構中派生出新的串口設備模型,然后根據自己的設備類型定義私有數據域。特別是在可能有多個類似設備的情況下(例如串口1、串口2),設備接口可以共用同一套接口,不同的只是各自的數據域(例如寄存器基地址)。
例如,STM32的UART設備模型從struct rt_serial_device派生,并增加了STM32UART的特有數據結構,如STM32串口句柄、串口配置信息、DMA結構信息等。
/* stm32 uart dirver class */
struct stm32_uart
{
UART_HandleTypeDef handle;
struct stm32_uart_config *config;
#ifdef RT_SERIAL_USING_DMA
struct
{
DMA_HandleTypeDef handle;
rt_size_t last_index;
} dma_rx;
struct
{
DMA_HandleTypeDef handle;
} dma_tx;
#endif
rt_uint16_t uart_dma_flag;
struct rt_serial_device serial;
};
實現UART設備的操作方法
struct rt_uart_ops
{
rt_err_t (*configure)(struct rt_serial_device *serial,
struct serial_configure *cfg);
rt_err_t (*control)(struct rt_serial_device *serial,
int cmd,
void *arg);
int (*putc)(struct rt_serial_device *serial, char c);
int (*getc)(struct rt_serial_device *serial);
rt_size_t (*transmit)(struct rt_serial_device *serial,
rt_uint8_t *buf,
rt_size_t size,
rt_uint32_t tx_flag);
};
這些操作方法會完成串口的基本操作,例如:configure方法用于配置串口(波特率等);control方法用于控制串口;putc方法用于串口向外發送字符數據;getc方法用于串口獲取字符數據;transmit方法用于數據發送,主要是進行多字節數據的發送。下面繼續講解如何實現這些操作方法。
注冊UART設備
UART設備的操作方法實現后需要注冊設備到操作系統,注冊UART設備的接口是rt_err_t rt_hw_serial_register(struct rt_serial_device serial,const char name,rt_uint32_t flag, void *data)。
UART設備中斷處理
增加DMA模式
增加UART設備DMA模式,需要首先對每個UART的DMA進行配置,接著進行DMA初始化和中斷處理,最后完成DMA發送。以下是DMA配置代碼。
驅動配置
RT-Thread使用SCons構建工程,使用基于Kconfig機制的menuconfig工具配置工程。因此不僅要實現驅動,還要實現驅動相關的配置選項:一是Kconfig配置,配置好的配置文件將會在menuconfig工具中形成對應的配置界面;二是進行SConscript配置,配置好后,相應的驅動文件將會被添加到工程中。后面各章的驅動相關配置選項與此類似,如無特殊配置將不再贅述。
1.Kconfig配置
下面參考bsp/stm32/stm32f407-atk-explorer/board/Kconfig文件配置串口驅動的相關選項,如下所示:
menuconfig BSP_USING_UART
bool "Enable UART"
default y
select RT_USING_SERIAL
if BSP_USING_UART
config BSP_USING_UART0
bool "Enable UART0"
default y
config BSP_UART0_RX_USING_DMA
bool "Enable UART0 RX DMA"
depends on BSP_USING_UART0
select RT_SERIAL_USING_DMA
default n
config BSP_USING_UART1
bool "Enable UART1"
default n
config BSP_UART1_RX_USING_DMA
bool "Enable UART1 RX DMA"
depends on BSP_USING_UART1
select RT_SERIAL_USING_DMA
default n
config BSP_USING_UART2
bool "Enable UART2"
default n
config BSP_UART2_RX_USING_DMA
bool "Enable UART2 RX DMA"
depends on BSP_USING_UART2
select RT_SERIAL_USING_DMA
default n
config BSP_USING_UART3
bool "Enable UART3"
default n
config BSP_UART3_RX_USING_DMA
bool "Enable UART3 RX DMA"
depends on BSP_USING_UART3
select RT_SERIAL_USING_DMA
default n
config BSP_USING_UART4
bool "Enable UART4"
default n
config BSP_UART4_RX_USING_DMA
bool "Enable UART4 RX DMA"
depends on BSP_USING_UART4
select RT_SERIAL_USING_DMA
default n
config BSP_USING_UART5
bool "Enable UART5"
default n
config BSP_UART5_RX_USING_DMA
bool "Enable UART5 RX DMA"
depends on BSP_USING_UART5
select RT_SERIAL_USING_DMA
default n
config BSP_USING_UART6
bool "Enable UART6"
default n
config BSP_UART6_RX_USING_DMA
bool "Enable UART6 RX DMA"
depends on BSP_USING_UART6
select RT_SERIAL_USING_DMA
default n
config BSP_USING_UART7
bool "Enable UART7"
default n
config BSP_UART7_RX_USING_DMA
bool "Enable UART7 RX DMA"
depends on BSP_USING_UART7
select RT_SERIAL_USING_DMA
default n
endif
代碼段中相關宏的說明如下所示。
BSP_USING_UART:串口驅動代碼對應的宏定義,這個宏控制串口驅動相關代碼是否會添加到工程中。
RT_USING_SERIAL:串口驅動框架代碼對應的宏定義,這個宏控制串口驅動框架的相關代碼是否會添加到工程中。
BSP_USING_UART1:串口設備1對應的宏定義,這個宏控制串口設備1是否會注冊到系統中。
BSP_UART1_RX_USING_DMA:串口設備1使用DMA接收數據。
2.SConscript配置
在HAL_Drivers/SConscript文件中為串口驅動添加判斷選項,代碼如下所示。這是一段Python代碼,表示如果定義了宏BSP_USING_UART,則drv_uart.c會被添加到工程的源文件中。
if GetDepend(['RT_USING_SERIAL']):
src += ['drv_usart.c']
注冊設備之后,UART設備將以字符設備的形式在I/O設備管理器中存在。系統啟動并開始運行后,可以在終端使用list_device命令看到注冊的設備包含了UART設備,之后則可以使用UART設備驅動框架提供的統一API對UART設備進行操作。
小結
在RT-Thread中,將UART外設抽象為UART設備,并結合UART設備的通用操作方法與驅動框架思想設計出UART設備驅動框架,這為開發者提供了更便利的設備控制方式。同時,這使基于UART設備編寫出來的應用代碼更具兼容性與通用性。開發者還需要注意以下兩點。
1)操作方法的名稱可以自定義,但不要脫離實際意義,并且需要遵守代碼規范。所有的操作方法/函數都屬于內部函數,在函數實現時,需要使用static進行修飾。本條注意事項對每種驅動都適用,后面章節將不再贅述。
2)在進入與退出中斷時,需要調用中斷進入和中斷退出函數,如下所示。本條注意事項對每種驅動都適用,后面章節將不再贅述。
評論
查看更多