С внешними прерываниями на других МК мы уже ранее работали, поэтому, в принципе, нет необходимости объяснять очень подробно, что это такое.
У ESP8266 также имеется механизм обработки внешних прерываний.
Тем не менее повторюсь.
Внешние прерывания — это такие прерывания, которые обрабатываются вследствие возникновения некоторых событий на определённой ножке порта микроконтроллера. Таких событий может быть несколько, не смотря на всего лишь 2 возможных логических состояния ножки.
ESP8266 умеет отслеживать следующие виды событий на ножке порта, судя по данным перечисляемым типам
Из данных констант видно, что существует 5 видов обрабатываемых событий (не считая DISABLE):
- переход из низкого состояния в высокое,
- переход из высокого состояния в низкое,
- переход из любого состояния в любое,
- пребывание ножки в низком состоянии,
- пребывание ножки в высоком состоянии.
Если посмотреть Technical Reference, то там описаны все типы данных событий (там где рассматриваются биты регистров)
Для того, чтобы на практике увидеть, как именно работают прерывания, мы подключим кнопку так же, как мы делали в уроке 6, и будем следить за переходом состояния ножки, к которой подключена кнопка, из низкого состояния в высокое. А так как ножка у нас по-прежнему через резистор будет притянута к питанию, а второй контакт кнопки будет подключен к общему проводу, то данный переход мы получим в момент отжатия кнопки, что, кстати, чаще всего и используется в практике.
Как включить и настроить данные прерывания, мы изучим уже на практике.
Схема урока у нас та же что и в уроке 6
Проект мы сделаем из проекта урока 7 с именем UART_TX и назовём его EXTI01.
Откроем наш проект в Eclipse и в main.c добавим макрос для ножки кнопки
1 2 |
#define LED 2 #define BUTTON 4 |
Добавим переменную для пользовательского флага
1 2 3 4 |
#define BUTTON 4 //------------------------------------------------------ uint8_t fl; //------------------------------------------------------ |
Удалим подключение библиотеки для UART, сегодня он нам не нужен
#include «driver/uart.h»
В user_init() удалим объявление локальной переменной и удалим инициализацию UART, сегодня он нам не нужен
uint16_t i=0;
// Configure the UART
uart_init(BIT_RATE_115200, BIT_RATE_115200);
Из бесконечного цикла удалим инкрементирование переменной
i++;
if(i>9999) i=1;
Также удалим весь вывод информации в UART
os_printf(«String %04d\r\n», i);
os_printf(«SDK version: %s\n», system_get_sdk_version());
os_printf(«Version info of boot: %d\n», system_get_boot_version());
os_printf(«Userbin address: 0x%x\n», system_get_userbin_addr());
os_printf(«Time = %ld\r\n», system_get_time());
os_printf(«RTC time = %ld\r\n», system_get_rtc_time());
os_printf(«Chip id = 0x%x\r\n», system_get_chip_id());
os_printf(«CPU freq = %d MHz\r\n», system_get_cpu_freq());
os_printf(«Flash size map = %d\r\n», system_get_flash_size_map());
os_printf(«Free heap size = %d\r\n», system_get_free_heap_size());
system_print_meminfo();
Задержку уменьшим в 10 раз в обоих местах
ets_delay_us(100000);
Проинициализируем нулём наш пользовательский флаг
1 2 |
gpio_init(); fl = 0; |
Включим нашу ножку как обычный GPIO и подтянем на него резистор с шины питания
1 2 3 |
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2); PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO4_U, FUNC_GPIO4); PIN_PULLUP_EN(PERIPHS_IO_MUX_GPIO4_U); |
Настроим ножку на вход
1 2 |
gpio_output_set(0, 0, (1 << LED), 0); gpio_output_set(0, 0, 0, (1 << BUTTON)); |
Прежде чем производить серьёзные настройки прерываний, сначала их нужно будет отключить
1 2 |
gpio_output_set(0, 0, 0, (1 << BUTTON)); ETS_GPIO_INTR_DISABLE(); |
Теперь нам надо будет добавить функцию обратного вызова, которая будет играть роль обработчика прерывания. Имя функции можно использовать любое, главное чтобы потом при регистрации данной функции внести в параметр то же имя. Добавим такую функцию выше функции user_rf_cal_sector_set
1 2 3 4 5 |
//------------------------------------------------------ static void gpio_intr_handler() { } //------------------------------------------------------ |
В теле данной функции сначала возьмём данные из регистра состояния GPIO
1 2 3 |
static void gpio_intr_handler() { uint32_t gpio_st = GPIO_REG_READ(GPIO_STATUS_ADDRESS); |
Отключим прерывания GPIO
1 2 |
uint32_t gpio_st = GPIO_REG_READ(GPIO_STATUS_ADDRESS); ETS_GPIO_INTR_DISABLE(); |
Если установлен бит, соответствующий нашей ножке, то установим наш пользовательский флаг, если он сброшен, а если установлен, то сбросим, и после этого подождём немного
1 2 3 4 5 6 7 |
ETS_GPIO_INTR_DISABLE(); if(gpio_st & BIT(4)) { if (!fl) fl=1; else fl=0; os_delay_us(100000); } |
С помощью занесения значения регистра состояния в специальный регистр, отвечающий за сброс флагов прерываний GPIO, мы сбросим флаги перерываний, которые были установлены, так как делать это нужно программно
1 2 3 |
os_delay_us(100000); } GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_st); |
Также нужно не забыть опять включить прерывания GPIO
1 2 |
GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_st); ETS_GPIO_INTR_ENABLE(); |
Вернёмся в user_init() и зарегистрируем наш обработчик с помощью специального макроса
1 2 |
ETS_GPIO_INTR_DISABLE(); ETS_GPIO_INTR_ATTACH(gpio_intr_handler,NULL); |
Если после этого мы попытаемся собрать наш код, то мы получим следующую ошибку
Всё дело в том, что в файле ets_sys.h в вызываемом макросе вызывается функция ets_isr_mask, которая реально существует, но прототипа на неё нигде нет. Видимо, это какие-то недоработки версии SDK. То же самое мы получим и касательно отключения прерываний, там уже нужен прототип функции ets_isr_unmask. Выход из данной ситуации — простое добавление прототипов в файле main.c
1 2 3 4 5 |
#define BUTTON 4 //------------------------------------------------------ void ets_isr_mask(unsigned intr); void ets_isr_unmask(unsigned intr); //------------------------------------------------------ |
Теперь всё соберётся. А когда это поправят, убрать данные прототипы будет несложно.
Далее в main() мы с помощью специальной функции SDK настроим прерывание на нашу ножку по событию перехода из низкого логического состояния в высокое
1 2 |
ETS_GPIO_INTR_ATTACH(gpio_intr_handler,NULL); gpio_pin_intr_state_set(GPIO_ID_PIN(4),GPIO_PIN_INTR_POSEDGE); |
Включим прерывания GPIO
1 2 |
gpio_pin_intr_state_set(GPIO_ID_PIN(4),GPIO_PIN_INTR_POSEDGE); ETS_GPIO_INTR_ENABLE(); |
В бесконечном цикле мы устанавливать нулевой уровень на ножке, к которой подключен светодиод будем только при условии установленного пользовательского флага
1 2 3 4 5 |
system_soft_wdt_feed(); if (fl) { gpio_output_set(0, (1 << LED), 0, 0); } |
Вот и весь код.
Соберём код, прошьём контроллер.
Теперь у нас светодиоды сразу мигать не будут, для того, чтобы мигалка заработала, надо будет нажать и отжать кнопку
Если мы повторим данные действия ещё раз, то светодиод мигать перестанет
Также вы можете попробовать, как будет работать программа при использовании четырёх других событий.
Итак, на данном уроке мы изучили, как работает механизм прерываний от порта GPIO, а также закрепили данные знания на практике.
Всем спасибо за внимание!
Предыдущий урок Программирование МК ESP8266 Следующий урок
Модуль ESP NodeMCU можно купить здесь: Модуль ESP NodeMCU
Различные модули ЕSP8266 можно приобрести здесь Модули ЕSP8266
Переходник USB to TTL можно приобрести здесь ftdi ft232rl
Многофункциональный переходник JTAG UART FIFO SPI I2C можно приобрести здесь CJMCU FT232H USB к JTAG UART FIFO SPI I2C
Логический анализатор 16 каналов можно приобрести здесь
Смотреть ВИДЕОУРОК (нажмите на картинку)
Здравствуйте! Скажите пожалуйста, а у Вас есть урок по таймерам прерывания на esp8266
Здравствуйте! Да. Есть.
Здравствуйте. При использовании SDK внешнее прерывание периодически самопроизвольно вызывается(без нажатия кнопки). Не подскажите в чем причина.