forked from Grapsus/cc254x_sdcc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathuart.c
331 lines (281 loc) · 10.8 KB
/
uart.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
/** \file uart.c
* This is the main source file for <code>uart.lib</code>. See uart.h for
* information on how to use this library.
*/
#include "cc254x_map.h"
#include "cc254x_types.h"
#if defined(__CDT_PARSER__)
#define UART0
#endif
#if defined(UART0)
#include "uart0.h"
#define INTERRUPT_PRIORITY_GROUP 2
#define ISR_URX() ISR(URX0, 0)
#define ISR_UTX() ISR(UTX0, 0)
#define UTXNIF UTX0IF
#define URXNIF URX0IF
#define URXNIE URX0IE
#define UNCSR U0CSR
#define UNGCR U0GCR
#define UNUCR U0UCR
#define UNBAUD U0BAUD
#define UNDBUF U0DBUF
#define BV_UTXNIE (1<<2)
#define uartNRxParityErrorOccurred uart0RxParityErrorOccurred
#define uartNRxFramingErrorOccurred uart0RxFramingErrorOccurred
#define uartNRxBufferFullOccurred uart0RxBufferFullOccurred
#define uartNRxAvailable uart0RxAvailable
#define uartNTxAvailable uart0TxAvailable
#define uartNInit uart0Init
#define uartNSetBaudRate uart0SetBaudRate
#define uartNSetParity uart0SetParity
#define uartNSetStopBits uart0SetStopBits
#define uartNTxSend uart0TxSend
#define uartNRxReceiveByte uart0RxReceiveByte
#define uartNTxSend uart0TxSend
#define uartNTxSendByte uart0TxSendByte
#elif defined(UART1)
#include "uart1.h"
#define INTERRUPT_PRIORITY_GROUP 3
#define ISR_URX() ISR(URX1, 0)
#define ISR_UTX() ISR(UTX1, 0)
#define UTXNIF UTX1IF
#define URXNIF URX1IF
#define URXNIE URX1IE
#define UNCSR U1CSR
#define UNGCR U1GCR
#define UNUCR U1UCR
#define UNBAUD U1BAUD
#define UNDBUF U1DBUF
#define BV_UTXNIE (1<<3)
#define uartNRxParityErrorOccurred uart1RxParityErrorOccurred
#define uartNRxFramingErrorOccurred uart1RxFramingErrorOccurred
#define uartNRxBufferFullOccurred uart1RxBufferFullOccurred
#define uartNRxAvailable uart1RxAvailable
#define uartNTxAvailable uart1TxAvailable
#define uartNInit uart1Init
#define uartNSetBaudRate uart1SetBaudRate
#define uartNSetParity uart1SetParity
#define uartNSetStopBits uart1SetStopBits
#define uartNTxSend uart1TxSend
#define uartNRxReceiveByte uart1RxReceiveByte
#define uartNTxSend uart1TxSend
#define uartNTxSendByte uart1TxSendByte
#endif
static volatile uint8 XDATA uartTxBuffer[256]; // sizeof(uartTxBuffer) must be a power of two
static volatile uint8 DATA uartTxBufferMainLoopIndex; // Index of next byte main loop will write.
static volatile uint8 DATA uartTxBufferInterruptIndex; // Index of next byte interrupt will read.
#define UART_TX_BUFFER_FREE_BYTES() ((uartTxBufferInterruptIndex - uartTxBufferMainLoopIndex - 1) & (sizeof(uartTxBuffer) - 1))
static volatile uint8 XDATA uartRxBuffer[256]; // sizeof(uartRxBuffer) must be a power of two
static volatile uint8 DATA uartRxBufferMainLoopIndex; // Index of next byte main loop will read.
static volatile uint8 DATA uartRxBufferInterruptIndex; // Index of next byte interrupt will write.
#define UART_RX_BUFFER_FREE_BYTES() ((uartRxBufferMainLoopIndex - uartRxBufferInterruptIndex - 1) & (sizeof(uartRxBuffer) - 1))
#define UART_RX_BUFFER_USED_BYTES() ((uartRxBufferInterruptIndex - uartRxBufferMainLoopIndex) & (sizeof(uartRxBuffer) - 1))
volatile BIT uartNRxParityErrorOccurred;
volatile BIT uartNRxFramingErrorOccurred;
volatile BIT uartNRxBufferFullOccurred;
/* getchar and putchar must be implemented in order to make
* SDCC's stdio work */
char getchar(void)
{
// TODO fixme make the waiting less power hungry
while( ! uartNRxAvailable()) {}
return uartNRxReceiveByte();
}
void putchar(char c)
{
if(uart0TxAvailable()) {
uart0TxSendByte(c);
}
}
void uartNInit(void)
{
/* USART0 UART Alt. 1:
* TX = P0_3
* RX = P0_2
*/
/* USART1 UART Alt. 2:
* TX = P1_6
* RX = P1_7
*/
uartTxBufferMainLoopIndex = 0;
uartTxBufferInterruptIndex = 0;
uartRxBufferMainLoopIndex = 0;
uartRxBufferInterruptIndex = 0;
uartNRxParityErrorOccurred = 0;
uartNRxFramingErrorOccurred = 0;
uartNRxBufferFullOccurred = 0;
// Note: We do NOT set the mode of the RX pin to "peripheral function"
// because that seems to have no benefits, and is actually bad because
// it disables the internal pull-up resistor.
#ifdef UART0
P2DIR &= ~0xC0; // P2DIR.PRIP0 (7:6) = 00 : USART0 takes priority over USART1.
PERCFG &= ~0x01; // PERCFG.U0CFG (0) = 0 (Alt. 1) : USART0 uses alt. location 1.
#else
P2SEL |= 0x40; // USART1 takes priority over USART0 on Port 1.
PERCFG |= 0x02; // PERCFG.U1CFG (1) = 1 (Alt. 2) : USART1 uses alt. location 2.
#endif
UNUCR = 0x82; // Stops the "current operation" and resets settings to their defaults.
UNCSR |= 0xc0; // Enable UART mode and enable receiver. TODO: change '|=' to '='
// Set the mode of the TX pin to "peripheral function". This must be done AFTER
// enabling the UART, or else we get a tiny glitch on the TX line.
#ifdef UART0
P0SEL |= (1<<3); // P0SEL.SELP0_3 = 1
#else
P1SEL |= (1<<6); // P1SEL.SELP1_6 = 1
#endif
// Below, we set the priority of the RX and TX interrupts to be 1 (second lowest priority).
// They need to be higher than the RF interrupt because that one could take a long time.
// The UART0 interrupts are grouped with the T2 interrupt, so its priority also gets set.
// The UART1 interrupts are grouped with the T3 interrupts, so its priority also gets set.
IP0 |= (1<<INTERRUPT_PRIORITY_GROUP);
IP1 &= ~(1<<INTERRUPT_PRIORITY_GROUP);
UTXNIF = 1; // Set TX flag so the interrupt fires when we enable it for the first time.
URXNIF = 0; // Clear RX flag.
URXNIE = 1; // Enable Rx interrupt.
EA = 1; // Enable interrupts in general.
}
void uartNSetBaudRate(uint32 baud)
{
uint32 baudMPlus256;
uint8 baudE = 0;
// max baud rate is 1500000 (F/16); min is 23 (baudM = 1)
if (baud < 23 || baud > 1500000)
return;
// 495782 is the largest value that will not overflow the following calculation
while (baud > 495782)
{
baudE++;
baud /= 2;
}
// calculate baud rate - see datasheet 12.14.3
// this is derived from (baudM + 256) = baud * 2^28 / 24000000
baudMPlus256 = (baud * 11) + (baud * 8663 / 46875);
// get baudMPlus256 into the range 256-511 (so BAUD_M is in the range 0-255)
while (baudMPlus256 > 0x1ff)
{
baudE++;
baudMPlus256 /= 2;
}
UNGCR = baudE; // UNGCR.BAUD_E (4:0)
UNBAUD = baudMPlus256; // UNBAUD.BAUD_M (7:0) - only the lowest 8 bits of baudMPlus256 are used, so this is effectively baudMPlus256 - 256
}
void uartNSetParity(uint8 parity)
{
// parity D9 BIT9 PARITY
// 0 None x 0 x
// 1 Odd 1 1 1
// 2 Even 0 1 1
// 3 Mark 1 1 0
// 4 Space 0 1 0
uint8 tmp = 0;
switch(parity)
{
case PARITY_ODD: tmp = 0b111 << 3; break;
case PARITY_EVEN: tmp = 0b011 << 3; break;
case PARITY_MARK: tmp = 0b110 << 3; break;
case PARITY_SPACE: tmp = 0b010 << 3; break;
}
UNUCR = (UNUCR & 0b01000111) | tmp;
}
void uartNSetStopBits(uint8 stopBits)
{
if (stopBits == STOP_BITS_2)
{
UNUCR |= (1<<2); // 2 stop bits
}
else
{
UNUCR &= ~(1<<2); // 1 stop bit
// NOTE: An argument of STOP_BITS_1_5 is treated the same as STOP_BITS_1.
}
}
uint8 uartNTxAvailable(void)
{
return UART_TX_BUFFER_FREE_BYTES();
}
void uartNTxSend(const uint8 XDATA * buffer, uint8 size)
{
// Assumption: uartNTxAvailable() was recently called and it returned a number at least as big as 'size'.
// TODO: after DMA memcpy is implemented, use it to make this function faster
while (size)
{
uartTxBuffer[uartTxBufferMainLoopIndex] = *buffer;
buffer++;
uartTxBufferMainLoopIndex = (uartTxBufferMainLoopIndex + 1) & (sizeof(uartTxBuffer) - 1);
size--;
IEN2 |= BV_UTXNIE; // Enable TX interrupt
}
}
void uartNTxSendByte(uint8 byte)
{
// Assumption: uartNTxAvailable() was recently called and it returned a non-zero number.
uartTxBuffer[uartTxBufferMainLoopIndex] = byte;
uartTxBufferMainLoopIndex = (uartTxBufferMainLoopIndex + 1) & (sizeof(uartTxBuffer) - 1);
IEN2 |= BV_UTXNIE; // Enable TX interrupt
}
uint8 uartNRxAvailable(void)
{
return UART_RX_BUFFER_USED_BYTES();
}
uint8 uartNRxReceiveByte(void)
{
// Assumption: uartNRxAvailable was recently called and it returned a non-zero value.
uint8 byte = uartRxBuffer[uartRxBufferMainLoopIndex];
uartRxBufferMainLoopIndex = (uartRxBufferMainLoopIndex + 1) & (sizeof(uartRxBuffer) - 1);
return byte;
}
ISR_UTX()
{
// A byte has just started transmitting on TX and there is room in
// the UART's hardware buffer for us to add another byte.
if (uartTxBufferInterruptIndex != uartTxBufferMainLoopIndex)
{
// There more bytes available in our software buffer, so send
// the next byte.
UTXNIF = 0;
UNDBUF = uartTxBuffer[uartTxBufferInterruptIndex];
uartTxBufferInterruptIndex = (uartTxBufferInterruptIndex + 1) & (sizeof(uartTxBuffer) - 1);
}
else
{
// There are no more bytes to send in our buffer, so disable the TX interrupt.
IEN2 &= ~BV_UTXNIE;
}
}
ISR_URX()
{
uint8 csr;
URXNIF = 0;
// Read the Control and Status register for the UART.
// Reading this register clears the FE and ERR bits,
// which we need to check later.
csr = UNCSR;
// check for frame and parity errors
if (!(csr & 0x18)) // UNCSR.FE (4) == 0; UNCSR.ERR (3) == 0
{
// There were no errors.
if (UART_RX_BUFFER_FREE_BYTES())
{
// The software RX buffer has space, so add this new byte to the buffer.
uartRxBuffer[uartRxBufferInterruptIndex] = UNDBUF;
uartRxBufferInterruptIndex = (uartRxBufferInterruptIndex + 1) & (sizeof(uartRxBuffer) - 1);
}
else
{
// The buffer is full, so discard the received byte and report and overflow error.
uartNRxBufferFullOccurred = 1;
}
}
else
{
if (csr & 0x10) // UNCSR.FE (4) == 1
{
uartNRxFramingErrorOccurred = 1;
}
if (csr & 0x08) // UNCSR.ERR (3) == 1
{
uartNRxParityErrorOccurred = 1;
}
}
}