avr: initial commit
[z80.git] / avr / uart.c
1 /* interrupt-driven UART routines */
2
3 #include <stdio.h>
4
5 #include <avr/io.h>
6 #include <avr/interrupt.h>
7
8 #include "uart.h"
9
10 static volatile uint8_t uart_txbuf[UART_TXBUFSIZE];
11 static volatile uint8_t uart_tx_rp = 0;
12 static volatile uint8_t uart_tx_wp = 0;
13 static volatile uint8_t uart_rxbuf[UART_RXBUFSIZE];
14 static volatile uint8_t uart_rx_rp = 0;
15 static volatile uint8_t uart_rx_wp = 0;
16
17 /* global, external accessable variables */
18 volatile uint8_t uart_break = 0;        /* 0: no break detected */
19 volatile uint8_t uart_echo  = 0;        /* 0: disabled, otherwise enabled */
20
21 void uart_init(void)
22 {
23         UBRR0  = ((F_CPU + BAUDRATE * 8) / (BAUDRATE * 16) - 1);
24         UCSR0B = (1<<RXCIE0) | (1<<RXEN0) | (1<<TXEN0);
25         UCSR0C = (1<<UCSZ01) | (1<<UCSZ00);
26 }
27
28 uint8_t uart_rx_state(void)
29 {
30         /* number of bytes in Rx buffer */
31         return((uint8_t)(uart_rx_wp - uart_rx_rp) % UART_RXBUFSIZE);
32 }
33
34 uint8_t uart_tx_state(void)
35 {
36         /* number of bytes in Tx buffer */
37         return((uint8_t)(uart_tx_wp - uart_tx_rp) & UART_TXBUFSIZE);
38 }
39
40 int uart_putc(char c, FILE *stream)
41 {
42         if(c == '\n') uart_putc('\r', stream);
43
44         uint8_t tmp = (uart_tx_wp + 1) % UART_TXBUFSIZE;
45
46         /* buffer full -> block */
47         while(tmp == uart_tx_rp);
48
49         /* enqueue */
50         uart_txbuf[uart_tx_wp] = c;
51         uart_tx_wp = tmp;
52
53         /* enable UDRE interrupt */
54         UCSR0B |= (1<<UDRIE0);
55
56         return 0;
57 }
58
59 ISR(USART_UDRE_vect)
60 {
61         uint8_t tmp;
62
63         if(uart_tx_rp != uart_tx_wp) {
64                 /* dequeue */
65                 tmp = (uart_tx_rp + 1) % UART_TXBUFSIZE;
66                 UDR0 = uart_txbuf[uart_tx_rp];
67                 uart_tx_rp = tmp;
68         } else {
69                 /* buffer empty -> disable interrupt */
70                 UCSR0B &= ~(1<<UDRIE0);
71         }
72 }
73
74 int uart_getc(FILE *stream)
75 {
76         uint8_t tmp = (uart_rx_rp + 1) % UART_RXBUFSIZE;
77
78         /* empty buffer -> block */
79         while(uart_rx_rp == uart_rx_wp);
80
81         /* dequeue data */
82         uart_rx_rp = tmp;
83         unsigned char c = uart_rxbuf[uart_rx_rp];
84
85         if(uart_echo) {
86                 /* echo */
87                 if(c == '\r') uart_putc('\n', stream);
88                 uart_putc(c, stream);
89         }
90
91         return c;
92 }
93
94 ISR(USART_RX_vect) {
95         uint8_t tmp  = (uart_rx_wp + 1) % UART_RXBUFSIZE;
96         uint8_t ferr = UCSR0A & (1<<FE0);
97         uint8_t data = UDR0;
98
99         /* check for BREAK */
100         if(ferr && (data == 0)) {
101                 uart_break = 1;
102                 return;
103         }
104
105         /* enqueue if space in buffer */
106         if(uart_rx_rp != tmp) {
107                 uart_rx_wp = tmp;
108                 uart_rxbuf[uart_rx_wp] = data;
109         }
110 }
111
112 FILE uart_out = FDEV_SETUP_STREAM(uart_putc, NULL, _FDEV_SETUP_WRITE);
113 FILE uart_in  = FDEV_SETUP_STREAM(NULL, uart_getc, _FDEV_SETUP_READ);