项目场景:
接受思翼遥控器(当然所有带有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)