-
Notifications
You must be signed in to change notification settings - Fork 0
/
lib_serial.cpp
344 lines (298 loc) · 12 KB
/
lib_serial.cpp
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
/*
Copyright 2013 Brad Quick
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// This library manages serial ports. It sets up input and output buffers and sends and receieves using interrupts.
// To use this code, define which ports to include and the size of their buffers in projectsettings.h
// Then use the library like this:
//
// lib_serial_initport(2,9600); // init serial port 2 to 9600 baud
// lib_serial_sendstring(2,"Send this string");
// lib_serial_sendchar(2,'.');
// lib_serial_senddata(2,"This is Data",4); // sends the first 4 characters of the string
// int count=lib_serial_numcharsavailable(2);
// if (count>0)
// {
// lib_serial_getdata(2,data,count);
// }
#include <avr/io.h>
#include <avr/interrupt.h>
#include "lib_serial.h"
#include "projectsettings.h"
// set the USESERIALPORTx flags and buffer sizes in projectsettings.h
// also set SERIALxOUTPUTBUFFERSIZE and SERIALxINPUTBUFFERSIZE projectsettings.h
#ifdef USESERIALPORT0
unsigned char serialtxbuffer0[SERIAL0OUTPUTBUFFERSIZE];
unsigned char serialrxbuffer0[SERIAL0INPUTBUFFERSIZE];
#endif
#ifdef USESERIALPORT1
unsigned char serialtxbuffer1[SERIAL1OUTPUTBUFFERSIZE];
unsigned char serialrxbuffer1[SERIAL1INPUTBUFFERSIZE];
#endif
#ifdef USESERIALPORT2
unsigned char serialtxbuffer2[SERIAL2OUTPUTBUFFERSIZE];
unsigned char serialrxbuffer2[SERIAL2INPUTBUFFERSIZE];
#endif
#ifdef USESERIALPORT3
unsigned char serialtxbuffer3[SERIAL3OUTPUTBUFFERSIZE];
unsigned char serialrxbuffer3[SERIAL3INPUTBUFFERSIZE];
#endif
#ifdef USESERIALPORTUSB
#include "lib_usb.h"
#endif
#ifdef USESERIALPORT3
#define NUMSERIALPORTS 4
#else
#ifdef USESERIALPORT2
#define NUMSERIALPORTS 3
#else
#ifdef USESERIALPORT1
#define NUMSERIALPORTS 2
#else
#define NUMSERIALPORTS 1
#endif
#endif
#endif
// for processors that don't number their serial ports:
// this hasn't been tested
#ifndef UCSR0B
#define UCSR0B UCSRB
#define RXEN0 RXEN
#define TXEN0 TXEN
#define UCSZ00 UCSZ0
#define UCSZ01 UCSZ1
#define UBRR0L UBRRL
#define UBRR0H UBRRH
#define RXCIE0 RXCIE
#define UDR0 UDR
#endif
unsigned char *serialtxbuffer[NUMSERIALPORTS];
int serialtxbuffersize[NUMSERIALPORTS];
volatile int serialtxbufferstartindex[NUMSERIALPORTS];
volatile int serialtxbufferendindex[NUMSERIALPORTS];
unsigned char *serialrxbuffer[NUMSERIALPORTS];
int serialrxbuffersize[NUMSERIALPORTS];
int serialrxbufferstartindex[NUMSERIALPORTS];
volatile int serialrxbufferendindex[NUMSERIALPORTS];
serialcallbackfunctptr serialrxcallback[NUMSERIALPORTS];
int lib_serial_availableoutputbuffersize(unsigned char serialportnumber)
{ // returns how many more bytes can fit in the outputbuffer
#ifdef USESERIALPORTUSB
if (serialportnumber==USBPORTNUMBER) return(9999);
#endif
int startindex=serialtxbufferstartindex[serialportnumber];
int endindex=serialtxbufferendindex[serialportnumber];
if (startindex>endindex)
return(startindex-endindex-1);
else
return(serialtxbuffersize[serialportnumber]-(endindex-startindex)-1);
}
void lib_serial_setrxcallback(unsigned char serialportnumber,serialcallbackfunctptr callback)
{
serialrxcallback[serialportnumber]=callback;
}
#define INITSERIALPORTMACRO(portnumber,ucsra,ucsrb,uscrc,rxen,txen,uscz0,uscz1,ubrrl,ubrrh,rxcie,utx,txbuffer,rxbuffer,txsize,rxsize)\
if (serialportnumber==portnumber)\
{\
serialtxbuffer[portnumber]=txbuffer;\
serialtxbuffersize[portnumber]=txsize;\
serialrxbuffer[portnumber]=rxbuffer;\
serialrxbuffersize[portnumber]=rxsize;\
\
ucsra = (1<<utx);\
ucsrb |= (1 << rxen) | (1 << txen); /* Turn on the transmission and reception circuitry */\
uscrc |= (1 << uscz0) | (1 << uscz1); /* Use 8-bit character sizes */\
\
ubrrl = baudprescale; /* Load lower 8-bits of the baud rate value into the low byte of the UBRR register */\
ubrrh = (baudprescale >> 8); /* Load upper 8-bits of the baud rate value into the high byte of the UBRR register */\
\
ucsrb |= (1 << rxcie); /* Enable the USART Recieve Complete */\
}
void lib_serial_initport(unsigned char serialportnumber,long baud)
{ // initialize the serial port and set up a read buffer and interrupts so that we don't lose any data from reading too slowly
#ifdef USESERIALPORTUSB
if (serialportnumber==USBPORTNUMBER)
{
lib_usb_init();
return;
}
#endif
unsigned long baudprescale=((F_CPU / 4 / baud -1) / 2);
serialtxbufferstartindex[serialportnumber]=serialtxbufferendindex[serialportnumber]=0;
serialrxbufferstartindex[serialportnumber]=serialrxbufferendindex[serialportnumber]=0;
#ifdef USESERIALPORT0
INITSERIALPORTMACRO(0,UCSR0A,UCSR0B,UCSR0C,RXEN0,TXEN0,UCSZ00,UCSZ01,UBRR0L,UBRR0H,RXCIE0,U2X0,serialtxbuffer0,serialrxbuffer0,SERIAL0OUTPUTBUFFERSIZE,SERIAL0INPUTBUFFERSIZE);
#endif
#ifdef USESERIALPORT1
INITSERIALPORTMACRO(1,UCSR1A,UCSR1B,UCSR1C,RXEN1,TXEN1,UCSZ10,UCSZ11,UBRR1L,UBRR1H,RXCIE1,U2X1,serialtxbuffer1,serialrxbuffer1,SERIAL1OUTPUTBUFFERSIZE,SERIAL1INPUTBUFFERSIZE);
#endif
#ifdef USESERIALPORT2
INITSERIALPORTMACRO(2,UCSR2A,UCSR2B,UCSR2C,RXEN2,TXEN2,UCSZ20,UCSZ21,UBRR2L,UBRR2H,RXCIE2,U2X2,serialtxbuffer2,serialrxbuffer2,SERIAL2OUTPUTBUFFERSIZE,SERIAL2INPUTBUFFERSIZE);
#endif
#ifdef USESERIALPORT3
INITSERIALPORTMACRO(3,UCSR3A,UCSR3B,UCSR3C,RXEN3,TXEN3,UCSZ30,UCSZ31,UBRR3L,UBRR3H,RXCIE3,U2X3,serialtxbuffer3,serialrxbuffer3,SERIAL3OUTPUTBUFFERSIZE,SERIAL3INPUTBUFFERSIZE);
#endif
sei(); // turn on all interrupts
}
#define SERIALSENDTHENEXTCHARMACRO(portnum,ucsrb,udrie,udr)\
if (serialportnumber==portnum)\
{\
if (serialtxbufferstartindex[serialportnumber]==serialtxbufferendindex[serialportnumber]) \
{ /* there's nothing left to send */\
/* turn off the transmit registre empty interrupt so it doesn't keep firing.*/\
/* we also use this as a flag to tell us whether we are currently sending or not.*/\
ucsrb &= ~(1 << udrie);\
}\
else\
{\
udr = serialtxbuffer[serialportnumber][serialtxbufferstartindex[serialportnumber]++];\
ucsrb |= ( 1 << udrie ); /* turn on the interrupt to tell us when we are ready to send another character*/\
if (serialtxbufferstartindex[serialportnumber]==serialtxbuffersize[serialportnumber]) serialtxbufferstartindex[serialportnumber]=0;\
}\
}
void serialsendthenextchar(unsigned char serialportnumber)
{ // this should only be called internally. It sends the next character in the send buffer to the hardware
#ifdef USESERIALPORT0
SERIALSENDTHENEXTCHARMACRO(0,UCSR0B,UDRIE0,UDR0);
#endif
#ifdef USESERIALPORT1
SERIALSENDTHENEXTCHARMACRO(1,UCSR1B,UDRIE1,UDR1);
#endif
#ifdef USESERIALPORT2
SERIALSENDTHENEXTCHARMACRO(2,UCSR2B,UDRIE2,UDR2);
#endif
#ifdef USESERIALPORT3
SERIALSENDTHENEXTCHARMACRO(3,UCSR3B,UDRIE3,UDR3);
#endif
}
#define SERIALSENDCHARMACRO(portnumber,ucsrb,udrie)\
if (serialportnumber==portnumber && !(ucsrb & ( 1 << udrie ))) serialsendthenextchar(serialportnumber);
void lib_serial_sendchar(unsigned char serialportnumber,unsigned char c)
{ // add a character to the send buffer
#ifdef USESERIALPORTUSB
if (serialportnumber==USBPORTNUMBER)
{
lib_usb_send(USB_CDC_TX,&c,1);
return;
}
#endif
sei();
// cli(); // disable interrupts while we do this so the interrupt handler doesn't change things on us
serialtxbuffer[serialportnumber][serialtxbufferendindex[serialportnumber]++]=c;
if (serialtxbufferendindex[serialportnumber]==serialtxbuffersize[serialportnumber]) serialtxbufferendindex[serialportnumber]=0;
// if we aren't already sending something, we need to get the ball rolling by sending the first character
// if the transmit buffer empty interrupt flag isn't set then we aren't currently sending anything.
#ifdef USESERIALPORT0
SERIALSENDCHARMACRO(0,UCSR0B,UDRIE0);
#endif
#ifdef USESERIALPORT1
SERIALSENDCHARMACRO(1,UCSR1B,UDRIE1);
#endif
#ifdef USESERIALPORT2
SERIALSENDCHARMACRO(2,UCSR2B,UDRIE2);
#endif
#ifdef USESERIALPORT3
SERIALSENDCHARMACRO(3,UCSR3B,UDRIE3);
#endif
// sei();
}
void lib_serial_sendstring(unsigned char serialportnumber,const char *string)
{ // adds the string to the output buffer.
while (*string) lib_serial_sendchar(serialportnumber,*string++);
}
void lib_serial_senddata(unsigned char serialportnumber,unsigned char *data,int datalength)
{ // send datalength bytes of data to the serial port
while (datalength-- >0) lib_serial_sendchar(serialportnumber,*data++);
}
int lib_serial_numcharsavailable(unsigned char serialportnumber)
{ // returns number of characters available in the rx buffer
int returnvalue;
#ifdef USESERIALPORTUSB
if (serialportnumber==USBPORTNUMBER)
{
return(lib_usb_charsavailable(USB_CDC_RX));
}
#endif
int endindex=serialrxbufferendindex[serialportnumber];
returnvalue=endindex-serialrxbufferstartindex[serialportnumber];
if (serialrxbufferstartindex[serialportnumber]>endindex)
returnvalue+=serialrxbuffersize[serialportnumber];
return(returnvalue);
}
unsigned char lib_serial_getchar(unsigned char serialportnumber)
{ // get the next character from the serial port
// I don't think we need to disable interrupts here since the interrupt handler shouldn't be
// changing any of the things we are looking at here.
#ifdef USESERIALPORTUSB
if (serialportnumber==USBPORTNUMBER)
{
return(lib_usb_recv(USB_CDC_RX));
}
#endif
unsigned char returnvalue=serialrxbuffer[serialportnumber][serialrxbufferstartindex[serialportnumber]++];
if (serialrxbufferstartindex[serialportnumber]==serialrxbuffersize[serialportnumber]) serialrxbufferstartindex[serialportnumber]=0;
return(returnvalue);
}
void lib_serial_getdata(unsigned char serialportnumber,unsigned char *data,int numchars)
{
for (int x=0;x<numchars;++x)
*data++=lib_serial_getchar(serialportnumber);
}
void serialaddcharactertoreceivebuffer(unsigned char serialportnumber,unsigned char c)
{ // used internally by the interrupt handler
sei();
// if there's a callback associated with this port, use it instead of our buffer
if (serialrxcallback[serialportnumber])
{
(*serialrxcallback[serialportnumber])(c);
}
else
{
serialrxbuffer[serialportnumber][serialrxbufferendindex[serialportnumber]++]=c;
if (serialrxbufferendindex[serialportnumber]==serialrxbuffersize[serialportnumber]) serialrxbufferendindex[serialportnumber]=0;
}
}
#define SERIALISRMACRO(number,txvector,rxvector,udr)\
ISR(txvector) \
{ /* we just finished sending a character from our transmit buffer. Send the next one.*/\
serialsendthenextchar(number);\
}\
\
ISR(rxvector)\
{ /* we just received an incoming character*/\
unsigned char c=udr;\
serialaddcharactertoreceivebuffer(number,c);\
}
// This interrupt gets called when the transmit buffer is ready for another byte.
#ifdef USESERIALPORT0
// unfortunately, different processors use different names for their interrupt vectors
#ifdef USART_RX_vect
#define USART0_RX_vect USART_RX_vect
#else
#ifdef USART_RXC_vect
#define USART0_RX_vect USART_RXC_vect
#endif
#endif
#ifdef USART_UDRE_vect
#define USART0_UDRE_vect USART_UDRE_vect
#endif
SERIALISRMACRO(0,USART0_UDRE_vect,USART0_RX_vect,UDR0);
#endif
#ifdef USESERIALPORT1
SERIALISRMACRO(1,USART1_UDRE_vect,USART1_RX_vect,UDR1);
#endif
#ifdef USESERIALPORT2
SERIALISRMACRO(2,USART2_UDRE_vect,USART2_RX_vect,UDR2);
#endif
#ifdef USESERIALPORT3
SERIALISRMACRO(3,USART3_UDRE_vect,USART3_RX_vect,UDR3);
#endif