/* * Transmit an FM-modulated signal using the USART in SPI master mode. * As per ABC standard, this signal is transmitted MSB first. */ #include "usbcas.h" #define BITS_PER_PATTERN_BYTE 2 #define PATTERN_BIT_MASK ((1 << BITS_PER_PATTERN_BYTE) - 1) #include "fmpat.h" #define TX_BUSY 0 #define TX_IDLE (1 << TXC1) static inline ATTR_ALWAYS_INLINE void fmtx_disable(void) { UCSR1B &= ~(1 << UDRIE1); PORTB |= (1 << 0); } static uint8_t parity; /* * Send stuff if we have anything to send */ ISR(USART1_UDRE_vect) { static uint8_t data, left, gap; static uint16_t block_bytes; uint8_t pattern; if (!left) { /* Fetch a new byte if there is one, unless motor is off */ int nd; if (!motor_relay() || (nd = fmtx_next_byte()) < 0) { /* * Nothing to send. Hold the line for some * number of byte-times in case we suffered an * underrun. Afterwards, the hardware will * drive the line high. */ UCSR1A = TX_IDLE; UCSR1A = parity; if (!++gap) fmtx_disable(); return; } data = nd; if (!block_bytes) { if (data == 0x02) { /* * Beginning of a data block, suppress * FF = silence until the end of the block * * STX+header+data+ETX+csum */ block_bytes = 1+3+253+1+2; } } else { block_bytes--; } data = nd; left = 8; gap = 0; } if (!block_bytes && data == 0xff) { pattern = parity; } else { pattern = fm_pat[data & PATTERN_BIT_MASK] ^ parity; data >>= BITS_PER_PATTERN_BYTE; parity = -((int8_t)pattern < 0); } left -= BITS_PER_PATTERN_BYTE; UCSR1A = TX_IDLE; UDR1 = pattern; } /* * Synchronize transmitter before reconfiguration */ void fmtx_drain(void) { /* Wait until input queue is idle and we have had at least one output */ while ((UCSR1B & (1 << UDRIE1)) || !(UCSR1A & TX_IDLE)) do_usb_tasks(); } /* * Initialize the USART hardware and set transmission baudrate. * This is mostly the same as SerialSPI_Init(). * Returns the actual baud rate adjusted for underflow. */ #define FMTX_UBRRVAL(b) (SERIAL_SPI_UBBRVAL((b) * (8/BITS_PER_PATTERN_BYTE))) #define UBRR_MAX 4095 #define FMTX_MIN_BAUD (F_CPU / (2 * (8/BITS_PER_PATTERN_BYTE) * (UBRR_MAX+1UL))) #define FMTX_MAX_BAUD (F_CPU / (2 * (8/BITS_PER_PATTERN_BYTE))) #if FMTX_MIN_BAUD > CAS_BAUDRATE_ABC80 # error "UBRR overflows for standard ABC80 baud rate" #endif uint32_t fmtx_real_baudrate(uint32_t baudrate) { if (baudrate < FMTX_MIN_BAUD || baudrate > FMTX_MAX_BAUD) baudrate = CAS_BAUDRATE_ABC80; return baudrate; } void fmtx_init_speed(uint32_t baudrate) { parity = 0xff; /* We start out with a high idle */ DDRD |= 0x28; /* PD3 = outdata, PD5 = XCK/TXLED */ PORTD |= 0x28; /* Drive data and clock high while idle */ UCSR1B = 0; /* Disable transmitter and ISR */ /* * SPI master mode, mode 1, LSB first * UCPHA doesn't matter, but have UCPOL=1 to that the clock * is 1 when idle and the TXLED is OFF. */ UCSR1C = (3 << UMSEL10) | (1 << UDORD1) | (1 << UCPOL1); /* UBRR must be zero when the transmitter is enabled */ UBRR1 = 0; /* Enable transmitter, but not receiver and not the ISR (yet) */ UCSR1B = (1 << TXEN1); /* Configure baud rate */ UBRR1 = FMTX_UBRRVAL(baudrate); /* * Enable transmitter ISR (which probably will immediately * go to idle...) */ fmtx_enable(); }