UART介紹
UART(Universal Asynchronous Receiver/Transmitter,通用異步收發(fā)傳輸器)也常被稱(chēng)為串口。UART作為異步串口通信協(xié)議的一種,工作原理是將傳輸數(shù)據(jù)的每個(gè)字符一位接一位地傳輸。UART是在應(yīng)用程序開(kāi)發(fā)過(guò)程中使用頻率最高的數(shù)據(jù)總線(xiàn)。在嵌入式設(shè)計(jì)中,UART常用于主機(jī)與輔助設(shè)備通信,如嵌入式設(shè)備與外接模塊(Wi-Fi、藍(lán)牙模塊等)的通信,嵌入式設(shè)備與PC監(jiān)視器的通信,或用于兩個(gè)嵌入式設(shè)備之間的通信。
UART串口屬于字符設(shè)備的一種,它的硬件連接也比較簡(jiǎn)單,只要兩根傳輸線(xiàn)就可以實(shí)現(xiàn)雙向通信:一根線(xiàn)(TX)發(fā)送數(shù)據(jù),另一根線(xiàn)(RX)接收數(shù)據(jù)。
UART串口通信有幾個(gè)重要的參數(shù),分別是波特率、起始位、數(shù)據(jù)位、停止位和奇偶檢驗(yàn)位,對(duì)于兩個(gè)使用UART串口通信的端口,這些參數(shù)必須匹配,否則通信將無(wú)法正常完成。
數(shù)據(jù)格式包含起始位、數(shù)據(jù)位、奇偶校驗(yàn)位、停止位。
起始位:表示數(shù)據(jù)傳輸?shù)拈_(kāi)始,電平邏輯為“0”。
數(shù)據(jù)位:數(shù)據(jù)位通常為8bit的數(shù)據(jù)(一個(gè)字節(jié)),但也可以是其他大小,例如5bit、6bit、7bit,表示傳輸數(shù)據(jù)的位數(shù)。
奇偶校驗(yàn)位:用于接收方對(duì)接收到的數(shù)據(jù)進(jìn)行校驗(yàn),校驗(yàn)一個(gè)二進(jìn)制數(shù)中“1”的個(gè)數(shù)為偶數(shù)(偶校驗(yàn))或奇數(shù)(奇校驗(yàn)),以此來(lái)校驗(yàn)數(shù)據(jù)傳送的正確性,使用時(shí)也可以不需要此位。
停止位:表示一幀數(shù)據(jù)的結(jié)束,電平邏輯為“1”。
波特率:串口通信時(shí)的速率,它用單位時(shí)間內(nèi)傳輸?shù)亩M(jìn)制代碼的有效位數(shù)來(lái)表示,其單位為bit/s。常見(jiàn)的波特率值有4800、9600、14400、38400、115200等,數(shù)值越大數(shù)據(jù)傳輸越快,波特率為115200表示每秒傳輸115200位數(shù)據(jù)。
UART v2.0版本的UART框架和驅(qū)動(dòng)講解
UART層級(jí)結(jié)構(gòu)
1)I/O設(shè)備管理層向應(yīng)用層提供rt_device_read/write等標(biāo)準(zhǔn)接口,應(yīng)用層可以通過(guò)這些標(biāo)準(zhǔn)接口訪(fǎng)問(wèn)UART設(shè)備。
2)UART設(shè)備驅(qū)動(dòng)框架源碼文件為serial_v2.c,位于RT-Thread源碼的componentsdriversserial文件夾中。抽象出的UART設(shè)備驅(qū)動(dòng)框架和平臺(tái)無(wú)關(guān),是一層通用的軟件層。UART設(shè)備驅(qū)動(dòng)框架提供以下功能。
①對(duì)接上層的I/O設(shè)備管理層,以讓?xiě)?yīng)用層調(diào)用I/O設(shè)備管理層提供的統(tǒng)一接口對(duì)UART進(jìn)行操作。
②UART設(shè)備驅(qū)動(dòng)框架向UART設(shè)備驅(qū)動(dòng)層提供UART設(shè)備操作方法接口struct rt_uart_ops(如configure、control、putc、getc、transmit),驅(qū)動(dòng)開(kāi)發(fā)者需要實(shí)現(xiàn)這些接口。
③提供設(shè)備注冊(cè)管理接口rt_hw_serial_register和中斷處理接口rt_hw_serial_isr。4929.html
3)UART設(shè)備驅(qū)動(dòng)源碼文件為drv_usartv2.c,放在具體bsp目錄下,v2表示對(duì)接在串口v2版本的設(shè)備驅(qū)動(dòng)框架上。UART設(shè)備驅(qū)動(dòng)的實(shí)現(xiàn)與平臺(tái)相關(guān),它操作具體的MCU UART控制器。UART設(shè)備驅(qū)動(dòng)需要實(shí)現(xiàn)UART設(shè)備的操作方法struct rt_uart_ops,以提供訪(fǎng)問(wèn)和控制UART硬件的能力。這一層也負(fù)責(zé)調(diào)用rt_hw_serial_register函數(shù)將UART設(shè)備注冊(cè)到操作系統(tǒng)。最后還需調(diào)用中斷處理接口rt_hw_serial_isr,通知UART設(shè)備驅(qū)動(dòng)框架層處理數(shù)據(jù)。
4)最下面一層是MCU外接的UART模塊,如UART通信模塊、RS-232芯片或者RS-485芯片電路模塊等,這樣MCU就可以與外接模塊進(jìn)行數(shù)據(jù)通信了。
UART設(shè)備驅(qū)動(dòng)開(kāi)發(fā)的主要任務(wù)就是實(shí)現(xiàn)串口設(shè)備操作方法接口struct rt_uart_ops,然后注冊(cè)串口設(shè)備。
查看代碼,串口初始化:
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;
}
創(chuàng)建UART設(shè)備。對(duì)UART設(shè)備來(lái)說(shuō),在驅(qū)動(dòng)開(kāi)發(fā)時(shí)需要先從struct rt_serial_device結(jié)構(gòu)中派生出新的串口設(shè)備模型,然后根據(jù)自己的設(shè)備類(lèi)型定義私有數(shù)據(jù)域。特別是在可能有多個(gè)類(lèi)似設(shè)備的情況下(例如串口1、串口2),設(shè)備接口可以共用同一套接口,不同的只是各自的數(shù)據(jù)域(例如寄存器基地址)。
例如,STM32的UART設(shè)備模型從struct rt_serial_device派生,并增加了STM32UART的特有數(shù)據(jù)結(jié)構(gòu),如STM32串口句柄、串口配置信息、DMA結(jié)構(gòu)信息等。
/* 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;
};
實(shí)現(xiàn)UART設(shè)備的操作方法
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);
};
這些操作方法會(huì)完成串口的基本操作,例如:configure方法用于配置串口(波特率等);control方法用于控制串口;putc方法用于串口向外發(fā)送字符數(shù)據(jù);getc方法用于串口獲取字符數(shù)據(jù);transmit方法用于數(shù)據(jù)發(fā)送,主要是進(jìn)行多字節(jié)數(shù)據(jù)的發(fā)送。下面繼續(xù)講解如何實(shí)現(xiàn)這些操作方法。
注冊(cè)UART設(shè)備
UART設(shè)備的操作方法實(shí)現(xiàn)后需要注冊(cè)設(shè)備到操作系統(tǒng),注冊(cè)UART設(shè)備的接口是rt_err_t rt_hw_serial_register(struct rt_serial_device serial,const char name,rt_uint32_t flag, void *data)。
UART設(shè)備中斷處理
增加DMA模式
增加UART設(shè)備DMA模式,需要首先對(duì)每個(gè)UART的DMA進(jìn)行配置,接著進(jìn)行DMA初始化和中斷處理,最后完成DMA發(fā)送。以下是DMA配置代碼。
驅(qū)動(dòng)配置
RT-Thread使用SCons構(gòu)建工程,使用基于Kconfig機(jī)制的menuconfig工具配置工程。因此不僅要實(shí)現(xiàn)驅(qū)動(dòng),還要實(shí)現(xiàn)驅(qū)動(dòng)相關(guān)的配置選項(xiàng):一是Kconfig配置,配置好的配置文件將會(huì)在menuconfig工具中形成對(duì)應(yīng)的配置界面;二是進(jìn)行SConscript配置,配置好后,相應(yīng)的驅(qū)動(dòng)文件將會(huì)被添加到工程中。后面各章的驅(qū)動(dòng)相關(guān)配置選項(xiàng)與此類(lèi)似,如無(wú)特殊配置將不再贅述。
1.Kconfig配置
下面參考bsp/stm32/stm32f407-atk-explorer/board/Kconfig文件配置串口驅(qū)動(dòng)的相關(guān)選項(xiàng),如下所示:
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
代碼段中相關(guān)宏的說(shuō)明如下所示。
BSP_USING_UART:串口驅(qū)動(dòng)代碼對(duì)應(yīng)的宏定義,這個(gè)宏控制串口驅(qū)動(dòng)相關(guān)代碼是否會(huì)添加到工程中。
RT_USING_SERIAL:串口驅(qū)動(dòng)框架代碼對(duì)應(yīng)的宏定義,這個(gè)宏控制串口驅(qū)動(dòng)框架的相關(guān)代碼是否會(huì)添加到工程中。
BSP_USING_UART1:串口設(shè)備1對(duì)應(yīng)的宏定義,這個(gè)宏控制串口設(shè)備1是否會(huì)注冊(cè)到系統(tǒng)中。
BSP_UART1_RX_USING_DMA:串口設(shè)備1使用DMA接收數(shù)據(jù)。
2.SConscript配置
在HAL_Drivers/SConscript文件中為串口驅(qū)動(dòng)添加判斷選項(xiàng),代碼如下所示。這是一段Python代碼,表示如果定義了宏BSP_USING_UART,則drv_uart.c會(huì)被添加到工程的源文件中。
if GetDepend(['RT_USING_SERIAL']):
src += ['drv_usart.c']
注冊(cè)設(shè)備之后,UART設(shè)備將以字符設(shè)備的形式在I/O設(shè)備管理器中存在。系統(tǒng)啟動(dòng)并開(kāi)始運(yùn)行后,可以在終端使用list_device命令看到注冊(cè)的設(shè)備包含了UART設(shè)備,之后則可以使用UART設(shè)備驅(qū)動(dòng)框架提供的統(tǒng)一API對(duì)UART設(shè)備進(jìn)行操作。
小結(jié)
在RT-Thread中,將UART外設(shè)抽象為UART設(shè)備,并結(jié)合UART設(shè)備的通用操作方法與驅(qū)動(dòng)框架思想設(shè)計(jì)出UART設(shè)備驅(qū)動(dòng)框架,這為開(kāi)發(fā)者提供了更便利的設(shè)備控制方式。同時(shí),這使基于UART設(shè)備編寫(xiě)出來(lái)的應(yīng)用代碼更具兼容性與通用性。開(kāi)發(fā)者還需要注意以下兩點(diǎn)。
1)操作方法的名稱(chēng)可以自定義,但不要脫離實(shí)際意義,并且需要遵守代碼規(guī)范。所有的操作方法/函數(shù)都屬于內(nèi)部函數(shù),在函數(shù)實(shí)現(xiàn)時(shí),需要使用static進(jìn)行修飾。本條注意事項(xiàng)對(duì)每種驅(qū)動(dòng)都適用,后面章節(jié)將不再贅述。
2)在進(jìn)入與退出中斷時(shí),需要調(diào)用中斷進(jìn)入和中斷退出函數(shù),如下所示。本條注意事項(xiàng)對(duì)每種驅(qū)動(dòng)都適用,后面章節(jié)將不再贅述。
評(píng)論