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