serial.c 6.4 KB

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