fmrx.c 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. /*
  2. * Receive an FM-modulated signal by using a timer with input capture
  3. */
  4. #include "usbcas.h"
  5. static inline void fmrx_led_on(void)
  6. {
  7. PORTB &= ~(1 << 0);
  8. }
  9. static inline void fmrx_led_off(void)
  10. {
  11. PORTB |= (1 << 0);
  12. }
  13. void fmrx_init(void)
  14. {
  15. uint8_t tccr1b;
  16. /* ICP1/PD4 is input, with pullup */
  17. DDRD &= ~(1 << 4);
  18. PORTD |= (1 << 4);
  19. /* PB0 is RXLED */
  20. DDRB |= (1 << 0);
  21. fmrx_led_off();
  22. /* Stop counter */
  23. TCCR1B = 0;
  24. /* Normal mode, no comparators */
  25. TCCR1A = 0;
  26. /* Enable interrupts on input capture */
  27. TIFR1 = TIMSK1 = 1 << ICIE1;
  28. /* Clear timer counter */
  29. TCNT1 = 0;
  30. /*
  31. * Input capture noise canceler on, normal mode; not counting yet
  32. */
  33. tccr1b = (1 << ICNC1) + (0 << CS10);
  34. if (!(PIND & (1 << 4)))
  35. tccr1b |= 1 << ICES1;
  36. TCCR1B = tccr1b;
  37. /*
  38. * Default speed
  39. */
  40. fmrx_set_speed(CAS_DIVISOR_ABC80);
  41. }
  42. /* Timing constants: start and end of data bit interval, and timeout */
  43. static uint16_t one_bit_lo, one_bit_hi, one_bit_timeout;
  44. static uint16_t bytes;
  45. static uint16_t last_edge;
  46. static uint16_t data; /* 16 bit for sync pattern detect */
  47. static uint8_t bits;
  48. static uint16_t bytes;
  49. static bool started;
  50. /* Bit receive timeout on/off */
  51. static inline void enable_timeout(uint16_t when)
  52. {
  53. OCR1A = when;
  54. TIMSK1 = TIFR1 = (1 << ICIE1) | (1 << OCIE1A);
  55. }
  56. static inline void disable_timeout(void)
  57. {
  58. TIFR1 = TIMSK1 = (1 << ICIE1);
  59. }
  60. void fmrx_set_speed(uint8_t divisor)
  61. {
  62. uint8_t prescale = 1; /* No prescaling */
  63. uint32_t baudrate;
  64. if (!divisor)
  65. divisor = CAS_DIVISOR_ABC80;
  66. baudrate = (CAS_BASE_CLOCK+(divisor >> 1)-1)/divisor;
  67. /* Enable prescaler for low baud rates to reduce wraparound */
  68. if (baudrate < 976) {
  69. baudrate <<= 3; /* ... for calculation purposes ... */
  70. prescale++; /* Prescale by 8 */
  71. }
  72. TCCR1B &= ~7; /* Stop timer */
  73. one_bit_lo = F_CPU / (4 * baudrate);
  74. one_bit_hi = 3 * one_bit_lo;
  75. one_bit_timeout = 16 * one_bit_lo; /* 4 total bit times... */
  76. disable_timeout();
  77. started = false;
  78. bits = bytes = 0;
  79. last_edge = TCNT1;
  80. TCCR1B |= prescale; /* Start timer */
  81. }
  82. /*
  83. * When the motor is switched off, reset the download speed, so
  84. * we can load the next header block correctly
  85. */
  86. void fmrx_motor_off(void)
  87. {
  88. fmrx_set_speed(CAS_DIVISOR_ABC80);
  89. }
  90. /* We "hunt" for a block until we find this bit pattern; same as ABC800 */
  91. #define CAS_SYNC_PATTERN 0x0216 /* SYNC + STX */
  92. #define CAS_BLOCK_LEN (1+2+253+1+2) /* type+num+data+ETX+checksum */
  93. /* Finish a bit due to clock edge or timeout */
  94. static inline ATTR_ALWAYS_INLINE void finish_bit(void)
  95. {
  96. static uint16_t rx_csum;
  97. static uint8_t header;
  98. static uint8_t header_divisor;
  99. if (!started)
  100. return;
  101. if (bytes) {
  102. if (--bits == 0) {
  103. uint8_t outbyte = data >> 8;
  104. data = 0; /* To avoid false sync */
  105. bits = 8;
  106. switch (--bytes) {
  107. case 0:
  108. fmrx_led_off();
  109. if (header)
  110. fmrx_set_speed(header_divisor);
  111. return;
  112. case 1:
  113. if (rx_csum != data)
  114. set_modem_status(CDC_CONTROL_LINE_IN_PARITYERROR);
  115. return;
  116. case 2:
  117. /* First checksum byte */
  118. return;
  119. case 3:
  120. if (outbyte != 0x03)
  121. set_modem_status(CDC_CONTROL_LINE_IN_FRAMEERROR);
  122. return;
  123. case CAS_BLOCK_LEN-1:
  124. header = outbyte;
  125. break;
  126. case CAS_BLOCK_LEN-3-11-1: /* Byte after filename */
  127. header_divisor = outbyte;
  128. break;
  129. default:
  130. break;
  131. }
  132. rx_csum += outbyte;
  133. fmrx_recv_byte(outbyte);
  134. }
  135. } else if (data == CAS_SYNC_PATTERN) {
  136. bits = 8;
  137. bytes = CAS_BLOCK_LEN;
  138. rx_csum = 0x03; /* Final ETX */
  139. fmrx_led_on();
  140. }
  141. }
  142. /* Interrupt routine for edge capture */
  143. ISR(TIMER1_CAPT_vect)
  144. {
  145. uint16_t edge, delta;
  146. TCCR1B ^= (1 << ICES1); /* Next edge -> opposite polarity */
  147. edge = ICR1;
  148. delta = edge - last_edge;
  149. if (delta < one_bit_lo) {
  150. /* Early edge, no action */
  151. return;
  152. } else if (delta < one_bit_hi) {
  153. /* A data bit edge */
  154. data |= (1 << 15);
  155. } else {
  156. /* Clock edge */
  157. last_edge = edge;
  158. enable_timeout(edge + one_bit_timeout);
  159. finish_bit();
  160. started = true;
  161. data >>= 1;
  162. }
  163. }
  164. /* Interrupt routine for bit timeout */
  165. ISR(TIMER1_COMPA_vect)
  166. {
  167. disable_timeout();
  168. finish_bit();
  169. started = false;
  170. }