| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152 | /* * 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;	TIFR1 = TIMSK1 = (1 << ICIE1) | (1 << OCIE1A);}static inline void disable_timeout(void){	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 */	}	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 = (TCCR1B & ~7) | prescale;}/* 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 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;}
 |