【STM32】HAL库CubeMX之串口接受、SBUS输出、SBUS解析 项目场景1.接受部分空闲中断加DMA输出:标准16通道的SBUS格式1.接受部分标准16通道的SBUS格式输出:串口打印16通道的值SBUS协议简介通信接口USARTTTL通信参数1起始位8数据位偶校验位2停止位波特率100K,电平逻辑反转。通信速率每14ms模拟模式或7ms高速模式发送数据帧格式SBUS原理图设计左侧接收右边发送DV3.0也可以用DV3.3该图为其他地方截取不做本次的串口1和串口3的参考NC为不焊接。cubemx操作步骤1.手动输入STM32F103CBT6/STM32F103CBU6,选高速晶振方便第二栏的时钟配置Clock ConfigurationSystem Core–RCC --High Speed Clock(HSE)–Crystal/Ceramic Resonator2.配置下载SW模式System Core–SYS–Dedug–Serial Wire添加DMA接受3.配置串口1参数添加DMA接受Connectivity–USART1–Mode–AsynchronousConnectivity–USART1–Parameter Settings–115200 8 N 1Connectivity–USART1–DMA settings–add 仅添加接受即可,通道自动分配默认即可4.配置串口3参数Connectivity–USART1–Mode–AsynchronousConnectivity–USART1–Parameter Settings–100000 9 NEVEN 1SBUS参数5.配置FREERTOSMiddleware and Solft–FREERTOS–interface–CMSIS_V1Middleware and Solft–FREERTOS–Tasks and Queues–Add 加两个任务即可6.配置输出的参数按如图配置即可注意路径不要出现中文SBUS输出程序修改步骤找到usart.c为方便使用printf打印函数和串口各种标志位在最开始的/* USER CODE BEGIN 0 */添加如果huart3报错需要在cubemx中打开串口3#includestdio.hintfputc(intch,FILE*f){HAL_UART_Transmit(huart3,(uint8_t*)ch,1,0xffff);//此处更改串口几 huart3returnch;}intfgetc(FILE*f){uint8_tch0;HAL_UART_Receive(huart3,ch,1,0xffff);returnch;}#includestdio.h#includestring.h//因为后面要用到清空数组的函数#includestdlib.h// 包含 strtol 所需uint8_trx_buffer1[BUFFER_SIZE1];// DMA接收缓冲区uint16_trx_len10;// 实际接收长度uint8_trecv_end_flag10;// 接收完成标志并在usart.h定义全局在/* USER CODE BEGIN Private defines */下添加#defineBUFFER_SIZE1128externuint8_trx_buffer1[BUFFER_SIZE1];// DMA接收缓冲区externuint16_trx_len1;// 实际接收长度externuint8_trecv_end_flag1;// 接收完成标志回到usart.c找到void MX_USART1_UART_Init(void)在/* USER CODE BEGIN USART1_Init 2 */下添加/* USER CODE BEGIN USART1_Init 2 */// 开启串口空闲中断__HAL_UART_ENABLE_IT(huart1,UART_IT_IDLE);// 启动DMA接收 注意不要同时使用 HAL_UART_Receive_IT() 和 HAL_UART_Receive_DMA()否则会发生冲突HAL_UART_Receive_DMA(huart1,rx_buffer1,BUFFER_SIZE1);/* USER CODE END USART1_Init 2 */划到最下方在/* USER CODE BEGIN 1 */下加自己的处理代码voidUsart1_Protocol(void){}找到stm32f1xx_it.c 在/* USER CODE BEGIN Includes */添加#includeusart.h找到stm32f1xx_it.c的中断函数void USART1_IRQHandler(void)在user code 添加/* USER CODE BEGIN USART1_IRQn 1 */if(__HAL_UART_GET_FLAG(huart1,UART_FLAG_IDLE)!RESET){__HAL_UART_CLEAR_IDLEFLAG(huart1);// 清除空闲中断标志HAL_UART_DMAStop(huart1);// 停止DMA传输rx_len1BUFFER_SIZE1-__HAL_DMA_GET_COUNTER(hdma_usart1_rx);// 实际接收长度recv_end_flag11;// 设置接收完成标志}/* USER CODE END USART1_IRQn 1 */执行函数 Usart1_Protocol放在freertos的while1里即可。以上就是简单的框架以下是接受处理部分比较简单识别固定长度的某个数据位识别带了就输出SBUS信息//接受变量缓冲区unsignedcharMotorStop_Flag0;unsignedcharDebug_Flag0;uint16_tchannels[SBUS_CHANNELS]{1100,1500,1500,1500,1500,1500,1500,1500,1500,1500,1500,1100,1100,1500,1500,1500};uint8_tsbusFrame[SBUS_FRAME_SIZE];// 存储 SBUS 数据帧voidUsart1_Protocol(void){unsignedchari0;unsignedcharCKM0;unsignedshortCrc_Dat;if(recv_end_flag11)//接受完成标志位{//校验1种 41 75 0C 01 11 0A 04 0A 00 62 F7 C0 B5 E6 F4 (尾部两位为CRC)/********************************************帧头校验与尾校验***************************/if((rx_len13)||rx_buffer1[0]!0x41||rx_buffer1[1]!0x75)//帧头校验{recv_end_flag10;rx_len10;memset(rx_buffer1,0,sizeof(rx_buffer1));HAL_UART_Receive_DMA(huart1,rx_buffer1,BUFFER_SIZE1);return;}if(rx_len115)// CRC16 校验总长度为15{Crc_DatCRC_16(rx_buffer1,13);if(Crc_Dat!((rx_buffer1[14]8)|rx_buffer1[13])){recv_end_flag10;rx_len10;memset(rx_buffer1,0,sizeof(rx_buffer1));HAL_UART_Receive_DMA(huart1,rx_buffer1,BUFFER_SIZE1);return;}}else{// 不符合任何已知协议格式recv_end_flag10;rx_len10;memset(rx_buffer1,0,sizeof(rx_buffer1));HAL_UART_Receive_DMA(huart1,rx_buffer1,BUFFER_SIZE1);return;}/********************************************解析并处理***************************/if(rx_len115){if(rx_buffer1[2]0x0C)//区分帧ID 做状态输出解析{Debug_Flag1;if(rx_buffer1[11]0x1F)MotorStop_Flag1;}//SBUS输出输出部分 //此处可以放到任务3做100Hz输出if(MotorStop_Flag0){memset(sbusFrame,0,25);sbusFrame[0]0x0F;// Start bytesbusFrame[23]0x00;// Flags and ch17-24 (optional)sbusFrame[24]0x00;// End byteuint32_tbuffer0;uint8_tbits_left0;uint8_t*psbusFrame[1];// Start of channel datafor(inti0;iSBUS_CHANNELS;i){uint32_tvalchannels[i];// 11-bit value (must be in range 0~2047)buffer|(valbits_left);// Add current value to bufferbits_left11;// While we have at least 8 bits, write a bytewhile(bits_left8){*p(uint8_t)(buffer0xFF);buffer8;bits_left-8;}}// After loop, there may be leftover bitsif(bits_left0){*p(uint8_t)(buffer0xFF);}HAL_UART_Transmit(huart3,sbusFrame,SBUS_FRAME_SIZE,100);}}///**************************解析完清空***************************/recv_end_flag10;rx_len10;memset(rx_buffer1,0,sizeof(rx_buffer1));HAL_UART_Receive_DMA(huart1,rx_buffer1,BUFFER_SIZE1);}}SBUS解析程序修改步骤定义都相同仅解析函数不同此处不分串口1和串口3只给出解析的代码//接受变量缓冲区unsignedcharMotorStop_Flag0;unsignedcharDebug_Flag0;uint16_tchannels[SBUS_CHANNELS]{1500,1500,1500,1500,1500,1500,1500,1500,1500,1500,1500,1500,1500,1500,1500,1500};uint8_tsbusFrame[SBUS_FRAME_SIZE];// 存储 SBUS 数据帧voidUsart1_Protocol(void){unsignedchari0;unsignedcharCKM0;if(recv_end_flag11)//接受完成标志位{// HAL_UART_Transmit(huart3,rx_buffer1, sizeof(rx_buffer1),100); //调试用//校验1种 帧头0x0F 帧尾0x00/********************************************帧头校验与尾校验***************************/if((rx_len13)||rx_buffer1[0]!0x0F||rx_buffer1[24]!0x00){recv_end_flag10;rx_len10;memset(rx_buffer1,0,sizeof(rx_buffer1));HAL_UART_Receive_DMA(huart1,rx_buffer1,BUFFER_SIZE1);return;}/********************************************解析并处理***************************/if(rx_len125){Debug_Flag1;channels[0]((int16_t)rx_buffer1[1]0|((int16_t)rx_buffer1[2]8))0x07FF;channels[1]((int16_t)rx_buffer1[2]3|((int16_t)rx_buffer1[3]5))0x07FF;channels[2]((int16_t)rx_buffer1[3]6|((int16_t)rx_buffer1[4]2)|(int16_t)rx_buffer1[5]10)0x07FF;channels[3]((int16_t)rx_buffer1[5]1|((int16_t)rx_buffer1[6]7))0x07FF;channels[4]((int16_t)rx_buffer1[6]4|((int16_t)rx_buffer1[7]4))0x07FF;channels[5]((int16_t)rx_buffer1[7]7|((int16_t)rx_buffer1[8]1)|(int16_t)rx_buffer1[9]9)0x07FF;channels[6]((int16_t)rx_buffer1[9]2|((int16_t)rx_buffer1[10]6))0x07FF;channels[7]((int16_t)rx_buffer1[10]5|((int16_t)rx_buffer1[11]3))0x07FF;channels[8]((int16_t)rx_buffer1[12]0|((int16_t)rx_buffer1[13]8))0x07FF;channels[9]((int16_t)rx_buffer1[13]3|((int16_t)rx_buffer1[14]5))0x07FF;channels[10]((int16_t)rx_buffer1[14]6|((int16_t)rx_buffer1[15]2)|(int16_t)rx_buffer1[16]10)0x07FF;channels[11]((int16_t)rx_buffer1[16]1|((int16_t)rx_buffer1[17]7))0x07FF;channels[12]((int16_t)rx_buffer1[17]4|((int16_t)rx_buffer1[18]4))0x07FF;channels[13]((int16_t)rx_buffer1[18]7|((int16_t)rx_buffer1[19]1)|(int16_t)rx_buffer1[20]9)0x07FF;channels[14]((int16_t)rx_buffer1[20]2|((int16_t)rx_buffer1[21]6))0x07FF;channels[15]((int16_t)rx_buffer1[21]5|((int16_t)rx_buffer1[22]3))0x07FF;//串口3打印16通道数据printf(CH1%d,CH2%d,CH3%d,CH4%d,CH5%d,CH6%d,CH7%d,CH8%d,CH9%d,CH10%d,CH11%d,CH12%d,CH13%d,CH14%d,CH15%d,CH16%d\r\n,channels[0],channels[1],channels[2],channels[3],channels[4],channels[5],channels[6],channels[7],channels[8],channels[9],channels[10],channels[11],channels[12],channels[13],channels[14],channels[15]);}/**************************解析完清空***************************/recv_end_flag10;rx_len10;memset(rx_buffer1,0,sizeof(rx_buffer1));HAL_UART_Receive_DMA(huart1,rx_buffer1,BUFFER_SIZE1);}}数据打印16通道数据LED指示与按键触发操作HAL_Delay(1000); //毫秒级延时,若定义freertos里面的延时为osDelay(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)

相关新闻

最新新闻

飞凌嵌入式ElfBoard-test命令

飞凌嵌入式ElfBoard-test命令

test命令用于测试某个条件是否成立,它可以进行数值、字符和文件三个方面的测试。在shell文件中输入命令,通过特定的参数可以对数值、字符串进行比较,如下参数及示例。1. 数值比较参数参数说明-eq等于为真-ne不等于为真-gt大于为真-ge大于等于…

2026/9/1 12:16:53
高压开关电源设计实战:从拓扑选型到PCB布局与调试全解析

高压开关电源设计实战:从拓扑选型到PCB布局与调试全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/1 12:16:53
Libgdx游戏开发入门:从零搭建跨平台Java游戏框架项目

Libgdx游戏开发入门:从零搭建跨平台Java游戏框架项目

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/1 12:16:53
.NET 8依赖注入实战:从核心原理到ASP.NET Core最佳实践

.NET 8依赖注入实战:从核心原理到ASP.NET Core最佳实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/1 12:16:53
用Python和FFmpeg搭建跑团视频字幕流水线:编码、时间轴与压制实践

用Python和FFmpeg搭建跑团视频字幕流水线:编码、时间轴与压制实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/1 12:16:53
从oqc0514.zip看OQC出货检验:数据规范与改善价值

从oqc0514.zip看OQC出货检验:数据规范与改善价值

简介:这份压缩包是一套制造执行系统(MES)基础版的完整前后端项目,面向工厂信息化实施人员、工业软件开发者及MES初学者,适合用于二次开发、功能定制或学习生产管理流程的落地实现。包体共80个文件,总大小约…

2026/9/1 12:11:53