aboutsummaryrefslogtreecommitdiff
path: root/lib/chibios-contrib/os/hal/ports/MSP430X/hal_serial_lld.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/chibios-contrib/os/hal/ports/MSP430X/hal_serial_lld.c')
-rw-r--r--lib/chibios-contrib/os/hal/ports/MSP430X/hal_serial_lld.c668
1 files changed, 668 insertions, 0 deletions
diff --git a/lib/chibios-contrib/os/hal/ports/MSP430X/hal_serial_lld.c b/lib/chibios-contrib/os/hal/ports/MSP430X/hal_serial_lld.c
new file mode 100644
index 000000000..acd0208d6
--- /dev/null
+++ b/lib/chibios-contrib/os/hal/ports/MSP430X/hal_serial_lld.c
@@ -0,0 +1,668 @@
1/*
2 ChibiOS - Copyright (C) 2016 Andrew Wygle aka awygle
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15*/
16
17/**
18 * @file MSP430X/hal_serial_lld.c
19 * @brief MSP430X serial subsystem low level driver source.
20 *
21 * @addtogroup SERIAL
22 * @{
23 */
24
25#include "hal.h"
26
27#if (HAL_USE_SERIAL == TRUE) || defined(__DOXYGEN__)
28
29/*===========================================================================*/
30/* Driver local definitions. */
31/*===========================================================================*/
32
33/*===========================================================================*/
34/* Driver exported variables. */
35/*===========================================================================*/
36
37/** @brief USART0 serial driver identifier.*/
38#if (MSP430X_SERIAL_USE_USART0 == TRUE) || defined(__DOXYGEN__)
39#ifndef __MSP430_HAS_EUSCI_A0__
40#error "Cannot find USCI module to use for SD0"
41#endif
42#ifdef MSP430X_USCI_A0_USED
43#error "USCI module A0 already in use - USART0 not available"
44#endif
45SerialDriver SD0;
46#define MSP430X_USCI_A0_USED
47#endif
48
49/** @brief USART1 serial driver identifier.*/
50#if (MSP430X_SERIAL_USE_USART1 == TRUE) || defined(__DOXYGEN__)
51#ifndef __MSP430_HAS_EUSCI_A1__
52#error "Cannot find USCI module to use for SD1"
53#endif
54#ifdef MSP430X_USCI_A1_USED
55#error "USCI module A1 already in use - USART1 not available"
56#endif
57SerialDriver SD1;
58#define MSP430X_USCI_A1_USED
59#endif
60
61/** @brief USART2 serial driver identifier.*/
62#if (MSP430X_SERIAL_USE_USART2 == TRUE) || defined(__DOXYGEN__)
63#ifndef __MSP430_HAS_EUSCI_A2__
64#error "Cannot find USCI module to use for SD2"
65#endif
66#ifdef MSP430X_USCI_A2_USED
67#error "USCI module A2 already in use - USART2 not available"
68#endif
69SerialDriver SD2;
70#define MSP430X_USCI_A2_USED
71#endif
72
73/** @brief USART3 serial driver identifier.*/
74#if (MSP430X_SERIAL_USE_USART3 == TRUE) || defined(__DOXYGEN__)
75#ifndef __MSP430_HAS_EUSCI_A3__
76#error "Cannot find USCI module to use for SD3"
77#endif
78#ifdef MSP430X_USCI_A3_USED
79#error "USCI module A3 already in use - USART3 not available"
80#endif
81SerialDriver SD3;
82#define MSP430X_USCI_A3_USED
83#endif
84
85/*===========================================================================*/
86/* Driver local variables and types. */
87/*===========================================================================*/
88
89/**
90 * @brief Driver default configuration.
91 */
92static const SerialConfig default_config = { SERIAL_DEFAULT_BITRATE };
93
94/*===========================================================================*/
95/* Driver local functions. */
96/*===========================================================================*/
97
98/**
99 * @brief UCBRS calculation.
100 * @details This function calculates the UCBRS value for oversampled baud
101 * rates.
102 *
103 * @param[in] frac Fractional part of baud rate division, times 10000.
104 */
105static uint8_t UCBRS(uint16_t frac) {
106 /* TODO there must be a better way */
107 if (frac < 529)
108 return 0x00;
109 else if (frac < 715)
110 return 0x01;
111 else if (frac < 835)
112 return 0x02;
113 else if (frac < 1001)
114 return 0x04;
115 else if (frac < 1252)
116 return 0x08;
117 else if (frac < 1430)
118 return 0x10;
119 else if (frac < 1670)
120 return 0x20;
121 else if (frac < 2147)
122 return 0x11;
123 else if (frac < 2224)
124 return 0x21;
125 else if (frac < 2503)
126 return 0x22;
127 else if (frac < 3000)
128 return 0x44;
129 else if (frac < 3335)
130 return 0x25;
131 else if (frac < 3575)
132 return 0x49;
133 else if (frac < 3753)
134 return 0x4A;
135 else if (frac < 4003)
136 return 0x52;
137 else if (frac < 4286)
138 return 0x92;
139 else if (frac < 4378)
140 return 0x53;
141 else if (frac < 5002)
142 return 0x55;
143 else if (frac < 5715)
144 return 0xAA;
145 else if (frac < 6003)
146 return 0x6B;
147 else if (frac < 6254)
148 return 0xAD;
149 else if (frac < 6432)
150 return 0xB5;
151 else if (frac < 6667)
152 return 0xB6;
153 else if (frac < 7001)
154 return 0xD6;
155 else if (frac < 7147)
156 return 0xB7;
157 else if (frac < 7503)
158 return 0xBB;
159 else if (frac < 7861)
160 return 0xDD;
161 else if (frac < 8004)
162 return 0xED;
163 else if (frac < 8333)
164 return 0xEE;
165 else if (frac < 8464)
166 return 0xBF;
167 else if (frac < 8572)
168 return 0xDF;
169 else if (frac < 8751)
170 return 0xEF;
171 else if (frac < 9004)
172 return 0xF7;
173 else if (frac < 9170)
174 return 0xFB;
175 else if (frac < 9288)
176 return 0xFD;
177 else
178 return 0xFE;
179}
180
181/**
182 * @brief Modulation control word calculator.
183 * @details This function calculates the modulation control word from the
184 * input clock frequency and the requested baud rate.
185 *
186 * @param[in] baud Requested baud rate
187 * @param[in] freq Frequency of the clock driving the USCI module
188 */
189static uint16_t UCAxMCTLW(uint32_t baud, uint32_t freq) {
190
191 uint16_t n = freq / baud;
192 /*uint16_t frac = (freq * 10000 / baud) - ((freq / baud) * 10000);*/
193 uint16_t frac = (freq - (n * baud)) * 10000 / baud;
194 if (n > 16) {
195 while (n > 16) {
196 n -= 16;
197 }
198 return (UCBRS(frac) << 8) | (n << 4) | UCOS16;
199 }
200 return UCBRS(frac) << 8;
201}
202
203/**
204 * @brief UCBRW calculation.
205 * @details This function calculates the UCBRW value for all baud
206 * rates.
207 *
208 * @param[in] baud Requested baud rate
209 * @param[in] freq Frequency of the clock driving the USCI module
210 */
211static uint16_t UCAxBRW(uint32_t baud, uint32_t freq) {
212 uint16_t n = freq / baud;
213 if (n > 16) {
214 return n >> 4;
215 }
216 return n;
217}
218
219#if (MSP430X_SERIAL_USE_USART0 == TRUE) || defined(__DOXYGEN__)
220static void usart0_init(const SerialConfig * config) {
221 UCA0BRW = UCAxBRW(config->sc_bitrate, MSP430X_USART0_CLK_FREQ);
222 UCA0MCTLW = UCAxMCTLW(config->sc_bitrate, MSP430X_USART0_CLK_FREQ);
223 UCA0STATW = 0;
224 UCA0ABCTL = 0;
225 UCA0IRCTL = 0;
226 UCA0CTLW0 = (MSP430X_USART0_PARITY << 14) | (MSP430X_USART0_ORDER << 13) |
227 (MSP430X_USART0_SIZE << 12) | (MSP430X_USART0_STOP << 11) |
228 (MSP430X_USART0_UCSSEL);
229 UCA0IE = UCRXIE;
230}
231#endif
232
233#if (MSP430X_SERIAL_USE_USART1 == TRUE) || defined(__DOXYGEN__)
234static void usart1_init(const SerialConfig * config) {
235 UCA1BRW = UCAxBRW(config->sc_bitrate, MSP430X_USART1_CLK_FREQ);
236 UCA1MCTLW = UCAxMCTLW(config->sc_bitrate, MSP430X_USART1_CLK_FREQ);
237 UCA1STATW = 0;
238 UCA1ABCTL = 0;
239 UCA1IRCTL = 0;
240 UCA1CTLW0 = (MSP430X_USART1_PARITY << 14) | (MSP430X_USART1_ORDER << 13) |
241 (MSP430X_USART1_SIZE << 12) | (MSP430X_USART1_STOP << 11) |
242 (MSP430X_USART1_UCSSEL);
243 UCA1IE = UCRXIE;
244}
245#endif
246
247#if (MSP430X_SERIAL_USE_USART2 == TRUE) || defined(__DOXYGEN__)
248static void usart2_init(const SerialConfig * config) {
249 UCA2BRW = UCAxBRW(config->sc_bitrate, MSP430X_USART2_CLK_FREQ);
250 UCA2MCTLW = UCAxMCTLW(config->sc_bitrate);
251 UCA2STATW = 0;
252 UCA2ABCTL = 0;
253 UCA2IRCTL = 0;
254 UCA2CTLW0 = (MSP430X_USART2_PARITY << 14) | (MSP430X_USART2_ORDER << 13) |
255 (MSP430X_USART2_SIZE << 12) | (MSP430X_USART2_STOP << 11) |
256 (MSP430X_USART2_UCSSEL);
257 UCA2IE = UCRXIE;
258}
259#endif
260
261#if (MSP430X_SERIAL_USE_USART3 == TRUE) || defined(__DOXYGEN__)
262static void usart3_init(const SerialConfig * config) {
263 UCA3BRW = UCAxBRW(config->sc_bitrate, MSP430X_USART3_CLK_FREQ);
264 UCA3MCTLW = UCAxMCTLW(config->sc_bitrate, MSP430X_USART3_CLK_FREQ);
265 UCA3STATW = 0;
266 UCA3ABCTL = 0;
267 UCA3IRCTL = 0;
268 UCA3CTLW0 = (MSP430X_USART3_PARITY << 14) | (MSP430X_USART3_ORDER << 13) |
269 (MSP430X_USART3_SIZE << 12) | (MSP430X_USART3_STOP << 11) |
270 (MSP430X_USART3_UCSSEL);
271 UCA3IE = UCRXIE;
272}
273#endif
274
275#if (MSP430X_SERIAL_USE_USART0 == TRUE) || defined(__DOXYGEN__)
276static void notify0(io_queue_t * qp) {
277
278 (void)qp;
279 UCA0IE |= UCTXIE;
280}
281#endif
282
283#if (MSP430X_SERIAL_USE_USART1 == TRUE) || defined(__DOXYGEN__)
284static void notify1(io_queue_t * qp) {
285
286 (void)qp;
287 UCA1IE |= UCTXIE;
288}
289#endif
290
291#if (MSP430X_SERIAL_USE_USART2 == TRUE) || defined(__DOXYGEN__)
292static void notify2(io_queue_t * qp) {
293
294 (void)qp;
295 UCA2IE |= UCTXIE;
296}
297#endif
298
299#if (MSP430X_SERIAL_USE_USART3 == TRUE) || defined(__DOXYGEN__)
300static void notify3(io_queue_t * qp) {
301
302 (void)qp;
303 UCA3IE |= UCTXIE;
304}
305#endif
306
307/**
308 * @brief Error handling routine.
309 *
310 * @param[in] sra USCI status register containing errors
311 * @param[in] sdp pointer to a @p SerialDriver object
312 */
313static void set_error(uint16_t sra, SerialDriver * sdp) {
314 eventflags_t sts = 0;
315
316 if (sra & UCOE)
317 sts |= SD_OVERRUN_ERROR;
318 if (sra & UCPE)
319 sts |= SD_PARITY_ERROR;
320 if (sra & UCFE)
321 sts |= SD_FRAMING_ERROR;
322 osalSysLockFromISR();
323 chnAddFlagsI(sdp, sts);
324 osalSysUnlockFromISR();
325}
326
327/*===========================================================================*/
328/* Driver interrupt handlers. */
329/*===========================================================================*/
330
331#if MSP430X_SERIAL_USE_USART0 || defined(__DOXYGEN__)
332/**
333 * @brief USART0 interrupt handler.
334 *
335 * @isr
336 */
337PORT_IRQ_HANDLER(USCI_A0_VECTOR) {
338 msg_t b;
339
340 OSAL_IRQ_PROLOGUE();
341
342 switch (__even_in_range(UCA0IV, USCI_UART_UCTXCPTIFG)) {
343 case USCI_UART_UCRXIFG: /* RX interrupt */
344
345 /* Detect errors */
346 if (UCA0STATW & UCRXERR)
347 set_error(UCA0STATW, &SD0);
348
349 /* Data available */
350 osalSysLockFromISR();
351 sdIncomingDataI(&SD0, UCA0RXBUF);
352 osalSysUnlockFromISR();
353 break;
354
355 case USCI_UART_UCTXIFG: /* TX interrupt */
356
357 /* Transmission buffer empty */
358 osalSysLockFromISR();
359 b = sdRequestDataI(&SD0);
360 if (b < Q_OK) {
361 chnAddFlagsI(&SD0, CHN_OUTPUT_EMPTY);
362 UCA0IE = (UCA0IE & ~UCTXIE) | UCTXCPTIE;
363 UCA0IFG |= UCTXIFG; /* If we don't write to TXBUF, IFG won't get set */
364 }
365 else
366 UCA0TXBUF = b;
367 osalSysUnlockFromISR();
368 break;
369
370 case USCI_UART_UCTXCPTIFG: /* TX complete interrupt */
371
372 /* Physical transmission end */
373 osalSysLockFromISR();
374 if (oqIsEmptyI(&SD0.oqueue))
375 chnAddFlagsI(&SD0, CHN_TRANSMISSION_END);
376 UCA0IE &= ~UCTXCPTIE;
377 osalSysUnlockFromISR();
378 break;
379
380 default: /* other interrupts */
381 osalDbgAssert(false, "unhandled serial interrupt");
382 break;
383 }
384
385 OSAL_IRQ_EPILOGUE();
386}
387#endif
388
389#if MSP430X_SERIAL_USE_USART1 || defined(__DOXYGEN__)
390/**
391 * @brief USART1 interrupt handler.
392 *
393 * @isr
394 */
395PORT_IRQ_HANDLER(USCI_A1_VECTOR) {
396 msg_t b;
397
398 OSAL_IRQ_PROLOGUE();
399
400 switch (__even_in_range(UCA1IV, USCI_UART_UCTXCPTIFG)) {
401 case USCI_UART_UCRXIFG: /* RX interrupt */
402
403 /* Detect errors */
404 if (UCA1STATW & UCRXERR)
405 set_error(UCA1STATW, &SD1);
406
407 /* Data available */
408 osalSysLockFromISR();
409 sdIncomingDataI(&SD1, UCA1RXBUF);
410 osalSysUnlockFromISR();
411 break;
412
413 case USCI_UART_UCTXIFG: /* TX interrupt */
414
415 /* Transmission buffer empty */
416 osalSysLockFromISR();
417 b = sdRequestDataI(&SD1);
418 if (b < Q_OK) {
419 chnAddFlagsI(&SD1, CHN_OUTPUT_EMPTY);
420 UCA1IE = (UCA1IE & ~UCTXIE) | UCTXCPTIE;
421 UCA1IFG |= UCTXIFG; /* If we don't write to TXBUF, IFG won't get set */
422 }
423 else
424 UCA1TXBUF = b;
425 osalSysUnlockFromISR();
426 break;
427
428 case USCI_UART_UCTXCPTIFG: /* TX complete interrupt */
429
430 /* Physical transmission end */
431 osalSysLockFromISR();
432 if (oqIsEmptyI(&SD1.oqueue))
433 chnAddFlagsI(&SD1, CHN_TRANSMISSION_END);
434 UCA1IE &= ~UCTXCPTIE;
435 osalSysUnlockFromISR();
436 break;
437
438 default: /* other interrupts */
439 osalDbgAssert(false, "unhandled serial interrupt");
440 break;
441 }
442
443 OSAL_IRQ_EPILOGUE();
444}
445#endif
446
447#if MSP430X_SERIAL_USE_USART2 || defined(__DOXYGEN__)
448/**
449 * @brief USART2 interrupt handler.
450 *
451 * @isr
452 */
453PORT_IRQ_HANDLER(USCI_A2_VECTOR) {
454 msg_t b;
455
456 OSAL_IRQ_PROLOGUE();
457
458 switch (__even_in_range(UCA2IV, USCI_UART_UCTXCPTIFG)) {
459 case USCI_UART_UCRXIFG: /* RX interrupt */
460
461 /* Detect errors */
462 if (UCA2STATW & UCRXERR)
463 set_error(UCA2STATW, &SD2);
464
465 /* Data available */
466 osalSysLockFromISR();
467 sdIncomingDataI(&SD2, UCA2RXBUF);
468 osalSysUnlockFromISR();
469 break;
470
471 case USCI_UART_UCTXIFG: /* TX interrupt */
472
473 /* Transmission buffer empty */
474 osalSysLockFromISR();
475 b = sdRequestDataI(&SD2);
476 if (b < Q_OK) {
477 chnAddFlagsI(&SD2, CHN_OUTPUT_EMPTY);
478 UCA2IE = (UCA2IE & ~UCTXIE) | UCTXCPTIE;
479 UCA2IFG |= UCTXIFG; /* If we don't write to TXBUF, IFG won't get set */
480 }
481 else
482 UCA2TXBUF = b;
483 osalSysUnlockFromISR();
484 break;
485
486 case USCI_UART_UCTXCPTIFG: /* TX complete interrupt */
487
488 /* Physical transmission end */
489 osalSysLockFromISR();
490 if (oqIsEmptyI(&SD2.oqueue))
491 chnAddFlagsI(&SD2, CHN_TRANSMISSION_END);
492 UCA2IE &= ~UCTXCPTIE;
493 osalSysUnlockFromISR();
494 break;
495
496 default: /* other interrupts */
497 osalDbgAssert(false, "unhandled serial interrupt");
498 break;
499 }
500
501 OSAL_IRQ_EPILOGUE();
502}
503#endif
504
505#if MSP430X_SERIAL_USE_USART3 || defined(__DOXYGEN__)
506/**
507 * @brief USART3 interrupt handler.
508 *
509 * @isr
510 */
511PORT_IRQ_HANDLER(USCI_A3_VECTOR) {
512 msg_t b;
513
514 OSAL_IRQ_PROLOGUE();
515
516 switch (__even_in_range(UCA3IV, USCI_UART_UCTXCPTIFG)) {
517 case USCI_UART_UCRXIFG: /* RX interrupt */
518
519 /* Detect errors */
520 if (UCA3STATW & UCRXERR)
521 set_error(UCA3STATW, &SD3);
522
523 /* Data available */
524 osalSysLockFromISR();
525 sdIncomingDataI(&SD3, UCA3RXBUF);
526 osalSysUnlockFromISR();
527 break;
528
529 case USCI_UART_UCTXIFG: /* TX interrupt */
530
531 /* Transmission buffer empty */
532 osalSysLockFromISR();
533 b = sdRequestDataI(&SD3);
534 if (b < Q_OK) {
535 chnAddFlagsI(&SD3, CHN_OUTPUT_EMPTY);
536 UCA3IE = (UCA3IE & ~UCTXIE) | UCTXCPTIE;
537 UCA3IFG |= UCTXIFG; /* If we don't write to TXBUF, IFG won't get set */
538 }
539 else
540 UCA3TXBUF = b;
541 osalSysUnlockFromISR();
542 break;
543
544 case USCI_UART_UCTXCPTIFG: /* TX complete interrupt */
545
546 /* Physical transmission end */
547 osalSysLockFromISR();
548 if (oqIsEmptyI(&SD3.oqueue))
549 chnAddFlagsI(&SD3, CHN_TRANSMISSION_END);
550 UCA3IE &= ~UCTXCPTIE;
551 osalSysUnlockFromISR();
552 break;
553
554 default: /* other interrupts */
555 osalDbgAssert(false, "unhandled serial interrupt");
556 break;
557 }
558
559 OSAL_IRQ_EPILOGUE();
560}
561#endif
562
563/*===========================================================================*/
564/* Driver exported functions. */
565/*===========================================================================*/
566
567/**
568 * @brief Low level serial driver initialization.
569 *
570 * @notapi
571 */
572void sd_lld_init(void) {
573
574#if MSP430X_SERIAL_USE_USART0 == TRUE
575 sdObjectInit(&SD0, NULL, notify0);
576#endif
577
578#if MSP430X_SERIAL_USE_USART1 == TRUE
579 sdObjectInit(&SD1, NULL, notify1);
580#endif
581
582#if MSP430X_SERIAL_USE_USART2 == TRUE
583 sdObjectInit(&SD2, NULL, notify2);
584#endif
585
586#if MSP430X_SERIAL_USE_USART3 == TRUE
587 sdObjectInit(&SD3, NULL, notify3);
588#endif
589}
590
591/**
592 * @brief Low level serial driver configuration and (re)start.
593 *
594 * @param[in] sdp pointer to a @p SerialDriver object
595 * @param[in] config the architecture-dependent serial driver configuration.
596 * If this parameter is set to @p NULL then a default
597 * configuration is used.
598 *
599 * @notapi
600 */
601void sd_lld_start(SerialDriver * sdp, const SerialConfig * config) {
602
603 if (config == NULL) {
604 config = &default_config;
605 }
606
607 if (sdp->state == SD_STOP) {
608#if MSP430X_SERIAL_USE_USART0 == TRUE
609 if (&SD0 == sdp) {
610 usart0_init(config);
611 }
612#endif
613#if MSP430X_SERIAL_USE_USART1 == TRUE
614 if (&SD1 == sdp) {
615 usart1_init(config);
616 }
617#endif
618#if MSP430X_SERIAL_USE_USART2 == TRUE
619 if (&SD2 == sdp) {
620 usart2_init(config);
621 }
622#endif
623#if MSP430X_SERIAL_USE_USART3 == TRUE
624 if (&SD3 == sdp) {
625 usart3_init(config);
626 }
627#endif
628 }
629}
630
631/**
632 * @brief Low level serial driver stop.
633 * @details De-initializes the USART, stops the associated clock, resets the
634 * interrupt vector.
635 *
636 * @param[in] sdp pointer to a @p SerialDriver object
637 *
638 * @notapi
639 */
640void sd_lld_stop(SerialDriver * sdp) {
641
642 if (sdp->state == SD_READY) {
643#if MSP430X_SERIAL_USE_USART0 == TRUE
644 if (&SD0 == sdp) {
645 UCA0CTLW0 = UCSWRST;
646 }
647#endif
648#if MSP430X_SERIAL_USE_USART1 == TRUE
649 if (&SD1 == sdp) {
650 UCA1CTLW0 = UCSWRST;
651 }
652#endif
653#if MSP430X_SERIAL_USE_USART2 == TRUE
654 if (&SD2 == sdp) {
655 UCA2CTLW0 = UCSWRST;
656 }
657#endif
658#if MSP430X_SERIAL_USE_USART3 == TRUE
659 if (&SD3 == sdp) {
660 UCA3CTLW0 = UCSWRST;
661 }
662#endif
663 }
664}
665
666#endif /* HAL_USE_SERIAL == TRUE */
667
668/** @} */