07、W601串口驱动

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);		//清除状态标志位
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值