【STM32】HAL库CubeMX之SBUS解析串口处理

项目场景:

接受思翼遥控器(当然所有带有SBUS协议的均可)的16通道数据来控制履带车的运动或者喷火器的俯仰,水平旋转,喷油、喷火、灯光等动作。

SBUS协议简介:

通信接口:USART(TTL)
通信参数:1起始位+8数据位+偶校验位+2停止位,波特率=100K,电平逻辑反转。
通信速率:每14ms(模拟模式)或7ms(高速模式)发送
数据帧格式
在这里插入图片描述

CUBEMX配置准备《会配置的跳过此步骤》:

以下警告都没事,是因为IO口占用,部分功能不能用导致的,例如:配置了某些io口输出模式而不能用定时器的一些通道。
1.选择芯片STM32F103C8T6后 ,设置外部晶振在这里插入图片描述
2.设置时钟
在这里插入图片描述
3.设置调试SW模式,其他不变
在这里插入图片描述

4.设置串口2作为SBUS接受口 选择异步通讯asynchronous 不使用硬件流 波特率100K ,8位数据位 偶校验 2位停止位 仅接受模式即可 NVIC开启中断 DMA开启接受 (后来发现配置为9位数据位 偶校验 1位停止位数据输出才正确吗,这是个坑,要注意)
在这里插入图片描述
5.设置串口1作为无线通讯调试口 115200 8 N 1 NVIC开启中断 DMA开启接受
在这里插入图片描述

6.使能串口中断并设置优先级
在这里插入图片描述

7.为了方便定时多任务处理,用FREERTOS添加三个任务(CMSISV1就够),参数全部默认

V2是V1的扩展,增加了对Armv8-M的支持,还增加了动态对象,多核系统一些内容,当然,内容越多,代码量就越大,占用的内存就多了。
所以使用原版的FreeRTOS最精简,占的内存最小,V1使用方便一些,如果V1不能满足你的使用,你可以选择V2。一般使用STM32CubeMX使用CMSIS_V1就可以了。

在这里插入图片描述
8.注意:选择freertos系统时不能选择systick为时钟源,选择其他定时器,这里选择TM1
然后配置任务与队列,用于配置任务体以及消息队列
在这里插入图片描述

9.工程管理像这样设置即可,Advanced setting默认即可:
在这里插入图片描述
在这里插入图片描述

generate code 生成代码框架
当程序打开的时候重新配置了,点击generation直接点close!
在这里插入图片描述

代码添加准备

先处理串口2(既配置DMA定长接受处理方法)
main函数前加定义:

 unsigned char receivebuffer2[24];

大循环之前加:

	__HAL_UART_ENABLE_IT(&huart2,UART_IT_RXNE);   //开启串口x接受中断
	HAL_UART_Receive_IT(&huart2,(uint8_t*)receivebuffer2,25); //中断模式下接收数据的函数初始化缓冲

在uart.c文件添加:

#include <stdio.h>
#include <string.h>  //因为后面要用到清空数组的函数
volatile uint8_t rx_len2 = 0;  //接收一帧数据的长度
volatile uint8_t recv_end_flag2 = 0; //一帧数据接收完成标志
unsigned char rx_buffer2[25]={0};  //接收数据缓存数组

并在.h里 :

   #define BUFFER_SIZE2  6 

在uart.c文件找到初始化,添加:

  /* USER CODE BEGIN USART2_Init 2 */

	__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE); //使能IDLE中断
 
//DMA接收函数,此句一定要加,不加接收不到第一次传进来的实数据,是空的,且此时接收到的数据长度为缓存器的数据长度
	HAL_UART_Receive_DMA(&huart2,rx_buffer2,BUFFER_SIZE2);

	
  /* USER CODE END USART2_Init 2 */

在stm32f1xx.it.c文件中包含 #include “usart.h” , 找到USART1_IRQHandler,在uercode里添加 :

  /* USER CODE BEGIN USART1_IRQn 0 */
	uint32_t tmp_flag = 0;
	uint32_t temp;
	if(USART2 == huart2.Instance)     // 判断是否是空闲中断                                
  {     
    if(RESET != __HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE))  
    {         
       __HAL_UART_CLEAR_IDLEFLAG(&huart2);  // 清除空闲中断标志)                 
      //  HAL_UART_RxCpltCallback(&huart2); //调用用户空闲中断回调函数 
			HAL_UART_DMAStop(&huart2); //  停止DMA传输,防止   
      temp  =  __HAL_DMA_GET_COUNTER(&hdma_usart2_rx);// 获取DMA中未传输的数据个数   	
	    rx_len2 =  BUFFER_SIZE2 - temp; //总计数减去未传输的数据个数,得到已经接收的数据个数
		  recv_end_flag2 = 1;	// 接受完成标志位置1				
    }
  }
  /* USER CODE END USART1_IRQn 0 */

在uart.c文件中添加处理函数,当然.h文件要定义此函数

/* USER CODE BEGIN 1 */
unsigned int CH[25];
unsigned char Dog_buffer2[25];
void  Usart2_Protocol(void)
{
	
	 unsigned char i=0;
   unsigned char CKM=0;	
		if(recv_end_flag2==1)	   //接受完成标志位
		{
	//	HAL_UART_Transmit(&huart1,rx_buffer2, sizeof(rx_buffer2),100);
 /********************************************校验***************************/
		if((rx_buffer2[0]!=0x0F)||(rx_buffer2[24]!=0x00))   
		{
	    rx_len2=0;
      recv_end_flag2=0; //清除接受完成标志位
	    memset(rx_buffer2,0x00,sizeof(rx_buffer2)); //清空数组
			return;
		}	
 	

			//HAL_UART_Transmit(&huart1,rx_buffer2, sizeof(rx_buffer2),100);

///*********************************SBUS通道数值解析********************************/
        CH[0] = ((int16_t)rx_buffer2[ 1] >> 0 | ((int16_t)rx_buffer2[ 2] << 8 )) & 0x07FF;
        CH[1] = ((int16_t)rx_buffer2[ 2] >> 3 | ((int16_t)rx_buffer2[ 3] << 5 )) & 0x07FF;
        CH[2] = ((int16_t)rx_buffer2[ 3] >> 6 | ((int16_t)rx_buffer2[ 4] << 2 ) | (int16_t)rx_buffer2[ 5] << 10 ) & 0x07FF;
        CH[3] = ((int16_t)rx_buffer2[ 5] >> 1 | ((int16_t)rx_buffer2[ 6] << 7 )) & 0x07FF;
        CH[4] = ((int16_t)rx_buffer2[ 6] >> 4 | ((int16_t)rx_buffer2[ 7] << 4 )) & 0x07FF;
        CH[5] = ((int16_t)rx_buffer2[ 7] >> 7 | ((int16_t)rx_buffer2[ 8] << 1 ) | (int16_t)rx_buffer2[9] << 9 ) & 0x07FF;
        CH[6] = ((int16_t)rx_buffer2[ 9] >> 2 | ((int16_t)rx_buffer2[10] << 6 )) & 0x07FF;
        CH[7] = ((int16_t)rx_buffer2[10] >> 5 | ((int16_t)rx_buffer2[11] << 3 )) & 0x07FF;
        CH[8] = ((int16_t)rx_buffer2[12] << 0 | ((int16_t)rx_buffer2[13] << 8 )) & 0x07FF;
        CH[9] = ((int16_t)rx_buffer2[13] >> 3 | ((int16_t)rx_buffer2[14] << 5 )) & 0x07FF;
        CH[10] = ((int16_t)rx_buffer2[14] >> 6 | ((int16_t)rx_buffer2[15] << 2 ) | (int16_t)rx_buffer2[16] << 10 ) & 0x07FF;
        CH[11] = ((int16_t)rx_buffer2[16] >> 1 | ((int16_t)rx_buffer2[17] << 7 )) & 0x07FF;
        CH[12] = ((int16_t)rx_buffer2[17] >> 4 | ((int16_t)rx_buffer2[18] << 4 )) & 0x07FF;
        CH[13] = ((int16_t)rx_buffer2[18] >> 7 | ((int16_t)rx_buffer2[19] << 1 ) | (int16_t)rx_buffer2[20] << 9 ) & 0x07FF;
        CH[14] = ((int16_t)rx_buffer2[20] >> 2 | ((int16_t)rx_buffer2[21] << 6 )) & 0x07FF;
        CH[15] = ((int16_t)rx_buffer2[21] >> 5 | ((int16_t)rx_buffer2[22] << 3 )) & 0x07FF;
				
///*********************************标准SBUS转YS SBUS********************************/				
				Dog_buffer2[0] = 0x0f;
				Dog_buffer2[1] = (unsigned char)((CH[0] & 0x07FF));
				Dog_buffer2[2] = (unsigned char)((CH[0] & 0x07FF) >> 8 |
													(CH[1] & 0x07FF) << 3);
				Dog_buffer2[3] = (unsigned char)((CH[1] & 0x07FF) >> 5 |
													(CH[2] & 0x07FF) << 6);
				Dog_buffer2[4] = (unsigned char)((CH[2] & 0x07FF) >> 2);
				Dog_buffer2[5] = (unsigned char)((CH[2] & 0x07FF) >> 10 |
													(CH[3] & 0x07FF) << 1);
				Dog_buffer2[6] = (unsigned char)((CH[3] & 0x07FF) >> 7 |
													(CH[4] & 0x07FF) << 4);
				Dog_buffer2[7] = (unsigned char)((CH[4] & 0x07FF) >> 4 |
													(CH[5] & 0x07FF) << 7);
				Dog_buffer2[8] = (unsigned char)((CH[5] & 0x07FF) >> 1);
				Dog_buffer2[9] = (unsigned char)((CH[5] & 0x07FF) >> 9   |
													(CH[6] & 0x07FF) << 2);
				Dog_buffer2[10] = (unsigned char)((CH[6] & 0x07FF) >> 6   |
														(CH[7] & 0x07FF) << 5);
				Dog_buffer2[11] = (unsigned char)((CH[7] & 0x07FF) >> 3);
				Dog_buffer2[12] = (unsigned char)((CH[8] & 0x07FF));
				Dog_buffer2[13] = (unsigned char)((CH[8] & 0x07FF) >> 8 |
														(CH[9]   & 0x07FF) << 3);
				Dog_buffer2[14] = (unsigned char)((CH[9] & 0x07FF) >> 5 |
														(CH[10] & 0x07FF) << 6);
				Dog_buffer2[15] = (unsigned char)((CH[10] & 0x07FF) >> 2);
				Dog_buffer2[16] = (unsigned char)((CH[10] & 0x07FF) >> 10 |
														(CH[11] & 0x07FF) << 1);
				Dog_buffer2[17] = (unsigned char)((CH[11] & 0x07FF) >> 7   |
														(CH[12] & 0x07FF) << 4);
				Dog_buffer2[18] = (unsigned char)((CH[12] & 0x07FF) >> 4   |
														(CH[13] & 0x07FF) << 7);
				Dog_buffer2[19] = (unsigned char)((CH[13] & 0x07FF) >> 1);
				Dog_buffer2[20] = (unsigned char)((CH[13] & 0x07FF) >> 9   |
														(CH[14] & 0x07FF) << 2);
				Dog_buffer2[21] = (unsigned char)((CH[14] & 0x07FF) >> 6   |
														(CH[15] & 0x07FF) << 5);
				Dog_buffer2[22] = (unsigned char)((CH[15] & 0x07FF) >> 3);
				Dog_buffer2[23] = 0x00;
				Dog_buffer2[24] = 0x00;
				
			//	HAL_UART_Transmit(&huart1,Dog_buffer2, sizeof(Dog_buffer2),100);

///**************************解析完清空***************************/		
	//Send_RET(0,0,0);   //每一帧都回传 y也可以做调试用
  rx_len2=0;
  recv_end_flag2=0;   //清除接受完成标志位
	memset(rx_buffer2,0x00,sizeof(rx_buffer2)); //清空数组
   }
		HAL_UART_Receive_DMA(&huart2,(uint8_t *)rx_buffer2,BUFFER_SIZE2);   //再次开启DMA接收
}
/* USER CODE END 1 */

验证通道

将16通道值CH[0]–CH[15]打印出来,拨动摇杆,观察16通道值的变化。我试验的是思翼RC2024款的遥控器,取值变化范围272-1712,中间值为992
在这里插入图片描述

备用复制代码《与该工程无关》

GPIO篇:
在这里插入图片描述
毫秒级延时:
HAL_Delay(1000);
GPIO输出高或者低:《直接调用》
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_RESET);
HAL_GPIO_TogglePin(GPIOB GPIO_PIN_15); //相比于标准库多的一个函数
当然PB5可以定义在cubemx定义标签,如:HAL_GPIO_WritePin(FIRE_CTR1_GPIO_Port, FIRE_CTR1_Pin, GPIO_PIN_SET);
输入模式使用:通过判断IO口的返回值if(HAL_GPIO_ReadPin(MGG_CHECK_VOL_GPIO_Port,MGG_CHECK_VOL_Pin)==GPIO_PIN_SET)

### SBus 协议与 STM32 微控制器实现 #### 理解 SBus 协议特性 SBus 是一种由 Futaba 开发的双向通信协议,广泛应用于无线电控制领域。该协议采用单线串行接口,在接收机和飞行控制系统间传递数据。其特点在于支持多达 18 路通道的数据传输,并具备低延迟优势。由于 SBus 数据帧结构特殊——起始标志为 `0xF`, 结束标志为 `0x0` ——这使得识别变得简单明了[^1]。 #### 接收机到 STM32 的连接方式 为了使 STM32 正确读取来自接收机的 SBus 信号,通常需要在两者之间加入硬件取反电路。这是因为 SBus 使用逻辑电平反转来表示有效状态;而大多数 MCU 默认按照标准 TTL/CMOS 电平工作,因此通过增加一个简单的非门可以解决这一差异问题。 #### UART 配置细节 当利用 STM32 解析 SBus 信号时,UART 设置至关重要。具体来说,波特率应设为 100 kbps (即 100,000 bps),并且需指定两个停止位及偶校验(EVEN)模式。这些参数确保了与 SBus 物理层的一致性,从而保障可靠的数据交换过程[^3]。 ```c // 初始化USART配置函数示例 void MX_USART1_UART_Init(void){ huart1.Instance = USART1; huart1.Init.BaudRate = 100000; // 设定波特率为100k huart1.Init.WordLength = UART_WORDLENGTH_9B;// 字符长度设置为9位(含奇偶校验) huart1.Init.StopBits = UART_STOPBITS_2; // 停止位数量设定为2 huart1.Init.Parity = UART_PARITY_EVEN; // 启用偶校验 HAL_UART_Init(&huart1); } ``` #### 数据处理流程概述 一旦完成了上述初始化操作之后,就可以编写程序去监听并捕获完整的 SBus 数据包。每当检测到新的消息到达时,应用程序应当能够提取各个通道的具体数值,并将其转换成适合进一步使用的格式。对于某些应用场合而言,可能还需要考虑如何将接收到的信息转发给其他设备或系统组件,比如经由 CAN 总线发送出去[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

梁山1号

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值