You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

295 lines
6.8 KiB

  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 USE_SERIAL
  13. #define _delay_sub_us(x) __builtin_avr_delay_cycles(x)
  14. // Serial pulse period in microseconds.
  15. #define SELECT_SERIAL_SPEED 1
  16. #if SELECT_SERIAL_SPEED == 0
  17. // Very High speed
  18. #define SERIAL_DELAY 4 // micro sec
  19. #define READ_WRITE_START_ADJUST 30 // cycles
  20. #define READ_WRITE_WIDTH_ADJUST 10 // cycles
  21. #elif SELECT_SERIAL_SPEED == 1
  22. // High speed
  23. #define SERIAL_DELAY 6 // micro sec
  24. #define READ_WRITE_START_ADJUST 23 // cycles
  25. #define READ_WRITE_WIDTH_ADJUST 10 // cycles
  26. #elif SELECT_SERIAL_SPEED == 2
  27. // Middle speed
  28. #define SERIAL_DELAY 12 // micro sec
  29. #define READ_WRITE_START_ADJUST 25 // cycles
  30. #define READ_WRITE_WIDTH_ADJUST 10 // cycles
  31. #elif SELECT_SERIAL_SPEED == 3
  32. // Low speed
  33. #define SERIAL_DELAY 24 // micro sec
  34. #define READ_WRITE_START_ADJUST 25 // cycles
  35. #define READ_WRITE_WIDTH_ADJUST 10 // cycles
  36. #elif SELECT_SERIAL_SPEED == 4
  37. // Very Low speed
  38. #define SERIAL_DELAY 50 // micro sec
  39. #define READ_WRITE_START_ADJUST 25 // cycles
  40. #define READ_WRITE_WIDTH_ADJUST 10 // cycles
  41. #else
  42. #error Illegal Serial Speed
  43. #endif
  44. #define SERIAL_DELAY_HALF1 (SERIAL_DELAY/2)
  45. #define SERIAL_DELAY_HALF2 (SERIAL_DELAY - SERIAL_DELAY/2)
  46. #define SLAVE_INT_WIDTH 1
  47. #define SLAVE_INT_RESPONSE_TIME SERIAL_DELAY
  48. uint8_t volatile serial_slave_buffer[SERIAL_SLAVE_BUFFER_LENGTH] = {0};
  49. uint8_t volatile serial_master_buffer[SERIAL_MASTER_BUFFER_LENGTH] = {0};
  50. #define SLAVE_DATA_CORRUPT (1<<0)
  51. volatile uint8_t status = 0;
  52. inline static
  53. void serial_delay(void) {
  54. _delay_us(SERIAL_DELAY);
  55. }
  56. inline static
  57. void serial_delay_half1(void) {
  58. _delay_us(SERIAL_DELAY_HALF1);
  59. }
  60. inline static
  61. void serial_delay_half2(void) {
  62. _delay_us(SERIAL_DELAY_HALF2);
  63. }
  64. inline static
  65. void serial_output(void) {
  66. SERIAL_PIN_DDR |= SERIAL_PIN_MASK;
  67. }
  68. // make the serial pin an input with pull-up resistor
  69. inline static
  70. void serial_input_with_pullup(void) {
  71. SERIAL_PIN_DDR &= ~SERIAL_PIN_MASK;
  72. SERIAL_PIN_PORT |= SERIAL_PIN_MASK;
  73. }
  74. inline static
  75. uint8_t serial_read_pin(void) {
  76. return !!(SERIAL_PIN_INPUT & SERIAL_PIN_MASK);
  77. }
  78. inline static
  79. void serial_low(void) {
  80. SERIAL_PIN_PORT &= ~SERIAL_PIN_MASK;
  81. }
  82. inline static
  83. void serial_high(void) {
  84. SERIAL_PIN_PORT |= SERIAL_PIN_MASK;
  85. }
  86. void serial_master_init(void) {
  87. serial_output();
  88. serial_high();
  89. }
  90. void serial_slave_init(void) {
  91. serial_input_with_pullup();
  92. #if SERIAL_PIN_MASK == _BV(PD0)
  93. // Enable INT0
  94. EIMSK |= _BV(INT0);
  95. // Trigger on falling edge of INT0
  96. EICRA &= ~(_BV(ISC00) | _BV(ISC01));
  97. #elif SERIAL_PIN_MASK == _BV(PD2)
  98. // Enable INT2
  99. EIMSK |= _BV(INT2);
  100. // Trigger on falling edge of INT2
  101. EICRA &= ~(_BV(ISC20) | _BV(ISC21));
  102. #else
  103. #error unknown SERIAL_PIN_MASK value
  104. #endif
  105. }
  106. // Used by the sender to synchronize timing with the reciver.
  107. static
  108. void sync_recv(void) {
  109. for (int i = 0; i < SERIAL_DELAY*5 && serial_read_pin(); i++ ) {
  110. }
  111. // This shouldn't hang if the slave disconnects because the
  112. // serial line will float to high if the slave does disconnect.
  113. while (!serial_read_pin());
  114. }
  115. // Used by the reciver to send a synchronization signal to the sender.
  116. static
  117. void sync_send(void) {
  118. serial_low();
  119. serial_delay();
  120. serial_high();
  121. }
  122. // Reads a byte from the serial line
  123. static
  124. uint8_t serial_read_byte(void) {
  125. uint8_t byte = 0;
  126. _delay_sub_us(READ_WRITE_START_ADJUST);
  127. for ( uint8_t i = 0; i < 8; ++i) {
  128. serial_delay_half1(); // read the middle of pulses
  129. byte = (byte << 1) | serial_read_pin();
  130. _delay_sub_us(READ_WRITE_WIDTH_ADJUST);
  131. serial_delay_half2();
  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 = 1<<7;
  139. while( b ) {
  140. if(data & b) {
  141. serial_high();
  142. } else {
  143. serial_low();
  144. }
  145. b >>= 1;
  146. serial_delay();
  147. }
  148. serial_low(); // sync_send() / senc_recv() need raise edge
  149. }
  150. // interrupt handle to be used by the slave device
  151. ISR(SERIAL_PIN_INTERRUPT) {
  152. serial_output();
  153. // slave send phase
  154. uint8_t checksum = 0;
  155. for (int i = 0; i < SERIAL_SLAVE_BUFFER_LENGTH; ++i) {
  156. sync_send();
  157. serial_write_byte(serial_slave_buffer[i]);
  158. checksum += serial_slave_buffer[i];
  159. }
  160. sync_send();
  161. serial_write_byte(checksum);
  162. // slave switch to input
  163. sync_send(); //0
  164. serial_delay_half1(); //1
  165. serial_low(); //2
  166. serial_input_with_pullup(); //2
  167. serial_delay_half1(); //3
  168. // slave recive phase
  169. uint8_t checksum_computed = 0;
  170. for (int i = 0; i < SERIAL_MASTER_BUFFER_LENGTH; ++i) {
  171. sync_recv();
  172. serial_master_buffer[i] = serial_read_byte();
  173. checksum_computed += serial_master_buffer[i];
  174. }
  175. sync_recv();
  176. uint8_t checksum_received = serial_read_byte();
  177. if ( checksum_computed != checksum_received ) {
  178. status |= SLAVE_DATA_CORRUPT;
  179. } else {
  180. status &= ~SLAVE_DATA_CORRUPT;
  181. }
  182. sync_recv(); //weit master output to high
  183. }
  184. inline
  185. bool serial_slave_DATA_CORRUPT(void) {
  186. return status & SLAVE_DATA_CORRUPT;
  187. }
  188. // Copies the serial_slave_buffer to the master and sends the
  189. // serial_master_buffer to the slave.
  190. //
  191. // Returns:
  192. // 0 => no error
  193. // 1 => slave did not respond
  194. // 2 => checksum error
  195. int serial_update_buffers(void) {
  196. // this code is very time dependent, so we need to disable interrupts
  197. cli();
  198. // signal to the slave that we want to start a transaction
  199. serial_output();
  200. serial_low();
  201. _delay_us(SLAVE_INT_WIDTH);
  202. // wait for the slaves response
  203. serial_input_with_pullup();
  204. _delay_us(SLAVE_INT_RESPONSE_TIME);
  205. // check if the slave is present
  206. if (serial_read_pin()) {
  207. // slave failed to pull the line low, assume not present
  208. serial_output();
  209. serial_high();
  210. sei();
  211. return 1;
  212. }
  213. // master recive phase
  214. // if the slave is present syncronize with it
  215. uint8_t checksum_computed = 0;
  216. // receive data from the slave
  217. for (int i = 0; i < SERIAL_SLAVE_BUFFER_LENGTH; ++i) {
  218. sync_recv();
  219. serial_slave_buffer[i] = serial_read_byte();
  220. checksum_computed += serial_slave_buffer[i];
  221. }
  222. sync_recv();
  223. uint8_t checksum_received = serial_read_byte();
  224. if (checksum_computed != checksum_received) {
  225. serial_output();
  226. serial_high();
  227. sei();
  228. return 2;
  229. }
  230. // master switch to output
  231. sync_recv(); //0
  232. serial_delay(); //1
  233. serial_low(); //3
  234. serial_output(); // 3
  235. serial_delay_half1(); //4
  236. // master send phase
  237. uint8_t checksum = 0;
  238. for (int i = 0; i < SERIAL_MASTER_BUFFER_LENGTH; ++i) {
  239. sync_send();
  240. serial_write_byte(serial_master_buffer[i]);
  241. checksum += serial_master_buffer[i];
  242. }
  243. sync_send();
  244. serial_write_byte(checksum);
  245. // always, release the line when not in use
  246. sync_send();
  247. sei();
  248. return 0;
  249. }
  250. #endif