/* * Copyright (C) 2015 Freie Universität Berlin * * This file is subject to the terms and conditions of the GNU Lesser * General Public License v2.1. See the file LICENSE in the top level * directory for more details. */ /** * @ingroup driver_periph * @{ * * @file * @brief Low-level UART driver implementation * * @author Thomas Eichinger * @author Troels Hoffmeyer * @author Hauke Petersen * * @} */ #include "board.h" #include "cpu.h" #include "periph/uart.h" #include "periph_conf.h" #include "sched.h" #include "thread.h" /* guard file in case no UART device was specified */ #if UART_NUMOF /** * @brief Each UART device has to store two callbacks. */ typedef struct { uart_rx_cb_t rx_cb; uart_tx_cb_t tx_cb; void *arg; } uart_conf_t; /** * @brief Unified interrupt handler for all UART devices * * @param uartnum the number of the UART that triggered the ISR * @param uart the UART device that triggered the ISR */ static inline void irq_handler(uart_t uartnum, SercomUsart *uart); /** * @brief Allocate memory to store the callback functions. */ static uart_conf_t uart_config[UART_NUMOF]; int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, uart_tx_cb_t tx_cb, void *arg) { /* initialize basic functionality */ int res = uart_init_blocking(uart, baudrate); if (res != 0) { return res; } /* register callbacks */ uart_config[uart].rx_cb = rx_cb; uart_config[uart].tx_cb = tx_cb; uart_config[uart].arg = arg; /* configure interrupts and enable RX interrupt */ switch (uart) { case UART_0: NVIC_SetPriority(UART_0_IRQ, UART_IRQ_PRIO); NVIC_EnableIRQ(UART_0_IRQ); UART_0_DEV.INTENSET.bit.RXC = 1; break; } return 0; } int uart_init_blocking(uart_t uart, uint32_t baudrate) { uint32_t baud = ((((uint32_t)CLOCK_CORECLOCK * 10) / baudrate) / 16); switch (uart) { #if UART_0_EN case UART_0: /* Turn on power manager for sercom */ PM->APBCMASK.reg |= PM_APBCMASK_SERCOM0; /* configure GCLK0 to feed sercom0 */; GCLK->CLKCTRL.reg = (uint16_t)((GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | (SERCOM0_GCLK_ID_CORE << GCLK_CLKCTRL_ID_Pos))); while (GCLK->STATUS.bit.SYNCBUSY); /* configure PINS to input/output*/ UART_0_PORT.DIRSET.reg = (1 << UART_0_TX_PIN); /* tx's direction is output */ UART_0_PORT.PINCFG[UART_0_RX_PIN % 32].bit.INEN = true; /* buffer rx pin's value */ /* enable PMUX for pins and set to config D. See spec p. 12 */ UART_0_PORT.WRCONFIG.reg = PORT_WRCONFIG_WRPINCFG \ | PORT_WRCONFIG_WRPMUX \ | PORT_WRCONFIG_PMUX(0x3) \ | PORT_WRCONFIG_PMUXEN \ | UART_0_PINS; UART_0_DEV.CTRLA.bit.ENABLE = 0; //Disable to write, need to sync tho while(UART_0_DEV.SYNCBUSY.bit.ENABLE); /* set to LSB, asynchronous mode without parity, PAD0 Tx, PAD1 Rx, * 16x over-sampling, internal clk */ UART_0_DEV.CTRLA.reg = SERCOM_USART_CTRLA_DORD \ | SERCOM_USART_CTRLA_RXPO(0x1) \ | SERCOM_USART_CTRLA_SAMPR(0x1) \ | SERCOM_USART_CTRLA_MODE_USART_INT_CLK; UART_0_DEV.BAUD.FRAC.FP = (baud % 10); UART_0_DEV.BAUD.FRAC.BAUD = (baud / 10); /* enable receiver and transmitter, one stop bit*/ UART_0_DEV.CTRLB.reg = (SERCOM_USART_CTRLB_RXEN | SERCOM_USART_CTRLB_TXEN); while(UART_0_DEV.SYNCBUSY.bit.CTRLB); break; #endif } uart_poweron(uart); return 0; } void uart_tx_begin(uart_t uart) { } void uart_tx_end(uart_t uart) { } int uart_write(uart_t uart, char data) { switch (uart) { case UART_0: UART_0_DEV.DATA.reg = (uint8_t)data; break; } return 1; } int uart_read_blocking(uart_t uart, char *data) { switch (uart) { case UART_0: while (UART_0_DEV.INTFLAG.bit.RXC == 0); *data = (char)(0x00ff & UART_0_DEV.DATA.reg); break; } return 1; } int uart_write_blocking(uart_t uart, char data) { switch (uart) { case UART_0: while (UART_0_DEV.INTFLAG.bit.DRE == 0); UART_0_DEV.DATA.reg = (uint8_t)data; break; } return 1; } void uart_poweron(uart_t uart) { while (UART_0_DEV.SYNCBUSY.reg); UART_0_DEV.CTRLA.reg |= SERCOM_USART_CTRLA_ENABLE; } void uart_poweroff(uart_t uart) { while (UART_0_DEV.SYNCBUSY.reg); UART_0_DEV.CTRLA.reg &= ~SERCOM_USART_CTRLA_ENABLE; } #if UART_0_EN void UART_0_ISR(void) { irq_handler(UART_0, &UART_0_DEV); } #endif static inline void irq_handler(uint8_t uartnum, SercomUsart *dev) { if (dev->INTFLAG.bit.RXC) { /* cleared by reading DATA regiser */ char data = (char)dev->DATA.reg; uart_config[uartnum].rx_cb(uart_config[uartnum].arg, data); } else if (dev->INTFLAG.bit.TXC) { if (uart_config[uartnum].tx_cb(uart_config[uartnum].arg) == 0) { /* TXC flag is also cleared by writing data to DATA register */ if (dev->INTFLAG.bit.TXC) { /* cleared by writing 1 to TXC */ dev->INTFLAG.bit.TXC = 1; } } } else if (dev->INTFLAG.bit.ERROR) { /* clear error flag */ dev->INTFLAG.bit.ERROR = 1; } if (sched_context_switch_request) { thread_yield(); } } #endif /* UART_NUMOF */