/* * uart/uart.c -- hardware UART interface * * Copyright (C) 2016-2017 Tomasz Kramkowski * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include "uart.h" enum { SBR_VAL = 39, BRFA_VAL = 2, SRBSZ = 2048, FIFOSZ = 8, }; static struct { unsigned char buf[SRBSZ]; int start, end; } send_rb = { .start = 0, .end = 0 }; static inline size_t rb_store(void *_data, size_t size) { unsigned char *data = _data; size_t avail; int start, end; start = send_rb.start; end = send_rb.end; avail = (start + sizeof send_rb.buf - end) % sizeof send_rb.buf - 1; if (size > avail) { GPIOC_PSOR = BV(5); size = avail; } for (size_t i = 0; i < size; i++) send_rb.buf[(end + i) % sizeof send_rb.buf] = data[i]; send_rb.end = (end + size) % sizeof send_rb.buf; /* Enable Transmission Interrupt */ CLI(); SET_BIT(UART0_C2, C2_TIE); STI(); return size; } void uart_setup(void) { /* Clock gating for UART0 */ SET_BIT(SIM_SCGC4, SCGC4_UART0); /* Disable TX and RX */ SET_MASKED(UART0_C2, BV(C2_TE) | BV(C2_RE), 0); /* Default settings */ UART0_C1 = 0; /* SBR and BRFA for baud rate */ SET_MASKED(UART0_BDH, BDH_SBR_M, ((SBR_VAL & 0x1f00) >> 8) << BDH_SBR); SET_MASKED(UART0_BDL, BDL_SBR_M, (SBR_VAL & 0xff) << BDL_SBR); SET_MASKED(UART0_C4, C4_BRFA_M, BRFA_VAL << C4_BRFA); /* flush and enable TX FIFO */ UART0_CFIFO |= BV(CFIFO_TXFLUSH); UART0_PFIFO |= BV(PFIFO_TXFE); /* set watermark at 1 entry */ SET_MASKED(UART0_TWFIFO, TWFIFO_TXWATER_M, 1 << TWFIFO_TXWATER); /* NVIC enable interrupt */ ISR_CLRPEND(45); INTPRI(45) = 0xf0; ISR_SETENA(45); /* enable correct ALT MUX on TX pin */ PORTB_PCR(17) = 3 << PCR_MUX; /* enable TX */ SET_MASKED(UART0_C2, BV(C2_TE), BV(C2_TE)); } void uart_putchar(int c) { static unsigned char buf[128]; static size_t size = 0; if (c == '\n') uart_putchar('\r'); buf[size++] = c; if (size >= sizeof buf || c == '\n') { rb_store(buf, size); size = 0; } } void uart_puts(const char *s) { for (size_t i = 0; s[i]; i++) uart_putchar(s[i]); uart_putchar('\n'); } static inline void putul(unsigned long n, int base) { static const char *digits = "0123456789abcdef"; int i; char tmp[64]; if (base < 2 || base > 16) return; if (n == 0) { uart_putchar('0'); return; } for (i = 0; n; i++) { int res = n % base; n = n / base; tmp[i] = digits[res]; } for (i -= 1; i >= 0; i--) uart_putchar(tmp[i]); } static inline void putl(long n) { int i; char tmp[64]; if (n < 0) { uart_putchar('-'); n *= -1; } if (n == 0) { uart_putchar('0'); return; } for (i = 0; n; i++) { int res = n % 10; n = n / 10; tmp[i] = '0' + res; } for (i -= 1; i >= 0; i--) uart_putchar(tmp[i]); } void uart_printf(const char *fmt, ...) { va_list ap; const char *p; int ival; unsigned uival; long lival; unsigned long ulival; va_start(ap, fmt); for (p = fmt; *p; p++) { if (*p != '%') { uart_putchar(*p); continue; } switch (*++p) { case 'd': case 'i': ival = va_arg(ap, int); putl(ival); break; case 'o': case 'u': case 'x': uival = va_arg(ap, unsigned); switch (*p) { case 'o': putul(uival, 8); break; case 'u': putul(uival, 10); break; case 'x': putul(uival, 16); break; } break; case 'l': switch (*++p) { case 'd': case 'i': lival = va_arg(ap, long); putl(lival); break; case 'o': case 'u': case 'x': ulival = va_arg(ap, unsigned long); switch (*p) { case 'o': putul(ulival, 8); break; case 'u': putul(ulival, 10); break; case 'x': putul(ulival, 16); break; } break; } } } va_end(ap); } void uart0_isr(void) { int avail, start, end, tosend; if (!GET_BIT(UART0_S1, S1_TDRE)) return; avail = FIFOSZ - UART0_TCFIFO; CLI(); start = send_rb.start; end = send_rb.end; tosend = (end + sizeof send_rb.buf - start) % sizeof send_rb.buf; if (tosend == 0) UNSET_BIT(UART0_C2, C2_TIE); STI(); if (tosend > avail) tosend = avail; for (int i = 0; i < tosend; i++) UART0_D = send_rb.buf[(start + i) % sizeof send_rb.buf]; send_rb.start = (start + tosend) % sizeof send_rb.buf; }