1 /* I-Bus servo mixer for FlySky iA6B receiver */
4 #include <avr/interrupt.h>
6 #include <avr/pgmspace.h>
7 #include <util/atomic.h>
8 #include <util/delay.h>
10 #define N_SERVO_CHANNELS 18
11 #define IBUS_SERVO_FRAME_SIZE 32
12 #define IBUS_SERVO_FRAME_ID 0x40 // first byte after the length
14 /* ---------- LEDs for debugging ---------- */
32 /* ----------------- Timer ----------------- */
34 typedef uint16_t time_t;
36 static void timer_init(void)
38 TCCR1A = 0; // no PWM or WGM output
39 TCCR1B = _BV(CS11); // clk/8
41 DDRD |= _BV(PD2) | _BV(PD3) | _BV(PD4) | _BV(PD5);
45 static time_t inline get_time(void)
49 ATOMIC_BLOCK(ATOMIC_FORCEON) {
57 /* ----------------- USART ----------------- */
59 // I-Bus uses 115200n8
60 #define UART_BAUD 115200
61 #define UBRR_VAL ((F_CPU + 8UL * UART_BAUD) / (16UL*UART_BAUD) - 1)
63 volatile uint8_t serial_frame[IBUS_SERVO_FRAME_SIZE];
64 volatile uint8_t serial_frame_ready;
66 static volatile uint8_t serial_frame_pos;
68 void serial_init(void)
73 UCSR0B = _BV(RXEN0) | _BV(RXCIE0);
74 UCSR0C = _BV(UCSZ01)|_BV(UCSZ00);
76 serial_frame_ready = serial_frame_pos = 0;
79 static void serial_enable_rx(void)
81 UCSR0B &= ~(_BV(TXEN0) | _BV(TXCIE0));
82 UCSR0B |= _BV(RXEN0) | _BV(RXCIE0);
85 #define serial_rx_vect USART_RX_vect
87 #define serial_data UDR0
90 * USART receive interrupt
92 * In order to save the latency, we do only the necessary things here,
93 * and postpone parsing the frame to tne non-irq time in the main
94 * loop. Also, we expect the main loop to finish before the second
95 * byte of the next frame is read, so we do not do atomic reads on top
100 uint8_t val = serial_data;
102 // a shorthand - for now, we accept fixed-size packets only
103 if (serial_frame_pos == 0 && val != IBUS_SERVO_FRAME_SIZE)
106 if (serial_frame_pos == 1
107 && serial_frame[0] == IBUS_SERVO_FRAME_SIZE
108 && val != IBUS_SERVO_FRAME_ID)
111 serial_frame[serial_frame_pos++] = val;
113 if (serial_frame_pos == serial_frame[0])
114 serial_frame_ready = 1;
116 serial_frame_pos = 0;
119 /* ----------------- Servos ----------------- */
121 #define SERVO_MASK (_BV(PD2)|_BV(PD3)|_BV(PD4)|_BV(PD5)|_BV(PD6)|_BV(PD7))
123 static const PROGMEM uint8_t servo_bits[] = {
132 static void xxx_init(void)
134 DDRD |= _BV(PD2) | _BV(PD3) | _BV(PD4) | _BV(PD5);
138 // run this inside interrupt or in atomic context
139 static void interrupt_after(uint16_t delay)
141 uint16_t now = TCNT1;
144 TIMSK1 |= _BV(OCIE1A);
147 ISR(TIMER1_COMPA_vect) {
148 static uint16_t led = 2000;
154 interrupt_after(65000);
158 interrupt_after(led);
164 /* ----------------- iBus ------------------ */
166 static void ibus_servo_frame(void)
168 uint16_t csum = 0xFFFF, servo;
171 for (i = 0; i < serial_frame[0]-2; i++)
172 csum -= (uint16_t)serial_frame[i];
174 if ((serial_frame[serial_frame[0]-2] != (csum & 0xFF))
175 || (serial_frame[serial_frame[0]-1] != (csum >> 8))) { // invalid csum
179 servo = serial_frame[12];
180 servo |= ((uint16_t)serial_frame[13] & 0x000F) << 8;
183 * -120 % == 0x384 == 900
184 * -100 % == 0x3e8 == 1000
185 * 0 % == 0x5dc == 1500
206 if (serial_frame_ready) {
207 serial_frame_ready = 0;