]> git.donarmstrong.com Git - qmk_firmware.git/blob - keyboards/deltasplit75/serial.c
Updated my makefile
[qmk_firmware.git] / keyboards / deltasplit75 / serial.c
1 /*\r
2  * WARNING: be careful changing this code, it is very timing dependent\r
3  */\r
4 \r
5 #ifndef F_CPU\r
6 #define F_CPU 16000000\r
7 #endif\r
8 \r
9 #include <avr/io.h>\r
10 #include <avr/interrupt.h>\r
11 #include <util/delay.h>\r
12 #include <stdbool.h>\r
13 #include "serial.h"\r
14 \r
15 #ifdef USE_SERIAL\r
16 \r
17 // Serial pulse period in microseconds. Its probably a bad idea to lower this\r
18 // value.\r
19 #define SERIAL_DELAY 24\r
20 \r
21 uint8_t volatile serial_slave_buffer[SERIAL_SLAVE_BUFFER_LENGTH] = {0};\r
22 uint8_t volatile serial_master_buffer[SERIAL_MASTER_BUFFER_LENGTH] = {0};\r
23 \r
24 #define SLAVE_DATA_CORRUPT (1<<0)\r
25 volatile uint8_t status = 0;\r
26 \r
27 inline static\r
28 void serial_delay(void) {\r
29   _delay_us(SERIAL_DELAY);\r
30 }\r
31 \r
32 inline static\r
33 void serial_output(void) {\r
34   SERIAL_PIN_DDR |= SERIAL_PIN_MASK;\r
35 }\r
36 \r
37 // make the serial pin an input with pull-up resistor\r
38 inline static\r
39 void serial_input(void) {\r
40   SERIAL_PIN_DDR  &= ~SERIAL_PIN_MASK;\r
41   SERIAL_PIN_PORT |= SERIAL_PIN_MASK;\r
42 }\r
43 \r
44 inline static\r
45 uint8_t serial_read_pin(void) {\r
46   return !!(SERIAL_PIN_INPUT & SERIAL_PIN_MASK);\r
47 }\r
48 \r
49 inline static\r
50 void serial_low(void) {\r
51   SERIAL_PIN_PORT &= ~SERIAL_PIN_MASK;\r
52 }\r
53 \r
54 inline static\r
55 void serial_high(void) {\r
56   SERIAL_PIN_PORT |= SERIAL_PIN_MASK;\r
57 }\r
58 \r
59 void serial_master_init(void) {\r
60   serial_output();\r
61   serial_high();\r
62 }\r
63 \r
64 void serial_slave_init(void) {\r
65   serial_input();\r
66 \r
67   // Enable INT0\r
68   EIMSK |= _BV(INT0);\r
69   // Trigger on falling edge of INT0\r
70   EICRA &= ~(_BV(ISC00) | _BV(ISC01));\r
71 }\r
72 \r
73 // Used by the master to synchronize timing with the slave.\r
74 static\r
75 void sync_recv(void) {\r
76   serial_input();\r
77   // This shouldn't hang if the slave disconnects because the\r
78   // serial line will float to high if the slave does disconnect.\r
79   while (!serial_read_pin());\r
80   serial_delay();\r
81 }\r
82 \r
83 // Used by the slave to send a synchronization signal to the master.\r
84 static\r
85 void sync_send(void) {\r
86   serial_output();\r
87 \r
88   serial_low();\r
89   serial_delay();\r
90 \r
91   serial_high();\r
92 }\r
93 \r
94 // Reads a byte from the serial line\r
95 static\r
96 uint8_t serial_read_byte(void) {\r
97   uint8_t byte = 0;\r
98   serial_input();\r
99   for ( uint8_t i = 0; i < 8; ++i) {\r
100     byte = (byte << 1) | serial_read_pin();\r
101     serial_delay();\r
102     _delay_us(1);\r
103   }\r
104 \r
105   return byte;\r
106 }\r
107 \r
108 // Sends a byte with MSB ordering\r
109 static\r
110 void serial_write_byte(uint8_t data) {\r
111   uint8_t b = 8;\r
112   serial_output();\r
113   while( b-- ) {\r
114     if(data & (1 << b)) {\r
115       serial_high();\r
116     } else {\r
117       serial_low();\r
118     }\r
119     serial_delay();\r
120   }\r
121 }\r
122 \r
123 // interrupt handle to be used by the slave device\r
124 ISR(SERIAL_PIN_INTERRUPT) {\r
125   sync_send();\r
126 \r
127   uint8_t checksum = 0;\r
128   for (int i = 0; i < SERIAL_SLAVE_BUFFER_LENGTH; ++i) {\r
129     serial_write_byte(serial_slave_buffer[i]);\r
130     sync_send();\r
131     checksum += serial_slave_buffer[i];\r
132   }\r
133   serial_write_byte(checksum);\r
134   sync_send();\r
135 \r
136   // wait for the sync to finish sending\r
137   serial_delay();\r
138 \r
139   // read the middle of pulses\r
140   _delay_us(SERIAL_DELAY/2);\r
141 \r
142   uint8_t checksum_computed = 0;\r
143   for (int i = 0; i < SERIAL_MASTER_BUFFER_LENGTH; ++i) {\r
144     serial_master_buffer[i] = serial_read_byte();\r
145     sync_send();\r
146     checksum_computed += serial_master_buffer[i];\r
147   }\r
148   uint8_t checksum_received = serial_read_byte();\r
149   sync_send();\r
150 \r
151   serial_input(); // end transaction\r
152 \r
153   if ( checksum_computed != checksum_received ) {\r
154     status |= SLAVE_DATA_CORRUPT;\r
155   } else {\r
156     status &= ~SLAVE_DATA_CORRUPT;\r
157   }\r
158 }\r
159 \r
160 inline\r
161 bool serial_slave_DATA_CORRUPT(void) {\r
162   return status & SLAVE_DATA_CORRUPT;\r
163 }\r
164 \r
165 // Copies the serial_slave_buffer to the master and sends the\r
166 // serial_master_buffer to the slave.\r
167 //\r
168 // Returns:\r
169 // 0 => no error\r
170 // 1 => slave did not respond\r
171 int serial_update_buffers(void) {\r
172   // this code is very time dependent, so we need to disable interrupts\r
173   cli();\r
174 \r
175   // signal to the slave that we want to start a transaction\r
176   serial_output();\r
177   serial_low();\r
178   _delay_us(1);\r
179 \r
180   // wait for the slaves response\r
181   serial_input();\r
182   serial_high();\r
183   _delay_us(SERIAL_DELAY);\r
184 \r
185   // check if the slave is present\r
186   if (serial_read_pin()) {\r
187     // slave failed to pull the line low, assume not present\r
188     sei();\r
189     return 1;\r
190   }\r
191 \r
192   // if the slave is present syncronize with it\r
193   sync_recv();\r
194 \r
195   uint8_t checksum_computed = 0;\r
196   // receive data from the slave\r
197   for (int i = 0; i < SERIAL_SLAVE_BUFFER_LENGTH; ++i) {\r
198     serial_slave_buffer[i] = serial_read_byte();\r
199     sync_recv();\r
200     checksum_computed += serial_slave_buffer[i];\r
201   }\r
202   uint8_t checksum_received = serial_read_byte();\r
203   sync_recv();\r
204 \r
205   if (checksum_computed != checksum_received) {\r
206     sei();\r
207     return 1;\r
208   }\r
209 \r
210   uint8_t checksum = 0;\r
211   // send data to the slave\r
212   for (int i = 0; i < SERIAL_MASTER_BUFFER_LENGTH; ++i) {\r
213     serial_write_byte(serial_master_buffer[i]);\r
214     sync_recv();\r
215     checksum += serial_master_buffer[i];\r
216   }\r
217   serial_write_byte(checksum);\r
218   sync_recv();\r
219 \r
220   // always, release the line when not in use\r
221   serial_output();\r
222   serial_high();\r
223 \r
224   sei();\r
225   return 0;\r
226 }\r
227 \r
228 #endif\r