1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-01-18 12:52:44 +01:00
RIOT/cpu/msp430/include/x1xx/periph_cpu.h
2024-02-07 20:07:53 +01:00

393 lines
14 KiB
C

/*
* Copyright (C) 2015 Freie Universität Berlin
*
* 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.
*/
/**
* @ingroup cpu_msp430_x1xx
* @{
*
* @file
* @brief CPU specific definitions for internal peripheral handling
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*/
#ifndef X1XX_PERIPH_CPU_H
#define X1XX_PERIPH_CPU_H
#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
* @{
*/
#define HAVE_SPI_MODE_T /**< MSP430 x1xx has a custom spi_mode_t */
/**
* @brief Support SPI modes
*
* | Field | Name | Description |
* |:------ |:--------------------- |:----------------------------------------------------- |
* | CKPH | Clock phase select | 0 = capture on second edge, 1 = capture on first |
* | CKPL | Clock polarity select | 0 = clock is idle-low, 1 = clock is high idle-high |
*/
typedef enum {
SPI_MODE_0 = (CKPH), /**< CPOL=0, CPHA=0 */
SPI_MODE_1 = 0, /**< CPOL=0, CPHA=1 */
SPI_MODE_2 = (CKPL | CKPH), /**< CPOL=1, CPHA=0 */
SPI_MODE_3 = (CKPL) /**< CPOL=1, CPHA=1 */
} spi_mode_t;
/** @} */
/**
* @name Override SPI clock speed selection values
* @{
*/
#define HAVE_SPI_CLK_T /**< MSP430 x1xx has a custom spi_clock_t */
/**
* @brief Support SPI clock frequencies
*/
typedef enum {
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 SPI configuration
*/
extern const msp430_usart_spi_params_t usart1_as_spi;
/**
* @name declare needed generic SPI functions
* @{
*/
#define PERIPH_SPI_NEEDS_INIT_CS /**< use shared spi_init_cs() */
#define PERIPH_SPI_NEEDS_TRANSFER_BYTE /**< use shared spi_transfer_byte() */
#define PERIPH_SPI_NEEDS_TRANSFER_REG /**< use shared spi_transfer_reg() */
#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
#endif /* X1XX_PERIPH_CPU_H */
/** @} */