-
Notifications
You must be signed in to change notification settings - Fork 0
/
bufserial.c
351 lines (295 loc) · 7.61 KB
/
bufserial.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
#include "bufserial.h"
/** Serial buffers */
uint8_t serRxBuffer[SER_BUF_SIZE];
uint8_t serTxBuffer[SER_BUF_SIZE];
/** Index into rx/txBuffer. Start = where to enqueue new bytes; End = byte currently being sent. */
volatile uint8_t serRxBufferStart = 0, serRxBufferEnd = 0;
volatile uint8_t serTxBufferStart = 0, serTxBufferEnd = 0;
/**
* Set up all the registers needed for operation, flush buffers
*/
void serInit(void) {
serTxBufferStart = 0;
serTxBufferEnd = 0;
serRxBufferStart = 0;
serRxBufferEnd = 0;
// Initialize async serial port
SER_PORT_TXD_EN();
SER_PORT.CTRLA = 0;
SER_PORT.CTRLB = USART_RXEN_bm | USART_TXEN_bm;
SER_PORT.CTRLC = USART_CHSIZE_8BIT_gc;
// 115.2k baud
// SER_PORT.BAUDCTRLA = (2094 & 0xff) << USART_BSEL_gp;
// SER_PORT.BAUDCTRLB = ((-7) << USART_BSCALE_gp) | ((2094 >> 8) << USART_BSEL_gp);
// 230.4k baud
SER_PORT.BAUDCTRLA = (983 & 0xff) << USART_BSEL_gp;
SER_PORT.BAUDCTRLB = ((-7) << USART_BSCALE_gp) | ((983 >> 8) << USART_BSEL_gp);
SER_ENABLE_RXC();
// Enable interrupt sources
PMIC.CTRL |= PMIC_LOLVLEN_bm;
sei();
while (!serRxBufferEmpty()) {
serGetByte();
}
}
/**
* Purge the transmit buffer.
*/
void serWaitTransmit(void) {
while (!serTxBufferEmpty()) {}
}
/**
* Retrieves a byte from the receive queue.
* @return byte dequeued, or zero if empty.
* (Check rxBufferEmpty() before executing to ensure no ambiguity.)
*/
uint8_t serGetByte(void) {
uint8_t r = 0;
// Disable interrupt for atomic access
SER_DISABLE_RXC();
if (!serRxBufferEmpty()) {
// More bytes to go, pop off the next one
r = serRxBuffer[serRxBufferEnd++];
if (serRxBufferEnd >= SER_BUF_SIZE) {
serRxBufferEnd = 0;
}
}
SER_ENABLE_RXC();
return r;
}
/**
* Places a byte into the transmit queue, and starts
* the transmitter if it wasn't already running.
* @param b byte to enqueue
* @return number of bytes not queued:
* false if successful, true if buffer full.
*/
bool serPutByte(uint8_t b) {
bool r = false;
// Disable interrupt for atomic access
SER_DISABLE_DRE();
if (serTxBufferFull()) {
// no room, just leave
r = true;
} else {
// enqueue the byte
serTxBuffer[serTxBufferStart++] = b;
if (serTxBufferStart >= SER_BUF_SIZE) {
serTxBufferStart = 0;
}
}
SER_ENABLE_DRE();
return r;
}
/**
* Places an ASCII formatted hexadecimal number into the transmit
* queue, and starts the transmitter if it wasn't already running.
* @param n number to transmit
* @return number of bytes enqueued
*/
uint8_t serPutNumHex(uint16_t n) {
uint8_t l, tmpBuf[8];
utoa(n, (char*)tmpBuf, 16);
l = strlen((char*)tmpBuf);
return l - serPutString(l, tmpBuf);
}
/**
* Places an ASCII formatted hexadecimal number into the transmit
* queue, and starts the transmitter if it wasn't already running.
* Fixed length (padded with leading zeroes if applicable).
* @param n number to transmit
* @return number of bytes enqueued
*/
uint8_t serPutNumHexFixed_w(uint16_t n) {
uint8_t b, i;
b = n >> 8;
for (i = 0; i < 4; i++) {
if (i == 2) {
b = n;
}
b = b >> 4 | b << 4;
serPutByte(nibbleToAscii(b));
}
return i;
}
/**
* Places an ASCII formatted hexadecimal number into the transmit
* queue, and starts the transmitter if it wasn't already running.
* Fixed length (padded with leading zeroes if applicable).
* @param n number to transmit
* @return number of bytes enqueued
*/
uint8_t serPutNumHexFixed_b(uint8_t n) {
return serPutByte(nibbleToAscii(n >> 4 | n << 4))
+ serPutByte(nibbleToAscii(n));
}
/**
* Places an ASCII formatted decimal number into the transmit
* queue, and starts the transmitter if it wasn't already running.
* @param n number to transmit
* @return number of bytes enqueued
*/
uint8_t serPutNumDec(uint16_t n) {
uint8_t l, tmpBuf[8];
utoa(n, (char*)tmpBuf, 10);
l = strlen((char*)tmpBuf);
return l - serPutString(l, tmpBuf);
}
/**
* Places an ASCII formatted decimal number into the transmit
* queue, and starts the transmitter if it wasn't already running.
* @param n number to transmit
* @return number of bytes enqueued
*/
uint8_t serPutNumDecL(uint32_t n) {
uint8_t l, tmpBuf[12];
ultoa(n, (char*)tmpBuf, 10);
l = strlen((char*)tmpBuf);
return l - serPutString(l, tmpBuf);
}
/**
* Copies a buffer into the transmit queue, and starts
* the transmitter if it wasn't already running.
* Queuing starts from the beginning of buf and ends at
* one less than buf + len.
* @param len number of bytes
* @param buf buffer to enqueue
* @return number of bytes remaining in buf. Zero when successful.
*/
uint8_t serPutString(uint8_t len, uint8_t* buf) {
uint8_t i;
// Disable interrupt for atomic access
SER_DISABLE_DRE();
for (i = 0; len; i++, len--) {
if (serPutByte(buf[i])) {
break;
}
// if (serTxBufferFull()) {
// // no room, just leave
// break;
// } else {
// // enqueue the byte
// serTxBuffer[serTxBufferStart++] = buf[i];
// if (serTxBufferStart >= SER_BUF_SIZE) {
// serTxBufferStart = 0;
// }
// }
}
SER_ENABLE_DRE();
return len;
}
/**
* Copies a PROGMEM string into the transmit queue, and starts
* the transmitter if it wasn't already running.
* @param s PROGMEM string to transmit
* @return number of bytes written (compare with strlen_P(s)),
* up to a maximum of 255 bytes.
*/
uint8_t serPutStringPgm(const uint8_t* s) {
uint8_t l = strlen_P((char*)s);
return l - serPutStringPgmL(l, s);
}
/**
* Copies a PROGMEM string into the transmit queue, and starts
* the transmitter if it wasn't already running.
* Stops if the transmit buffer is full; in that case, the
* return is nonzero.
* @param len Length of the string to transmit
* @param s PROGMEM string to transmit
* @return number of bytes remaining.
*/
uint8_t serPutStringPgmL(uint8_t len, const uint8_t* s) {
// Disable interrupt for atomic access
SER_DISABLE_DRE();
while (len--) {
if (serTxBufferFull()) {
// no room, just leave
break;
}
// enqueue the byte
serTxBuffer[serTxBufferStart++] = pgm_read_byte(s++);
if (serTxBufferStart >= SER_BUF_SIZE) {
serTxBufferStart = 0;
}
}
SER_ENABLE_DRE();
return len;
}
/**
* Checks if the serial receive buffer is full.
*/
inline bool serRxBufferFull() {
uint8_t s = serRxBufferStart + 1;
if (s > SER_BUF_SIZE) {
s = 0;
}
return s == serRxBufferEnd;
}
/**
* Checks if the serial receive buffer is empty.
*/
inline bool serRxBufferEmpty() {
return serRxBufferStart == serRxBufferEnd;
}
/**
* Checks if the serial transmit buffer is full.
*/
inline bool serTxBufferFull(void) {
uint8_t s = serTxBufferStart + 1;
if (s > SER_BUF_SIZE) {
s = 0;
}
return s == serTxBufferEnd;
}
/**
* Checks if the serial transmit buffer is empty.
*/
inline bool serTxBufferEmpty(void) {
return serTxBufferStart == serTxBufferEnd;
}
/**
* Converts the low 4 bits of c to ASCII coded hexadecimal.
*/
uint8_t nibbleToAscii(uint8_t c) {
c &= 0x0f;
if (c >= 10) {
c += 'A' - 10;
} else {
c += '0';
}
return c;
}
/* * * Interrupt Handlers * * */
/**
* Asynchronous serial port buffered receive handler
*/
ISR(SER_PORT_RXC_v) {
if (serRxBufferFull()) {
// Can't do much but refuse additional inputs
SER_DISABLE_RXC();
//if (serAsciiMode) {
// serPutByte('\b'); // Ding on buffer overflow
//}
} else {
// enqueue the byte
serRxBuffer[serRxBufferStart++] = SER_PORT.DATA;
if (serRxBufferStart >= SER_BUF_SIZE) {
serRxBufferStart = 0;
}
}
}
/**
* Asynchronous serial port buffered transmit handler
*/
ISR(SER_PORT_DRE_v) {
if (!serTxBufferEmpty()) {
// More bytes to go, pop off the next one
SER_PORT.DATA = serTxBuffer[serTxBufferEnd++];
if (serTxBufferEnd >= SER_BUF_SIZE) {
serTxBufferEnd = 0;
}
} else {
// Done, turn off interrupt
SER_DISABLE_DRE();
}
}