/* * Transmit an FM-modulated signal using the USART in SPI master mode. * As per ABC standard, this signal is transmitted LSB first. */ #include "usbcas.h" #define BITS_PER_PATTERN_BYTE 2 #define PATTERN_BIT_MASK ((1 << BITS_PER_PATTERN_BYTE) - 1) #include "fmpat.h" static uint8_t parity = 0; static uint8_t data; static uint8_t left; static inline bool fmtx_send_ready(void) ATTR_ALWAYS_INLINE; static inline bool fmtx_send_ready(void) { return !!(UCSR1A & (1 << UDRE1)); } /* * Sends another chunk of output. */ uint8_t fmtx_send_more(void) { uint8_t l = left; if (!fmtx_send_ready()) return l; /* Not ready yet */ uint8_t d = data; uint8_t pattern = fm_pat[d & PATTERN_BIT_MASK] ^ parity; UCSR1A = (1 << TXC1); UDR1 = pattern; data = d >> BITS_PER_PATTERN_BYTE; left -= BITS_PER_PATTERN_BYTE; parity = -((int8_t)pattern < 0); return left; } /* * Begin sending a new byte. Use when fmtx_send_*() has returned zero. */ uint8_t fmtx_send_byte(uint8_t d) { data = d; left = 8; return fmtx_send_more(); } /* * Synchronize transmitter before reconfiguration */ void fmtx_drain(void) { while (!(UCSR1A & (1 << TXC1))) ; } /* * 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) { uint32_t ubrrval; ubrrval = FMTX_UBRRVAL(baudrate); DDRD |= 0x28; /* PD3 = outdata, PD5 = XCK/TXLED */ PORTD = (PORTD & ~0x28) | (parity & 0x20) | 0x08; UCSR1B = 0; /* Disable USART */ UCSR1A = 0; /* Clear error bits */ /* * 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 */ UCSR1B = (1 << TXEN1); /* Configure baud rate */ UBRR1 = ubrrval; /* Output the current idle state of the line */ UDR1 = parity; }