返回列表 发新帖我要提问本帖赏金: 30.00元(功能说明)

[技术手册] 介绍CW32L052优秀的外设功能UART空闲定时器

[复制链接]
 楼主| lulugl 发表于 2023-7-17 13:33 | 显示全部楼层 |阅读模式
<
本帖最后由 lulugl 于 2023-7-18 07:33 编辑

#申请原创# #有奖活动#@21小跑堂
串口作为单片机开发的一个常用的外设,应用范围非常广。大部分时候,串口需要接收处理的数据长度是不定的。常见的有几种实现方式:一种是通过固定的帧头+帧尾来来判断;
二是通过串口空闲中断来判断,但是串口空闲中断往往在实际的项目应用中,对不同的波特率会出现不稳定的现象;
三是通过串口中断+定时器来实现,这种方式可以跟据具体的应用需求来定义空闲检测的时间,稳定可靠,但是这需要占用MCU的一个定时器。
这里我要隆重的介绍一下武汉芯源的CW32L052这款芯片,CW32L052在UART外设中增加了一个定时器,初始化其定时器简单,对于实现不定长接收非常之方便。在我了解的UART外设中,是最好用的一个功能!
在其的用户手册中描述了其功能如下:
串口空闲.png

下面介绍一下如何使用这个功能:
1. 串口初始化,我们按照常规的使能UART1的时钟,以及使用PF04、PF05的GPIO时钟,获取系统钟实,以及配置波特率为115200;同时使能串口空闲计时器,配置串口接收中断以及空闲计时器的中断(缺一不可),同时我们装载ARR值(这个ARR值,最起先与CW沟通,技术解读为UCLK,我经过设置后感要比计算中的慢,待与官方沟通后,技术及时进行了确认,延时时间为ARR+1/波特率,单位时间为秒,这里为CW的技术支持点个赞)。 确认延时图片.png
初始化代码如下:
  1. void uart1_init(void)
  2. {
  3.         uint32_t PCLK_Freq;
  4.         GPIO_InitTypeDef GPIO_InitStructure = {0};
  5.         UART_InitTypeDef UART_InitStructure = {0};
  6.         /* 开始 PF端口时钟 */
  7.         __RCC_GPIOF_CLK_ENABLE();
  8.         
  9.         /* 使用UART1 时钟 */
  10.         __RCC_UART1_CLK_ENABLE();
  11.         
  12.         /* 复用GPIOF04-05 */
  13.         PF04_AFx_UART1TXD();
  14.         PF05_AFx_UART1RXD();
  15.         
  16.         /*  初始化TX IO */
  17.         GPIO_InitStructure.Pins = GPIO_PIN_4;
  18.         GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
  19.         GPIO_Init(CW_GPIOF, &GPIO_InitStructure);
  20.         
  21.         /*  初始化RX IO */
  22.         GPIO_InitStructure.Pins = GPIO_PIN_5;
  23.         GPIO_InitStructure.Mode = GPIO_MODE_INPUT_PULLUP;
  24.         GPIO_Init(CW_GPIOF, &GPIO_InitStructure);
  25.         /* 计算串口输入时钟频率 */
  26.         PCLK_Freq = SystemCoreClock >> pow2_table[CW_SYSCTRL->CR0_f.HCLKPRS];
  27.         PCLK_Freq >>= pow2_table[CW_SYSCTRL->CR0_f.PCLKPRS];        
  28.         
  29.         /* 配置串口信息 */
  30.         UART_InitStructure.UART_BaudRate = 115200;
  31.         UART_InitStructure.UART_Over = UART_Over_16;
  32.         UART_InitStructure.UART_Source = UART_Source_PCLK;
  33.         UART_InitStructure.UART_UclkFreq = PCLK_Freq;
  34.         UART_InitStructure.UART_StartBit = UART_StartBit_FE;
  35.         UART_InitStructure.UART_StopBits = UART_StopBits_1;
  36.         UART_InitStructure.UART_Parity = UART_Parity_No;
  37.         UART_InitStructure.UART_HardwareFlowControl = UART_HardwareFlowControl_None;
  38.         UART_InitStructure.UART_Mode = UART_Mode_Rx | UART_Mode_Tx ;
  39.         
  40.         UART_Init(CW_UART1, &UART_InitStructure);
  41.         
  42.         
  43.         CW_UART1->CR2_f.TIMCR = 0x03; //接收空闲检测模式
  44.          /* 重装ARR */
  45.         CW_UART1->TIMARR_f.TIMARR = 8000;  /* 重载值为 PCLK_Freq 8M 即8000 000/8000 = 1khz 等待时间为1ms */
  46.         /* 使能 UART中断 */
  47.         NVIC_SetPriority(UART1_IRQn, 0);
  48.         NVIC_EnableIRQ(UART1_IRQn);
  49.         

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

2. 中断接收函数,在中断接收中,如果我们检测为接收中断,则把接收的字符放到接收数组中,如果检测到的为定时器超时,则更新接收空闲标志。并且重新使能串口超时接收位。(在这里要特别的注明一下,空闲检测位使能,与串口中断使用位不同,我试验后需要每一次都要启用)。
  1. /**
  2. * [url=home.php?mod=space&uid=247401]@brief[/url] This funcation handles UART1
  3. */

  4. uint8_t uart1_rx_buff[UART1_RX_MAXLEN] = {0};
  5. uint8_t uart1_rx_cnt = 0;
  6. uint8_t uart1_rx_state = 0;

  7. void UART1_UART4_IRQHandler(void)
  8. {
  9.     /* USER CODE BEGIN */
  10.     if(UART_GetITStatus(CW_UART1, UART_IT_RC) != RESET)
  11.     {
  12.                                 if(uart1_rx_cnt<UART1_RX_MAXLEN)
  13.                                 {
  14.                                         uart1_rx_buff[uart1_rx_cnt] = UART_ReceiveData_8bit(CW_UART1);
  15.                                         uart1_rx_cnt++;
  16.                                 }else {
  17.                                         uart1_rx_cnt = 0;
  18.                                         uart1_rx_buff[uart1_rx_cnt] = UART_ReceiveData_8bit(CW_UART1);
  19.                                         uart1_rx_cnt++;
  20.                                 }
  21.                                 
  22.         UART_ClearITPendingBit(CW_UART1, UART_IT_RC);
  23.     }
  24.                 if(CW_UART1->ISR_f.TIMOV == 1)
  25.                 {
  26.                         uart1_rx_state = 1;
  27.                         CW_UART1->ICR_f.TIMOV = 0;    //清除定时器中断
  28.                         CW_UART1->CR2_f.TIMCR = 0x03; //使能接收空闲检测模式
  29.                 }
  30.     /* USER CODE END */
  31. }

3. 在主程序中我们编写测试函数如下,检测空闲接收位,如果检测到了则打印出接收的数据。
  1. int32_t main(void)
  2. {
  3.                 uart1_init();

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

  5.     while(1)
  6.     {
  7.                         if(uart1_rx_state == 1)
  8.                         {
  9.                                 uart1_rx_state = 0;
  10.                                 uart1_rx_cnt = 0;
  11.                                 UART_SendString(CW_UART1,uart1_rx_buff);
  12.                                 UART_SendString(CW_UART1, "\r\n");
  13.                                 memset(uart1_rx_buff, 0, UART1_RX_MAXLEN);
  14.                                 
  15.                         }
  16.     }

  17. }

【实验效果】可以接收不定长的数据,我们打开终串终端,如期实现如下效果:
不定长数据收接收.png
【讨论】
串口空闲检测定时器配置简单方便,可以应用到多个超时检测的场景,比如AT指令中需要长时间等待串口响应的,这样我们就可以直接装载空闲ARR的数时来实现延时,而不需要使用阻塞式的延时函数。
【建议】
1. CW32l052_uart库函数中没有更新定时器超时的中断标志,需要用寄器来操作。
2. 在手户手册中还没有详细的对串口定时器的中断如何设置的流程,用户理解起来不是很好。
3. 串口空闲计时器的位在每次进入中断后需要重新写入,感觉不是很方便,能不能象串口接收中断RC位一样,写入一次就可以了,需要停止时清除该位即可。
3. 建议在下一版的用户手册中,详细的介绍如何使用这个优秀的UART空闲检测定时器,使得用户及时掌握使用方法。




串口空闲.png

打赏榜单

21小跑堂 打赏了 30.00 元 2023-07-18
理由:恭喜通过原创审核!期待您更多的原创作品~

评论

优秀的UART空闲定时器,解决不定长数据的接受问题,使不定长数据的接受变得更为简单高效  发表于 2023-7-18 16:38
tpgf 发表于 2023-8-12 16:09 | 显示全部楼层
空闲定时器是一个专用的定时器吗
 楼主| lulugl 发表于 2023-8-12 18:04 | 显示全部楼层
tpgf 发表于 2023-8-12 16:09
空闲定时器是一个专用的定时器吗

他这个是串口模块时面的一个定时器。
晓伍 发表于 2023-8-13 09:22 | 显示全部楼层
很多模块都有空闲定时器吗?还是说只有串口有空闲定时器啊
八层楼 发表于 2023-8-13 09:49 | 显示全部楼层
空闲定时器的定时时长是根据什么来的呢

评论

设置控制寄存器UARTx_CR2的TIMCR位域为0x3,定时器工作于接收空闲检测模式,检测空闲时间为(TIMARR+1)/fBaudRate。  发表于 2023-8-14 10:59
观海 发表于 2023-8-13 10:17 | 显示全部楼层
空闲定时器是不是可以起到一个缓冲的作用呢
木木guainv 发表于 2023-8-13 10:44 | 显示全部楼层
可以非常灵活的设置等待时间了
godlovedeath 发表于 2023-8-13 12:02 | 显示全部楼层
L083好像沒有這個功能?
guanjiaer 发表于 2023-8-13 17:00 | 显示全部楼层
空闲定时器是最近才有的功能定时器吧
 楼主| lulugl 发表于 2023-8-13 21:59 | 显示全部楼层
pengf 发表于 2023-8-13 18:58
很多模块都有空闲定时器吗?

好象定时器,目前我只看到CW32L052有,其他的都是产生空闲中断。
chenqianqian 发表于 2023-8-14 07:56 来自手机 | 显示全部楼层
空闲中断本身也是计时,只是时间固定的而已。
godlovedeath 发表于 2023-8-14 09:22 | 显示全部楼层
看着L083应该是比较厉害的片子呀,主频,内存的,咋功能还少一些呢
 楼主| lulugl 发表于 2023-8-14 12:02 | 显示全部楼层
godlovedeath 发表于 2023-8-14 09:22
看着L083应该是比较厉害的片子呀,主频,内存的,咋功能还少一些呢

L052是后面出的,L083可以用定时器做一个。
小夏天的大西瓜 发表于 2023-8-26 22:18 | 显示全部楼层
串口收发基本就用板子自带的串口
星辰大海不退缩 发表于 2023-8-27 19:42 | 显示全部楼层
直接按照标准库函数进行调用应该没问题吧
AdaMaYun 发表于 2023-8-27 21:51 | 显示全部楼层
tpgf 发表于 2023-8-12 16:09
空闲定时器是一个专用的定时器吗

应该是一个专用的定时器
OKAKAKO 发表于 2023-8-27 22:07 | 显示全部楼层
使用过固定的帧头+帧尾来来判断有时会存在误判的情况
 楼主| lulugl 发表于 2023-8-28 07:27 | 显示全部楼层
OKAKAKO 发表于 2023-8-27 22:07
使用过固定的帧头+帧尾来来判断有时会存在误判的情况

对的,但是在固定长度的场景还是可以的,比如485modebus就可以。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

180

主题

830

帖子

12

粉丝
快速回复 在线客服 返回列表 返回顶部

180

主题

830

帖子

12

粉丝
快速回复 在线客服 返回列表 返回顶部