文章目录
一、IBUS协议
IBUS (Intelligent Bus)是一种用于电子设备之间通信的协议,采用串行通信方式,允许多设备通过单一数据线通信,较低延迟,支持多主机和从机结构,常用于遥控器与天空端之间,富斯官网已公开协议,协议格式如下可见:
一共32字节,2 字节帧头+28字节数据位 + 2字节校验位组成
解码如下:
c
#define IBUS_USER_CHANNELS 10
#define IBUS_LENGTH 0x20
#define IBUS_COMMAND40 0x40
#define IBUS_MAX_CHANNLES 14
uint8_t rx_buffer[32] = {0};
uint16_t channel[IBUS_USER_CHANNELS] = {0};
uint16_t checksum_cal, checksum_ibus;
void IBUS_READ_CHANNEL(uint8_t user_channels)
{
uint16_t channel_buffer[IBUS_MAX_CHANNLES] = {0};
if(rx_buffer[0] == IBUS_LENGTH && rx_buffer[1] == IBUS_COMMAND40)
{
checksum_cal = 0xffff - rx_buffer[0] - rx_buffer[1];
for(int i = 0; i < IBUS_MAX_CHANNLES; i++)
{
channel_buffer[i] = (uint16_t)(rx_buffer[i * 2 + 3] << 8 | rx_buffer[i * 2 + 2]);
checksum_cal = checksum_cal - rx_buffer[i * 2 + 3] - rx_buffer[i * 2 + 2];
}
checksum_ibus = rx_buffer[31] << 8 | rx_buffer[30];
if(checksum_cal == checksum_ibus)
{
for(int j = 0; j < user_channels; j++)
{
channel[j] = channel_buffer[j];
}
}
}
HAL_UART_Receive_IT(IBUS_UART, rx_buffer, 32);
}
二、SBUS协议
SBUS(Serial Bus)也是一种用于遥控模型、无人机和其他应用程序中的串行通信协议,特别是在飞控系统和遥控设备之间,以实现高效的数据传输。
采用串行通信,单线信号传输,支持最多16个通道的控制,实时性强,可反向兼容PWM,设计考虑冗余性。
协议格式共有25字节数据,由首部(1字节)+ 数据(22字节)+ 标志位(1字节)+ 结束符(1字节)
组成
- 帧头: 0x0F
- 数据: 22 字节的数据,分别代表16个通道的数据,也即是每个通道的值用了 11 位来表示,22x8/16 = 11,每个通道的取值范围为 0~2047,低位在前、高位在后
- 标志位: 1字节,高四位从高到低依次表示:
bit7:CH17数字通道
bit6:CH16数字通道
bit5:帧丢失(Frame lost)
bit4:安全保护(Failsafe):失控保护激活位(0x10)判断飞机是否失控
bit3~bit0:低四位不用 - 结束符: 0x00
通道解析:
解码如下:
c
#define USART_BUF_SIZE 8
#define SBUS_DATA_SIZE 25
struct SBUS_t{
uint8_t head; // 1字节帧头
uint16_t ch[16]; // 16个通道数据
uint8_t flag; // 1字节标志位
uint8_t end; // 1字节结束
};
uint8_t usart_buf[USART_BUF_SIZE];
uint8_t sbus_rx_head = 0; // 发现起始字节 0x0F
uint8_t sbus_rx_sta = 0; // sbus 接收状态,0:未完成,1:已完成一帧接收
uint8_t sbus_rx_index; // 接收字节计数
uint8_t sbus_rx_buf[SBUS_DATA_SIZE]; // 接收sbus数据缓冲区
struct SBUS_t sbus; // SBUS 结构体实例化
void USART2_IRQHandler(void) //中断函数
{
uint8_t chr;
if ((__HAL_UART_GET_FLAG(&UART2_Handler, UART_FLAG_RXNE) != RESET)) // 接收中断
{
HAL_UART_Receive(&UART2_Handler, &chr, 1, 1000); // 接收一个字符
if (sbus_rx_sta == 0) // 接收未完成
{
if ((chr == 0x0F) || sbus_rx_head) // 找到首字节或已经找到首字节
{
sbus_rx_head = 1; // 标明已经找到首字母
if (sbus_rx_index < SBUS_DATA_SIZE) // 未接收到25个字符
{
sbus_rx_buf[sbus_rx_index] = chr; // 不断接收
sbus_rx_index ++;
}
else // 接收到25个字符了
{
sbus_rx_sta = 1; // 接收完成
sbus_rx_head = 0; // 清零,准备下一次接收
sbus_rx_index = 0;
}
}
}
}
HAL_UART_IRQHandler(&UART2_Handler);
}
/* 对SBUS协议数据进行解析 */
/* 实现对S.BUS协议缓存,头部为 0x0F,结尾为 0x00, 中间22Bytes16通道数据,1Byte标志符 */
void SbusParseTask(void *arg)
{
while (1)
{
if(sbus_rx_sta==1) // 接收完一帧
{
NVIC_DisableIRQ(USART2_IRQn); // 要关闭中断,防止读写混乱
sbus.head = sbus_rx_buf[0]; // 首部
sbus.flag = sbus_rx_buf[23]; // 标志符
sbus.end = sbus_rx_buf[24]; // 结尾
sbus.ch[0] =((sbus_rx_buf[2]<<8) + (sbus_rx_buf[1])) & 0x07ff;
sbus.ch[1] =((sbus_rx_buf[3]<<5) + (sbus_rx_buf[2]>>3)) & 0x07ff;
sbus.ch[2] =((sbus_rx_buf[5]<<10) + (sbus_rx_buf[4]<<2) + (sbus_rx_buf[3]>>6)) & 0x07ff;
sbus.ch[3] =((sbus_rx_buf[6]<<7) + (sbus_rx_buf[5]>>1)) & 0x07ff;
sbus.ch[4] =((sbus_rx_buf[7]<<4) + (sbus_rx_buf[6]>>4)) & 0x07ff;
sbus.ch[5] =((sbus_rx_buf[9]<<9) + (sbus_rx_buf[8]<<1) + (sbus_rx_buf[7]>>7)) & 0x07ff;
sbus.ch[6] =((sbus_rx_buf[10]<<6) + (sbus_rx_buf[9]>>2)) & 0x07ff;
sbus.ch[7] =((sbus_rx_buf[11]<<3) + (sbus_rx_buf[10]>>5)) & 0x07ff;
sbus.ch[8] =((sbus_rx_buf[13]<<8) + sbus_rx_buf[12]) & 0x07ff;
sbus.ch[9] =((sbus_rx_buf[14]<<5) + (sbus_rx_buf[13]>>3)) & 0x07ff;
sbus.ch[10]=((sbus_rx_buf[16]<<10) + (sbus_rx_buf[15]<<2) + (sbus_rx_buf[14]>>6)) & 0x07ff;
sbus.ch[11]=((sbus_rx_buf[17]<<7) + (sbus_rx_buf[16]>>1)) & 0x07ff;
sbus.ch[12]=((sbus_rx_buf[18]<<4) + (sbus_rx_buf[17]>>4)) & 0x07ff;
sbus.ch[13]=((sbus_rx_buf[20]<<9) + (sbus_rx_buf[19]<<1) + (sbus_rx_buf[18]>>7)) & 0x07ff;
sbus.ch[14]=((sbus_rx_buf[21]<<6) + (sbus_rx_buf[20]>>2)) & 0x07ff;
sbus.ch[15]=((sbus_rx_buf[22]<<3) + (sbus_rx_buf[21]>>5)) & 0x07ff;
delay_ms(500); // 先做完延时再开启中断与下一次捕获,否则延时期间中断到来,没有达到预期效果
NVIC_EnableIRQ(USART2_IRQn); // 打开串口中断
sbus_rx_sta = 0; // 准备下一次接收
}
else
{
delay_ms(500); // 免得异常时,到此处使得低优先级任务无法执行
}
}
}
三、PPM信号
PPM(Pulse Position Modulation,脉冲位置调制)信号是一种用于传输控制信号的调制方式,尤其在遥控系统中非常常见。PPM信号通过改变脉冲在时间上的位置来代表不同的信息,在遥控模型、无人机和一些工业控制系统中被广泛应用。
采用串行通信,信号频率只有50Hz,略逊于ibus和sbus,对于一些高精度仪器不可用。
这里提一下,PWM 表示脉冲宽度调制,也就是高电平维持的时间,而PPM 实质上就是将多个通道的PWM放到"一根线"上进行传输,一个完整的PPM信号帧包含了多个通道的PWM值。
PPM 信号一帧数据分为低电平(0.5ms)+高电平(0.5ms-1.5ms)
,高电平长度与PWM占空比成正比。因为一帧信号最多要2ms,信号周期为20ms,所以理论一次PPM信号可以发送10帧数据,但是由于要确定帧头信号,所以要加入同步帧,真正的PPM信号里面最多有9帧数据帧。
解析方式:①外部中断 ②定时器输入捕获
考虑安全条件下,建议使用定时器输入捕获方式进行解码,具体解码参考如下:
c
uint16_t PPM_Sample_Cnt=0;
uint32_t PPM_Time=0;
uint16_t PPM_Okay=0;
uint16_t PPM_Databuf[8]={0}; //PPM信号存储
uint8_t TIM2_CH2_CAPTURE_STA=0;
void TIM2_IRQHandler(void)
{
if(TIM_GetITStatus(TIM2,TIM_IT_CC2)==!RESET)//捕获中断
{
if(TIM2_CH2_CAPTURE_STA&0x01)//符合条件的话说明上次捕获了高电平,那么这次捕获的一定是低电平
{
PPM_Time=TIM_GetCapture2(TIM2);
if(PPM_Time>0)
PPM_Time++;
if(PPM_Okay==1)
{
PPM_Databuf[PPM_Sample_Cnt]=PPM_Time;
PPM_Sample_Cnt++;
if(PPM_Sample_Cnt>8)
PPM_Okay=0;
}
if(PPM_Time>7000)//识别到帧尾
{
PPM_Okay=1;
PPM_Sample_Cnt=0;
}
TIM_OC2PolarityConfig(TIM2,TIM_ICPolarity_Rising);
TIM2_CH2_CAPTURE_STA=0;//清掉标志位准备开始下一次上升沿和下降沿检测
}
else
{
TIM_SetCounter(TIM2,0);//以上为清零
TIM2_CH2_CAPTURE_STA|=0x01;//高电平指示被赋值
TIM_OC1PolarityConfig(TIM2,TIM_ICPolarity_Falling); //当捕获上升沿后改为捕获下降沿
}
}
TIM_ClearITPendingBit(TIM2, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位,一定不要忘,要不然下次进不了中断
}