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

四、总结

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

 

 

  • 51
    点赞
  • 71
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
STM32HAL库的串口接收到数据时,会触发中断。中断处理函数会在接收数据缓冲区中读取数据,并将其存储在一个缓冲区中。一旦接收到完整的数据包,它就会被传递给应用程序处理。 以下是一个简单的示例代码,演示如何使用STM32HAL库接收串口数据中断: ```c #include "stm32f4xx_hal.h" #define BUFFER_SIZE 100 UART_HandleTypeDef huart2; uint8_t rxBuffer[BUFFER_SIZE]; uint8_t index = 0; void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart == &huart2) { if (rxBuffer[index - 1] == '\n') { // complete data has been received rxBuffer[index] = '\0'; // add null termination index = 0; // reset index // process data } else { index++; if (index >= BUFFER_SIZE) { // buffer overflow index = 0; } } HAL_UART_Receive_IT(&huart2, &rxBuffer[index], 1); // enable receive interrupt again } } int main(void) { HAL_Init(); __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_USART2_CLK_ENABLE(); GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = GPIO_PIN_2 | GPIO_PIN_3; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF7_USART2; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); huart2.Instance = USART2; huart2.Init.BaudRate = 115200; huart2.Init.WordLength = UART_WORDLENGTH_8B; huart2.Init.StopBits = UART_STOPBITS_1; huart2.Init.Parity = UART_PARITY_NONE; huart2.Init.Mode = UART_MODE_TX_RX; huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart2.Init.OverSampling = UART_OVERSAMPLING_16; HAL_UART_Init(&huart2); HAL_UART_Receive_IT(&huart2, &rxBuffer[index], 1); // enable receive interrupt while (1); } ``` 在上面的代码中,当串口接收到数据时,会调用`HAL_UART_RxCpltCallback()`函数。这个函数会检查接收的数据是否已经完整,如果是,则将其存储在缓冲区中,并将缓冲区重置为下一次接收。如果数据还不完整,则继续等待接收下一个字符,并再次启用接收中断。 注意,使用`HAL_UART_Receive_IT()`函数启用接收中断时,需要传递一个指向缓冲区的指针和要接收的字节数。在上面的代码中,我们每次只接收一个字节,因为我们需要检查完整性。如果您的应用程序可以保证数据完整性,那么可以一次性接收多个字节。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值