]> cloudbase.mooo.com Git - z180-stamp.git/blob - avr/serial.c
4b3cd42c580e0ca95d8f7a136e7878a6f81925b1
[z180-stamp.git] / avr / serial.c
1 /*
2 */
3
4 #include <avr/io.h>
5 #include <avr/interrupt.h>
6 #include <util/atomic.h>
7 #include <errno.h>
8 #include <stdio.h>
9
10 #include "serial.h"
11
12
13 static int _write(char c, FILE *stream);
14 static FILE mystdout = FDEV_SETUP_STREAM(_write,
15 NULL, _FDEV_SETUP_WRITE);
16
17 #define CTL_S ('S'-'@') /* DC3 (Stop) */
18 #define CTL_Q ('Q'-'@') /* DC1 (Start) */
19
20 typedef enum {
21 IDLE,
22 ACTIVE,
23 REQ_STOP,
24 REQ_CONT,
25 STOPPED
26 } xmit_stat_t;
27
28
29 static volatile uint8_t stat_tx;
30 static volatile uint8_t stat_rx;
31
32
33 struct ring {
34 uint8_t *data;
35 uint_fast8_t mask;
36 volatile uint_fast8_t begin;
37 volatile uint_fast8_t end;
38 };
39
40
41 #define BUFFER_SIZE 256
42
43 #if ((BUFFER_SIZE-1) & BUFFER_SIZE)
44 # error: BUFFER_SIZE not power of 2
45 #endif
46
47 #if ((BUFFER_SIZE) > 256)
48 # error: BUFFER_SIZE
49 #endif
50
51 struct ring rx_ring;
52 struct ring tx_ring;
53 uint8_t rx_ring_buffer[BUFFER_SIZE];
54 uint8_t tx_ring_buffer[BUFFER_SIZE];
55
56
57 static void ring_init(struct ring *ring, uint8_t *buf, int size)
58 {
59 ring->data = buf;
60 ring->mask = (size-1) & 0xff;
61 ring->begin = 0;
62 ring->end = 0;
63 }
64
65 static int ring_write_ch(struct ring *ring, uint8_t ch)
66 {
67 uint_fast8_t ep = (ring->end + 1) & ring->mask;
68
69 if ((ep) != ring->begin) {
70 ring->data[ring->end] = ch;
71 ring->end = ep;
72 return 1;
73 }
74
75 return -1;
76 }
77
78 #if 0
79 static int ring_write(struct ring *ring, uint8_t *data, int size)
80 {
81 int i;
82
83 for (i = 0; i < size; i++) {
84 if (ring_write_ch(ring, data[i]) < 0)
85 return -i;
86 }
87
88 return i;
89 }
90 #endif
91
92 static int ring_read_ch(struct ring *ring)
93 {
94 int ret = -1;
95 uint_fast8_t i = ring->begin;
96
97 if (i != ring->end) {
98 ret = ring->data[i];
99 ring->begin = (i +1) & ring->mask;
100 }
101
102 return ret;
103 }
104
105
106 static int_fast8_t ring_is_empty(struct ring *ring)
107 {
108 return ring->begin == ring->end;
109 }
110
111
112 /* Initialize UART */
113
114 void usart0_setup(void) {
115
116 ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
117
118 PRR0 &= ~_BV(PRUSART0);
119 UCSR0B = 0;
120
121 /* Initialize ring buffers. */
122 ring_init(&rx_ring, rx_ring_buffer, BUFFER_SIZE);
123 ring_init(&tx_ring, tx_ring_buffer, BUFFER_SIZE);
124
125 stat_tx = ACTIVE;
126 stat_rx = ACTIVE;
127
128 UCSR0A = _BV(U2X0);
129 UBRR0L = F_CPU / BAUD / 8 - 1;
130 UCSR0B = _BV(RXCIE0) | _BV(RXEN0) | _BV(TXEN0);
131 UCSR0C = 3 << UCSZ00;
132 };
133 }
134
135
136
137 /* UART RXC interrupt */
138
139 ISR(USART0_RX_vect)
140 {
141 uint8_t d;
142
143 d = UDR0;
144
145 switch (d) {
146 case CTL_S:
147 stat_tx = REQ_STOP;
148 break;
149 case CTL_Q:
150 if ((stat_tx == STOPPED) || stat_tx == REQ_STOP) {
151 UCSR0B = _BV(RXCIE0) | _BV(RXEN0) | _BV(TXEN0) | _BV(UDRIE0);
152 stat_tx = ACTIVE;
153 }
154 break;
155 default:
156 ring_write_ch(&rx_ring, d);
157 break;
158 }
159 }
160
161 /* UART UDRE interrupt */
162
163 ISR(USART0_UDRE_vect)
164 {
165 uint8_t s;
166
167 s = !ring_is_empty(&tx_ring);
168 if ((s == 0) || (stat_tx != ACTIVE)) {
169 UCSR0B = _BV(RXCIE0) | _BV(RXEN0) | _BV(TXEN0);
170 if (s)
171 stat_tx = STOPPED;
172 } else {
173 UDR0 = ring_read_ch(&tx_ring);
174 }
175 }
176
177
178
179 /*--------------------------------------------------------------------------*/
180
181 void serial_setup(void)
182 {
183 stdout = &mystdout;
184 usart0_setup();
185 }
186
187 /*--------------------------------------------------------------------------*/
188
189 int _write(char c, FILE *stream)
190 {
191 (void) stream;
192
193 if (c == '\n')
194 serial_putc('\r');
195 serial_putc(c);
196
197 return 0;
198 }
199
200 int serial_getc(void)
201 {
202 return ring_read_ch(&rx_ring);
203 }
204
205 void serial_putc(uint8_t data)
206 {
207 while (ring_write_ch(&tx_ring, data) < 0)
208 ;
209 switch (stat_tx) {
210 case ACTIVE:
211 default:
212 /* Enable the TXE interrupt. */
213 UCSR0B = _BV(RXCIE0) | _BV(RXEN0) | _BV(TXEN0) | _BV(UDRIE0);
214 break;
215 }
216 }
217