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