使用STM32 串口空闲中断 加DMA 解析SBUS协议数据
硬件接线
*当前使用单片机的型号是stm32g031g6u6,接线简单粗暴,直连串口就行,但中间最好串联一个100R的电阻,以防万一
*目前使用PA3口接收数据,也可以自行扩展到其他串口
*ST G0系列的单片机串口有内置的电平取反功能,因此不需要外接反向电路
SBUS协议的数据格式
startbyte | data1 | data2 | …… | …… | data22 | data23 | flags | endbyte |
---|---|---|---|---|---|---|---|---|
0x0f | / | / | / | / | / | / | / | 0x00 |
从data1 至 data23 ,需要对其数据进行拆分拼接才能得到通道数据
处理流程
- 根据协议,在高速模式下,两包数据间隔7ms ,100k波特率传输25字节 大约2.5ms,可以使用空闲中断+dma配合接收数据
- 收到数据后直接将数据丢入解析函数,就可以得到相应的通道值数据
串口初始化.c文件
Uart2.c文件
#define SYSCLK 64000000
#define PCLK 64000000
#define USART2_TX_SHIFT 2
#define USART2_RX_SHIFT 3
#define USART2_BAUD_RATE 100000 //sbus波特率为100k
//创建dma存储数据的buf
#define USART2_DMARX_BUFFER_SIZE 100
uint8_t Uart2_DRx_Buff[USART2_DMARX_BUFFER_SIZE];
#define USART2_RECEIVE_BUFFER_SIZE 40
#pragma pack(push, 1)
typedef union {
uint8_t uart2RxBuff[USART2_RECEIVE_BUFFER_SIZE];
tSbuspacket sBus;
}uBusPack;
uBusPack RxData = {0};
#pragma pack(pop)
这里使用uart2 空闲中断 + DMA接收 SBUS数据
如果不需要 此串口 发送数据的话,可以不用初始化PA2引脚
注意配置波特率,奇偶校验位和引脚电平翻转
void Usart2Init(void)
{
//配置io口
RCC->IOPENR |= RCC_IOPENR_GPIOAEN;
GPIOA->MODER &=~ ((3<<(2*USART2_TX_SHIFT)) | (3<<(2*USART2_RX_SHIFT)));
GPIOA->MODER |= ((2<<(2*USART2_TX_SHIFT)) | (2<<(2*USART2_RX_SHIFT)));
GPIOA->PUPDR &=~ ((3<<(2*USART2_TX_SHIFT)) | (3<<(2*USART2_RX_SHIFT)));
GPIOA->PUPDR |= ((1<<(2*USART2_TX_SHIFT)) | (1<<(2*USART2_RX_SHIFT)));
GPIOA->AFR[0] &= ~((0x0fU << (USART2_TX_SHIFT%8)*4) | (0x0fU << (USART2_RX_SHIFT%8)*4));
GPIOA->AFR[0] |= ((0x01U << (USART2_TX_SHIFT%8)*4) | (0x01U << (USART2_RX_SHIFT%8)*4));
//配置串口
RCC->APBENR1 |= RCC_APBENR1_USART2EN;
RCC->APBRSTR1|= RCC_APBRSTR1_USART2RST;
RCC->APBRSTR1&=~RCC_APBRSTR1_USART2RST;
USART2->CR1 &= ~USART_CR1_UE;
USART2->CR1 |= USART_CR1_TE | USART_CR1_RE;
USART2->BRR = ((PCLK/(USART2_BAUD_RATE*16))<<4) | ((PCLK%USART2_BAUD_RATE+USART2_BAUD_RATE/2)/USART2_BAUD_RATE);
USART2->CR1 |= USART_CR1_M0 | USART_CR1_PCE;
USART2->CR2 |= USART_CR2_STOP_1;
//配置 DMA
RCC->AHBENR |= RCC_AHBENR_DMA1EN;
DMAMUX1_Channel3[0].CCR=(unsigned long)52;
//DMA_CCR_MSIZE_0 01 memsize HalfWord
//DMA_CCR_MSIZE_1 10 memsize Word
//将DMA_CCR_MSIZE清0 将DMA_CCR_PSIZE清0
// DMA_CCR_MSIZE->00->BYTE DMA_CCR_PSIZE-> 00->BYTE
DMA1_Channel4->CCR &= ~(DMA_CCR_MSIZE | DMA_CCR_PSIZE); //8bit
//DMA_CCR_MINC mem地址自增
//DMA_CCR_CIRC 循环模式
DMA1_Channel4->CCR |= DMA_CCR_PL_1|DMA_CCR_MINC|DMA_CCR_CIRC;
DMA1_Channel4->CNDTR = USART2_DMARX_BUFFER_SIZE;
//CPAR -> CMAR
DMA1_Channel4->CMAR = (uint32_t)Uart2_DRx_Buff; //地址
DMA1_Channel4->CPAR = (uint32_t)&USART2->RDR;
DMA1_Channel4->CCR |= DMA_CCR_EN;
//usart2启用dma
USART2->CR3 |= USART_CR3_DMAR;
//引脚电平翻转
USART2->CR2 |= USART_CR2_RXINV;
//使能USART2空闲中断
USART2->CR1 |= USART_CR1_IDLEIE;
NVIC_SetPriority(USART2_IRQn, 2);
NVIC_EnableIRQ(USART2_IRQn);
//enable
USART2->CR1 |= USART_CR1_UE;
}
串口中断里的数据接收
void Usart2ReceiveData(){
unsigned short DmaRxHeadp = 0;
static unsigned short DmaRxTailp = 0;
DmaRxHeadp = USART2_DMARX_BUFFER_SIZE - \
((((DMA_Channel_TypeDef *)((uint32_t)DMA1 + CHANNEL_OFFSET_TAB[LL_DMA_CHANNEL_4]))->CNDTR)&DMA_CNDTR_NDT);
if (DmaRxHeadp >= DmaRxTailp) {
uint16_t size0 = DmaRxHeadp - DmaRxTailp;
memcpy(RxData.uart2RxBuff ,Uart2_DRx_Buff + DmaRxTailp ,size0);
U2RxDataLength = size0;
} else {
uint16_t size1 = USART2_DMARX_BUFFER_SIZE - DmaRxTailp;
memcpy(RxData.uart2RxBuff ,Uart2_DRx_Buff + DmaRxTailp ,size1);
memcpy(RxData.uart2RxBuff + size1 ,Uart2_DRx_Buff , DmaRxHeadp);
U2RxDataLength = size1 + DmaRxHeadp;
}
DmaRxTailp = DmaRxHeadp;
SbusAlreadyInstall = SerialBusAnalysis(RxData.uart2RxBuff);
}
串口2中断函数
void USART2_IRQHandler(void){
if( !!READ_BIT(USART2->ISR, USART_ISR_IDLE) ){
// 清除空闲中断标志
USART2->ICR = USART_ICR_IDLECF;
Usart2ReceiveData();
}
}
信号解析函数
sbus.h文件
// SBUS 数据包
typedef struct {
uint8_t start; // 起始字节 0x0F
uint8_t data[22]; // 16 通道的 11 位数据(共 22 字节)
uint8_t flags; // 标志位
uint8_t end; // 结束字节 0x00
} tSbuspacket;
//解析后的数据
typedef __packed struct {
uint16_t channels[16];
unsigned char frame_lost;
unsigned char failsafe;
}tSbusData;
sbus.c文件
unsigned char SerialBusAnalysis(const uint8_t* raw_data) {
uint8_t bits_remaining = 0;
uint8_t byte_index = 0;
uint8_t bit_index = 0;
const tSbuspacket* packet = (const tSbuspacket*)raw_data;
if (packet->start != 0x0F || packet->end != 0x00) {
return FALSE; // 无效数据包
}
for (int ch = 0; ch < 16; ch++) {
Thezg.Signal.Sbus.channels[ch] = 0;
if (bit_index < 8) {
Thezg.Signal.Sbus.channels[ch] = packet->data[byte_index] >> bit_index;
}
bits_remaining = 11 - (8 - bit_index);
if (bits_remaining > 0) {
Thezg.Signal.Sbus.channels[ch] |= (packet->data[byte_index + 1] << (8 - bit_index));
}
if (bits_remaining > 8) {
Thezg.Signal.Sbus.channels[ch] |= (packet->data[byte_index + 2] << (16 - bit_index));
}
Thezg.Signal.Sbus.channels[ch] &= 0x07FF;
bit_index += 11;
while (bit_index >= 8) {
bit_index -= 8;
byte_index++;
}
}
// 标志位具体根据实际情况而定
uint8_t sig_lost_flg = (packet->flags & 0x04) != 0; // 第x位为帧丢失标志
uint8_t sig_failsafe_flg = (packet->flags & 0x08) != 0; // 第x位为故障保护标志
return TRUE;
}