07、W601串口驱动
其实串口驱动应该在开发最初就应该写好,不然那些log日志也不会被输出,之所以放到现在是考虑到驱动的难易程度,在了解了IO口的输入输出功能和配置之后就可以更容易配置其他的设备驱动了!
一、什么是UART
UART 是一种通用串行数据总线,用于异步通信。该总线支持双向通信,可以实现全双工传输和接收。
二、W601的UART
W601 共 2 组普通 UART 口(uart0 和 uart1),通过精细的时钟分频组合可以实现各种波特率的设置,最大可支持 2Mbps 的通信速率。W601 UART 能和硬件 DMA 配合使用,实现数据的高效异步传输。
- 符合 APB 总线接口协议,全双工异步通信方式
- 支持中断或轮询工作方式
- 支持 DMA Byte 传输模式,发送接收各 32-byte FIFO
- 波特率可编程,最大支持 2Mbps
- 5-8bit 数据长度,以及 parity 极性可配置
- 1 或 2 个 stop 位可配置
- 支持 RTS/CTS 流控
- 支持 Break 帧发送与接收
- 支持 Overrun,parity error,frame error,rx break frame 中断指示
三、W601串口的波特率计算
W601可 以 通 过 波 特 率 设 置 寄 存 器 BAUD_RATE_CTRL 寄 存 器 来 实 现 精 细 的 波 特 率 控 制。
BAUD_RATE_CTRL 是一个32位的寄存器[31:20]保留,低16位[15:0]命名为ubdiv,高16位[19:16]命名为ubdiv_frac
需要设置的波特率 baudrate,计算公式如下:
ubdiv = apbclk / (16 * baudrate) – 1 //取整数
ubdiv_frac = (apbclk % (baudrate * 16)) / baudrate //取整数
以 APB 时钟 40MHz,波特率 19200bps 为例:
ubdiv = 40000000 / (16 * 19200) – 1 = 129
ubdiv_frac = (40000000 % (19200* 16)) / 19200 = 3
根据以上公式计算 APB 时钟 40MHz,波特率 19200bps 的情况下,波特率寄存器应该设置为:
BAUD_RATE _CTRL = (3<<16) | 129 = 0x0003_0081。
有关串口的协议内容会放在协议专题中讲解,这里只是为了驱动w601的串口
W601串口的驱动步骤:
-
配置IO口,tx和rx
-
配置波特率
-
配置串口的参数
- 8位数据位
- 1位停止位
- 无奇偶校验
- 发送使能
- 接受使能
-
关闭硬件控制流
-
不使能DMA
-
FIFO触发设置
-
开启RX中断,配置接受中断
四、代码实现
/**
* @brief 初始化串口0函数
*
* @param bound 串口波特率
*
* @return void
*/
void uart_init(u32 bound)
{
u32 bd;
u32 apbclk;
tls_sys_clk sysclk;
/* 1.配置IO */
wm_uart0_tx_config(WM_IO_PA_04);
wm_uart0_rx_config(WM_IO_PA_05);
/* 2.波特率设置:
ubdiv = (apbclk / (16 * bound) - 1)
ubdiv_frac = ((apbclk % (bound * 16)) / (bound)) */
tls_sys_clk_get(&sysclk);
apbclk = sysclk.apbclk * 1000000;
bd = (apbclk / (16 * bound) - 1) | (((apbclk % (bound * 16)) / (bound)) << 16);
tls_reg_write32(HR_UART0_BAUD_RATE_CTRL, bd);
/* 2.串口参数设置:8位数据位/1位停止位/无奇偶校验位/发送使能/接收使能 */
tls_reg_write32(HR_UART0_LINE_CTRL, ULCON_WL8 | ULCON_TX_EN | ULCON_RX_EN);
/* 3.硬件流控关闭 */
tls_reg_write32(HR_UART0_FLOW_CTRL, 0);
/* 3.不使能DMA */
tls_reg_write32(HR_UART0_DMA_CTRL, 0);
/* 4.FIFO触发设置:1个字节 */
tls_reg_write32(HR_UART0_FIFO_CTRL, 0);
/* 5.开启RX中断:接收触发中断和接收超时中断*/
tls_reg_write32(HR_UART0_INT_MASK, 0xFF & (~(UIS_RX_FIFO | UIS_RX_FIFO_TIMEOUT)));
/* 6.串口接收中断配置 */
NVIC_ClearPendingIRQ(UART0_IRQn);
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = UART0_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 7;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
}
/**
* @brief 串口0中断服务程序
*
* @param void
*
* @return void
*/
void UART0_IRQHandler(void)
{
u8 res;
if(tls_reg_read32(HR_UART0_INT_SRC) & UIS_RX_FIFO) //接收到数据
{
res = (u8)tls_reg_read32(HR_UART0_RX_WIN);
if((USART_RX_STA & 0x8000) == 0) //接收未完成
{
if(USART_RX_STA & 0x4000) //接收到了0x0d
{
if(res != 0x0a)USART_RX_STA = 0; //接收错误,重新开始
else USART_RX_STA |= 0x8000; //接收完成了
}
else //还没收到0X0D
{
if(res == 0x0d)USART_RX_STA |= 0x4000;
else
{
USART_RX_BUF[USART_RX_STA & 0X3FFF] = res;
USART_RX_STA++;
if(USART_RX_STA > (USART_REC_LEN - 1))USART_RX_STA = 0; //接收数据错误,重新开始接收
}
}
}
tls_reg_write32(HR_UART0_INT_SRC, UIS_RX_FIFO); //清除状态标志位
}
if(tls_reg_read32(HR_UART0_INT_SRC) & UIS_RX_FIFO_TIMEOUT) //接收到数据
{
tls_reg_write32(HR_UART0_INT_SRC, UIS_RX_FIFO_TIMEOUT); //清除状态标志位
}
}