mbed-os/targets/TARGET_TI/TARGET_MSP432/gpio_irq_api.c

220 lines
6.1 KiB
C

/* mbed Microcontroller Library
* Copyright (c) 2019 ARM Limited
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* Low-level implementation of gpio interrupts for MSP432.
* This implementation uses bit-banding, so no masking of bits
* is necessary during simple get/set operations for a single pin.
*
* See hal/gpio_api.h for a detailed description of the following
* functions.
*/
#if DEVICE_INTERRUPTIN
#include "gpio_irq_api.h"
#include "mbed_error.h"
#include "gpio_msp432.h"
static uint32_t irq_ids [6][8] = {0};
static uint8_t both_edges[6][8] = {0};
static gpio_irq_handler irq_handler = 0;
/** Initialize the GPIO IRQ pin
*
* @param obj The GPIO object to initialize
* @param pin The GPIO pin name
* @param handler The handler to be attached to GPIO IRQ
* @param id The object ID (id != 0, 0 is reserved)
* @return -1 if pin is NC, 0 otherwise
*/
int gpio_irq_init(gpio_irq_t *obj, PinName pin, gpio_irq_handler handler, uint32_t id)
{
if (pin == (PinName)NC) {
obj->pin_base = 0;
return -1;
}
uint8_t port_index = MSP432_PORT_IDX(pin);
uint8_t pin_index = MSP432_PIN_IDX(pin);
if (port_index > 5 || pin_index > 7) {
obj->pin_base = 0;
error("IRQs not supported on GPIO P%d.%d\r\n", port_index + 1, pin_index);
return -1;
}
// Initialize the gpio_t object
obj->pin_base = PIN_BASE(port_index, pin_index);
obj->port = port_index;
obj->pin = pin_index;
obj->rise = 0;
obj->fall = 0;
// Store irq handler. We do not have to store
// a separate handler for every pin, because the
// InterruptIn-driver will always use the same one...
irq_handler = handler;
// Store the ID for this specific pin. It will be
// used as a pointer to one InterruptIn instance by
// the InterruptIn class.
irq_ids[port_index][pin_index] = id;
// Clear pending IRQ
PIN_REG(obj->pin_base, REG_IFG) = 0;
// Enable IRQs on this port
NVIC_EnableIRQ((IRQn_Type)(35 + port_index));
return 0;
}
/** Release the GPIO IRQ PIN
*
* @param obj The gpio object
*/
void gpio_irq_free(gpio_irq_t *obj)
{
// Disable the interrupts
gpio_irq_disable(obj);
// Clear the ID
irq_ids[obj->port][obj->pin] = 0;
// Clear the gpio_irq_t object
obj->pin_base = 0;
obj->port = 0;
obj->pin = 0;
obj->rise = 0;
obj->fall = 0;
}
/** Enable/disable pin IRQ event
*
* @param obj The GPIO object
* @param event The GPIO IRQ event
* @param enable The enable flag
*/
void gpio_irq_set(gpio_irq_t *obj, gpio_irq_event event, uint32_t enable)
{
// Disable irqs for this pin while we are in the
// configuration process (so we have a chance to
// clear spurious interrupts.
PIN_REG(obj->pin_base, REG_IE) = 0;
switch (event) {
case IRQ_RISE: {
// Listen to rising edges
PIN_REG(obj->pin_base, REG_IES) = 0;
obj->rise = enable;
// Check if we have enabled both edges
both_edges[obj->port][obj->pin] = obj->rise && obj->fall;
break;
}
case IRQ_FALL: {
// Listen to falling edges
PIN_REG(obj->pin_base, REG_IES) = 1;
obj->fall = enable;
// Check if we have enabled both edges
both_edges[obj->port][obj->pin] = obj->rise && obj->fall;
break;
}
case IRQ_NONE:
default:
break;
}
// Enable IRQ as requested
enable ? gpio_irq_enable(obj) : gpio_irq_disable(obj);
}
void gpio_irq_enable(gpio_irq_t *obj)
{
// Clear pending interrupts (just in case...)
PIN_REG(obj->pin_base, REG_IFG) = 0;
// Check if both edges need to be detected and
// set initial edge select accordingly
if (both_edges[obj->port][obj->pin]) {
PIN_REG(obj->pin_base, REG_IES) = PIN_REG(obj->pin_base, REG_IN);
// Make sure we don't accidently trigger a irq!
PIN_REG(obj->pin_base, REG_IFG) = 0;
}
// Enable irqs for this pin
PIN_REG(obj->pin_base, REG_IE) = 1;
}
void gpio_irq_disable(gpio_irq_t *obj)
{
// Disable irqs for this pin
PIN_REG(obj->pin_base, REG_IE) = 0;
// Clear pending interrupts
PIN_REG(obj->pin_base, REG_IFG) = 0;
}
/**********************************/
/* Interrupt handlers for P1...P6 */
/**********************************/
void handle_interrupt_in(uint8_t port, uint8_t pin)
{
__IO uint8_t *pin_base = PIN_BASE(port, pin);
// Call the second-stage handler
if (irq_handler) {
irq_handler(irq_ids[port][pin],
PIN_REG(pin_base, REG_IN) ? IRQ_RISE : IRQ_FALL);
}
// Change the edge select when listening on both edges
if (both_edges[port][pin]) {
PIN_REG(pin_base, REG_IES) = PIN_REG(pin_base, REG_IN);
// Make sure we don't accidently trigger a irq!
PIN_REG(pin_base, REG_IFG) = 0;
}
}
void PORT1_IRQHandler(void)
{
uint32_t pin = P1->IV >> 1;
if (pin--) {
handle_interrupt_in(0, pin);
}
}
void PORT2_IRQHandler(void)
{
uint32_t pin = P2->IV >> 1;
if (pin--) {
handle_interrupt_in(1, pin);
}
}
void PORT3_IRQHandler(void)
{
uint32_t pin = P3->IV >> 1;
if (pin--) {
handle_interrupt_in(2, pin);
}
}
void PORT4_IRQHandler(void)
{
uint32_t pin = P4->IV >> 1;
if (pin--) {
handle_interrupt_in(3, pin);
}
}
void PORT5_IRQHandler(void)
{
uint32_t pin = P5->IV >> 1;
if (pin--) {
handle_interrupt_in(4, pin);
}
}
void PORT6_IRQHandler(void)
{
uint32_t pin = P6->IV >> 1;
if (pin--) {
handle_interrupt_in(5, pin);
}
}
#endif /* DEVICE_INTERRUPTIN */