21 通用异步收发传输器(UART)
21.1 功能概述
UART全称Universal Asynchronous Receiver/Transmitter,即通用异步收发传输器件。 UART对接收的数据执行串并转换,对发送的数据执行并串转换。
特性:
- 支持硬件流控
- 可编程波特率发生器,最高波特率可达7000000bps
- 独立的发送和接收 FIFO
- 单个组合中断,包括接收(包括超时)、传输、调制解调器状态和错误状态中断,每个中断可屏蔽
- 支持 DMA 方式
21.2 使用说明
21.2.1 设置波特率
使用 apUART_BaudRateSet
设置对应UART设备的波特率。
void apUART_BaudRateSet(
* pBase, //UART参数结构体&设备地址
UART_TypeDefuint32_t ClockFrequency, //时钟信号的频率
uint32_t BaudRate //波特率
);
21.2.2 获取波特率
使用 apUART_BaudRateGet
获取对应UART设备的波特率。
uint32_t apUART_BaudRateGet (
* pBase,
UART_TypeDefuint32_t ClockFrequency
);
21.2.3 UART初始化
在使用UART之前,需要先通过 apUART_Initialize
对UART进行初始化。
void apUART_Initialize(
* pBase,
UART_TypeDef* UARTx, //uart状态结构体
UART_sStateStructuint32_t IntMask //中断掩码
);
初始化 UART 之前需要初始化如下所示的 UART 状态结构体:
typedef struct UART_xStateStruct
{
// Line Control Register, UARTLCR_H
; // WLEN
UART_eWLEN word_length; // PEN, EPS, SPS
UART_ePARITY parityuint8_t fifo_enable; // FEN
uint8_t two_stop_bits; // STP2
// Control Register, UARTCR
uint8_t receive_en; // RXE
uint8_t transmit_en; // TXE
uint8_t UART_en; // UARTEN
uint8_t cts_en; //CTSEN
uint8_t rts_en; //RTSEN
// Interrupt FIFO Level Select Register, UARTIFLS
uint8_t rxfifo_waterlevel; // RXIFLSEL
uint8_t txfifo_waterlevel; // TXIFLSEL
//UART_eFIFO_WATERLEVEL rxfifo_waterlevel; // RXIFLSEL
//UART_eFIFO_WATERLEVEL txfifo_watchlevel; // TXIFLSEL
// UART Clock Frequency
uint32_t ClockFrequency;
uint32_t BaudRate;
} UART_sStateStruct;
常用的中断掩码如下所示, IntMask
是它们的组合。
#define UART_INTBIT_RECEIVE 0x10 //receive interrupt
#define UART_INTBIT_TRANSMIT 0x20 //transmit interrupt
例如,配置并初始化串口,开启接收中断和发送中断,设置串口波特率为115200:
首先,创建UART配置函数
config_uart
,在函数内初始化UART状态结构体,并配置必要的状态参数, 然后调用apUART_Initialize
初始化串口。void config_uart(uint32_t freq, uint32_t baud) { ; UART_sStateStruct config .word_length = UART_WLEN_8_BITS; config.parity = UART_PARITY_NOT_CHECK; config.fifo_enable = 1; config.two_stop_bits = 0; config.receive_en = 1; config.transmit_en = 1; config.UART_en = 1; config.cts_en = 0; config.rts_en = 0; config.rxfifo_waterlevel = 1; config.txfifo_waterlevel = 1; config.ClockFrequency = freq; config.BaudRate = baud; config (PRINT_PORT, &config, UART_INTBIT_RECEIVE | UART_INTBIT_TRANSMIT); apUART_Initialize}
使用时只需要如下所示调用
config_uart
函数即可。(OSC_CLK_FREQ, 115200); config_uart
21.2.4 UART轮询模式
在轮询模式下,CPU通过检查线路状态寄存器中的位来检测事件:
使用
apUART_Check_Rece_ERROR
查询接收产生的错误字。uint8_t apUART_Check_Rece_ERROR( * pBase UART_TypeDef);
用
apUART_Check_RXFIFO_EMPTY
查询Rx FIFO是否为空。uint8_t apUART_Check_RXFIFO_EMPTY( * pBase UART_TypeDef);
使用
apUART_Check_RXFIFO_FULL
查询Rx FIFO是否已满。uint8_t apUART_Check_RXFIFO_FULL( * pBase UART_TypeDef);
使用
apUART_Check_TXFIFO_EMPTY
查询Tx FIFO是否为空。uint8_t apUART_Check_TXFIFO_EMPTY( * pBase UART_TypeDef);
使用
apUART_Check_TXFIFO_FULL
查询Tx FIFO是否已满。uint8_t apUART_Check_TXFIFO_FULL( * pBase UART_TypeDef);
21.2.5 UART中断使能/禁用
用 apUART_Enable_TRANSMIT_INT
使能发送中断,用 apUART_Disable_TRANSMIT_INT
禁用发送中断;
用 apUART_Enable_RECEIVE_INT
使能接收中断,用 apUART_Disable_RECEIVE_INT
禁用接收中断。
中断默认是禁用的,使能中断既能用上述 apUART_Enable_TRANSMIT_INT
和 apUART_Enable_RECEIVE_INT
的方式,也可以通过 apUART_Initialize
初始化串口是设置参数 IntMask
的值使能相应的中断,详情请参考
UART初始化
21.2.6 处理中断状态
用 apUART_Get_ITStatus
获取某个UART上的中断触发状态,返回非 0 值表示该 UART
上产生了中断请求;用 apUART_Get_all_raw_int_stat
一次性获取所有 UART 的中断触发状态,
第 \(n\) 比特(第 0 比特为最低比特)对应 UART \(n\) 上的中断触发状态。
UART产生中断后,需要消除中断状态方可再次触发。用 apUART_Clr_RECEIVE_INT
消除某个 UART上接收中断的状态,
用 apUART_Clr_TX_INT
消除某个 UART上发送中断的状态。用 apUART_Clr_NonRx_INT
消除某个 UART上除接收以外的中断状态。
21.3 示例代码
21.3.1 UART接收变长字节数据
- UART+FIFO方式
#define RX_FIFO_WATER_LEVEL 0x10
char dst[256];
void config_uart(uint32_t freq, uint32_t BaudRate)
{
//config uarts parameter
;
UART_sStateStruct config
.word_length = UART_WLEN_8_BITS;
config.parity = UART_PARITY_NOT_CHECK;
config.fifo_enable = 1;
config.two_stop_bits = 0;
config.receive_en = 1;
config.transmit_en = 1;
config.UART_en = 1;
config.cts_en = 0;
config.rts_en = 0;
config.rxfifo_waterlevel = RX_FIFO_WATER_LEVEL;
config.txfifo_waterlevel = 1;
config.ClockFrequency = freq;
config.BaudRate = BaudRate;
config
(APB_UART0, &config, UART_INTBIT_TIMEOUT | UART_INTBIT_RECEIVE);
apUART_Initialize}
uint32_t uart_isr(void *user_data)
{
uint32_t status;
static int index = 0;
uint8_t cnt = 0;
while(1)
{
= apUART_Get_all_raw_int_stat(APB_UART0);
status
if (status == 0)
break;
->IntClear = status;
APB_UART0
// rx int
if (status & (1 << bsUART_RECEIVE_INTENAB))
{
while (apUART_Check_RXFIFO_EMPTY(APB_UART0) != 1)
{
char c = APB_UART0->DataRead;
[index++] = c;
dst++;
cnt/* To avoid the situation where the RX FIFO data length is the same as that
of the RX_FIFO_WATER_LEVEL, the rx timeout interrupt cannot be generated.*/
if(cnt >= RX_FIFO_WATER_LEVEL - 1)
{
break;
}
}
}
// rx timeout_int
if (status & (1 << bsUART_TIMEOUT_INTENAB))
{
while (apUART_Check_RXFIFO_EMPTY(APB_UART0) != 1)
{
char c = APB_UART0->DataRead;
[index++] = c;
dst}
[index] = 0;
dst// 一包数据接收完毕,可在此进行数据处理
= 0;
index }
}
return 0;
}
void uart_peripherals_read_data()
{
//注册uart0中断
(PLATFORM_CB_IRQ_UART0, uart_isr, NULL);
platform_set_irq_callback(OSC_CLK_FREQ, 115200);
config_uart}
- UART+FIFO+DMA方式
char dst[256];
char src[] = "Finished to receive a frame!\n";
#define DMA_RX_CHANNEL_ID 1
#define DMA_TX_CHANNEL_ID 0
void config_uart(uint32_t freq, uint32_t BaudRate)
{
//config uarts parameter
;
UART_sStateStruct config
.word_length = UART_WLEN_8_BITS;
config.parity = UART_PARITY_NOT_CHECK;
config.fifo_enable = 1;
config.two_stop_bits = 0;
config.receive_en = 1;
config.transmit_en = 1;
config.UART_en = 1;
config.cts_en = 0;
config.rts_en = 0;
config.rxfifo_waterlevel = 7;
config.txfifo_waterlevel = 1;
config.ClockFrequency = freq;
config.BaudRate = BaudRate;
config
(APB_UART0, &config, UART_INTBIT_TIMEOUT);
apUART_Initialize(APB_UART0, 1, 1, 0);
UART_DmaEnable}
static void setup_peripheral_dma(void)
{
((aligned (8))) = {0};
DMA_Descriptor descriptor __attribute__( &descriptor,
DMA_PreparePeripheral2Mem, SYSCTRL_DMA_UART0_RX,
dstsizeof(dst), DMA_ADDRESS_INC, 0);
(DMA_RX_CHANNEL_ID, &descriptor);
DMA_EnableChannel}
//添加UART通过DMA发送的配置
void UART_trigger_DmaSend(void)
{
((aligned (8))) = {0};
DMA_Descriptor descriptor __attribute__( &descriptor,
DMA_PrepareMem2Peripheral,
SYSCTRL_DMA_UART0_TX, strlen(src),
src, 0);
DMA_ADDRESS_INC(DMA_TX_CHANNEL_ID, &descriptor);
DMA_EnableChannel}
uint32_t uart_isr(void *user_data)
{
uint32_t status;
int index = 0;
while(1)
{
= apUART_Get_all_raw_int_stat(APB_UART0);
status
if (status == 0)
break;
->IntClear = status;
APB_UART0
// rx timeout_int
if (status & (1 << bsUART_TIMEOUT_INTENAB))
{
= APB_DMA->Channels[DMA_RX_CHANNEL_ID].Descriptor.DstAddr - (uint32_t)rx_buffer;
index while (apUART_Check_RXFIFO_EMPTY(APB_UART0) != 1)
{
char c = APB_UART0->DataRead;
[index++] = c;
dst}
[index] = 0;
dst// 一包数据接收完毕,可在此进行数据处理
();
UART_trigger_DmaSend();
setup_peripheral_dma}
}
return 0;
}
void uart_peripherals_read_data()
{
//注册uart0中断
(PLATFORM_CB_IRQ_UART0, uart_isr, NULL);
platform_set_irq_callback(OSC_CLK_FREQ, 115200);
config_uart();
setup_peripheral_dma}