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.

445 lines
11 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 <stddef.h>
  11. #include <stdbool.h>
  12. #include "serial.h"
  13. //#include <pro_micro.h>
  14. #ifdef USE_SERIAL
  15. //#ifndef USE_SERIAL_PD2
  16. #ifndef SERIAL_USE_MULTI_TRANSACTION
  17. /* --- USE Simple API (OLD API, compatible with let's split serial.c) */
  18. #if SERIAL_SLAVE_BUFFER_LENGTH > 0
  19. uint8_t volatile serial_slave_buffer[SERIAL_SLAVE_BUFFER_LENGTH] = {0};
  20. #endif
  21. #if SERIAL_MASTER_BUFFER_LENGTH > 0
  22. uint8_t volatile serial_master_buffer[SERIAL_MASTER_BUFFER_LENGTH] = {0};
  23. #endif
  24. uint8_t volatile status0 = 0;
  25. SSTD_t transactions[] = {
  26. { (uint8_t *)&status0,
  27. #if SERIAL_MASTER_BUFFER_LENGTH > 0
  28. sizeof(serial_master_buffer), (uint8_t *)serial_master_buffer,
  29. #else
  30. 0, (uint8_t *)NULL,
  31. #endif
  32. #if SERIAL_SLAVE_BUFFER_LENGTH > 0
  33. sizeof(serial_slave_buffer), (uint8_t *)serial_slave_buffer
  34. #else
  35. 0, (uint8_t *)NULL,
  36. #endif
  37. }
  38. };
  39. void serial_master_init(void)
  40. { soft_serial_initiator_init(transactions); }
  41. void serial_slave_init(void)
  42. { soft_serial_target_init(transactions); }
  43. // 0 => no error
  44. // 1 => slave did not respond
  45. // 2 => checksum error
  46. int serial_update_buffers()
  47. { return soft_serial_transaction(); }
  48. #endif // Simple API (OLD API, compatible with let's split serial.c)
  49. #define ALWAYS_INLINE __attribute__((always_inline))
  50. #define NO_INLINE __attribute__((noinline))
  51. #define _delay_sub_us(x) __builtin_avr_delay_cycles(x)
  52. // Serial pulse period in microseconds.
  53. #define TID_SEND_ADJUST 14
  54. #define SELECT_SERIAL_SPEED 1
  55. #if SELECT_SERIAL_SPEED == 0
  56. // Very High speed
  57. #define SERIAL_DELAY 4 // micro sec
  58. #define READ_WRITE_START_ADJUST 33 // cycles
  59. #define READ_WRITE_WIDTH_ADJUST 3 // cycles
  60. #elif SELECT_SERIAL_SPEED == 1
  61. // High speed
  62. #define SERIAL_DELAY 6 // micro sec
  63. #define READ_WRITE_START_ADJUST 30 // cycles
  64. #define READ_WRITE_WIDTH_ADJUST 3 // cycles
  65. #elif SELECT_SERIAL_SPEED == 2
  66. // Middle speed
  67. #define SERIAL_DELAY 12 // micro sec
  68. #define READ_WRITE_START_ADJUST 30 // cycles
  69. #define READ_WRITE_WIDTH_ADJUST 3 // cycles
  70. #elif SELECT_SERIAL_SPEED == 3
  71. // Low speed
  72. #define SERIAL_DELAY 24 // micro sec
  73. #define READ_WRITE_START_ADJUST 30 // cycles
  74. #define READ_WRITE_WIDTH_ADJUST 3 // cycles
  75. #elif SELECT_SERIAL_SPEED == 4
  76. // Very Low speed
  77. #define SERIAL_DELAY 50 // micro sec
  78. #define READ_WRITE_START_ADJUST 30 // cycles
  79. #define READ_WRITE_WIDTH_ADJUST 3 // cycles
  80. #else
  81. #error Illegal Serial Speed
  82. #endif
  83. #define SERIAL_DELAY_HALF1 (SERIAL_DELAY/2)
  84. #define SERIAL_DELAY_HALF2 (SERIAL_DELAY - SERIAL_DELAY/2)
  85. #define SLAVE_INT_WIDTH_US 1
  86. #ifndef SERIAL_USE_MULTI_TRANSACTION
  87. #define SLAVE_INT_RESPONSE_TIME SERIAL_DELAY
  88. #else
  89. #define SLAVE_INT_ACK_WIDTH_UNIT 2
  90. #define SLAVE_INT_ACK_WIDTH 4
  91. #endif
  92. static SSTD_t *Transaction_table = NULL;
  93. inline static
  94. void serial_delay(void) {
  95. _delay_us(SERIAL_DELAY);
  96. }
  97. inline static
  98. void serial_delay_half1(void) {
  99. _delay_us(SERIAL_DELAY_HALF1);
  100. }
  101. inline static
  102. void serial_delay_half2(void) {
  103. _delay_us(SERIAL_DELAY_HALF2);
  104. }
  105. inline static void serial_output(void) ALWAYS_INLINE;
  106. inline static
  107. void serial_output(void) {
  108. SERIAL_PIN_DDR |= SERIAL_PIN_MASK;
  109. }
  110. // make the serial pin an input with pull-up resistor
  111. inline static void serial_input_with_pullup(void) ALWAYS_INLINE;
  112. inline static
  113. void serial_input_with_pullup(void) {
  114. SERIAL_PIN_DDR &= ~SERIAL_PIN_MASK;
  115. SERIAL_PIN_PORT |= SERIAL_PIN_MASK;
  116. }
  117. inline static
  118. uint8_t serial_read_pin(void) {
  119. return !!(SERIAL_PIN_INPUT & SERIAL_PIN_MASK);
  120. }
  121. inline static void serial_low(void) ALWAYS_INLINE;
  122. inline static
  123. void serial_low(void) {
  124. SERIAL_PIN_PORT &= ~SERIAL_PIN_MASK;
  125. }
  126. inline static void serial_high(void) ALWAYS_INLINE;
  127. inline static
  128. void serial_high(void) {
  129. SERIAL_PIN_PORT |= SERIAL_PIN_MASK;
  130. }
  131. void soft_serial_initiator_init(SSTD_t *sstd_table)
  132. {
  133. Transaction_table = sstd_table;
  134. serial_output();
  135. serial_high();
  136. }
  137. void soft_serial_target_init(SSTD_t *sstd_table)
  138. {
  139. Transaction_table = sstd_table;
  140. serial_input_with_pullup();
  141. #if SERIAL_PIN_MASK == _BV(PD0)
  142. // Enable INT0
  143. EIMSK |= _BV(INT0);
  144. // Trigger on falling edge of INT0
  145. EICRA &= ~(_BV(ISC00) | _BV(ISC01));
  146. #elif SERIAL_PIN_MASK == _BV(PD2)
  147. // Enable INT2
  148. EIMSK |= _BV(INT2);
  149. // Trigger on falling edge of INT2
  150. EICRA &= ~(_BV(ISC20) | _BV(ISC21));
  151. #else
  152. #error unknown SERIAL_PIN_MASK value
  153. #endif
  154. }
  155. // Used by the sender to synchronize timing with the reciver.
  156. static void sync_recv(void) NO_INLINE;
  157. static
  158. void sync_recv(void) {
  159. for (uint8_t i = 0; i < SERIAL_DELAY*5 && serial_read_pin(); i++ ) {
  160. }
  161. // This shouldn't hang if the target disconnects because the
  162. // serial line will float to high if the target does disconnect.
  163. while (!serial_read_pin());
  164. }
  165. // Used by the reciver to send a synchronization signal to the sender.
  166. static void sync_send(void)NO_INLINE;
  167. static
  168. void sync_send(void) {
  169. serial_low();
  170. serial_delay();
  171. serial_high();
  172. }
  173. // Reads a byte from the serial line
  174. static uint8_t serial_read_chunk(uint8_t *pterrcount, uint8_t bit) NO_INLINE;
  175. static uint8_t serial_read_chunk(uint8_t *pterrcount, uint8_t bit) {
  176. uint8_t byte, i, p, pb;
  177. _delay_sub_us(READ_WRITE_START_ADJUST);
  178. for( i = 0, byte = 0, p = 0; i < bit; i++ ) {
  179. serial_delay_half1(); // read the middle of pulses
  180. if( serial_read_pin() ) {
  181. byte = (byte << 1) | 1; p ^= 1;
  182. } else {
  183. byte = (byte << 1) | 0; p ^= 0;
  184. }
  185. _delay_sub_us(READ_WRITE_WIDTH_ADJUST);
  186. serial_delay_half2();
  187. }
  188. /* recive parity bit */
  189. serial_delay_half1(); // read the middle of pulses
  190. pb = serial_read_pin();
  191. _delay_sub_us(READ_WRITE_WIDTH_ADJUST);
  192. serial_delay_half2();
  193. *pterrcount += (p != pb)? 1 : 0;
  194. return byte;
  195. }
  196. // Sends a byte with MSB ordering
  197. void serial_write_chunk(uint8_t data, uint8_t bit) NO_INLINE;
  198. void serial_write_chunk(uint8_t data, uint8_t bit) {
  199. uint8_t b, p;
  200. for( p = 0, b = 1<<(bit-1); b ; b >>= 1) {
  201. if(data & b) {
  202. serial_high(); p ^= 1;
  203. } else {
  204. serial_low(); p ^= 0;
  205. }
  206. serial_delay();
  207. }
  208. /* send parity bit */
  209. if(p & 1) { serial_high(); }
  210. else { serial_low(); }
  211. serial_delay();
  212. serial_low(); // sync_send() / senc_recv() need raise edge
  213. }
  214. static void serial_send_packet(uint8_t *buffer, uint8_t size) NO_INLINE;
  215. static
  216. void serial_send_packet(uint8_t *buffer, uint8_t size) {
  217. for (uint8_t i = 0; i < size; ++i) {
  218. uint8_t data;
  219. data = buffer[i];
  220. sync_send();
  221. serial_write_chunk(data,8);
  222. }
  223. }
  224. static uint8_t serial_recive_packet(uint8_t *buffer, uint8_t size) NO_INLINE;
  225. static
  226. uint8_t serial_recive_packet(uint8_t *buffer, uint8_t size) {
  227. uint8_t pecount = 0;
  228. for (uint8_t i = 0; i < size; ++i) {
  229. uint8_t data;
  230. sync_recv();
  231. data = serial_read_chunk(&pecount, 8);
  232. buffer[i] = data;
  233. }
  234. return pecount == 0;
  235. }
  236. inline static
  237. void change_sender2reciver(void) {
  238. sync_send(); //0
  239. serial_delay_half1(); //1
  240. serial_low(); //2
  241. serial_input_with_pullup(); //2
  242. serial_delay_half1(); //3
  243. }
  244. inline static
  245. void change_reciver2sender(void) {
  246. sync_recv(); //0
  247. serial_delay(); //1
  248. serial_low(); //3
  249. serial_output(); //3
  250. serial_delay_half1(); //4
  251. }
  252. // interrupt handle to be used by the target device
  253. ISR(SERIAL_PIN_INTERRUPT) {
  254. #ifndef SERIAL_USE_MULTI_TRANSACTION
  255. serial_low();
  256. serial_output();
  257. SSTD_t *trans = Transaction_table;
  258. #else
  259. // recive transaction table index
  260. uint8_t tid;
  261. uint8_t pecount = 0;
  262. sync_recv();
  263. tid = serial_read_chunk(&pecount,4);
  264. if(pecount> 0)
  265. return;
  266. serial_delay_half1();
  267. serial_high(); // response step1 low->high
  268. serial_output();
  269. _delay_sub_us(SLAVE_INT_ACK_WIDTH_UNIT*SLAVE_INT_ACK_WIDTH);
  270. SSTD_t *trans = &Transaction_table[tid];
  271. serial_low(); // response step2 ack high->low
  272. #endif
  273. // target send phase
  274. if( trans->target2initiator_buffer_size > 0 )
  275. serial_send_packet((uint8_t *)trans->target2initiator_buffer,
  276. trans->target2initiator_buffer_size);
  277. // target switch to input
  278. change_sender2reciver();
  279. // target recive phase
  280. if( trans->initiator2target_buffer_size > 0 ) {
  281. if (serial_recive_packet((uint8_t *)trans->initiator2target_buffer,
  282. trans->initiator2target_buffer_size) ) {
  283. *trans->status = TRANSACTION_ACCEPTED;
  284. } else {
  285. *trans->status = TRANSACTION_DATA_ERROR;
  286. }
  287. } else {
  288. *trans->status = TRANSACTION_ACCEPTED;
  289. }
  290. sync_recv(); //weit initiator output to high
  291. }
  292. /////////
  293. // start transaction by initiator
  294. //
  295. // int soft_serial_transaction(int sstd_index)
  296. //
  297. // Returns:
  298. // TRANSACTION_END
  299. // TRANSACTION_NO_RESPONSE
  300. // TRANSACTION_DATA_ERROR
  301. // this code is very time dependent, so we need to disable interrupts
  302. #ifndef SERIAL_USE_MULTI_TRANSACTION
  303. int soft_serial_transaction(void) {
  304. SSTD_t *trans = Transaction_table;
  305. #else
  306. int soft_serial_transaction(int sstd_index) {
  307. SSTD_t *trans = &Transaction_table[sstd_index];
  308. #endif
  309. cli();
  310. // signal to the target that we want to start a transaction
  311. serial_output();
  312. serial_low();
  313. _delay_us(SLAVE_INT_WIDTH_US);
  314. #ifndef SERIAL_USE_MULTI_TRANSACTION
  315. // wait for the target response
  316. serial_input_with_pullup();
  317. _delay_us(SLAVE_INT_RESPONSE_TIME);
  318. // check if the target is present
  319. if (serial_read_pin()) {
  320. // target failed to pull the line low, assume not present
  321. serial_output();
  322. serial_high();
  323. *trans->status = TRANSACTION_NO_RESPONSE;
  324. sei();
  325. return TRANSACTION_NO_RESPONSE;
  326. }
  327. #else
  328. // send transaction table index
  329. sync_send();
  330. _delay_sub_us(TID_SEND_ADJUST);
  331. serial_write_chunk(sstd_index, 4);
  332. serial_delay_half1();
  333. // wait for the target response (step1 low->high)
  334. serial_input_with_pullup();
  335. while( !serial_read_pin() ) {
  336. _delay_sub_us(2);
  337. }
  338. // check if the target is present (step2 high->low)
  339. for( int i = 0; serial_read_pin(); i++ ) {
  340. if (i > SLAVE_INT_ACK_WIDTH + 1) {
  341. // slave failed to pull the line low, assume not present
  342. serial_output();
  343. serial_high();
  344. *trans->status = TRANSACTION_NO_RESPONSE;
  345. sei();
  346. return TRANSACTION_NO_RESPONSE;
  347. }
  348. _delay_sub_us(SLAVE_INT_ACK_WIDTH_UNIT);
  349. }
  350. #endif
  351. // initiator recive phase
  352. // if the target is present syncronize with it
  353. if( trans->target2initiator_buffer_size > 0 ) {
  354. if (!serial_recive_packet((uint8_t *)trans->target2initiator_buffer,
  355. trans->target2initiator_buffer_size) ) {
  356. serial_output();
  357. serial_high();
  358. *trans->status = TRANSACTION_DATA_ERROR;
  359. sei();
  360. return TRANSACTION_DATA_ERROR;
  361. }
  362. }
  363. // initiator switch to output
  364. change_reciver2sender();
  365. // initiator send phase
  366. if( trans->initiator2target_buffer_size > 0 ) {
  367. serial_send_packet((uint8_t *)trans->initiator2target_buffer,
  368. trans->initiator2target_buffer_size);
  369. }
  370. // always, release the line when not in use
  371. sync_send();
  372. *trans->status = TRANSACTION_END;
  373. sei();
  374. return TRANSACTION_END;
  375. }
  376. #ifdef SERIAL_USE_MULTI_TRANSACTION
  377. int soft_serial_get_and_clean_status(int sstd_index) {
  378. SSTD_t *trans = &Transaction_table[sstd_index];
  379. cli();
  380. int retval = *trans->status;
  381. *trans->status = 0;;
  382. sei();
  383. return retval;
  384. }
  385. #endif
  386. #endif