[分享] 软件如何解决 ESP32 GPIO 中断丢失的问题
Posted: Tue May 21, 2019 12:59 pm
ESP32 共有 40 个 GPIO, 每个 GPIO 都可响应外部中断, 中断触发模式有边沿触发和电平触发. 其中 GPIO0 ~ GPIO31 中断状态由寄存器 GPIO_STATUS_W1TC_REG 清除, GPIO32 ~ GPIO39 中断状态由寄存器 GPIO_STATUS1_W1TC_REG 清除. 在清除中断状态时, 如果位于同一组寄存器的 GPIO 刚好产生中断, 很有可能会导致该中断丢失. 即在置位 GPIO_STATUS_W1TC_REG 中某一 bit 时, 如果 GPIO0~GPIO31 刚好有一个 GPIO 产生一个中断, 则该中断很有可能会丢失. GPIO32~GPIO39 同理. 但在置位 GPIO_STATUS_W1TC_REG 时, 不会导致 GPIO32~GPIO39 的中断丢失. 该问题只会导致边沿触发类型的中断丢失. 下面介绍如何通过软件来解决这个问题.
对于电平触发的中断, 当触发电平有效时, 中断状态被清除, 硬件会继续产生中断, 所以电平触发的中断不会丢失. 解决中断丢失的方案就是使用电平触发的中断, 以下的示例代码将在高电平的时候产生一次中断, 与 rising edge 类型中断效果相同.
以上代码可以解决中断丢失的问题, 但是带来的代价是中断次数会加倍(相对于 rising edge 和 falling edge )
对于电平触发的中断, 当触发电平有效时, 中断状态被清除, 硬件会继续产生中断, 所以电平触发的中断不会丢失. 解决中断丢失的方案就是使用电平触发的中断, 以下的示例代码将在高电平的时候产生一次中断, 与 rising edge 类型中断效果相同.
Code: Select all
const uint64_t INPUT_PIIN_MASK = (1ULL<<GPIO_NUM_34) | (1ULL<<GPIO_NUM_36) | (1ULL<<GPIO_NUM_39);
typedef struct io_isr_param_ {
uint32_t io_tagle;
uint32_t io_number;
uint32_t isr_cnt;
} io_isr_param_t;
uint32_t isr_mode[2] = {GPIO_INTR_HIGH_LEVEL, GPIO_INTR_LOW_LEVEL};
io_isr_param_t io34_param = {
.io_tagle = 0x1,
.io_number = 34,
};
io_isr_param_t io36_param = {
.io_tagle = 0x1,
.io_number = 36,
};
io_isr_param_t io39_param = {
.io_tagle = 0x1,
.io_number = 39,
};
IRAM_ATTR void gpio_isr(void *param)
{
io_isr_param_t *io_param = (io_isr_param_t *)param;
if(io_param->io_tagle) {
//do some thing here;
}
gpio_set_intr_type(io_param->io_number, isr_mode[io_param->io_tagle]);
io_param->io_tagle ^= 0x1;
}
void io_init(void)
{
gpio_config_t io_conf = {
.intr_type = GPIO_PIN_INTR_DISABLE,
.mode = GPIO_MODE_INPUT,
.pin_bit_mask = INPUT_PIIN_MASK,
.pull_down_en = 0,
.pull_up_en = 1,
};
//configure GPIO with the given settings
gpio_config(&io_conf);
if(gpio_install_isr_service( 0 ) != ESP_OK) {
printf("ISR install fail\n");
return;
}
//hook isr handler for specific gpio pin
gpio_isr_handler_add(GPIO_NUM_34, gpio_isr, (void*) &io34_param);
gpio_isr_handler_add(GPIO_NUM_36, gpio_isr, (void*) &io36_param);
gpio_isr_handler_add(GPIO_NUM_39, gpio_isr, (void*) &io39_param);
gpio_set_intr_type(GPIO_NUM_34, GPIO_INTR_HIGH_LEVEL);
gpio_intr_enable(GPIO_NUM_34);
gpio_set_intr_type(GPIO_NUM_36, GPIO_INTR_HIGH_LEVEL);
gpio_intr_enable(GPIO_NUM_36);
gpio_set_intr_type(GPIO_NUM_39, GPIO_INTR_HIGH_LEVEL);
gpio_intr_enable(GPIO_NUM_39);
}
以上代码可以解决中断丢失的问题, 但是带来的代价是中断次数会加倍(相对于 rising edge 和 falling edge )