-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathserial.c
More file actions
312 lines (259 loc) · 7.71 KB
/
serial.c
File metadata and controls
312 lines (259 loc) · 7.71 KB
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
// serial.c
// Copyright (c) 2018 J. M. Spivey
#include "microbian.h"
#include "hardware.h"
#include <stdarg.h>
#ifdef UBIT
#define TX USB_TX
#define RX USB_RX
#endif
static int SERIAL_TASK;
/* Message types for serial task */
#define PUTC 16
#define GETC 17
#define PUTBUF 18
/* There are two buffers, one for characters waiting to be output, and
another for input characters waiting to be read by other processes.
The input buffer has |n_edit| characters in the current line, still
subject to editing, and |n_avail| characters in previous lines that
are available to other processes. */
/* NBUF -- size of input and output buffers. Should be a power of 2. */
#define NBUF 256
/* wrap -- reduce index to range [0..NBUF) */
#define wrap(x) ((x) & (NBUF-1))
/* Input buffer */
static char rxbuf[NBUF]; /* Circular buffer for input */
static int rx_inp = 0; /* In pointer */
static int rx_outp = 0; /* Out pointer */
static int n_avail = 0; /* Number of chars avail for input */
static int n_edit = 0; /* Number of chars in current line */
/* Output buffer */
static char txbuf[NBUF]; /* Circular buffer for output */
static int tx_inp = 0; /* In pointer */
static int tx_outp = 0; /* Out pointer */
static int n_tx = 0; /* Character count */
static int reader = -1; /* Process waiting to read */
static int txidle = 1; /* True if transmitter is idle */
/* echo -- echo input character */
static void echo(char ch) {
if (n_tx == NBUF) return;
txbuf[tx_inp] = ch;
tx_inp = wrap(tx_inp+1);
n_tx++;
}
#define CTRL(x) ((x) & 0x1f)
/* keypress -- deal with keyboard character by editing buffer */
static void keypress(char ch) {
switch (ch) {
case '\b':
case 0177:
/* Delete last character */
if (n_edit > 0) {
n_edit--;
rx_inp = wrap(rx_inp-1);
/* This doesn't work well with TAB and other control chars */
echo('\b'); echo(' '); echo('\b');
}
break;
case '\r':
case '\n':
/* Make line available to clients */
if (n_avail + n_edit == NBUF) break;
rxbuf[rx_inp] = '\n';
rx_inp = wrap(rx_inp+1);
n_edit++;
n_avail += n_edit; n_edit = 0;
echo('\r'); echo('\n');
break;
case CTRL('B'):
/* Print process table dump */
dump();
break;
default:
/* Ignore other control characters */
if (ch < 040 || ch >= 0177) break;
/* Add character to line */
if (n_avail + n_edit == NBUF) break;
rxbuf[rx_inp] = ch;
rx_inp = wrap(rx_inp+1);
n_edit++;
echo(ch);
}
}
/* The clear_pending() call below is needed because the UART interrupt
handler disables the IRQ for the UART in the NVIC, but doesn't disable
the UART itself from sending interrupts. The pending bit is cleared
on return from the interrupt handler, but that doesn't stop the UART
from setting it again. */
#ifdef UBIT
/* serial_interrupt -- handle serial interrupt */
static void serial_interrupt(void) {
if (UART_RXDRDY) {
char ch = UART_RXD;
keypress(ch);
UART_RXDRDY = 0;
}
if (UART_TXDRDY) {
txidle = 1;
UART_TXDRDY = 0;
}
clear_pending(UART_IRQ);
enable_irq(UART_IRQ);
}
#endif
#ifdef KL25Z
/* serial_interrupt -- handle serial interrupt */
static void serial_interrupt(void) {
if (UART0_S1 & BIT(UART_S1_RDRF)) {
char ch = UART0_D;
keypress(ch);
}
if (UART0_S1 & BIT(UART_S1_TDRE)) {
txidle = 1;
CLR_BIT(UART0_C2, UART_C2_TIE);
}
clear_pending(UART0_IRQ);
enable_irq(UART0_IRQ);
}
#endif
/* reply -- send reply or start transmitter if possible */
static void reply(void) {
// Can we satisfy a reader?
if (reader >= 0 && n_avail > 0) {
send_int(reader, REPLY, rxbuf[rx_outp]);
rx_outp = wrap(rx_outp+1);
n_avail--;
reader = -1;
}
// Can we start transmitting a character?
if (txidle && n_tx > 0) {
#ifdef UBIT
UART_TXD = txbuf[tx_outp];
#endif
#ifdef KL25Z
UART0_D = txbuf[tx_outp];
SET_BIT(UART0_C2, UART_C2_TIE);
#endif
tx_outp = wrap(tx_outp+1);
n_tx--;
txidle = 0;
}
}
/* queue_char -- add character to output buffer */
static void queue_char(char ch) {
while (n_tx == NBUF) {
// The buffer is full -- wait for a space to appear
receive(INTERRUPT, NULL);
serial_interrupt();
reply();
}
txbuf[tx_inp] = ch;
tx_inp = wrap(tx_inp+1);
n_tx++;
}
/* serial_task -- driver process for UART */
static void serial_task(int arg) {
message m;
int client, n;
char ch;
char *buf;
#ifdef UBIT
UART_ENABLE = UART_ENABLE_Disabled;
UART_BAUDRATE = UART_BAUDRATE_9600; // 9600 baud
UART_CONFIG = FIELD(UART_CONFIG_PARITY, UART_PARITY_None);
// format 8N1
UART_PSELTXD = TX; // choose pins
UART_PSELRXD = RX;
UART_ENABLE = UART_ENABLE_Enabled;
UART_STARTTX = 1;
UART_STARTRX = 1;
UART_RXDRDY = 0;
UART_INTENSET = BIT(UART_INT_RXDRDY) | BIT(UART_INT_TXDRDY);
connect(UART_IRQ);
enable_irq(UART_IRQ);
#endif
#ifdef KL25Z
// enable PLL clock
SET_FIELD(SIM_SOPT2, SIM_SOPT2_UART0SRC, SIM_SOPT2_SRC_PLL);
SET_BIT(SIM_SCGC4, SIM_SCGC4_UART0);
// Disable UART before changing registers
UART0_C2 &= ~(BIT(UART_C2_RE) | BIT(UART_C2_TE));
// set baud rate
unsigned BR = UART_BAUD_9600;
SET_FIELD(UART0_BDH, UART_BDH_SBR, BR >> 8);
UART0_BDL = BR & 0xff;
// 8N1 format
UART0_C1 = 0;
CLR_BIT(UART0_BDH, UART_BDH_SBNS);
// set mux for rx/tx pins and enable PullUp mode
pin_function(USB_TX, 2);
pin_mode(USB_TX, PORT_MODE_PullUp);
pin_function(USB_RX, 2);
pin_mode(USB_RX, PORT_MODE_PullUp);
// Enable UART
UART0_C2 |= BIT(UART_C2_RE) | BIT(UART_C2_TE);
SET_BIT(UART0_C2, UART_C2_RIE);
enable_irq(UART0_IRQ);
connect(UART0_IRQ);
enable_irq(UART0_IRQ);
#endif
txidle = 1;
while (1) {
receive(ANY, &m);
client = m.sender;
switch (m.type) {
case INTERRUPT:
serial_interrupt();
break;
case GETC:
if (reader >= 0)
panic("Two clients cannot wait for input at once");
reader = client;
break;
case PUTC:
ch = m.int1;
if (ch == '\n') queue_char('\r');
queue_char(ch);
break;
case PUTBUF:
buf = m.ptr1;
n = m.int2;
for (int i = 0; i < n; i++) {
char ch = buf[i];
if (ch == '\n') queue_char('\r');
queue_char(ch);
}
send_msg(client, REPLY);
break;
default:
badmesg(m.type);
}
reply();
}
}
/* serial_init -- start the serial driver task */
void serial_init(void) {
SERIAL_TASK = start("Serial", serial_task, 0, 256);
}
/* serial_putc -- queue a character for output */
void serial_putc(char ch) {
send_int(SERIAL_TASK, PUTC, ch);
}
/* serial_getc -- request an input character */
char serial_getc(void) {
message m;
m.type = GETC;
sendrec(SERIAL_TASK, &m);
return m.int1;
}
/* print_buf -- output routine for use by printf */
void print_buf(char *buf, int n) {
/* Using sendrec() here avoids a potential priority inversion:
with separate send() and receive() calls, a lower-priority
client process can block a reply from the device driver. */
message m;
m.type = PUTBUF;
m.ptr1 = buf;
m.int2 = n;
sendrec(SERIAL_TASK, &m);
}