新手談SBUS接收與轉換
阿新 • • 發佈:2018-12-17
SBUS串列埠接收部分
1、硬體輸入一定要取反,別的帖子有取反電路我就不搬了
2、包尾不一定與協議中的一致,我之前就是在糾結為什麼包尾與別人的不同從而懷疑自己是不是沒有收到正確的資料
3、STM32串列埠配置一定要正確
串列埠配置
void USART2_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; //配置IO口 RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; GPIO_Init(GPIOA,&GPIO_InitStructure); //配置外設 USART_InitStructure.USART_BaudRate = 100000; //波特率設定 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //硬體流失能 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收發使能 USART_InitStructure.USART_WordLength = USART_WordLength_9b; //9bit模式 USART_InitStructure.USART_Parity = USART_Parity_Even; //偶校驗 USART_InitStructure.USART_StopBits = USART_StopBits_2; USART_Init(USART2, &USART_InitStructure); USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); //串列埠接收中斷 USART_ITConfig(USART2, USART_IT_IDLE, ENABLE); //串列埠空閒中斷 USART_Cmd(USART2, ENABLE); }
串列埠中斷接收函式
使用前記得給串列埠中斷分配中斷優先順序 串列埠接收的思路就是通過空閒中斷判斷作為一幀結束的標誌,然後0x0F來判斷包頭。如果同時滿足這兩個條件就開始接收資料。這個接收程式實測6208SB,R9DS,R7008SB可已使用。
void USART2_IRQHandler(void) { uint8_t current_byte; //當前接收位元組 uint8_t clear; static uint8_t byte_cnt = 0; //位元組記數 static uint8_t receive_flag = 0; //接收標誌 static uint8_t IDLE_flag = 0; //串列埠空閒標誌 if(USART_GetITStatus(USART2,USART_IT_RXNE ) != RESET ) { current_byte = USART2->DR; if(receive_flag == 0x66) //判斷接收標誌決定是否接收資料 { Sbus_Buff[byte_cnt] = current_byte; byte_cnt++; if(byte_cnt > 25) { Sbus_Buff[24] = 0x00; //重寫包尾 byte_cnt = 0; receive_flag = 0; IDLE_flag = 0; } } else { if((IDLE_flag==1) && (current_byte==0x0F)) { receive_flag =0x66; Sbus_Buff[0] = 0x0F ; byte_cnt = 1; } } } if(USART_GetITStatus(USART2,UART_FLAG_IDLE) != RESET ) { clear = USART2->SR; clear = USART2->DR; IDLE_flag = 1; } }
Sbus_Buff[24]這個位元組包含兩個數字通道以及狀態位,需要用到的的可以看看SBUS協議。目前我只通過0x0C來判斷遙控訊號丟失,其他的還不需要用到。
串列埠資料轉成實際脈寬值
資料拿到後需要先拼湊成每個通道的數值a,得到數值後再轉換成實際脈寬值b。我將實際脈寬乘以2000後與對應數值a進行擬合得到一個y=ax+b的式子,將得到的數值a輸入後即可得到脈寬值b。資料內有測到數值與脈寬的對應值有需要可以自己重新擬合,資料轉換的方法是copy的PIX飛控內的程式。
struct sbus_bit_pick { uint8_t byte; uint8_t rshift; uint8_t mask; uint8_t lshift; }; static const struct sbus_bit_pick sbus_decoder[SBUS_INPUT_CHANNELS][3] = { // /* 0 */ { { 0, 0, 0xff, 0}, { 1, 0, 0x07, 8}, { 0, 0, 0x00, 0} }, /* 1 */ { { 1, 3, 0x1f, 0}, { 2, 0, 0x3f, 5}, { 0, 0, 0x00, 0} }, /* 2 */ { { 2, 6, 0x03, 0}, { 3, 0, 0xff, 2}, { 4, 0, 0x01, 10} }, /* 3 */ { { 4, 1, 0x7f, 0}, { 5, 0, 0x0f, 7}, { 0, 0, 0x00, 0} }, /* 4 */ { { 5, 4, 0x0f, 0}, { 6, 0, 0x7f, 4}, { 0, 0, 0x00, 0} }, /* 5 */ { { 6, 7, 0x01, 0}, { 7, 0, 0xff, 1}, { 8, 0, 0x03, 9} }, /* 6 */ { { 8, 2, 0x3f, 0}, { 9, 0, 0x1f, 6}, { 0, 0, 0x00, 0} }, /* 7 */ { { 9, 5, 0x07, 0}, {10, 0, 0xff, 3}, { 0, 0, 0x00, 0} }, /* 8 */ { {11, 0, 0xff, 0}, {12, 0, 0x07, 8}, { 0, 0, 0x00, 0} }, /* 9 */ { {12, 3, 0x1f, 0}, {13, 0, 0x3f, 5}, { 0, 0, 0x00, 0} }, /* 10 */ { {13, 6, 0x03, 0}, {14, 0, 0xff, 2}, {15, 0, 0x01, 10} }, /* 11 */ { {15, 1, 0x7f, 0}, {16, 0, 0x0f, 7}, { 0, 0, 0x00, 0} }, /* 12 */ { {16, 4, 0x0f, 0}, {17, 0, 0x7f, 4}, { 0, 0, 0x00, 0} }, /* 13 */ { {17, 7, 0x01, 0}, {18, 0, 0xff, 1}, {19, 0, 0x03, 9} }, /* 14 */ { {19, 2, 0x3f, 0}, {20, 0, 0x1f, 6}, { 0, 0, 0x00, 0} }, /* 15 */ { {20, 5, 0x07, 0}, {21, 0, 0xff, 3}, { 0, 0, 0x00, 0} } }; void Conversion_PWM() { unsigned char channel,pick; for (channel = 0; channel < SBUS_INPUT_CHANNELS; channel++) //迴圈次數 { unsigned value = 0; for (pick = 0; pick < 3; pick++) { const struct sbus_bit_pick *decode = &sbus_decoder[channel][pick]; if (decode->mask != 0) { unsigned piece = Sbus_Buff[1 + decode->byte]; piece >>= decode->rshift; piece &= decode->mask; piece <<= decode->lshift; value |= piece; } } Sbus_PWM[channel] = (uint16_t)(value * 1.2504 + 1761.1); } }
Sbus_PWM[]陣列內的值除以2000就能得到實際的1.5ms左右的實際脈寬值。 程式碼是經過測試的將檔案新增進工程就能夠直接使用,中斷優先順序需要自己配置。 程式碼及擬合資料下載地址:https://download.csdn.net/download/mr_tang199/10739630