#include // Register offsets from base address #define RXTX 0 #define IER 1 #define IIR 2 #define LCR 3 #define MCR 4 #define LSR 5 #define MSR 6 #define DIVL 8 #define DIVH 9 // Interrupt Enable Register (IER) bits #define IER_RBRI 0x01 // Receive buffer register interrupt (RHR != empty) #define IER_THRI 0x02 // Transmitter hold register interrupt (THR == empty) #define IER_RLSI 0x04 // Reciever line status interrupt (LSR error bits) #define IER_MSI 0x08 // Modem status interrupt (MSR change) // Line Status Register (LSR) bits #define LSR_DR 0x01 // Data Ready #define LSR_OE 0x02 // Overrun Error #define LSR_PE 0x04 // Parity Error #define LSR_FE 0x08 // Framing Error #define LSR_BI 0x10 // Break Interrupt #define LSR_THRE 0x20 // Transmit Holding Register Empty #define LSR_TEMT 0x40 // Transmit Holding & Shift Register Empty // Modem Status Register (MSR) bits #define MSR_DCTS 0x01 // Delta CTS (changed since last read) #define MSR_DDSR 0x02 // Delta DSR (changed since last read) #define MSR_TERI 0x04 // Trailing Edge of RIn seen #define MSR_DDCD 0x08 // Delta DCD (changed since last read) #define MSR_CTS 0x10 // Current status of Clear To Send #define MSR_DSR 0x20 // Current status of Data Set Ready #define MSR_RI 0x40 // Current status of Ring Indicator #define MSR_DCD 0x80 // Current status of Data Carrier Detect // Helper macros #define RD_RBR(base) __builtin_import((size_t) (base) + RXTX) #define WR_THR(base,data) __builtin_export((uint8_t) (data), (size_t) (base) + RXTX) #define RD_IER(base) __builtin_import((size_t) (base) + IER) #define WR_IER(base,data) __builtin_export((uint8_t) (data), (size_t) (base) + IER) #define RD_IIR(base) __builtin_import((size_t) (base) + IIR) #define WR_IIR(base,data) __builtin_export((uint8_t) (data), (size_t) (base) + IIR) #define RD_LCR(base) __builtin_import((size_t) (base) + LCR) #define WR_LCR(base,data) __builtin_export((uint8_t) (data), (size_t) (base) + LCR) #define RD_LSR(base) __builtin_import((size_t) (base) + LSR) #define RD_MCR(base) __builtin_import((size_t) (base) + MCR) #define WR_MCR(base,data) __builtin_export((uint8_t) (data), (size_t) (base) + MCR) #define RD_MSR(base) __builtin_import((size_t) (base) + MSR) #define RD_DIV(base) \ (((uint16_t) __builtin_import((size_t) (base) + DIVH)) << 8) | \ ((uint16_t) __builtin_import((size_t) (base) + DIVL)) #define WR_DIV(base,data) \ (__builtin_export((uint8_t) ((data) >> 8), ((size_t) (base) + DIVH)),\ __builtin_export((uint8_t) ((data) & 0xff), ((size_t) (base) + DIVL))) #define LM8_UART_COUNT 1 #define TX_FIFO_LEN 4 #define RX_FIFO_LEN 4 typedef struct _st_uart { size_t base; uint8_t irq; volatile uint8_t rx_data[RX_FIFO_LEN]; volatile uint8_t tx_data[TX_FIFO_LEN]; uint8_t rx_rd; uint8_t rx_wr; uint8_t tx_rd; uint8_t tx_wr; volatile uint8_t rx_count; volatile uint8_t tx_count; uint8_t error; } uart_t; static uart_t uart_ctx[LM8_UART_COUNT]; #define UART_CLOCK 20000000UL #define UART_SPEED 115200UL #define UART_DIVISOR (UART_CLOCK / UART_SPEED) void lm8_uart_init (uint8_t idx, size_t base, uint8_t irq) { uart_t *uart = &uart_ctx[idx]; uart->base = base; uart->irq = irq; uart->rx_count = 0; uart->tx_count = 0; uart->rx_rd = 0; uart->rx_wr = 0; uart->tx_rd = 0; uart->tx_wr = 0; uart->error = 0; WR_DIV (uart->base, UART_DIVISOR); // Setup transmission for 8,N,1 WR_LCR (uart->base, 0x03); // Enable all but transmit interrupt //lm8_interrupt_enable (irq); WR_IER (uart->base, (IER_RBRI | IER_RLSI | IER_MSI)); return; } char lm8_uart_getc (uint8_t idx) { uart_t *uart = &uart_ctx[idx]; uint8_t data; while (!uart->rx_count); data = uart->rx_data[uart->rx_rd]; uart->rx_rd++; if (uart->rx_rd >= RX_FIFO_LEN) uart->rx_rd = 0; uart->rx_count--; return (char) data; } void lm8_uart_putc (uint8_t idx, char ch) { uart_t *uart = &uart_ctx[idx]; while (uart->tx_count == TX_FIFO_LEN); uart->tx_data[uart->tx_wr] = (uint8_t) ch; uart->tx_wr++; if (uart->tx_wr >= TX_FIFO_LEN) uart->tx_wr = 0; lm8_interrupt_global_disable (); uart->tx_count++; uint8_t ier = RD_IER (uart->base); WR_IER (uart->base, (ier | IER_THRI)); lm8_interrupt_global_enable (); return; } void lm8_uart_isr (uint8_t idx) { uart_t *uart = &uart_ctx[idx]; uint8_t reg; reg = RD_IIR (uart->base); switch (reg) { case 0: // Modem status change reg = RD_MSR (uart->base); // Do nothing ATM break; case 2: // Transmit holding empty WR_THR (uart->base, uart->tx_data[uart->tx_rd]); uart->tx_rd++; if (uart->tx_rd >= TX_FIFO_LEN) uart->tx_rd = 0; uart->tx_count--; if (uart->tx_count == 0) { reg = RD_IER (uart->base); reg &= ~IER_THRI; WR_IER (uart->base, reg); } break; case 4: // Receive data ready uart->rx_data[uart->rx_wr] = RD_RBR (uart->base); uart->rx_wr++; if (uart->rx_wr >= RX_FIFO_LEN) uart->rx_wr = 0; if (uart->rx_count == RX_FIFO_LEN) uart->error++; else uart->rx_count++; break; case 6: // Error flags reg = RD_LSR (uart->base); if (reg & (LSR_OE | LSR_PE | LSR_FE)) uart->error++; break; } return; } void simple_putc (char ch) { uart_t *uart = &uart_ctx[0]; WR_THR(uart->base, ch); uint8_t lsr; while (1) { lsr = RD_LSR(uart->base); if ((lsr & 0x60) == 0x60) break; } return; }