[MCU] 【CW32L052评测】优秀的UART空闲定时器

lugl4313820   2023-7-17 13:29 楼主

 

MCU的串口要实现不定长接收数据,有几种实现方式,一种是通过固定的帧头+帧尾来来判断,二是通过串口空闲中断来判断,但是串口空闲中断往往在实际的项目应用中,对不同的波特率会出现不稳定的现象。三是通过串口中断+定时器来实现,这种方式可以跟据具体的应用需求来定义空闲检测的时间,稳定可靠,但是这需要占用MCU的一个定时器。而CW32L052在UART外设中增加了一个定时器,初始化其定时器简单,对于实现不定长接收非常之方便。在我了解的UART外设中,是最好用的一个功能!

下面介绍一下如何使用这个功能:

  1. 串口初始化:
    void uart1_init(void)
    {
    uint32_t PCLK_Freq;
    GPIO_InitTypeDef GPIO_InitStructure = {0};
    UART_InitTypeDef UART_InitStructure = {0};
    /* 开始 PF端口时钟 */
    __RCC_GPIOF_CLK_ENABLE();

    /* 使用UART1 时钟 */
    __RCC_UART1_CLK_ENABLE();

    /* 复用GPIOF04-05 */
    PF04_AFx_UART1TXD();
    PF05_AFx_UART1RXD();

    /* 初始化TX IO */
    GPIO_InitStructure.Pins = GPIO_PIN_4;
    GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_Init(CW_GPIOF, &GPIO_InitStructure);

    /* 初始化RX IO */
    GPIO_InitStructure.Pins = GPIO_PIN_5;
    GPIO_InitStructure.Mode = GPIO_MODE_INPUT_PULLUP;
    GPIO_Init(CW_GPIOF, &GPIO_InitStructure);
    /* 计算串口输入时钟频率 */
    PCLK_Freq = SystemCoreClock >> pow2_table[CW_SYSCTRL->CR0_f.HCLKPRS];
    PCLK_Freq >>= pow2_table[CW_SYSCTRL->CR0_f.PCLKPRS];

    /* 配置串口信息 */
    UART_InitStructure.UART_BaudRate = 115200;
    UART_InitStructure.UART_Over = UART_Over_16;
    UART_InitStructure.UART_Source = UART_Source_PCLK;
    UART_InitStructure.UART_UclkFreq = PCLK_Freq;
    UART_InitStructure.UART_StartBit = UART_StartBit_FE;
    UART_InitStructure.UART_StopBits = UART_StopBits_1;
    UART_InitStructure.UART_Parity = UART_Parity_No;
    UART_InitStructure.UART_HardwareFlowControl = UART_HardwareFlowControl_None;
    UART_InitStructure.UART_Mode = UART_Mode_Rx | UART_Mode_Tx ;

    UART_Init(CW_UART1, &UART_InitStructure);


    CW_UART1->CR2_f.TIMCR = 0x03; //接收空闲检测模式
    /* 重装ARR */
    CW_UART1->TIMARR_f.TIMARR = 8000; /* 重载值为 PCLK_Freq 8M 即8000 000/8000 = 1khz 等待时间为1ms */
    /* 使能 UART中断 */
    NVIC_SetPriority(UART1_IRQn, 0);
    NVIC_EnableIRQ(UART1_IRQn);

    CW_UART1->IER_f.RC = 1; //使能接收中断
    CW_UART1->IER_f.TIMOV = 1; //打开定时器中断

    }
  2. 中断接收函数,在中断接收中,如果我们检测为接收中断,则把接收的字符放到接收数组中,如果检测到的为定时器超时,则更新接收空闲标志。并且重新使能串口超时接收位。
    void UART1_UART4_IRQHandler(void)
    {
    /* USER CODE BEGIN */
    if(UART_GetITStatus(CW_UART1, UART_IT_RC) != RESET)
    {
    if(uart1_rx_cnt<UART1_RX_MAXLEN)
    {
    uart1_rx_buff[uart1_rx_cnt] = UART_ReceiveData_8bit(CW_UART1);
    uart1_rx_cnt++;
    }else {
    uart1_rx_cnt = 0;
    uart1_rx_buff[uart1_rx_cnt] = UART_ReceiveData_8bit(CW_UART1);
    uart1_rx_cnt++;
    }

    UART_ClearITPendingBit(CW_UART1, UART_IT_RC);
    }
    if(CW_UART1->ISR_f.TIMOV == 1)
    {
    uart1_rx_state = 1;
    CW_UART1->ICR_f.TIMOV = 0; //清除定时器中断
    CW_UART1->CR2_f.TIMCR = 0x03; //使能接收空闲检测模式
    }
    /* USER CODE END */
    }
  3. 在主程序中我们编写测试函数如下,检测空闲接收位,如果检测到了则打印出接收的数据。

int32_t main(void)

{

uart1_init();

UART_SendString(CW_UART1, "start\r\n");

while(1)

{

if(uart1_rx_state == 1)

{

uart1_rx_state = 0;

uart1_rx_cnt = 0;

UART_SendString(CW_UART1,uart1_rx_buff);

UART_SendString(CW_UART1, "\r\n");

memset(uart1_rx_buff, 0, UART1_RX_MAXLEN);

 

}

}

}

 

【实验效果】

image.png  

【讨论】

串口空闲检测定时器配置简单方便,可以应用到多个超时检测的场景,比如AT指令中需要长时间等待串口响应的,这样我们就可以直接装载空闲ARR的数时来实现延时,而不需要使用阻塞式的延时函数。

【建议】

  1. CW32l052_uart库函数中没有更新定时器超时的中断标志,需要用寄器来操作。
  2. 在手户手册中还没有详细的对串口定时器的中断如何设置的流程,用户理解起来不是很好。
  3. 建议在下一版的用户手册中,详细的介绍如何使用这个优秀的UART空闲检测定时器,使得用户及时掌握使用方法。

回复评论 (6)

CW32l052这个想法还是很不错的!

点赞  2023-7-17 13:59

这个自行设计的功能还是非常不错的,尤其在部署了Modbus-RTU协议下的RS485通讯网络。

谢谢楼主的分享了

点赞  2023-7-17 15:07
引用: jobszheng5 发表于 2023-7-17 15:07 这个自行设计的功能还是非常不错的,尤其在部署了Modbus-RTU协议下的RS485通讯网络。 谢谢楼主的分享了 ...

感谢大佬的关注与肯定,CW32L052是新出的一支独秀,功耗控制非常不错。

点赞  2023-7-17 15:21
引用: lugl4313820 发表于 2023-7-17 15:21 感谢大佬的关注与肯定,CW32L052是新出的一支独秀,功耗控制非常不错。

版主,您这就谦虚了

我只是普通的一名小小的工程师

点赞  2023-7-17 15:25

GTIM定时器 里面就设计了UART1,2,3的RTX信号触发定时器功能  这样接收时间就好确定了。

 

1123.png

点赞  2023-7-17 16:00
引用: damiaa 发表于 2023-7-17 16:00 GTIM定时器 里面就设计了UART1,2,3的RTX信号触发定时器功能  这样接收时间就好确定了。 &nb ...

对的,但是会占用GTIM,而且他这个初始化、中断都在UART里面,操作方便一些。

点赞  2023-7-17 16:44
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复