/* * 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" static uint8_t parity = 0; /* * 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 */ int nd = fmtx_next_byte(); if (nd < 0) { /* * Nothing to send. Disable interrupt. */ UCSR1B &= ~(1 << UDRIE1); return; } data = nd; left = 8; } pattern = fm_pat[data & PATTERN_BIT_MASK] ^ parity; UCSR1A = (1 << TXC1); /* Make TXC1 bit useful */ 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) { const uint8_t ucsr1a_mask = (1 << UDRE1) + (1 << TXC1); /* Wait until UDRE1 and TXC1 are both set */ while ((UCSR1A & ucsr1a_mask) != ucsr1a_mask) 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) { UCSR1B = 0; /* Disable transmitter and ISR */ UCSR1A = (1 << TXC1); /* Clear TXC bit */ DDRD |= 0x28; /* PD3 = outdata, PD5 = XCK/TXLED */ PORTD = (PORTD & ~0x28) | 0x20 | (parity & 0x08); /* * 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); /* * Output the current idle state of the line. This will also * cause the TXC bit to get set afterwards. */ UDR1 = parity; /* * Enable transmitter ISR (which may immediately disable itself...) */ fmtx_enable(); }