[开发板] 【CW32L031CxTx StartKit评估板测评】5.串口的使用

[复制链接]
 楼主| yuyy1989 发表于 2023-9-26 17:22 | 显示全部楼层 |阅读模式
#申请原创# @21小跑堂  
CW32L031 内部集成 3 个通用异步收发器 (UART),评估板上集成了一个USB转串口,不过用的是比较古老的MINIUSB,以前的MP3和MP4上经常会用到这个接口,现在几乎绝迹了,连接的是PA08 和PA09,对应UART1
QQ截图20230925161007.png QQ截图20230925161130.png
简单做个收发测试,使用轮询方式接收串口数据以\n作为结束符,然后将接收到的数据再通过串口发送回去
  1. void uart1_init()
  2. {
  3.     GPIO_InitTypeDef GPIO_InitStructure = {0};
  4.     USART_InitTypeDef USART_InitStructure = {0};
  5.     __RCC_GPIOA_CLK_ENABLE();
  6.     __RCC_UART1_CLK_ENABLE();
  7.     PA08_AFx_UART1TXD();
  8.     PA09_AFx_UART1RXD();
  9.     GPIO_InitStructure.Pins = GPIO_PIN_8;
  10.     GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
  11.     GPIO_Init(CW_GPIOA, &GPIO_InitStructure);
  12.     GPIO_InitStructure.Pins = GPIO_PIN_9;
  13.     GPIO_InitStructure.Mode = GPIO_MODE_INPUT_PULLUP;
  14.     GPIO_Init(CW_GPIOA, &GPIO_InitStructure);
  15.    
  16.     USART_InitStructure.USART_BaudRate = 115200;
  17.     USART_InitStructure.USART_Over = USART_Over_16;
  18.     USART_InitStructure.USART_Source = USART_Source_PCLK;
  19.     USART_InitStructure.USART_UclkFreq = SystemCoreClock;
  20.     USART_InitStructure.USART_StartBit = USART_StartBit_FE;
  21.     USART_InitStructure.USART_StopBits = USART_StopBits_1;
  22.     USART_InitStructure.USART_Parity = USART_Parity_No ;
  23.     USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  24.     USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
  25.     USART_Init(CW_UART1, &USART_InitStructure);
  26. }

  27. int32_t main(void)
  28. {
  29.     uint8_t uart_msg[10];
  30.     uint8_t uart_msg_len = 0,uart_msg_index = 0;
  31.     RCC_HSI_48M_init();
  32.     RCC_HCLKPRS_Config(RCC_HCLK_DIV1);
  33.     RCC_PCLKPRS_Config(RCC_PCLK_DIV1);
  34.     uart1_init();
  35.     while (1)
  36.     {
  37.         do
  38.         {
  39.             while(USART_GetFlagStatus(CW_UART1, USART_FLAG_RC) == RESET);
  40.             USART_ClearFlag(CW_UART1, USART_FLAG_RC);
  41.             if(USART_GetFlagStatus(CW_UART1, USART_FLAG_PE|USART_FLAG_FE))
  42.             {
  43.                 USART_ClearFlag(CW_UART1, USART_FLAG_PE|USART_FLAG_FE);
  44.                 uart_msg_len = 0x00;
  45.             }
  46.             else
  47.             {
  48.                 uart_msg[uart_msg_len] = USART_ReceiveData_8bit(CW_UART1);
  49.                 uart_msg_len++;
  50.             }
  51.         }
  52.         while(uart_msg[uart_msg_len-1] != '\n' && uart_msg_len < 10);
  53.         uart_msg_index = 0;
  54.         while(uart_msg_index < uart_msg_len)
  55.         {
  56.             USART_SendData_8bit(CW_UART1, uart_msg[uart_msg_index]);
  57.             uart_msg[uart_msg_index] = 0;
  58.             while(USART_GetFlagStatus(CW_UART1, USART_FLAG_TXE) == RESET);
  59.             uart_msg_index += 1;
  60.         }
  61.         while(USART_GetFlagStatus(CW_UART1, USART_FLAG_TXBUSY) == SET);
  62.         uart_msg_len = 0;
  63.     }
  64. }
运行效果
QQ截图20230926121119.png
接下来实现一下printf重定向到串口,这样以后就方便输出调试信息了,在MDK中实现这个功能最简单的方法就是先在工程设置中勾选Use MacroLib
QQ截图20230926122151.png
包含stdio.h后重写fputc就行了
  1. int fputc(int ch, FILE *f)
  2. {
  3.     while(USART_GetFlagStatus(CW_UART1, USART_FLAG_TXBUSY) == SET);
  4.     USART_SendData_8bit(CW_UART1,ch&0xFF);
  5.     return ch;
  6. }

  7. int32_t main(void)
  8. {
  9.     uint8_t testnum = 0;
  10.     RCC_HSI_48M_init();
  11.     RCC_HCLKPRS_Config(RCC_HCLK_DIV1);
  12.     RCC_PCLKPRS_Config(RCC_PCLK_DIV1);
  13.     uart1_init();
  14.     while (1)
  15.     {
  16.         printf("printf重定向测试 %d",testnum++);
  17.         yuyy_delay_ms(1000);
  18.     }
  19. }
运行效果
QQ截图20230926123658.png
接下来试试中断模式下的数据收发,CW32L031串口支持的中断如下
QQ截图20230926121540.png
CW32L031的串口有个内置的定时器,可以实现接收空闲检测,这个就方便了很多,不用自己再用定时器检测接收超时,可以实现不定长的数据接收
QQ截图20230926124336.png
  1. void uart1_init()
  2. {
  3.     GPIO_InitTypeDef GPIO_InitStructure = {0};
  4.     USART_InitTypeDef USART_InitStructure = {0};
  5.     __RCC_GPIOA_CLK_ENABLE();
  6.     __RCC_UART1_CLK_ENABLE();
  7.     PA08_AFx_UART1TXD();
  8.     PA09_AFx_UART1RXD();
  9.     GPIO_InitStructure.Pins = GPIO_PIN_8;
  10.     GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
  11.     GPIO_Init(CW_GPIOA, &GPIO_InitStructure);
  12.     GPIO_InitStructure.Pins = GPIO_PIN_9;
  13.     GPIO_InitStructure.Mode = GPIO_MODE_INPUT_PULLUP;
  14.     GPIO_Init(CW_GPIOA, &GPIO_InitStructure);
  15.    
  16.     USART_InitStructure.USART_BaudRate = 115200;
  17.     USART_InitStructure.USART_Over = USART_Over_16;
  18.     USART_InitStructure.USART_Source = USART_Source_PCLK;
  19.     USART_InitStructure.USART_UclkFreq = SystemCoreClock;
  20.     USART_InitStructure.USART_StartBit = USART_StartBit_FE;
  21.     USART_InitStructure.USART_StopBits = USART_StopBits_1;
  22.     USART_InitStructure.USART_Parity = USART_Parity_No ;
  23.     USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  24.     USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
  25.     USART_Init(CW_UART1, &USART_InitStructure);
  26.     USART_TimerModeConfig(CW_UART1,USART_TimerMode_Idle);
  27.     USART_SetAutoReload(CW_UART1,480);
  28.     USART_ITConfig(CW_UART1, USART_IT_RC, ENABLE);
  29.     USART_ITConfig(CW_UART1, USART_IT_TIMOV, ENABLE);
  30.     NVIC_SetPriority(UART1_IRQn, 0);
  31.     NVIC_EnableIRQ(UART1_IRQn);
  32. }

  33. #define UART_BUFFER_LEN 20
  34. uint8_t uart_buffer[UART_BUFFER_LEN] = {0};
  35. uint8_t uart_rxindex = 0;
  36. uint8_t uart_rxlen = 0;
  37. uint8_t uart_txindex = 0;
  38. uint8_t uart_txlen = 0;

  39. void readrxtotxbuffer()
  40. {
  41.     uart_txlen += uart_rxlen;
  42.     uart_rxlen = 0;
  43.     USART_ITConfig(CW_UART1, USART_IT_TXE, ENABLE);
  44. }

  45. void UART1_IRQHandler(void)
  46. {
  47.     if(USART_GetITStatus(CW_UART1, USART_IT_RC) != RESET)
  48.     {
  49.         uart_buffer[uart_rxindex++] = USART_ReceiveData_8bit(CW_UART1);
  50.         if(uart_rxindex == UART_BUFFER_LEN)
  51.             uart_rxindex = 0;
  52.         uart_rxlen++;
  53.         USART_ClearITPendingBit(CW_UART1, USART_IT_RC);
  54.     }
  55.     if(USART_GetITStatus(CW_UART1, USART_IT_TIMOV) != RESET || uart_rxlen == 10)
  56.     {
  57.         USART_ClearITPendingBit(CW_UART1, USART_IT_TIMOV);
  58.         USART_TimerModeConfig(CW_UART1,USART_TimerMode_Idle);
  59.         readrxtotxbuffer();
  60.     }
  61.     if(USART_GetITStatus(CW_UART1, USART_IT_TXE) != RESET)
  62.     {
  63.         if(uart_txlen > 0)
  64.         {
  65.             USART_SendData_8bit(CW_UART1, uart_buffer[uart_txindex++]);
  66.             if(uart_txindex == UART_BUFFER_LEN)
  67.                 uart_txindex = 0;
  68.             uart_txlen--;
  69.         }
  70.         else
  71.         {
  72.             USART_ITConfig(CW_UART1, USART_IT_TXE, DISABLE);
  73.         }
  74.         USART_ClearITPendingBit(CW_UART1, USART_IT_TXE);
  75.     }
  76. }
需要注意的是当uart定时器溢出后需要再次配置定时器,不然后面不会再触发空闲中断,另外这个空闲时间的判定似乎有问题,原来配置的是48000,按照手册描述空闲时间=48000/48000000=1ms,但实际时间有500ms左右
运行结果
QQ截图20230926135952.png
利用这个定时器还能实现自动侦测波特率
QQ截图20230926162145.png
来实验一下,直接使用例程里的代码
  1. void uart1_init()
  2. {
  3.     GPIO_InitTypeDef GPIO_InitStructure = {0};
  4.     USART_InitTypeDef USART_InitStructure = {0};
  5.     __RCC_GPIOA_CLK_ENABLE();
  6.     __RCC_UART1_CLK_ENABLE();
  7.     PA08_AFx_UART1TXD();
  8.     PA09_AFx_UART1RXD();
  9.     GPIO_InitStructure.Pins = GPIO_PIN_8;
  10.     GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
  11.     GPIO_Init(CW_GPIOA, &GPIO_InitStructure);
  12.     GPIO_InitStructure.Pins = GPIO_PIN_9;
  13.     GPIO_InitStructure.Mode = GPIO_MODE_INPUT_PULLUP;
  14.     GPIO_Init(CW_GPIOA, &GPIO_InitStructure);
  15.    
  16.     USART_InitStructure.USART_BaudRate = 115200;
  17.     USART_InitStructure.USART_Over = USART_Over_16;
  18.     USART_InitStructure.USART_Source = USART_Source_PCLK;
  19.     USART_InitStructure.USART_UclkFreq = SystemCoreClock;
  20.     USART_InitStructure.USART_StartBit = USART_StartBit_FE;
  21.     USART_InitStructure.USART_StopBits = USART_StopBits_1;
  22.     USART_InitStructure.USART_Parity = USART_Parity_No ;
  23.     USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  24.     USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
  25.     USART_Init(CW_UART1, &USART_InitStructure);
  26.     USART_SetAutoReload(CW_UART1, 0xFFFFFF);
  27.     USART_TimerModeConfig(CW_UART1, USART_TimerMode_AutoBaudRate1);
  28.     USART_ITConfig(CW_UART1, USART_IT_TIMOV | USART_IT_BAUD, ENABLE);
  29.     NVIC_SetPriority(UART1_IRQn, 0);
  30.     NVIC_EnableIRQ(UART1_IRQn);
  31. }
  32. uint8_t uart_baud_fin = 0;
  33. uint8_t uart_ov_count = 0;

  34. void readrxtotxbuffer()
  35. {
  36.     uart_txlen += uart_rxlen;
  37.     uart_rxlen = 0;
  38.     USART_ITConfig(CW_UART1, USART_IT_TXE, ENABLE);
  39. }

  40. void UART1_IRQHandler(void)
  41. {
  42.     uint32_t uart_baud_count = 0;
  43.     if(uart_baud_fin == 0)
  44.     {
  45.         if(USART_GetITStatus(CW_UART1, USART_IT_TIMOV))
  46.         {
  47.             USART_ClearITPendingBit(CW_UART1, USART_IT_TIMOV);
  48.             uart_ov_count += 1;
  49.         }
  50.         if(USART_GetITStatus(CW_UART1, USART_IT_BAUD))
  51.         {
  52.             uart_baud_count = (0x1000000*uart_ov_count + USART_GetCounter(CW_UART1)) / 4;
  53.             CW_UART1->BRRI = (uint16_t)(uart_baud_count >> 4);
  54.             CW_UART1->BRRF = (uint16_t)(uart_baud_count & 0x0F);
  55.             USART_ClearITPendingBit(CW_UART1, USART_IT_BAUD);
  56.             USART_ClearITPendingBit(CW_UART1, USART_IT_RC);
  57.             USART_ITConfig(CW_UART1, USART_IT_RC, ENABLE);
  58.             USART_TimerModeConfig(CW_UART1,USART_TimerMode_Idle);
  59.             USART_SetAutoReload(CW_UART1, 480);
  60.             uart_ov_count = 0;
  61.             uart_baud_fin = 1;
  62.         }
  63.     }
  64.     else
  65.     {
  66.         if(USART_GetITStatus(CW_UART1, USART_IT_RC) != RESET)
  67.         {
  68.             uart_buffer[uart_rxindex++] = USART_ReceiveData_8bit(CW_UART1);
  69.             if(uart_rxindex == UART_BUFFER_LEN)
  70.                 uart_rxindex = 0;
  71.             uart_rxlen++;
  72.             USART_ClearITPendingBit(CW_UART1, USART_IT_RC);
  73.         }
  74.         if(USART_GetITStatus(CW_UART1, USART_IT_TIMOV) != RESET || uart_rxlen == 10)
  75.         {
  76.             USART_ClearITPendingBit(CW_UART1, USART_IT_TIMOV);
  77.             USART_TimerModeConfig(CW_UART1,USART_TimerMode_Idle);
  78.             readrxtotxbuffer();
  79.         }
  80.         if(USART_GetITStatus(CW_UART1, USART_IT_TXE) != RESET)
  81.         {
  82.             if(uart_txlen > 0)
  83.             {
  84.                 USART_SendData_8bit(CW_UART1, uart_buffer[uart_txindex++]);
  85.                 if(uart_txindex == UART_BUFFER_LEN)
  86.                     uart_txindex = 0;
  87.                 uart_txlen--;
  88.             }
  89.             else
  90.             {
  91.                 USART_ITConfig(CW_UART1, USART_IT_TXE, DISABLE);
  92.             }
  93.             USART_ClearITPendingBit(CW_UART1, USART_IT_TXE);
  94.         }
  95.     }
  96. }
运行效果
QQ截图20230926165159.png
复位MCU修改上位机串口波特率
QQ截图20230926165228.png
如果改变了消息头波特率计算就会出错

QQ截图20230926165249.png
接下来简单说一下是如何实现的,先来了解一下串口数据的结构
QQ截图20230926162547.png
先是起始帧为低电平,计数器在下降沿开始计数,当遇到上升沿停止计数,这时累积的计数结果就是起始帧加上开头这几个为0的bit的总计数,以上面的为例用F8作为消息头消息结构如下
0 00011111 1
用总计数结果除以4就得到了1个bit所用的计数,波特率计数器和UART定时器的时钟源都是UCLK,可以认为它们的频率相同,之后用这个结果重新设置波特率计数器就能达到自动侦测波特率的功能
如果上位机单独改变了消息头比如F0(00001111),这时候加上起始帧就有5个为0的bit,这时程序里还是按照4个去计算就会造成计算出的波特率不准确,因此在实际使用时要协商好作为判断波特率的消息头




您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:同飞软件研发工程师
简介:制冷系统单片机软件开发,使用PID控制温度

161

主题

815

帖子

10

粉丝
快速回复 在线客服 返回列表 返回顶部
认证:同飞软件研发工程师
简介:制冷系统单片机软件开发,使用PID控制温度

161

主题

815

帖子

10

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