1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

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.
This commit is contained in:
Marian Buschsieweke 2023-07-18 15:05:49 +02:00
parent bb07bb6613
commit 675dcc381c
No known key found for this signature in database
GPG Key ID: 77AA882EC78084E6
11 changed files with 714 additions and 205 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -21,12 +21,31 @@
#include <stdbool.h>
#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

View File

@ -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

View File

@ -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 <hauke.petersen@fu-berlin.de>
* @author Marian Buschsieweke <marian.buschsieweke@posteo.net>
*
* @}
*/
#include <assert.h>
#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(&params->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(&params->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 = &params->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;
}
}

View File

@ -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 <hauke.petersen@fu-berlin.de>
* @author Marian Buschsieweke <marian.buschsieweke@posteo.net>
*
* @}
*/
@ -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(&params->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(&params->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(&params->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(&params->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

163
cpu/msp430/periph/usart.c Normal file
View File

@ -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;
}

View File

@ -19,6 +19,7 @@ BOARD_INSUFFICIENT_MEMORY := \
nucleo-l011k4 \
nucleo-l031k6 \
nucleo-l053r8 \
olimex-msp430-h1611 \
samd10-xmini \
slstk3400a \
stk3200 \

View File

@ -10,6 +10,7 @@ BOARD_INSUFFICIENT_MEMORY := \
nucleo-l011k4 \
nucleo-l031k6 \
nucleo-l053r8 \
olimex-msp430-h1611 \
samd10-xmini \
slstk3400a \
stk3200 \