]> git.donarmstrong.com Git - qmk_firmware.git/blob - keyboards/helix/serial.c
Helix serial bugfix (#3255)
[qmk_firmware.git] / keyboards / helix / 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 <stdbool.h>
13 #include "serial.h"
14
15 #ifdef USE_SERIAL
16
17 #define _delay_sub_us(x)    __builtin_avr_delay_cycles(x)
18
19 // Serial pulse period in microseconds.
20 #define SELECT_SERIAL_SPEED 1
21 #if SELECT_SERIAL_SPEED == 0
22   // Very High speed
23   #define SERIAL_DELAY 4             // micro sec
24   #define READ_WRITE_START_ADJUST 30 // cycles
25   #define READ_WRITE_WIDTH_ADJUST 10 // cycles
26 #elif SELECT_SERIAL_SPEED == 1
27   // High speed
28   #define SERIAL_DELAY 6             // micro sec
29   #define READ_WRITE_START_ADJUST 23 // cycles
30   #define READ_WRITE_WIDTH_ADJUST 10 // cycles
31 #elif SELECT_SERIAL_SPEED == 2
32   // Middle speed
33   #define SERIAL_DELAY 12            // micro sec
34   #define READ_WRITE_START_ADJUST 25 // cycles
35   #define READ_WRITE_WIDTH_ADJUST 10 // cycles
36 #elif SELECT_SERIAL_SPEED == 3
37   // Low speed
38   #define SERIAL_DELAY 24            // micro sec
39   #define READ_WRITE_START_ADJUST 25 // cycles
40   #define READ_WRITE_WIDTH_ADJUST 10 // cycles
41 #elif SELECT_SERIAL_SPEED == 4
42   // Very Low speed
43   #define SERIAL_DELAY 50            // micro sec
44   #define READ_WRITE_START_ADJUST 25 // cycles
45   #define READ_WRITE_WIDTH_ADJUST 10 // cycles
46 #else
47 #error Illegal Serial Speed
48 #endif
49
50
51 #define SERIAL_DELAY_HALF1 (SERIAL_DELAY/2)
52 #define SERIAL_DELAY_HALF2 (SERIAL_DELAY - SERIAL_DELAY/2)
53
54 #define SLAVE_INT_WIDTH 1
55 #define SLAVE_INT_RESPONSE_TIME SERIAL_DELAY
56
57 uint8_t volatile serial_slave_buffer[SERIAL_SLAVE_BUFFER_LENGTH] = {0};
58 uint8_t volatile serial_master_buffer[SERIAL_MASTER_BUFFER_LENGTH] = {0};
59
60 #define SLAVE_DATA_CORRUPT (1<<0)
61 volatile uint8_t status = 0;
62
63 inline static
64 void serial_delay(void) {
65   _delay_us(SERIAL_DELAY);
66 }
67
68 inline static
69 void serial_delay_half1(void) {
70   _delay_us(SERIAL_DELAY_HALF1);
71 }
72
73 inline static
74 void serial_delay_half2(void) {
75   _delay_us(SERIAL_DELAY_HALF2);
76 }
77
78 inline static
79 void serial_output(void) {
80   SERIAL_PIN_DDR |= SERIAL_PIN_MASK;
81 }
82
83 // make the serial pin an input with pull-up resistor
84 inline static
85 void serial_input_with_pullup(void) {
86   SERIAL_PIN_DDR  &= ~SERIAL_PIN_MASK;
87   SERIAL_PIN_PORT |= SERIAL_PIN_MASK;
88 }
89
90 inline static
91 uint8_t serial_read_pin(void) {
92   return !!(SERIAL_PIN_INPUT & SERIAL_PIN_MASK);
93 }
94
95 inline static
96 void serial_low(void) {
97   SERIAL_PIN_PORT &= ~SERIAL_PIN_MASK;
98 }
99
100 inline static
101 void serial_high(void) {
102   SERIAL_PIN_PORT |= SERIAL_PIN_MASK;
103 }
104
105 void serial_master_init(void) {
106   serial_output();
107   serial_high();
108 }
109
110 void serial_slave_init(void) {
111   serial_input_with_pullup();
112
113 #ifndef USE_SERIAL_PD2
114   // Enable INT0
115   EIMSK |= _BV(INT0);
116   // Trigger on falling edge of INT0
117   EICRA &= ~(_BV(ISC00) | _BV(ISC01));
118 #else
119   // Enable INT2
120   EIMSK |= _BV(INT2);
121   // Trigger on falling edge of INT2
122   EICRA &= ~(_BV(ISC20) | _BV(ISC21));
123 #endif
124 }
125
126 // Used by the sender to synchronize timing with the reciver.
127 static
128 void sync_recv(void) {
129   for (int i = 0; i < SERIAL_DELAY*5 && serial_read_pin(); i++ ) {
130   }
131   // This shouldn't hang if the slave disconnects because the
132   // serial line will float to high if the slave does disconnect.
133   while (!serial_read_pin());
134 }
135
136 // Used by the reciver to send a synchronization signal to the sender.
137 static
138 void sync_send(void) {
139   serial_low();
140   serial_delay();
141   serial_high();
142 }
143
144 // Reads a byte from the serial line
145 static
146 uint8_t serial_read_byte(void) {
147   uint8_t byte = 0;
148   _delay_sub_us(READ_WRITE_START_ADJUST);
149   for ( uint8_t i = 0; i < 8; ++i) {
150     serial_delay_half1();   // read the middle of pulses
151     byte = (byte << 1) | serial_read_pin();
152     _delay_sub_us(READ_WRITE_WIDTH_ADJUST);
153     serial_delay_half2();
154   }
155   return byte;
156 }
157
158 // Sends a byte with MSB ordering
159 static
160 void serial_write_byte(uint8_t data) {
161   uint8_t b = 1<<7;
162   while( b ) {
163     if(data & b) {
164       serial_high();
165     } else {
166       serial_low();
167     }
168     b >>= 1;
169     serial_delay();
170   }
171   serial_low(); // sync_send() / senc_recv() need raise edge
172 }
173
174 // interrupt handle to be used by the slave device
175 ISR(SERIAL_PIN_INTERRUPT) {
176   serial_output();
177
178   // slave send phase
179   uint8_t checksum = 0;
180   for (int i = 0; i < SERIAL_SLAVE_BUFFER_LENGTH; ++i) {
181     sync_send();
182     serial_write_byte(serial_slave_buffer[i]);
183     checksum += serial_slave_buffer[i];
184   }
185   sync_send();
186   serial_write_byte(checksum);
187
188   // slave switch to input
189   sync_send(); //0
190   serial_delay_half1(); //1
191   serial_low();         //2
192   serial_input_with_pullup(); //2
193   serial_delay_half1(); //3
194
195   // slave recive phase
196   uint8_t checksum_computed = 0;
197   for (int i = 0; i < SERIAL_MASTER_BUFFER_LENGTH; ++i) {
198     sync_recv();
199     serial_master_buffer[i] = serial_read_byte();
200     checksum_computed += serial_master_buffer[i];
201   }
202   sync_recv();
203   uint8_t checksum_received = serial_read_byte();
204
205   if ( checksum_computed != checksum_received ) {
206     status |= SLAVE_DATA_CORRUPT;
207   } else {
208     status &= ~SLAVE_DATA_CORRUPT;
209   }
210
211   sync_recv(); //weit master output to high
212 }
213
214 inline
215 bool serial_slave_DATA_CORRUPT(void) {
216   return status & SLAVE_DATA_CORRUPT;
217 }
218
219 // Copies the serial_slave_buffer to the master and sends the
220 // serial_master_buffer to the slave.
221 //
222 // Returns:
223 // 0 => no error
224 // 1 => slave did not respond
225 // 2 => checksum error
226 int serial_update_buffers(void) {
227   // this code is very time dependent, so we need to disable interrupts
228   cli();
229
230   // signal to the slave that we want to start a transaction
231   serial_output();
232   serial_low();
233   _delay_us(SLAVE_INT_WIDTH);
234
235   // wait for the slaves response
236   serial_input_with_pullup();
237   _delay_us(SLAVE_INT_RESPONSE_TIME);
238
239   // check if the slave is present
240   if (serial_read_pin()) {
241     // slave failed to pull the line low, assume not present
242     serial_output();
243     serial_high();
244     sei();
245     return 1;
246   }
247
248   // master recive phase
249   // if the slave is present syncronize with it
250
251   uint8_t checksum_computed = 0;
252   // receive data from the slave
253   for (int i = 0; i < SERIAL_SLAVE_BUFFER_LENGTH; ++i) {
254     sync_recv();
255     serial_slave_buffer[i] = serial_read_byte();
256     checksum_computed += serial_slave_buffer[i];
257   }
258   sync_recv();
259   uint8_t checksum_received = serial_read_byte();
260
261   if (checksum_computed != checksum_received) {
262     serial_output();
263     serial_high();
264     sei();
265     return 2;
266   }
267
268   // master switch to output
269   sync_recv(); //0
270   serial_delay();  //1
271   serial_low();    //3
272   serial_output(); // 3
273   serial_delay_half1(); //4
274
275   // master send phase
276   uint8_t checksum = 0;
277
278   for (int i = 0; i < SERIAL_MASTER_BUFFER_LENGTH; ++i) {
279     sync_send();
280     serial_write_byte(serial_master_buffer[i]);
281     checksum += serial_master_buffer[i];
282   }
283   sync_send();
284   serial_write_byte(checksum);
285
286   // always, release the line when not in use
287   sync_send();
288
289   sei();
290   return 0;
291 }
292
293 #endif