From 675dcc381cfcec125419d8b1df93440314d1261a Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Tue, 18 Jul 2023 15:05:49 +0200 Subject: [PATCH] cpu/msp430: rework MSP430 x1xx periph drivers - Move common code for USART (shared SPI / UART peripheral) to its own file and allow sharing the USART peripheral to provide both UART and SPI in round-robin fashion. - Configure both UART and SPI bus via a `struct` in the board's `periph_conf.h` - this allows allocating the two UARTs as needed by the use case - since both USARTs signals have a fixed connection to a single GPIO, most configuration is moved to the CPU - the board now only needs to decide which bus is provided by which USART Note: Sharing an USART used as UART requires cooperation from the app: - If the UART is used in TX-only mode (no RX callback), the driver will release the USART while not sending - If the UART is used to also receive, the application needs to power the UART down while not expecting something to send. An `spi_acquire()` will be blocked while the UART is powered up. --- boards/msb-430/include/periph_conf.h | 39 +-- boards/msb-430h/include/periph_conf.h | 36 +- .../olimex-msp430-h1611/include/periph_conf.h | 36 +- boards/telosb/include/periph_conf.h | 35 +- cpu/msp430/include/x1xx/periph_cpu.h | 317 +++++++++++++++++- cpu/msp430/periph/Makefile | 1 + cpu/msp430/periph/spi_usart.c | 132 +++++--- cpu/msp430/periph/uart_usart.c | 158 +++++---- cpu/msp430/periph/usart.c | 163 +++++++++ .../Makefile.ci | 1 + tests/sys/suit_manifest/Makefile.ci | 1 + 11 files changed, 714 insertions(+), 205 deletions(-) create mode 100644 cpu/msp430/periph/usart.c diff --git a/boards/msb-430/include/periph_conf.h b/boards/msb-430/include/periph_conf.h index f4c9f03653..40d7839379 100644 --- a/boards/msb-430/include/periph_conf.h +++ b/boards/msb-430/include/periph_conf.h @@ -35,7 +35,7 @@ extern "C" { * @brief Clock configuration */ static const msp430_clock_params_t clock_params = { - .target_dco_frequency = 7372800U, + .target_dco_frequency = MHZ(8), .lfxt1_frequency = 32768, .main_clock_source = MAIN_CLOCK_SOURCE_DCOCLK, .submain_clock_source = SUBMAIN_CLOCK_SOURCE_DCOCLK, @@ -49,35 +49,30 @@ static const msp430_clock_params_t clock_params = { * @name UART configuration * @{ */ -#define UART_NUMOF (1U) +static const uart_conf_t uart_config[] = { + { + .uart = &usart1_as_uart, + }, +}; -#define UART_BASE (&USART_1) -#define UART_SFR (&USART_1_SFR) -#define UART_IE_RX_BIT (1 << 4) -#define UART_IE_TX_BIT (1 << 5) -#define UART_ME_BITS (0x30) -#define UART_PORT (&PORT_3) -#define UART_RX_PIN (1 << 6) -#define UART_TX_PIN (1 << 7) -#define UART_RX_ISR (USART1RX_VECTOR) -#define UART_TX_ISR (USART1TX_VECTOR) +#define UART0_RX_ISR (USART1RX_VECTOR) /**< RX IRQ vector of UART 0 */ +#define UART0_TX_ISR (USART1TX_VECTOR) /**< TX IRQ vector of UART 0 */ + +#define UART_NUMOF ARRAY_SIZE(uart_config) /** @} */ /** * @name SPI configuration * @{ */ -#define SPI_NUMOF (1U) +static const spi_conf_t spi_config[] = { + { + /* beware of resource conflict with UART */ + .spi = &usart1_as_spi, + }, +}; -/* SPI configuration */ -#define SPI_BASE (&USART_1) -#define SPI_SFR (&USART_1_SFR) -#define SPI_IE_RX_BIT (1 << 6) -#define SPI_IE_TX_BIT (1 << 7) -#define SPI_ME_BIT (1 << 6) -#define SPI_PIN_MISO GPIO_PIN(P5, 2) -#define SPI_PIN_MOSI GPIO_PIN(P5, 1) -#define SPI_PIN_CLK GPIO_PIN(P5, 3) +#define SPI_NUMOF ARRAY_SIZE(spi_config) /** @} */ #ifdef __cplusplus diff --git a/boards/msb-430h/include/periph_conf.h b/boards/msb-430h/include/periph_conf.h index d7151bbc3d..8596b681c7 100644 --- a/boards/msb-430h/include/periph_conf.h +++ b/boards/msb-430h/include/periph_conf.h @@ -48,35 +48,29 @@ static const msp430_clock_params_t clock_params = { * @name UART configuration * @{ */ -#define UART_NUMOF (1U) +static const uart_conf_t uart_config[] = { + { + .uart = &usart1_as_uart, + }, +}; -#define UART_BASE (&USART_1) -#define UART_SFR (&USART_1_SFR) -#define UART_IE_RX_BIT (1 << 4) -#define UART_IE_TX_BIT (1 << 5) -#define UART_ME_BITS (0x30) -#define UART_PORT (&PORT_3) -#define UART_RX_PIN (1 << 6) -#define UART_TX_PIN (1 << 7) -#define UART_RX_ISR (USART1RX_VECTOR) -#define UART_TX_ISR (USART1TX_VECTOR) +#define UART0_RX_ISR (USART1RX_VECTOR) /**< RX IRQ vector of UART 0 */ +#define UART0_TX_ISR (USART1TX_VECTOR) /**< TX IRQ vector of UART 0 */ + +#define UART_NUMOF ARRAY_SIZE(uart_config) /** @} */ /** * @name SPI configuration * @{ */ -#define SPI_NUMOF (1U) +static const spi_conf_t spi_config[] = { + { + .spi = &usart0_as_spi, + }, +}; -/* SPI configuration */ -#define SPI_BASE (&USART_0) -#define SPI_SFR (&USART_0_SFR) -#define SPI_IE_RX_BIT (1 << 6) -#define SPI_IE_TX_BIT (1 << 7) -#define SPI_ME_BIT (1 << 6) -#define SPI_PIN_MISO GPIO_PIN(P3, 2) -#define SPI_PIN_MOSI GPIO_PIN(P3, 1) -#define SPI_PIN_CLK GPIO_PIN(P3, 3) +#define SPI_NUMOF ARRAY_SIZE(spi_config) /** @} */ #ifdef __cplusplus diff --git a/boards/olimex-msp430-h1611/include/periph_conf.h b/boards/olimex-msp430-h1611/include/periph_conf.h index 79603ceccc..aaf928c29a 100644 --- a/boards/olimex-msp430-h1611/include/periph_conf.h +++ b/boards/olimex-msp430-h1611/include/periph_conf.h @@ -48,35 +48,29 @@ static const msp430_clock_params_t clock_params = { * @name UART configuration * @{ */ -#define UART_NUMOF (1U) +static const uart_conf_t uart_config[] = { + { + .uart = &usart1_as_uart, + }, +}; -#define UART_BASE (&USART_1) -#define UART_SFR (&USART_1_SFR) -#define UART_IE_RX_BIT (1 << 4) -#define UART_IE_TX_BIT (1 << 5) -#define UART_ME_BITS (0x30) -#define UART_PORT (&PORT_3) -#define UART_RX_PIN (1 << 6) -#define UART_TX_PIN (1 << 7) -#define UART_RX_ISR (USART1RX_VECTOR) -#define UART_TX_ISR (USART1TX_VECTOR) +#define UART0_RX_ISR (USART1RX_VECTOR) /**< RX IRQ vector of UART 0 */ +#define UART0_TX_ISR (USART1TX_VECTOR) /**< TX IRQ vector of UART 0 */ + +#define UART_NUMOF ARRAY_SIZE(uart_config) /** @} */ /** * @name SPI configuration * @{ */ -#define SPI_NUMOF (1U) +static const spi_conf_t spi_config[] = { + { + .spi = &usart0_as_spi, + }, +}; -/* SPI configuration */ -#define SPI_BASE (&USART_0) -#define SPI_SFR (&USART_0_SFR) -#define SPI_IE_RX_BIT (1 << 6) -#define SPI_IE_TX_BIT (1 << 7) -#define SPI_ME_BIT (1 << 6) -#define SPI_PIN_MISO GPIO_PIN(P3, 2) -#define SPI_PIN_MOSI GPIO_PIN(P3, 1) -#define SPI_PIN_CLK GPIO_PIN(P3, 3) +#define SPI_NUMOF ARRAY_SIZE(spi_config) /** @} */ #ifdef __cplusplus diff --git a/boards/telosb/include/periph_conf.h b/boards/telosb/include/periph_conf.h index b5d27ee68f..4a367cd722 100644 --- a/boards/telosb/include/periph_conf.h +++ b/boards/telosb/include/periph_conf.h @@ -48,34 +48,29 @@ static const msp430_clock_params_t clock_params = { * @name UART configuration * @{ */ -#define UART_NUMOF (1U) +static const uart_conf_t uart_config[] = { + { + .uart = &usart1_as_uart, + }, +}; -#define UART_BASE (&USART_1) -#define UART_SFR (&USART_1_SFR) -#define UART_IE_RX_BIT (1 << 4) -#define UART_IE_TX_BIT (1 << 5) -#define UART_ME_BITS (0x30) -#define UART_PORT (&PORT_3) -#define UART_RX_PIN (1 << 6) -#define UART_TX_PIN (1 << 7) -#define UART_RX_ISR (USART1RX_VECTOR) -#define UART_TX_ISR (USART1TX_VECTOR) +#define UART0_RX_ISR (USART1RX_VECTOR) /**< RX IRQ vector of UART 0 */ +#define UART0_TX_ISR (USART1TX_VECTOR) /**< TX IRQ vector of UART 0 */ + +#define UART_NUMOF ARRAY_SIZE(uart_config) /** @} */ /** * @name SPI configuration * @{ */ -#define SPI_NUMOF (1U) +static const spi_conf_t spi_config[] = { + { + .spi = &usart0_as_spi, + }, +}; -#define SPI_BASE (&USART_0) -#define SPI_SFR (&USART_0_SFR) -#define SPI_IE_RX_BIT (1 << 6) -#define SPI_IE_TX_BIT (1 << 7) -#define SPI_ME_BIT (1 << 6) -#define SPI_PIN_MISO GPIO_PIN(P3, 2) -#define SPI_PIN_MOSI GPIO_PIN(P3, 1) -#define SPI_PIN_CLK GPIO_PIN(P3, 3) +#define SPI_NUMOF ARRAY_SIZE(spi_config) /** @} */ #ifdef __cplusplus diff --git a/cpu/msp430/include/x1xx/periph_cpu.h b/cpu/msp430/include/x1xx/periph_cpu.h index 700e20aa38..70cb1f54a6 100644 --- a/cpu/msp430/include/x1xx/periph_cpu.h +++ b/cpu/msp430/include/x1xx/periph_cpu.h @@ -21,12 +21,31 @@ #include +#include "compiler_hints.h" #include "periph_cpu_common.h" +#include "macros/units.h" #ifdef __cplusplus extern "C" { #endif +/** + * @brief All MSP430 x1xx MCUs have two USART peripherals + */ +#define USART_NUMOF 2 + +/** + * @brief The clock divider of the UASRT (BR register) must be at least 2 + * when in SPI mode + */ +#define USART_MIN_BR_SPI 2 + +/** + * @brief The clock divider of the UASRT (BR register) must be at least 3 + * when in UART mode + */ +#define USART_MIN_BR_UART 3 + /** * @name Override SPI mode selection values * @{ @@ -57,14 +76,154 @@ typedef enum { * @brief Support SPI clock frequencies */ typedef enum { - SPI_CLK_100KHZ = 100000, /**< 100 kHz */ - SPI_CLK_400KHZ = 400000, /**< 400 kHz */ - SPI_CLK_1MHZ = 1000000, /**< 1 MHz */ - SPI_CLK_5MHZ = 5000000, /**< 5 MHz */ + SPI_CLK_100KHZ = KHZ(100), /**< 100 kHz */ + SPI_CLK_400KHZ = KHZ(400), /**< 400 kHz */ + SPI_CLK_1MHZ = MHZ(1), /**< 1 MHz */ + SPI_CLK_5MHZ = MHZ(5), /**< 5 MHz */ SPI_CLK_10MHZ = SPI_CLK_5MHZ, /**< 10 MHz not supported, falling back to 5 MHz */ } spi_clk_t; /** @} */ +/** + * @brief MSP430 x1xx USART configuration + * + * @details This is intended to be stored in flash. + */ +typedef struct { + msp430_usart_t *dev; /**< The USART device to use */ + msp430_usart_sfr_t *sfr; /**< The corresponding SFR registers */ + uint8_t tx_irq_mask; /**< The bitmask to enable the TX IRQ for this + USART */ + uint8_t rx_irq_mask; /**< The bitmask to enable the TX IRQ for this + USART */ + uint8_t num; /**< Number of the USART */ +} msp430_usart_params_t; + +/** + * @brief MSP430 x1xx USART clock source + * + * @details The UC + */ +typedef enum { + USART_CLK_UCLKI = UXTCTL_SSEL_UCLKI, /**< UCLKI clock source (not supported yet) */ + USART_CLK_AUX = UXTCTL_SSEL_ACLK, /**< auxiliary clock source */ + USART_CLK_SUBMAIN = UXTCTL_SSEL_SMCLK, /**< sub-system master clock source */ +} msp430_usart_clk_t; + +/** + * @brief MSP430 x1xx USART prescaler configuration + */ +typedef struct { + msp430_usart_clk_t clk_source; /**< Clock source to use */ + uint8_t br0; /**< What to write in the BR0 register */ + uint8_t br1; /**< What to write in the BR1 register */ + uint8_t mctl; /**< USART modulation control register */ +} msp430_usart_prescaler_t; + +/** + * @brief MSP430 x1xx USART configuration registers + * + * @details Unlike @ref msp430_usart_params_t this contains configuration that + * may depends on runtime settings + */ +typedef struct { + msp430_usart_prescaler_t prescaler; /**< Prescaler configuration */ + uint8_t ctl; /**< USART control register */ +} msp430_usart_conf_t; + +/** + * @brief MSP430 x1xx UART configuration, CPU level + * + * The MSP430 x1xx has two USART peripherals which both can be operated in + * UART mode. Each is connected to a fixed GPIO for RXD and TXD, respectively. + * Hence, there is not much left for the board to configure anyway, so we just + * prepare UART configurations at CPU level for the board to refer to. The + * unused configuration(s) will be garbage collected by the linker. + */ +typedef struct { + msp430_usart_params_t usart_params; /**< The USART params */ + /** + * @brief The bitmask to write to the SFR register to enable the USART + * device in UART mode with TX enabled + * + * @note Sadly, the layout of the SFR registers corresponding to the + * USART peripherals differs. + */ + uint8_t tx_enable_mask; + /** + * @brief The bitmask to write to the SFR register to enable the USART + * device in UART mode with RX+TX enabled + * + * @details The RX and TX part of the UART can be enabled individually. + * But since there is no way to enable only RX with RIOT's UART + * API and this is an internal structure anyway, we optimize this + * to avoid an OR function to compile `tx_enable_mask` and + * `rx_enable_mask`. + */ + uint8_t rxtx_enable_mask; + gpio_t rxd; /**< RXD pin */ + gpio_t txd; /**< TXD pin */ +} msp430_usart_uart_params_t; + +/** + * @brief MSP430 x1xx UART configuration, board level + */ +typedef struct { + const msp430_usart_uart_params_t *uart; /**< The UART configuration to use */ +} uart_conf_t; + +/** + * @brief MSP430 x1xx SPI configuration, CPU level + * + * The MSP430 x1xx has two USART peripherals which both can be operated in + * SPI mode. Each is connected to a fixed GPIO for COPI (MOSI), CIPO (MISO), + * and SCK, respectively. Hence, there is not much left for the board to + * configure anyway, so we just prepare UART configurations at CPU level for + * the board to refer to. The unused configuration(s) will be garbage collected + * by the linker. + */ +typedef struct { + msp430_usart_params_t usart_params; /**< The USART parameters */ + /** + * @brief The bitmask to write to the SFR register to enable the USART + * device in SPI mode + * + * @note Sadly, the layout of the SFR registers corresponding to the + * USART peripherals differs. + */ + uint8_t enable_mask; + gpio_t miso; /**< CIPO (MISO) pin */ + gpio_t mosi; /**< COPI (MOSI) pin */ + gpio_t sck; /**< SCK pin */ +} msp430_usart_spi_params_t; + +/** + * @brief MSP430 x1xx SPI configuration, board level + */ +typedef struct { + const msp430_usart_spi_params_t *spi; /**< The SPI configuration to use */ +} spi_conf_t; + +/** + * @brief MSP430 x1xx USART0 in UART configuration + */ +extern const msp430_usart_uart_params_t usart0_as_uart; + +/** + * @brief MSP430 x1xx USART1 in UART configuration + */ +extern const msp430_usart_uart_params_t usart1_as_uart; + +/** + * @brief MSP430 x1xx USART0 in SPI configuration + */ +extern const msp430_usart_spi_params_t usart0_as_spi; + +/** + * @brief MSP430 x1xx USART1 in UART configuration + */ +extern const msp430_usart_spi_params_t usart1_as_spi; + /** * @name declare needed generic SPI functions * @{ @@ -75,6 +234,156 @@ typedef enum { #define PERIPH_SPI_NEEDS_TRANSFER_REGS /**< use shared spi_transfer_regs() */ /** @} */ +/** + * @brief Enable the TX interrupt on the given USART + * @param[in] usart_conf USART config of the USART to enable the TX IRQ on + * + * @warning This will not work while the peripheral is still under reset, + * as the IRQ configuration is constantly reset while the software + * reset bit is set. + */ +static inline void msp430_usart_enable_tx_irq(const msp430_usart_params_t *usart_conf) +{ + usart_conf->sfr->IE |= usart_conf->tx_irq_mask; +} + +/** + * @brief Enable the RX interrupt on the given USART + * @param[in] usart_conf USART config of the USART to enable the RX IRQ on + * + * @warning This will not work while the peripheral is still under reset, + * as the IRQ configuration is constantly reset while the software + * reset bit is set. + */ +static inline void msp430_usart_enable_rx_irq(const msp430_usart_params_t *usart_conf) +{ + usart_conf->sfr->IE |= usart_conf->rx_irq_mask; +} + +/** + * @brief Disable the TX interrupt on the given USART + * @param[in] usart_conf USART config of the USART to disable the TX IRQ on + * + * @warning This will not work while the peripheral is still under reset, + * as the IRQ configuration is constantly reset while the software + * reset bit is set. + */ +static inline void msp430_usart_disable_tx_irq(const msp430_usart_params_t *usart_conf) +{ + usart_conf->sfr->IE &= ~(usart_conf->tx_irq_mask); +} + +/** + * @brief Disable the RX interrupt on the given USART + * @param[in] usart_conf USART config of the USART to disable the RX IRQ on + * + * @warning This will not work while the peripheral is still under reset, + * as the IRQ configuration is constantly reset while the software + * reset bit is set. + */ +static inline void msp430_usart_disable_rx_irq(const msp430_usart_params_t *usart_conf) +{ + usart_conf->sfr->IE &= ~(usart_conf->rx_irq_mask); +} + +/** + * @brief Get the TX IRQ flag of the given USART + * @param[in] usart_conf USART config of the USART to disable the RX IRQ on + * @retval true The interrupt flag is set + * @retval false The interrupt flag is **NOT** set + */ +static inline bool msp430_usart_get_tx_irq_flag(const msp430_usart_params_t *usart_conf) +{ + return usart_conf->sfr->IFG & usart_conf->tx_irq_mask; +} + +/** + * @brief Get the RX IRQ flag of the given USART + * @param[in] usart_conf USART config of the USART to disable the RX IRQ on + * @retval true The interrupt flag is set + * @retval false The interrupt flag is **NOT** set + */ +static inline bool msp430_usart_get_rx_irq_flag(const msp430_usart_params_t *usart_conf) +{ + return usart_conf->sfr->IFG & usart_conf->rx_irq_mask; +} + +/** + * @brief Check if both TX *and* RX IRQ flags are set on the given USART + * @param[in] usart_conf USART config of the USART to disable the RX IRQ on + * @retval true The interrupt flag *both* TX and RX is set + * @retval false Either TX IRQ flag *not* set, or RX *not* set, or *neither* set + */ +static inline bool msp430_usart_are_both_irq_flags_set(const msp430_usart_params_t *usart_conf) +{ + const uint8_t mask = usart_conf->tx_irq_mask | usart_conf->rx_irq_mask; + return (usart_conf->sfr->IFG & mask) == mask; +} + +/** + * @brief Clear the TX IRQ flag of the given USART + * @param[in] usart_conf USART config of the USART to disable the RX IRQ on + */ +static inline void msp430_usart_clear_tx_irq_flag(const msp430_usart_params_t *usart_conf) +{ + usart_conf->sfr->IFG &= ~(usart_conf->tx_irq_mask); +} + +/** + * @brief Clear the RX IRQ flag of the given USART + * @param[in] usart_conf USART config of the USART to disable the RX IRQ on + */ +static inline void msp430_usart_clear_rx_irq_flag(const msp430_usart_params_t *usart_conf) +{ + usart_conf->sfr->IFG &= ~(usart_conf->rx_irq_mask); +} + +/** + * @brief Get exclusive access to an USART peripheral and initialize it + * for operation as configured + * @param[in] params Static USART configuration + * @param[in] conf Dynamic USART configuration + * @param[in] enable_mask Bitmask to write to the SFR register to enable the + * peripheral in the intended mode. + * + * @warning The freshly acquired USART will still be placed in software + * reset when this function returns. The caller is expected to + * finish configuration such as configuring GPIOs connected to + * the USART suitably, clearing error flags, setting up IRQ + * handlers etc. and finally releasing the USART from reset. + */ +void msp430_usart_acquire(const msp430_usart_params_t *params, + const msp430_usart_conf_t *conf, + uint8_t enable_mask); + +/** + * @brief Release exclusive access to an USART peripheral and turn it + * off again + * @param[in] params Configuration specifying the USART to release + */ +void msp430_usart_release(const msp430_usart_params_t *params); + +/** + * @brief Compute a suitable prescaler + * + * @param[in] clock SPI clock in Hz or UART symbol rate in Baud + * @param[in] min_br Smallest BR0/BR1 value supported by hardware + * (@ref USART_MIN_BR_SPI or @ref USART_MIN_BR_UART) + * @return Suitable prescaler config + * @note If the board has an auxiliary clock generated from a + * 32,678 Hz watch crystal with no clock divider, the auxiliary + * clock is used for symbol rates of 9,600 Bd, 4,800 Bd, 2,400 Bd, + * and 1,200 Bd. For every other symbol rate the subsystem main + * clock is used instead. + * @warning This will compute the prescaler generating the frequency + * *closest* to @p clock. It may generate a frequency higher than + * requested, if this is closer to the target frequency than the + * next lower frequency. This makes bit-timings better in UART + * @warning The mctl `struct` field in the result may be non-zero. Callers + * must clear this, if the intend to use the USART in modes + * other than UART. + */ +msp430_usart_prescaler_t msp430_usart_prescale(uint32_t clock, uint16_t min_br); #ifdef __cplusplus } #endif diff --git a/cpu/msp430/periph/Makefile b/cpu/msp430/periph/Makefile index 4c9c3578a7..9c0cfe1b20 100644 --- a/cpu/msp430/periph/Makefile +++ b/cpu/msp430/periph/Makefile @@ -6,6 +6,7 @@ MODULE = periph # family it is the (incompatible) USCI. ifeq (msp430_x1xx,$(CPU_FAM)) SERIAL_IP_BLOCK := usart + SRC += $(SERIAL_IP_BLOCK).c endif ifeq (msp430_f2xx_g2xx,$(CPU_FAM)) SERIAL_IP_BLOCK := usci diff --git a/cpu/msp430/periph/spi_usart.c b/cpu/msp430/periph/spi_usart.c index c3b7170fe5..ef9cb33f00 100644 --- a/cpu/msp430/periph/spi_usart.c +++ b/cpu/msp430/periph/spi_usart.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2015-2016 Freie Universität Berlin + * 2023 Otto-von-Guericke-Universität Magdeburg * * 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 @@ -19,32 +20,20 @@ * devices - one used as UART and one as SPI. * * @author Hauke Petersen + * @author Marian Buschsieweke * * @} */ #include -#include "cpu.h" -#include "mutex.h" +#include "compiler_hints.h" #include "periph/spi.h" - -/** - * @brief Mutex for locking the SPI device - */ -static mutex_t spi_lock = MUTEX_INIT; +#include "periph_cpu.h" void spi_init(spi_t bus) { - assert((unsigned)bus < SPI_NUMOF); - - /* put SPI device in reset state */ - SPI_BASE->CTL = SWRST; - SPI_BASE->CTL |= (CHAR | SYNC | MM); - SPI_BASE->RCTL = 0; - SPI_BASE->MCTL = 0; - /* enable SPI mode */ - SPI_SFR->ME |= SPI_ME_BIT; + assume((unsigned)bus < SPI_NUMOF); /* trigger the pin configuration */ spi_init_pins(bus); @@ -52,53 +41,78 @@ void spi_init(spi_t bus) void spi_init_pins(spi_t bus) { - (void)bus; + assume((unsigned)bus < SPI_NUMOF); - gpio_periph_mode(SPI_PIN_MISO, true); - gpio_periph_mode(SPI_PIN_MOSI, true); - gpio_periph_mode(SPI_PIN_CLK, true); + const msp430_usart_spi_params_t *params = spi_config[bus].spi; + + /* set output GPIOs to idle levels of the peripheral */ + gpio_set(params->mosi); + gpio_clear(params->sck); + + /* configure the pins as GPIOs, not attaching to the peripheral as of now */ + gpio_init(params->miso, GPIO_IN); + gpio_init(params->mosi, GPIO_OUT); + gpio_init(params->sck, GPIO_OUT); } void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) { - (void)bus; + assume((unsigned)bus < SPI_NUMOF); (void)cs; - assert((unsigned)bus < SPI_NUMOF); - assert(clk != SPI_CLK_10MHZ); - /* lock the bus */ - mutex_lock(&spi_lock); + const msp430_usart_spi_params_t *params = spi_config[bus].spi; + msp430_usart_t *dev = params->usart_params.dev; - /* calculate baudrate */ - uint32_t br = msp430_submain_clock_freq() / clk; - /* make sure the is not smaller then 2 */ - if (br < 2) { - br = 2; - } - SPI_BASE->BR0 = (uint8_t)br; - SPI_BASE->BR1 = (uint8_t)(br >> 8); + msp430_usart_conf_t conf = { + .prescaler = msp430_usart_prescale(clk, USART_MIN_BR_SPI), + .ctl = CHAR | SYNC | MM, + }; + /* get exclusive access to the USART (this will also indirectly ensure + * exclusive SPI bus access */ + msp430_usart_acquire(¶ms->usart_params, &conf, params->enable_mask); + + /* clock and phase are encoded in mode so that they can be directly be + * written into TCTL. TCTL has been initialized by + * msp430_usart_acquire(), so we don't need to wipe any previous clock + * phase or polarity state. + * + * STC disables "multi-master" mode, in which the STE pin would be connected + * to the CS output of any other SPI controller */ + dev->TCTL |= STC | mode; - /* configure bus mode */ - /* configure mode */ - SPI_BASE->TCTL = (UXTCTL_SSEL_SMCLK | STC | mode); /* release from software reset */ - SPI_BASE->CTL &= ~(SWRST); + dev->CTL &= ~(SWRST); + + /* attach the pins only now after the peripheral is up and running, as + * otherwise noise is send out (could be observed on SCK with a logic + * analyzer). */ + gpio_periph_mode(params->miso, true); + gpio_periph_mode(params->mosi, true); + gpio_periph_mode(params->sck, true); } void spi_release(spi_t bus) { - (void)bus; - /* put SPI device back in reset state */ - SPI_BASE->CTL |= SWRST; + assume((unsigned)bus < SPI_NUMOF); + const msp430_usart_spi_params_t *params = spi_config[bus].spi; - /* release the bus */ - mutex_unlock(&spi_lock); + /* release the pins to avoid sending noise while the peripheral is + * reconfigured or used to provide other interfaces */ + gpio_periph_mode(params->miso, false); + gpio_periph_mode(params->mosi, false); + gpio_periph_mode(params->sck, false); + + /* release the peripheral */ + msp430_usart_release(¶ms->usart_params); } void spi_transfer_bytes(spi_t bus, spi_cs_t cs, bool cont, const void *out, void *in, size_t len) { - (void)bus; + assume((unsigned)bus < SPI_NUMOF); + const msp430_usart_spi_params_t *params = spi_config[bus].spi; + const msp430_usart_params_t *usart = ¶ms->usart_params; + msp430_usart_t *dev = params->usart_params.dev; const uint8_t *out_buf = out; uint8_t *in_buf = in; @@ -112,26 +126,36 @@ void spi_transfer_bytes(spi_t bus, spi_cs_t cs, bool cont, /* if we only send out data, we do this the fast way... */ if (!in_buf) { for (size_t i = 0; i < len; i++) { - while (!(SPI_SFR->IFG & SPI_IE_TX_BIT)) {} - SPI_BASE->TXBUF = out_buf[i]; + while (!msp430_usart_get_tx_irq_flag(usart)) { + /* still busy waiting for TX to complete */ + } + dev->TXBUF = out_buf[i]; } /* finally we need to wait, until all transfers are complete */ - while (!(SPI_SFR->IFG & SPI_IE_TX_BIT) || !(SPI_SFR->IFG & SPI_IE_RX_BIT)) {} - SPI_BASE->RXBUF; + while (!msp430_usart_are_both_irq_flags_set(usart)) { + /* still either TX, or RX, or both not completed */ + } + (void)dev->RXBUF; } else if (!out_buf) { for (size_t i = 0; i < len; i++) { - SPI_BASE->TXBUF = 0; - while (!(SPI_SFR->IFG & SPI_IE_RX_BIT)) {} - in_buf[i] = (char)SPI_BASE->RXBUF; + dev->TXBUF = 0; + while (!msp430_usart_get_rx_irq_flag(usart)) { + /* still busy waiting for RX to complete */ + } + in_buf[i] = dev->RXBUF; } } else { for (size_t i = 0; i < len; i++) { - while (!(SPI_SFR->IFG & SPI_IE_TX_BIT)) {} - SPI_BASE->TXBUF = out_buf[i]; - while (!(SPI_SFR->IFG & SPI_IE_RX_BIT)) {} - in_buf[i] = (char)SPI_BASE->RXBUF; + while (!msp430_usart_get_tx_irq_flag(usart)) { + /* still busy waiting for TX to complete */ + } + dev->TXBUF = out_buf[i]; + while (!msp430_usart_get_rx_irq_flag(usart)) { + /* still busy waiting for RX to complete */ + } + in_buf[i] = dev->RXBUF; } } diff --git a/cpu/msp430/periph/uart_usart.c b/cpu/msp430/periph/uart_usart.c index 7ece90a1be..cbd6f3b351 100644 --- a/cpu/msp430/periph/uart_usart.c +++ b/cpu/msp430/periph/uart_usart.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2015 Freie Universität Berlin + * 2023 Otto-von-Guericke-Universität Magdeburg * * 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 @@ -15,6 +16,7 @@ * @brief Low-level UART driver implementation * * @author Hauke Petersen + * @author Marian Buschsieweke * * @} */ @@ -23,105 +25,135 @@ #include "periph_cpu.h" #include "periph_conf.h" #include "periph/uart.h" +#include "periph/gpio.h" +#include "compiler_hints.h" -/** - * @brief Keep track of the interrupt context - * @{ - */ -static uart_rx_cb_t ctx_rx_cb; -static void *ctx_isr_arg; -/** @} */ +static uart_isr_ctx_t ctx[UART_NUMOF]; +static msp430_usart_conf_t confs[UART_NUMOF]; -static int init_base(uart_t uart, uint32_t baudrate); - -int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg) +static void init(uart_t uart) { - int res = init_base(uart, baudrate); - if (res != UART_OK) { - return res; + const msp430_usart_uart_params_t *params = uart_config[uart].uart; + msp430_usart_t *dev = params->usart_params.dev; + uint8_t enable_mask = params->rxtx_enable_mask; + + /* most of the time UART is used to both send and receive */ + if (unlikely(!ctx[uart].rx_cb)) { + enable_mask = params->tx_enable_mask; } - /* save interrupt context */ - ctx_rx_cb = rx_cb; - ctx_isr_arg = arg; - /* reset interrupt flags and enable RX interrupt */ - UART_SFR->IE &= ~(UART_IE_TX_BIT); - UART_SFR->IFG &= ~(UART_IE_RX_BIT); - UART_SFR->IFG |= (UART_IE_TX_BIT); - UART_SFR->IE |= (UART_IE_RX_BIT); - return UART_OK; + /* acquire USART and put in in UART mode */ + msp430_usart_acquire(¶ms->usart_params, &confs[uart], enable_mask); + + /* configure pins */ + gpio_set(params->txd); + gpio_init(params->txd, GPIO_OUT); + gpio_periph_mode(params->txd, true); + + gpio_init(params->rxd, GPIO_IN); + gpio_periph_mode(params->rxd, true); + + /* now that everything is configured, release the software reset bit */ + dev->CTL &= ~(SWRST); + + /* finally, enable the RX IRQ (won't work prior to releasing the software + * reset bit, as this is cleared under reset) */ + if (likely(ctx[uart].rx_cb)) { + msp430_usart_enable_rx_irq(¶ms->usart_params); + } } -static int init_base(uart_t uart, uint32_t baudrate) +int uart_init(uart_t uart, uint32_t symbolrate, uart_rx_cb_t rx_cb, void *arg) { - if (uart != 0) { - return UART_NODEV; + assume((unsigned)uart < UART_NUMOF); + + /* save interrupt context */ + ctx[uart].rx_cb = rx_cb; + ctx[uart].arg = arg; + + /* prepare and save UART config */ + confs[uart].ctl = CHAR; + confs[uart].prescaler = msp430_usart_prescale(symbolrate, USART_MIN_BR_UART); + + + if (rx_cb) { + init(uart); } - /* get the default UART for now -> TODO: enable for multiple devices */ - msp430_usart_t *dev = UART_BASE; - - /* power off and reset device */ - uart_poweroff(uart); - dev->CTL = SWRST; - /* configure to 8N1 and using the SMCLK*/ - dev->CTL |= CHAR; - dev->TCTL = (TXEPT | UXTCTL_SSEL_SMCLK); - dev->RCTL = 0x00; - /* baudrate configuration */ - uint16_t br = (uint16_t)(msp430_submain_clock_freq() / baudrate); - dev->BR0 = (uint8_t)br; - dev->BR1 = (uint8_t)(br >> 8); - /* TODO: calculate value for modulation register */ - dev->MCTL = 0; - /* configure pins -> TODO: move into GPIO driver (once implemented) */ - UART_PORT->SEL |= (UART_RX_PIN | UART_TX_PIN); - msp430_port_t *port = &UART_PORT->base; - port->OD |= UART_RX_PIN; - port->OD &= ~(UART_TX_PIN); - port->DIR |= UART_TX_PIN; - port->DIR &= ~(UART_RX_PIN); - /* enable receiver and transmitter */ - uart_poweron(uart); - /* and finally release the software reset bit */ - dev->CTL &= ~(SWRST); return UART_OK; } void uart_write(uart_t uart, const uint8_t *data, size_t len) { - (void)uart; - msp430_usart_t *dev = UART_BASE; + assume((unsigned)uart < UART_NUMOF); + const msp430_usart_uart_params_t *params = uart_config[uart].uart; + msp430_usart_t *dev = params->usart_params.dev; + + /* If UART is in TX-only, the USART is released when done sending. + * This is not only helpful for power saving, but also to share the USART. + */ + if (!unlikely(ctx[uart].rx_cb)) { + init(uart); + } for (size_t i = 0; i < len; i++) { while (!(dev->TCTL & TXEPT)) {} dev->TXBUF = data[i]; } + + if (!unlikely(ctx[uart].rx_cb)) { + msp430_usart_release(¶ms->usart_params); + } } void uart_poweron(uart_t uart) { - (void)uart; - UART_SFR->ME |= UART_ME_BITS; + assume((unsigned)uart < UART_NUMOF); + /* In TX-only mode, the USART is only powered on when transmitting */ + if (likely(ctx[uart].rx_cb)) { + init(uart); + } } void uart_poweroff(uart_t uart) { - (void)uart; - UART_SFR->ME &= ~(UART_ME_BITS); + assume((unsigned)uart < UART_NUMOF); + const msp430_usart_uart_params_t *params = uart_config[uart].uart; + /* In TX-only mode, the USART is only powered on when transmitting */ + if (likely(ctx[uart].rx_cb)) { + msp430_usart_release(¶ms->usart_params); + } } -ISR(UART_RX_ISR, isr_uart_0_rx) +static void uart_rx_isr(uart_t uart) { - __enter_isr(); + assume((unsigned)uart < UART_NUMOF); + const msp430_usart_uart_params_t *params = uart_config[uart].uart; + msp430_usart_t *dev = params->usart_params.dev; /* read character (resets interrupt flag) */ - char c = UART_BASE->RXBUF; + char c = dev->RXBUF; /* only call callback if there was no receive error */ - if(! (UART_BASE->RCTL & RXERR)) { - ctx_rx_cb(ctx_isr_arg, c); + if (!(dev->RCTL & RXERR)) { + ctx[uart].rx_cb(ctx[uart].arg, c); } +} +#ifdef UART0_RX_ISR +ISR(UART0_RX_ISR, isr_uart_0_rx) +{ + __enter_isr(); + uart_rx_isr(UART_DEV(0)); __exit_isr(); } +#endif + +#ifdef UART1_RX_ISR +ISR(UART1_RX_ISR, isr_uart_0_rx) +{ + __enter_isr(); + uart_rx_isr(UART_DEV(1)); + __exit_isr(); +} +#endif diff --git a/cpu/msp430/periph/usart.c b/cpu/msp430/periph/usart.c new file mode 100644 index 0000000000..27585cd269 --- /dev/null +++ b/cpu/msp430/periph/usart.c @@ -0,0 +1,163 @@ +#include "macros/math.h" +#include "mutex.h" +#include "periph_conf.h" +#include "periph_cpu.h" + +const msp430_usart_uart_params_t usart0_as_uart = { + .usart_params = { + .num = 0, + .dev = &USART_0, + .sfr = &USART_0_SFR, + .tx_irq_mask = UTXE0, + .rx_irq_mask = URXE0, + }, + .tx_enable_mask = UTXE0, + .rxtx_enable_mask = URXE0 | UTXE0, + .txd = GPIO_PIN(P3, 4), + .rxd = GPIO_PIN(P3, 5), +}; + +const msp430_usart_uart_params_t usart1_as_uart = { + .usart_params = { + .num = 1, + .dev = &USART_1, + .sfr = &USART_1_SFR, + .tx_irq_mask = UTXE1, + .rx_irq_mask = URXE1, + }, + .tx_enable_mask = UTXE1, + .rxtx_enable_mask = URXE1 | UTXE1, + .txd = GPIO_PIN(P3, 6), + .rxd = GPIO_PIN(P3, 7), +}; + +const msp430_usart_spi_params_t usart0_as_spi = { + .usart_params = { + .num = 0, + .dev = &USART_0, + .sfr = &USART_0_SFR, + .tx_irq_mask = UTXE0, + .rx_irq_mask = URXE0, + }, + .enable_mask = USPIE0, + .mosi = GPIO_PIN(P3, 1), + .miso = GPIO_PIN(P3, 2), + .sck = GPIO_PIN(P3, 3), +}; + +const msp430_usart_spi_params_t usart1_as_spi = { + .usart_params = { + .num = 1, + .dev = &USART_1, + .sfr = &USART_1_SFR, + .tx_irq_mask = UTXE1, + .rx_irq_mask = URXE1, + }, + .enable_mask = USPIE1, + .mosi = GPIO_PIN(P5, 1), + .miso = GPIO_PIN(P5, 2), + .sck = GPIO_PIN(P5, 3), +}; + +static mutex_t usart_locks[USART_NUMOF] = { + MUTEX_INIT, + MUTEX_INIT, +}; + +void msp430_usart_acquire(const msp430_usart_params_t *params, + const msp430_usart_conf_t *conf, + uint8_t enable_mask) +{ + assume(params->num < USART_NUMOF); + + mutex_lock(&usart_locks[params->num]); + msp430_usart_t *dev = params->dev; + msp430_usart_sfr_t *sfr = params->sfr; + + /* first, make sure USART is off before reconfiguring it */ + sfr->ME = 0; + /* reset USART */ + dev->CTL = SWRST; + + /* apply given configuration */ + dev->CTL = conf->ctl | SWRST; + dev->MCTL = conf->prescaler.mctl; + dev->TCTL = conf->prescaler.clk_source; + dev->BR0 = conf->prescaler.br0; + dev->BR1 = conf->prescaler.br1; + + /* disable USART IRQs and clear any spurious IRQ flags */ + sfr->IE = 0; + sfr->IFG = 0; + /* enable USART as specified */ + sfr->ME = enable_mask; +} + +void msp430_usart_release(const msp430_usart_params_t *params) +{ + assume(params->num < USART_NUMOF); + + msp430_usart_sfr_t *sfr = params->sfr; + + /* Disable USART */ + sfr->ME = 0; + /* disable USART IRQs and clear any spurious IRQ flags */ + sfr->IE = 0; + sfr->IFG = 0; + + /* Release mutex */ + mutex_unlock(&usart_locks[params->num]); +} + +msp430_usart_prescaler_t msp430_usart_prescale(uint32_t clock, uint16_t min_br) +{ + msp430_usart_prescaler_t result = { .mctl = 0 }; + uint32_t clk_hz; + + /* If a watch crystal is used for the auxiliary clock, allow using the + * auxiliary clock to be used as clock source for well-known + * symbol rates, so that enabling low power modes is possible while + * UART RX is active */ + if ((clock_params.lfxt1_frequency == 32768) + && (clock_params.auxiliary_clock_divier == AUXILIARY_CLOCK_DIVIDE_BY_1)) { + clk_hz = msp430_auxiliary_clock_freq(); + assume(clk_hz == 32768); + result.clk_source = USART_CLK_AUX; + /* Rather than calculating the correct modulation control register + * values, just hard-code it for four well-known symbol rates. If the + * symbol rate is something else, we go for the high frequency + * subsystem main clock, where bit timings are easier to hit even + * without fine-tuning it via the modulation control register */ + switch (clock) { + case 9600: + result.mctl = 0x4a; + break; + case 4800: + result.mctl = 0x6f; + break; + case 2400: + result.mctl = 0x6b; + break; + case 1200: + result.mctl = 0x03; + break; + default: + clk_hz = msp430_submain_clock_freq(); + result.clk_source = USART_CLK_SUBMAIN; + } + } + else { + clk_hz = msp430_submain_clock_freq(); + result.clk_source = USART_CLK_SUBMAIN; + } + + uint16_t br = DIV_ROUND(clk_hz, clock); + + if (br < min_br) { + br = min_br; + } + + result.br0 = (uint8_t)br; + result.br1 = (uint8_t)(br >> 8); + return result; +} diff --git a/tests/net/gnrc_netif_ipv6_wait_for_global_address/Makefile.ci b/tests/net/gnrc_netif_ipv6_wait_for_global_address/Makefile.ci index 99e4a29d44..eab8f4ca33 100644 --- a/tests/net/gnrc_netif_ipv6_wait_for_global_address/Makefile.ci +++ b/tests/net/gnrc_netif_ipv6_wait_for_global_address/Makefile.ci @@ -19,6 +19,7 @@ BOARD_INSUFFICIENT_MEMORY := \ nucleo-l011k4 \ nucleo-l031k6 \ nucleo-l053r8 \ + olimex-msp430-h1611 \ samd10-xmini \ slstk3400a \ stk3200 \ diff --git a/tests/sys/suit_manifest/Makefile.ci b/tests/sys/suit_manifest/Makefile.ci index abfced5570..e1c052f549 100644 --- a/tests/sys/suit_manifest/Makefile.ci +++ b/tests/sys/suit_manifest/Makefile.ci @@ -10,6 +10,7 @@ BOARD_INSUFFICIENT_MEMORY := \ nucleo-l011k4 \ nucleo-l031k6 \ nucleo-l053r8 \ + olimex-msp430-h1611 \ samd10-xmini \ slstk3400a \ stk3200 \