yincheng.zhong
8 天以前 b9aca884f88abdda860d5c62be841a9e21ce68a1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
/*******************************************************************************
 * File Name         : SBUS.c
 * Description       : SBUS RC receiver protocol parser implementation
 * Created on        : 2025-11-13
 * Author            : Auto-generated
 *******************************************************************************/
 
/*******************************************************************************
 *                              Include Files                                  *
 *******************************************************************************/
#include "SBUS.h"
#include "Uart.h"
#include "stm32h7xx_hal.h"
#include "stm32h7xx_hal_uart.h"
#include "stm32h7xx_hal_dma.h"
#include <string.h>
#include "pwm_ctrol.h"
 
/*******************************************************************************
 *                                  Macro                                      *
 *******************************************************************************/
#define SBUS_TIMEOUT_MS 100
 
// 调试开关:设置为 1 启用调试输出(需要 printf 重定向),设置为 0 禁用
#define SBUS_DEBUG_ENABLE           0
 
#if SBUS_DEBUG_ENABLE
    #define SBUS_DEBUG_PRINT(...)   printf(__VA_ARGS__)
#else
    #define SBUS_DEBUG_PRINT(...)   ((void)0)
#endif
 
/*******************************************************************************
 *                             Global Variables                                *
 *******************************************************************************/
// SBUS 接收缓冲区 (DMA 写入)
static HIDO_UINT8 g_au8SBUSRxBuf[SBUS_UART_RX_BUF_SIZE] = {0};
 
// SBUS 当前通道数据
static ST_SBUSData g_stSBUSData = {0};
 
// 临时解析缓冲区 (用于帧边界对齐)
static HIDO_UINT8 g_au8ParseBuf[SBUS_FRAME_SIZE] = {0};
static HIDO_UINT8 g_u8ParseBufIdx = 0;
 
// 调试统计
static HIDO_UINT32 g_u32DebugPollCount = 0;
static HIDO_UINT32 g_u32DebugLastDmaRemaining = 0;
// SBUS failsafe active flag
static volatile HIDO_UINT8 g_bSBUSFailsafeActive = 0;
 
/*******************************************************************************
 *                             Local Function                                  *
 *******************************************************************************/
 
/**
 * @brief 解析单个 SBUS 帧 (25 字节)
 * @param _pu8Frame: 帧数据指针 (必须指向完整的 25 字节)
 * @return HIDO_TRUE: 解析成功, HIDO_FALSE: 校验失败
 */
static HIDO_INT32 SBUS_ParseFrame(const HIDO_UINT8 *_pu8Frame)
{
    // 1. 校验帧头和帧尾
    if (_pu8Frame[0] != SBUS_HEADER || _pu8Frame[24] != SBUS_FOOTER)
    {
        g_stSBUSData.m_u32ErrorCount++;
        return HIDO_FALSE;
    }
 
    // 2. 提取 16 个 11-bit 通道 (bit-packed in bytes 1~22)
    // SBUS 协议: 176 bits (16 channels * 11 bits) 存储在 22 字节中
    // Channel layout (LSB first):
    // Byte 1:   Ch0[0:7]
    // Byte 2:   Ch0[8:10] | Ch1[0:4]
    // Byte 3:   Ch1[5:10] | Ch2[0:1]
    // ... (依次类推)
    
    g_stSBUSData.m_au16Channels[0]  = (HIDO_UINT16)((_pu8Frame[1]      | _pu8Frame[2] << 8)                    & 0x07FF);
    g_stSBUSData.m_au16Channels[1]  = (HIDO_UINT16)((_pu8Frame[2] >> 3 | _pu8Frame[3] << 5)                    & 0x07FF);
    g_stSBUSData.m_au16Channels[2]  = (HIDO_UINT16)((_pu8Frame[3] >> 6 | _pu8Frame[4] << 2 | _pu8Frame[5] << 10) & 0x07FF);
    g_stSBUSData.m_au16Channels[3]  = (HIDO_UINT16)((_pu8Frame[5] >> 1 | _pu8Frame[6] << 7)                    & 0x07FF);
    g_stSBUSData.m_au16Channels[4]  = (HIDO_UINT16)((_pu8Frame[6] >> 4 | _pu8Frame[7] << 4)                    & 0x07FF);
    g_stSBUSData.m_au16Channels[5]  = (HIDO_UINT16)((_pu8Frame[7] >> 7 | _pu8Frame[8] << 1 | _pu8Frame[9] << 9)  & 0x07FF);
    g_stSBUSData.m_au16Channels[6]  = (HIDO_UINT16)((_pu8Frame[9] >> 2 | _pu8Frame[10] << 6)                   & 0x07FF);
    g_stSBUSData.m_au16Channels[7]  = (HIDO_UINT16)((_pu8Frame[10] >> 5 | _pu8Frame[11] << 3)                  & 0x07FF);
    g_stSBUSData.m_au16Channels[8]  = (HIDO_UINT16)((_pu8Frame[12]      | _pu8Frame[13] << 8)                  & 0x07FF);
    g_stSBUSData.m_au16Channels[9]  = (HIDO_UINT16)((_pu8Frame[13] >> 3 | _pu8Frame[14] << 5)                  & 0x07FF);
    g_stSBUSData.m_au16Channels[10] = (HIDO_UINT16)((_pu8Frame[14] >> 6 | _pu8Frame[15] << 2 | _pu8Frame[16] << 10) & 0x07FF);
    g_stSBUSData.m_au16Channels[11] = (HIDO_UINT16)((_pu8Frame[16] >> 1 | _pu8Frame[17] << 7)                  & 0x07FF);
    g_stSBUSData.m_au16Channels[12] = (HIDO_UINT16)((_pu8Frame[17] >> 4 | _pu8Frame[18] << 4)                  & 0x07FF);
    g_stSBUSData.m_au16Channels[13] = (HIDO_UINT16)((_pu8Frame[18] >> 7 | _pu8Frame[19] << 1 | _pu8Frame[20] << 9) & 0x07FF);
    g_stSBUSData.m_au16Channels[14] = (HIDO_UINT16)((_pu8Frame[20] >> 2 | _pu8Frame[21] << 6)                  & 0x07FF);
    g_stSBUSData.m_au16Channels[15] = (HIDO_UINT16)((_pu8Frame[21] >> 5 | _pu8Frame[22] << 3)                  & 0x07FF);
 
    // 3. 提取标志位 (Byte 23)
    // Bit 0: CH17 digital
    // Bit 1: CH18 digital
    // Bit 2: Frame Lost
    // Bit 3: Failsafe
    g_stSBUSData.m_u8Ch17       = (_pu8Frame[23] & 0x01) ? 1 : 0;
    g_stSBUSData.m_u8Ch18       = (_pu8Frame[23] & 0x02) ? 1 : 0;
    g_stSBUSData.m_u8FrameLost  = (_pu8Frame[23] & 0x04) ? 1 : 0;
    g_stSBUSData.m_u8Failsafe   = (_pu8Frame[23] & 0x08) ? 1 : 0;
 
    // 4. 更新时间戳和计数
    g_stSBUSData.m_u32LastUpdateTick = HAL_GetTick();
    g_stSBUSData.m_u32FrameCount++;
 
    // 解析成功后立即触发 PWM 控制(最小延迟)
    // 同时清除 failsafe 标志(如果之前触发过)
    if (g_bSBUSFailsafeActive)
    {
        g_bSBUSFailsafeActive = 0;
    }
    SBUS_Control_PWM();
 
    return HIDO_TRUE;
}
 
/**
 * @brief 从 DMA 缓冲区中查找并解析 SBUS 帧
 * @param _pu8Buf: DMA 缓冲区指针
 * @param _u32Len: 可读数据长度
 * @return 无
 */
static HIDO_VOID SBUS_ProcessBuffer(const HIDO_UINT8 *_pu8Buf, HIDO_UINT32 _u32Len)
{
    static HIDO_UINT32 s_u32TotalBytesProcessed = 0;
    
    for (HIDO_UINT32 i = 0; i < _u32Len; i++)
    {
        HIDO_UINT8 byte = _pu8Buf[i];
        s_u32TotalBytesProcessed++;
 
        // 状态机: 寻找帧头 0x0F
        if (g_u8ParseBufIdx == 0)
        {
            if (byte == SBUS_HEADER)
            {
                g_au8ParseBuf[g_u8ParseBufIdx++] = byte;
            }
            // 否则丢弃非帧头字节
        }
        else
        {
            g_au8ParseBuf[g_u8ParseBufIdx++] = byte;
 
            // 收满 25 字节后尝试解析
            if (g_u8ParseBufIdx >= SBUS_FRAME_SIZE)
            {
                SBUS_ParseFrame(g_au8ParseBuf);
                g_u8ParseBufIdx = 0;  // 重置状态机
            }
        }
    }
}
 
/*******************************************************************************
 *                             Global Function                                 *
 *******************************************************************************/
 
/**
 * @brief 初始化 SBUS 接收模块
 */
HIDO_INT32 SBUS_Init(HIDO_VOID)
{
    HIDO_INT32 ret;
    ST_UartInit stUartInit;
    UART_HandleTypeDef *pUart = NULL;
 
    // 先检查 UART 是否已经注册
    ret = Uart_GetHandle(UART_ID_SBUS, (HIDO_VOID **)&pUart);
    
    if (pUart == NULL)
    {
        return HIDO_FALSE;
    }
 
    // 初始化 SBUS 数据结构
    memset(&g_stSBUSData, 0, sizeof(g_stSBUSData));
    memset(g_au8ParseBuf, 0, sizeof(g_au8ParseBuf));
    memset(g_au8SBUSRxBuf, 0, sizeof(g_au8SBUSRxBuf));
    g_u8ParseBufIdx = 0;
    g_u32DebugPollCount = 0;
    g_u32DebugLastDmaRemaining = 0;
 
    // 配置 UART4 接收 (DMA 模式)
    stUartInit.m_eRxMode = UART_RX_MODE_DMA;
    stUartInit.m_pu8RxBuf = g_au8SBUSRxBuf;
    stUartInit.m_u32RxBufSize = SBUS_UART_RX_BUF_SIZE;
 
    // 不使用发送功能 (SBUS 仅单向接收)
    stUartInit.m_eTxMode = UART_TX_MODE_POLL;
    stUartInit.m_pu8TxBuf = NULL;
    stUartInit.m_u32TxBufSize = 0;
    stUartInit.m_u32TxQueueMemberCnt = 0;
    stUartInit.m_fnRxISR = NULL;
 
    ret = Uart_Init(UART_ID_SBUS, &stUartInit);
    if (ret != HIDO_OK)
    {
        return HIDO_FALSE;
    }
 
    // 获取 UART 句柄以验证初始化
    ret = Uart_GetHandle(UART_ID_SBUS, (HIDO_VOID **)&pUart);
    if (ret != HIDO_OK || pUart == NULL)
    {
        return HIDO_FALSE;
    }
 
    return HIDO_TRUE;
}
 
/**
 * @brief SBUS 轮询函数 (主循环调用)
 */
HIDO_VOID SBUS_Poll(HIDO_VOID)
{
    UART_HandleTypeDef *pUart = NULL;
    HIDO_INT32 ret;
 
    g_u32DebugPollCount++;
 
    // 获取 UART 句柄
    ret = Uart_GetHandle(UART_ID_SBUS, (HIDO_VOID **)&pUart);
    if (ret != HIDO_OK || pUart == NULL)
    {
        return;
    }
 
    // 检查 DMA 句柄
    if (pUart->hdmarx == NULL)
    {
        return;
    }
 
    // 计算 DMA 已接收的数据量
    // DMA->CNDTR 返回剩余未传输的数据个数,因此已接收 = BufSize - CNDTR
    HIDO_UINT32 dmaRemaining = __HAL_DMA_GET_COUNTER(pUart->hdmarx);
    HIDO_UINT32 receivedBytes = SBUS_UART_RX_BUF_SIZE - dmaRemaining;
 
    g_u32DebugLastDmaRemaining = dmaRemaining;
 
    // 处理新接收的数据
    // 注意: 这里使用环形缓冲区简单读取策略 (适合短周期轮询)
    static HIDO_UINT32 s_u32LastProcessedIdx = 0;
 
    if (receivedBytes > s_u32LastProcessedIdx)
    {
        HIDO_UINT32 newBytes = receivedBytes - s_u32LastProcessedIdx;
        SBUS_ProcessBuffer(&g_au8SBUSRxBuf[s_u32LastProcessedIdx], newBytes);
        s_u32LastProcessedIdx = receivedBytes;
    }
 
    // 处理 DMA 环形回绕 (当 CNDTR 重载时)
    if (receivedBytes < s_u32LastProcessedIdx)
    {
        // 先处理缓冲区尾部
        HIDO_UINT32 tailBytes = SBUS_UART_RX_BUF_SIZE - s_u32LastProcessedIdx;
        if (tailBytes > 0)
        {
            SBUS_ProcessBuffer(&g_au8SBUSRxBuf[s_u32LastProcessedIdx], tailBytes);
        }
        // 再处理缓冲区头部
        if (receivedBytes > 0)
        {
            SBUS_ProcessBuffer(&g_au8SBUSRxBuf[0], receivedBytes);
        }
        s_u32LastProcessedIdx = receivedBytes;
    }
 
    // Failsafe: 如果距离上次有效帧超过 1000ms,触发中立脉宽(1500us)
    if (!g_bSBUSFailsafeActive)
    {
        HIDO_UINT32 currentTick = HAL_GetTick();
        HIDO_UINT32 elapsed = currentTick - g_stSBUSData.m_u32LastUpdateTick;
        if (elapsed >= 1000)
        {
            // 触发一次性 failsafe:将转向与电机脉宽设为中立 1500us
            Set_Steering_Pulse(1500);
            Set_Motor_Pulse(1500);
            g_bSBUSFailsafeActive = 1;
        }
    }
}
 
/**
 * @brief 获取 SBUS 通道数据副本
 */
HIDO_INT32 SBUS_GetData(ST_SBUSData *_pstData)
{
    if (_pstData == NULL)
    {
        return HIDO_FALSE;
    }
 
    // 检查信号是否有效 (最近 100ms 内收到帧)
    if (!SBUS_IsSignalValid(SBUS_TIMEOUT_MS))
    {
        return HIDO_FALSE;
    }
 
    // 拷贝数据 (简单拷贝, 若需线程安全可加临界区)
    memcpy(_pstData, &g_stSBUSData, sizeof(ST_SBUSData));
    return HIDO_TRUE;
}
 
/**
 * @brief 获取单个通道值
 */
HIDO_UINT16 SBUS_GetChannel(HIDO_UINT8 _u8Channel)
{
    if (_u8Channel >= SBUS_NUM_CHANNELS)
    {
        return SBUS_CENTER_VALUE;
    }
 
    if (!SBUS_IsSignalValid(SBUS_TIMEOUT_MS))
    {
        return SBUS_CENTER_VALUE;
    }
 
    return g_stSBUSData.m_au16Channels[_u8Channel];
}
 
/**
 * @brief 获取数字通道状态
 */
HIDO_UINT8 SBUS_GetDigitalChannel(HIDO_UINT8 _u8DigitalChannel)
{
    if (!SBUS_IsSignalValid(SBUS_TIMEOUT_MS))
    {
        return 0;
    }
 
    if (_u8DigitalChannel == 17)
    {
        return g_stSBUSData.m_u8Ch17;
    }
    else if (_u8DigitalChannel == 18)
    {
        return g_stSBUSData.m_u8Ch18;
    }
 
    return 0;
}
 
/**
 * @brief 检查 SBUS 信号是否有效
 */
HIDO_INT32 SBUS_IsSignalValid(HIDO_UINT32 _u32TimeoutMs)
{
    HIDO_UINT32 currentTick = HAL_GetTick();
    HIDO_UINT32 elapsed = currentTick - g_stSBUSData.m_u32LastUpdateTick;
 
    return (elapsed < _u32TimeoutMs) ? HIDO_TRUE : HIDO_FALSE;
}
 
/**
 * @brief 获取接收统计信息
 */
HIDO_VOID SBUS_GetStats(HIDO_UINT32 *_pu32FrameCount, HIDO_UINT32 *_pu32ErrorCount)
{
    if (_pu32FrameCount != NULL)
    {
        *_pu32FrameCount = g_stSBUSData.m_u32FrameCount;
    }
    if (_pu32ErrorCount != NULL)
    {
        *_pu32ErrorCount = g_stSBUSData.m_u32ErrorCount;
    }
}
 
/**
 * @brief 调试函数:打印 SBUS 状态和统计信息
 */
HIDO_VOID SBUS_PrintDebugInfo(HIDO_VOID)
{
    UART_HandleTypeDef *pUart = NULL;
    HIDO_INT32 ret;
 
    HIDO_Debug2("\r\n========== SBUS Debug Info ==========\r\n");
    HIDO_Debug2("Poll count:      %u\r\n", g_u32DebugPollCount);
    HIDO_Debug2("Frame count:     %u\r\n", g_stSBUSData.m_u32FrameCount);
    HIDO_Debug2("Error count:     %u\r\n", g_stSBUSData.m_u32ErrorCount);
    HIDO_Debug2("Last update:     %u ms ago\r\n", 
                HAL_GetTick() - g_stSBUSData.m_u32LastUpdateTick);
    HIDO_Debug2("Parse buf idx:   %u\r\n", g_u8ParseBufIdx);
    
    ret = Uart_GetHandle(UART_ID_SBUS, (HIDO_VOID **)&pUart);
    if (ret == HIDO_OK && pUart != NULL)
    {
        if (pUart->hdmarx != NULL)
        {
            HIDO_UINT32 dmaRemaining = __HAL_DMA_GET_COUNTER(pUart->hdmarx);
            HIDO_Debug2("DMA remaining:   %u\r\n", dmaRemaining);
            HIDO_Debug2("DMA received:    %u\r\n", SBUS_UART_RX_BUF_SIZE - dmaRemaining);
            HIDO_Debug2("DMA state:       %u\r\n", HAL_DMA_GetState(pUart->hdmarx));
        }
        else
        {
            HIDO_Debug2("DMA handle:      NULL!\r\n");
        }
    }
    else
    {
        HIDO_Debug2("UART handle:     NULL!\r\n");
    }
 
    if (g_stSBUSData.m_u32FrameCount > 0)
    {
        HIDO_Debug2("\r\nLast frame channels:\r\n");
        for (HIDO_UINT8 i = 0; i < 8; i++)
        {
            HIDO_Debug2("  CH%02u: %4u", i, g_stSBUSData.m_au16Channels[i]);
            if ((i + 1) % 4 == 0) HIDO_Debug2("\r\n");
        }
        HIDO_Debug2("  CH17: %u, CH18: %u\r\n", 
                    g_stSBUSData.m_u8Ch17, g_stSBUSData.m_u8Ch18);
        HIDO_Debug2("  FrameLost: %u, Failsafe: %u\r\n",
                    g_stSBUSData.m_u8FrameLost, g_stSBUSData.m_u8Failsafe);
    }
 
    HIDO_Debug2("\r\nRaw RX buffer (first 32 bytes):\r\n");
    for (HIDO_UINT8 i = 0; i < 32; i++)
    {
        HIDO_Debug2("%02X ", g_au8SBUSRxBuf[i]);
        if ((i + 1) % 16 == 0) HIDO_Debug2("\r\n");
    }
    HIDO_Debug2("====================================\r\n\r\n");
}