serial.c 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  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. #ifndef SOFT_SERIAL_PIN
  14. #error quantum/split_common/serial.c need SOFT_SERIAL_PIN define
  15. #endif
  16. #ifdef __AVR_ATmega32U4__
  17. // if using ATmega32U4 I2C, can not use PD0 and PD1 in soft serial.
  18. #ifdef USE_I2C
  19. #if SOFT_SERIAL_PIN == D0 || SOFT_SERIAL_PIN == D1
  20. #error Using ATmega32U4 I2C, so can not use PD0, PD1
  21. #endif
  22. #endif
  23. #if SOFT_SERIAL_PIN >= D0 && SOFT_SERIAL_PIN <= D3
  24. #define SERIAL_PIN_DDR DDRD
  25. #define SERIAL_PIN_PORT PORTD
  26. #define SERIAL_PIN_INPUT PIND
  27. #if SOFT_SERIAL_PIN == D0
  28. #define SERIAL_PIN_MASK _BV(PD0)
  29. #define EIMSK_BIT _BV(INT0)
  30. #define EICRx_BIT (~(_BV(ISC00) | _BV(ISC01)))
  31. #define SERIAL_PIN_INTERRUPT INT0_vect
  32. #elif SOFT_SERIAL_PIN == D1
  33. #define SERIAL_PIN_MASK _BV(PD1)
  34. #define EIMSK_BIT _BV(INT1)
  35. #define EICRx_BIT (~(_BV(ISC10) | _BV(ISC11)))
  36. #define SERIAL_PIN_INTERRUPT INT1_vect
  37. #elif SOFT_SERIAL_PIN == D2
  38. #define SERIAL_PIN_MASK _BV(PD2)
  39. #define EIMSK_BIT _BV(INT2)
  40. #define EICRx_BIT (~(_BV(ISC20) | _BV(ISC21)))
  41. #define SERIAL_PIN_INTERRUPT INT2_vect
  42. #elif SOFT_SERIAL_PIN == D3
  43. #define SERIAL_PIN_MASK _BV(PD3)
  44. #define EIMSK_BIT _BV(INT3)
  45. #define EICRx_BIT (~(_BV(ISC30) | _BV(ISC31)))
  46. #define SERIAL_PIN_INTERRUPT INT3_vect
  47. #endif
  48. #elif SOFT_SERIAL_PIN == E6
  49. #define SERIAL_PIN_DDR DDRE
  50. #define SERIAL_PIN_PORT PORTE
  51. #define SERIAL_PIN_INPUT PINE
  52. #define SERIAL_PIN_MASK _BV(PE6)
  53. #define EIMSK_BIT _BV(INT6)
  54. #define EICRx_BIT (~(_BV(ISC60) | _BV(ISC61)))
  55. #define SERIAL_PIN_INTERRUPT INT6_vect
  56. #else
  57. #error invalid SOFT_SERIAL_PIN value
  58. #endif
  59. #else
  60. #error serial.c now support ATmega32U4 only
  61. #endif
  62. // Serial pulse period in microseconds. Its probably a bad idea to lower this
  63. // value.
  64. #define SERIAL_DELAY 24
  65. uint8_t volatile serial_slave_buffer[SERIAL_SLAVE_BUFFER_LENGTH] = {0};
  66. uint8_t volatile serial_master_buffer[SERIAL_MASTER_BUFFER_LENGTH] = {0};
  67. #define SLAVE_DATA_CORRUPT (1<<0)
  68. volatile uint8_t status = 0;
  69. inline static
  70. void serial_delay(void) {
  71. _delay_us(SERIAL_DELAY);
  72. }
  73. inline static
  74. void serial_output(void) {
  75. SERIAL_PIN_DDR |= SERIAL_PIN_MASK;
  76. }
  77. // make the serial pin an input with pull-up resistor
  78. inline static
  79. void serial_input(void) {
  80. SERIAL_PIN_DDR &= ~SERIAL_PIN_MASK;
  81. SERIAL_PIN_PORT |= SERIAL_PIN_MASK;
  82. }
  83. inline static
  84. uint8_t serial_read_pin(void) {
  85. return !!(SERIAL_PIN_INPUT & SERIAL_PIN_MASK);
  86. }
  87. inline static
  88. void serial_low(void) {
  89. SERIAL_PIN_PORT &= ~SERIAL_PIN_MASK;
  90. }
  91. inline static
  92. void serial_high(void) {
  93. SERIAL_PIN_PORT |= SERIAL_PIN_MASK;
  94. }
  95. void serial_master_init(void) {
  96. serial_output();
  97. serial_high();
  98. }
  99. void serial_slave_init(void) {
  100. serial_input();
  101. // Enable INT0
  102. EIMSK |= _BV(INT0);
  103. // Trigger on falling edge of INT0
  104. EICRA &= ~(_BV(ISC00) | _BV(ISC01));
  105. }
  106. // Used by the master to synchronize timing with the slave.
  107. static
  108. void sync_recv(void) {
  109. serial_input();
  110. // This shouldn't hang if the slave disconnects because the
  111. // serial line will float to high if the slave does disconnect.
  112. while (!serial_read_pin());
  113. serial_delay();
  114. }
  115. // Used by the slave to send a synchronization signal to the master.
  116. static
  117. void sync_send(void) {
  118. serial_output();
  119. serial_low();
  120. serial_delay();
  121. serial_high();
  122. }
  123. // Reads a byte from the serial line
  124. static
  125. uint8_t serial_read_byte(void) {
  126. uint8_t byte = 0;
  127. serial_input();
  128. for ( uint8_t i = 0; i < 8; ++i) {
  129. byte = (byte << 1) | serial_read_pin();
  130. serial_delay();
  131. _delay_us(1);
  132. }
  133. return byte;
  134. }
  135. // Sends a byte with MSB ordering
  136. static
  137. void serial_write_byte(uint8_t data) {
  138. uint8_t b = 8;
  139. serial_output();
  140. while( b-- ) {
  141. if(data & (1 << b)) {
  142. serial_high();
  143. } else {
  144. serial_low();
  145. }
  146. serial_delay();
  147. }
  148. }
  149. // interrupt handle to be used by the slave device
  150. ISR(SERIAL_PIN_INTERRUPT) {
  151. sync_send();
  152. uint8_t checksum = 0;
  153. for (int i = 0; i < SERIAL_SLAVE_BUFFER_LENGTH; ++i) {
  154. serial_write_byte(serial_slave_buffer[i]);
  155. sync_send();
  156. checksum += serial_slave_buffer[i];
  157. }
  158. serial_write_byte(checksum);
  159. sync_send();
  160. // wait for the sync to finish sending
  161. serial_delay();
  162. // read the middle of pulses
  163. _delay_us(SERIAL_DELAY/2);
  164. uint8_t checksum_computed = 0;
  165. for (int i = 0; i < SERIAL_MASTER_BUFFER_LENGTH; ++i) {
  166. serial_master_buffer[i] = serial_read_byte();
  167. sync_send();
  168. checksum_computed += serial_master_buffer[i];
  169. }
  170. uint8_t checksum_received = serial_read_byte();
  171. sync_send();
  172. serial_input(); // end transaction
  173. if ( checksum_computed != checksum_received ) {
  174. status |= SLAVE_DATA_CORRUPT;
  175. } else {
  176. status &= ~SLAVE_DATA_CORRUPT;
  177. }
  178. }
  179. inline
  180. bool serial_slave_DATA_CORRUPT(void) {
  181. return status & SLAVE_DATA_CORRUPT;
  182. }
  183. // Copies the serial_slave_buffer to the master and sends the
  184. // serial_master_buffer to the slave.
  185. //
  186. // Returns:
  187. // 0 => no error
  188. // 1 => slave did not respond
  189. int serial_update_buffers(void) {
  190. // this code is very time dependent, so we need to disable interrupts
  191. cli();
  192. // signal to the slave that we want to start a transaction
  193. serial_output();
  194. serial_low();
  195. _delay_us(1);
  196. // wait for the slaves response
  197. serial_input();
  198. serial_high();
  199. _delay_us(SERIAL_DELAY);
  200. // check if the slave is present
  201. if (serial_read_pin()) {
  202. // slave failed to pull the line low, assume not present
  203. sei();
  204. return 1;
  205. }
  206. // if the slave is present syncronize with it
  207. sync_recv();
  208. uint8_t checksum_computed = 0;
  209. // receive data from the slave
  210. for (int i = 0; i < SERIAL_SLAVE_BUFFER_LENGTH; ++i) {
  211. serial_slave_buffer[i] = serial_read_byte();
  212. sync_recv();
  213. checksum_computed += serial_slave_buffer[i];
  214. }
  215. uint8_t checksum_received = serial_read_byte();
  216. sync_recv();
  217. if (checksum_computed != checksum_received) {
  218. sei();
  219. return 1;
  220. }
  221. uint8_t checksum = 0;
  222. // send data to the slave
  223. for (int i = 0; i < SERIAL_MASTER_BUFFER_LENGTH; ++i) {
  224. serial_write_byte(serial_master_buffer[i]);
  225. sync_recv();
  226. checksum += serial_master_buffer[i];
  227. }
  228. serial_write_byte(checksum);
  229. sync_recv();
  230. // always, release the line when not in use
  231. serial_output();
  232. serial_high();
  233. sei();
  234. return 0;
  235. }
  236. #endif