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