SBUS协议的串口发送数据和DMA解码数据问题详解

1 前言

在自己学习SBUS协议的过程中到处翻资料,看了很多也走了许多弯路,所以把这些记录下来供大家学习。

2 简介

SUBS协议,全称Serial Bus,即串行通信总线。本质上是一种串口通信协议,采用100K的波特率,8位数据位,2位停止位,偶效验的模式

SBUS协议通常使用在用于接收飞控遥控器的接收器上,如果这样使用,需要在硬件电路中加入反相器,进行硬件电路取反,软件取反是没用的

本实验中我们采用两块stm32f1开发板进行发送与接收,所以并不需要取反电路。

3 协议规则

3.1协议格式

一帧数据发送25个字节
总共发送16个通道的数据,每个通道用11位bit表示,即 11bit*16/8 = 22byte

Byte0Byte1–Byte22Byte23Byte24
0x0FData0x000x00
起始字节数据字节标志位结束字节

标志位:1字节,高四位从高到低依次表示:
bit7:CH17数字通道
bit6:CH16数字通道
bit5:帧丢失(Frame lost)
bit4:安全保护(Failsafe):失控保护激活位(0x10)判断飞机是否失控
bit3~bit0:低四位不用

3.2 协议发送

  • 高速模式:每隔7ms一帧数据,因为两帧的间隔只有超过3ms,才会被接受;而根据波特率计算一下,发送25字节需要的时间+3~4ms=7ms
  • 普通模式:每隔14ms一帧数据;

4 数据编码

4.1 串口发送初始化配置

注意:在stm32中使用校验位,数据字长选择USART_WordLength_9b

void USART1_SBUS_init(u32 bound)
{
	//GPIO端口设置
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);	//使能USART1,GPIOA时钟
	//USART1_TX   GPIOA.9
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//复用推挽输出
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
	
	//USART1_RX	  GPIOA.10初始化
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10  

	//Usart1 NVIC 配置
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 4 ;//抢占优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;		//子优先级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器
	
	//USART 初始化设置
	USART_InitStructure.USART_BaudRate = bound;//串口波特率
	USART_InitStructure.USART_WordLength = USART_WordLength_9b;//字长为9位数据格式
	USART_InitStructure.USART_StopBits = USART_StopBits_2;//两个停止位
	USART_InitStructure.USART_Parity = USART_Parity_Even;//偶校验位
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//收发模式

	USART_Init(USART1, &USART_InitStructure); //初始化串口1
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
	USART_Cmd(USART1, ENABLE);                    //使能串口1 
}

4.2 协议编码

数组SBUS_Data[25]为我们串口发送的数组,数组ChValue[16]为我们具体要发送的每个通道值,最大为11bit,所以赋值在0-2047之间

		SBUS_Data[0] = 0x0F;
		// 16 ChValue of 11 bit data
		SBUS_Data[1]  = (unsigned char) ((ChValue[0] & 0x07FF));
		SBUS_Data[2]  = (unsigned char) ((ChValue[0] & 0x07FF)>>8   | (ChValue[1] & 0x07FF)<<3);
		SBUS_Data[3]  = (unsigned char) ((ChValue[1] & 0x07FF)>>5   | (ChValue[2] & 0x07FF)<<6);
		SBUS_Data[4]  = (unsigned char) ((ChValue[2] & 0x07FF)>>2);
		SBUS_Data[5]  = (unsigned char) ((ChValue[2] & 0x07FF)>>10  | (ChValue[3] & 0x07FF)<<1);
		SBUS_Data[6]  = (unsigned char) ((ChValue[3] & 0x07FF)>>7   | (ChValue[4] & 0x07FF)<<4);
		SBUS_Data[7]  = (unsigned char) ((ChValue[4] & 0x07FF)>>4   | (ChValue[5] & 0x07FF)<<7);
		SBUS_Data[8]  = (unsigned char) ((ChValue[5] & 0x07FF)>>1);
		SBUS_Data[9]  = (unsigned char) ((ChValue[5] & 0x07FF)>>9   | (ChValue[6] & 0x07FF)<<2);
		SBUS_Data[10] = (unsigned char) ((ChValue[6] & 0x07FF)>>6   | (ChValue[7] & 0x07FF)<<5);
		SBUS_Data[11] = (unsigned char) ((ChValue[7] & 0x07FF)>>3);
		SBUS_Data[12] = (unsigned char) ((ChValue[8] & 0x07FF));
		SBUS_Data[13] = (unsigned char) ((ChValue[8] & 0x07FF)>>8   | (ChValue[9] & 0x07FF)<<3);
		SBUS_Data[14] = (unsigned char) ((ChValue[9] & 0x07FF)>>5   | (ChValue[10] & 0x07FF)<<6); 
		SBUS_Data[15] = (unsigned char) ((ChValue[10] & 0x07FF)>>2);
		SBUS_Data[16] = (unsigned char) ((ChValue[10] & 0x07FF)>>10 | (ChValue[11] & 0x07FF)<<1);
		SBUS_Data[17] = (unsigned char) ((ChValue[11] & 0x07FF)>>7  | (ChValue[12] & 0x07FF)<<4);
		SBUS_Data[18] = (unsigned char) ((ChValue[12] & 0x07FF)>>4  | (ChValue[13] & 0x07FF)<<7);
		SBUS_Data[19] = (unsigned char) ((ChValue[13] & 0x07FF)>>1);
		SBUS_Data[20] = (unsigned char) ((ChValue[13] & 0x07FF)>>9  | (ChValue[14] & 0x07FF)<<2);
		SBUS_Data[21] = (unsigned char) ((ChValue[14] & 0x07FF)>>6  | (ChValue[15] & 0x07FF)<<5);
		SBUS_Data[22] = (unsigned char) ((ChValue[15] & 0x07FF)>>3);
		// flags
		SBUS_Data[23] = 0x00;
		// footer
		SBUS_Data[24] = 0X00;
	

5 数据解析

5.1串口接收初始化配置

在数据接受阶段我们采用DMA来读取串口缓冲区的值,使用空闲中断

void usart2_SBUS_init(uint32_t BaudRate)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	DMA_InitTypeDef DMA_InitStructure;
	
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);      //DMA1时钟
	
    USART_InitStructure.USART_BaudRate = BaudRate;
	USART_InitStructure.USART_WordLength = USART_WordLength_9b;
	USART_InitStructure.USART_StopBits = USART_StopBits_2;
	USART_InitStructure.USART_Parity = USART_Parity_Even;
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_Init(USART2,&USART_InitStructure);
    
	//USART2_TX   GPIOA2
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	 //复用推挽输出
	GPIO_Init(GPIOA, &GPIO_InitStructure); 
	//USART2_RX	  GPIOA3
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);

	//USART2 NVIC 
	NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;		
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			
	NVIC_Init(&NVIC_InitStructure);	
	
	USART_Init(USART2, &USART_InitStructure); 
	USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
	USART_ITConfig(USART2, USART_IT_IDLE, ENABLE);
	USART_Cmd(USART2,ENABLE);
	
	//串口2的DMA接收配置
	DMA_DeInit(DMA1_Channel6);//设置DMA通道x为缺省值
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART2->DR;//外设地址
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)Sbus_receive;//内存地址
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//外设作为数据传输的来源
	DMA_InitStructure.DMA_BufferSize = 25;//缓存大小
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//内存地址递增
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设地址不变
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//外设数据宽度8位
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//内存数据宽度8位
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//正常工作模式,只运行一次
	DMA_InitStructure.DMA_Priority = DMA_Priority_High;//高优先级
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//不是内存到内存传输
	DMA_Init(DMA1_Channel6,&DMA_InitStructure);
	DMA_Cmd(DMA1_Channel6, ENABLE);//启动DMA1的第六通道 uart2->rx
	USART_DMACmd(USART2, USART_DMAReq_Rx, ENABLE);//使能串口DMA接受请求
}

5.2 串口中断函数配置

注意,空闲中断需要读取SR和DR寄存器才能清除
单次DMA读取完后,需要重新设置和使能

void USART2_IRQHandler(void)
{
	//IDLE中断:可接受不定长度的字节,一帧数据都接受完后才触发IDLE中断
	uint8_t temp;
	if(USART_GetITStatus(USART2,USART_IT_IDLE)!=RESET)
	{
		//清除IDLE中断标志,先读SR寄存器,再读DR寄存器
		temp = USART2->SR;
		temp = USART2->DR;
		//关闭DMA接收
		DMA_Cmd(DMA1_Channel6,DISABLE);
		DMA_ClearFlag(DMA1_FLAG_TC6);
		//重新设置缓存字节
		DMA_SetCurrDataCounter(DMA1_Channel6,25);
		//使能DMA接收
		DMA_Cmd(DMA1_Channel6,ENABLE);
	}
}

5.3 协议解码

数组Sbus_receive[25]是DMA搬运的数据存储区,数组buffer[25]为mencpy过来的数据,数组channels[16]为我们解码还原的每个通道具体值

		memcpy(buffer,Sbus_receive,sizeof(Sbus_receive));
		channels[0]  = ((buffer[1]    |buffer[2]<<8)                 & 0x07FF);
		channels[1]  = ((buffer[2]>>3 |buffer[3]<<5)                 & 0x07FF);
		channels[2]  = ((buffer[3]>>6 |buffer[4]<<2 |buffer[5]<<10)  & 0x07FF);
		channels[3]  = ((buffer[5]>>1 |buffer[6]<<7)                 & 0x07FF);
		channels[4]  = ((buffer[6]>>4 |buffer[7]<<4)                 & 0x07FF);
		channels[5]  = ((buffer[7]>>7 |buffer[8]<<1 |buffer[9]<<9)   & 0x07FF);
		channels[6]  = ((buffer[9]>>2 |buffer[10]<<6)                & 0x07FF);
		channels[7]  = ((buffer[10]>>5|buffer[11]<<3)                & 0x07FF);
		channels[8]  = ((buffer[12]   |buffer[13]<<8)                & 0x07FF);
		channels[9]  = ((buffer[13]>>3|buffer[14]<<5)                & 0x07FF);
		channels[10] = ((buffer[14]>>6|buffer[15]<<2|buffer[16]<<10) & 0x07FF);
		channels[11] = ((buffer[16]>>1|buffer[17]<<7)                & 0x07FF);
		channels[12] = ((buffer[17]>>4|buffer[18]<<4)                & 0x07FF);
		channels[13] = ((buffer[18]>>7|buffer[19]<<1|buffer[20]<<9)  & 0x07FF);
		channels[14] = ((buffer[20]>>2|buffer[21]<<6)                & 0x07FF);
		channels[15] = ((buffer[21]>>5|buffer[22]<<3)                & 0x07FF);

6 结束语

这就是我在整个SBUS协议学习中遇到的问题和感悟,如果能帮助到的你的话,我很荣幸,如果还有其他问题,欢迎私信,共同学习,共同进步。

  • 13
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Hal Dac dma 是指硬盘管理器 (Hal)、直接访问控制器 (Dac) 和直接内存访问 (dma) 技术。在计算机系统中,硬盘管理器 (Hal) 是负责管理硬盘驱动器的软件或硬件组件,它能够管理硬盘的存储和读写操作。直接访问控制器 (Dac) 是一种允许系统直接与外部设备进行数据传输的硬件接口,它能够控制数据的输入和输出,提高系统的数据传输效率。直接内存访问 (dma) 技术是一种数据传输方式,它允许外部设备直接访问计算机的内存,从而减轻了中央处理器的负担,提高了数据传输速度。 在计算机应用中,Hal Dac dma 技术起到了重要的作用。硬盘管理器 (Hal) 可以管理硬盘的存储和读写操作,保证数据的安全和可靠性。直接访问控制器 (Dac) 能够实现高效的数据传输,提高了外部设备与计算机系统的互联互通速度。直接内存访问 (dma) 技术通过减少中央处理器的负担,提高了数据传输效率和系统的整体性能。 通过 Hal Dac dma 技术,计算机系统可以更高效地处理大量数据传输任务,提高整体的数据处理能力。无论是在个人计算机还是服务器等大规模计算环境中,Hal Dac dma 技术都能够提供卓越的数据传输性能和稳定性,对于提升计算机系统的运行效果具有重要意义。 总之,Hal Dac dma 技术是在计算机系统中应用广泛的技术,它能够提高数据传输速度和效率,保证数据的安全和可靠性,对于提升计算机系统的整体性能起到了关键作用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值