1 /*
2 * Copyright (c) 2006-2021, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2013-05-18 Bernard The first version for LPC40xx
9 * 2014-12-16 RT_learning The first version for LPC5410x
10 */
11
12
13 #include <rthw.h>
14 #include <rtthread.h>
15 #include <rtdevice.h>
16
17 #include "chip.h"
18
19 static uint32_t _UART_DivClk(uint32_t pclk, uint32_t m);
20 static uint32_t _UART_GetHighDiv(uint32_t val, uint8_t strict);
21 static int32_t _CalcErr(uint32_t n, uint32_t d, uint32_t *prev);
22 static ErrorCode_t _UART_CalcDiv(UART_BAUD_T *ub);
23 static void _UART_CalcMul(UART_BAUD_T *ub);
24
25 struct lpc_uart
26 {
27 LPC_USART_T *UART;
28 IRQn_Type UART_IRQn;
29 };
30
lpc_configure(struct rt_serial_device * serial,struct serial_configure * cfg)31 static rt_err_t lpc_configure(struct rt_serial_device *serial, struct serial_configure *cfg)
32 {
33 struct lpc_uart *uart;
34
35 UART_BAUD_T baud;
36 UART_CFG_T UART_cfg;
37
38 RT_ASSERT(serial != RT_NULL);
39 uart = (struct lpc_uart *)serial->parent.user_data;
40
41 /* Initialize UART Configuration parameter structure to default state:
42 * Baudrate = 115200 b
43 * 8 data bit
44 * 1 Stop bit
45 * None parity
46 */
47
48 /* Set up baudrate parameters */
49 baud.clk = Chip_Clock_GetAsyncSyscon_ClockRate(); /* Clock frequency */
50 baud.baud = cfg->baud_rate; /* Required baud rate */
51 baud.ovr = 0; /* Set the oversampling to the recommended rate */
52 baud.mul = baud.div = 0;
53
54 if(!baud.mul)
55 {
56 _UART_CalcMul(&baud);
57 }
58 _UART_CalcDiv(&baud);
59
60 /* Set fractional control register */
61 LPC_ASYNC_SYSCON->FRGCTRL = ((uint32_t) baud.mul << 8) | 0xFF;
62
63 /* Configure the UART */
64 UART_cfg.cfg = UART_CFG_8BIT;
65 UART_cfg.div = baud.div; /* Use the calculated div value */
66 UART_cfg.ovr = baud.ovr; /* Use oversampling rate from baud */
67 UART_cfg.res = UART_BIT_DLY(cfg->baud_rate);
68
69 /* P254,255,246 */
70 uart->UART->OSR = (UART_cfg.ovr - 1) & 0x0F;
71 uart->UART->BRG = (UART_cfg.div - 1) & 0xFFFF;
72 uart->UART->CFG = UART_CFG_ENABLE | (UART_cfg.cfg & ~UART_CFG_RES);
73
74 return RT_EOK;
75 }
76
lpc_control(struct rt_serial_device * serial,int cmd,void * arg)77 static rt_err_t lpc_control(struct rt_serial_device *serial, int cmd, void *arg)
78 {
79 struct lpc_uart *uart;
80
81 RT_ASSERT(serial != RT_NULL);
82 uart = (struct lpc_uart *)serial->parent.user_data;
83
84 switch (cmd)
85 {
86 case RT_DEVICE_CTRL_CLR_INT:
87 /* disable rx irq */
88 uart->UART->INTENCLR &= ~0x01;
89 break;
90 case RT_DEVICE_CTRL_SET_INT:
91 /* enable rx irq */
92 uart->UART->INTENSET |= 0x01;
93 break;
94 }
95
96 return RT_EOK;
97 }
98
lpc_putc(struct rt_serial_device * serial,char c)99 static int lpc_putc(struct rt_serial_device *serial, char c)
100 {
101 struct lpc_uart *uart;
102
103 uart = (struct lpc_uart *)serial->parent.user_data;
104 while(!(uart->UART->STAT & (0x01<<2)));
105
106 uart->UART->TXDAT = c ;
107
108
109 return 1;
110 }
111
112
113
lpc_getc(struct rt_serial_device * serial)114 static int lpc_getc(struct rt_serial_device *serial)
115 {
116 struct lpc_uart *uart;
117
118 uart = (struct lpc_uart *)serial->parent.user_data;
119 if (uart->UART->STAT & 0x01)
120 return (uart->UART->RXDAT);
121 else
122 return -1;
123 }
124
125 static const struct rt_uart_ops lpc_uart_ops =
126 {
127 lpc_configure,
128 lpc_control,
129 lpc_putc,
130 lpc_getc,
131 };
132
133
134 /* UART0 device driver structure */
135 struct lpc_uart uart0 =
136 {
137 LPC_USART0,
138 UART0_IRQn,
139 };
140 struct rt_serial_device serial0;
141
142
143
UART0_IRQHandler(void)144 void UART0_IRQHandler(void)
145 {
146 volatile uint32_t INTSTAT, tmp;
147 /* enter interrupt */
148 rt_interrupt_enter();
149
150 INTSTAT = LPC_USART0->INTSTAT;
151
152 INTSTAT &= 0x01;
153 switch (INTSTAT)
154 {
155 case 0x01:
156 rt_hw_serial_isr(&serial0, RT_SERIAL_EVENT_RX_IND);
157 break;
158 default :
159 tmp = LPC_USART0->INTSTAT;
160 RT_UNUSED(tmp);
161 break;
162 }
163 /* leave interrupt */
164 rt_interrupt_leave();
165 }
166
rt_hw_uart_init(void)167 void rt_hw_uart_init(void)
168 {
169 struct lpc_uart *uart;
170 struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;
171
172 uart = &uart0;
173
174 serial0.ops = &lpc_uart_ops;
175 serial0.config = config;
176 serial0.parent.user_data = uart;
177
178 /* Enable IOCON clock Then your cfg will effective P38 */
179 LPC_SYSCON->AHBCLKCTRLSET[0] = (1UL << 13);
180
181 /* Setup UART TX,RX Pin configuration cfg Pin as Tx, Rx */
182 /* P63,P77
183 Selects pin function 1 IOCON_FUNC1
184 No addition pin function IOCON_MODE_INACT
185 Enables digital function by setting 1 to bit 7(default) IOCON_DIGITAL_EN
186 */
187 LPC_IOCON->PIO[0][0] = (0x1 | (0x0 << 3) | (0x1 << 7));
188 LPC_IOCON->PIO[0][1] = (0x1 | (0x0 << 3) | (0x1 << 7));
189
190
191 /* Enable asynchronous APB bridge and subsystem P30 */
192 LPC_SYSCON->ASYNCAPBCTRL = 0x01;
193
194 /* The UART clock rate is the main system clock divided by this value P59 */
195 LPC_ASYNC_SYSCON->ASYNCCLKDIV = 1; /* Set Async clock divider to 1 */
196
197 /* Enable peripheral clock(asynchronous APB) to UART0 P57*/
198 LPC_ASYNC_SYSCON->ASYNCAPBCLKCTRLSET = (1 << 0x01);
199
200 /* Controls the clock for the Fractional Rate Generator used with the USARTs P57*/
201 LPC_ASYNC_SYSCON->ASYNCAPBCLKCTRLSET = (1 << 0x0F); /* Enable clock to Fractional divider */
202
203 /* preemption = 1, sub-priority = 1 */
204 NVIC_SetPriority(uart->UART_IRQn, ((0x01 << 3) | 0x01));
205
206 /* Enable Interrupt for UART channel */
207 NVIC_EnableIRQ(uart->UART_IRQn);
208
209 /* register UART0 device */
210 rt_hw_serial_register(&serial0, "uart0",
211 RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_STREAM,
212 uart);
213 }
214
215
216 /* PRIVATE: Division logic to divide without integer overflow */
_UART_DivClk(uint32_t pclk,uint32_t m)217 static uint32_t _UART_DivClk(uint32_t pclk, uint32_t m)
218 {
219 uint32_t q, r, u = pclk >> 24, l = pclk << 8;
220 m = m + 256;
221 q = (1 << 24) / m;
222 r = (1 << 24) - (q * m);
223 return ((q * u) << 8) + (((r * u) << 8) + l) / m;
224 }
225
226 /* PRIVATE: Get highest Over sampling value */
_UART_GetHighDiv(uint32_t val,uint8_t strict)227 static uint32_t _UART_GetHighDiv(uint32_t val, uint8_t strict)
228 {
229 int32_t i, max = strict ? 16 : 5;
230 for (i = 16; i >= max; i--)
231 {
232 if (!(val % i))
233 {
234 return i;
235 }
236 }
237 return 0;
238 }
239
240 /* Calculate error difference */
_CalcErr(uint32_t n,uint32_t d,uint32_t * prev)241 static int32_t _CalcErr(uint32_t n, uint32_t d, uint32_t *prev)
242 {
243 uint32_t err = n - (n / d) * d;
244 uint32_t herr = ((n / d) + 1) * d - n;
245 if (herr < err) {
246 err = herr;
247 }
248
249 if (*prev <= err) {
250 return 0;
251 }
252 *prev = err;
253 return (herr == err) + 1;
254 }
255
256 /* Calculate the base DIV value */
_UART_CalcDiv(UART_BAUD_T * ub)257 static ErrorCode_t _UART_CalcDiv(UART_BAUD_T *ub)
258 {
259 int32_t i = 0;
260 uint32_t perr = ~0UL;
261
262 if (!ub->div) {
263 i = ub->ovr ? ub->ovr : 16;
264 }
265
266 for (; i > 4; i--) {
267 int32_t tmp = _CalcErr(ub->clk, ub->baud * i, &perr);
268
269 /* Continue when no improvement seen in err value */
270 if (!tmp) {
271 continue;
272 }
273
274 ub->div = tmp - 1;
275 if (ub->ovr == i) {
276 break;
277 }
278 ub->ovr = i;
279 }
280
281 if (!ub->ovr) {
282 return ERR_UART_BAUDRATE;
283 }
284
285 ub->div += ub->clk / (ub->baud * ub->ovr);
286 if (!ub->div) {
287 return ERR_UART_BAUDRATE;
288 }
289
290 ub->baud = ub->clk / (ub->div * ub->ovr);
291 return LPC_OK;
292 }
293
294 /* Calculate the best MUL value */
_UART_CalcMul(UART_BAUD_T * ub)295 static void _UART_CalcMul(UART_BAUD_T *ub)
296 {
297 uint32_t m, perr = ~0UL, pclk = ub->clk, ovr = ub->ovr;
298
299 /* If clock is UART's base clock calculate only the divider */
300 for (m = 0; m < 256; m++) {
301 uint32_t ov = ovr, x, v, tmp;
302
303 /* Get clock and calculate error */
304 x = _UART_DivClk(pclk, m);
305 tmp = _CalcErr(x, ub->baud, &perr);
306 v = (x / ub->baud) + tmp - 1;
307
308 /* Update if new error is better than previous best */
309 if (!tmp || (ovr && (v % ovr)) ||
310 (!ovr && ((ov = _UART_GetHighDiv(v, ovr)) == 0))) {
311 continue;
312 }
313
314 ub->ovr = ov;
315 ub->mul = m;
316 ub->clk = x;
317 ub->div = tmp - 1;
318 }
319 }
320