| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # SBUS èªæ¨¡é¥æ§å¨æ¥æ¶æ¨¡å使ç¨è¯´æ |
| | | |
| | | ## æ¦è¿° |
| | | æ¬æ¨¡åå®ç°äº SBUS åè®®ï¼Futaba SBUSï¼çè§£æï¼ç¨äºæ¥æ¶èªæ¨¡é¥æ§å¨ä¿¡å·ã |
| | | |
| | | ### 硬件é
ç½® |
| | | - **UART**: UART4 (STM32H743) |
| | | - **æ³¢ç¹ç**: 100kbps |
| | | - **æ ¼å¼**: 8E2 (8 æ°æ®ä½, å¶æ ¡éª, 2 忢ä½) |
| | | - **æ¥æ¶æ¨¡å¼**: DMA å¾ªç¯æ¥æ¶ |
| | | - **ä¿¡å·åå**: å·²å¯ç¨ RX åååè½ï¼éé
SBUS ååé»è¾ï¼ |
| | | |
| | | ### åè®®è§æ ¼ |
| | | - **帧é¿åº¦**: 25 åè |
| | | - **帧头**: 0x0F |
| | | - **帧尾**: 0x00 |
| | | - **ééæ°**: 16 个模æéé (11-bit, 0~2047) + 2 个æ°åéé (CH17/CH18) |
| | | - **æ å¿ä½**: Frame LostãFailsafe |
| | | - **帧ç**: 约 7~14 ms/帧 (åå³äºåå°æº) |
| | | |
| | | --- |
| | | |
| | | ## API æ¥å£ |
| | | |
| | | ### 1. åå§å |
| | | ```c |
| | | #include "SBUS.h" |
| | | |
| | | void app_task(void *pvParameters) |
| | | { |
| | | // å¨ä»»å¡åå§åé¶æ®µè°ç¨ï¼å·²éæå° app.cï¼ |
| | | SBUS_Init(); |
| | | |
| | | while(1) |
| | | { |
| | | // 主循ç¯è½®è¯¢ |
| | | SBUS_Poll(); |
| | | // ... |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | ### 2. è·å宿´ééæ°æ® |
| | | ```c |
| | | ST_SBUSData sbusData; |
| | | |
| | | if (SBUS_GetData(&sbusData) == HIDO_TRUE) |
| | | { |
| | | // æåè·åæ°æ®ï¼ä¿¡å·ææï¼ |
| | | printf("CH0: %u, CH1: %u, CH2: %u\n", |
| | | sbusData.m_au16Channels[0], |
| | | sbusData.m_au16Channels[1], |
| | | sbusData.m_au16Channels[2]); |
| | | |
| | | // æ£æ¥å¤±æ§ä¿æ¤æ å¿ |
| | | if (sbusData.m_u8Failsafe) |
| | | { |
| | | printf("WARNING: Failsafe activated!\n"); |
| | | } |
| | | |
| | | // æ£æ¥å¸§ä¸¢å¤±æ å¿ |
| | | if (sbusData.m_u8FrameLost) |
| | | { |
| | | printf("WARNING: Frame lost!\n"); |
| | | } |
| | | |
| | | // æ°åéé |
| | | printf("CH17: %u, CH18: %u\n", sbusData.m_u8Ch17, sbusData.m_u8Ch18); |
| | | } |
| | | else |
| | | { |
| | | // ä¿¡å·è¶
æ¶ææ æ°æ® |
| | | printf("SBUS signal lost\n"); |
| | | } |
| | | ``` |
| | | |
| | | ### 3. è·åå个ééå¼ |
| | | ```c |
| | | // è·åæ²¹é¨éé (å设 CH2 为油é¨) |
| | | HIDO_UINT16 throttle = SBUS_GetChannel(2); // 0~2047 |
| | | |
| | | // å½ä¸åå° [-1.0, 1.0] |
| | | float throttle_normalized = (throttle - 1024.0f) / 1024.0f; |
| | | |
| | | // æ å°å°çµæºé度 (0~100%) |
| | | uint8_t motor_speed = (throttle * 100) / 2047; |
| | | ``` |
| | | |
| | | ### 4. è·åæ°åéé |
| | | ```c |
| | | // è·åå¼å
³ç¶æ (CH17 é常对åºé¥æ§å¨å¼å
³) |
| | | HIDO_UINT8 switch_state = SBUS_GetDigitalChannel(17); // 0 æ 1 |
| | | |
| | | if (switch_state == 1) |
| | | { |
| | | printf("Switch ON\n"); |
| | | } |
| | | else |
| | | { |
| | | printf("Switch OFF\n"); |
| | | } |
| | | ``` |
| | | |
| | | ### 5. æ£æ¥ä¿¡å·æææ§ |
| | | ```c |
| | | // æ£æ¥æè¿ 100ms å
æ¯å¦æ¶å° SBUS 帧 |
| | | if (SBUS_IsSignalValid(100) == HIDO_TRUE) |
| | | { |
| | | printf("SBUS signal OK\n"); |
| | | } |
| | | else |
| | | { |
| | | printf("SBUS signal LOST (timeout)\n"); |
| | | // è¿å
¥å¤±æ§ä¿æ¤æ¨¡å¼ |
| | | // ... |
| | | } |
| | | ``` |
| | | |
| | | ### 6. è·åç»è®¡ä¿¡æ¯ |
| | | ```c |
| | | HIDO_UINT32 frameCount, errorCount; |
| | | |
| | | SBUS_GetStats(&frameCount, &errorCount); |
| | | |
| | | printf("Total frames: %u, Errors: %u (%.2f%%)\n", |
| | | frameCount, errorCount, |
| | | (frameCount > 0) ? (errorCount * 100.0f / frameCount) : 0.0f); |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## æ°æ®ç»æè¯¦è§£ |
| | | |
| | | ### ST_SBUSData |
| | | ```c |
| | | typedef struct |
| | | { |
| | | HIDO_UINT16 m_au16Channels[16]; // 16 个模æéé, èå´ 0~2047 |
| | | HIDO_UINT8 m_u8Ch17; // æ°åéé 17, å¼ 0 æ 1 |
| | | HIDO_UINT8 m_u8Ch18; // æ°åéé 18, å¼ 0 æ 1 |
| | | HIDO_UINT8 m_u8FrameLost; // 帧丢失æ å¿ (åå°æºæ è®°) |
| | | HIDO_UINT8 m_u8Failsafe; // 失æ§ä¿æ¤æ å¿ (æ¥æ¶æºæ è®°) |
| | | HIDO_UINT32 m_u32LastUpdateTick; // æåæ´æ°æ¶å» (HAL_GetTick) |
| | | HIDO_UINT32 m_u32FrameCount; // ç´¯è®¡æ¥æ¶å¸§æ° |
| | | HIDO_UINT32 m_u32ErrorCount; // 累计éè¯¯å¸§æ° (帧头/å¸§å°¾æ ¡éªå¤±è´¥) |
| | | } ST_SBUSData; |
| | | ``` |
| | | |
| | | ### ééæ å°ç¤ºä¾ï¼å¸¸è§é¥æ§å¨å¸å±ï¼ |
| | | | éé | 常è§åè½ | å¼èå´ | ä¸å¿å¼ | |
| | | |------|----------|--------|--------| |
| | | | CH0 | æ¨ªæ» (Aileron) | 0~2047 | 1024 | |
| | | | CH1 | 俯仰 (Elevator) | 0~2047 | 1024 | |
| | | | CH2 | æ²¹é¨ (Throttle) | 0~2047 | 172 (æä½) | |
| | | | CH3 | æ¹å (Rudder) | 0~2047 | 1024 | |
| | | | CH4 | è¾
å©å¼å
³ 1 | 0~2047 | - | |
| | | | CH5 | è¾
å©å¼å
³ 2 | 0~2047 | - | |
| | | | ... | å
¶ä»èªå®ä¹ | 0~2047 | - | |
| | | | CH17 | æ°åå¼å
³ 1 | 0/1 | - | |
| | | | CH18 | æ°åå¼å
³ 2 | 0/1 | - | |
| | | |
| | | --- |
| | | |
| | | ## å
¸ååºç¨åºæ¯ |
| | | |
| | | ### åºæ¯1ï¼æå¨é¥æ§èåªæº |
| | | ```c |
| | | void Manual_Control_Task(void) |
| | | { |
| | | ST_SBUSData sbus; |
| | | |
| | | if (SBUS_GetData(&sbus) != HIDO_TRUE) |
| | | { |
| | | // ä¿¡å·ä¸¢å¤±ï¼åæ¢çµæº |
| | | Motor_Stop(); |
| | | return; |
| | | } |
| | | |
| | | // 失æ§ä¿æ¤è§¦åï¼åæ¢ |
| | | if (sbus.m_u8Failsafe) |
| | | { |
| | | Motor_Stop(); |
| | | return; |
| | | } |
| | | |
| | | // æåæ§å¶é (å设 CH1=åè¿/åé, CH3=å·¦å³è½¬å) |
| | | int16_t forward = (int16_t)sbus.m_au16Channels[1] - 1024; // -1024~+1023 |
| | | int16_t turn = (int16_t)sbus.m_au16Channels[3] - 1024; // -1024~+1023 |
| | | |
| | | // å½ä¸åå° [-100, 100] |
| | | int8_t forward_cmd = (forward * 100) / 1024; |
| | | int8_t turn_cmd = (turn * 100) / 1024; |
| | | |
| | | // åéå°å·®éçµæºæ§å¶å¨ |
| | | DiffDrive_SetCommand(forward_cmd, turn_cmd); |
| | | } |
| | | ``` |
| | | |
| | | ### åºæ¯2ï¼æ¨¡å¼åæ¢ï¼èªå¨/æå¨ï¼ |
| | | ```c |
| | | void Mode_Switch_Task(void) |
| | | { |
| | | static uint8_t last_switch = 0; |
| | | uint8_t current_switch = SBUS_GetDigitalChannel(17); |
| | | |
| | | // æ£æµå¼å
³ç¶æåå |
| | | if (current_switch != last_switch) |
| | | { |
| | | if (current_switch == 1) |
| | | { |
| | | // 忢å°èªå¨æ¨¡å¼ |
| | | System_SetMode(MODE_AUTO); |
| | | printf("Switched to AUTO mode\n"); |
| | | } |
| | | else |
| | | { |
| | | // 忢尿卿¨¡å¼ |
| | | System_SetMode(MODE_MANUAL); |
| | | printf("Switched to MANUAL mode\n"); |
| | | } |
| | | last_switch = current_switch; |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | ### åºæ¯3ï¼å¤±æ§ä¿æ¤çæ§ |
| | | ```c |
| | | void Failsafe_Monitor_Task(void) |
| | | { |
| | | static uint32_t last_valid_tick = 0; |
| | | |
| | | if (SBUS_IsSignalValid(200) == HIDO_TRUE) |
| | | { |
| | | last_valid_tick = HAL_GetTick(); |
| | | } |
| | | else |
| | | { |
| | | uint32_t lost_duration = HAL_GetTick() - last_valid_tick; |
| | | |
| | | if (lost_duration > 1000) // ä¿¡å·ä¸¢å¤±è¶
è¿ 1 ç§ |
| | | { |
| | | // 触åç´§æ¥åæ¢ |
| | | Emergency_Stop(); |
| | | printf("EMERGENCY: SBUS signal lost for %u ms\n", lost_duration); |
| | | } |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## è°è¯ä¸è¯æ |
| | | |
| | | ### æå°å®æ¶ééå¼ |
| | | ```c |
| | | void SBUS_Debug_Print(void) |
| | | { |
| | | ST_SBUSData sbus; |
| | | |
| | | if (SBUS_GetData(&sbus) == HIDO_TRUE) |
| | | { |
| | | printf("SBUS Channels:\n"); |
| | | for (uint8_t i = 0; i < 16; i++) |
| | | { |
| | | printf(" CH%02u: %4u", i, sbus.m_au16Channels[i]); |
| | | if ((i + 1) % 4 == 0) printf("\n"); |
| | | } |
| | | printf(" CH17: %u, CH18: %u\n", sbus.m_u8Ch17, sbus.m_u8Ch18); |
| | | printf(" FrameLost: %u, Failsafe: %u\n", sbus.m_u8FrameLost, sbus.m_u8Failsafe); |
| | | printf(" Stats: Frames=%u, Errors=%u\n", sbus.m_u32FrameCount, sbus.m_u32ErrorCount); |
| | | } |
| | | else |
| | | { |
| | | printf("SBUS: No signal\n"); |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | ### ä¿¡å·è´¨éæ£æµ |
| | | ```c |
| | | void SBUS_Quality_Check(void) |
| | | { |
| | | HIDO_UINT32 frames, errors; |
| | | SBUS_GetStats(&frames, &errors); |
| | | |
| | | if (frames > 1000) // ç´¯ç§¯è¶³å¤æ ·æ¬ |
| | | { |
| | | float error_rate = (errors * 100.0f) / frames; |
| | | |
| | | if (error_rate > 5.0f) |
| | | { |
| | | printf("WARNING: High SBUS error rate: %.2f%%\n", error_rate); |
| | | printf(" -> Check cable connection\n"); |
| | | printf(" -> Check RX pin inversion setting\n"); |
| | | } |
| | | else |
| | | { |
| | | printf("SBUS quality: Good (error rate: %.2f%%)\n", error_rate); |
| | | } |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## 注æäºé¡¹ |
| | | |
| | | ### 1. ä¿¡å·åå |
| | | - SBUS å议使ç¨ååé»è¾ï¼idle high = 0V, idle low = 3.3Vï¼ |
| | | - STM32H7 UART4 å·²å¯ç¨ `UART_ADVFEATURE_RXINV_ENABLE` |
| | | - 妿æ¶å°çæ°æ®å
¨æ¯éè¯¯å¸§ï¼æ£æ¥æ¯å¦éè¦ç¦ç¨åååè½ |
| | | |
| | | ### 2. æ³¢ç¹ç容差 |
| | | - SBUS æ åæ³¢ç¹ç为 100kbps |
| | | - STM32 å
鍿¶éè¯¯å·®éæ§å¶å¨ ±2% 以å
|
| | | - 妿叧é误çé«ï¼å¯å¾®è°æ³¢ç¹ç (99000 æ 101000) |
| | | |
| | | ### 3. DMA ç¼å²åºå¤§å° |
| | | - å½å设置为 128 åèï¼å¯å®¹çº³ 5 å¸§ï¼ |
| | | - å¦æä¸»å¾ªç¯è½®è¯¢é´éè¾é¿ï¼>50msï¼ï¼å»ºè®®å¢å¤§ç¼å²åº |
| | | |
| | | ### 4. 帧çä¸å»¶è¿ |
| | | - SBUS 帧é´é约 7~14ms |
| | | - `SBUS_Poll()` åºè³å°ä»¥ 20Hz é¢çè°ç¨ï¼å½å app_task 约 74Hzï¼ |
| | | - æ§å¶å»¶è¿: éæ ·å»¶è¿ + è§£æå»¶è¿ < 20ms |
| | | |
| | | ### 5. 失æ§ä¿æ¤çç¥ |
| | | - å»ºè®®åæ¶çæ§ `m_u8Failsafe` æ å¿å `SBUS_IsSignalValid()` è¶
æ¶ |
| | | - å¤±æ§æ¶ç«å³åæ¢ææè¿å¨è¾åº |
| | | - æ¢å¤ä¿¡å·åéæå¨éæ°ä½¿è½ï¼é²æ¢æå¤å¯å¨ï¼ |
| | | |
| | | ### 6. å¤çº¿ç¨å®å
¨ |
| | | - å½åå®ç°æªä½¿ç¨ä¸´çåºä¿æ¤ |
| | | - å¦éå¨ ISR æå
¶ä»é«ä¼å
级任å¡ä¸è®¿é®ï¼éæ·»å äºæ¥é |
| | | |
| | | --- |
| | | |
| | | ## æ
éææ¥ |
| | | |
| | | | é®é¢ | å¯è½åå | è§£å³æ¹æ¡ | |
| | | |------|----------|----------| |
| | | | æ æ³æ¥æ¶æ°æ® | UART4 æªæ£ç¡®åå§å | æ£æ¥ `MX_UART4_Init()` è°ç¨é¡ºåº | |
| | | | 帧é误ç 100% | RX ä¿¡å·åå设置é误 | 忢 `UART_ADVFEATURE_RXINV` ç¶æ | |
| | | | ä¿¡å·è¶
æ¶ | åå°æºæªå¼å¯æè·ç¦»è¿è¿ | æ£æ¥é¥æ§å¨çµæºå天线 | |
| | | | ééå¼æå¨ | å¹²æ°ææ¥è§¦ä¸è¯ | æ£æ¥æ¥çº¿å±è½åæ¥å° | |
| | | | é¨åééæ æ | åå°æºééæªæ¿æ´» | æ£æ¥åå°æºééé
ç½® | |
| | | | DMA æªè§¦å | DMA åå§åæªå®æ | 确认 `HAL_UART_Receive_DMA()` å·²è°ç¨ | |
| | | |
| | | --- |
| | | |
| | | ## æ¥çº¿åè |
| | | |
| | | ### æ å SBUS æ¥å£ï¼3 çº¿ï¼ |
| | | ``` |
| | | SBUS 模å STM32H743 |
| | | --------- ---------- |
| | | GND -------> GND |
| | | 5V -------> 5V (æ VCC) |
| | | SBUS -------> UART4_RX (PA1 ææ ¹æ®å®é
å¼è) |
| | | ``` |
| | | |
| | | ### 注æ |
| | | - SBUS ä¿¡å·çµå¹³ä¸º 3.3V TTLï¼é¨å模åéè¦çµå¹³è½¬æ¢ï¼ |
| | | - ç¡®ä¿ SBUS è¾åºç«¯ä¸ STM32 RX å¼èç´è¿ï¼æ é䏿/䏿çµé»ï¼ |
| | | |
| | | --- |
| | | |
| | | ## æ©å±åè½å»ºè®® |
| | | |
| | | ### 1. ä¸ä½æ ¡å |
| | | ```c |
| | | void SBUS_CalibrateCenter(uint8_t channel) |
| | | { |
| | | uint16_t center_value = SBUS_GetChannel(channel); |
| | | // ä¿åå° Flash æé
ç½®æä»¶ |
| | | Config_SetChannelCenter(channel, center_value); |
| | | } |
| | | ``` |
| | | |
| | | ### 2. ééæ å°éå®ä¹ |
| | | ```c |
| | | typedef enum { |
| | | RC_AILERON = 0, |
| | | RC_ELEVATOR = 1, |
| | | RC_THROTTLE = 2, |
| | | RC_RUDDER = 3, |
| | | RC_SWITCH_A = 4, |
| | | RC_SWITCH_B = 5, |
| | | } E_RCChannel; |
| | | |
| | | uint16_t RC_GetMappedChannel(E_RCChannel ch) |
| | | { |
| | | return SBUS_GetChannel(g_channel_map[ch]); |
| | | } |
| | | ``` |
| | | |
| | | ### 3. ææ°æ²çº¿è°æ´ |
| | | ```c |
| | | int16_t RC_ApplyExpo(int16_t raw_value, float expo_factor) |
| | | { |
| | | float normalized = raw_value / 1024.0f; // -1.0 ~ 1.0 |
| | | float expo = expo_factor * normalized * normalized * normalized + |
| | | (1 - expo_factor) * normalized; |
| | | return (int16_t)(expo * 1024.0f); |
| | | } |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | çææ¶é´ï¼2025-11-13 |
| | | 模åçæ¬ï¼v1.0 |
| | | ç»´æ¤è
ï¼é¡¹ç®å¢é |