123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388 |
- /*
- * 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 BIT_PATTERN_LEN_LG2 2
- #define BITS_PER_PATTERN_BYTE (8 >> BIT_PATTERN_LEN_LG2)
- #define PATTERN_BIT_MASK ((1 << BITS_PER_PATTERN_BYTE) - 1)
- #include "fmpat.h"
- static void fmtx_set_speed(uint8_t divisor);
- /* Post-block silent pause, in pattern times */
- static unsigned int block_pause(uint8_t blktype, uint8_t divisor)
- {
- if (blktype == 0) /* data block */
- return 362 << BIT_PATTERN_LEN_LG2;
- else /* header block */
- return (3*362) << BIT_PATTERN_LEN_LG2;
- }
- /* Triple buffer: one being transmitted, one being received, and one ready. */
- static struct cas_block block[3];
- #define TX_BUSY 0
- #define TX_IDLE (1 << TXC1)
- /* Disable the TX ISR */
- 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 tx_data, tx_bits;
- static struct cas_block *blk = &block[0];
- uint8_t pattern;
- while (!tx_bits) {
- if (!blk->ready || !motor_relay()) {
- /* Hold the line for one pattern time, then idle */
- UCSR1A = TX_IDLE;
- UDR1 = parity;
- fmtx_disable(); /* Nothing left to do */
- return;
- } else if (blk->divisor) {
- fmtx_set_speed(blk->divisor);
- blk->divisor = 0;
- return;
- } else if (blk->offset < sizeof blk->data) {
- tx_data = ((const uint8_t *)&blk->data)[blk->offset++];
- tx_bits = 8;
- } else if (blk->pause) {
- /* Interblock silence; hold the line */
- UCSR1A = TX_IDLE;
- UDR1 = parity;
- blk->pause--;
- return;
- } else {
- blk->ready = false; /* Free buffer */
- blk = blk->next;
- }
- }
- /* Actual data bits available */
- pattern = fm_pat[tx_data & PATTERN_BIT_MASK] ^ parity;
- tx_data >>= BITS_PER_PATTERN_BYTE;
- parity = -((int8_t)pattern < 0);
- tx_bits -= 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();
- }
- static inline uint16_t divisor_to_ubrr(uint8_t divisor)
- {
- uint16_t ubrr;
- if (!divisor)
- divisor = CAS_DIVISOR_ABC80;
- #if F_CPU != 16000000
- #error "Assuming F_CPU is 16 MHz"
- #endif
- ubrr = ((uint16_t)divisor << (8-BIT_PATTERN_LEN_LG2))/3 - 1;
- if (ubrr > 4095)
- ubrr = 4095;
- return ubrr;
- }
- static void fmtx_set_speed(uint8_t divisor)
- {
- if (!divisor)
- divisor = CAS_DIVISOR_ABC80;
- 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 = divisor_to_ubrr(divisor);
- /*
- * Enable transmitter ISR (which probably will immediately
- * go to idle...)
- */
- fmtx_enable();
- }
- static enum rx_state {
- rx_amp, /* Waiting for & */
- rx_cmd, /* & received */
- rx_filename, /* Getting filename */
- rx_num, /* Getting numeric arguments */
- rx_data, /* Receiving data */
- rx_idle = rx_num /* Root state */
- } rx_state;
- static unsigned int rx_blk_cnt;
- static struct cas_block *rx_blk;
- void fmtx_init(void)
- {
- int i;
- /* Fill in the invariant part of the block buffers */
- memset(block, 0, sizeof block);
- block[0].next = &block[1];
- block[1].next = &block[2];
- block[2].next = &block[0];
- rx_blk = &block[0];
- rx_state = rx_idle;
- for (i = 0; i <= 2; i++) {
- memset(block[i].data.sync, 0x16, sizeof block[i].data.sync);
- block[i].data.stx = 0x02;
- block[i].data.etx = 0x03;
- }
- fmtx_set_speed(CAS_DIVISOR_ABC80);
- }
- bool fmtx_full(void)
- {
- return rx_blk->ready;
- }
- static void fmtx_wait_for_free_buffer(void)
- {
- while (rx_blk->ready)
- do_usb_tasks();
- }
- static enum rx_state setup_block(unsigned int blkno)
- {
- uint8_t blktype;
- rx_blk->data.blkno = blkno;
- blktype = -(blkno == -1U);
- rx_blk->data.blktype = blktype;
- rx_blk->pause = block_pause(blktype, rx_blk->divisor);
- /* Initialize checksum */
- rx_blk->data.csum = 0x03 /* ETX is part of the checksum */
- + blktype + (uint8_t)blkno + (uint8_t)(blkno >> 8);
- /* Start at beginning of data */
- rx_blk->offset = 0;
- return rx_data;
- }
- static uint16_t checksum_range(const uint8_t *data, unsigned int len)
- {
- uint16_t csum = 0;
- while (len--)
- csum += *data++;
- return csum;
- }
- #define MAX_ARGS 4
- static enum rx_state do_cmd(uint8_t cmd, const uint16_t *arg)
- {
- switch (cmd) {
- case 'U':
- /* Firmware update, kick us to boot loader */
- /* &U */
- go_to_bootloader();
- return rx_idle;
- case 'R':
- /* Set receive baudrate divisor */
- /* &R divisor */
- fmtx_wait_for_free_buffer();
- rx_blk->divisor = arg[0];
- return rx_idle;
- case 'H':
- /* Send header block */
- /* &H */
- rx_blk_cnt = 1;
- return setup_block(-1);
- case 'D':
- /* Send data block */
- /* &D blocknr */
- rx_blk_cnt = 1;
- return setup_block(arg[0]);
- case 'F':
- {
- /* Send file */
- /* &F filename,blocks[,divisor] */
- uint8_t divisor = arg[1];
- if (divisor == CAS_DIVISOR_ABC80)
- divisor = 0;
- setup_block(-1);
- rx_blk_cnt = arg[0];
- rx_blk->divisor = CAS_DIVISOR_ABC80; /* Header always slow */
- rx_blk->data.hdr.divisor = divisor;
- memset(rx_blk->data.hdr.zero, 0, sizeof rx_blk->data.hdr.zero);
- rx_blk->data.hdr.nblocks = rx_blk_cnt;
- /* Checksum filename and divisor */
- rx_blk->data.csum += checksum_range(rx_blk->data.data, 12);
- rx_blk->data.csum += (uint8_t)rx_blk_cnt
- + (uint8_t)(rx_blk_cnt >> 8);
- rx_blk->ready = true;
- rx_blk = rx_blk->next;
- if (!rx_blk_cnt)
- return rx_idle;
- if (divisor) {
- fmtx_wait_for_free_buffer();
- rx_blk->divisor = divisor;
- }
- return rx_data;
- }
- case 'P':
- /* Pause (produce silence) */
- /* &P byte_times */
- if (arg[0]) {
- fmtx_wait_for_free_buffer();
- rx_blk->pause = arg[0] << BIT_PATTERN_LEN_LG2;
- rx_blk->offset = sizeof rx_blk->data; /* No data */
- rx_blk->ready = true;
- }
- return rx_idle;
- default:
- return rx_idle; /* Unknown/unimplemented command */
- }
- }
- void fmtx_recv_byte(uint8_t byte)
- {
- static uint8_t cmd; /* Command byte */
- static uint8_t ctr; /* State-specific counter */
- static uint16_t arg[4];
- switch (rx_state) {
- case rx_amp:
- if (byte == '&')
- rx_state = rx_cmd;
- break;
- case rx_cmd:
- ctr = 0;
- memset(arg, 0, sizeof arg);
- cmd = byte;
- if (byte == 'F') {
- memset(rx_blk->data.hdr.filename, ' ',
- sizeof rx_blk->data.hdr.filename);
- rx_state = rx_filename;
- } else if (byte > ' ' && byte <= '~')
- rx_state = rx_num;
- else
- rx_state = rx_idle;
- break;
- case rx_filename:
- switch (byte) {
- case '\n':
- ctr = 0;
- rx_state = do_cmd(cmd, arg);
- break;
- case '.':
- memset(rx_blk->data.hdr.filename+8, ' ', 3);
- ctr = 8;
- break;
- case ':':
- ctr = 0;
- break;
- case ',':
- rx_state = rx_num;
- break;
- default:
- if (byte <= ' ' || byte > '~' || ctr >= 11)
- break;
- if (byte & 0x40)
- byte &= ~0x20; /* Upper case */
- rx_blk->data.hdr.filename[ctr++] = byte;
- break;
- }
- break;
- case rx_num:
- if (byte == '\n') {
- ctr = 0;
- rx_state = do_cmd(cmd, arg);
- break;
- } else if (byte == ',') {
- ctr++;
- } else if (ctr < MAX_ARGS) {
- byte -= '0';
- if (byte < 10)
- arg[ctr] = arg[ctr]*10 + byte;
- }
- break;
- case rx_data:
- rx_blk->data.data[ctr++] = byte;
- rx_blk->data.csum += byte;
- if (ctr >= 253) {
- uint16_t nextblk = rx_blk->data.blkno + 1;
- rx_blk->ready = true;
- rx_blk = rx_blk->next;
- fmtx_enable();
- if (--rx_blk_cnt) {
- fmtx_wait_for_free_buffer();
- setup_block(nextblk);
- } else {
- rx_state = rx_idle;
- }
- }
- break;
- }
- }
|