]> git.donarmstrong.com Git - qmk_firmware.git/blob - keyboards/ergoinu/serial.c
Remove more commented out MCUs
[qmk_firmware.git] / keyboards / ergoinu / 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 #if SERIAL_PIN_MASK == _BV(PD0)
114   // Enable INT0
115   EIMSK |= _BV(INT0);
116   // Trigger on falling edge of INT0
117   EICRA &= ~(_BV(ISC00) | _BV(ISC01));
118 #elif SERIAL_PIN_MASK == _BV(PD2)
119   // Enable INT2
120   EIMSK |= _BV(INT2);
121   // Trigger on falling edge of INT2
122   EICRA &= ~(_BV(ISC20) | _BV(ISC21));
123 #else
124  #error unknown SERIAL_PIN_MASK value
125 #endif
126 }
127
128 // Used by the sender to synchronize timing with the reciver.
129 static
130 void sync_recv(void) {
131   for (int i = 0; i < SERIAL_DELAY*5 && serial_read_pin(); i++ ) {
132   }
133   // This shouldn't hang if the slave disconnects because the
134   // serial line will float to high if the slave does disconnect.
135   while (!serial_read_pin());
136 }
137
138 // Used by the reciver to send a synchronization signal to the sender.
139 static
140 void sync_send(void) {
141   serial_low();
142   serial_delay();
143   serial_high();
144 }
145
146 // Reads a byte from the serial line
147 static
148 uint8_t serial_read_byte(void) {
149   uint8_t byte = 0;
150   _delay_sub_us(READ_WRITE_START_ADJUST);
151   for ( uint8_t i = 0; i < 8; ++i) {
152     serial_delay_half1();   // read the middle of pulses
153     byte = (byte << 1) | serial_read_pin();
154     _delay_sub_us(READ_WRITE_WIDTH_ADJUST);
155     serial_delay_half2();
156   }
157   return byte;
158 }
159
160 // Sends a byte with MSB ordering
161 static
162 void serial_write_byte(uint8_t data) {
163   uint8_t b = 1<<7;
164   while( b ) {
165     if(data & b) {
166       serial_high();
167     } else {
168       serial_low();
169     }
170     b >>= 1;
171     serial_delay();
172   }
173   serial_low(); // sync_send() / senc_recv() need raise edge
174 }
175
176 // interrupt handle to be used by the slave device
177 ISR(SERIAL_PIN_INTERRUPT) {
178   serial_output();
179
180   // slave send phase
181   uint8_t checksum = 0;
182   for (int i = 0; i < SERIAL_SLAVE_BUFFER_LENGTH; ++i) {
183     sync_send();
184     serial_write_byte(serial_slave_buffer[i]);
185     checksum += serial_slave_buffer[i];
186   }
187   sync_send();
188   serial_write_byte(checksum);
189
190   // slave switch to input
191   sync_send(); //0
192   serial_delay_half1(); //1
193   serial_low();         //2
194   serial_input_with_pullup(); //2
195   serial_delay_half1(); //3
196
197   // slave recive phase
198   uint8_t checksum_computed = 0;
199   for (int i = 0; i < SERIAL_MASTER_BUFFER_LENGTH; ++i) {
200     sync_recv();
201     serial_master_buffer[i] = serial_read_byte();
202     checksum_computed += serial_master_buffer[i];
203   }
204   sync_recv();
205   uint8_t checksum_received = serial_read_byte();
206
207   if ( checksum_computed != checksum_received ) {
208     status |= SLAVE_DATA_CORRUPT;
209   } else {
210     status &= ~SLAVE_DATA_CORRUPT;
211   }
212
213   sync_recv(); //weit master output to high
214 }
215
216 inline
217 bool serial_slave_DATA_CORRUPT(void) {
218   return status & SLAVE_DATA_CORRUPT;
219 }
220
221 // Copies the serial_slave_buffer to the master and sends the
222 // serial_master_buffer to the slave.
223 //
224 // Returns:
225 // 0 => no error
226 // 1 => slave did not respond
227 // 2 => checksum error
228 int serial_update_buffers(void) {
229   // this code is very time dependent, so we need to disable interrupts
230   cli();
231
232   // signal to the slave that we want to start a transaction
233   serial_output();
234   serial_low();
235   _delay_us(SLAVE_INT_WIDTH);
236
237   // wait for the slaves response
238   serial_input_with_pullup();
239   _delay_us(SLAVE_INT_RESPONSE_TIME);
240
241   // check if the slave is present
242   if (serial_read_pin()) {
243     // slave failed to pull the line low, assume not present
244     serial_output();
245     serial_high();
246     sei();
247     return 1;
248   }
249
250   // master recive phase
251   // if the slave is present syncronize with it
252
253   uint8_t checksum_computed = 0;
254   // receive data from the slave
255   for (int i = 0; i < SERIAL_SLAVE_BUFFER_LENGTH; ++i) {
256     sync_recv();
257     serial_slave_buffer[i] = serial_read_byte();
258     checksum_computed += serial_slave_buffer[i];
259   }
260   sync_recv();
261   uint8_t checksum_received = serial_read_byte();
262
263   if (checksum_computed != checksum_received) {
264     serial_output();
265     serial_high();
266     sei();
267     return 2;
268   }
269
270   // master switch to output
271   sync_recv(); //0
272   serial_delay();  //1
273   serial_low();    //3
274   serial_output(); // 3
275   serial_delay_half1(); //4
276
277   // master send phase
278   uint8_t checksum = 0;
279
280   for (int i = 0; i < SERIAL_MASTER_BUFFER_LENGTH; ++i) {
281     sync_send();
282     serial_write_byte(serial_master_buffer[i]);
283     checksum += serial_master_buffer[i];
284   }
285   sync_send();
286   serial_write_byte(checksum);
287
288   // always, release the line when not in use
289   sync_send();
290
291   sei();
292   return 0;
293 }
294
295 #endif