fmrx.c 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  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. /* Timing constants: start and end of data bit interval, and timeout */
  39. static uint16_t one_bit_lo, one_bit_hi, one_bit_timeout;
  40. static uint16_t bytes;
  41. static uint16_t last_edge;
  42. static uint16_t data; /* 16 bit for sync pattern detect */
  43. static uint8_t bits;
  44. static uint16_t bytes;
  45. static bool started;
  46. /* Bit receive timeout on/off */
  47. static inline void enable_timeout(uint16_t when)
  48. {
  49. OCR1A = when;
  50. TIFR1 = TIMSK1 = (1 << ICIE1) | (1 << OCIE1A);
  51. }
  52. static inline void disable_timeout(void)
  53. {
  54. TIMSK1 = (1 << ICIE1);
  55. }
  56. void fmrx_set_speed(uint32_t baudrate)
  57. {
  58. uint8_t prescale = 1; /* No prescaling */
  59. /* Enable prescaler for low baud rates to reduce wraparound */
  60. if (baudrate < 976) {
  61. baudrate <<= 3; /* ... for calculation purposes ... */
  62. prescale++; /* Prescale by 8 */
  63. }
  64. one_bit_lo = F_CPU / (4 * baudrate);
  65. one_bit_hi = 3 * one_bit_lo;
  66. one_bit_timeout = 16 * one_bit_lo; /* 4 total bit times... */
  67. disable_timeout();
  68. started = false;
  69. bits = bytes = 0;
  70. last_edge = TCNT1;
  71. TCCR1B = (TCCR1B & ~7) | prescale;
  72. }
  73. /* We "hunt" for a block until we find this bit pattern; same as ABC800 */
  74. #define CAS_SYNC_PATTERN 0x0216 /* SYNC + STX */
  75. #define CAS_BLOCK_LEN (1+2+253+1+2) /* type+num+data+ETX+checksum */
  76. /* Finish a bit due to clock edge or timeout */
  77. static void finish_bit(void)
  78. {
  79. if (!started)
  80. return;
  81. if (bytes) {
  82. if (--bits == 0) {
  83. uint8_t outbyte = data >> 8;
  84. fmrx_recv_byte(outbyte);
  85. data = 0; /* To avoid false sync */
  86. bits = 8;
  87. if (--bytes == 0)
  88. fmrx_led_off();
  89. }
  90. } else if (data == CAS_SYNC_PATTERN) {
  91. bits = 8;
  92. bytes = CAS_BLOCK_LEN;
  93. fmrx_led_on();
  94. }
  95. }
  96. /* Interrupt routine for edge capture */
  97. ISR(TIMER1_CAPT_vect)
  98. {
  99. uint16_t edge, delta;
  100. TCCR1B ^= (1 << ICES1); /* Next edge -> opposite polarity */
  101. edge = ICR1;
  102. delta = edge - last_edge;
  103. if (delta < one_bit_lo) {
  104. /* Early edge, no action */
  105. return;
  106. } else if (delta < one_bit_hi) {
  107. /* A data bit edge */
  108. data |= (1 << 15);
  109. } else {
  110. /* Clock edge */
  111. last_edge = edge;
  112. enable_timeout(edge + one_bit_timeout);
  113. finish_bit();
  114. started = true;
  115. data >>= 1;
  116. }
  117. }
  118. /* Interrupt routine for bit timeout */
  119. ISR(TIMER1_COMPA_vect)
  120. {
  121. disable_timeout();
  122. finish_bit();
  123. started = false;
  124. }