serial.c 4.9 KB

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