25
25
#include "uart.h"
26
26
27
27
// Size must be 2^n
28
- #define BUFFER_SIZE (4096)
29
-
30
- #define UART_ERRORS (MXC_F_UART_INTFL_RX_FRAMING_ERR | \
31
- MXC_F_UART_INTFL_RX_PARITY_ERR | \
32
- MXC_F_UART_INTFL_RX_FIFO_OVERFLOW)
33
-
28
+ #define BUFFER_SIZE (4096)
34
29
35
30
// Track bit rate to avoid calculation from bus clock, clock scaler and baud divisor values
36
31
static uint32_t baudrate ;
@@ -39,6 +34,7 @@ static mxc_uart_regs_t *CdcAcmUart = NULL;
39
34
static mxc_uart_fifo_regs_t * CdcAcmUartFifo = NULL ;
40
35
static IRQn_Type CdcAcmUartIrqNumber = MXC_IRQ_EXT_COUNT ;
41
36
37
+ // Ring buffer
42
38
static struct {
43
39
uint8_t data [BUFFER_SIZE ];
44
40
volatile uint16_t idx_in ;
@@ -48,15 +44,85 @@ static struct {
48
44
} write_buffer , read_buffer ;
49
45
50
46
/******************************************************************************/
51
- static void set_bitrate (uint32_t bps )
47
+ static void set_bitrate (uint32_t target_baud )
52
48
{
53
- uint32_t baud_divisor ;
49
+ uint32_t clk_scale ;
50
+ uint32_t min_baud ;
51
+ uint32_t uart_clk ;
52
+ uint16_t baud_shift ;
53
+ uint16_t baud_div ;
54
+ uint32_t baud , diff_baud ;
55
+ uint32_t baud_1 , diff_baud_1 ;
56
+
57
+ // Setup system clock divider for given bit rate
58
+ clk_scale = 0 ;
59
+ do {
60
+ min_baud = (SystemCoreClock >> clk_scale ++ ) / (16 * (MXC_F_UART_BAUD_BAUD_DIVISOR >> MXC_F_UART_BAUD_BAUD_DIVISOR_POS ));
61
+ } while ((target_baud < min_baud ) && (clk_scale < MXC_V_CLKMAN_CLK_SCALE_DIV_256 ));
62
+
63
+ // Check if the bit rate can be reached
64
+ if (target_baud < min_baud ) {
65
+ // Fail silently
66
+ return ;
67
+ }
68
+
69
+ if (MXC_CLKMAN -> sys_clk_ctrl_8_uart != clk_scale ) {
70
+ MXC_CLKMAN -> sys_clk_ctrl_8_uart = clk_scale ;
71
+ }
72
+
73
+ uart_clk = SystemCoreClock >> (clk_scale - 1 );
54
74
55
- baud_divisor = SystemCoreClock / (1 << (MXC_CLKMAN -> sys_clk_ctrl_8_uart - 1 ));
56
- baud_divisor /= (bps * 16 );
57
- CdcAcmUart -> baud = baud_divisor ;
75
+ baud_shift = 2 ;
76
+ baud_div = uart_clk / (target_baud * 4 );
77
+
78
+ // Bitrate too high
79
+ if (baud_div == 0 ) {
80
+ // Fail silently
81
+ return ;
82
+ }
83
+
84
+ // Decrease the divisor if baud_div is overflowing
85
+ while (baud_div > 0x00FF ) {
86
+ if (baud_shift == 0 ) {
87
+ // Fail silently
88
+ return ;
89
+ }
90
+ baud_shift -- ;
91
+ baud_div = uart_clk / (target_baud * (16 >> baud_shift ));
92
+ }
93
+
94
+ // Adjust baud_div to avoid overflow in the calculations below
95
+ if (baud_div == 0x00FF ) {
96
+ baud_div = 0x00FE ;
97
+ }
98
+
99
+ if (baud_div == 0x0000 ) {
100
+ baud_div = 0x0001 ;
101
+ }
58
102
59
- baudrate = bps ;
103
+ // Determine if truncation increases the error
104
+ baud = uart_clk / (baud_div * (16 >> baud_shift ));
105
+ baud_1 = uart_clk / ((baud_div + 1 ) * (16 >> baud_shift ));
106
+
107
+ if (target_baud > baud ) {
108
+ diff_baud = target_baud - baud ;
109
+ } else {
110
+ diff_baud = baud - target_baud ;
111
+ }
112
+
113
+ if (target_baud > baud_1 ) {
114
+ diff_baud_1 = target_baud - baud_1 ;
115
+ } else {
116
+ diff_baud_1 = baud_1 - target_baud ;
117
+ }
118
+
119
+ if (diff_baud < diff_baud_1 ) {
120
+ CdcAcmUart -> baud = (baud_div & MXC_F_UART_BAUD_BAUD_DIVISOR ) | (baud_shift << MXC_F_UART_BAUD_BAUD_MODE_POS );
121
+ } else {
122
+ CdcAcmUart -> baud = ((baud_div + 1 ) & MXC_F_UART_BAUD_BAUD_DIVISOR ) | (baud_shift << MXC_F_UART_BAUD_BAUD_MODE_POS );
123
+ }
124
+
125
+ baudrate = target_baud ;
60
126
}
61
127
62
128
/******************************************************************************/
@@ -81,21 +147,18 @@ int32_t uart_initialize(void)
81
147
{
82
148
int idx ;
83
149
150
+ /* Do not enable UART Common Clock here, must happen inside uart_set_configuration()
151
+ to ensure correct clock scale for a given baud rate */
152
+
84
153
if (CdcAcmUart == MXC_UART0 ) {
85
154
MXC_CLKMAN -> clk_gate_ctrl1 |= MXC_F_CLKMAN_CLK_GATE_CTRL1_UART0_CLK_GATER ;
86
- if (MXC_CLKMAN -> sys_clk_ctrl_8_uart != MXC_S_CLKMAN_CLK_SCALE_DIV_4 ) {
87
- MXC_CLKMAN -> sys_clk_ctrl_8_uart = MXC_S_CLKMAN_CLK_SCALE_DIV_4 ;
88
- }
89
155
90
156
// Configure GPIO for UART
91
157
MXC_IOMAN -> uart0_req = ((MXC_V_IOMAN_MAP_A << MXC_F_IOMAN_UART0_REQ_IO_MAP_POS ) | MXC_F_IOMAN_UART0_REQ_IO_REQ );
92
158
while (MXC_IOMAN -> uart0_ack != ((MXC_V_IOMAN_MAP_A << MXC_F_IOMAN_UART0_REQ_IO_MAP_POS ) | MXC_F_IOMAN_UART0_REQ_IO_REQ ));
93
159
94
160
} else if (CdcAcmUart == MXC_UART2 ) {
95
161
MXC_CLKMAN -> clk_gate_ctrl1 |= MXC_F_CLKMAN_CLK_GATE_CTRL1_UART2_CLK_GATER ;
96
- if (MXC_CLKMAN -> sys_clk_ctrl_8_uart != MXC_S_CLKMAN_CLK_SCALE_DIV_4 ) {
97
- MXC_CLKMAN -> sys_clk_ctrl_8_uart = MXC_S_CLKMAN_CLK_SCALE_DIV_4 ;
98
- }
99
162
100
163
// Configure GPIO for UART
101
164
MXC_IOMAN -> uart2_req = ((MXC_V_IOMAN_MAP_A << MXC_F_IOMAN_UART2_REQ_IO_MAP_POS ) | MXC_F_IOMAN_UART2_REQ_IO_REQ );
@@ -132,7 +195,7 @@ int32_t uart_initialize(void)
132
195
CdcAcmUart -> tx_fifo_ctrl |= (MXC_UART_FIFO_DEPTH - (MXC_UART_FIFO_DEPTH >> 2 )) << MXC_F_UART_TX_FIFO_CTRL_FIFO_AE_LVL_POS ;
133
196
134
197
// Enable RX and TX interrupts
135
- CdcAcmUart -> inten = (MXC_F_UART_INTEN_RX_FIFO_NOT_EMPTY | MXC_F_UART_INTFL_RX_FIFO_OVERFLOW | MXC_F_UART_INTEN_TX_FIFO_AE );
198
+ CdcAcmUart -> inten = (MXC_F_UART_INTEN_RX_FIFO_NOT_EMPTY | MXC_F_UART_INTEN_RX_FIFO_OVERFLOW | MXC_F_UART_INTEN_TX_FIFO_AE );
136
199
137
200
// Enable UART
138
201
CdcAcmUart -> ctrl |= MXC_F_UART_CTRL_UART_EN ;
@@ -177,14 +240,11 @@ int32_t uart_set_configuration(UART_Configuration *config)
177
240
{
178
241
uint32_t ctrl ;
179
242
180
- // Get current configuration; clearing parameters that may be configured here
181
- ctrl = CdcAcmUart -> ctrl & ~(MXC_F_UART_CTRL_PARITY |
182
- MXC_F_UART_CTRL_DATA_SIZE |
183
- MXC_F_UART_CTRL_EXTRA_STOP |
184
- MXC_F_UART_CTRL_CTS_EN |
185
- MXC_F_UART_CTRL_RTS_EN );
243
+ // Disable UART, clear FIFOs and configuration
244
+ CdcAcmUart -> ctrl = 0 ;
186
245
187
246
switch (config -> Parity ) {
247
+ default :
188
248
case UART_PARITY_NONE : break ;
189
249
case UART_PARITY_ODD : ctrl |= MXC_S_UART_CTRL_PARITY_ODD ;
190
250
case UART_PARITY_EVEN : ctrl |= MXC_S_UART_CTRL_PARITY_EVEN ;
@@ -196,26 +256,30 @@ int32_t uart_set_configuration(UART_Configuration *config)
196
256
case UART_DATA_BITS_5 : ctrl |= MXC_S_UART_CTRL_DATA_SIZE_5_BITS ; break ;
197
257
case UART_DATA_BITS_6 : ctrl |= MXC_S_UART_CTRL_DATA_SIZE_6_BITS ; break ;
198
258
case UART_DATA_BITS_7 : ctrl |= MXC_S_UART_CTRL_DATA_SIZE_7_BITS ; break ;
259
+ default :
199
260
case UART_DATA_BITS_8 : ctrl |= MXC_S_UART_CTRL_DATA_SIZE_8_BITS ; break ;
200
261
case UART_DATA_BITS_16 : return 0 ;
201
262
}
202
263
203
264
switch (config -> StopBits ) {
265
+ default :
204
266
case UART_STOP_BITS_1 : break ;
205
267
case UART_STOP_BITS_1_5 :
206
268
case UART_STOP_BITS_2 : ctrl |= MXC_F_UART_CTRL_EXTRA_STOP ; break ;
207
269
}
208
270
209
271
switch (config -> FlowControl ) {
272
+ default :
210
273
case UART_FLOW_CONTROL_NONE : break ;
211
274
case UART_FLOW_CONTROL_RTS_CTS : return 0 ;
212
275
case UART_FLOW_CONTROL_XON_XOFF : return 0 ;
213
276
}
214
277
215
278
set_bitrate (config -> Baudrate );
216
279
217
- // Set the new configuration
280
+ // Set the new configuration, enable FIFOs and UART
218
281
CdcAcmUart -> ctrl = ctrl ;
282
+ CdcAcmUart -> ctrl |= MXC_F_UART_CTRL_RX_FIFO_EN | MXC_F_UART_CTRL_TX_FIFO_EN | MXC_F_UART_CTRL_UART_EN ;
219
283
220
284
return 1 ;
221
285
}
@@ -273,6 +337,7 @@ int32_t uart_write_data(uint8_t *data, uint16_t size)
273
337
{
274
338
uint16_t xfer_count = size ;
275
339
340
+ // Prioritize writes to TX FIFO, then to write_buffer
276
341
if (write_buffer .cnt_in == write_buffer .cnt_out ) {
277
342
while ((((CdcAcmUart -> tx_fifo_ctrl & MXC_F_UART_TX_FIFO_CTRL_FIFO_ENTRY ) >> MXC_F_UART_TX_FIFO_CTRL_FIFO_ENTRY_POS ) < MXC_UART_FIFO_DEPTH ) &&
278
343
(xfer_count > 0 )) {
@@ -326,19 +391,21 @@ void UART_IRQHandler(void)
326
391
CdcAcmUart -> intfl = intfl ;
327
392
328
393
if (intfl & MXC_F_UART_INTFL_RX_FIFO_OVERFLOW ) {
329
- read_buffer . data [ read_buffer . idx_in ++ ] = '*' ;
330
- read_buffer . idx_in &= ( BUFFER_SIZE - 1 ) ;
331
- read_buffer . cnt_in ++ ;
394
+ // Flush RX FIFO, prepare for new characters
395
+ CdcAcmUart -> ctrl &= ~ MXC_F_UART_CTRL_RX_FIFO_EN ;
396
+ CdcAcmUart -> ctrl |= MXC_F_UART_CTRL_RX_FIFO_EN ;
332
397
}
333
398
334
- if (intfl & ( MXC_F_UART_INTFL_RX_FIFO_NOT_EMPTY | UART_ERRORS ) ) {
399
+ if (intfl & MXC_F_UART_INTFL_RX_FIFO_NOT_EMPTY ) {
335
400
while ((CdcAcmUart -> rx_fifo_ctrl & MXC_F_UART_RX_FIFO_CTRL_FIFO_ENTRY ) &&
336
401
((read_buffer .cnt_in - read_buffer .cnt_out ) < BUFFER_SIZE )) {
337
402
read_buffer .data [read_buffer .idx_in ++ ] = CdcAcmUartFifo -> rx ;
338
403
CdcAcmUart -> intfl = MXC_F_UART_INTFL_RX_FIFO_NOT_EMPTY ;
339
404
read_buffer .idx_in &= (BUFFER_SIZE - 1 );
340
405
read_buffer .cnt_in ++ ;
341
406
}
407
+
408
+ // Ring buffer overflow
342
409
if (((read_buffer .cnt_in - read_buffer .cnt_out ) >= BUFFER_SIZE )) {
343
410
read_buffer .data [read_buffer .idx_in ++ ] = '%' ;
344
411
read_buffer .idx_in &= (BUFFER_SIZE - 1 );
0 commit comments