2 * WARNING: be careful changing this code, it is very timing dependent
10 #include <avr/interrupt.h>
11 #include <util/delay.h>
15 #ifdef SOFT_SERIAL_PIN
17 #ifdef __AVR_ATmega32U4__
18 // if using ATmega32U4 I2C, can not use PD0 and PD1 in soft serial.
20 #if SOFT_SERIAL_PIN == D0 || SOFT_SERIAL_PIN == D1
21 #error Using ATmega32U4 I2C, so can not use PD0, PD1
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
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
59 #error invalid SOFT_SERIAL_PIN value
63 #error serial.c now support ATmega32U4 only
66 // Serial pulse period in microseconds. Its probably a bad idea to lower this
68 #define SERIAL_DELAY 24
70 uint8_t volatile serial_slave_buffer[SERIAL_SLAVE_BUFFER_LENGTH] = {0};
71 uint8_t volatile serial_master_buffer[SERIAL_MASTER_BUFFER_LENGTH] = {0};
73 #define SLAVE_DATA_CORRUPT (1<<0)
74 volatile uint8_t status = 0;
77 void serial_delay(void) {
78 _delay_us(SERIAL_DELAY);
82 void serial_output(void) {
83 SERIAL_PIN_DDR |= SERIAL_PIN_MASK;
86 // make the serial pin an input with pull-up resistor
88 void serial_input(void) {
89 SERIAL_PIN_DDR &= ~SERIAL_PIN_MASK;
90 SERIAL_PIN_PORT |= SERIAL_PIN_MASK;
94 uint8_t serial_read_pin(void) {
95 return !!(SERIAL_PIN_INPUT & SERIAL_PIN_MASK);
99 void serial_low(void) {
100 SERIAL_PIN_PORT &= ~SERIAL_PIN_MASK;
104 void serial_high(void) {
105 SERIAL_PIN_PORT |= SERIAL_PIN_MASK;
108 void serial_master_init(void) {
113 void serial_slave_init(void) {
118 // Trigger on falling edge of INT0
119 EICRA &= ~(_BV(ISC00) | _BV(ISC01));
122 // Used by the master to synchronize timing with the slave.
124 void sync_recv(void) {
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());
132 // Used by the slave to send a synchronization signal to the master.
134 void sync_send(void) {
143 // Reads a byte from the serial line
145 uint8_t serial_read_byte(void) {
148 for ( uint8_t i = 0; i < 8; ++i) {
149 byte = (byte << 1) | serial_read_pin();
157 // Sends a byte with MSB ordering
159 void serial_write_byte(uint8_t data) {
163 if(data & (1 << b)) {
172 // interrupt handle to be used by the slave device
173 ISR(SERIAL_PIN_INTERRUPT) {
176 uint8_t checksum = 0;
177 for (int i = 0; i < SERIAL_SLAVE_BUFFER_LENGTH; ++i) {
178 serial_write_byte(serial_slave_buffer[i]);
180 checksum += serial_slave_buffer[i];
182 serial_write_byte(checksum);
185 // wait for the sync to finish sending
188 // read the middle of pulses
189 _delay_us(SERIAL_DELAY/2);
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();
195 checksum_computed += serial_master_buffer[i];
197 uint8_t checksum_received = serial_read_byte();
200 serial_input(); // end transaction
202 if ( checksum_computed != checksum_received ) {
203 status |= SLAVE_DATA_CORRUPT;
205 status &= ~SLAVE_DATA_CORRUPT;
210 bool serial_slave_DATA_CORRUPT(void) {
211 return status & SLAVE_DATA_CORRUPT;
214 // Copies the serial_slave_buffer to the master and sends the
215 // serial_master_buffer to the slave.
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
224 // signal to the slave that we want to start a transaction
229 // wait for the slaves response
232 _delay_us(SERIAL_DELAY);
234 // check if the slave is present
235 if (serial_read_pin()) {
236 // slave failed to pull the line low, assume not present
241 // if the slave is present syncronize with it
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();
249 checksum_computed += serial_slave_buffer[i];
251 uint8_t checksum_received = serial_read_byte();
254 if (checksum_computed != checksum_received) {
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]);
264 checksum += serial_master_buffer[i];
266 serial_write_byte(checksum);
269 // always, release the line when not in use
277 #endif /* SOFT_SERIAL_PIN */