serial.c 4.8 KB

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