/* * Receive an FM-modulated signal by using a timer with input capture */ #include "usbcas.h" static inline void fmrx_led_on(void) { //PORTB &= ~(1 << 0); } static inline void fmrx_led_off(void) { // PORTB |= (1 << 0); } void fmrx_init(void) { uint8_t tccr1b; /* ICP1/PD4 is input, with pullup */ DDRD &= ~(1 << 4); PORTD |= (1 << 4); /* PB0 is RXLED */ DDRB |= (1 << 0); fmrx_led_off(); /* Stop counter */ TCCR1B = 0; /* Normal mode, no comparators */ TCCR1A = 0; /* Enable interrupts on input capture */ TIFR1 = TIMSK1 = 1 << ICIE1; /* Clear timer counter */ TCNT1 = 0; /* * Input capture noise canceler on, normal mode; not counting yet */ tccr1b = (1 << ICNC1) + (0 << CS10); if (!(PIND & (1 << 4))) tccr1b |= 1 << ICES1; TCCR1B = tccr1b; } /* Timing constants: start and end of data bit interval, and timeout */ static uint16_t one_bit_lo, one_bit_hi, one_bit_timeout; static uint16_t bytes; static uint16_t last_edge; static uint16_t data; /* 16 bit for sync pattern detect */ static uint8_t bits; static uint16_t bytes; static bool started; /* Bit receive timeout on/off */ static inline void enable_timeout(uint16_t when) { OCR1A = when; TIMSK1 = TIFR1 = (1 << ICIE1) | (1 << OCIE1A); } static inline void disable_timeout(void) { TIFR1 = TIMSK1 = (1 << ICIE1); } void fmrx_set_speed(uint32_t baudrate) { uint8_t prescale = 1; /* No prescaling */ /* Enable prescaler for low baud rates to reduce wraparound */ if (baudrate < 976) { baudrate <<= 3; /* ... for calculation purposes ... */ prescale++; /* Prescale by 8 */ } TCCR1B &= ~7; /* Stop timer */ one_bit_lo = F_CPU / (4 * baudrate); one_bit_hi = 3 * one_bit_lo; one_bit_timeout = 16 * one_bit_lo; /* 4 total bit times... */ disable_timeout(); started = false; bits = bytes = 0; last_edge = TCNT1; TCCR1B |= prescale; /* Start timer */ } /* We "hunt" for a block until we find this bit pattern; same as ABC800 */ #define CAS_SYNC_PATTERN 0x0216 /* SYNC + STX */ #define CAS_BLOCK_LEN (1+2+253+1+2) /* type+num+data+ETX+checksum */ /* Finish a bit due to clock edge or timeout */ static inline ATTR_ALWAYS_INLINE void finish_bit(void) { if (!started) return; if (bytes) { if (--bits == 0) { uint8_t outbyte = data >> 8; fmrx_recv_byte(outbyte); data = 0; /* To avoid false sync */ bits = 8; if (--bytes == 0) fmrx_led_off(); } } else if (data == CAS_SYNC_PATTERN) { bits = 8; bytes = CAS_BLOCK_LEN; fmrx_led_on(); } } /* Interrupt routine for edge capture */ ISR(TIMER1_CAPT_vect) { uint16_t edge, delta; TCCR1B ^= (1 << ICES1); /* Next edge -> opposite polarity */ edge = ICR1; delta = edge - last_edge; if (delta < one_bit_lo) { /* Early edge, no action */ return; } else if (delta < one_bit_hi) { /* A data bit edge */ data |= (1 << 15); } else { /* Clock edge */ last_edge = edge; enable_timeout(edge + one_bit_timeout); finish_bit(); started = true; data >>= 1; } } /* Interrupt routine for bit timeout */ ISR(TIMER1_COMPA_vect) { disable_timeout(); finish_bit(); started = false; }