/* * 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); } static uint8_t parity; /* * Send stuff if we have anything to send */ ISR(USART1_UDRE_vect) { static uint8_t data, left; uint8_t pattern; if (!left) { /* Fetch a new byte if there is one, unless motor is off */ int nd; nd = motor_relay ? fmtx_next_byte() : -1; if (nd < 0) { /* * Nothing to send. Disable the interrupt and * send out one last edge. Afterwards, the hardware * will drive the line high. */ fmtx_disable(); UCSR1A = TX_IDLE; UDR1 = parity; /* One last edge before going high */ parity = 0; return; } data = nd; left = 8; } pattern = fm_pat[data & PATTERN_BIT_MASK] ^ ~parity; UCSR1A = TX_IDLE; UDR1 = pattern; data >>= BITS_PER_PATTERN_BYTE; left -= BITS_PER_PATTERN_BYTE; parity = -((int8_t)pattern >= 0); } /* * 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_task(); } /* * 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 = 0; /* We start out with a high idle */ UCSR1B = 0; /* Disable transmitter and ISR */ UCSR1A = (1 << TXC1); /* Clear TXC bit */ DDRD |= 0x28; /* PD3 = outdata, PD5 = XCK/TXLED */ PORTD |= 0x28; /* Drive data and clock high while idle */ /* * 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(); }