123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143 |
- /*
- * 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();
- }
|