Thursday, 13 February 2020

Custom Flight Controller Part 2.5: Receiver with SBUS

This particular post documents writing STM32 driver code to receive SBUS data from RC receiver. For the complete code, refer to my Github.

SBUS protocol is developed by Futaba. It used to be proprietary but it has been reverse-engineered to become an Arduino library. Since I am using STM32F103, I have to translate the library to be used by STM32. 

The details of SBUS protocol can be found here. To follow the protocol, the UART3 on STM32CubeMX is set up as follows:

DMA is set up to speed up data transmission. 

With the above DMA configuration, STM32 will use DMA to transmit data received via UART to DMA buffer and notify application once all data is received.

However, additional work needs to be done to make it work robustly. To use DMA, one has to know the number of bytes to receive in advance. In this case I know that every SBUS data is 25 bytes. But unfortunately, I do not have guarantee that the first byte I receive after turning on UART communication is the first byte of SBUS data. This situation is likely to happen because receiver is powered on instantly with STM32 while UART communication is turned on after STM32 finishes initialization.

To solve this problem, I have to detect the start of a new SBUS communication. The way to do it is to utilize the IDLE line interrupt from UART. The detailed step is as follows:
1. Enable UART3 global interrupt in STM32CubeMX.
2. In code, wait for UART3 interrupt.
3. Check whether interrupt source is IDLE line. If it is, call HAL_UART_Receive_DMA()
void SBUS_InterruptHandler() {
    // Custom handling. Check whether interrupt source is IDLE line. This ensures the start of SBUS data.
    if (__HAL_UART_GET_IT_SOURCE(&huart3, UART_IT_IDLE)) {
        HAL_UART_Receive_DMA(&huart3, sRecBuffer, SBUS_MSG_LENGTH); // this is time-critical.
        __HAL_UART_DISABLE_IT(&huart3, UART_IT_IDLE);
    }

    // HAL handling
    HAL_UART_IRQHandler(&huart3);
}
4. Handle data in HAL_UART_RxCpltCallback()
Lastly, a ping-pong buffer is used to cache command for one timestamp in case STM32 is not fast enough to receive data in desired frequency.

No comments:

Post a Comment