serial.c 4.9 KB

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