1
完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
1个回答
|
|
1. SBUS信号简介
最近在搞一个项目的通信和控制,用到了SBUS,记录一下心得。 SBUS全称serial-bus,是一种串口通信协议,广泛应用于航模遥控器(接收机)中。只用一根信号线就能传输多达16通道的数据,比多路PWM捕获高效且省资源。
清楚了通信协议,解析就很简单了。我使用的是正点原子的阿波罗F7开发板,其他的板子是一样的。 (1) 串口配置 首先一些变量声明,串口uart.c里用到的 #define USART_REC_LEN 100 //定义最大接收字节数 200 #define RXBUFFERSIZE 1 //缓存大小 u8 USART1_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节. u16 USART1_RX_STA = 0; //接收状态标记 u8 aRxBuffer1[RXBUFFERSIZE]; //HAL库使用的串口接收缓冲 UART_HandleTypeDef UART1_Handler; //UART句柄 串口初始化函数 void uart1_init(u32 bound) { //UART 初始化设置 UART1_Handler.Instance = USART1; //USART1 UART1_Handler.Init.BaudRate = bound; //波特率 UART1_Handler.Init.WordLength = UART_WORDLENGTH_9B; //字长为8位数据格式 UART1_Handler.Init.StopBits = UART_STOPBITS_1; //一个停止位 UART1_Handler.Init.Parity = UART_PARITY_EVEN; //无奇偶校验位 UART1_Handler.Init.HwFlowCtl = UART_HWCONTROL_NONE; //无硬件流控 UART1_Handler.Init.Mode = UART_MODE_TX_RX; //收发模式 HAL_UART_Init(&UART1_Handler); //HAL_UART_Init()会使能UART1 HAL_UART_Receive_IT(&UART1_Handler, (u8 *)aRxBuffer1, RXBUFFERSIZE); //该函数会开启接收中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量 } void HAL_UART_MspInit(UART_HandleTypeDef *huart) { //GPIO端口设置 GPIO_InitTypeDef GPIO_Initure; if (huart->Instance == USART1) //如果是串口1,进行串口1 MSP初始化 { __HAL_RCC_GPIOA_CLK_ENABLE(); //使能GPIOA时钟 __HAL_RCC_USART1_CLK_ENABLE(); //使能USART1时钟 GPIO_Initure.Pin = GPIO_PIN_9; //PA9 GPIO_Initure.Mode = GPIO_MODE_AF_PP; //复用推挽输出 GPIO_Initure.Pull = GPIO_PULLUP; //上拉 GPIO_Initure.Speed = GPIO_SPEED_FAST; //高速 GPIO_Initure.Alternate = GPIO_AF7_USART1; //复用为USART1 HAL_GPIO_Init(GPIOA, &GPIO_Initure); //初始化PA9 GPIO_Initure.Pin = GPIO_PIN_10; //PA10 HAL_GPIO_Init(GPIOA, &GPIO_Initure); //初始化PA10 #if EN_USART1_RX HAL_NVIC_EnableIRQ(USART1_IRQn); //使能USART1中断通道 HAL_NVIC_SetPriority(USART1_IRQn, 3, 2); //抢占优先级3,子优先级3 #endif } } 这里有个诡异的地方就是stm32要设置成9个数据位,一个停止位,我一开始按照8个数据位、两个停止位读出来的数据是错的,后来改了之后才正常了。是不是和stm32内部的串口配置有关,哪位大神弄明白了告诉我哈。 (2) 串口中断接收 串口中断函数,在中断函数里面接收数据,进行SBUS信号解析。 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { int i; while (huart->Instance == USART1) //如果是串口1 { USART1_RX_BUF[USART1_RX_STA] = aRxBuffer1[0]; if (USART1_RX_STA == 0 && USART1_RX_BUF[USART1_RX_STA] != 0x0F) break; //帧头不对,丢掉 USART1_RX_STA++; if (USART1_RX_STA > USART_REC_LEN) USART1_RX_STA = 0; ///接收数据错误,重新开始接收 if (USART1_RX_BUF[0] == 0x0F && USART1_RX_BUF[24] == 0x00 && USART1_RX_STA == 25) //接受完一帧数据 { update_***us(USART1_RX_BUF); for (i = 0; i<25; i++) //清空缓存区 USART1_RX_BUF = 0; USART1_RX_STA = 0; } break; } } (3) 信号解析 上面中断函数里面有一个update_***us函数,原型为u8 update_***us(u8 *buf),解析subs信号全靠它了!!新建一个***us.c文件,输入如下代码 #include "***us.h" SBUS_CH_Struct SBUS_CH; //将***us信号转化为通道值 u8 update_***us(u8 *buf) { int i; if (buf[23] == 0) { SBUS_CH.ConnectState = 1; SBUS_CH.CH1 = ((int16_t)buf[ 1] >> 0 | ((int16_t)buf[ 2] << 8 )) & 0x07FF; SBUS_CH.CH2 = ((int16_t)buf[ 2] >> 3 | ((int16_t)buf[ 3] << 5 )) & 0x07FF; SBUS_CH.CH3 = ((int16_t)buf[ 3] >> 6 | ((int16_t)buf[ 4] << 2 ) | (int16_t)buf[ 5] << 10 ) & 0x07FF; SBUS_CH.CH4 = ((int16_t)buf[ 5] >> 1 | ((int16_t)buf[ 6] << 7 )) & 0x07FF; SBUS_CH.CH5 = ((int16_t)buf[ 6] >> 4 | ((int16_t)buf[ 7] << 4 )) & 0x07FF; SBUS_CH.CH6 = ((int16_t)buf[ 7] >> 7 | ((int16_t)buf[ 8] << 1 ) | (int16_t)buf[9] << 9 ) & 0x07FF; SBUS_CH.CH7 = ((int16_t)buf[ 9] >> 2 | ((int16_t)buf[10] << 6 )) & 0x07FF; SBUS_CH.CH8 = ((int16_t)buf[10] >> 5 | ((int16_t)buf[11] << 3 )) & 0x07FF; SBUS_CH.CH9 = ((int16_t)buf[12] << 0 | ((int16_t)buf[13] << 8 )) & 0x07FF; SBUS_CH.CH10 = ((int16_t)buf[13] >> 3 | ((int16_t)buf[14] << 5 )) & 0x07FF; SBUS_CH.CH11 = ((int16_t)buf[14] >> 6 | ((int16_t)buf[15] << 2 ) | (int16_t)buf[16] << 10 ) & 0x07FF; SBUS_CH.CH12 = ((int16_t)buf[16] >> 1 | ((int16_t)buf[17] << 7 )) & 0x07FF; SBUS_CH.CH13 = ((int16_t)buf[17] >> 4 | ((int16_t)buf[18] << 4 )) & 0x07FF; SBUS_CH.CH14 = ((int16_t)buf[18] >> 7 | ((int16_t)buf[19] << 1 ) | (int16_t)buf[20] << 9 ) & 0x07FF; SBUS_CH.CH15 = ((int16_t)buf[20] >> 2 | ((int16_t)buf[21] << 6 )) & 0x07FF; SBUS_CH.CH16 = ((int16_t)buf[21] >> 5 | ((int16_t)buf[22] << 3 )) & 0x07FF; return 1; } else { SBUS_CH.ConnectState = 0; return 0; } } u16 ***us_to_pwm(u16 ***us_value) { float pwm; pwm = (float)SBUS_TARGET_MIN + (float)(***us_value - SBUS_RANGE_MIN) * SBUS_SCALE_FACTOR; // 1000 300 1000/1400 if (pwm > 2000) pwm = 2000; if (pwm < 1000) pwm = 1000; return (u16)pwm; } 上面定义了一个SBUS_CH_Struct 结构体类型的变量SBUS_CH,该结构体在***us.h中定义 typedef struct { uint16_t CH1;//通道1数值 uint16_t CH2;//通道2数值 uint16_t CH3;//通道3数值 uint16_t CH4;//通道4数值 uint16_t CH5;//通道5数值 uint16_t CH6;//通道6数值 uint16_t CH7;//通道7数值 uint16_t CH8;//通道8数值 uint16_t CH9;//通道9数值 uint16_t CH10;//通道10数值 uint16_t CH11;//通道11数值 uint16_t CH12;//通道12数值 uint16_t CH13;//通道13数值 uint16_t CH14;//通道14数值 uint16_t CH15;//通道15数值 uint16_t CH16;//通道16数值 uint8_t ConnectState;//遥控器与接收器连接状态 0=未连接,1=正常连接 }SBUS_CH_Struct; u16 ***us_to_pwm(u16 ***us_value)很好理解了,就是把***us的值转化为标准的1000-2000的pwm值,因为我用的遥控器***us值是300-1700,大家用的时候具体数值到时候可以通过串口直接读出来看看。 这样就读出了16个通道的数据啦。同时通过读取ConnectState位判断遥控器的状态,在主函数中采取失控保护。 上面这段解析数据的代码是国际通用的,可以用在任何使用***us协议的场合,可以很方便的移植到arduino、51、树莓派上面。 最后main函数里面就很简单了,只注意初始化串口设置为100K波特率。 void main() { /* 省略 */ uart1_init(100000); /* 省略 */ } |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1767 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1619 浏览 1 评论
1069 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
724 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1673 浏览 2 评论
1935浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
728浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
567浏览 3评论
592浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
551浏览 3评论
小黑屋| 手机版| Archiver| 德赢Vwin官网 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-21 23:37 , Processed in 0.717931 second(s), Total 76, Slave 60 queries .
Powered by 德赢Vwin官网 网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
德赢Vwin官网 观察
版权所有 © 湖南华秋数字科技有限公司
德赢Vwin官网 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号