]> git.donarmstrong.com Git - qmk_firmware.git/blob - quantum/split_common/serial.c
13e58d825904143d6cda2d329e6d2b93affbae4b
[qmk_firmware.git] / quantum / split_common / 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 SOFT_SERIAL_PIN
16
17 #ifdef __AVR_ATmega32U4__
18   // if using ATmega32U4 I2C, can not use PD0 and PD1 in soft serial.
19   #ifdef USE_AVR_I2C
20      #if SOFT_SERIAL_PIN == D0 || SOFT_SERIAL_PIN == D1
21        #error Using ATmega32U4 I2C, so can not use PD0, PD1
22      #endif
23   #endif
24
25   #if SOFT_SERIAL_PIN >= D0 && SOFT_SERIAL_PIN <= D3
26     #define SERIAL_PIN_DDR   DDRD
27     #define SERIAL_PIN_PORT  PORTD
28     #define SERIAL_PIN_INPUT PIND
29     #if SOFT_SERIAL_PIN == D0
30       #define SERIAL_PIN_MASK _BV(PD0)
31       #define EIMSK_BIT       _BV(INT0)
32       #define EICRx_BIT       (~(_BV(ISC00) | _BV(ISC01)))
33       #define SERIAL_PIN_INTERRUPT INT0_vect
34     #elif  SOFT_SERIAL_PIN == D1
35       #define SERIAL_PIN_MASK _BV(PD1)
36       #define EIMSK_BIT       _BV(INT1)
37       #define EICRx_BIT       (~(_BV(ISC10) | _BV(ISC11)))
38       #define SERIAL_PIN_INTERRUPT INT1_vect
39     #elif  SOFT_SERIAL_PIN == D2
40       #define SERIAL_PIN_MASK _BV(PD2)
41       #define EIMSK_BIT       _BV(INT2)
42       #define EICRx_BIT       (~(_BV(ISC20) | _BV(ISC21)))
43       #define SERIAL_PIN_INTERRUPT INT2_vect
44     #elif  SOFT_SERIAL_PIN == D3
45       #define SERIAL_PIN_MASK _BV(PD3)
46       #define EIMSK_BIT       _BV(INT3)
47       #define EICRx_BIT       (~(_BV(ISC30) | _BV(ISC31)))
48       #define SERIAL_PIN_INTERRUPT INT3_vect
49     #endif
50   #elif  SOFT_SERIAL_PIN == E6
51     #define SERIAL_PIN_DDR   DDRE
52     #define SERIAL_PIN_PORT  PORTE
53     #define SERIAL_PIN_INPUT PINE
54     #define SERIAL_PIN_MASK  _BV(PE6)
55     #define EIMSK_BIT        _BV(INT6)
56     #define EICRx_BIT        (~(_BV(ISC60) | _BV(ISC61)))
57     #define SERIAL_PIN_INTERRUPT INT6_vect
58   #else
59   #error invalid SOFT_SERIAL_PIN value
60   #endif
61
62 #else
63  #error serial.c now support ATmega32U4 only
64 #endif
65
66 // Serial pulse period in microseconds. Its probably a bad idea to lower this
67 // value.
68 #define SERIAL_DELAY 24
69
70 uint8_t volatile serial_slave_buffer[SERIAL_SLAVE_BUFFER_LENGTH] = {0};
71 uint8_t volatile serial_master_buffer[SERIAL_MASTER_BUFFER_LENGTH] = {0};
72
73 #define SLAVE_DATA_CORRUPT (1<<0)
74 volatile uint8_t status = 0;
75
76 inline static
77 void serial_delay(void) {
78   _delay_us(SERIAL_DELAY);
79 }
80
81 inline static
82 void serial_output(void) {
83   SERIAL_PIN_DDR |= SERIAL_PIN_MASK;
84 }
85
86 // make the serial pin an input with pull-up resistor
87 inline static
88 void serial_input(void) {
89   SERIAL_PIN_DDR  &= ~SERIAL_PIN_MASK;
90   SERIAL_PIN_PORT |= SERIAL_PIN_MASK;
91 }
92
93 inline static
94 uint8_t serial_read_pin(void) {
95   return !!(SERIAL_PIN_INPUT & SERIAL_PIN_MASK);
96 }
97
98 inline static
99 void serial_low(void) {
100   SERIAL_PIN_PORT &= ~SERIAL_PIN_MASK;
101 }
102
103 inline static
104 void serial_high(void) {
105   SERIAL_PIN_PORT |= SERIAL_PIN_MASK;
106 }
107
108 void serial_master_init(void) {
109   serial_output();
110   serial_high();
111 }
112
113 void serial_slave_init(void) {
114   serial_input();
115
116   // Enable INT0
117   EIMSK |= _BV(INT0);
118   // Trigger on falling edge of INT0
119   EICRA &= ~(_BV(ISC00) | _BV(ISC01));
120 }
121
122 // Used by the master to synchronize timing with the slave.
123 static
124 void sync_recv(void) {
125   serial_input();
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   serial_delay();
130 }
131
132 // Used by the slave to send a synchronization signal to the master.
133 static
134 void sync_send(void) {
135   serial_output();
136
137   serial_low();
138   serial_delay();
139
140   serial_high();
141 }
142
143 // Reads a byte from the serial line
144 static
145 uint8_t serial_read_byte(void) {
146   uint8_t byte = 0;
147   serial_input();
148   for ( uint8_t i = 0; i < 8; ++i) {
149     byte = (byte << 1) | serial_read_pin();
150     serial_delay();
151     _delay_us(1);
152   }
153
154   return byte;
155 }
156
157 // Sends a byte with MSB ordering
158 static
159 void serial_write_byte(uint8_t data) {
160   uint8_t b = 8;
161   serial_output();
162   while( b-- ) {
163     if(data & (1 << b)) {
164       serial_high();
165     } else {
166       serial_low();
167     }
168     serial_delay();
169   }
170 }
171
172 // interrupt handle to be used by the slave device
173 ISR(SERIAL_PIN_INTERRUPT) {
174   sync_send();
175
176   uint8_t checksum = 0;
177   for (int i = 0; i < SERIAL_SLAVE_BUFFER_LENGTH; ++i) {
178     serial_write_byte(serial_slave_buffer[i]);
179     sync_send();
180     checksum += serial_slave_buffer[i];
181   }
182   serial_write_byte(checksum);
183   sync_send();
184
185   // wait for the sync to finish sending
186   serial_delay();
187
188   // read the middle of pulses
189   _delay_us(SERIAL_DELAY/2);
190
191   uint8_t checksum_computed = 0;
192   for (int i = 0; i < SERIAL_MASTER_BUFFER_LENGTH; ++i) {
193     serial_master_buffer[i] = serial_read_byte();
194     sync_send();
195     checksum_computed += serial_master_buffer[i];
196   }
197   uint8_t checksum_received = serial_read_byte();
198   sync_send();
199
200   serial_input(); // end transaction
201
202   if ( checksum_computed != checksum_received ) {
203     status |= SLAVE_DATA_CORRUPT;
204   } else {
205     status &= ~SLAVE_DATA_CORRUPT;
206   }
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 int serial_update_buffers(void) {
221   // this code is very time dependent, so we need to disable interrupts
222   cli();
223
224   // signal to the slave that we want to start a transaction
225   serial_output();
226   serial_low();
227   _delay_us(1);
228
229   // wait for the slaves response
230   serial_input();
231   serial_high();
232   _delay_us(SERIAL_DELAY);
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     sei();
238     return 1;
239   }
240
241   // if the slave is present syncronize with it
242   sync_recv();
243
244   uint8_t checksum_computed = 0;
245   // receive data from the slave
246   for (int i = 0; i < SERIAL_SLAVE_BUFFER_LENGTH; ++i) {
247     serial_slave_buffer[i] = serial_read_byte();
248     sync_recv();
249     checksum_computed += serial_slave_buffer[i];
250   }
251   uint8_t checksum_received = serial_read_byte();
252   sync_recv();
253
254   if (checksum_computed != checksum_received) {
255     sei();
256     return 1;
257   }
258
259   uint8_t checksum = 0;
260   // send data to the slave
261   for (int i = 0; i < SERIAL_MASTER_BUFFER_LENGTH; ++i) {
262     serial_write_byte(serial_master_buffer[i]);
263     sync_recv();
264     checksum += serial_master_buffer[i];
265   }
266   serial_write_byte(checksum);
267   sync_recv();
268
269   // always, release the line when not in use
270   serial_output();
271   serial_high();
272
273   sei();
274   return 0;
275 }
276
277 #endif /* SOFT_SERIAL_PIN */