- ESP8266 Урок 5. Настраиваем toolchain и пишем свою прошивку в IDE Eclipse. Часть 1
- Xtensa lx106 elf gcc windows
- 1) Сборка esp-open-sdk
- 2) Создание базового проекта
- 3) Описание API для работы с GPIO
- 4) Краткое описание ассемблера Xtensa
- 5) Подключение JTAG отладчика на FT232H чипе
- 6) Работа с GPIO16 через NonOS-SDK, подключение библиотеки «Driver_Lib»
- 7) Вывод через UART
ESP8266 Урок 5. Настраиваем toolchain и пишем свою прошивку в IDE Eclipse. Часть 1
Ну вот, наконец-то пришла пора нам вернуться к контроллеру с WiFi-модулем ESP8266.
До сих пор мы его изучали, пользуясь ПО, установленном в нём при покупке, а также прошивали готовое ПО.
А теперь мы попробуем прошивку для него написать самостоятельно.
Это можно сделать несколькими способами. Можно воспользоваться Arduino IDE, но только мне это что-то не понравилось из-за отсутствия гибкости написания кода и по нескольким другим причинам. Также можно воспользоваться SDK, который предоставляет нам производитель. Но воспользоваться им тоже можно по-разному. Само собой, нам потребуется кросс-компилятор, мы же ведь пишем и собираем ПО не на той машине, на которой оно будет работать. Кросс-компилятор нам, к сожалению, производитель не предоставляет, но найти его не сложно, так как существует много сборок в сети, большинство которых работают в ОС Linux, также есть несколько сборок и для Windows. Одной из таких сборок мы и воспользуемся.
Скачать её можно здесь. Также я добавлю на всякий случай ссылочку внизу страницы, чтобы не затерялась.
Также нам потребуется утилита esptool. Где её взять и как пользоваться, мы знаем. Она представляет из себя файл, интерпретируемый средой Python. Если у кого-то нет желания устанавливать Python, я дам ссылку на комплект утилит внизу страницы. В принципе, условимся на том, что его нет ни у кого, и данной утилитой, скомпилированной для Windows, мы и будем пользоваться.
Собирать проект мы будем в среде программирования Eclipse, с которой мы уже давно знакомы из уроков по языку C. Также для автоматизации процесса сборки прошивки, а также процесса загрузки её в FLASH-память контроллера, мы собственноручно напишем сценарий Makefile. А проект, как всегда, мы напишем простейший, который будет мигать светодиодом на плате.
Плату мы будем использовать другую – NodeMCU, в которой распаяна микросхема-конвертер USB-TTL, также управление перезагрузкой модуля и подготовкой его к прошиванию тоже настроено через интерфейс UART с помощью CTS и RTS.
Вот так выглядит модуль
Микросхема-конвертер может попасться разная. Поэтому драйвер нужен будет соответствующий. Ну, думаю, с этим проблем не будет. У меня лично CH340, пока проблем не заметил.
К плюсам данной сборки можно отнести помимо её удобного управления через USART размер FLASH-памяти 4 мегабайта, а также вывод огромного количества ножек наружу.
Если вы ещё не успели приобрести такой модуль, то можно по-прежнему пользоваться ESP-01, но лучше всё-таки заиметь именно такой, так как он намного удобнее и стоит также очень недорого.
Теперь давайте приступим к установке и разворачиванию нашего ПО.
По ссылке, которую я дал выше, скачаем самую последнюю версию тулчейна
Начнём его установку, откроется диалог, в котором снимем галку добавления системного пути, так как мы не будем использовать весь комплект, а только его часть, и скопируем мы потом всё, что нам из него надо, в другой каталог. Также согласимся с лицензией использования
Жмём кнопку Install и тулчейн распакуется по указанному пути.
Теперь создадим какую-нибудь папку где угодно, в которой мы будем хранить весь наш тулчейн и SDK. Я, например, на диске D создал папку с именем ESP8266.
В данную папку скопируем из папки, в которую распаковался только что наш полный тулчейн, папку xtensa-lx106-elf, находящуюся в папке opt.
Также в папке D:\ESP8266 создадим папку ESP8266_NONOS_SDK, в которую скопируем содержимое ESP8266 NONOS SDK V2.2.1, скачанного с сайта espressif.
Также в папку D:\ESP8266 скопируем папку utils с тремя утилитами из архива с утилитами, ссылка на который есть внизу страницы.
Вот, в принципе, и весь тулчейн.
Надеюсь, что все смотрят мои уроки по языку C и у всех Eclipse и MinGW уже установлен, поэтому заострять на этом внимание не будем. А если это не так, то есть в данном курсе уроки по установке и того и другого.
Запустим IDE Eclipse и начнём создание нового проекта, для чего пройдём по меню File -> New -> Project
В открывшемся диалоге выберем C/C++ -> C Project и жмём Next
В следующем открывшемся диалоге выбираем Executable -> Empty Project -> Cross GCC, дадим имя нашему проекту и снова жмём Next
Затем в следующем разделе галку оставляем только на Release и жмём Finish
В дереве проектов у нас создалась пустая папка с именем проекта. Настроим некоторые свойства нашего проекта, вызвав соответствующее контекстное меню.
В свойствах в пункте C/C++ Build/Tool Chain Editor открывшегося диалога изменим текущий сборщик
В пункте C/C++ Build снимем 2 галки и напишем команду для сборки проекта в соответствующее поле редактирования, а текст другого поля отредактируем, убрав оттуда папку Debug, и нажмём кнопку Apply and Close
Остальные настройки мы добавим позже.
Как создавать папки внутри дерева проекта, а также как добавлять туда новые файлы с исходным кодом и заголовочные файлы, мы знаем из этого урока, поэтому повторять это смысла нет.
Создадим новые папки с именами inc. src и build в дереве с проектом
Xtensa lx106 elf gcc windows
Artwork by Hanzheng Tang
Как я уже говорил, ESP8266 можно программировать двумя способами: либо через Arduino IDE, либо через тулчейн esp-open-sdk. Первый вариант я уже рассматривал на примере разработки температурного логера, в этот раз я хочу рассказать о работе с esp-open-sdk.
Тулчейн позволяет программировать на SDK функциях, которые поставляются в закрытых скомпилированных библиотеках называемых SDK. Имеется две версии SDK: RTOS SDK и NONOS SDK. Я буду рассматривать вариант без RTOS, при необходимости, «прикрутить» простенький диспечер задач будет несложно.
Non-OS SDK — это библиотека предоставляющая программный интерфейс приложения(API) для ESP8266 и включающая стек функций для приема и передачи через WiFi-соединение, доступа к аппаратным ресурсам и базовые функции контроля и управления модулем. Данное API позволяет программировать на более высоком уровне не вдаваясь в особенности архитектуры ESP8266.
SDK может быть интересен для опытных embedded — программистов, которых, возможно, тяготит использование Arduino IDE и Wiring, и которые не боятся остаться один на один с Си. Взамен вы получите: избавление от прослойки Arduino/Wiring, возможность использования вашей любимой системы управления проектом, возможность использования стороннего IDE для написания кода, а также возможность отладки через JTAG. Что вы теряете? Возможность использования Arduino библиотек.
Однако, если вы используете фреймворк Arduino, то для вас не доложно быть секретом, что сам он написан на функциях SDK. Т.о. изучение SDK существенно расширит ваши возможности при написании своих библиотек, да и собственно само программирование ESP8266, т.к. функции SDK доступны из Arduino.
- Если верить википедии, то в данный момент существует несколько тулчейнов для eps8266:
- официальный который поставляется в виде образа виртуальной машины с ubuntu 14.4. Работа с ним описана в ESP8266 SDK Getting Started Guide;
- esp-open-sdk о котором пойдет речь в данной статье;
- Не тулчейн, а скорее способ сборки тулчейна от Макса Филиповаhttps://github.com/esp8266/esp8266-wiki/wiki/Toolchain. Насколько я понял, предназначено для тех, у кого проблемы с сборкой esp-open-sdk;
- Unofficial Development Kit или UDK тулчейн Михаила Григорьева, который содержит бинарные сборки для Windows. Проект содержит несколько хороших статей на хабре по работе с тулчейном.
Далее речь пойдёт исключительно о тулчейне «esp-open-sdk». В качестве целевой платы я буду использовать плату NodeMCU ESP8266, т.к. там есть автозагрузка прошивки, но в принципе может быть использована любая другая плата на модуле ESP12E/ESP12F.
Посмотреть исходники, сборочные файлы, скачать скомпилированные прошивки, можно с портала GITLAB https://gitlab.com/flank1er/esp8266_sdk_examples
1) Сборка esp-open-sdk
Сборка тулчейна с одной стороны производится легко, всего двумя командами, а с другой стороны, при проблемах со сборкой, все становится очень сложно. Но обо всём по порядку.
Страницей проекта esp-open-sdk является https://github.com/pfalcon/esp-open-sdk, и там же имеется краткая инструкция по сборке тулчейна. Действуя в соответствии с этой инструкцией, перевым делом нужно будет скачать тулчейн:
После чего следует зайти в каталог esp-open-sdk и запустить сборку командой:
Сборка полностью автоматическая и занимает примерно минут 50-60. Первые полчаса происходит скачивание исходников в директорию crosstool-NG/.build/tarballs:
После завершения скачивания, будет распаковка тарболов, наложение патчей и последующая сборка, которая в зависимости от мощности вашего компьютера может занять от 10-15 минут и больше. После завершения сборки, появится сообщение, что для использования тулчейна, следует добавить путь к нему в переменной окружения PATH:
К сожалению, не всегда сборка проходит успешно. Тулчейн нормально собирался у меня в Slackware 14.2 с gcc версии 5.3.0, так же успешно проходила сборка в виртуалке Ubuntu 14.4, которая идёт с официальным тулченом. А вот в Slackware-current с gcc-8.2.0 сборка валится на этапе сборки GDB. В любом случае, если вам не удаётся собрать тулчейн, то следует обратиться к инструкции поэтапной сборки Макса Филипова.
После завершения сборки, также автоматически скачивается и устанавливается SDK версии: ESP8266_NONOS_SDK-2.1.0-18-g61248df.
Чтобы проверить работоспособность тулчейна, следует перейти в директорию examples/blinky, где будет исходник проверочной мигалки:
Командой make собираем прошивку:
Далее имеется неприятный подводный камень. Прошивка будет работать, только если в ESP12 предварительно была записана прошивка AT-интерпретатора (проверьте!).
Подключаем плату NodeMCU к компьютеру и предварительно очищаем флешку командой:
Далее переходим в директорию SDK «sdk/bin» и прошиваем модуль ESP12 AT-интерпретатором:
Теперь возвращаемся в директорию с примером blinky, и загружаем прошивку командой «make flash»:
Если всё было сделано правильно, то светодиод на GPIO_2 начнёт мигать с полупериодом в половину секунды.
Для дальнейших прошивок, загружать прошивку AT-интерпретатора больше не нужно.
2) Создание базового проекта
Сейчас мы напишем свою упрощённую версию тестовой мигалки и свой Makefile для сборки проекта.
Для начала создаём структуру каталогов:
Переходим в каталог 00_blink и создаём файл main.c с исходным текстом мигалки:
Здесь функция user_init() — это функция которая по умолчанию вызывается первой при старте прошивки. Атрибут ICACHE_FLASH_ATTR указывает, что функция должна выполняться из флеш-памяти. При атрибуте IRAM_ATTR, функция будет выполняться из оперативной памяти. Все обработчики прерываний должны иметь атрибут IARM_ATTR.
Далее добавляем Makefile:
В Makefile я установил стандарт С99, и выставил флаг «-g» для последующей отладки прошивки в gdb. В качестве скрипта компоновщика используется eagle.app.v6.ld.
Структура файлов теперь выглядит так:
Но не все так просто. После пошивки, немого помигав, работа программы остановится. Если перезагрузить esp8266 нажатием на клавишу RST, то мигать будет, но цикл будет иногда сбиваться. ESP8266 постоянно перезагружается и это не дело. С помощью отладчика я выяснил, что происходит сброс по прерыванию Watchdog’а.
Согласно документации на SDK, если какая-либо функция выполняется слишком долго, скажем более 500 мс, то она должна вызывать функцию system_soft_wdt_feed() для сброса сторожевого таймера. Отключение watchdog’а производится вызовом функции system_soft_wdt_stop(), но использовать ее очень сильно не рекомендуют.
В Arduino имеется метод EspClass::wdtDisable(), и если посмотреть его реализацию там есть любопытный комментарий:
Т.е. получается, что совсем отключить watchdog штатными методами не удастся.
С учётом всего вышесказанного, добавляем в главный цикл вызов функции system_soft_wdt_feed():
В каталог inс необходимо будет добавить пока пустой заголовочный файл user_config.h. В этом файле предполагается хранение настроек WiFi: ssid, пароль и т.д. У нас он будет пока пустым.
После пересборки и перепрошивки, перезагрузки eps8266 должны исчезнуть.
В грубом приближении данная программа может служить тестом производительности архитектуры. У esp8266 тактовая частота составляет 80 МГц, у stm32f103c8t6 она равняется 72 МГц. Прошив их одной и той же программой (на уровне Си естественно), и положив обе платы рядом, я бы сказал, что светодиод на esp8266 мигает несколько быстрее.
3) Описание API для работы с GPIO
Наряду с WiFi модулем, GPIO являются для нас самой важной подсистемой в ESP8266. При работе с GPIO нас в первую очередь будет интересовать режим bit-banging на котором строятся программные варианты различных протоколов. С быстродействием у ESP8266 проблем нет, так же как и со свободным местом на флешке, зато есть проблемы со свободными выводами и аппаратными протоколами. На первое время, bit-banging будет у нас вместо лома для ответа на любые технические сложности. Поэтому изучение ESP8266 на мой взгляд логично будет начать с именно с подсистемы GPIO.
Функция gpio_init() которую мы использовали, не описана в ESP8266 Non-OS SDK API Reference, зато её объявление есть в заголовочном gpio.h со следующим комментарием:
Можно предположить, что данная функция сбрасывает GPIO порты в какое-то начальное состояние.
Далее нас будут интересовать следующие макросы:
PIN_PULLUP_DIS(PIN_NAME) Disable pin pull-up /Отключить подтягивающий резистор/. Пример:
//Использовать MTDI пин в качестве GPIO12.
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, FUNC_GPIO12);PIN_PULLUP_EN(PIN_NAME) Enable pin pull up /Включить подтягивающий резистор/. PIN_FUNC_SELECT(PIN_NAME, FUNC) Select pin function /Выбор функции/. ESP8266 Non-OS SDK API Reference
Используемый в программе макрос PIN_FUNC_SELECT определён в заголовочном файле eagle_soc.h:
Варианты значений FUNC для различных GPIO перечислены тут же:
Честно говоря, мне не понятно, почему в строке:
производятся манипуляции с GPIO1, в то время как светодиод находится на GPIO2 .
Функция gpio_output_set
Назначение функции Установка режима GPIO Прототип void gpio_output_set(
uint32 set_mask,
uint32 clear_mask,
uint32 enable_mask,
uint32 disable_mask)Параметры uint32 set_mask : установка высокого логического значения; 1: высокое значение; 0: устанавливать высокое значение не требуется;
uint32 clear_mask : установка низкого логического значения; 1: установить низкое значение; 0: устанавливать низкое значение не требуется;
uint32 enable_mask : включить заданный GPIO на выход
uint32 disable_mask : включить заданный GPIO на вход.Возвращаемое значение отсутствует Примеры gpio_output_set(BIT12, 0, BIT12, 0) : Установить на GPIO12 высокий уровень.
gpio_output_set(0, BIT12, BIT12, 0) : Установить на GPIO12 низкий уровень.
gpio_output_set(BIT12, BIT13, BIT12|BIT13, 0) : Установить на GPIO12 высокий уровен, а на GPIO13 низкий уровень.
gpio_output_set(0, 0, 0, BIT12) : Установить на GPIO12 режим цифрового входа.ESP8266 Non-OS SDK API Reference
Макросы для управления режимом GPIO:
GPIO input and output macros GPIO_OUTPUT_SET(gpio_no, bit_value) Переводит GPIO в режим выхода. GPIO_DIS_OUTPUT(gpio_no) Переводит GPIO в режим входа. GPIO_INPUT_GET(gpio_no) Возвращает логический уровень GPIO находящегося в режиме входа. ESP8266 Non-OS SDK API Reference
Макросы для управления внешними прерываниями:
GPIO interrupt ETS_GPIO_INTR_ATTACH(func, arg) Установить обработчик прерывания ETS_GPIO_INTR_DISABLE() Отключить внешнее прерывание. ETS_GPIO_INTR_ENABLE() Включить внешнее прерывание. ESP8266 Non-OS SDK API Reference
Функция для установки режима внешнего прерывания:
Функция gpio_pin_intr_state_set
Назначение функции установка условия срабатывания внешнего прерывания: по нарастающему фронту, по падающему и т.д. Прототип void gpio_pin_intr_state_set(
uint32 i,
GPIO_INT_TYPE intr_state
)Параметры uint32 i : ID пина. Если вы хотите указать к примеру GPIO14, то пожалуйста тспользуйте: GPIO_ID_PIN(14);
GPIO_INT_TYPE intr_state : условие срабатывания внешнего прерывания:
typedef enum <
GPIO_PIN_INTR_DISABLE = 0,
GPIO_PIN_INTR_POSEDGE = 1,
GPIO_PIN_INTR_NEGEDGE = 2,
GPIO_PIN_INTR_ANYEDGE = 3,
GPIO_PIN_INTR_LOLEVEL = 4,
GPIO_PIN_INTR_HILEVEL = 5
> GPIO_INT_TYPE;Возвращаемое значение отсутствует ESP8266 Non-OS SDK API Reference
Также могут быть интересны функции настойки пробуждения из спящего режима с помощью внешнего прерывания:
Но должен обратить внимание, что они могут работать только в режиме энергосбережения LIGT_SLEEP. Из deep_sleep esp8266 может разбудить только таймер или кнопка Reset.
4) Краткое описание ассемблера Xtensa
На сайте я уже рассматривал ассемблер следующих архитектур: AVR, MSP430, STM8, ARM. Сейчас я предлагаю вкратце познакомиться с ассемблером Xtensa, а точнее, с его вариантом для чипа ESP8266. Как обычно, я ни в коей мере не предлагаю писать прошивки целиком на ассемблере, но считаю, что знание ассемблера целевой архитектуры как минимум полезно. Так же как и в предыдущих случаях, я предлагаю начать знакомство с ассемблером Xtensa через дизассемблирование прошивки.
Прошивка складывается из собственного кода и кода закрытых библиотек SDK, которые линкуются вместе с нашим объектным модулем main.o. Чтобы не выискивать свой код по всей прошивке, предлагаю просто дизассемблировать объектный модуль main.o. Для этого в консоли выполняем команду: «xtensa-lx106-elf-objdump -S main.o», в результате чего получим следующий листинг:
здесь секция literal — это зарезервированная память в ОЗУ или IRAM, если угодно. Это память глобальных переменных. На ассемблерные инструкции в этой секции не стоит обращать внимания. Секция text — это сама программа которая располагается во флеш-памяти, и именно она будет нас интересовать.
Основным документом описывающим архитектуру Xtensa является Xtensa Instruction Set Architecture (ISA). Однако, в виду того, что процессоры Xtensa конфигурируемые, а указанный документ описывает общую архитектуру Xtensa, то приходится делать поправки с учётом спецификации esp8266. К счастью, на настоящее время, архитектура esp8266 уже довольно изученная. Хороший обзор основных фишек на английском можно почитать например здесь: http://cholla.mmto.org/esp8266/xtensa.html. Далее я предлагаю вольный перевод этого обзора:
- ассемблерные инструкции esp8266 имеют 24-битную ширину, но многие инструкции имеют сокращённые 16-битные аналоги. Сокращённые аналоги имеют добавочное «.n» в суффиксе мнемоники команды.
- esp8266 имеет 16 регистров общего назначения (РОН), которые обозначаются как a0, . a15. Сокращение «a» происходит от «address registers». Регистры равноправные, т.е. здесь нет деления на старшие и младшие как в AVR или Thumb/Thumb2.
- Кроме регистров общего назначения, так же имеется регистр прерываний PS, 8-битный сдвиговый регистр ASR, регистр счётчик команд PC.
- Регистра указателя стека нет, регистра состояния также нет.
- Имеются специальные регистры, доступ к которым осуществляется через инструкции wsr и rsr
- Gcc использует регистры: a0 в качестве регистра хранения адреса возврата, а1 — в качестве указателя стека, а2 — в качестве регистра передачи и возврата параметра в функцию.
- Часто используется три РОН в качестве операндов. Например: and a5, a4, a3, будет означать a5=(a4 and a3).
- Вызов подпрограмм и переходы походят на таковые в ARM/Thumb. По инструкции «j» осуществляется безусловный переход по относительному адресу. По инструкции «jx» осуществляется безусловный переход по адресу хранящемуся в регистре. По инструкции «call0» осуществляется вызов подпрограммы по относительному адресу. По инструкции «callx0» осуществляется вызов подпрограммы по адресу хранящемуся в регистре. Инструкция «ret» или «ret.n» осуществляет возврат из подпрограммы и эквивалента «jx a0».
- Суффиксы в мнемонике команд указывают на режим адресации: «i» — означает непосредственную адресацию (immediate). «r» — означает относительную адресацию.
- Суффикс «u» означает, что число в операнде является беззнаковым.
- Загрузка и сохранение 32-битного числа осуществляется инструкциями l32 и s32. Число может быть загружено или сохранено с помощью относительной адресации инструкциями l32r и s32r, либо с помощью индексной адресации. Например: «l32i.n a3, a2, 0», «s32i.n a3, a2, 1». В первом случае в регистр а3 загружается 32-битное число содержащееся по адресу хранящемуся в регистре a2. Во втором случае в ячейку памяти по адресу хранящемуся в регистре a2 плюс один байт сохраняется число из регистра а3.
- Возможно также загрузка и сохранение 16-и и 8-битного числа. Например инструкция l8ui загрузит 8-битное беззнаковое число. Для загрузки 16-битного числа имеются инструкции l16si и l16ui, которые загружают знаковое или беззнаковое число.
- Инструкция mov может работать между регистрами, или может загрузить в регистр небольшое число. Инструкция mov в суффиксе может содержать условие своего выполнения. Например:
5) Подключение JTAG отладчика на FT232H чипе
FT232H Board
Когда я в последний раз писал о STM32, то упоминал о недорогой плате с чипом FT232H, который может служить одноканальным JTAG отладчиком: «Отладка с помощью JTAG адаптера на чипе FT232H». Я понимаю, что тогда это привлекло мало внимания, т.к. китайские клоны ST-Link выглядят несколько привлекательнее, и ещё один отладчик, это уже не так интересно.
Однако в случае esp8266 у нас нет альтернативы JTAG, и данная плата — это самый дешёвый вариант. Проект добавления в OpenOCD поддержки eps8266 начался когда-то с этой темы: PRELIMINARY OPENOCD JTAG DEBUGGER SUPPORT FOR XTENSA/ESP8266, страница проекта: https://github.com/projectgus/openocd.
Проект в данное время заброшен, но ребята из VisualGDB сделали форк: https://github.com/sysprogs/esp8266-openocd, в котором была заявлена стабильная работа, им я и предлагаю воспользоваться.
Я собирал проект компилятором gcc-5.3.0. Для корректной сборки пришлось править права на исполняемые файлы, а также в одном заголовочном файле пришлось поправить директиву препроцессора. Преодолев все трудности, я таки заполучил OpenOCD c поддержкой esp8266.
Теперь про подключение esp8266 к JTAG адаптеру. Мне бы не хотелось повторять материал написанный ранее в «Отладка с помощью JTAG адаптера на чипе FT232H», поэтому буду предполагать, что с основами вы уже знакомы.
Для подключения JTAG отладчика будем использовать пины AD0/TCK, AD1/TDI, AD2/TDO, AD3/TMS и пин «земли»:
Для справки, в руководстве на чип можно найти полную распиновку чипа ft232h:
Со стороны esp8266 нам понадобятся следующие пины:
Для подключения JTAG адаптера к NodeMCU нужно соединить «землю» адаптера и «землю» платы NodeMCU. После этого подсоединяем: AD0 к GPIO13/D7, AD1 к GPIO12/D6, AD2 к GPIO15/D8, AD3 к GPIO14/D5. При подключении будем использовать независимое питание на NodeMCU и для JTAG адаптера. Адаптер нужно будет подключить в USB порт компьютера, а NodeMCU можно подключить к компьютеру или зарядке с microUSB.
Я должен предупредить о подводных камнях. В случае подключения JTAG-адаптера и платы NodeMCU к одному компьютеру, подключать следует сначала плату NodeMCU и только потом JTAG-адаптер. Предположим, что у нас в esp8266 залита прошивка мигалки базового проекта. При подключении NodeMCU к компьютеру, на плате начнёт мигать светодиод. Далее, при последующем подключении к компьютеру JTAG-адаптера, светодиод продолжит мигать. И мы будем использовать именно такой порядок подключения.
Если же сделать наоборот, т.е. сначала подключить к компьютеру JTAG-адаптер и только потом NodeMCU, то светодиод мигать не будет, даже если нажать RESET на плате. Такой вариант корректно работать не будет.
Разобравшись с подключением, создадим скрипт для JTAG адаптера:
Назовём его «interface.cfg». Теперь запускаем OpenOCD командой:
В логе получим следующий вывод:
В другом окне запускаем отладчик:
Подключаемся к OpenOCD:
После этого даём команду OpenOCD
В ответ мы должны получить сообщение:
После чего мигание светодиода должно прекратиться, т.к. процессор ESP8266 был остановлен на адресе 0x401000c5.
Командой load загружаем прошивку:
В итоге мы оказываемся в начале выполнения прошиивки:
Если мы поставим точку останова, скажем на функцию dummy_loop, то трассировки у нас не получится, т.к. мы постоянно будем вываливаться в обработчик прерывания watchdog’а.
Вводим команду monitor help и получаем список доступных команд OpenOCD. Нас наиболее будут интересовать эти команды:
Команда esp8266.cpu xtensa_no_interrupts_during_steps enable запретит выполнение прерываний во время трассировки программы.
Теперь ставим точку останова:
И запускаем прошивку на выполнение командой: «continue» или её сокращением: «c«. Далее отладка выполняется обычным образом.
6) Работа с GPIO16 через NonOS-SDK, подключение библиотеки «Driver_Lib»
Теперь, если мы захотим помигать не тем светодиодом, который расположен на модуле ESP12, а тем, что подключен к D0/GPIO16, то на нам придется научится работать с GPIO16.
GPIO16 занимает особое положение в ESP8266, так как относится к подсистеме RTC. Через GPIO16 происходит пробуждение из спящего режима, поэтому управляется он немного по другому. Подсказка, о том, как это делается, была найдена здесь: GPIO16 and setting RST line — ESP8266 Developer Zone.
Пример который у меня впервые заработал, выглядел так:
Это не совсем корректный пример, он работает по тому принципу, что и неисправные часы, которые дважды в день показывают правильное время. Что же здесь происходит?
Первым делом, «мапится» вывод XPD_DCDC (он же GPIO16) в качестве пина ввода-вывода:
Затем GPIO16 переводится в push-pull режим:
В главном цикле, вместо задания GPIO16 высокого или низкого логического уровня, попросту GPIO16 отключается или включается:
Программа некорректная, но создает иллюзию обратного. Что можно сказать по этому коду? Здесь мы имеем сплошное использование макросов, непонятных регистров: PAD_XPD_DCDC_CONF, RTC_GPIO_ENABLE, «магических чисел» навроде: 0xffffffbc или 0xfffffffe.
Определения макросов можно найди в заголовочном файле eagle_soc.h:
Там же можно найти регистры RTC модуля:
Но вот описания этих регистров, вы нигде не найдете.
Корректный вариант программы с последовательным переводом GPIO16 в высокое и низкое логическое значение, выглядит так:
Проблема этого примера в том, что здесь все делается через регистры. Кроме того, по этим регистрам нет никакой документации. Это совсем не то что обещалось в начале: «Данное API позволяет программировать на более высоком уровне не вдаваясь в особенности архитектуры ESP8266». На самом деле, в составе SDK имеется библиотека «driver_lib» c открытыми исходными кодам для работы с GPIO16 и другой периферией. Ее структура выглядит так:
Для примера, файл gpio16.c имеет следующее содержание:
Т.е. весь нужный функционал для работы с GPIO16 в нем уже есть.
Нам потребуется внести изменения в Makefile, чтобы добавить библиотеку в проект:
Тогда программа с использование этой библиотеки будет выглядеть так:
Ну, это уже кое-что.
7) Вывод через UART
В ESP8266 имеется два UART интерфейса: UART0 и UART1. UART0 имеет два независимых fifo буфера размером в 128 байт, по опустошению которого или по переполнению, можно вызывать прерывание. К UART0 подключен UART-USB адаптер CP2102 и он является основным интерфейсом. UART1 имеет только одну линию для передачи, он является отладочным интерфесом:
Т.к. к USB у нас подключен UART0, его и попытаемся задействовать. В ESP8266 Non-OS SDK API Reference описаны три функции для работы UART. Первая функция инициализирует UART интерфейс:
Функция uart_init
Назначение функции Устанавливает скорость работы обоих UART интерфейсов Прототип void uart_init (UartBautRateuart0_br, UartBautRate uart1_br) Параметры UartBautRate uart0_br : устанавливаемый битрейт интерфейса uart0;
UartBautRate uart1_br : устанавливаемый битрейт интерфейса uart1;Возможные значения битрейта typedef enum < BIT_RATE_9600 = 9600, BIT_RATE_19200 = 19200, BIT_RATE_38400 = 38400, BIT_RATE_57600 = 57600, BIT_RATE_74880 = 74880, BIT_RATE_115200 = 115200, BIT_RATE_230400 = 230400, BIT_RATE_460800 = 460800, BIT_RATE_921600 = 921600 >UartBautRate; Возвращаемое значение отсутствует. ESP8266 Non-OS SDK API Reference
Следующая функция позволяет переслать содержимое буфера через UART0:
uart0_tx_buffer Назначение Пересылает содержимое буфера через UART0 Прототип void uart0_tx_buffer(uint8 *buf, uint16 len) Параметры uint8 *buf : указатель на буфер, содержимое которого следует переправить через UART0;
uint16 len : длина буфера.Возвращаемое значение отсутствует. ESP8266 Non-OS SDK API Reference
Третья функция может содержать обработчик прерывания интерфейса UART0:
uart0_rx_intr_handler Назначение позволяет установить обработчик прерывания для входящих данных Прототип void uart0_rx_intr_handler(void *para) Параметры void *para: : указатель на RcvMsgBuff структуру. ESP8266 Non-OS SDK API Reference
Остальные функции объявлены в заголовочном файле «driver_lib/include/driver/uart.h«, и никак не документированы:
Но это еще не все неприятности. Дело в том, что просто так добавить в проект файл «driver_lib/driver/uart.c» не получится. В Espressif «забыли» добавить в SDK прототипы функций которые нужны для компиляции модуля. И при попытке компиляции выдаст ошибку об отсутствии объявлений функций ets_isr_mask() и ets_isr_unmask().
Проблема широко известная и легко гуглится, так же как и файл с прототипами недостающих функций. Я взял вариант такого файла у Михаила Григорьева https://github.com/CHERTS/esp8266-devkit/blob/master/Espressif/examples/ESP8266/esphttpd/libesphttpd/include/espmissingincludes.h, закоментировал в нем лишнее для меня пока функции, и положил его в директорию inc текущего проекта.
Т.о. содержимое файла «inc/espmissingincludes.h» получилось таким:
Объявление этого файла нужно будет добавить в «driver_lib/include/driver/uart.h«. Также, модуль uart.o нужно будет добавить в Makefile:
Осталось дело за малым, написать тестовую программу:
Также полезной функцией может os_printf(). C ее помощью можно вывести различную отладочную информацию. Например, составим такую программу: