четверг, 31 марта 2016 г.

Ардуино. Альтернативное начало.


Arduino Pro Mini и преобразователь USB-USART


Всем привет. Меня зовут Никита. Я один из участников проекта «Философия Звука». Это моя первая статья на блоге - строго не судите. В комментариях можете поделиться своими размышлениями по теме. Это будет полезно как мне, так и всем остальным. Начнём).

Arduino

Только «ленивый» не знает про такую разработку как «Arduino». Хотя как раз и «ленивые», в том числе, используют её в своих задачах. Существует много разных мнений по поводу «Arduino». Есть как сторонники, так и противники – классика жанра. К какому лагерю отношусь я? Сложно ответить однозначно. Могу лишь сказать, что «Arduino» мне нравится в том варианте, который использую именно я).

Вкратце расскажу из чего состоит платформа:

Схема работы Arduino

Имеется ПО Arduino, в котором пользователь пишет специальный текст - код. Далее этот код преобразуется встроенным в ПО компилятором AVR GCC в другой специальный текст (машинный язык). Также в ПО Arduino интегрирована консольная программка AVRDude для прошивки микроконтроллеров AVR. С её помощью написанный нами программный код отправляется в память микроконтроллера через USB порт ПК.

Со стороны «железа» Arduino есть, или приобретается отдельно, преобразователь USB-USART. Он служит мостом между программной и аппаратной частью Arduino. Вообще, «чистый» контроллер AVR прошить по интерфейсу USART нельзя. Поэтому изготовитель 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() ... нет, так и должно было быть :)


Файл main.cpp в Arduino IDE

Вот и нашлись наши знакомые void setup() и void loop(), которые нужно определять в каждом скетче. Эти функции мы перенесём в свой файл, например, main_my.cpp и там определим. Это позволит разбить программу на файлы. 

Из интересного тут ещё функция init(). Её определение можно найти в файле wiring.c. Функция длинная, поэтому покажу только её начало:


Стандартная инициализация периферии в Arduino

Здесь глобально разрешаются прерывания. Происходит настройка и запуск всех таймеров, которые имеются в микроконтроллере. Да-да, всё, что связано с функциями задержек, ШИМ, отсчётов временных интервалов от старта контроллера, предварительно настраивается тут. Выбирается частота работы АЦП, и сразу же АЦП включается. И в самом конце сбрасываются настройки USART, который использовался для «загрузчика».

Получается, что все предварительные настройки, которые используют библиотеки Arduino, описаны в init(). Если мы уберём эту функцию, то сможем работать с регистрами и векторами прерываний микроконтроллера напрямую, не боясь, что они уже используются.

И что теперь будем делать? Править функцию main(), чтобы отключать ненужную нам работу таймеров или АЦП? А если хочется совмещать свой код, например, с уже готовой удобной библиотечной отладкой по USART? Да и не все пользователи Arduino оценят такой подход. Нужно такое решение, которое бы устроило всех, и оно есть: 


Отключение периферии 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)); 

Выглядеть эта доработка будет так:

Перегрузка вектора прерывания TIM0_OVF_vect в Arduino IDE

Аналогично можно дообъявить другие уже использующиеся вектора. Да, нам придётся исправить исходные файлы среды Arduino. Но зато всего один раз, и потом мы «забудем » об этом :).

Универсальный скетч

Больше не буду забивать Вам голову умными мыслями), а покажу как теперь будет выглядеть скетч для всех проектов. Он поможет отвязаться от написания всей программы в одном окне Arduino IDE:


Универсальный скетч Arduino для всех программ

Файл main_my.h пустой и находится вместе со всеми файлами нашей программы. В файле main_my.cpp определены функции void setup() и void loop(). Собственно, дальше можно уже писать свою программу в любом удобном для Вас текстовом редакторе, например Notepad++. Добавлять столько *.cpp и *.h файлов, сколько нужно для проекта. Если необходимо, делать код ёмким и быстрым - без использования функций Arduino. Или, для быстроты реализации программы, совмещать свой код с кодом Arduino.

На этом всё :). Ну а в следующий раз напишем какую-нибудь очень полезную программу).


С уважением, Никита О.