2017-06-27 21:13:41 +02:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2015 Freie Universität Berlin
|
|
|
|
* 2015 FreshTemp, LLC.
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
2017-08-31 09:47:09 +02:00
|
|
|
* @ingroup cpu_sam0_common
|
|
|
|
* @ingroup drivers_periph_uart
|
2017-06-27 21:13:41 +02:00
|
|
|
* @{
|
|
|
|
*
|
|
|
|
* @file
|
|
|
|
* @brief Low-level UART driver implementation
|
|
|
|
*
|
|
|
|
* @author Thomas Eichinger <thomas.eichinger@fu-berlin.de>
|
|
|
|
* @author Troels Hoffmeyer <troels.d.hoffmeyer@gmail.com>
|
|
|
|
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
|
|
|
* @author Dylan Laduranty <dylanladuranty@gmail.com>
|
|
|
|
*
|
|
|
|
* @}
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "cpu.h"
|
|
|
|
|
|
|
|
#include "periph/uart.h"
|
|
|
|
#include "periph/gpio.h"
|
|
|
|
|
|
|
|
#define ENABLE_DEBUG (0)
|
|
|
|
#include "debug.h"
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Allocate memory to store the callback functions
|
|
|
|
*/
|
|
|
|
static uart_isr_ctx_t uart_ctx[UART_NUMOF];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Get the pointer to the base register of the given UART device
|
|
|
|
*
|
|
|
|
* @param[in] dev UART device identifier
|
|
|
|
*
|
|
|
|
* @return base register address
|
|
|
|
*/
|
2017-09-01 14:51:56 +02:00
|
|
|
static inline SercomUsart *dev(uart_t dev)
|
2017-06-27 21:13:41 +02:00
|
|
|
{
|
|
|
|
return uart_config[dev].dev;
|
|
|
|
}
|
|
|
|
|
|
|
|
int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg)
|
|
|
|
{
|
2017-08-31 09:47:09 +02:00
|
|
|
if (uart >= UART_NUMOF) {
|
2017-06-27 21:13:41 +02:00
|
|
|
return UART_NODEV;
|
|
|
|
}
|
|
|
|
|
2017-10-05 16:23:09 +02:00
|
|
|
/* must disable here first to ensure idempotency */
|
|
|
|
dev(uart)->CTRLA.reg &= ~(SERCOM_USART_CTRLA_ENABLE);
|
|
|
|
|
2017-06-27 21:13:41 +02:00
|
|
|
/* configure pins */
|
2017-10-18 19:34:23 +02:00
|
|
|
if (uart_config[uart].rx_pin != GPIO_UNDEF) {
|
|
|
|
gpio_init(uart_config[uart].rx_pin, GPIO_IN);
|
|
|
|
gpio_init_mux(uart_config[uart].rx_pin, uart_config[uart].mux);
|
|
|
|
}
|
2017-06-27 21:13:41 +02:00
|
|
|
gpio_init(uart_config[uart].tx_pin, GPIO_OUT);
|
2017-09-01 14:51:56 +02:00
|
|
|
gpio_set(uart_config[uart].tx_pin);
|
2017-06-27 21:13:41 +02:00
|
|
|
gpio_init_mux(uart_config[uart].tx_pin, uart_config[uart].mux);
|
|
|
|
|
2017-09-01 14:51:56 +02:00
|
|
|
/* enable peripheral clock */
|
|
|
|
sercom_clk_en(dev(uart));
|
|
|
|
|
2017-06-27 21:13:41 +02:00
|
|
|
/* reset the UART device */
|
2017-09-01 14:51:56 +02:00
|
|
|
dev(uart)->CTRLA.reg = SERCOM_USART_CTRLA_SWRST;
|
2017-09-22 09:53:02 +02:00
|
|
|
while (dev(uart)->SYNCBUSY.bit.SWRST) {}
|
2017-09-01 14:51:56 +02:00
|
|
|
|
|
|
|
/* configure clock generator */
|
|
|
|
sercom_set_gen(dev(uart), uart_config[uart].gclk_src);
|
|
|
|
|
2017-06-27 21:13:41 +02:00
|
|
|
/* set asynchronous mode w/o parity, LSB first, TX and RX pad as specified
|
|
|
|
* by the board in the periph_conf.h, x16 sampling and use internal clock */
|
2017-09-01 14:51:56 +02:00
|
|
|
dev(uart)->CTRLA.reg = (SERCOM_USART_CTRLA_DORD |
|
2017-09-22 09:53:02 +02:00
|
|
|
SERCOM_USART_CTRLA_SAMPR(0x1) |
|
|
|
|
SERCOM_USART_CTRLA_TXPO(uart_config[uart].tx_pad) |
|
|
|
|
SERCOM_USART_CTRLA_RXPO(uart_config[uart].rx_pad) |
|
|
|
|
SERCOM_USART_CTRLA_MODE(0x1));
|
2017-09-12 02:38:19 +02:00
|
|
|
/* Set run in standby mode if enabled */
|
|
|
|
if (uart_config[uart].flags & UART_FLAG_RUN_STANDBY) {
|
|
|
|
dev(uart)->CTRLA.reg |= SERCOM_USART_CTRLA_RUNSTDBY;
|
|
|
|
}
|
2017-06-27 21:13:41 +02:00
|
|
|
|
2017-09-01 14:51:56 +02:00
|
|
|
/* calculate and set baudrate */
|
|
|
|
uint32_t baud = ((((uint32_t)CLOCK_CORECLOCK * 10) / baudrate) / 16);
|
|
|
|
dev(uart)->BAUD.FRAC.FP = (baud % 10);
|
|
|
|
dev(uart)->BAUD.FRAC.BAUD = (baud / 10);
|
2017-08-31 09:47:09 +02:00
|
|
|
|
2017-09-01 14:51:56 +02:00
|
|
|
/* enable transmitter, and configure 8N1 mode */
|
2017-09-22 09:53:02 +02:00
|
|
|
dev(uart)->CTRLB.reg = SERCOM_USART_CTRLB_TXEN;
|
2017-09-01 14:51:56 +02:00
|
|
|
/* enable receiver and RX interrupt if configured */
|
2017-10-18 19:34:23 +02:00
|
|
|
if ((rx_cb) && (uart_config[uart].rx_pin != GPIO_UNDEF)) {
|
2017-08-31 09:47:09 +02:00
|
|
|
uart_ctx[uart].rx_cb = rx_cb;
|
|
|
|
uart_ctx[uart].arg = arg;
|
2017-09-01 14:51:56 +02:00
|
|
|
NVIC_EnableIRQ(SERCOM0_IRQn + sercom_id(dev(uart)));
|
|
|
|
dev(uart)->CTRLB.reg |= SERCOM_USART_CTRLB_RXEN;
|
|
|
|
dev(uart)->INTENSET.reg |= SERCOM_USART_INTENSET_RXC;
|
2017-09-12 02:38:19 +02:00
|
|
|
/* set wakeup receive from sleep if enabled */
|
|
|
|
if (uart_config[uart].flags & UART_FLAG_WAKEUP) {
|
|
|
|
dev(uart)->CTRLB.reg |= SERCOM_USART_CTRLB_SFDE;
|
|
|
|
}
|
2017-08-31 09:47:09 +02:00
|
|
|
}
|
2017-09-22 09:53:02 +02:00
|
|
|
while (dev(uart)->SYNCBUSY.bit.CTRLB) {}
|
2017-09-01 14:51:56 +02:00
|
|
|
|
|
|
|
/* and finally enable the device */
|
|
|
|
dev(uart)->CTRLA.reg |= SERCOM_USART_CTRLA_ENABLE;
|
2017-08-31 09:47:09 +02:00
|
|
|
|
2017-06-27 21:13:41 +02:00
|
|
|
return UART_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
void uart_write(uart_t uart, const uint8_t *data, size_t len)
|
|
|
|
{
|
|
|
|
for (size_t i = 0; i < len; i++) {
|
2017-09-22 09:53:02 +02:00
|
|
|
while (!dev(uart)->INTFLAG.bit.DRE) {}
|
2017-09-01 14:51:56 +02:00
|
|
|
dev(uart)->DATA.reg = data[i];
|
2017-06-27 21:13:41 +02:00
|
|
|
}
|
2017-09-22 09:53:02 +02:00
|
|
|
while (!dev(uart)->INTFLAG.bit.TXC) {}
|
2017-06-27 21:13:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void uart_poweron(uart_t uart)
|
|
|
|
{
|
2017-09-01 14:51:56 +02:00
|
|
|
sercom_clk_en(dev(uart));
|
|
|
|
dev(uart)->CTRLA.reg |= SERCOM_USART_CTRLA_ENABLE;
|
2017-06-27 21:13:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void uart_poweroff(uart_t uart)
|
|
|
|
{
|
2017-09-01 14:51:56 +02:00
|
|
|
dev(uart)->CTRLA.reg &= ~(SERCOM_USART_CTRLA_ENABLE);
|
|
|
|
sercom_clk_dis(dev(uart));
|
2017-06-27 21:13:41 +02:00
|
|
|
}
|
|
|
|
|
2017-09-01 14:51:56 +02:00
|
|
|
static inline void irq_handler(unsigned uartnum)
|
2017-06-27 21:13:41 +02:00
|
|
|
{
|
2017-09-22 09:53:02 +02:00
|
|
|
if (dev(uartnum)->INTFLAG.bit.RXC) {
|
2017-06-27 21:13:41 +02:00
|
|
|
/* interrupt flag is cleared by reading the data register */
|
2017-06-27 21:14:31 +02:00
|
|
|
uart_ctx[uartnum].rx_cb(uart_ctx[uartnum].arg,
|
2017-09-01 14:51:56 +02:00
|
|
|
(uint8_t)(dev(uartnum)->DATA.reg));
|
2017-06-27 21:13:41 +02:00
|
|
|
}
|
2017-09-22 09:53:02 +02:00
|
|
|
else if (dev(uartnum)->INTFLAG.bit.ERROR) {
|
2017-06-27 21:13:41 +02:00
|
|
|
/* clear error flag */
|
2017-09-01 14:51:56 +02:00
|
|
|
dev(uartnum)->INTFLAG.reg = SERCOM_USART_INTFLAG_ERROR;
|
2017-06-27 21:13:41 +02:00
|
|
|
}
|
2017-09-01 14:51:56 +02:00
|
|
|
|
2017-06-27 21:13:41 +02:00
|
|
|
cortexm_isr_end();
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef UART_0_ISR
|
|
|
|
void UART_0_ISR(void)
|
|
|
|
{
|
|
|
|
irq_handler(0);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef UART_1_ISR
|
|
|
|
void UART_1_ISR(void)
|
|
|
|
{
|
|
|
|
irq_handler(1);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef UART_2_ISR
|
|
|
|
void UART_2_ISR(void)
|
|
|
|
{
|
|
|
|
irq_handler(2);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef UART_3_ISR
|
|
|
|
void UART_3_ISR(void)
|
|
|
|
{
|
|
|
|
irq_handler(3);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef UART_4_ISR
|
|
|
|
void UART_4_ISR(void)
|
|
|
|
{
|
|
|
|
irq_handler(4);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef UART_5_ISR
|
|
|
|
void UART_5_ISR(void)
|
|
|
|
{
|
|
|
|
irq_handler(5);
|
|
|
|
}
|
|
|
|
#endif
|