[APM32E0] APM32E030串口中断与shell命令

[复制链接]
 楼主| LIZARD925 发表于 2025-7-12 00:52 | 显示全部楼层 |阅读模式
本帖最后由 LIZARD925 于 2025-7-12 22:03 编辑

#技术资源#  #申请原创#
APM32E030系列使用记录--串口的使用与命令行的移植
  • 串口发送+接收中断
目标:实现阻塞式发送+中断接收
程序中使用串口1,配置串口为8位数据位、1位停止位、无奇偶校验,波特率115200,都为通用的串口配置,初始化程序如下
  1. void Serial_Init(void)
  2. {
  3.         GPIO_Config_T GPIO_InitStructure;   
  4.         USART_Config_T USART_InitStructure;        
  5.         RCM_EnableAHBPeriphClock(RCM_AHB_PERIPH_GPIOA);
  6.         RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_USART1);
  7.         
  8.     GPIO_ConfigPinAF(GPIOA, GPIO_PIN_SOURCE_9, GPIO_AF_PIN1);
  9.     GPIO_ConfigPinAF(GPIOA, GPIO_PIN_SOURCE_10, GPIO_AF_PIN1);
  10.         
  11.         GPIO_InitStructure.mode = GPIO_MODE_AF;                    // 复用模式
  12.         GPIO_InitStructure.pin = GPIO_PIN_9;      
  13.         GPIO_InitStructure.speed = GPIO_SPEED_50MHz;        
  14.         GPIO_InitStructure.pupd = GPIO_PUPD_PU;                        //上拉输入
  15.         GPIO_InitStructure.outtype = GPIO_OUT_TYPE_PP;        //推挽输出
  16.         GPIO_Config(GPIOA,&GPIO_InitStructure);
  17.         
  18.         GPIO_InitStructure.pin  = GPIO_PIN_10;
  19.     GPIO_Config(GPIOA, &GPIO_InitStructure);
  20.         

  21.         USART_InitStructure.baudRate = 115200;
  22.         USART_InitStructure.hardwareFlowCtrl = USART_FLOW_CTRL_NONE;
  23.         USART_InitStructure.mode = USART_MODE_TX_RX;
  24.         USART_InitStructure.parity = USART_PARITY_NONE;        //无奇偶校验
  25.         USART_InitStructure.stopBits = USART_STOP_BIT_1;
  26.         USART_InitStructure.wordLength = USART_WORD_LEN_8B;
  27.         USART_Config(USART1, &USART_InitStructure);
  28.         
  29.         USART_EnableInterrupt(USART1, USART_INT_RXBNEIE);
  30.         NVIC_EnableIRQRequest(USART1_IRQn, 2);
  31.         
  32.         USART_Enable(USART1);

  33. }
发送函数使用阻塞发送,等待发送结束,并重映射printf函数,如下:
  1. void Serial_SendByte(uint8_t Byte)
  2. {
  3.         USART_TxData(USART1, Byte);
  4.         while (USART_ReadStatusFlag(USART1, USART_FLAG_TXBE) == RESET);
  5. }
使用时需要打开
195996871117dca242.png
否则串口发送不成功。
中断接收函数如下,实现接收到数据后,原封不动的返回数据:
  1. void USART1_IRQHandler(void)
  2. {
  3.         uint8_t dat;

  4.     if (USART_ReadStatusFlag(USART1, USART_FLAG_RXBNE) == SET)
  5.     {
  6.         dat = (uint8_t)USART_RxData(USART1);

  7.         printf("%c", dat);
  8.     }
  9. }
现象:串口发送的字符芯片会原封不动的返回 9793768726924c4014.png
  • 串口中断发送+接收中断
目标:实现中断发送+中断接收
程序中使用串口1,配置串口为8位数据位、1位停止位、无奇偶校验,波特率115200,都为通用的串口配置,初始化程序如下
  1. #define TINY_COM1                        USART1
  2. #define TINY_COM1_CLK                    RCM_APB2_PERIPH_USART1

  3. #define TINY_COM1_TX_PIN                 GPIO_PIN_9
  4. #define TINY_COM1_TX_GPIO_PORT           GPIOA
  5. #define TINY_COM1_TX_GPIO_CLK            RCM_AHB_PERIPH_GPIOA
  6. #define TINY_COM1_TX_SOURCE              GPIO_PIN_SOURCE_9
  7. #define TINY_COM1_TX_AF                  GPIO_AF_PIN1

  8. #define TINY_COM1_RX_PIN                 GPIO_PIN_10
  9. #define TINY_COM1_RX_GPIO_PORT           GPIOA
  10. #define TINY_COM1_RX_GPIO_CLK            RCM_AHB_PERIPH_GPIOA
  11. #define TINY_COM1_RX_SOURCE              GPIO_PIN_SOURCE_10
  12. #define TINY_COM1_RX_AF                  GPIO_AF_PIN1

  13. #define TINY_COM1_IRQn                   USART1_IRQn
  1. void Serial_Init(uint32_t baud)
  2. {
  3.         GPIO_Config_T GPIO_InitStructure;   
  4.         USART_Config_T USART_InitStructure;        
  5.         RCM_EnableAHBPeriphClock(TINY_COM1_TX_GPIO_CLK|TINY_COM1_RX_GPIO_CLK);
  6.         RCM_EnableAPB2PeriphClock(TINY_COM1_CLK);
  7.         
  8.     GPIO_ConfigPinAF(TINY_COM1_TX_GPIO_PORT, TINY_COM1_TX_SOURCE, TINY_COM1_TX_AF);
  9.     GPIO_ConfigPinAF(TINY_COM1_RX_GPIO_PORT, TINY_COM1_RX_SOURCE, TINY_COM1_RX_AF);
  10.         
  11.         GPIO_InitStructure.mode = GPIO_MODE_AF;                    // 复用模式
  12.         GPIO_InitStructure.pin = TINY_COM1_TX_PIN;      
  13.         GPIO_InitStructure.speed = GPIO_SPEED_50MHz;        
  14.         GPIO_InitStructure.pupd = GPIO_PUPD_PU;                        //上拉输入
  15.         GPIO_InitStructure.outtype = GPIO_OUT_TYPE_PP;        //推挽输出
  16.         GPIO_Config(TINY_COM1_TX_GPIO_PORT,&GPIO_InitStructure);
  17.         
  18.         GPIO_InitStructure.pin  = TINY_COM1_RX_PIN;
  19.     GPIO_Config(TINY_COM1_RX_GPIO_PORT, &GPIO_InitStructure);
  20.         

  21.         USART_InitStructure.baudRate = 115200;
  22.         USART_InitStructure.hardwareFlowCtrl = USART_FLOW_CTRL_NONE;
  23.         USART_InitStructure.mode = USART_MODE_TX_RX;
  24.         USART_InitStructure.parity = USART_PARITY_NONE;        //无奇偶校验
  25.         USART_InitStructure.stopBits = USART_STOP_BIT_1;
  26.         USART_InitStructure.wordLength = USART_WORD_LEN_8B;
  27.         USART_Config(TINY_COM1, &USART_InitStructure);
  28.         
  29.         USART_EnableInterrupt(TINY_COM1, USART_INT_RXBNEIE);
  30.         USART_DisableInterrupt(TINY_COM1, USART_INT_TXBEIE);        //失能中断发送
  31.         NVIC_EnableIRQRequest(TINY_COM1_IRQn, 2);
  32.         
  33.         USART_Enable(TINY_COM1);
  34.         
  35.         ring_buf_init(&Tbsend, UART1_TxBuff, sizeof(UART1_TxBuff));        /*初始化环形缓冲发送区*/
  36.     ring_buf_init(&Rbrecv, UART1_RxBuff, sizeof(UART1_RxBuff)); /*初始化环形缓冲接收区*/
  37. }
此程序只在第一个的基础上,增加了环形缓冲队列的初始化和失能中断发送,环形缓冲队列有两个,一个为接收一个为发送
  1. #define UART1_RX_SIZE 512                //接收缓冲区长度
  2. #define UART1_TX_SIZE 512                //发送缓冲区长度

  3. uint8_t  UART1_RxBuff[UART1_RX_SIZE];                //串口接收缓冲区
  4. uint8_t  UART1_TxBuff[UART1_TX_SIZE];                //串口发送缓冲区

  5. static ring_buf_t Tbsend, Rbrecv;                        /*收发缓冲区管理*/
我们将接收缓冲队列发到中断接收中,进行数据的接收,这样收到的数据将会存在接收队列中,如果在存满前,不将数据取出来,在满了后,新数据将无法存进去
  1.    if (USART_ReadStatusFlag(TINY_COM1, USART_FLAG_RXBNE) == SET)
  2.     {
  3.                 uint8_t dat;
  4.         dat = (uint8_t)USART_RxData(TINY_COM1);
  5.                 ring_buf_put(&Rbrecv, &dat, 1);           /*将数据放入接收缓冲区*/   
  6.     }
对于发送中断,单个数据发送我们修改 Serial_SendByte 函数,将数据放入发送缓冲区,并启动中断发送
  1. void Serial_SendByte(uint8_t Byte)
  2. {
  3.         ring_buf_put(&Tbsend, (unsigned char *)&Byte, 1);  
  4.         USART_EnableInterrupt(TINY_COM1, USART_INT_TXBEIE);        //中断发送
  5. }
多数据发送我们修改 Serial_SendArray函数,并在串口中断中添加如下程序,进行串口的数据发送,当得到数据长度为0时,关闭发送中断
  1. unsigned int Serial_SendArray(uint8_t *Array, uint16_t Length)
  2. {
  3.     unsigned int ret;
  4.     ret = ring_buf_put(&Tbsend, (unsigned char *)Array, Length);  
  5.     USART_EnableInterrupt(TINY_COM1, USART_INT_TXBEIE);        //中断发送
  6.     return ret; //返回实际放入的数据长度
  7. }
  1. if (USART_ReadStatusFlag(TINY_COM1, USART_FLAG_TXBE) == SET)//发送
  2.     {
  3.                 unsigned char send_data;
  4.                 USART_ClearStatusFlag(TINY_COM1, USART_FLAG_TXC);
  5.                 if (ring_buf_get(&Tbsend, &send_data, 1))                              /*从缓冲区中取出数据-返回数据的长度---*/
  6.                 {
  7.                         USART_TxData(TINY_COM1, send_data);  
  8.                 }                        
  9.                 else
  10.                 {
  11.                         USART_DisableInterrupt(TINY_COM1, USART_INT_TXBEIE);        //中断发送
  12.                 }
  13.     }
此时我们的中断发送+接收基本完成,将接收到的数据再次通过串口发送出去
  1. void rx_test(void)        //放到主循环,如果接受到数据,将数据发出去
  2. {   
  3.         unsigned char rx_data;
  4.    if (ring_buf_get(&Rbrecv, &rx_data, 1))
  5.    {
  6.            USART_TxData(TINY_COM1, rx_data);
  7.    }
  8. }
主函数
  1. int main (void)
  2. {
  3.         LED_init();

  4.         Serial_Init(115200);
  5.         Serial_SendByte(0x01);
  6.         Serial_SendString("Hello !\r\n");
  7.         Serial_SendNumber(123,3);
  8.         printf("APM32E030\r\n");
  9.         Serial_SendArray("123",3);
  10.         
  11.         while(1)
  12.         {
  13.                 printf("测试\r\n");
  14.                 LED1_turn();
  15.                 Delay_ms(1000);
  16.                 rx_test();
  17.         }
  18. }
现象:串口每一秒发送数据给串口助手,并将串口助手发送的数据原封不动的发送回去
60518687211458481b.png
  • nr_micro_shell 移植
下载nr_micro_shell 源码,地址为:
  1. https://gitee.com/nrush/nr_micro_shell
复制串口中断接收和发送的工程,将inc与src文件夹添加到自己的文件中,并添加到工程中
381868724a8d6f5ac.png
修改nr_micro_shell_config.h 中的部分参数,将打印函数重定向,并在 static_cmd 中添加自己的命令,将shell的处理函数放入此函数中进行解析
5094268724d83839ac.png
主函数中初始化shell命令与串口;
  1. #include "apm32e030.h"                  // Device header
  2. #include "Delay.h"
  3. #include "LED.h"
  4. #include "key.h"  
  5. #include "serial.h"  
  6. #include "nr_micro_shell.h"

  7. int main (void)
  8. {
  9.         LED_init();
  10.         Serial_Init(115200);
  11.     /* 初始化 */
  12.     shell_init();
  13.    
  14.         
  15.         while(1)
  16.         {
  17.                 rx_test();
  18.         }
  19. }
此时我们下载后,打开串口可看到如下所示界面:
1078268724ea30f99a.png
此时可输入ls -h 命令进行测试,根据命令提示进行使用
6297068724f322cde7.png
到此,串口的中断发送与shell命令行移植完成。



nr_micro_shell-master.zip

716.43 KB, 下载次数: 2

5-1 APM32E030-串口.zip

173.05 KB, 下载次数: 2

5-2 APM32E030-串口-中断发送.zip

177.14 KB, 下载次数: 3

5-3 APM32E030-串口-shell命令.zip

194.92 KB, 下载次数: 3

星云狂想曲 发表于 2025-7-14 18:50 | 显示全部楼层
楼主这个态度真好!
在分享知识之后还附带把实现工程提供了。
赞一下
夜幕叙事曲 发表于 2025-7-15 21:07 | 显示全部楼层
这个shell小库对于串口接收的方式没有对MCU的特点进行针对性的处理。
感觉仍然停留在51单片机的处理方式呢!
星云避风港 发表于 2025-7-17 19:48 | 显示全部楼层
啥样的应用会使用上命令行小工具啊?
我们项目的串口只有输出功能。
天体书记 发表于 2025-7-18 12:16 | 显示全部楼层
为什么要使用一个环形队列做缓冲啊
永恒回声 发表于 2025-7-21 17:24 | 显示全部楼层
串口接收还是挺建议使用DMA的。
我觉得这种命令行的脚本库挺不实用的。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

17

主题

19

帖子

0

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