Всем привет. Меня зовут Никита. Я один из участников проекта «Философия Звука». Это моя первая статья на блоге - строго не судите. В комментариях можете поделиться своими размышлениями по теме. Это будет полезно как мне, так и всем остальным. Начнём).
Только «ленивый» не знает про такую разработку как «Arduino». Хотя как раз и «ленивые», в том числе, используют её в своих задачах. Существует много разных мнений по поводу «Arduino». Есть как сторонники, так и противники – классика жанра. К какому лагерю отношусь я? Сложно ответить однозначно. Могу лишь сказать, что «Arduino» мне нравится в том варианте, который использую именно я).
Вкратце расскажу из чего состоит платформа:
Имеется ПО Arduino, в котором пользователь пишет специальный
текст - код. Далее этот код преобразуется встроенным в ПО компилятором AVR GCC в другой специальный текст (машинный
язык). Также в ПО Arduino интегрирована консольная программка AVRDude для
прошивки микроконтроллеров AVR. С её помощью написанный нами программный код
отправляется в память микроконтроллера через USB порт ПК.
Со стороны «железа» Arduino есть, или приобретается отдельно, преобразователь USB-USART. Он служит мостом между программной и
аппаратной частью Arduino. Вообще, «чистый» контроллер AVR прошить по интерфейсу
USART нельзя. Поэтому изготовитель Arduino зашивает в микроконтроллер программу - «загрузчик» (пользователь тоже может это проделать). Вот теперь контроллер
принимает наш «машинный код» и сохраняет его у себя в памяти. Ну и
потом мы проверяем, что всё правильно работает или опять не работает).
Недостатки программирования в Arduino
Добрались до самого интересного - программирования). Эта статья не посвящена какому-то отдельному проекту. Она только подготовит тех, кто выберет тот же путь программирования в Arduino, что и я.
Казалось бы, что не нравится? Многие пишут в Arduino так, как задумали разработчики, и даже проекты рабочие и симпатичные выходят. Есть куча библиотек для различных подключаемых электронных устройств: датчики температуры, дисплеи, сервоприводы, GSM модули и многое другое. Но у этих библиотек есть недостатки, связанные со стилем программирования в Arduino:
Если коротко, то мне хотелось бы совместить лёгкую прошивку микроконтроллера AVR, как это сделано в Arduino IDE, и возможность написания «быстрого», «прозрачного», разбитого на файлы и занимающего мало места кода на C/C++ как, например, в IAR Embedded Workbench.
«Исправление» Arduino IDE
Мы используем 8-битные микроконтроллеры от AVR, поэтому будем работать с последней классической версией Arduino 1.0.6.
Программисты на C/C++ привыкли, что главной функцией является функция main(), но здесь её нет... или может быть она скрыта?
Копнём в папку установки Arduino. Вот здесь можно много чего интересного найти: C:\Program Files (x86)\Arduino-1.0.6\hardware\arduino\cores\arduino. Начнём, конечно же, с найденного там файла main.cpp. О чудо, вот она main() ... нет, так и должно было быть :)
Вот и нашлись наши знакомые void setup() и void loop(), которые нужно определять в каждом скетче. Эти функции мы перенесём в свой файл, например, main_my.cpp и там определим. Это позволит разбить программу на файлы.
Из интересного тут ещё функция init(). Её определение можно найти в файле wiring.c. Функция длинная, поэтому покажу только её начало:
Здесь глобально разрешаются прерывания. Происходит настройка и запуск всех таймеров, которые имеются в микроконтроллере. Да-да, всё, что связано с функциями задержек, ШИМ, отсчётов временных интервалов от старта контроллера, предварительно настраивается тут. Выбирается частота работы АЦП, и сразу же АЦП включается. И в самом конце сбрасываются настройки USART, который использовался для «загрузчика».
Получается, что все предварительные настройки, которые используют библиотеки Arduino, описаны в init(). Если мы уберём эту функцию, то сможем работать с регистрами и векторами прерываний микроконтроллера напрямую, не боясь, что они уже используются.
И что теперь будем делать? Править функцию main(), чтобы отключать ненужную нам работу таймеров или АЦП? А если хочется совмещать свой код, например, с уже готовой удобной библиотечной отладкой по USART? Да и не все пользователи Arduino оценят такой подход. Нужно такое решение, которое бы устроило всех, и оно есть:
Оставим всё как есть. И в нашем проекте, если это нужно, остановим всю периферию, которой не будем пользоваться с помощью библиотек Arduino или которая нам не нужна совсем.
Атрибуты
Кто-то спросит, а что там с прерываниями? Например, если остановим Timer 0, то как мы сможем использовать прерывание TIM0_OVF_vect, ведь этот вектор уже используется средой для формирования задержек, а компилятор обязательно будет ругаться на повторное применение? Выход такой: в компиляторе GCC существуют особые способы объявления функций, называются они атрибуты. Ключевое слово для использования __attribute__. В частности есть такой атрибут weak. Он делает функцию «слабой». Это значит, что если функция определена и объявлена как weak в одном месте, то в другом - мы можем написать её второе определение, и вызываться будет именно оно. А если не напишем, тогда будет использоваться определение с атрибутом weak.
Теперь по-простому: как сделать так, чтобы можно было у себя в программе, если это нужно, использовать TIM0_OVF_vect, но чтобы была возможность в другом проекте использовать функции Arduino? Нужно в исходниках Arduino в файле wiring.c (именно тут используется этот вектор) написать такую строчку:
Аналогично можно дообъявить другие уже использующиеся вектора. Да, нам придётся исправить исходные файлы среды Arduino. Но зато всего один раз, и потом мы «забудем » об этом :).
Универсальный скетч
Больше не буду забивать Вам голову умными мыслями), а покажу как теперь будет выглядеть скетч для всех проектов. Он поможет отвязаться от написания всей программы в одном окне Arduino IDE:
Файл main_my.h пустой и находится вместе со всеми файлами нашей программы. В файле main_my.cpp определены функции void setup() и void loop(). Собственно, дальше можно уже писать свою программу в любом удобном для Вас текстовом редакторе, например Notepad++. Добавлять столько *.cpp и *.h файлов, сколько нужно для проекта. Если необходимо, делать код ёмким и быстрым - без использования функций Arduino. Или, для быстроты реализации программы, совмещать свой код с кодом Arduino.
На этом всё :). Ну а в следующий раз напишем какую-нибудь очень полезную программу).
Недостатки программирования в Arduino
Казалось бы, что не нравится? Многие пишут в Arduino так, как задумали разработчики, и даже проекты рабочие и симпатичные выходят. Есть куча библиотек для различных подключаемых электронных устройств: датчики температуры, дисплеи, сервоприводы, GSM модули и многое другое. Но у этих библиотек есть недостатки, связанные со стилем программирования в Arduino:
- Из-за универсальности код в Arduino IDE выполняется медленно и занимает много места в памяти микроконтроллера - критично для «насыщенных» проектов.
- Даже в библиотеках используются задержки, во время которых контроллер ничего не делает - неэффективное использование ресурсов.
- Весь код программы приходится писать в одной странице Arduino IDE, что затрудняет написание, чтение и редактирование большого проекта.
- Нет «прозрачности» написания программ. Мы не видим, что скрывается за скетчем - не знаем, какие регистры используются, какие прерывания выполняются без нашего ведома. Поэтому не можем напрямую работать с регистрами и прерываниями.
Хочется устранить недостатки и расширить возможности платформы Arduino. К лёгкой прошивке по USB кабелю добавить возможность написания программы с разбиением на файлы. Использовать полноценное программирование микроконтроллера, с применением регистров и векторов прерываний.
Если коротко, то мне хотелось бы совместить лёгкую прошивку микроконтроллера AVR, как это сделано в Arduino IDE, и возможность написания «быстрого», «прозрачного», разбитого на файлы и занимающего мало места кода на C/C++ как, например, в IAR Embedded Workbench.
«Исправление» Arduino IDE
Мы используем 8-битные микроконтроллеры от AVR, поэтому будем работать с последней классической версией Arduino 1.0.6.
Программисты на C/C++ привыкли, что главной функцией является функция main(), но здесь её нет... или может быть она скрыта?
Копнём в папку установки Arduino. Вот здесь можно много чего интересного найти: C:\Program Files (x86)\Arduino-1.0.6\hardware\arduino\cores\arduino. Начнём, конечно же, с найденного там файла main.cpp. О чудо, вот она main() ... нет, так и должно было быть :)
Вот и нашлись наши знакомые void setup() и void loop(), которые нужно определять в каждом скетче. Эти функции мы перенесём в свой файл, например, main_my.cpp и там определим. Это позволит разбить программу на файлы.
Из интересного тут ещё функция init(). Её определение можно найти в файле wiring.c. Функция длинная, поэтому покажу только её начало:
Здесь глобально разрешаются прерывания. Происходит настройка и запуск всех таймеров, которые имеются в микроконтроллере. Да-да, всё, что связано с функциями задержек, ШИМ, отсчётов временных интервалов от старта контроллера, предварительно настраивается тут. Выбирается частота работы АЦП, и сразу же АЦП включается. И в самом конце сбрасываются настройки USART, который использовался для «загрузчика».
Получается, что все предварительные настройки, которые используют библиотеки Arduino, описаны в init(). Если мы уберём эту функцию, то сможем работать с регистрами и векторами прерываний микроконтроллера напрямую, не боясь, что они уже используются.
И что теперь будем делать? Править функцию main(), чтобы отключать ненужную нам работу таймеров или АЦП? А если хочется совмещать свой код, например, с уже готовой удобной библиотечной отладкой по USART? Да и не все пользователи Arduino оценят такой подход. Нужно такое решение, которое бы устроило всех, и оно есть:
Оставим всё как есть. И в нашем проекте, если это нужно, остановим всю периферию, которой не будем пользоваться с помощью библиотек Arduino или которая нам не нужна совсем.
Атрибуты
Кто-то спросит, а что там с прерываниями? Например, если остановим Timer 0, то как мы сможем использовать прерывание TIM0_OVF_vect, ведь этот вектор уже используется средой для формирования задержек, а компилятор обязательно будет ругаться на повторное применение? Выход такой: в компиляторе GCC существуют особые способы объявления функций, называются они атрибуты. Ключевое слово для использования __attribute__. В частности есть такой атрибут weak. Он делает функцию «слабой». Это значит, что если функция определена и объявлена как weak в одном месте, то в другом - мы можем написать её второе определение, и вызываться будет именно оно. А если не напишем, тогда будет использоваться определение с атрибутом weak.
Теперь по-простому: как сделать так, чтобы можно было у себя в программе, если это нужно, использовать TIM0_OVF_vect, но чтобы была возможность в другом проекте использовать функции Arduino? Нужно в исходниках Arduino в файле wiring.c (именно тут используется этот вектор) написать такую строчку:
ISR(TIMER0_OVF_vect) __attribute__((weak));
Выглядеть эта доработка будет так:
Аналогично можно дообъявить другие уже использующиеся вектора. Да, нам придётся исправить исходные файлы среды Arduino. Но зато всего один раз, и потом мы «забудем » об этом :).
Универсальный скетч
Больше не буду забивать Вам голову умными мыслями), а покажу как теперь будет выглядеть скетч для всех проектов. Он поможет отвязаться от написания всей программы в одном окне Arduino IDE:
Файл main_my.h пустой и находится вместе со всеми файлами нашей программы. В файле main_my.cpp определены функции void setup() и void loop(). Собственно, дальше можно уже писать свою программу в любом удобном для Вас текстовом редакторе, например Notepad++. Добавлять столько *.cpp и *.h файлов, сколько нужно для проекта. Если необходимо, делать код ёмким и быстрым - без использования функций Arduino. Или, для быстроты реализации программы, совмещать свой код с кодом Arduino.
На этом всё :). Ну а в следующий раз напишем какую-нибудь очень полезную программу).
С уважением, Никита О.
а где продолжение?
ОтветитьУдалить