-
Notifications
You must be signed in to change notification settings - Fork 3
/
Serial.c
475 lines (430 loc) · 19.8 KB
/
Serial.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
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
/***********************************************************************************************************************
PicoMite MMBasic
Serial.c
<COPYRIGHT HOLDERS> Geoff Graham, Peter Mather
Copyright (c) 2021, <COPYRIGHT HOLDERS> All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the distribution.
3. The name MMBasic be used when referring to the interpreter in any documentation and promotional material and the original copyright message be displayed
on the console at startup (additional copyright messages may be added).
4. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed
by the <copyright holder>.
5. Neither the name of the <copyright holder> nor the names of its contributors may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDERS> AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDERS> BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
************************************************************************************************************************/
#include "MMBasic_Includes.h"
#include "Hardware_Includes.h"
#include "hardware/uart.h"
#include "hardware/irq.h"
// variables for com1
int com1 = 0; // true if COM1 is enabled
int com1_buf_size; // size of the buffer used to receive chars
int com1_baud = 0; // determines the baud rate
char *com1_interrupt; // pointer to the interrupt routine
int com1_ilevel; // number nbr of chars in the buffer for an interrupt
int com1_TX_complete = false;
unsigned char *com1Rx_buf; // pointer to the buffer for received characters
volatile int com1Rx_head, com1Rx_tail; // head and tail of the ring buffer for com1
unsigned char *com1Tx_buf; // pointer to the buffer for transmitted characters
volatile int com1Tx_head, com1Tx_tail; // head and tail of the ring buffer for com1
volatile int com1complete=1;
uint16_t Rx1Buffer;
char com1_mode; // keeps track of the settings for com4
unsigned char com1_bit9 = 0; // used to track the 9th bit
extern uint32_t ticks_per_microsecond;
// variables for com2
int com2 = 0; // true if COM2 is enabled
int com2_buf_size; // size of the buffer used to receive chars
int com2_baud = 0; // determines the baud rate
char *com2_interrupt; // pointer to the interrupt routine
int com2_ilevel; // number nbr of chars in the buffer for an interrupt
int com2_TX_complete = false;
unsigned char *com2Rx_buf; // pointer to the buffer for received characters
volatile int com2Rx_head, com2Rx_tail; // head and tail of the ring buffer for com2 Rx
unsigned char *com2Tx_buf; // pointer to the buffer for transmitted characters
volatile int com2Tx_head, com2Tx_tail; // head and tail of the ring buffer for com2 Tx
volatile int com2complete=1;
char com2_mode; // keeps track of the settings for com4
unsigned char com2_bit9 = 0; // used to track the 9th bit
// uart interrupt handler
void on_uart_irq0() {
if(uart_is_readable(uart0)) {
char cc = uart_getc(uart0);
if(!(Option.SerialConsole & 1)){
if(GPSchannel==1){
*gpsbuf=cc;
gpsbuf++;
gpscount++;
if((char)cc==10 || gpscount==128){
if(gpscurrent){
*gpsbuf=0;
gpscurrent=0;
gpscount=0;
gpsbuf=gpsbuf1;
gpsready=gpsbuf2;
} else {
*gpsbuf=0;
gpscurrent=1;
gpscount=0;
gpsbuf=gpsbuf2;
gpsready=gpsbuf1;
}
}
} else {
com1Rx_buf[com1Rx_head] =cc; // store the byte in the ring buffer
com1Rx_head = (com1Rx_head + 1) % com1_buf_size; // advance the head of the queue
if(com1Rx_head == com1Rx_tail) { // if the buffer has overflowed
com1Rx_tail = (com1Rx_tail + 1) % com1_buf_size; // throw away the oldest char
}
}
} else {
ConsoleRxBuf[ConsoleRxBufHead] = cc; // store the byte in the ring buffer
if(BreakKey && ConsoleRxBuf[ConsoleRxBufHead] == BreakKey) {// if the user wants to stop the progran
MMAbort = true; // set the flag for the interpreter to see
ConsoleRxBufHead = ConsoleRxBufTail; // empty the buffer
} else {
ConsoleRxBufHead = (ConsoleRxBufHead + 1) % CONSOLE_RX_BUF_SIZE; // advance the head of the queue
if(ConsoleRxBufHead == ConsoleRxBufTail) { // if the buffer has overflowed
ConsoleRxBufTail = (ConsoleRxBufTail + 1) % CONSOLE_RX_BUF_SIZE; // throw away the oldest char
}
}
}
}
if(uart_is_writable(uart0)){
if(!(Option.SerialConsole & 1)){
if(com1Tx_head != com1Tx_tail) {
uart_putc_raw(uart0,com1Tx_buf[com1Tx_tail]);
com1Tx_tail = (com1Tx_tail + 1) % TX_BUFFER_SIZE; // advance the tail of the queue
} else {
uart_set_irq_enables(uart0, true, false);
com1_TX_complete=true;
}
} else {
if(ConsoleTxBufTail != ConsoleTxBufHead) {
uart_putc_raw(uart0,ConsoleTxBuf[ConsoleTxBufTail]);
ConsoleTxBufTail = (ConsoleTxBufTail + 1) % CONSOLE_TX_BUF_SIZE; // advance the tail of the queue
} else {
uart_set_irq_enables(uart0, true, false);
}
}
}
}
void on_uart_irq1() {
if (uart_is_readable(uart1)) {
char cc = uart_getc(uart1);
if(!(Option.SerialConsole & 2)){
if(GPSchannel==2){
*gpsbuf=cc;
gpsbuf++;
gpscount++;
if((char)cc==10 || gpscount==128){
if(gpscurrent){
*gpsbuf=0;
gpscurrent=0;
gpscount=0;
gpsbuf=gpsbuf1;
gpsready=gpsbuf2;
} else {
*gpsbuf=0;
gpscurrent=1;
gpscount=0;
gpsbuf=gpsbuf2;
gpsready=gpsbuf1;
}
}
} else {
com2Rx_buf[com2Rx_head] = cc; // store the byte in the ring buffer
com2Rx_head = (com2Rx_head + 1) % com2_buf_size; // advance the head of the queue
if(com2Rx_head == com2Rx_tail) { // if the buffer has overflowed
com2Rx_tail = (com2Rx_tail + 1) % com2_buf_size; // throw away the oldest char
}
}
} else {
ConsoleRxBuf[ConsoleRxBufHead] = cc; // store the byte in the ring buffer
if(BreakKey && ConsoleRxBuf[ConsoleRxBufHead] == BreakKey) {// if the user wants to stop the progran
MMAbort = true; // set the flag for the interpreter to see
ConsoleRxBufHead = ConsoleRxBufTail; // empty the buffer
} else {
ConsoleRxBufHead = (ConsoleRxBufHead + 1) % CONSOLE_RX_BUF_SIZE; // advance the head of the queue
if(ConsoleRxBufHead == ConsoleRxBufTail) { // if the buffer has overflowed
ConsoleRxBufTail = (ConsoleRxBufTail + 1) % CONSOLE_RX_BUF_SIZE; // throw away the oldest char
}
}
}
}
if(uart_is_writable(uart1)){
if(!(Option.SerialConsole & 2)){
if(com2Tx_head != com2Tx_tail) {
uart_putc_raw(uart1,com2Tx_buf[com2Tx_tail]);
com2Tx_tail = (com2Tx_tail + 1) % TX_BUFFER_SIZE; // advance the tail of the queue
} else {
uart_set_irq_enables(uart1, true, false);
com2_TX_complete=true;
}
} else {
if(ConsoleTxBufTail != ConsoleTxBufHead) {
uart_putc_raw(uart1,ConsoleTxBuf[ConsoleTxBufTail]);
ConsoleTxBufTail = (ConsoleTxBufTail + 1) % CONSOLE_TX_BUF_SIZE; // advance the tail of the queue
} else {
uart_set_irq_enables(uart1, true, false);
}
}
}
}
/***************************************************************************************************
Initialise the serial function including the timer and interrupts.
****************************************************************************************************/
#define UART_ID (uart ? uart1: uart0)
void invert_serial(int uart){
int txpin, rxpin;
if(uart==0){
txpin=PinDef[UART0TXpin].GPno;
rxpin=PinDef[UART0RXpin].GPno;
} else {
txpin=PinDef[UART1TXpin].GPno;
rxpin=PinDef[UART1RXpin].GPno;
}
gpio_set_outover(txpin, GPIO_OVERRIDE_INVERT);
gpio_set_inover(rxpin, GPIO_OVERRIDE_INVERT);
}
void MIPS16 setupuart(int uart, int s2,int parity, int b7, int baud, int inv){
uart_init(UART_ID,baud);
uart_set_hw_flow(UART_ID, false, false);
uart_set_format(UART_ID, b7, s2, parity);
uart_set_fifo_enabled(UART_ID, false);
if(inv)invert_serial(uart);
int UART_IRQ = (UART_ID == uart0 ? UART0_IRQ : UART1_IRQ);
if(uart){
irq_set_exclusive_handler(UART_IRQ, on_uart_irq1);
irq_set_enabled(UART_IRQ, true);
} else {
irq_set_exclusive_handler(UART_IRQ, on_uart_irq0);
irq_set_enabled(UART_IRQ, true);
}
uart_set_irq_enables(UART_ID, true, false);
uart_set_irq_enables(UART_ID, true, false);
}
/***************************************************************************************************
Initialise the serial function including the timer and interrupts.
****************************************************************************************************/
void MIPS16 SerialOpen(unsigned char *spec) {
int baud, i, s2, parity, b7, bufsize, inv=0, ilevel=1;
char *interrupt;
getargs(&spec, 21, (unsigned char *)":,"); // this is a macro and must be the first executable stmt
if(argc != 2 && (argc & 0x01) == 0) error("COM specification");
b7 = 8;
parity = UART_PARITY_NONE;
s2 = 1;
for(i = 0; i < 5; i++) {
if(str_equal(argv[argc - 1], (unsigned char *)"EVEN")) {
if(parity)error("Syntax");
else {parity = UART_PARITY_EVEN; argc -= 2; } // set even parity
}
if(str_equal(argv[argc - 1], (unsigned char *)"ODD")) {
if(parity)error("Syntax");
else {parity = UART_PARITY_ODD; argc -= 2; } // set even parity
}
if(str_equal(argv[argc - 1], (unsigned char *)"INV")) { inv = 1; argc -= 2; }; // invert the serial port
if(str_equal(argv[argc - 1], (unsigned char *)"DE")) error("DE not Supported"); // get the two stop bit option
if(str_equal(argv[argc - 1], (unsigned char *)"OC")) error("OC not Supported"); // get the two stop bit option
if(str_equal(argv[argc - 1], (unsigned char *)"9BIT")) error("9BIT not Supported"); // get the two stop bit option
if(str_equal(argv[argc - 1], (unsigned char *)"S2")) { s2 = 2; argc -= 2; } // get the two stop bit option
if(str_equal(argv[argc - 1], (unsigned char *)"7BIT")) { b7 = 7; argc -= 2; } // set the 7 bit byte option
}
if(argc < 1 || argc > 9) error("COM specification");
if(argc >= 3 && *argv[2]) {
baud = getint(argv[2],Option.CPU_Speed*1000/16/65535,921600); // get the baud rate as a number
} else
baud = COM_DEFAULT_BAUD_RATE;
if(argc >= 5 && *argv[4])
bufsize = getinteger(argv[4]); // get the buffer size as a number
else
bufsize = COM_DEFAULT_BUF_SIZE;
if(argc >= 7) {
InterruptUsed = true;
argv[6]=(unsigned char *)strupr((char *)argv[6]);
interrupt = (char *)GetIntAddress(argv[6]); // get the interrupt location
} else
interrupt = NULL;
if(argc == 9) {
ilevel = getinteger(argv[8]); // get the buffer level for interrupt as a number
if(ilevel < 1 || ilevel > bufsize) error("COM specification");
} else
ilevel = 1;
/* if(argc >= 11) {
InterruptUsed = true;
argv[6]=strupr(argv[10]);
TXinterrupt = GetIntAddress(argv[10]); // get the interrupt location
} else
TXinterrupt = NULL;
*/
if(spec[3] == '1') {
///////////////////////////////// this is COM1 ////////////////////////////////////
if(com1) error("Already open");
if(UART0TXpin==99 || UART0RXpin==99)error("Pins not set for COM1");
com1_buf_size = bufsize; // extracted from the comspec above
com1_interrupt = interrupt;
com1_ilevel = ilevel;
// setup for receive
com1Rx_buf = GetMemory(com1_buf_size); // setup the buffer
com1Rx_head = com1Rx_tail = 0;
ExtCfg(UART0RXpin, EXT_COM_RESERVED, 0); // reserve the pin for com use
// setup for transmit
com1Tx_buf = GetMemory(TX_BUFFER_SIZE); // setup the buffer
com1Tx_head = com1Tx_tail = 0;
ExtCfg(UART0TXpin, EXT_COM_RESERVED, 0);
setupuart(0, s2, parity, b7, baud, inv);
com1 = true;
uSec(1000);
com1Rx_head = com1Rx_tail = 0;
com1Tx_head = com1Tx_tail = 0;
}
else if (spec[3] == '2') {
///////////////////////////////// this is COM2 ////////////////////////////////////
if(com2) error("Already open");
if(UART1TXpin==99 || UART1RXpin==99)error("Pins not set for COM2");
com2_buf_size = bufsize; // extracted from the comspec above
com2_interrupt = interrupt;
com2_ilevel = ilevel;
// com2_TX_interrupt = TXinterrupt;
// com2_TX_complete = false;
// setup for receive
com2Rx_buf = GetMemory(com2_buf_size); // setup the buffer
com2Rx_head = com2Rx_tail = 0;
ExtCfg(UART1RXpin, EXT_COM_RESERVED, 0); // reserve the pin for com use
// setup for transmit
com2Tx_buf = GetMemory(TX_BUFFER_SIZE); // setup the buffer
com2Tx_head = com2Tx_tail = 0;
ExtCfg(UART1TXpin, EXT_COM_RESERVED, 0); // reserve the pin for com use
setupuart(1, s2, parity, b7, baud, inv);
com2 = true;
uSec(1000);
com2Rx_head = com2Rx_tail = 0;
com2Tx_head = com2Tx_tail = 0;
}
}
/***************************************************************************************************
Close a serial port.
****************************************************************************************************/
void MIPS16 SerialClose(int comnbr) {
if(comnbr == 1 && com1) {
uart_deinit(uart0);
com1 = false;
com1_interrupt = NULL;
if(UART0RXpin!=99)ExtCfg(UART0RXpin, EXT_NOT_CONFIG, 0);
if(UART0TXpin!=99)ExtCfg(UART0TXpin, EXT_NOT_CONFIG, 0);
if(com1Rx_buf!=NULL){FreeMemory(com1Rx_buf); com1Rx_buf=NULL;}
if(com1Tx_buf!=NULL){FreeMemory(com1Tx_buf); com1Tx_buf=NULL;}
}
else if(comnbr == 2 && com2) {
uart_deinit(uart1);
com2 = false;
com2_interrupt = NULL;
if(UART1RXpin!=99)ExtCfg(UART1RXpin, EXT_NOT_CONFIG, 0);
if(UART1TXpin!=99)ExtCfg(UART1TXpin, EXT_NOT_CONFIG, 0);
if(com2Rx_buf!=NULL){FreeMemory(com2Rx_buf); com2Rx_buf=NULL;}
if(com2Tx_buf!=NULL){FreeMemory(com2Tx_buf); com2Tx_buf=NULL;}
}
}
/***************************************************************************************************
Add a character to the serial output buffer.
****************************************************************************************************/
unsigned char SerialPutchar(int comnbr, unsigned char c) {
if(comnbr == 1) {
while(com1Tx_tail == ((com1Tx_head + 1) % TX_BUFFER_SIZE)) // wait if the buffer is full
if(MMAbort) { // allow the user to abort a hung serial port
com1Tx_tail = com1Tx_head = 0; // clear the buffer
longjmp(mark, 1); // and abort
}
int empty=uart_is_writable(uart0);
com1Tx_buf[com1Tx_head] = c; // add the char
com1Tx_head = (com1Tx_head + 1) % TX_BUFFER_SIZE; // advance the head of the queue
if(empty){
uart_set_irq_enables(uart0, true, true);
irq_set_pending(UART0_IRQ);
}
}
else if(comnbr == 2) {
while(com2Tx_tail == ((com2Tx_head + 1) % TX_BUFFER_SIZE)) // wait if the buffer is full
if(MMAbort) { // allow the user to abort a hung serial port
com2Tx_tail = com2Tx_head = 0; // clear the buffer
longjmp(mark, 1); // and abort
}
int empty=uart_is_writable(uart1);
com2Tx_buf[com2Tx_head] = c; // add the char
com2Tx_head = (com2Tx_head + 1) % TX_BUFFER_SIZE; // advance the head of the queue
if(empty){
uart_set_irq_enables(uart1, true, true);
irq_set_pending(UART1_IRQ);
}
}
return c;
}
/***************************************************************************************************
Get the status the serial receive buffer.
Returns the number of characters waiting in the buffer
****************************************************************************************************/
int SerialRxStatus(int comnbr) {
int i = 0;
if(comnbr == 1) {
uart_set_irq_enables(uart0, false, true);
i = com1Rx_head - com1Rx_tail;
uart_set_irq_enables(uart0, true, true);
if(i < 0) i += com1_buf_size;
}
else if(comnbr == 2) {
uart_set_irq_enables(uart1, false, true);
i = com2Rx_head - com2Rx_tail;
uart_set_irq_enables(uart1, true, true);
if(i < 0) i += com2_buf_size;
}
return i;
}
/***************************************************************************************************
Get the status the serial transmit buffer.
Returns the number of characters waiting in the buffer
****************************************************************************************************/
int SerialTxStatus(int comnbr) {
int i = 0;
if(comnbr == 1) {
i = com1Tx_head - com1Tx_tail;
if(i < 0) i += TX_BUFFER_SIZE;
}
else if(comnbr == 2) {
i = com2Tx_head - com2Tx_tail;
if(i < 0) i += TX_BUFFER_SIZE;
}
return i;
}
/***************************************************************************************************
Get a character from the serial receive buffer.
Note that this is returned as an integer and -1 means that there are no characters available
****************************************************************************************************/
int SerialGetchar(int comnbr) {
int c;
c = -1; // -1 is no data
if(comnbr == 1) {
uart_set_irq_enables(uart0, false, true);
if(com1Rx_head != com1Rx_tail) { // if the queue has something in it
c = com1Rx_buf[com1Rx_tail]; // get the char
com1Rx_tail = (com1Rx_tail + 1) % com1_buf_size; // and remove from the buffer
}
uart_set_irq_enables(uart0, true, true);
}
else if(comnbr == 2) {
uart_set_irq_enables(uart1, false, true);
if(com2Rx_head != com2Rx_tail) { // if the queue has something in it
c = com2Rx_buf[com2Rx_tail]; // get the char
com2Rx_tail = (com2Rx_tail + 1) % com2_buf_size; // and remove from the buffer
}
uart_set_irq_enables(uart1, true, true);
}
return c;
}