1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-01-18 12:52:44 +01:00
RIOT/cpu/sam0_common/periph/uart.c

305 lines
8.7 KiB
C
Raw Normal View History

/*
* 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
* @{
*
* @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"
2017-08-31 09:47:09 +02:00
/* do not build the file in case no UART is defined */
#ifdef UART_NUMOF
/**
* @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
*/
static inline SercomUsart *_uart(uart_t dev)
{
return uart_config[dev].dev;
}
#ifdef CPU_FAM_SAML21
static uint64_t _long_division(uint64_t n, uint64_t d);
static uint8_t sercom_gclk_id[] =
{
SERCOM0_GCLK_ID_CORE,
SERCOM1_GCLK_ID_CORE,
SERCOM2_GCLK_ID_CORE,
SERCOM3_GCLK_ID_CORE,
SERCOM4_GCLK_ID_CORE,
SERCOM5_GCLK_ID_CORE
};
#endif
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) {
return UART_NODEV;
}
/* configure pins */
gpio_init(uart_config[uart].rx_pin, GPIO_IN);
gpio_init_mux(uart_config[uart].rx_pin, uart_config[uart].mux);
gpio_init(uart_config[uart].tx_pin, GPIO_OUT);
gpio_init_mux(uart_config[uart].tx_pin, uart_config[uart].mux);
#ifdef CPU_FAM_SAMD21
/* calculate baudrate */
uint32_t baud = ((((uint32_t)CLOCK_CORECLOCK * 10) / baudrate) / 16);
/* enable sync and async clocks */
uart_poweron(uart);
/* reset the UART device */
_uart(uart)->CTRLA.reg = SERCOM_USART_CTRLA_SWRST;
while (_uart(uart)->SYNCBUSY.reg & SERCOM_USART_SYNCBUSY_SWRST) {}
/* 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 */
_uart(uart)->CTRLA.reg = (SERCOM_USART_CTRLA_DORD |
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_USART_INT_CLK |
(uart_config[uart].runstdby ?
SERCOM_USART_CTRLA_RUNSTDBY : 0));
/* set baudrate */
_uart(uart)->BAUD.FRAC.FP = (baud % 10);
_uart(uart)->BAUD.FRAC.BAUD = (baud / 10);
/* enable receiver and transmitter, use 1 stop bit */
_uart(uart)->CTRLB.reg = (SERCOM_USART_CTRLB_RXEN | SERCOM_USART_CTRLB_TXEN);
while (_uart(uart)->SYNCBUSY.reg & SERCOM_USART_SYNCBUSY_CTRLB) {}
#elif CPU_FAM_SAML21
/* Calculate the BAUD value */
uint64_t temp1 = ((16 * ((uint64_t)baudrate)) << 32);
uint64_t ratio = _long_division(temp1 , CLOCK_CORECLOCK);
uint64_t scale = ((uint64_t)1 << 32) - ratio;
uint64_t baud_calculated = (65536 * scale) >> 32;
_uart(uart)->CTRLA.bit.ENABLE = 0; /* Disable to write, need to sync tho */
while(_uart(uart)->SYNCBUSY.bit.ENABLE) {}
/* set to LSB, asynchronous mode without parity, PAD0 Tx, PAD1 Rx,
* 16x over-sampling, internal clk */
_uart(uart)->CTRLA.reg = SERCOM_USART_CTRLA_DORD \
| SERCOM_USART_CTRLA_FORM(0x0) \
| SERCOM_USART_CTRLA_SAMPA(0x0) \
| SERCOM_USART_CTRLA_TXPO(uart_config[uart].tx_pad) \
| SERCOM_USART_CTRLA_RXPO(uart_config[uart].rx_pad) \
| SERCOM_USART_CTRLA_SAMPR(0x0) \
| SERCOM_USART_CTRLA_MODE(0x1) \
| (uart_config[uart].runstdby ?
SERCOM_USART_CTRLA_RUNSTDBY : 0);
/* Set baud rate */
_uart(uart)->BAUD.bit.BAUD = baud_calculated;
/* enable receiver and transmitter, one stop bit*/
_uart(uart)->CTRLB.reg = (SERCOM_USART_CTRLB_RXEN | SERCOM_USART_CTRLB_TXEN);
while(_uart(uart)->SYNCBUSY.bit.CTRLB) {}
uart_poweron(uart);
#endif
2017-08-31 09:47:09 +02:00
/* finally, enable the device */
_uart(uart)->CTRLA.reg |= SERCOM_USART_CTRLA_ENABLE;
/* register callbacks */
if (rx_cb) {
uart_ctx[uart].rx_cb = rx_cb;
uart_ctx[uart].arg = arg;
/* configure interrupts and enable RX interrupt */
NVIC_EnableIRQ(SERCOM0_IRQn + sercom_id(_uart(uart)));
_uart(uart)->INTENSET.reg |= SERCOM_USART_INTENSET_RXC;
}
return UART_OK;
}
void uart_write(uart_t uart, const uint8_t *data, size_t len)
{
for (size_t i = 0; i < len; i++) {
while (!(_uart(uart)->INTFLAG.reg & SERCOM_USART_INTFLAG_DRE)) {}
_uart(uart)->DATA.reg = data[i];
while (_uart(uart)->INTFLAG.reg & SERCOM_USART_INTFLAG_TXC) {}
}
}
void uart_poweron(uart_t uart)
{
#ifdef CPU_FAM_SAMD21
PM->APBCMASK.reg |= (PM_APBCMASK_SERCOM0 << sercom_id(_uart(uart)));
GCLK->CLKCTRL.reg = (GCLK_CLKCTRL_CLKEN |
GCLK_CLKCTRL_GEN(uart_config[uart].gclk_src) |
(SERCOM0_GCLK_ID_CORE + sercom_id(_uart(uart))) <<
GCLK_CLKCTRL_ID_Pos);
while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY) {}
#elif CPU_FAM_SAML21
/* Enable the peripheral channel */
GCLK->PCHCTRL[sercom_gclk_id[sercom_id(_uart(uart))]].reg |=
GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_GEN(uart_config[uart].gclk_src);
while (!(GCLK->PCHCTRL[sercom_gclk_id[sercom_id(_uart(uart))]].reg &
GCLK_PCHCTRL_CHEN)) {}
if(sercom_gclk_id[sercom_id(_uart(uart))] < 5) {
MCLK->APBCMASK.reg |= MCLK_APBCMASK_SERCOM0 << sercom_id(_uart(uart));
}
else {
MCLK->APBDMASK.reg |= MCLK_APBDMASK_SERCOM5;
}
while (_uart(uart)->SYNCBUSY.reg) {}
#endif
/* finally, enable the device */
_uart(uart)->CTRLA.reg |= SERCOM_USART_CTRLA_ENABLE;
}
void uart_poweroff(uart_t uart)
{
#ifdef CPU_FAM_SAMD21
PM->APBCMASK.reg &= ~(PM_APBCMASK_SERCOM0 << sercom_id(_uart(uart)));
GCLK->CLKCTRL.reg = ((SERCOM0_GCLK_ID_CORE + sercom_id(_uart(uart))) <<
GCLK_CLKCTRL_ID_Pos);
while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY) {}
#elif CPU_FAM_SAML21
/* Enable the peripheral channel */
GCLK->PCHCTRL[sercom_gclk_id[sercom_id(_uart(uart))]].reg &= ~GCLK_PCHCTRL_CHEN;
if(sercom_gclk_id[sercom_id(_uart(uart))] < 5) {
MCLK->APBCMASK.reg &= ~(MCLK_APBCMASK_SERCOM0 << sercom_id(_uart(uart)));
}
else {
MCLK->APBDMASK.reg &= ~MCLK_APBDMASK_SERCOM5;
}
while (_uart(uart)->SYNCBUSY.reg) {}
#endif
}
static inline void irq_handler(uint8_t uartnum)
{
#ifdef CPU_FAM_SAMD21
if (_uart(uartnum)->INTFLAG.reg & SERCOM_USART_INTFLAG_RXC) {
/* interrupt flag is cleared by reading the data register */
uart_ctx[uartnum].rx_cb(uart_ctx[uartnum].arg,
(uint8_t)(_uart(uartnum)->DATA.reg));
}
else if (_uart(uartnum)->INTFLAG.reg & SERCOM_USART_INTFLAG_ERROR) {
/* clear error flag */
_uart(uartnum)->INTFLAG.reg = SERCOM_USART_INTFLAG_ERROR;
}
#elif CPU_FAM_SAML21
if (_uart(uartnum)->INTFLAG.bit.RXC) {
/* cleared by reading DATA regiser */
uint8_t data = (uint8_t)_uart(uartnum)->DATA.reg;
uart_ctx[uartnum].rx_cb(uart_ctx[uartnum].arg, data);
}
else if (_uart(uartnum)->INTFLAG.bit.ERROR) {
/* clear error flag */
_uart(uartnum)->INTFLAG.reg |= SERCOM_USART_INTFLAG_ERROR;
}
#endif
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
#ifdef CPU_FAM_SAML21
static uint64_t _long_division(uint64_t n, uint64_t d)
{
int32_t i;
uint64_t q = 0, r = 0, bit_shift;
for (i = 63; i >= 0; i--) {
bit_shift = (uint64_t)1 << i;
r = r << 1;
if (n & bit_shift) {
r |= 0x01;
}
if (r >= d) {
r = r - d;
q |= bit_shift;
}
}
return q;
}
#endif
2017-08-31 09:47:09 +02:00
#endif /* UART_NUMOF */