STM32 HAL库 串口中断接收数据包

目录

一、CUBEmx配置

1.设置系统时钟,配置SYS,配置时钟树

 ​编辑

 2.配置串口USART1

3.配置NVIC,开启串口中断

​编辑4.点击GENERATE CODE输出文件即可

二、代码部分

0.串口重定向——printf

1.关于舵机

2.开启串口中断函数

3.编写串口回调函数

4.主函数部分

三、实验现象:

四、总结


 

        刚从标准库转到HAL学习,最近需要做一个机械臂控制,打算用USART1串口中断的方式控制四个舵机运行,现在记录一下过程,主控使用STM32F103ZET6。(本文只讲中断的接收,舵机控制暂时不涉及)

一、CUBEmx配置

1.设置系统时钟,配置SYS,配置时钟树

d930e98adeeb420ab157db1588c24f0e.png

f02f3bcf46cb407ab230f43ae3ffd55e.png

 14185f53defc4b57be83a55849d1e199.png

 2.配置串口USART1

a73f50c94aa74ab3ad868d234065de87.png

3.配置NVIC,开启串口中断

这里我将中断的优先级设置为12(单独学习串口中断并不需要修改优先级配置,按照0级即可)

8bee212e58fc40ac8d1226c61be27aee.png4.点击GENERATE CODE输出文件即可

二、代码部分

我使用CLION进行编写,使用keil的方式也是一样的。

0.串口重定向——printf

在用户自定义区加上如下代码即可使用:

70b9a9980b254ee89a99d966d97d53b3.png

#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
PUTCHAR_PROTOTYPE
{
    HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1, 0xFFFF);
    return ch;
}

1.关于舵机

舵机使用了两个180°舵机和两个270°舵机,由于uint8_t最大到255,所以打算使用两个八位去存储舵机旋转的角度。

数据包采用如下形式,通过定义一个数组去存放数据包

uint8_t Rx_data[5];//数据包加上包头一共5个byte
uint8_t Rx_flag=0;//设置一个中断完成的标志位

1c4df32d3a81411184f4bdacf2eb8d66.png

2.开启串口中断函数

  HAL_UART_Receive_IT(&huart1,(uint8_t*)Rx_data,5);//开启串口接收中断函数

函数的三个参数,第一个指定是USART1的串口中断,第二个参数指定接受数据的位置,第三个指定数据接收的长度,当串口接收到5个字节的数据时,才会触发中断回调函数。 

3.编写串口回调函数

回调函数仅仅负责将接受完成的标志位置1,处理工作再主函数while循环中进行。 

 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if(huart->Instance==USART1)//首先判断是否是USART1触发的中断
    {
        Rx_flag=1;
    }
}

4.主函数部分

首先判断标志位,即是否接受完5个字节的数据,判断完成后,先将标志位即Rx_flag清零,否则会一直卡在主循环中,接着开启下一次的串口中断。

 if(Rx_flag==1)
      {
          Rx_flag=0;
          HAL_UART_Receive_IT(&huart1,(uint8_t*)Rx_data,5);
  
       }

接着就是对数据包中的数据进行解析了:

1.首先判断包头包尾是否满足协议规范。然后在处理里面的数据:这里我通过switch语句对舵机的ID进行判断:0x01-0x04代表着四个舵机。

2.关于度数的转换:angle=Rx_data[2] * 256 + Rx_data[3],即高位为Rx_data[2] ,低位为Rx_data[3]。

switch (Rx_data[1]) {
                  case 0x01: {
                      printf("Servo_1 turn\r\n");
                      printf("angle=%d\r\n", Rx_data[2] * 256 + Rx_data[3]);
                  }
                      break;
                  case 0x02: {
                      printf("Servo_2 turn\r\n");
                      printf("angle=%d\r\n", Rx_data[2] * 256 + Rx_data[3]);
                  }
                      break;
                  case 0x03: {
                      printf("Servo_3 turn\r\n");
                      printf("angle=%d\r\n", Rx_data[2] * 256 + Rx_data[3]);
                  }
                      break;
                  case 0x04: {
                      printf("Servo_4 turn\r\n");
                      printf("angle=%d\r\n", Rx_data[2] * 256 + Rx_data[3]);
                  }
                      break;
                  default:
                      printf("data_error!");
              }

 3.处理完数据后需要把Rx_data[]中的值全部清零以便下一次的接收:

          for (int i = 0; i < 5; ++i) {
              Rx_data[i]=0;
          }

4.主循环全部函数

  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
      if(Rx_flag==1)
      {
          Rx_flag=0;
          HAL_UART_Receive_IT(&huart1,(uint8_t*)Rx_data,5);
          if(Rx_data[0]==0xaa&&Rx_data[4]==0xff) {
              switch (Rx_data[1]) {
                  case 0x01: {
                      printf("Servo_1 turn\r\n");
                      printf("angle=%d\r\n", Rx_data[2] * 256 + Rx_data[3]);
                  }
                      break;
                  case 0x02: {
                      printf("Servo_2 turn\r\n");
                      printf("angle=%d\r\n", Rx_data[2] * 256 + Rx_data[3]);
                  }
                      break;
                  case 0x03: {
                      printf("Servo_3 turn\r\n");
                      printf("angle=%d\r\n", Rx_data[2] * 256 + Rx_data[3]);
                  }
                      break;
                  case 0x04: {
                      printf("Servo_4 turn\r\n");
                      printf("angle=%d\r\n", Rx_data[2] * 256 + Rx_data[3]);
                  }
                      break;
                  default:
                      printf("data_error!");
              }
          }
          else
          {
              printf("Data——error!\r\n");
          }
          for (int i = 0; i < 5; ++i) {
              Rx_data[i]=0;
          }
      }
  }

三、实验现象:

多数厂家的ZET6带有ch340芯片,不需要单独使用usb转串口的工具,直接使用USB连接电脑即可。

bfcdeb1636e348a7828e6c869daf043a.png

5e17bac494984d45becad2b4a4648e42.png

c04ab60d5d67456da97035acc1eacf08.png

四、总结

        这仅仅是串口中断接收的部分,关于舵机控制会在下一篇文章给出,第一次写文章,能力不足,有许多改进的地方,希望大家批评指正!

 

 

<think>嗯,用户想了解如何使用STM32 HAL实现串口中断接收功能,还要示例代码。首先,我需要回顾一下HAL串口中断处理流程。记得HAL提供了中断处理函数,比如HAL_UART_IRQHandler,但用户需要自己编写回调函数来处理接收完成的事件。 首先,用户可能已经用STM32CubeMX配置了串口,但需要确认是否开启了中断。在CubeMX中,启用USART的全局中断是必须的,否则无法触发中断。然后,初始化部分要调用HAL_UART_Init,接着启动接收,使用HAL_UART_Receive_IT函数,这个函数会启动中断接收,并设置接收缓冲区和长度。 接下来,中断服务函数的问题。HAL已经处理了大部分底层的中断标志,用户只需要在stm32f1xx_it.c中调用HAL_UART_IRQHandler,然后实现HAL_UART_RxCpltCallback回调函数。这个回调函数会在接收完成时被调用,用户可以在里面处理接收到的数据,比如回显或者存储。 用户可能遇到接收卡死的问题,比如没有及时重启接收中断,或者在回调函数中做了太多操作,导致中断被阻塞。这时候需要确保在回调函数中尽快处理数据,并重新启动接收。例如,在回调函数里调用HAL_UART_Receive_IT再次启动接收,以等待下一个数据。 示例代码方面,可能需要展示CubeMX的配置步骤,比如选择USART1,设置波特率,开启中断,生成代码。然后添加用户代码,在main函数中启动接收,并实现回调函数。还要注意volatile变量来存储接收数据,避免编译器优化导致的问题。 另外,用户可能对HAL的函数不太熟悉,需要解释HAL_UART_Receive_IT的参数,比如huart指向串口句柄,pData是缓冲区指针,Size是接收的字节数。还要提醒错误处理,检查返回值,确保接收启动成功。 可能还需要提到DMA方式与中断方式的区别,但用户明确要求中断接收,所以可以暂不展开。不过如果提到性能优化,可能可以建议在后续问题中探讨。 最后,确保回答结构清晰,分步骤说明配置和代码实现,并给出常见问题的解决方法,比如接收卡死的可能原因,如未重新启动接收中断。同时引用提供的参考资料,比如引用[1]提到的接收卡死问题,引用[2]和[3]中的HAL函数说明。</think>### STM32 HAL串口中断接收实现方法 #### 一、实现步骤 1. **CubeMX配置** - 启用USART模块并配置参数(波特率/数据位/校验位) - 在NVIC Settings中勾选USART全局中断使能 - 生成代码时选择生成HAL初始化代码 2. **关键代码实现** ```c // 在main.c中添加全局变量 volatile uint8_t rx_buffer[1]; // 单字节接收缓冲区 int main(void) { HAL_Init(); SystemClock_Config(); MX_USART1_UART_Init(); // 启动中断接收 HAL_UART_Receive_IT(&huart1, rx_buffer, 1); while (1) { // 主循环处理其他任务 } } // 中断回调函数 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if(huart->Instance == USART1) { // 处理接收到的数据(示例:回显) HAL_UART_Transmit(&huart1, rx_buffer, 1, 100); // 重新启动接收 HAL_UART_Receive_IT(&huart1, rx_buffer, 1); } } ``` #### 二、关键函数说明 1. `HAL_UART_Receive_IT()`:启动非阻塞接收 - 参数:串口句柄/接收缓冲区指针/接收数据长度 - 返回值:HAL_OK表示启动成功 2. `HAL_UART_RxCpltCallback()`:接收完成回调函数 - 在中断服务程序中被自动调用 - 必须重新调用`HAL_UART_Receive_IT()`以持续接收 #### 三、常见问题解决 1. **接收卡死**:确保在回调函数中重新启动接收[^1] 2. **数据丢失**:使用循环缓冲区并提高中断优先级 3. **波特率误差**:通过CubeMX时钟树配置确保波特率精度 #### 四、完整示例流程图 ``` CubeMX配置 ↓ 生成初始化代码 ↓ main()启动中断接收中断触发→调用HAL_UART_IRQHandler ↓ 自动调用RxCpltCallback ↓ 处理数据并重启接收 ```
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值