mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-12-25 14:33:52 +01:00
cpu/msp430: add periph_gpio_ll
This commit is contained in:
parent
5db461c28b
commit
0ce7b8dfde
@ -5,5 +5,10 @@ ifneq (,$(filter newlib,$(USEMODULE)))
|
||||
DEFAULT_MODULE += newlib_nano
|
||||
endif
|
||||
|
||||
ifneq (,$(filter periph_gpio,$(USEMODULE)))
|
||||
# the legacy periph_gpio driver uses gpio_port from periph_gpio_ll
|
||||
FEATURES_REQUIRED += periph_gpio_ll
|
||||
endif
|
||||
|
||||
# Make calls to malloc and friends thread-safe
|
||||
USEMODULE += malloc_thread_safe
|
||||
|
||||
@ -3,6 +3,8 @@ CPU_CORE = msp430
|
||||
|
||||
ifneq (,$(filter msp430f2% msp430g2%,$(CPU_MODEL)))
|
||||
CPU_FAM := msp430_f2xx_g2xx
|
||||
FEATURES_PROVIDED += periph_gpio_ll_input_pull_down
|
||||
FEATURES_PROVIDED += periph_gpio_ll_input_pull_up
|
||||
FEATURES_PROVIDED += periph_spi_reconfigure
|
||||
endif
|
||||
|
||||
@ -20,3 +22,6 @@ FEATURES_PROVIDED += periph_flashpage_in_address_space
|
||||
FEATURES_PROVIDED += periph_flashpage_pagewise
|
||||
FEATURES_PROVIDED += periph_pm
|
||||
FEATURES_PROVIDED += periph_timer_query_freqs
|
||||
|
||||
FEATURES_PROVIDED += periph_gpio_ll
|
||||
FEATURES_PROVIDED += periph_gpio_ll_switch_dir
|
||||
|
||||
@ -57,6 +57,31 @@ extern "C" {
|
||||
#define MSP430_USCI_B_FROM_USCI_A(usci_a) \
|
||||
((msp430_usci_b_t *)((uintptr_t)(usci_a) + MSP430_USCI_A_B_OFFSET))
|
||||
|
||||
/**
|
||||
* @brief GPIO Port 1/2 (with interrupt functionality)
|
||||
*/
|
||||
typedef struct {
|
||||
msp430_port_t base; /**< common GPIO port registers */
|
||||
REG8 IFG; /**< interrupt flag */
|
||||
REG8 IES; /**< interrupt edge select */
|
||||
REG8 IE; /**< interrupt enable */
|
||||
REG8 SEL; /**< alternative function select */
|
||||
REG8 REN; /**< pull resistor enable */
|
||||
} msp430_port_p1_p2_t;
|
||||
|
||||
/**
|
||||
* @brief GPIO Port 7/8 (different register layout than Ports 1-6)
|
||||
*/
|
||||
typedef struct {
|
||||
REG8 IN; /**< input data */
|
||||
uint8_t _padding1; /**< unrelated I/O */
|
||||
REG8 OD; /**< output data */
|
||||
uint8_t _padding2; /**< unrelated I/O */
|
||||
REG8 DIR; /**< pin direction */
|
||||
uint8_t _padding3; /**< unrelated I/O */
|
||||
REG8 SEL; /**< alternative function select */
|
||||
} msp430_port_p7_p8_t;
|
||||
|
||||
/**
|
||||
* @brief Universal Serial Control Interface Type A (USCI_A) Registers
|
||||
*/
|
||||
|
||||
@ -183,6 +183,16 @@ typedef struct {
|
||||
const msp430_usci_spi_params_t *spi; /**< The SPI configuration to use */
|
||||
} spi_conf_t;
|
||||
|
||||
/**
|
||||
* @brief Register map of GPIO PORT 7
|
||||
*/
|
||||
extern msp430_port_p7_p8_t PORT_7;
|
||||
|
||||
/**
|
||||
* @brief Register map of GPIO PORT 8
|
||||
*/
|
||||
extern msp430_port_p7_p8_t PORT_8;
|
||||
|
||||
/**
|
||||
* @brief Acquire and initialize USCI for use a SPI/UART peripheral
|
||||
*
|
||||
|
||||
208
cpu/msp430/include/gpio_ll_arch.h
Normal file
208
cpu/msp430/include/gpio_ll_arch.h
Normal file
@ -0,0 +1,208 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Marian Buschsieweke
|
||||
*
|
||||
* 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
|
||||
* @ingroup drivers_periph_gpio_ll
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief CPU specific part of the Peripheral GPIO Low-Level API
|
||||
*
|
||||
* @author Marian Buschsieweke <marian.buschsieweke@posteo.net>
|
||||
*/
|
||||
|
||||
#ifndef GPIO_LL_ARCH_H
|
||||
#define GPIO_LL_ARCH_H
|
||||
|
||||
#include "cpu.h"
|
||||
#include "periph_cpu.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef DOXYGEN /* hide implementation specific details from Doxygen */
|
||||
|
||||
/* the memory layout of all GPIO peripherals is compatible, but the location
|
||||
* in the address space is pretty much random */
|
||||
|
||||
#define GPIO_PORT_1 ((gpio_port_t)&PORT_1.base)
|
||||
#define GPIO_PORT_2 ((gpio_port_t)&PORT_2.base)
|
||||
#define GPIO_PORT_3 ((gpio_port_t)&PORT_3.base)
|
||||
#define GPIO_PORT_4 ((gpio_port_t)&PORT_4.base)
|
||||
#define GPIO_PORT_5 ((gpio_port_t)&PORT_5.base)
|
||||
#define GPIO_PORT_6 ((gpio_port_t)&PORT_6.base)
|
||||
/* Port 7 and 8 have different memory layout and are only available on F2xx/G2xx
|
||||
* MCUs */
|
||||
#if defined(CPU_FAM_MSP430_F2XX_G2XX)
|
||||
# define GPIO_PORT_7 ((gpio_port_t)&PORT_7)
|
||||
# define GPIO_PORT_8 ((gpio_port_t)&PORT_8)
|
||||
#endif
|
||||
|
||||
/* IMPORTANT IMPLEMENTATION INFO
|
||||
* =============================
|
||||
*
|
||||
* - MSP430 F2xx/G2xx do have PORT 7 and PORT 8, but those have an incompatible
|
||||
* memory layout compared to the other ports. Hence, they need extra handling.
|
||||
* However, constant folding should get ride of the branch and overhead if the
|
||||
* GPIO port is a compile time constant
|
||||
* - MSP430 has bit manipulation instructions that work on memory. E.g.
|
||||
* `BIC.B %[mask], @%[ptr]` will implement `*ptr &= ~(mask)` in a single
|
||||
* instruction. Same for setting or XORing bits. Hence, the code below
|
||||
* may often look like it is missing `irq_disable()` ... `irq_restore()`, but
|
||||
* in fact will be atomic due to the MSP430 instruction set.
|
||||
*/
|
||||
|
||||
gpio_port_t gpio_port(uword_t num);
|
||||
|
||||
static inline uword_t gpio_ll_read(gpio_port_t port)
|
||||
{
|
||||
#if defined(CPU_FAM_MSP430_F2XX_G2XX)
|
||||
if (port >= (uintptr_t)(&PORT_7)) {
|
||||
const msp430_port_p7_p8_t *p = (void *)port;
|
||||
return p->IN;
|
||||
}
|
||||
#endif
|
||||
const msp430_port_t *p = (void *)port;
|
||||
return p->IN;
|
||||
}
|
||||
|
||||
static inline uword_t gpio_ll_read_output(gpio_port_t port)
|
||||
{
|
||||
#if defined(CPU_FAM_MSP430_F2XX_G2XX)
|
||||
if (port >= (uintptr_t)(&PORT_7)) {
|
||||
const msp430_port_p7_p8_t *p = (void *)port;
|
||||
return p->OD;
|
||||
}
|
||||
#endif
|
||||
const msp430_port_t *p = (void *)port;
|
||||
return p->OD;
|
||||
}
|
||||
|
||||
static inline void gpio_ll_set(gpio_port_t port, uword_t mask)
|
||||
{
|
||||
#if defined(CPU_FAM_MSP430_F2XX_G2XX)
|
||||
if (port >= (uintptr_t)(&PORT_7)) {
|
||||
msp430_port_p7_p8_t *p = (void *)port;
|
||||
p->OD |= mask;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
msp430_port_t *p = (void *)port;
|
||||
p->OD |= mask;
|
||||
}
|
||||
|
||||
static inline void gpio_ll_clear(gpio_port_t port, uword_t mask)
|
||||
{
|
||||
#if defined(CPU_FAM_MSP430_F2XX_G2XX)
|
||||
if (port >= (uintptr_t)(&PORT_7)) {
|
||||
msp430_port_p7_p8_t *p = (void *)port;
|
||||
p->OD &= ~(mask);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
msp430_port_t *p = (void *)port;
|
||||
p->OD &= ~(mask);
|
||||
}
|
||||
|
||||
static inline void gpio_ll_toggle(gpio_port_t port, uword_t mask)
|
||||
{
|
||||
#if defined(CPU_FAM_MSP430_F2XX_G2XX)
|
||||
if (port >= (uintptr_t)(&PORT_7)) {
|
||||
msp430_port_p7_p8_t *p = (void *)port;
|
||||
p->OD ^= mask;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
msp430_port_t *p = (void *)port;
|
||||
p->OD ^= mask;
|
||||
}
|
||||
|
||||
static inline void gpio_ll_write(gpio_port_t port, uword_t value)
|
||||
{
|
||||
#if defined(CPU_FAM_MSP430_F2XX_G2XX)
|
||||
if (port >= (uintptr_t)(&PORT_7)) {
|
||||
msp430_port_p7_p8_t *p = (void *)port;
|
||||
p->OD = value;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
msp430_port_t *p = (void *)port;
|
||||
p->OD = value;
|
||||
}
|
||||
|
||||
static inline gpio_port_t gpio_get_port(gpio_t pin)
|
||||
{
|
||||
return gpio_port(gpio_get_pin_num(pin));
|
||||
}
|
||||
|
||||
static inline uint8_t gpio_get_pin_num(gpio_t pin)
|
||||
{
|
||||
return pin >> 8;
|
||||
}
|
||||
|
||||
static inline void gpio_ll_switch_dir_output(gpio_port_t port, uword_t outputs)
|
||||
{
|
||||
#if defined(CPU_FAM_MSP430_F2XX_G2XX)
|
||||
if (port >= (uintptr_t)(&PORT_7)) {
|
||||
msp430_port_p7_p8_t *p = (void *)port;
|
||||
p->DIR |= outputs;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
msp430_port_t *p = (void *)port;
|
||||
p->DIR |= outputs;
|
||||
}
|
||||
|
||||
static inline void gpio_ll_switch_dir_input(gpio_port_t port, uword_t inputs)
|
||||
{
|
||||
#if defined(CPU_FAM_MSP430_F2XX_G2XX)
|
||||
if (port >= (uintptr_t)(&PORT_7)) {
|
||||
msp430_port_p7_p8_t *p = (void *)port;
|
||||
p->DIR &= ~(inputs);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
msp430_port_t *p = (void *)port;
|
||||
p->DIR &= ~(inputs);
|
||||
}
|
||||
|
||||
static inline gpio_port_t gpio_port_pack_addr(void *addr)
|
||||
{
|
||||
return (gpio_port_t)addr;
|
||||
}
|
||||
|
||||
static inline void * gpio_port_unpack_addr(gpio_port_t port)
|
||||
{
|
||||
if (port < RAMSTART) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (void *)port;
|
||||
}
|
||||
|
||||
static inline bool is_gpio_port_num_valid(uint_fast8_t num)
|
||||
{
|
||||
#if defined(CPU_FAM_MSP430_F2XX_G2XX)
|
||||
return (num > 0) && (num <= 8);
|
||||
#else
|
||||
return (num > 0) && (num <= 6);
|
||||
#endif
|
||||
}
|
||||
|
||||
uword_t gpio_port_num(gpio_port_t port);
|
||||
|
||||
#endif /* DOXYGEN */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GPIO_LL_ARCH_H */
|
||||
/** @} */
|
||||
@ -92,17 +92,6 @@ typedef struct {
|
||||
REG8 DIR; /**< pin direction */
|
||||
} msp430_port_t;
|
||||
|
||||
/**
|
||||
* @brief GPIO Port 1/2 (with interrupt functionality)
|
||||
*/
|
||||
typedef struct {
|
||||
msp430_port_t base; /**< common GPIO port registers */
|
||||
REG8 IFG; /**< interrupt flag */
|
||||
REG8 IES; /**< interrupt edge select */
|
||||
REG8 IE; /**< interrupt enable */
|
||||
REG8 SEL; /**< alternative function select */
|
||||
} msp430_port_p1_p2_t;
|
||||
|
||||
/**
|
||||
* @brief GPIO Port 3..6 (without interrupt functionality)
|
||||
*/
|
||||
@ -124,67 +113,6 @@ typedef struct {
|
||||
REG16 CCR[7]; /**< capture compare channel values */
|
||||
} msp430_timer_t;
|
||||
|
||||
/**
|
||||
* @name MSP430 Common Peripheral Register Maps
|
||||
*
|
||||
* @details The addresses will be provided by the linker script using the
|
||||
* vendor files.
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @brief Register map of GPIO PORT 1
|
||||
*/
|
||||
extern msp430_port_p1_p2_t PORT_1;
|
||||
/**
|
||||
* @brief Register map of GPIO PORT 2
|
||||
*/
|
||||
extern msp430_port_p1_p2_t PORT_2;
|
||||
/**
|
||||
* @brief Register map of GPIO PORT 3
|
||||
*/
|
||||
extern msp430_port_p3_p6_t PORT_3;
|
||||
/**
|
||||
* @brief Register map of GPIO PORT 4
|
||||
*/
|
||||
extern msp430_port_p3_p6_t PORT_4;
|
||||
/**
|
||||
* @brief Register map of GPIO PORT 5
|
||||
*/
|
||||
extern msp430_port_p3_p6_t PORT_5;
|
||||
/**
|
||||
* @brief Register map of GPIO PORT 6
|
||||
*/
|
||||
extern msp430_port_p3_p6_t PORT_6;
|
||||
|
||||
/**
|
||||
* @brief Register map of the timer A control registers
|
||||
*/
|
||||
extern msp430_timer_t TIMER_A;
|
||||
|
||||
/**
|
||||
* @brief IRQ flags for TIMER_A
|
||||
*
|
||||
* Called TAIV in the data sheet / vendor files. This shallow alias
|
||||
* makes the name more readable and does impedance matching for the type
|
||||
* (`volatile uint16_t` vs `volatile short`).
|
||||
*/
|
||||
extern REG16 TIMER_A_IRQFLAGS;
|
||||
|
||||
/**
|
||||
* @brief IRQ flags for TIMER_B
|
||||
*
|
||||
* Called TBIV in the data sheet / vendor files. This shallow alias
|
||||
* makes the name more readable and does impedance matching for the type
|
||||
* (`volatile uint16_t` vs `volatile short`).
|
||||
*/
|
||||
extern REG16 TIMER_B_IRQFLAGS;
|
||||
|
||||
/**
|
||||
* @brief Register map of the timer B control registers
|
||||
*/
|
||||
extern msp430_timer_t TIMER_B;
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -64,6 +64,11 @@ typedef uint16_t gpio_t;
|
||||
*/
|
||||
#define TIMER_CHANNEL_NUMOF 7
|
||||
|
||||
/**
|
||||
* @brief Lowest address of the RAM, peripherals are below
|
||||
*/
|
||||
#define RAMSTART 0x200
|
||||
|
||||
/**
|
||||
* @name Override flank selection values
|
||||
* @{
|
||||
@ -91,6 +96,42 @@ enum {
|
||||
P6 = 6, /**< PORT 6 */
|
||||
};
|
||||
|
||||
#ifndef DOXYGEN
|
||||
#define HAVE_GPIO_STATE_T
|
||||
typedef enum {
|
||||
GPIO_INPUT,
|
||||
GPIO_OUTPUT_PUSH_PULL,
|
||||
GPIO_OUTPUT_OPEN_DRAIN, /**< not supported */
|
||||
GPIO_OUTPUT_OPEN_SOURCE, /**< not supported */
|
||||
GPIO_USED_BY_PERIPHERAL, /**< not supported */
|
||||
GPIO_DISCONNECT = GPIO_INPUT,
|
||||
} gpio_state_t;
|
||||
|
||||
#define HAVE_GPIO_SLEW_T
|
||||
typedef enum {
|
||||
GPIO_SLEW_SLOWEST = 0,
|
||||
GPIO_SLEW_SLOW = 0,
|
||||
GPIO_SLEW_FAST = 0,
|
||||
GPIO_SLEW_FASTEST = 0,
|
||||
} gpio_slew_t;
|
||||
|
||||
#define HAVE_GPIO_PULL_STRENGTH_T
|
||||
typedef enum {
|
||||
GPIO_PULL_WEAKEST = 0,
|
||||
GPIO_PULL_WEAK = 0,
|
||||
GPIO_PULL_STRONG = 0,
|
||||
GPIO_PULL_STRONGEST = 0
|
||||
} gpio_pull_strength_t;
|
||||
|
||||
#define HAVE_GPIO_DRIVE_STRENGTH_T
|
||||
typedef enum {
|
||||
GPIO_DRIVE_WEAKEST = 0,
|
||||
GPIO_DRIVE_WEAK = 0,
|
||||
GPIO_DRIVE_STRONG = 0,
|
||||
GPIO_DRIVE_STRONGEST = 0
|
||||
} gpio_drive_strength_t;
|
||||
#endif /* !DOXYGEN */
|
||||
|
||||
/**
|
||||
* @brief Enable or disable a pin to be used by peripheral modules
|
||||
*
|
||||
@ -337,6 +378,68 @@ typedef struct {
|
||||
msp430_timer_clock_source_t clock_source; /**< Clock source to use */
|
||||
} timer_conf_t;
|
||||
|
||||
/**
|
||||
* @name MSP430 Common Peripheral Register Maps
|
||||
*
|
||||
* @details The addresses will be provided by the linker script using the
|
||||
* vendor files.
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @brief Register map of GPIO PORT 1
|
||||
*/
|
||||
extern msp430_port_p1_p2_t PORT_1;
|
||||
/**
|
||||
* @brief Register map of GPIO PORT 2
|
||||
*/
|
||||
extern msp430_port_p1_p2_t PORT_2;
|
||||
/**
|
||||
* @brief Register map of GPIO PORT 3
|
||||
*/
|
||||
extern msp430_port_p3_p6_t PORT_3;
|
||||
/**
|
||||
* @brief Register map of GPIO PORT 4
|
||||
*/
|
||||
extern msp430_port_p3_p6_t PORT_4;
|
||||
/**
|
||||
* @brief Register map of GPIO PORT 5
|
||||
*/
|
||||
extern msp430_port_p3_p6_t PORT_5;
|
||||
/**
|
||||
* @brief Register map of GPIO PORT 6
|
||||
*/
|
||||
extern msp430_port_p3_p6_t PORT_6;
|
||||
|
||||
/**
|
||||
* @brief Register map of the timer A control registers
|
||||
*/
|
||||
extern msp430_timer_t TIMER_A;
|
||||
|
||||
/**
|
||||
* @brief IRQ flags for TIMER_A
|
||||
*
|
||||
* Called TAIV in the data sheet / vendor files. This shallow alias
|
||||
* makes the name more readable and does impedance matching for the type
|
||||
* (`volatile uint16_t` vs `volatile short`).
|
||||
*/
|
||||
extern REG16 TIMER_A_IRQFLAGS;
|
||||
|
||||
/**
|
||||
* @brief IRQ flags for TIMER_B
|
||||
*
|
||||
* Called TBIV in the data sheet / vendor files. This shallow alias
|
||||
* makes the name more readable and does impedance matching for the type
|
||||
* (`volatile uint16_t` vs `volatile short`).
|
||||
*/
|
||||
extern REG16 TIMER_B_IRQFLAGS;
|
||||
|
||||
/**
|
||||
* @brief Register map of the timer B control registers
|
||||
*/
|
||||
extern msp430_timer_t TIMER_B;
|
||||
/** @} */
|
||||
|
||||
|
||||
/**
|
||||
* @brief Initialize the basic clock system to provide the main clock,
|
||||
* the subsystem clock, and the auxiliary clock.
|
||||
|
||||
@ -31,6 +31,17 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPIO Port 1/2 (with interrupt functionality)
|
||||
*/
|
||||
typedef struct {
|
||||
msp430_port_t base; /**< common GPIO port registers */
|
||||
REG8 IFG; /**< interrupt flag */
|
||||
REG8 IES; /**< interrupt edge select */
|
||||
REG8 IE; /**< interrupt enable */
|
||||
REG8 SEL; /**< alternative function select */
|
||||
} msp430_port_p1_p2_t;
|
||||
|
||||
/**
|
||||
* @brief USART (UART, SPI and I2C) Registers
|
||||
*/
|
||||
|
||||
@ -4,3 +4,5 @@ PROVIDE(USCI_A0 = UCA0ABCTL);
|
||||
PROVIDE(USCI_A1 = UCA1ABCTL);
|
||||
PROVIDE(USCI_B0 = UCB0CTL0);
|
||||
PROVIDE(USCI_B1 = UCB1CTL0);
|
||||
PROVIDE(PORT_7 = P7IN);
|
||||
PROVIDE(PORT_8 = P8IN);
|
||||
|
||||
@ -23,6 +23,7 @@
|
||||
#include "container.h"
|
||||
#include "cpu.h"
|
||||
#include "periph/gpio.h"
|
||||
#include "periph/gpio_ll.h"
|
||||
|
||||
/**
|
||||
* @brief Number of possible interrupt lines: 2 ports * 8 pins
|
||||
@ -36,22 +37,7 @@
|
||||
|
||||
static msp430_port_t *_port(gpio_t pin)
|
||||
{
|
||||
switch (pin >> 8) {
|
||||
case 1:
|
||||
return &PORT_1.base;
|
||||
case 2:
|
||||
return &PORT_2.base;
|
||||
case 3:
|
||||
return &PORT_3.base;
|
||||
case 4:
|
||||
return &PORT_4.base;
|
||||
case 5:
|
||||
return &PORT_5.base;
|
||||
case 6:
|
||||
return &PORT_6.base;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
return (msp430_port_t *)gpio_port(pin >> 8);
|
||||
}
|
||||
|
||||
static inline uint8_t _pin(gpio_t pin)
|
||||
|
||||
198
cpu/msp430/periph/gpio_ll.c
Normal file
198
cpu/msp430/periph/gpio_ll.c
Normal file
@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Marian Buschsieweke
|
||||
*
|
||||
* 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
|
||||
* @ingroup drivers_periph_gpio_ll
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Peripheral GPIO Low-Level API implementation for the MSP430 GPIO peripheral
|
||||
*
|
||||
* @author Marian Buschsieweke <marian.buschsieweke@posteo.net>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "periph/gpio_ll.h"
|
||||
|
||||
gpio_port_t gpio_port(uword_t num)
|
||||
{
|
||||
switch (num) {
|
||||
case 1:
|
||||
return (gpio_port_t)&PORT_1.base;
|
||||
case 2:
|
||||
return (gpio_port_t)&PORT_2.base;
|
||||
case 3:
|
||||
return (gpio_port_t)&PORT_3.base;
|
||||
case 4:
|
||||
return (gpio_port_t)&PORT_4.base;
|
||||
case 5:
|
||||
return (gpio_port_t)&PORT_5.base;
|
||||
case 6:
|
||||
return (gpio_port_t)&PORT_6.base;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uword_t gpio_port_num(gpio_port_t port)
|
||||
{
|
||||
/* we need to hard-code the values here to have constant initializers in
|
||||
* the look-up table :-/ */
|
||||
static const uint8_t map[] = {
|
||||
0x20, /* == GPIO_PORT_1 */
|
||||
0x28, /* == GPIO_PORT_2 */
|
||||
0x10, /* == GPIO_PORT_3 */
|
||||
0x1C, /* == GPIO_PORT_4 */
|
||||
0x30, /* == GPIO_PORT_5 */
|
||||
0x34, /* == GPIO_PORT_6 */
|
||||
#if CPU_FAM_MSP430_F2XX_G2XX
|
||||
0x38, /* == GPIO_PORT_7 */
|
||||
0x39, /* == GPIO_PORT_8 */
|
||||
#endif
|
||||
};
|
||||
|
||||
for (unsigned i = 0; i < ARRAY_SIZE(map); i++) {
|
||||
if (port == (gpio_port_t)map[i]) {
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if CPU_FAM_MSP430_F2XX_G2XX
|
||||
static void _set_pull(unsigned port_num, uword_t mask, bool enable)
|
||||
{
|
||||
volatile uint8_t *ren_reg;
|
||||
switch (port_num) {
|
||||
case 1:
|
||||
ren_reg = &PORT_1.REN;
|
||||
break;
|
||||
case 2:
|
||||
ren_reg = &PORT_2.REN;
|
||||
break;
|
||||
default:
|
||||
ren_reg = &P3REN;
|
||||
ren_reg += (port_num - 3);
|
||||
}
|
||||
|
||||
if (enable) {
|
||||
*ren_reg |= mask;
|
||||
}
|
||||
else {
|
||||
*ren_reg &= ~(mask);
|
||||
}
|
||||
}
|
||||
|
||||
static bool _get_pull(unsigned port_num, uword_t mask)
|
||||
{
|
||||
volatile uint8_t *ren_reg;
|
||||
switch (port_num) {
|
||||
case 1:
|
||||
ren_reg = &PORT_1.REN;
|
||||
break;
|
||||
case 2:
|
||||
ren_reg = &PORT_2.REN;
|
||||
break;
|
||||
default:
|
||||
ren_reg = &P3REN;
|
||||
ren_reg += (port_num - 3);
|
||||
}
|
||||
|
||||
return*ren_reg & mask;
|
||||
}
|
||||
#endif
|
||||
|
||||
int gpio_ll_init(gpio_port_t port, uint8_t pin, gpio_conf_t conf)
|
||||
{
|
||||
msp430_port_t *p = (void *)port;
|
||||
|
||||
if (!IS_ACTIVE(CPU_FAM_MSP430_F2XX_G2XX) && (conf.pull != GPIO_FLOATING)) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
switch (conf.state) {
|
||||
case GPIO_INPUT:
|
||||
case GPIO_OUTPUT_PUSH_PULL:
|
||||
break;
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
uword_t mask = 1U << pin;
|
||||
bool initial_value = conf.initial_value;
|
||||
|
||||
#if CPU_FAM_MSP430_F2XX_G2XX
|
||||
unsigned port_num = gpio_port_num(port);
|
||||
switch (conf.pull) {
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
case GPIO_FLOATING:
|
||||
_set_pull(port_num, mask, false);
|
||||
break;
|
||||
case GPIO_PULL_UP:
|
||||
_set_pull(port_num, mask, true);
|
||||
initial_value = true;
|
||||
break;
|
||||
case GPIO_PULL_DOWN:
|
||||
_set_pull(port_num, mask, true);
|
||||
initial_value = false;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (initial_value) {
|
||||
gpio_ll_set(port, mask);
|
||||
}
|
||||
else {
|
||||
gpio_ll_clear(port, mask);
|
||||
}
|
||||
|
||||
if (conf.state == GPIO_OUTPUT_PUSH_PULL) {
|
||||
/* No need to disable IRQs, the BIS.B instruction is atomically */
|
||||
p->DIR |= mask;
|
||||
}
|
||||
else {
|
||||
/* No need to disable IRQs, the BIC.B instruction is atomically */
|
||||
p->DIR &= ~mask;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
gpio_conf_t gpio_ll_query_conf(gpio_port_t port, uint8_t pin)
|
||||
{
|
||||
msp430_port_t *p = (void *)port;
|
||||
uword_t mask = 1U << pin;
|
||||
gpio_conf_t result = { 0 };
|
||||
if (p->DIR & mask) {
|
||||
result.state = GPIO_OUTPUT_PUSH_PULL;
|
||||
result.initial_value = gpio_ll_read_output(port) & mask;
|
||||
}
|
||||
else {
|
||||
result.state = GPIO_INPUT;
|
||||
result.initial_value = gpio_ll_read(port) & mask;
|
||||
#if CPU_FAM_MSP430_F2XX_G2XX
|
||||
uword_t port_num = gpio_port_num(port);
|
||||
if (_get_pull(port_num, mask)) {
|
||||
if (gpio_ll_read_output(port) & mask) {
|
||||
result.pull = GPIO_PULL_UP;
|
||||
}
|
||||
else {
|
||||
result.pull = GPIO_PULL_DOWN;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user