본문 바로가기
  • 경제적 자유를 얻는 그날까지
엔지니어링/드론

[STM32 자작드론] FrSky SBUS 수신기 구현

by 베니스상인 2023. 2. 18.

 

 

 

 

 

사용된 수신기는 FrSky 사의 XSR수신기이며, 16채널 SBUS, CPPM 형태로 출력이 가능한 수신기이다. 입출력 인터페이스를 보면,  SBUS와 S.Port, CPPM 포트가 나와있다. CPPM은 PWM 신호 형태로 입력을 받아서 사용해야 하고, S.Port는 텔레메트리 데이터 수신용으로 사용한다. 그리고 SBUS는 다채널 수신기 데이터 수신용으로 사용이 가능하다.

 

 

다만, 이 제품의 경우 SBUS 수신시 inverted serial 형태로 들어오기 때문에 inverting이 필요하다. 실제 수신기로 들어오는 SBUS 신호는 아래와 같이 측정된다. Serial 신호를 뒤집어 놓은 것과 같은 형태로 출력되기 때문에 inverted 되었다고 말한다. 

 

 

 

일부 프로세서들은 UART 입력 핀의 Polarity를 변경하여 입력단에서 non-inverting serial로 변경할 수 있는 제품들이 있지만 본인이 사용하고 있는 Revolt STM32F4 제품은 이런 기능이 없다.  그래서 SW적으로 inverting 기능이 있는지 검색해보았지만, UART의 입력단 핀을 뒤집어줘야하기 때문에 일반 GPIO를 이용해서 SoftSerial을 구현해줘야 한다. 하지만 SoftSerial은 리소스를 많이 사용하는데다가 115200bps까지 높은 속도로 구동이 어렵다.

 

 

다른 방법으로 검색해보니 FrSky 수신기에서 Inverting/Non-inverting 을 변환해주는 기능을 지원하고 있는 자료가 있었다.  

 

https://www.getfpv.com/learn/fpv-diy-repairs-and-mods/get-uninverted-sbus-frsky-receivers/

 

Get Uninverted SBUS from FrSKY Receivers | GetFPV Learn

Why would you want to "uninvert" the inverted FrSky SBUS or smartport signal from your receiver? This useful guide will teach you how to hack and modify your X4R-SB, XSR, XM+ and XM FrSky SBUS receivers to get uninverted SBUS and Smartport signals.

www.getfpv.com

 

이 자료를 살펴보면 수신기마다 Non-inverting으로 출력할 수 있는 하드웨어 핀이 있기 때문에 해당핀의 출력을 UART 입력으로  

사용할 수 있다.  

 

본인이 사용한 수신기는 FrSky 사의 XSR 수신기이다. 

 

FrSky XSR 수신기 형상

 

Non-inverting SBUS 출력은 아래 빨간색 동그라미 포트에서 출력을 연결하면 된다.

참고로 S(Smart).Port도 아래 위치의 출력을 뽑으면 Non-inverting S.Port를 출력할 수 있다.

 

 

FrSky사의 SBUS 수신기에서 Non-inverting SBUS 신호를 Revolt FC의 UART1번에 연결하였다.  Revolt FC 프로세서의 경우 사용할 수 있는 UART의 개수가 한정적이고 외부로 연결 가능한 UART는 1,3,4,6인데, 본인의 경우 UART3은 GPS수신기에 연결, UART6은 문자열 입출력, 그리고 UART1과 4는 RX없이 TX핀으로 TX/RX를 사용하는 Half Duplex 방식으로 사용해야 한다.

 

따라서 UART4는 향후 텔레메트리 정보 수신용으로 사용하기로 하고, UART1 TX 핀을 SBUS 수신기 연결에 사용하였다.  

 

Revolt FC Top

 

 

 

 

 


SBUS 데이터를 수신하기 위해 아래 자료를 참고하였다.

https://github.com/bolderflight/SBUS

 

GitHub - bolderflight/sbus: Arduino and CMake library for communicating with SBUS receivers and servos.

Arduino and CMake library for communicating with SBUS receivers and servos. - GitHub - bolderflight/sbus: Arduino and CMake library for communicating with SBUS receivers and servos.

github.com

 

 

STM32 CubeMX에서 SBUS 수신용 UART1은 아래와 같이 설정하였다.

  • Mode: Single wire(Half-Duplex)
  • baudrate: 100000 bps
  • stop bits: 2

Non-inverting SBUS 신호를 UART1의 TX에 연결하였고, Tx 핀으로 데이터를 수신한다. 해당 방법으로 DMA를 사용하려고 시도하였으나 DMA 인터럽트는 발생하지만 데이터가 수신되지 않았다. 이유는 정확하게 파악하지 못했지만, 설정된 DMA(DMA2,. Stream7)가 Single Wire에 연결된 Tx의 수신용이 아니라서 그런지 데이터를 수신할 수 없어서 UART 인터럽트 방식으로 데이터를 수집하였다.

 

 

인터럽트는 UART_RXNE와 UART_IDLE 인터럽트를 사용하였다. 각각의 인터럽트가 발생하면 플래그를 띄워서 메인에서 인터럽트 서비스를 실행하도록 하였다.

 

void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
	if(LL_USART_IsEnabledIT_RXNE(USART1) && LL_USART_IsActiveFlag_RXNE(USART1)) // 인터럽트중 USART6인지 확인
	{
        LL_USART_ClearFlag_RXNE(USART1); // 맞으면 비트 클리어
        flag_INT_UART1_RX = 1; // flag 클리어
	}
	if(LL_USART_IsEnabledIT_IDLE(USART1) && LL_USART_IsActiveFlag_IDLE(USART1)) {
		LL_USART_ClearFlag_IDLE(USART1);
		flag_INT_UART1_RX_DONE = 1; // 수신완료

	}
}

 stm32f4xx_it.c의 함수 내용 

 

 

RXNE는 UART 데이터가 한바이트씩 Data Regiter에 들어올 때 마다 발생하는 인터럽트이다. 이때마다 버퍼에 데이터를 저장한다. 저장할 때 버퍼의 크기는 SBUS 한 패킷의 크기로 정하고, 카운터값을 하나씩 증가하면서 버퍼에 저장한다.

 

IDLE은 데이터 수신이 완료되고 IDLE 상태가 감지되면 발생하는 인터럽트이다. 이때는 데이터 수신이 완료되었다고 생각하고 카운터 초기화와 버퍼 Parsing 작업을 수행한다.

while(1) 
{

	  if(flag_INT_UART1_RX==1){
		  raw_rx.rx_buf[cnt1++] = LL_USART_ReceiveData8(USART1);
          flag_INT_UART1_RX =0;
	  }

	  if(flag_INT_UART1_RX_DONE == 1)
	  {
		  SBUS_Parsing(&raw_rx, &msg_sbus, &rx_recv_cnt, &rx_err_cnt);
          cnt1=0;
		  flag_INT_UART1_RX_DONE = 0;
	  }
}

main.c 내용

 

 

 

SBUS 데이터가 수신되면 버퍼를 Scan하면서 SBUS 데이터를 Parsing 한다. 수신된 SBUS 데이터의 프로토콜은 아래와 같이 구성되어 있다. 

 

 

SBUS 프로토콜

  • Byte[0]: SBUS header, 0x0F
  • Byte[1 -22]: 16 servo channels, 11 bits each
  • Byte[23]
    • Bit 0: channel 17 (0x01)
    • Bit 1: channel 18 (0x02)
    • Bit 2: frame lost (0x04)
    • Bit 3: failsafe activated (0x08)
  • Byte[24]: SBUS footer

 

 

SBUS는 총 25바이트로 구성되어 있고, 첫 헤더는  0x0F이고, 그 다음부터 22바이트는 11비트로 구성된 16채널 데이터가 들어가 있다.  22*8 = 16*11 = 176bits , 따라서, 채널 데이터를 Parsing 하려면 헤더를 제외한 첫번째 바이트부터 11비트씩 읽어가면서 총 16개 채널데이터로 할당해야 한다. 그 다음 1바이트는 토클 17번 18번 채널과 Frame Lost, Failsafe 비트로 구성되어있다. 

 

void SBUS_Parsing(SBUS_RAW_MESSAGE* raw, MSG_SBUS* msg_sbus, int* rx_recv_cnt, int* rx_err_cnt)
{
	uint8_t* ptr = raw->rx_buf;
	int32_t temp;
	unsigned short msg_length = 0, checksum = 0;
	unsigned char classID = 0, messageID = 0;
	if(ptr[0]==MSG_SBUS_SOF ){
		msg_sbus->header = ptr[0];
	       msg_sbus->rx_channel[0]   = (int16_t)(ptr[1] | ((ptr[2] << 8) & 0x07FF));
	        msg_sbus->rx_channel[1]  = (int16_t)((ptr[2] >> 3) | ((ptr[3] << 5) & 0x07FF));
	        msg_sbus->rx_channel[2]  = (int16_t)((ptr[3] >> 6) | (ptr[4] << 2) | ((ptr[5] << 10) & 0x07FF));
	        msg_sbus->rx_channel[3]  = (int16_t)((ptr[5] >> 1) | ((ptr[6] << 7) & 0x07FF));
	        msg_sbus->rx_channel[4]  = (int16_t)((ptr[6] >> 4) | ((ptr[7] << 4) & 0x07FF));
	        msg_sbus->rx_channel[5]  = (int16_t)((ptr[7] >> 7) | (ptr[8] << 1) | ((ptr[9] << 9) & 0x07FF));
	        msg_sbus->rx_channel[6]  = (int16_t)((ptr[9] >> 2) | ((ptr[10] << 6) & 0x07FF));
	        msg_sbus->rx_channel[7]  = (int16_t)((ptr[10] >> 5) | ((ptr[11] << 3) & 0x07FF));
	        msg_sbus->rx_channel[8]  = (int16_t)(ptr[12] | ((ptr[13] << 8) & 0x07FF));
	        msg_sbus->rx_channel[9]  = (int16_t)((ptr[13] >> 3) | ((ptr[14] << 5) & 0x07FF));
	        msg_sbus->rx_channel[10] = (int16_t)((ptr[14] >> 6) | (ptr[15] << 2) |((ptr[16] << 10) & 0x07FF));
	        msg_sbus->rx_channel[11] = (int16_t)((ptr[16] >> 1) | ((ptr[17] << 7) & 0x07FF));
	        msg_sbus->rx_channel[12] = (int16_t)((ptr[17] >> 4) | ((ptr[18] << 4) & 0x07FF));
	        msg_sbus->rx_channel[13] = (int16_t)((ptr[18] >> 7) | (ptr[19] << 1) | ((ptr[20] << 9) & 0x07FF));
	        msg_sbus->rx_channel[14] = (int16_t)((ptr[20] >> 2) | ((ptr[21] << 6) & 0x07FF));
	        msg_sbus->rx_channel[15] = (int16_t)((ptr[21] >> 5) | ((ptr[22] << 3) & 0x07FF));
	        msg_sbus->rx_channel17 	= ptr[23] & MSG_SBUS_CH17_BIT_MASK;
	        msg_sbus->rx_channel18 	= ptr[23] & MSG_SBUS_CH18_BIT_MASK;
	        msg_sbus->frame_lost 	= ptr[23] & MSG_SBUS_FL_BIT_MASK;
	        msg_sbus->failsafe 		= ptr[23] & MSG_SBUS_FS_BIT_MASK;


		rx_recv_cnt[0]++;

		printf("cnt: %d\t ch[1]:%d\t ch[2]:%d\t ch[3]:%d\t ch[4]:%d\t ch[5]:%d\t ch[6]:%d\t ch[7]:%d\t ch[8]:%d\t FL:%d FS:%d\n",rx_recv_cnt[0], msg_sbus->rx_channel[0],msg_sbus->rx_channel[1],msg_sbus->rx_channel[2],msg_sbus->rx_channel[3],msg_sbus->rx_channel[4],msg_sbus->rx_channel[5],msg_sbus->rx_channel[6],msg_sbus->rx_channel[7], msg_sbus->frame_lost, msg_sbus->failsafe);
	}

	else
	{
		rx_err_cnt[0]++;
	}
//	printf("recv: %d\t err: %d\n", rx_recv_cnt[0], rx_err_cnt[0]);

}

 

 

SBUS로 수신된 데이터를 Parsing 하여 채널데이터로 출력해보면 아래와 같이 나타난다.

 

 

 

수신기와 함께 사용한 조종기는 Taranis X9D 조종기이며, 16채널중 8채널을 할당하여 사용하고 있다.

 

 

 

Taranis 9XD 채널매핑

 

조종기에 매핑된 수신기 신호를 분석해보면 아래와 같다. 

Channel Assignment Range(Min) Range(Middle) Range(Max)
CH1 Throttle 192 - 1792
CH2 Roll 193 994 1793
CH3 Pitch 193 992 1793
CH4 Rudder 193 984 1793
CH5 SA 172 992 1811
CH6 SD 172 992 1811
CH7 SG 172 992 1811
CH8 SF 172 - 1811

 

 

 

 

조종기의 각 키입력에 따라 수신기의 채널 범위값이 실시간으로 적절하게 바뀌는 것을 확인하였다. 이로써 수신채널 입력 기능도 확인하였고 조종기를 껐을 때 Frame Lost와 Failsafe 활성화 기능도 확인하였다. 

 

 

 

 

git log

swift@swift-HP-Pavilion-dv6-Notebook-PC:~/workspace/STM32CubeIDE/dev$ git add .


swift@swift-HP-Pavilion-dv6-Notebook-PC:~/workspace/STM32CubeIDE/dev$ git status
On branch main
Your branch is up to date with 'origin/main'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	modified:   STM32F4_FC_DEV/Core/Inc/GPS_Receiver.h
	new file:   STM32F4_FC_DEV/Core/Inc/SBUS_Receiver.h
	modified:   STM32F4_FC_DEV/Core/Inc/main.h
	modified:   STM32F4_FC_DEV/Core/Inc/stm32f4xx_it.h
	modified:   STM32F4_FC_DEV/Core/Inc/usart.h
	modified:   STM32F4_FC_DEV/Core/Src/GPS_Receiver.c
	modified:   STM32F4_FC_DEV/Core/Src/Quaternion.c
	new file:   STM32F4_FC_DEV/Core/Src/SBUS_Receiver.c
	modified:   STM32F4_FC_DEV/Core/Src/main.c
	modified:   STM32F4_FC_DEV/Core/Src/stm32f4xx_it.c
	modified:   STM32F4_FC_DEV/Core/Src/usart.c
	modified:   STM32F4_FC_DEV/Debug/Core/Src/GPS_Receiver.o
	modified:   STM32F4_FC_DEV/Debug/Core/Src/ICM20602.o
	modified:   STM32F4_FC_DEV/Debug/Core/Src/Quaternion.o
	new file:   STM32F4_FC_DEV/Debug/Core/Src/SBUS_Receiver.d
	new file:   STM32F4_FC_DEV/Debug/Core/Src/SBUS_Receiver.o
	new file:   STM32F4_FC_DEV/Debug/Core/Src/SBUS_Receiver.su
	modified:   STM32F4_FC_DEV/Debug/Core/Src/dma.o
	modified:   STM32F4_FC_DEV/Debug/Core/Src/gpio.o
	modified:   STM32F4_FC_DEV/Debug/Core/Src/main.d
	modified:   STM32F4_FC_DEV/Debug/Core/Src/main.o
	modified:   STM32F4_FC_DEV/Debug/Core/Src/main.su
	modified:   STM32F4_FC_DEV/Debug/Core/Src/spi.o
	modified:   STM32F4_FC_DEV/Debug/Core/Src/stm32f4xx_it.o
	modified:   STM32F4_FC_DEV/Debug/Core/Src/stm32f4xx_it.su
	modified:   STM32F4_FC_DEV/Debug/Core/Src/subdir.mk
	modified:   STM32F4_FC_DEV/Debug/Core/Src/tim.o
	modified:   STM32F4_FC_DEV/Debug/Core/Src/usart.o
	modified:   STM32F4_FC_DEV/Debug/Core/Src/usart.su
	modified:   STM32F4_FC_DEV/Debug/STM32F4_FC_DEV.elf
	modified:   STM32F4_FC_DEV/Debug/STM32F4_FC_DEV.list
	modified:   STM32F4_FC_DEV/Debug/STM32F4_FC_DEV.map
	modified:   STM32F4_FC_DEV/Debug/objects.list
	modified:   STM32F4_FC_DEV/STM32F4_FC_DEV.ioc


swift@swift-HP-Pavilion-dv6-Notebook-PC:~/workspace/STM32CubeIDE/dev$ git commit -m "done SBUS receiver using UART1 TX interrupt"
[main d760a42] done SBUS receiver using UART1 TX interrupt
 432 files changed, 137815 insertions(+), 19224 deletions(-)
 create mode 100644 STM32F4_FC_DEV/Core/Inc/SBUS_Receiver.h
 create mode 100644 STM32F4_FC_DEV/Core/Src/SBUS_Receiver.c
 rewrite STM32F4_FC_DEV/Debug/Core/Src/Quaternion.o (75%)
 create mode 100644 STM32F4_FC_DEV/Debug/Core/Src/SBUS_Receiver.d
 create mode 100644 STM32F4_FC_DEV/Debug/Core/Src/SBUS_Receiver.o
 create mode 100644 STM32F4_FC_DEV/Debug/Core/Src/SBUS_Receiver.su
 rewrite STM32F4_FC_DEV/Debug/Core/Src/main.o (81%)
 rewrite STM32F4_FC_DEV/Debug/Core/Src/stm32f4xx_it.o (79%)
 rewrite STM32F4_FC_DEV/Debug/Core/Src/usart.o (80%)
 rewrite STM32F4_FC_DEV/Debug/STM32F4_FC_DEV.elf (66%)
 rewrite STM32F4_FC_DEV/Debug/STM32F4_FC_DEV.list (69%)


swift@swift-HP-Pavilion-dv6-Notebook-PC:~/workspace/STM32CubeIDE/dev$ git push -u origin main
Username for 'https://github.com': shlee853
Password for 'https://shlee853@github.com': 
Enumerating objects: 833, done.
Counting objects: 100% (833/833), done.
Delta compression using up to 8 threads
Compressing objects: 100% (628/628), done.
Writing objects: 100% (635/635), 1.27 MiB | 892.00 KiB/s, done.
Total 635 (delta 437), reused 0 (delta 0)
remote: Resolving deltas: 100% (437/437), completed with 102 local objects.
To https://github.com/shlee853/STM32F4_FC_DEV.git
   c858506..d760a42  main -> main
Branch 'main' set up to track remote branch 'main' from 'origin'.


swift@swift-HP-Pavilion-dv6-Notebook-PC:~/workspace/STM32CubeIDE/dev$ git log --pretty=oneline
d760a42b258158daa1565754f2a4d9a5711b4daa (HEAD -> main, origin/main) done SBUS receiver using UART1 TX interrupt
c8585068d7bfc48fe531b63e0f518ee2d68291d8 done receiving  GPS data with DMA and Parsing UBX data format
cb2666fed0ff08b71738e501bc091ec138c4b98b done receiving GPS data with NMEA, but, can't configure UBX with CFG messages
2c7790379d578411ffd79b477bc3d011fbef1e4a done implementation for quaternion algorithm(Madgwick/Mahony) & serial plotter
a6c6c2c509d4b8f015c27d609f126741bb7b7c28 Implemented udDelay function using sysTick(obsoleted LL_mDelay)
891968755a226792e2732c03d22b81d1042d2bc1 done project ICM20602(gyro&accel sensors) interface using SPI
19c17e72497f7834e9a04da35338347f51a986c8 done project for receiving data using interupt and transmit received data
cc840798716809f6b47b4111b825a60381d1934d done project for poilling Transmit data using USART6
00af3f79301c64ba1b43044869f96cf8848c678a Initial Release & done project for LED blink

 

 

Source Code

https://github.com/shlee853/STM32F4_FC_DEV/tree/main/STM32F4_FC_DEV

 

GitHub - shlee853/STM32F4_FC_DEV: Development DIY Flight Controller using STM32F404 MCU

Development DIY Flight Controller using STM32F404 MCU - GitHub - shlee853/STM32F4_FC_DEV: Development DIY Flight Controller using STM32F404 MCU

github.com

 

728x90

댓글