Доброго времени суток всем. Продолжаю ковыряться с esp8266, и вот понадобилось мне получить прерывание от двух пинов GPIO.
Делаю следующим образом:
В результате получаю непрерывный вывод в консоль числа 0, однако при попытке замкнуть указанные GPIO на землю контроллер зависает, а потом перезагружается по ватчдогу. Иногда успевает проскочить 1 или -1.
Читал, что нужно обработчик прерывания декларировать как ICACHE_RAM_ATTR, однако в Non-OS SDK такого дефайна нет, код не компилируется. К тому же, если я правильно помню, функции, не описанные как ICACHE_FLASH_ATTR, и так попадают в RAM.
Скажите, что я делаю не так? Может быть, имеет смысл вообще забить на прерывания, и просто опрашивать ноги в таймере?
Делаю следующим образом:
C:
#define user_procTaskPrio 0
#define user_procTaskQueueLen 1
os_event_t user_procTaskQueue[user_procTaskQueueLen];
volatile int what;
static void ICACHE_FLASH_ATTR loop(os_event_t *event)
{
static int level;
os_printf("What: %d\n", what);
level = !level;
GPIO_OUTPUT_SET(GPIO_ID_PIN(2), ((level)? 1 : 0));
os_delay_us(100000);
system_os_post(user_procTaskPrio, 0, 0);
}
static void gpio_intr_handler(int *dummy)
{
// clear gpio status. Say ESP8266EX SDK Programming Guide in 5.1.6. GPIO interrupt handler
uint32 gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS);
// if the interrupt was by GPIO
if (gpio_status & BIT(4))
{
// disable interrupt for GPIO
gpio_pin_intr_state_set(GPIO_ID_PIN(4), GPIO_PIN_INTR_DISABLE);
// Do something
(*dummy)++;
//clear interrupt status for GPIO
GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status & BIT(4));
// Reactivate interrupts for GPIO
gpio_pin_intr_state_set(GPIO_ID_PIN(4), GPIO_PIN_INTR_NEGEDGE);
}
if (gpio_status & BIT(5))
{
gpio_pin_intr_state_set(GPIO_ID_PIN(5), GPIO_PIN_INTR_DISABLE);
// Do something
(*dummy)--;
GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status & BIT(5));
gpio_pin_intr_state_set(GPIO_ID_PIN(5), GPIO_PIN_INTR_NEGEDGE);
}
}
void ICACHE_FLASH_ATTR user_init(void)
{
system_timer_reinit();
uart_init(115200, 115200);
gpio_init();
// Configure as GPIO
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2); // LED
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO4_U, FUNC_GPIO4); // Button
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO5_U, FUNC_GPIO5); // Wire
// Set logic 1 for output
GPIO_OUTPUT_SET(GPIO_ID_PIN(2), 1);
// Disable output
GPIO_DIS_OUTPUT(GPIO_ID_PIN(4));
GPIO_DIS_OUTPUT(GPIO_ID_PIN(5));
// Enable pullup for inputs
PIN_PULLUP_EN(PERIPHS_IO_MUX_GPIO4_U);
PIN_PULLUP_EN(PERIPHS_IO_MUX_GPIO5_U);
// Disable interrupts by GPIO
ETS_GPIO_INTR_DISABLE();
// Attach interrupt handle to gpio interrupts.
// You can set a void pointer that will be passed to interrupt handler each interrupt
ETS_GPIO_INTR_ATTACH(gpio_intr_handler, &what);
// From include file
// Set the specified GPIO register to the specified value.
// This is a very general and powerful interface that is not
// expected to be used during normal operation. It is intended
// mainly for debug, or for unusual requirements.
//
// All people repeat this mantra but I don't know what it means
//
gpio_register_set(GPIO_PIN_ADDR(4),
GPIO_PIN_INT_TYPE_SET(GPIO_PIN_INTR_DISABLE) |
GPIO_PIN_PAD_DRIVER_SET(GPIO_PAD_DRIVER_DISABLE) |
GPIO_PIN_SOURCE_SET(GPIO_AS_PIN_SOURCE));
// clear gpio status. Say ESP8266EX SDK Programming Guide in 5.1.6. GPIO interrupt handler
GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, BIT(4));
// enable interrupt for his GPIO
// GPIO_PIN_INTR_... defined in gpio.h
gpio_pin_intr_state_set(GPIO_ID_PIN(4), GPIO_PIN_INTR_NEGEDGE);
gpio_register_set(GPIO_PIN_ADDR(5),
GPIO_PIN_INT_TYPE_SET(GPIO_PIN_INTR_DISABLE) |
GPIO_PIN_PAD_DRIVER_SET(GPIO_PAD_DRIVER_DISABLE) |
GPIO_PIN_SOURCE_SET(GPIO_AS_PIN_SOURCE));
GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, BIT(5));
gpio_pin_intr_state_set(GPIO_ID_PIN(5), GPIO_PIN_INTR_NEGEDGE);
ETS_GPIO_INTR_ENABLE();
system_os_task(loop, user_procTaskPrio, user_procTaskQueue, user_procTaskQueueLen);
system_os_post(user_procTaskPrio, 0, 0);
}
В результате получаю непрерывный вывод в консоль числа 0, однако при попытке замкнуть указанные GPIO на землю контроллер зависает, а потом перезагружается по ватчдогу. Иногда успевает проскочить 1 или -1.
Читал, что нужно обработчик прерывания декларировать как ICACHE_RAM_ATTR, однако в Non-OS SDK такого дефайна нет, код не компилируется. К тому же, если я правильно помню, функции, не описанные как ICACHE_FLASH_ATTR, и так попадают в RAM.
Скажите, что я делаю не так? Может быть, имеет смысл вообще забить на прерывания, и просто опрашивать ноги в таймере?