]> git.donarmstrong.com Git - qmk_firmware.git/blob - keyboards/lily58/serial.c
Second try at configuring i18n for docsify and gitbook (#4531)
[qmk_firmware.git] / keyboards / lily58 / serial.c
1 /*
2  * WARNING: be careful changing this code, it is very timing dependent
3  */
4
5 #ifndef F_CPU
6 #define F_CPU 16000000
7 #endif
8
9 #include <avr/io.h>
10 #include <avr/interrupt.h>
11 #include <util/delay.h>
12 #include <stddef.h>
13 #include <stdbool.h>
14 #include "serial.h"
15 //#include <pro_micro.h>
16
17 #ifdef USE_SERIAL
18 //#ifndef USE_SERIAL_PD2
19
20 #ifndef SERIAL_USE_MULTI_TRANSACTION
21 /* --- USE Simple API (OLD API, compatible with let's split serial.c) */
22   #if SERIAL_SLAVE_BUFFER_LENGTH > 0
23   uint8_t volatile serial_slave_buffer[SERIAL_SLAVE_BUFFER_LENGTH] = {0};
24   #endif
25   #if SERIAL_MASTER_BUFFER_LENGTH > 0
26   uint8_t volatile serial_master_buffer[SERIAL_MASTER_BUFFER_LENGTH] = {0};
27   #endif
28   uint8_t volatile status0 = 0;
29
30 SSTD_t transactions[] = {
31     { (uint8_t *)&status0,
32   #if SERIAL_MASTER_BUFFER_LENGTH > 0
33       sizeof(serial_master_buffer), (uint8_t *)serial_master_buffer,
34   #else
35       0, (uint8_t *)NULL,
36   #endif
37   #if SERIAL_SLAVE_BUFFER_LENGTH > 0
38       sizeof(serial_slave_buffer), (uint8_t *)serial_slave_buffer
39   #else
40       0, (uint8_t *)NULL,
41   #endif
42   }
43 };
44
45 void serial_master_init(void)
46 { soft_serial_initiator_init(transactions); }
47
48 void serial_slave_init(void)
49 { soft_serial_target_init(transactions); }
50
51 // 0 => no error
52 // 1 => slave did not respond
53 // 2 => checksum error
54 int serial_update_buffers()
55 { return soft_serial_transaction(); }
56
57 #endif // Simple API (OLD API, compatible with let's split serial.c)
58
59 #define ALWAYS_INLINE __attribute__((always_inline))
60 #define NO_INLINE __attribute__((noinline))
61 #define _delay_sub_us(x)    __builtin_avr_delay_cycles(x)
62
63 // Serial pulse period in microseconds.
64 #define TID_SEND_ADJUST 14
65
66 #define SELECT_SERIAL_SPEED 1
67 #if SELECT_SERIAL_SPEED == 0
68   // Very High speed
69   #define SERIAL_DELAY 4             // micro sec
70   #define READ_WRITE_START_ADJUST 33 // cycles
71   #define READ_WRITE_WIDTH_ADJUST 3 // cycles
72 #elif SELECT_SERIAL_SPEED == 1
73   // High speed
74   #define SERIAL_DELAY 6             // micro sec
75   #define READ_WRITE_START_ADJUST 30 // cycles
76   #define READ_WRITE_WIDTH_ADJUST 3 // cycles
77 #elif SELECT_SERIAL_SPEED == 2
78   // Middle speed
79   #define SERIAL_DELAY 12            // micro sec
80   #define READ_WRITE_START_ADJUST 30 // cycles
81   #define READ_WRITE_WIDTH_ADJUST 3 // cycles
82 #elif SELECT_SERIAL_SPEED == 3
83   // Low speed
84   #define SERIAL_DELAY 24            // micro sec
85   #define READ_WRITE_START_ADJUST 30 // cycles
86   #define READ_WRITE_WIDTH_ADJUST 3 // cycles
87 #elif SELECT_SERIAL_SPEED == 4
88   // Very Low speed
89   #define SERIAL_DELAY 50            // micro sec
90   #define READ_WRITE_START_ADJUST 30 // cycles
91   #define READ_WRITE_WIDTH_ADJUST 3 // cycles
92 #else
93 #error Illegal Serial Speed
94 #endif
95
96
97 #define SERIAL_DELAY_HALF1 (SERIAL_DELAY/2)
98 #define SERIAL_DELAY_HALF2 (SERIAL_DELAY - SERIAL_DELAY/2)
99
100 #define SLAVE_INT_WIDTH_US 1
101 #ifndef SERIAL_USE_MULTI_TRANSACTION
102   #define SLAVE_INT_RESPONSE_TIME SERIAL_DELAY
103 #else
104   #define SLAVE_INT_ACK_WIDTH_UNIT 2
105   #define SLAVE_INT_ACK_WIDTH 4
106 #endif
107
108 static SSTD_t *Transaction_table = NULL;
109
110 inline static
111 void serial_delay(void) {
112   _delay_us(SERIAL_DELAY);
113 }
114
115 inline static
116 void serial_delay_half1(void) {
117   _delay_us(SERIAL_DELAY_HALF1);
118 }
119
120 inline static
121 void serial_delay_half2(void) {
122   _delay_us(SERIAL_DELAY_HALF2);
123 }
124
125 inline static void serial_output(void) ALWAYS_INLINE;
126 inline static
127 void serial_output(void) {
128   SERIAL_PIN_DDR |= SERIAL_PIN_MASK;
129 }
130
131 // make the serial pin an input with pull-up resistor
132 inline static void serial_input_with_pullup(void) ALWAYS_INLINE;
133 inline static
134 void serial_input_with_pullup(void) {
135   SERIAL_PIN_DDR  &= ~SERIAL_PIN_MASK;
136   SERIAL_PIN_PORT |= SERIAL_PIN_MASK;
137 }
138
139 inline static
140 uint8_t serial_read_pin(void) {
141   return !!(SERIAL_PIN_INPUT & SERIAL_PIN_MASK);
142 }
143
144 inline static void serial_low(void) ALWAYS_INLINE;
145 inline static
146 void serial_low(void) {
147   SERIAL_PIN_PORT &= ~SERIAL_PIN_MASK;
148 }
149
150 inline static void serial_high(void) ALWAYS_INLINE;
151 inline static
152 void serial_high(void) {
153   SERIAL_PIN_PORT |= SERIAL_PIN_MASK;
154 }
155
156 void soft_serial_initiator_init(SSTD_t *sstd_table)
157 {
158     Transaction_table = sstd_table;
159     serial_output();
160     serial_high();
161 }
162
163 void soft_serial_target_init(SSTD_t *sstd_table)
164 {
165     Transaction_table = sstd_table;
166     serial_input_with_pullup();
167
168 #if SERIAL_PIN_MASK == _BV(PD0)
169     // Enable INT0
170     EIMSK |= _BV(INT0);
171     // Trigger on falling edge of INT0
172     EICRA &= ~(_BV(ISC00) | _BV(ISC01));
173 #elif SERIAL_PIN_MASK == _BV(PD2)
174     // Enable INT2
175     EIMSK |= _BV(INT2);
176     // Trigger on falling edge of INT2
177     EICRA &= ~(_BV(ISC20) | _BV(ISC21));
178 #else
179  #error unknown SERIAL_PIN_MASK value
180 #endif
181 }
182
183 // Used by the sender to synchronize timing with the reciver.
184 static void sync_recv(void) NO_INLINE;
185 static
186 void sync_recv(void) {
187   for (uint8_t i = 0; i < SERIAL_DELAY*5 && serial_read_pin(); i++ ) {
188   }
189   // This shouldn't hang if the target disconnects because the
190   // serial line will float to high if the target does disconnect.
191   while (!serial_read_pin());
192 }
193
194 // Used by the reciver to send a synchronization signal to the sender.
195 static void sync_send(void)NO_INLINE;
196 static
197 void sync_send(void) {
198   serial_low();
199   serial_delay();
200   serial_high();
201 }
202
203 // Reads a byte from the serial line
204 static uint8_t serial_read_chunk(uint8_t *pterrcount, uint8_t bit) NO_INLINE;
205 static uint8_t serial_read_chunk(uint8_t *pterrcount, uint8_t bit) {
206     uint8_t byte, i, p, pb;
207
208   _delay_sub_us(READ_WRITE_START_ADJUST);
209   for( i = 0, byte = 0, p = 0; i < bit; i++ ) {
210       serial_delay_half1();   // read the middle of pulses
211       if( serial_read_pin() ) {
212           byte = (byte << 1) | 1; p ^= 1;
213       } else {
214           byte = (byte << 1) | 0; p ^= 0;
215       }
216       _delay_sub_us(READ_WRITE_WIDTH_ADJUST);
217       serial_delay_half2();
218   }
219   /* recive parity bit */
220   serial_delay_half1();   // read the middle of pulses
221   pb = serial_read_pin();
222   _delay_sub_us(READ_WRITE_WIDTH_ADJUST);
223   serial_delay_half2();
224
225   *pterrcount += (p != pb)? 1 : 0;
226
227   return byte;
228 }
229
230 // Sends a byte with MSB ordering
231 void serial_write_chunk(uint8_t data, uint8_t bit) NO_INLINE;
232 void serial_write_chunk(uint8_t data, uint8_t bit) {
233     uint8_t b, p;
234     for( p = 0, b = 1<<(bit-1); b ; b >>= 1) {
235         if(data & b) {
236             serial_high(); p ^= 1;
237         } else {
238             serial_low();  p ^= 0;
239         }
240         serial_delay();
241     }
242     /* send parity bit */
243     if(p & 1) { serial_high(); }
244     else      { serial_low(); }
245     serial_delay();
246
247     serial_low(); // sync_send() / senc_recv() need raise edge
248 }
249
250 static void serial_send_packet(uint8_t *buffer, uint8_t size) NO_INLINE;
251 static
252 void serial_send_packet(uint8_t *buffer, uint8_t size) {
253   for (uint8_t i = 0; i < size; ++i) {
254     uint8_t data;
255     data = buffer[i];
256     sync_send();
257     serial_write_chunk(data,8);
258   }
259 }
260
261 static uint8_t serial_recive_packet(uint8_t *buffer, uint8_t size) NO_INLINE;
262 static
263 uint8_t serial_recive_packet(uint8_t *buffer, uint8_t size) {
264   uint8_t pecount = 0;
265   for (uint8_t i = 0; i < size; ++i) {
266     uint8_t data;
267     sync_recv();
268     data = serial_read_chunk(&pecount, 8);
269     buffer[i] = data;
270   }
271   return pecount == 0;
272 }
273
274 inline static
275 void change_sender2reciver(void) {
276     sync_send();          //0
277     serial_delay_half1(); //1
278     serial_low();         //2
279     serial_input_with_pullup(); //2
280     serial_delay_half1(); //3
281 }
282
283 inline static
284 void change_reciver2sender(void) {
285     sync_recv();     //0
286     serial_delay();  //1
287     serial_low();    //3
288     serial_output(); //3
289     serial_delay_half1(); //4
290 }
291
292 // interrupt handle to be used by the target device
293 ISR(SERIAL_PIN_INTERRUPT) {
294
295 #ifndef SERIAL_USE_MULTI_TRANSACTION
296   serial_low();
297   serial_output();
298   SSTD_t *trans = Transaction_table;
299 #else
300   // recive transaction table index
301   uint8_t tid;
302   uint8_t pecount = 0;
303   sync_recv();
304   tid = serial_read_chunk(&pecount,4);
305   if(pecount> 0)
306       return;
307   serial_delay_half1();
308
309   serial_high(); // response step1 low->high
310   serial_output();
311   _delay_sub_us(SLAVE_INT_ACK_WIDTH_UNIT*SLAVE_INT_ACK_WIDTH);
312   SSTD_t *trans = &Transaction_table[tid];
313   serial_low(); // response step2 ack high->low
314 #endif
315
316   // target send phase
317   if( trans->target2initiator_buffer_size > 0 )
318       serial_send_packet((uint8_t *)trans->target2initiator_buffer,
319                          trans->target2initiator_buffer_size);
320   // target switch to input
321   change_sender2reciver();
322
323   // target recive phase
324   if( trans->initiator2target_buffer_size > 0 ) {
325       if (serial_recive_packet((uint8_t *)trans->initiator2target_buffer,
326                                trans->initiator2target_buffer_size) ) {
327           *trans->status = TRANSACTION_ACCEPTED;
328       } else {
329           *trans->status = TRANSACTION_DATA_ERROR;
330       }
331   } else {
332       *trans->status = TRANSACTION_ACCEPTED;
333   }
334
335   sync_recv(); //weit initiator output to high
336 }
337
338 /////////
339 //  start transaction by initiator
340 //
341 // int  soft_serial_transaction(int sstd_index)
342 //
343 // Returns:
344 //    TRANSACTION_END
345 //    TRANSACTION_NO_RESPONSE
346 //    TRANSACTION_DATA_ERROR
347 // this code is very time dependent, so we need to disable interrupts
348 #ifndef SERIAL_USE_MULTI_TRANSACTION
349 int  soft_serial_transaction(void) {
350   SSTD_t *trans = Transaction_table;
351 #else
352 int  soft_serial_transaction(int sstd_index) {
353   SSTD_t *trans = &Transaction_table[sstd_index];
354 #endif
355   cli();
356
357   // signal to the target that we want to start a transaction
358   serial_output();
359   serial_low();
360   _delay_us(SLAVE_INT_WIDTH_US);
361
362 #ifndef SERIAL_USE_MULTI_TRANSACTION
363   // wait for the target response
364   serial_input_with_pullup();
365   _delay_us(SLAVE_INT_RESPONSE_TIME);
366
367   // check if the target is present
368   if (serial_read_pin()) {
369     // target failed to pull the line low, assume not present
370     serial_output();
371     serial_high();
372     *trans->status = TRANSACTION_NO_RESPONSE;
373     sei();
374     return TRANSACTION_NO_RESPONSE;
375   }
376
377 #else
378   // send transaction table index
379   sync_send();
380   _delay_sub_us(TID_SEND_ADJUST);
381   serial_write_chunk(sstd_index, 4);
382   serial_delay_half1();
383
384   // wait for the target response (step1 low->high)
385   serial_input_with_pullup();
386   while( !serial_read_pin() ) {
387       _delay_sub_us(2);
388   }
389
390   // check if the target is present (step2 high->low)
391   for( int i = 0; serial_read_pin(); i++ ) {
392       if (i > SLAVE_INT_ACK_WIDTH + 1) {
393           // slave failed to pull the line low, assume not present
394           serial_output();
395           serial_high();
396           *trans->status = TRANSACTION_NO_RESPONSE;
397           sei();
398           return TRANSACTION_NO_RESPONSE;
399       }
400       _delay_sub_us(SLAVE_INT_ACK_WIDTH_UNIT);
401   }
402 #endif
403
404   // initiator recive phase
405   // if the target is present syncronize with it
406   if( trans->target2initiator_buffer_size > 0 ) {
407       if (!serial_recive_packet((uint8_t *)trans->target2initiator_buffer,
408                                 trans->target2initiator_buffer_size) ) {
409           serial_output();
410           serial_high();
411           *trans->status = TRANSACTION_DATA_ERROR;
412           sei();
413           return TRANSACTION_DATA_ERROR;
414       }
415    }
416
417   // initiator switch to output
418   change_reciver2sender();
419
420   // initiator send phase
421   if( trans->initiator2target_buffer_size > 0 ) {
422       serial_send_packet((uint8_t *)trans->initiator2target_buffer,
423                          trans->initiator2target_buffer_size);
424   }
425
426   // always, release the line when not in use
427   sync_send();
428
429   *trans->status = TRANSACTION_END;
430   sei();
431   return TRANSACTION_END;
432 }
433
434 #ifdef SERIAL_USE_MULTI_TRANSACTION
435 int soft_serial_get_and_clean_status(int sstd_index) {
436     SSTD_t *trans = &Transaction_table[sstd_index];
437     cli();
438     int retval = *trans->status;
439     *trans->status = 0;;
440     sei();
441     return retval;
442 }
443 #endif
444
445 #endif