2 * WARNING: be careful changing this code, it is very timing dependent
10 #include <avr/interrupt.h>
11 #include <util/delay.h>
17 #ifndef SOFT_SERIAL_PIN
18 #error quantum/split_common/serial.c need SOFT_SERIAL_PIN define
21 #ifdef __AVR_ATmega32U4__
22 // if using ATmega32U4 I2C, can not use PD0 and PD1 in soft serial.
24 #if SOFT_SERIAL_PIN == D0 || SOFT_SERIAL_PIN == D1
25 #error Using ATmega32U4 I2C, so can not use PD0, PD1
29 #if SOFT_SERIAL_PIN >= D0 && SOFT_SERIAL_PIN <= D3
30 #define SERIAL_PIN_DDR DDRD
31 #define SERIAL_PIN_PORT PORTD
32 #define SERIAL_PIN_INPUT PIND
33 #if SOFT_SERIAL_PIN == D0
34 #define SERIAL_PIN_MASK _BV(PD0)
35 #define EIMSK_BIT _BV(INT0)
36 #define EICRx_BIT (~(_BV(ISC00) | _BV(ISC01)))
37 #define SERIAL_PIN_INTERRUPT INT0_vect
38 #elif SOFT_SERIAL_PIN == D1
39 #define SERIAL_PIN_MASK _BV(PD1)
40 #define EIMSK_BIT _BV(INT1)
41 #define EICRx_BIT (~(_BV(ISC10) | _BV(ISC11)))
42 #define SERIAL_PIN_INTERRUPT INT1_vect
43 #elif SOFT_SERIAL_PIN == D2
44 #define SERIAL_PIN_MASK _BV(PD2)
45 #define EIMSK_BIT _BV(INT2)
46 #define EICRx_BIT (~(_BV(ISC20) | _BV(ISC21)))
47 #define SERIAL_PIN_INTERRUPT INT2_vect
48 #elif SOFT_SERIAL_PIN == D3
49 #define SERIAL_PIN_MASK _BV(PD3)
50 #define EIMSK_BIT _BV(INT3)
51 #define EICRx_BIT (~(_BV(ISC30) | _BV(ISC31)))
52 #define SERIAL_PIN_INTERRUPT INT3_vect
54 #elif SOFT_SERIAL_PIN == E6
55 #define SERIAL_PIN_DDR DDRE
56 #define SERIAL_PIN_PORT PORTE
57 #define SERIAL_PIN_INPUT PINE
58 #define SERIAL_PIN_MASK _BV(PE6)
59 #define EIMSK_BIT _BV(INT6)
60 #define EICRx_BIT (~(_BV(ISC60) | _BV(ISC61)))
61 #define SERIAL_PIN_INTERRUPT INT6_vect
63 #error invalid SOFT_SERIAL_PIN value
67 #error serial.c now support ATmega32U4 only
70 // Serial pulse period in microseconds. Its probably a bad idea to lower this
72 #define SERIAL_DELAY 24
74 uint8_t volatile serial_slave_buffer[SERIAL_SLAVE_BUFFER_LENGTH] = {0};
75 uint8_t volatile serial_master_buffer[SERIAL_MASTER_BUFFER_LENGTH] = {0};
77 #define SLAVE_DATA_CORRUPT (1<<0)
78 volatile uint8_t status = 0;
81 void serial_delay(void) {
82 _delay_us(SERIAL_DELAY);
86 void serial_output(void) {
87 SERIAL_PIN_DDR |= SERIAL_PIN_MASK;
90 // make the serial pin an input with pull-up resistor
92 void serial_input(void) {
93 SERIAL_PIN_DDR &= ~SERIAL_PIN_MASK;
94 SERIAL_PIN_PORT |= SERIAL_PIN_MASK;
98 uint8_t serial_read_pin(void) {
99 return !!(SERIAL_PIN_INPUT & SERIAL_PIN_MASK);
103 void serial_low(void) {
104 SERIAL_PIN_PORT &= ~SERIAL_PIN_MASK;
108 void serial_high(void) {
109 SERIAL_PIN_PORT |= SERIAL_PIN_MASK;
112 void serial_master_init(void) {
117 void serial_slave_init(void) {
122 // Trigger on falling edge of INT0
123 EICRA &= ~(_BV(ISC00) | _BV(ISC01));
126 // Used by the master to synchronize timing with the slave.
128 void sync_recv(void) {
130 // This shouldn't hang if the slave disconnects because the
131 // serial line will float to high if the slave does disconnect.
132 while (!serial_read_pin());
136 // Used by the slave to send a synchronization signal to the master.
138 void sync_send(void) {
147 // Reads a byte from the serial line
149 uint8_t serial_read_byte(void) {
152 for ( uint8_t i = 0; i < 8; ++i) {
153 byte = (byte << 1) | serial_read_pin();
161 // Sends a byte with MSB ordering
163 void serial_write_byte(uint8_t data) {
167 if(data & (1 << b)) {
176 // interrupt handle to be used by the slave device
177 ISR(SERIAL_PIN_INTERRUPT) {
180 uint8_t checksum = 0;
181 for (int i = 0; i < SERIAL_SLAVE_BUFFER_LENGTH; ++i) {
182 serial_write_byte(serial_slave_buffer[i]);
184 checksum += serial_slave_buffer[i];
186 serial_write_byte(checksum);
189 // wait for the sync to finish sending
192 // read the middle of pulses
193 _delay_us(SERIAL_DELAY/2);
195 uint8_t checksum_computed = 0;
196 for (int i = 0; i < SERIAL_MASTER_BUFFER_LENGTH; ++i) {
197 serial_master_buffer[i] = serial_read_byte();
199 checksum_computed += serial_master_buffer[i];
201 uint8_t checksum_received = serial_read_byte();
204 serial_input(); // end transaction
206 if ( checksum_computed != checksum_received ) {
207 status |= SLAVE_DATA_CORRUPT;
209 status &= ~SLAVE_DATA_CORRUPT;
214 bool serial_slave_DATA_CORRUPT(void) {
215 return status & SLAVE_DATA_CORRUPT;
218 // Copies the serial_slave_buffer to the master and sends the
219 // serial_master_buffer to the slave.
223 // 1 => slave did not respond
224 int serial_update_buffers(void) {
225 // this code is very time dependent, so we need to disable interrupts
228 // signal to the slave that we want to start a transaction
233 // wait for the slaves response
236 _delay_us(SERIAL_DELAY);
238 // check if the slave is present
239 if (serial_read_pin()) {
240 // slave failed to pull the line low, assume not present
245 // if the slave is present syncronize with it
248 uint8_t checksum_computed = 0;
249 // receive data from the slave
250 for (int i = 0; i < SERIAL_SLAVE_BUFFER_LENGTH; ++i) {
251 serial_slave_buffer[i] = serial_read_byte();
253 checksum_computed += serial_slave_buffer[i];
255 uint8_t checksum_received = serial_read_byte();
258 if (checksum_computed != checksum_received) {
263 uint8_t checksum = 0;
264 // send data to the slave
265 for (int i = 0; i < SERIAL_MASTER_BUFFER_LENGTH; ++i) {
266 serial_write_byte(serial_master_buffer[i]);
268 checksum += serial_master_buffer[i];
270 serial_write_byte(checksum);
273 // always, release the line when not in use