]>
Commit | Line | Data |
---|---|---|
0c5890bb L |
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 |