fmrx.c 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  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. TIMSK1 = TIFR1 = (1 << ICIE1) | (1 << OCIE1A);
  51. }
  52. static inline void disable_timeout(void)
  53. {
  54. TIFR1 = 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. TCCR1B &= ~7; /* Stop timer */
  65. one_bit_lo = F_CPU / (4 * baudrate);
  66. one_bit_hi = 3 * one_bit_lo;
  67. one_bit_timeout = 16 * one_bit_lo; /* 4 total bit times... */
  68. disable_timeout();
  69. started = false;
  70. bits = bytes = 0;
  71. last_edge = TCNT1;
  72. TCCR1B |= prescale; /* Start timer */
  73. }
  74. /* We "hunt" for a block until we find this bit pattern; same as ABC800 */
  75. #define CAS_SYNC_PATTERN 0x0216 /* SYNC + STX */
  76. #define CAS_BLOCK_LEN (1+2+253+1+2) /* type+num+data+ETX+checksum */
  77. /* Finish a bit due to clock edge or timeout */
  78. static inline ATTR_ALWAYS_INLINE void finish_bit(void)
  79. {
  80. if (!started)
  81. return;
  82. if (bytes) {
  83. if (--bits == 0) {
  84. uint8_t outbyte = data >> 8;
  85. fmrx_recv_byte(outbyte);
  86. data = 0; /* To avoid false sync */
  87. bits = 8;
  88. if (--bytes == 0)
  89. fmrx_led_off();
  90. }
  91. } else if (data == CAS_SYNC_PATTERN) {
  92. bits = 8;
  93. bytes = CAS_BLOCK_LEN;
  94. fmrx_led_on();
  95. }
  96. }
  97. /* Interrupt routine for edge capture */
  98. ISR(TIMER1_CAPT_vect)
  99. {
  100. uint16_t edge, delta;
  101. TCCR1B ^= (1 << ICES1); /* Next edge -> opposite polarity */
  102. edge = ICR1;
  103. delta = edge - last_edge;
  104. if (delta < one_bit_lo) {
  105. /* Early edge, no action */
  106. return;
  107. } else if (delta < one_bit_hi) {
  108. /* A data bit edge */
  109. data |= (1 << 15);
  110. } else {
  111. /* Clock edge */
  112. last_edge = edge;
  113. enable_timeout(edge + one_bit_timeout);
  114. finish_bit();
  115. started = true;
  116. data >>= 1;
  117. }
  118. }
  119. /* Interrupt routine for bit timeout */
  120. ISR(TIMER1_COMPA_vect)
  121. {
  122. disable_timeout();
  123. finish_bit();
  124. started = false;
  125. }