mirror of https://github.com/ARMmbed/mbed-os.git
196 lines
5.8 KiB
C
196 lines
5.8 KiB
C
|
/* mbed Microcontroller Library
|
||
|
* Copyright (c) 2006-2018 ARM Limited
|
||
|
*
|
||
|
* 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.
|
||
|
*/
|
||
|
#include <stddef.h>
|
||
|
|
||
|
#include "gpio_irq_api.h"
|
||
|
#include "mbed_error.h"
|
||
|
#include "cmsis.h"
|
||
|
|
||
|
#if DEVICE_INTERRUPTIN
|
||
|
#if defined(GPIO_IRQ_DEBUG)
|
||
|
#include "mbed_interface.h"
|
||
|
#endif /* GPIO_IRQ_DEBUG */
|
||
|
|
||
|
#define GPIO_PINNUM 28
|
||
|
#define NONE (uint32_t)NC
|
||
|
#define GPIO_INT_CTRL_REG (RDA_GPIO->INTCTRL)
|
||
|
#define GPIO_INT_SEL_REG (RDA_GPIO->INTSEL)
|
||
|
#define GPIO_DATA_IN_REG (RDA_GPIO->DIN)
|
||
|
|
||
|
typedef enum {
|
||
|
GPIO_IRQ_CH0,
|
||
|
GPIO_IRQ_CH1,
|
||
|
CHANNEL_NUM
|
||
|
} GPIO_IRQ_IDX_T;
|
||
|
|
||
|
static uint32_t channel_ids[CHANNEL_NUM] = {0};
|
||
|
static uint32_t channel_pinidxs[CHANNEL_NUM] = {0};
|
||
|
static uint8_t channel_bothedge_flag[CHANNEL_NUM] = {0};
|
||
|
static gpio_irq_handler irq_handler[CHANNEL_NUM] = {NULL};
|
||
|
|
||
|
#if defined(GPIO_IRQ_DEBUG)
|
||
|
static uint32_t exception_cntr;
|
||
|
#endif /* GPIO_IRQ_DEBUG */
|
||
|
|
||
|
static GPIO_IRQ_IDX_T gpio_irq_ava_chidx(void)
|
||
|
{
|
||
|
GPIO_IRQ_IDX_T ret;
|
||
|
for (ret = GPIO_IRQ_CH0; ret < CHANNEL_NUM; ret++) {
|
||
|
if (0 == channel_ids[ret])
|
||
|
break;
|
||
|
}
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static uint32_t gpio_irq_pinidx(PinName pin)
|
||
|
{
|
||
|
uint8_t idx;
|
||
|
const uint32_t pinmap_gpio_irq[GPIO_PINNUM] = {
|
||
|
/* GPIO 0 ~ 13 */
|
||
|
PB_0, PB_1, PB_2, PB_3, PB_4, PB_5, PB_6, PB_7, PB_8, PB_9, PA_8, PA_9, PC_0, PC_1,
|
||
|
/* GPIO 14 ~ 21, Not support interrupt */
|
||
|
NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE,
|
||
|
/* GPIO 22 ~ 27 */
|
||
|
PD_0, PD_1, PD_2, PD_3, PA_0, PA_1
|
||
|
};
|
||
|
|
||
|
for (idx = 0; idx < GPIO_PINNUM; idx++) {
|
||
|
if (pinmap_gpio_irq[idx] == NONE) {
|
||
|
continue;
|
||
|
} else if (pinmap_gpio_irq[idx] == (uint32_t)pin) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (GPIO_PINNUM == idx) {
|
||
|
error("The pin cannot generate interrupt");
|
||
|
}
|
||
|
return idx;
|
||
|
}
|
||
|
|
||
|
static void handle_interrupt_in(void)
|
||
|
{
|
||
|
/* Read current interrupt register */
|
||
|
uint32_t int_ctrl = GPIO_INT_CTRL_REG;
|
||
|
uint32_t din_val = GPIO_DATA_IN_REG;
|
||
|
uint32_t idx;
|
||
|
|
||
|
if (int_ctrl & (0x01UL << 16)) {
|
||
|
GPIO_INT_CTRL_REG |= (0x01UL << 16);
|
||
|
while (GPIO_INT_CTRL_REG & (0x01UL << 16));
|
||
|
}
|
||
|
for (idx = GPIO_IRQ_CH0; idx < CHANNEL_NUM; idx++) {
|
||
|
if (int_ctrl & (0x01UL << (21 + idx))) {
|
||
|
gpio_irq_event flagRiseFall_1, flagRiseFall_2;
|
||
|
GPIO_INT_CTRL_REG |= (0x01UL << (17 + idx)); // clear int flag
|
||
|
flagRiseFall_1 = (int_ctrl & (0x01UL << (2 + idx))) ? IRQ_RISE : IRQ_FALL;
|
||
|
flagRiseFall_2 = (din_val & (0x01UL << channel_pinidxs[idx])) ? IRQ_RISE : IRQ_FALL;
|
||
|
if (flagRiseFall_1 == flagRiseFall_2) {
|
||
|
if (channel_bothedge_flag[idx]) {
|
||
|
GPIO_INT_CTRL_REG ^= (0x01UL << (2 + idx));
|
||
|
}
|
||
|
irq_handler[idx](channel_ids[idx], flagRiseFall_1);
|
||
|
#if defined(GPIO_IRQ_DEBUG)
|
||
|
exception_cntr = 0;
|
||
|
#endif /* GPIO_IRQ_DEBUG */
|
||
|
}
|
||
|
#if defined(GPIO_IRQ_DEBUG)
|
||
|
else {
|
||
|
exception_cntr++;
|
||
|
if (exception_cntr >= 2) {
|
||
|
exception_cntr = 0;
|
||
|
mbed_error_printf("invalid gpio irq: %d,%d\r\n", (int)flagRiseFall_1, (int)flagRiseFall_2);
|
||
|
}
|
||
|
}
|
||
|
#endif /* GPIO_IRQ_DEBUG */
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int gpio_irq_init(gpio_irq_t *obj, PinName pin, gpio_irq_handler handler, uint32_t id)
|
||
|
{
|
||
|
uint32_t regval;
|
||
|
|
||
|
if (pin == NC) return -1;
|
||
|
|
||
|
obj->ch = (uint16_t)gpio_irq_ava_chidx();
|
||
|
MBED_ASSERT(CHANNEL_NUM != obj->ch);
|
||
|
|
||
|
irq_handler[obj->ch] = handler;
|
||
|
|
||
|
channel_ids[obj->ch] = id;
|
||
|
channel_pinidxs[obj->ch] = gpio_irq_pinidx(pin);
|
||
|
|
||
|
regval = RDA_GPIO->INTSEL & ~(0x3FFUL << 10);
|
||
|
RDA_GPIO->INTSEL = regval | (0x3FFUL << 10);
|
||
|
|
||
|
NVIC_SetVector(GPIO_IRQn, (uint32_t)handle_interrupt_in);
|
||
|
NVIC_SetPriority(GPIO_IRQn, 0x1FUL);
|
||
|
NVIC_EnableIRQ(GPIO_IRQn);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void gpio_irq_free(gpio_irq_t *obj)
|
||
|
{
|
||
|
channel_ids[obj->ch] = 0;
|
||
|
}
|
||
|
|
||
|
void gpio_irq_set(gpio_irq_t *obj, gpio_irq_event event, uint32_t enable)
|
||
|
{
|
||
|
uint32_t reg_val;
|
||
|
uint16_t intEn;
|
||
|
MBED_ASSERT(1 >= obj->ch);
|
||
|
|
||
|
if (IRQ_RISE == event) {
|
||
|
obj->flagR = (uint8_t)enable;
|
||
|
} else {
|
||
|
obj->flagF = (uint8_t)enable;
|
||
|
}
|
||
|
if (obj->flagR && obj->flagF)
|
||
|
channel_bothedge_flag[obj->ch] = 1U;
|
||
|
else
|
||
|
channel_bothedge_flag[obj->ch] = 0U;
|
||
|
if (obj->flagR || obj->flagF)
|
||
|
intEn = 1;
|
||
|
else
|
||
|
intEn = 0;
|
||
|
|
||
|
if (0 == intEn) {
|
||
|
GPIO_INT_CTRL_REG &= ~(0x01UL << (6 + obj->ch));
|
||
|
} else {
|
||
|
/* Set interrupt select reg */
|
||
|
reg_val = GPIO_INT_SEL_REG & ~(0x1FUL << (5 * obj->ch));
|
||
|
GPIO_INT_SEL_REG = reg_val | ((0x1FUL & ((channel_pinidxs[obj->ch] >= 22) ? (channel_pinidxs[obj->ch] - 8) : channel_pinidxs[obj->ch])) << (5 * obj->ch));
|
||
|
|
||
|
/* Set interrupt control reg */
|
||
|
reg_val = GPIO_INT_CTRL_REG & ~(0x01UL << (2 + obj->ch));
|
||
|
GPIO_INT_CTRL_REG = reg_val | (((0U == channel_bothedge_flag[obj->ch]) && (1U == obj->flagR)) ? (0x01UL << (2 + obj->ch)) : (0x00UL))
|
||
|
| (0x01UL << (6 + obj->ch));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void gpio_irq_enable(gpio_irq_t *obj)
|
||
|
{
|
||
|
NVIC_EnableIRQ(GPIO_IRQn);
|
||
|
}
|
||
|
|
||
|
void gpio_irq_disable(gpio_irq_t *obj)
|
||
|
{
|
||
|
NVIC_DisableIRQ(GPIO_IRQn);
|
||
|
}
|
||
|
|
||
|
#endif /* DEVICE_INTERRUPTIN */
|