/* * 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_msp430fxyz * @{ * * @file * @brief Low-level GPIO driver implementation * * @author Hauke Petersen * * @} */ #include "cpu.h" #include "bitarithm.h" #include "periph/gpio.h" /** * @brief Number of possible interrupt lines: 2 ports * 8 pins */ #define ISR_NUMOF (16U) /** * @brief Number of pins on each port */ #define PINS_PER_PORT (8U) /** * @brief Interrupt context for each interrupt line */ static gpio_isr_ctx_t isr_ctx[ISR_NUMOF]; static msp_port_t *_port(gpio_t pin) { switch (pin >> 8) { case 1: return PORT_1; case 2: return PORT_2; case 3: return PORT_3; case 4: return PORT_4; case 5: return PORT_5; case 6: return PORT_6; default: return NULL; } } static inline msp_port_isr_t *_isr_port(gpio_t pin) { msp_port_t *p = _port(pin); if ((p == PORT_1) || (p == PORT_2)) { return (msp_port_isr_t *)p; } return NULL; } static inline uint8_t _pin(gpio_t pin) { return (uint8_t)(pin & 0xff); } static int _ctx(gpio_t pin) { int i = bitarithm_lsb(_pin(pin)); return (_port(pin) == PORT_1) ? i : (i + 8); } int gpio_init(gpio_t pin, gpio_mode_t mode) { msp_port_t *port = _port(pin); /* check if port is valid and mode applicable */ if ((port == NULL) || ((mode != GPIO_IN) && (mode != GPIO_OUT))) { return -1; } /* reset pin and output value */ if (mode == GPIO_OUT) { port->DIR |= _pin(pin); } else { port->DIR &= ~(_pin(pin)); } port->OD &= ~(_pin(pin)); return 0; } int gpio_init_int(gpio_t pin, gpio_mode_t mode, gpio_flank_t flank, gpio_cb_t cb, void *arg) { msp_port_isr_t *port = _isr_port(pin); /* check if port, pull resistor and flank configuration are valid */ if ((port == NULL) || (flank == GPIO_BOTH)) { return -1; } /* disable any activated interrupt */ port->IE &= ~(_pin(pin)); /* configure as input */ if (gpio_init(pin, mode) < 0) { return -1; } /* save ISR context */ isr_ctx[_ctx(pin)].cb = cb; isr_ctx[_ctx(pin)].arg = arg; /* configure flank */ port->IES &= ~(_pin(pin)); port->IES |= (flank & _pin(pin)); /* clear pending interrupts and enable the IRQ */ port->IFG &= ~(_pin(pin)); gpio_irq_enable(pin); return 0; } void gpio_periph_mode(gpio_t pin, bool enable) { REG8 *sel; msp_port_isr_t *isrport = _isr_port(pin); if (isrport) { sel = &(isrport->SEL); } else { msp_port_t *port = _port(pin); if (port) { sel = &(port->SEL); } else { return; } } if (enable) { *sel |= _pin(pin); } else { *sel &= ~(_pin(pin)); } } void gpio_irq_enable(gpio_t pin) { msp_port_isr_t *port = _isr_port(pin); if (port) { port->IE |= _pin(pin); } } void gpio_irq_disable(gpio_t pin) { msp_port_isr_t *port = _isr_port(pin); if (port) { port->IE &= ~(_pin(pin)); } } int gpio_read(gpio_t pin) { msp_port_t *port = _port(pin); if (port->DIR & _pin(pin)) { return (int)(port->OD & _pin(pin)); } else { return (int)(port->IN & _pin(pin)); } } void gpio_set(gpio_t pin) { _port(pin)->OD |= _pin(pin); } void gpio_clear(gpio_t pin) { _port(pin)->OD &= ~(_pin(pin)); } void gpio_toggle(gpio_t pin) { _port(pin)->OD ^= _pin(pin); } void gpio_write(gpio_t pin, int value) { if (value) { _port(pin)->OD |= _pin(pin); } else { _port(pin)->OD &= ~(_pin(pin)); } } static inline void isr_handler(msp_port_isr_t *port, int ctx) { for (int i = 0; i < PINS_PER_PORT; i++) { if ((port->IE & (1 << i)) && (port->IFG & (1 << i))) { port->IFG &= ~(1 << i); isr_ctx[i + ctx].cb(isr_ctx[i + ctx].arg); } } } ISR(PORT1_VECTOR, isr_port1) { __enter_isr(); isr_handler((msp_port_isr_t *)PORT_1, 0); __exit_isr(); } ISR(PORT2_VECTOR, isr_port2) { __enter_isr(); isr_handler((msp_port_isr_t *)PORT_2, 8); __exit_isr(); }