From 48ef1cd6c9cda47abb9ae8c1ccd5f24cdc41e313 Mon Sep 17 00:00:00 2001 From: Hauke Petersen Date: Mon, 4 Sep 2017 13:07:38 +0200 Subject: [PATCH 1/2] cpu/sam0_common: added SERCOM clk control functions --- cpu/sam0_common/include/periph_cpu_common.h | 54 +++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/cpu/sam0_common/include/periph_cpu_common.h b/cpu/sam0_common/include/periph_cpu_common.h index 81e4f22a3e..c13cf273b1 100644 --- a/cpu/sam0_common/include/periph_cpu_common.h +++ b/cpu/sam0_common/include/periph_cpu_common.h @@ -217,6 +217,60 @@ static inline int sercom_id(void *sercom) #endif } +/** + * @brief Enable peripheral clock for given SERCOM device + * + * @param[in] sercom SERCOM device + */ +static inline void sercom_clk_en(void *sercom) +{ +#if defined(CPU_FAM_SAMD21) + PM->APBCMASK.reg |= (PM_APBCMASK_SERCOM0 << sercom_id(sercom)); +#elif defined(CPU_FAM_SAML21) + if (sercom_id(sercom) < 5) { + MCLK->APBCMASK.reg |= (MCLK_APBCMASK_SERCOM0 << sercom_id(sercom)); + } else { + MCLK->ABPDMASK.reg |= (MCLK_APBCMASK_SERCOM5); + } +#endif +} + +/** + * @brief Disable peripheral clock for given SERCOM device + * + * @param[in] sercom SERCOM device + */ +static inline void sercom_clk_dis(void *sercom) +{ +#if defined(CPU_FAM_SAMD21) + PM->APBCMASK.reg &= ~(PM_APBCMASK_SERCOM0 << sercom_id(sercom)); +#elif defined(CPU_FAM_SAML21) + if (sercom_id(sercom) < 5) { + MCLK->APBCMASK.reg &= ~(MCLK_APBCMASK_SERCOM0 << sercom_id(sercom)); + } else { + MCLK->ABPDMASK.reg &= ~(MCLK_APBCMASK_SERCOM5); + } +#endif +} + +/** + * @brief Configure generator clock for given SERCOM device + * + * @param[in] sercom SERCOM device + * @param[in] gclk Generator clock + */ +static inline void sercom_set_gen(void *sercom, uint32_t gclk) +{ +#if defined(CPU_FAM_SAMD21) + GCLK->CLKCTRL.reg = (GCLK_CLKCTRL_CLKEN | gclk | + (SERCOM0_GCLK_ID_CORE + sercom_id(sercom))); + while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY) {} +#elif defined(CPU_FAM_SAML21) + GCLK->PCHCTRL[SERCOM0_GCLK_ID_CORE + sercom_id(sercom)].reg = + (GCLK_PCHCTRL_CHEN | gclk); +#endif +} + /** * @brief ADC Channel Configuration */ From 68abdff15a8fdf9a53b6444dab504c596f88eb83 Mon Sep 17 00:00:00 2001 From: Hauke Petersen Date: Fri, 1 Sep 2017 14:51:56 +0200 Subject: [PATCH 2/2] cpu/sam0: optimizations to the shared UART driver --- cpu/sam0_common/include/periph_cpu_common.h | 4 +- cpu/sam0_common/periph/uart.c | 196 ++++---------------- 2 files changed, 42 insertions(+), 158 deletions(-) diff --git a/cpu/sam0_common/include/periph_cpu_common.h b/cpu/sam0_common/include/periph_cpu_common.h index c13cf273b1..a2506d4a40 100644 --- a/cpu/sam0_common/include/periph_cpu_common.h +++ b/cpu/sam0_common/include/periph_cpu_common.h @@ -230,7 +230,7 @@ static inline void sercom_clk_en(void *sercom) if (sercom_id(sercom) < 5) { MCLK->APBCMASK.reg |= (MCLK_APBCMASK_SERCOM0 << sercom_id(sercom)); } else { - MCLK->ABPDMASK.reg |= (MCLK_APBCMASK_SERCOM5); + MCLK->APBDMASK.reg |= (MCLK_APBDMASK_SERCOM5); } #endif } @@ -248,7 +248,7 @@ static inline void sercom_clk_dis(void *sercom) if (sercom_id(sercom) < 5) { MCLK->APBCMASK.reg &= ~(MCLK_APBCMASK_SERCOM0 << sercom_id(sercom)); } else { - MCLK->ABPDMASK.reg &= ~(MCLK_APBCMASK_SERCOM5); + MCLK->APBDMASK.reg &= ~(MCLK_APBDMASK_SERCOM5); } #endif } diff --git a/cpu/sam0_common/periph/uart.c b/cpu/sam0_common/periph/uart.c index 30b6d7d02f..a26c2cb293 100644 --- a/cpu/sam0_common/periph/uart.c +++ b/cpu/sam0_common/periph/uart.c @@ -46,28 +46,11 @@ static uart_isr_ctx_t uart_ctx[UART_NUMOF]; * * @return base register address */ -static inline SercomUsart *_uart(uart_t dev) +static inline SercomUsart *dev(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) { if (uart >= UART_NUMOF) { @@ -78,74 +61,48 @@ int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg) 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_set(uart_config[uart].tx_pin); 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); + /* enable peripheral clock */ + sercom_clk_en(dev(uart)); + /* reset the UART device */ - _uart(uart)->CTRLA.reg = SERCOM_USART_CTRLA_SWRST; - while (_uart(uart)->SYNCBUSY.reg & SERCOM_USART_SYNCBUSY_SWRST) {} + dev(uart)->CTRLA.reg = SERCOM_USART_CTRLA_SWRST; + while (dev(uart)->SYNCBUSY.reg & SERCOM_USART_SYNCBUSY_SWRST) {} + + /* configure clock generator */ + sercom_set_gen(dev(uart), uart_config[uart].gclk_src); + /* 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 | + dev(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 | + SERCOM_USART_CTRLA_MODE(0x1) | (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; + /* 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); - _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 - - /* finally, enable the device */ - _uart(uart)->CTRLA.reg |= SERCOM_USART_CTRLA_ENABLE; - - /* register callbacks */ + /* enable transmitter, and configure 8N1 mode */ + dev(uart)->CTRLB.reg = (SERCOM_USART_CTRLB_TXEN); + /* enable receiver and RX interrupt if configured */ 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; + NVIC_EnableIRQ(SERCOM0_IRQn + sercom_id(dev(uart))); + dev(uart)->CTRLB.reg |= SERCOM_USART_CTRLB_RXEN; + dev(uart)->INTENSET.reg |= SERCOM_USART_INTENSET_RXC; } + while (dev(uart)->SYNCBUSY.reg & SERCOM_USART_SYNCBUSY_CTRLB) {} + + /* and finally enable the device */ + dev(uart)->CTRLA.reg |= SERCOM_USART_CTRLA_ENABLE; return UART_OK; } @@ -153,85 +110,36 @@ int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg) 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) {} + while (!(dev(uart)->INTFLAG.reg & SERCOM_USART_INTFLAG_DRE)) {} + dev(uart)->DATA.reg = data[i]; + while (dev(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; + sercom_clk_en(dev(uart)); + dev(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 + dev(uart)->CTRLA.reg &= ~(SERCOM_USART_CTRLA_ENABLE); + sercom_clk_dis(dev(uart)); } -static inline void irq_handler(uint8_t uartnum) +static inline void irq_handler(unsigned uartnum) { -#ifdef CPU_FAM_SAMD21 - if (_uart(uartnum)->INTFLAG.reg & SERCOM_USART_INTFLAG_RXC) { + if (dev(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)); + (uint8_t)(dev(uartnum)->DATA.reg)); } - else if (_uart(uartnum)->INTFLAG.reg & SERCOM_USART_INTFLAG_ERROR) { + else if (dev(uartnum)->INTFLAG.reg & SERCOM_USART_INTFLAG_ERROR) { /* clear error flag */ - _uart(uartnum)->INTFLAG.reg = SERCOM_USART_INTFLAG_ERROR; + dev(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(); } @@ -277,28 +185,4 @@ void UART_5_ISR(void) } #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 - #endif /* UART_NUMOF */