/* */ #include #include #include #include #include #include "serial.h" static int _write(char c, FILE *stream); static FILE mystdout = FDEV_SETUP_STREAM(_write, NULL, _FDEV_SETUP_WRITE); #define CTL_S ('S'-'@') /* DC3 (Stop) */ #define CTL_Q ('Q'-'@') /* DC1 (Start) */ typedef enum { IDLE, ACTIVE, REQ_STOP, REQ_CONT, STOPPED } xmit_stat_t; static volatile uint8_t stat_tx; static volatile uint8_t stat_rx; struct ring { uint8_t *data; uint_fast8_t mask; volatile uint_fast8_t begin; volatile uint_fast8_t end; }; #define BUFFER_SIZE 256 #if ((BUFFER_SIZE-1) & BUFFER_SIZE) # error: BUFFER_SIZE not power of 2 #endif #if ((BUFFER_SIZE) > 256) # error: BUFFER_SIZE #endif struct ring rx_ring; struct ring tx_ring; uint8_t rx_ring_buffer[BUFFER_SIZE]; uint8_t tx_ring_buffer[BUFFER_SIZE]; static void ring_init(struct ring *ring, uint8_t *buf, int size) { ring->data = buf; ring->mask = (size-1) & 0xff; ring->begin = 0; ring->end = 0; } static int ring_write_ch(struct ring *ring, uint8_t ch) { uint_fast8_t ep = (ring->end + 1) & ring->mask; if ((ep) != ring->begin) { ring->data[ring->end] = ch; ring->end = ep; return 1; } return -1; } #if 0 static int ring_write(struct ring *ring, uint8_t *data, int size) { int i; for (i = 0; i < size; i++) { if (ring_write_ch(ring, data[i]) < 0) return -i; } return i; } #endif static int ring_read_ch(struct ring *ring) { int ret = -1; uint_fast8_t i = ring->begin; if (i != ring->end) { ret = ring->data[i]; ring->begin = (i +1) & ring->mask; } return ret; } static int_fast8_t ring_is_empty(struct ring *ring) { return ring->begin == ring->end; } /* Initialize UART */ void usart0_setup(void) { ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { PRR0 &= ~_BV(PRUSART0); UCSR0B = 0; /* Initialize ring buffers. */ ring_init(&rx_ring, rx_ring_buffer, BUFFER_SIZE); ring_init(&tx_ring, tx_ring_buffer, BUFFER_SIZE); stat_tx = ACTIVE; stat_rx = ACTIVE; UCSR0A = _BV(U2X0); UBRR0L = F_CPU / BAUD / 8 - 1; UCSR0B = _BV(RXCIE0) | _BV(RXEN0) | _BV(TXEN0); UCSR0C = 3 << UCSZ00; }; } /* UART RXC interrupt */ ISR(USART0_RX_vect) { uint8_t d; d = UDR0; switch (d) { case CTL_S: stat_tx = REQ_STOP; break; case CTL_Q: if ((stat_tx == STOPPED) || stat_tx == REQ_STOP) { UCSR0B = _BV(RXCIE0) | _BV(RXEN0) | _BV(TXEN0) | _BV(UDRIE0); stat_tx = ACTIVE; } break; default: ring_write_ch(&rx_ring, d); break; } } /* UART UDRE interrupt */ ISR(USART0_UDRE_vect) { uint8_t s; s = !ring_is_empty(&tx_ring); if ((s == 0) || (stat_tx != ACTIVE)) { UCSR0B = _BV(RXCIE0) | _BV(RXEN0) | _BV(TXEN0); if (s) stat_tx = STOPPED; } else { UDR0 = ring_read_ch(&tx_ring); } } /*--------------------------------------------------------------------------*/ void serial_setup(void) { stdout = &mystdout; usart0_setup(); } /*--------------------------------------------------------------------------*/ int _write(char c, FILE *stream) { (void) stream; if (c == '\n') serial_putc('\r'); serial_putc(c); return 0; } int serial_getc(void) { return ring_read_ch(&rx_ring); } void serial_putc(uint8_t data) { while (ring_write_ch(&tx_ring, data) < 0) ; switch (stat_tx) { case ACTIVE: default: /* Enable the TXE interrupt. */ UCSR0B = _BV(RXCIE0) | _BV(RXEN0) | _BV(TXEN0) | _BV(UDRIE0); break; } }