(как работает, что содержит, куда пользоваться)
Библиотека предназначена для минимизации конечного кода скетчей. Разработка ещё "в процессе" (времени не так много), тестировано только то, что использовано в примерах.
Пожелания по разработке - принимаются на почту, но реализация только "по мере наличия времени".
По большей части сохранены типовые названия функций от Wiring или заменены макросами. Если чего-то "нехватает" и оно нужно из определений Wiring, буду добавлять постепенно, по мере выявления и необходимости.
- Ардуино ИДЕ, перед компиляцией каждого скетча, производит его преобразование: ПОСЛЕ всех директив препроцессора, если они в нем есть, добавляет директиву подключения заголовочного файла Arduino.h. Кроме этого, как и положено в нем самом, стоит защита от повторного включения файла.
Этим и пользуемся: В начале файла arhat.h имитируем "как будто бы" этот файл (Arduino.h), УЖЕ был запущен и подключен.
- Полностью заменен главный файл Wiring - wiring.c, поскольку основное его "достоинство" - создание таймера времени и функция millis() - работают "практически" некорректно. Миллисекунды тикают "скачками". Кроме этого, начальная инициализация "всего" - есть безусловное "зло" для целей минимизации итогового размера.
Написана собственная реализация wiring.c, по большей части в виде ассемблерных вставок. Поскольку, теперь функция millis() считает "корректно но медленно", упрощен обработчик прерываний и снижены требования к хранению переменных. К сожалению, это иногда требует ручного отключения файла wiring.c путем его переименования.
-
Передача параметров. Как выяснилось, основное увеличение типовых скетчей в размерах, особенно хоть сколько-то "сложных" связано с размером передаваемых параметров в имеющиеся функции. Соответственно, реализованы (и будут добавляться) функции с укороченными типами передаваемых параметров: uint8_t и uint16_t вместо uint32_t. Часть функций Wiring уже переименована именно в такие версии. В частности delay().
-
Сохранена возможность переопределения деталей реализации конкретной машинки и её микроконтроллера.
Вся реализация, в настоящее время выполнена и частично тестирована под машинку Arduino Mega2560 USB. Тем не менее, расширение использования и портирование библиотеки на другие устройства не должно представлять "трудностеЙ": Все описания, связанные с машинкой вынесены в отдельный файл: arhat_pins2560.h. Его подключение делается в основной файле с использованием стандартных констант определения "какой процессор конкретно сейчас компиляется оболочкой". Можно легко добавить/заменить на собственный набор определений.
5.1. upd-20151120: Добавлена "распальцовка" для Arduino UNO на базе ATmega328P, также разрешено использование для процессоров совместимых и определённых в одном datasheet: ATmega48P, ATmega88P, ATmega168P.
Принято следующие правила: а) по возможности сохраняется нумерация контактов на платах Ардуино, начиная с 0; б) нумерация аналоговых входов продолжает нумерацию остальных контактов платы; в) макроорпеделения пинов по специальным функциям сохраняют сквозную нумерацию пинов.
Как следствие, традиционное макроопределение Analog3 тут имеет номер 57 для Ардуино Мега2560. .. и все остальные специальные названия контактов возвращают сквозной номер пина.
а) Типовые определения констант. Собрано из разных мест, по возможности то, что наиболее часто требуется: а1) Битовые: SET_MASK_x CLR_MASK_x, Bxxxxxxxx - "удобства для". Используются в самой библиотеке для указания конкретного бита; а2) Типовые Wiring: HIGH,LOW,INPUT,OUTPUT ... - "совместимости для". Часто указаны в скетчах, определены и тут; а3) Математика: PI, HALF_PI, ... - дабы не вспоминать "сколько это". Дополнены константами преобразования времени звука в расстояние; а4) Константы из Cyberlib - для совместимости с этой библиотекой.
б) макро-функции:
Особенности использования: НЕ ВСЕ макроопределения (особенно для работы с битами и портами напрямую) позволяют корректное преобразование для номеров ног, указанных в оперативной памяти, через переменные! Особенно те, которые в своем "нутре" имеют команду препроцессора "##" (слипание строки для создания имени макроса).
Это важно помнить. Ниже, такие макросы специально помечены в скобках: (##)
б1) "общие", "удобные" - взяты из разных библиотек. Это в частности маркосы для работы с битами "напрямую": cbi(sfr, bit), sbi(sfr, bit), bitRead(value, bit), bitSet(value, bit), bitClear(value, bit), bitWrite(value, bit, bitvalue)
названия говорящие, думаю "пояснений" не требуют.
б2) (##) "битовые для Cyberlib": D_In(p), D_Out(p), D_High(p), D_Low(p), D_Inv(p), D_Read(p)
аналогично, названия "говорящие", особенно тем кто пользуется этой библиотекой.
б3) "общеупотребительная математика и прочая ерунда" в основном взято из Wiring: min(a,b), max(a,b), constrain(amt,low,high), radians(deg), degrees(rad), sq(x), ChatToDec(char), DecToChar(dec)
б4) (##) "битовые, местные" для прямого управления ножками Ардуины: pinModeIn(), pinModeOut(), pinModePullup(), pinMode() - установка ножки в режим "на вывод", "на ввод" и "куда сказано". Основное назначение - функция setup();
б5) (##) "таймерные". Для прямого управления и настройки таймеров, а также для создания процедур обработки прерываний от таймеров стандартным макросом ISR(). Все они имеют префикс timer: timerCount(t) // получить/установить регистр счетчика таймер 8/16bit. Значения t:[0,2,[1,3,4,5]] timerControl(t,r) // получить/установить регистр управления таймером. Значения t:[0,2,[1,3,4,5]], r:[A,B[,C]] timerCompare(t,r) // получить/установить регистр сравнения канала таймера. t:[0,2,[1,3,4,5]], r:[A,B[,C]] timerCapture(t) // получить/установить регистр захвата таймера (только для 16-битных!) p:[1,3,4,5] timerIFlag(t,v) // получить флаг прерывания от таймера. t:[0,2[,1,3,4,5]] v:[OVF,COMPA,COMPB[,COMPC,CAPT]] timerIMask(t,v,b) // установить бит маски прерывания таймера: b==1: set | b==0: clear, остальное - выше. ISRtimer(t,v) // приготовить название вектора прерываний таймера для ISR() prescalerMode(p) // получить код для битов прескалера по макро-константе режима работы (arhat_pins2560.h)
** Здесь, в arhat.h и ниже: В каждой строке описания функции указаны допустимые значения параметров в квадратных скобках через запятую. Если скобки двойные, то внутри указана дополнительная разновидность/расширение набора значений. Так например, выше: [1,2,[3,4,5]] - согласно предыдущему тексту "таймер 8/16bit", означает: номер 8бит-таймера 1 или 2 и 16бит-таймера 3,4 или 5. Соответственно, такие же двойные скобки перечисляют допустимый набор параметеров для второго (и т.д.) параметра, если он есть. К примеру, для timerCompare(t,r) можно указать канал A или B если таймер 8-и битный (1 или 2) И можно ещё указать канал C, для остальных, 16-и битных таймеров. Поскольку макросы создают ИМЕНА типовых макросов avr-gcclib, то значения надо указывать ровно так, как они описаны в строке. Это не имена других макросов или чего ещё. Часто это часть формируемого имени! Так например макрос ISRtimer(3,CAPT) создает имя типового макроса, используя указанные параметры как его часть, в результате в код подставляется текст: TIMER3_CAPT_vect, что для файла interrupt.h является типовым названием обработчика соотвествующего прерывания.
б6) (##) "ШИМ" (PWM). Для "аналогового" вывода на ножки PWM2..13 и PWM44..46: pwmGetTime(p) // получить номер таймера по номеру пина PWM. Оказалось полезно. pwmGetChannel(p) // получить букву канала таймера по номеру пина PWM. pwmPinMode(p,m) // получить режим для его установки по номеру пина и режиму: p[2..13,44,45,46,T1C], m[PWM_DISABLE,PWM_TOGGLE,PWM_NORMAL,PWM_INVERSE] pwmSet(p) // Включить режим ШИМ для ноги платы (настройка "по умолчанию": FAST-PWM, 1/64 (250kHz), PWM_NORMAL. pwmOff(p) // Выключить режим ШИМ. Любой. pwmWrite(p,v) // Установить ширину импульса ШИМ: номер ноги - выше, "яркость"(v):[0..255] pwmSetServo(p) // Установить ШИМ "Серводвигатель" (50Гц) и пин в сервовывод (без pinMode()!). Только для 16-битных таймеров! timerSetServo(t,pm) // Тотже ШИМ "Серводвигатель", но по номеру таймера. Принимает вторым параметром маску какие каналы будут использованы. // Позволяет установить ШИМ одновременно на всех заданных каналах таймера и сам таймер в режим. Смотрите примеры
** примечания: Если требуется использовать таймер 1, канал С (13 ножка!), то его номер пина надо указывать как T1C. Иначе, для 13-ноги, будет задействован таймер 0, канал А. Они совмещены в ATmega2560 на одном выводе для возможности аппаратной реализации двойного ШИМ (подача пачки импульсов заданной скважности с заданной периодичностью);
б7) (##) "АЦП" (ADC). Для работы с аналоговым вводом с ножек Analog0..15. Номера ножек тыт с 0 по 15!: adcOn() // Включить АЦП и установить делитель: 16Mhz/128 (125kHz) adcOff() // Выключить АЦП. Позволяет экономить батарейку, если не используется АЦП. admuxSrc(s) // получить маску источника оцифровки s:[AREF,AVCC,110,256] для установки далее. admux1Channel(src,pin,adlar) // Типовой режим АЦП: оцифровка с одной ноги по источнику и со сдвигом результата. admux2Channel(src,neg,poz,adlar) // Дифференциальный режим оцифровки с двух ножек по источнику. admux2Gain(src,neg,poz,g,adlar) // Дифференциальный режим с предусилителем сигнала (х10, х200).
** примечания: Последние 2 макроса работают далеко не для всех сочетаний ножек! Ну не умеет микроконтроллер.. смотреть описание макросов в самом файле. Там есть все ограничения!
Выделены отдельно макросы включения и выключения работы АЦП блока. Он оказывается жрет существенно. По умолчанию изначально - выключен. Надо помнить, что желательно, чтобы после включения блока и ДО первой оцифровки сигнала проходило 108 микросекунд для его "прогрева и запуска"... иначе, первая оцифровка будет НЕВЕРНА. Нормальное применение макросов - функция setup().
Функция чтения analogRead() в местной реализации предполагает, что все настройки сделаны "ДО" её использования.
В папке examples есть тестовый пример "как этим пользоваться": analogRead.ino
everyMillis() -- макрос для работы с задержками БЕЗ использования delay(). Хорошо и популярно описан во многих местах под разными названиями. Для удешевления работы дополнен макросом everyOvfCount() - делает тоже самое, но на основе счетчика переполнений таймера и соответственно работает в "тиках" по 1024 микросекунды
Нижеследующие функции заменены МАКРОСАМИ (ножку платы указывать числовой константой! Не переменная в памяти!):
pinMode(p,m), digitalRead(p), digitalWrite(p,v), turnOffPWM(t), analogWrite(p,v)
Ниже следующие функции изменены: millis() -> time_millis() micros() -> time_micros() delay() -> time_delay16(ms) -- использует 16-и битный параметр! Сокращает вызовы на 6 байт каждый, но считает только до 65.5секунд..
Примечание: возвращен выбор режимо компиляции скетчей константой ARHAT_MODE. В режиме совместимости все типовые имена функций
не переименовываются и вызываются из Wiring.
************ АВТОМАТНОЕ ПРОГРАММИРОВАНИЕ ************
В комплект библиотеки добавлены файлы tsc.h и tsc.c и примеры в папку examples, имеющие префикс TSC_ в названии.
В целом, там все достаточно просто и можно разобраться по примерам. Каждый "блок","устройство" представляет собой "конечный автомат" (КА) и работает "независимо" от других КА. Каждый КА имеет набор "состояний" и "функций" перехода между ними и "интервал времени ожидания". Для наглядности, управление КА вынесено в отдельную таблицу, в которой указывается интервал времени ожидания, команда и номера следующих команд. Табличку легко расширить и на более сложные виды КА, например для возвращающих значения текущей командой (true|false). порядок работы строки таблицы состояний КА:
-
При выборе номера строки (первоначально в tsc_init() или tc_next()) фиксируется начало интервала ожидания;
-
Завершение интервала ожидания происходит в tsc_step() или tsc_run();
-
По истечению интервала, tsc_step() устанавливает новое значение номера текущего состояния И фиксирует начало нового интервала;
-
И только потом вызывает выполнение команды. 3а. Функция tsc_run() по истечению интервала вызывает команду. И только. Переключение состояния, интервала и команды должно выполняться в текущей команде, вызываемой из tsc_run().
Это позволяет реализовать несколько разновидностей конечных автоматов:
-
Простой КА, логика управления которым сосредоточена в самих командах. Таблица состояний тут не требуется. Создается такой КА только структурой TSC_Control, в которую в setup() надо вручную прописать время начала первого интервала, длительность ожидания и первую исполняемую команду. В цикле loop() достаточно вызывать tsc_run() и, по истечению ожидания она вызовет указанную команду (функцию), которая должна установить "следующее действие" и "интервал ожидания". Повторно устанавливать стартовое время интервала уже не требуется.
-
КА, управляемый таблицей состояний" (к нему и примеры). Таблица состояний должна иметь префикс PROGMEM также, как это указано в примерах. Тогда её размещение не будет занимать оперативную память. Если не требуется логика переходов по false, то вторую колонку "next::false" можно заполнить нулями и команды в этом случае должны возвращать ненулевое значение.
Во всех КА допускается изменение следующего состояния в самих командах, исполняемых КА. Это возможно, поскольку tsc_step() СНАЧАЛА формирует данные о следующем состоянии КА, а вызов команды производит последним действием. Таким же образом можно управлять состоянием одного КА из действий другого.
**1 Для формирования КА, работающих с "двигателями" или иными "медленными" устройствами, крайне полезно выделять в отдельные состояния команды "запуска" действия с переходом на состояние "ожидания" его завершения, также как это показано в примере TSC_Servo.ino. В этом же примере показано изменение логики управления "сам себе мастер": при достижении крайнего положения серводвигателя, автомат переходит на обратное вращение.
**2 Для лучшего понимания "автоматного программирования" рекомендуется прочтение статьи в Википедии с таким названием.
В целом традиционна: распакуйте архив в каталоге ваших скетчей в подкаталог /libraries.
В результате, должна получиться следующая иерархия каталогов:
Arduino -- это мой каталог скетчей libraries -- подкаталог с библиотеками создается при установке, если нет - создайте сами. Arhat -- подкаталог с этой библиотекой arhat.h -- заголовочный файл для уменьшения размера скетчей и их ускорения. arhat.c -- файл реализации струткур, функций и методов arhat_pins2560.h -- файл "распиновки" Ардуино Мега2560. Макросы, преобразующие номер пина куда-то. arhat_pins***.h -- возможны и другие файлы распиновок. Делать по образцу! tsc.h -- заголовочный файл программирования конечных автоматов tsc.c -- файл реализации классов, структур, функций и методов автоматного программирования hcsr04.h -- template-файл библиотеки узв. датчика HCSR-04 keywords.txt -- файл подсветки синтаксиса help.txt -- краткое описание "чего и с чем" пользовать или не пользовать ... в разработке. ReadMe.txt -- этот файл. *** -- возможен ещё какой-нибудь мусор из отладки или недоделанного... examples -- подкаталог с примерами использования в скетчах.
Если у Вас при запуске скетчей из примеров появляется ошибка типа 'redefine __vector_23()', то Вам необходимо переименовать файл wiring.c, лежащий в папке Ардуино ...\hardware..\core. Где он конкретно лежит - зависит как от Вашей ОС, версии Ардуино ИДЕ, так и от метода установки (в Линукс). Для Линукс (Ubuntu), надо искать ту версию файла, на которую есть права для правки. Часто рабочая копия лежит в папке /usr/share/Arduino, а не в папке с программой! .. где-то работает нормально, а где-то глючит ... на Линуксах это зависит от способа установки Ардуино ИДЕ и её версии. Относительно устойчиво работает без переименования пока только в версии ИДЕ 1.6.4.
Библиотека для уменьшения и ускорения скетчей базируется на макросах, преобразующих типовые названия функций Wiring в одно-двухкомандные последовательности, за счет того, что номер используемого пина - указан или явной числовой константой (число) или используется макроопределение из файла arhat_pins***.h При указании номера пина в переменной - этот подход НЕ РАБОТАЕТ! Макрос развернется в код, склеивающий название переменной с текстом макроса и Вы получите сообщение о синтаксических ошибках и только. Такой подход имеет место быть, поскольку все пины Ардуино имеют жесткую привязку к своим функциям, особенно специальным и, соответственно, всегда явно известны при разработке скетчей. Желание "сделать файл настроек" - тут избыточно и имеет больше вреда, чем пользы.
Если Вы хотите использовать библиотеку с другим контроллером, то Вам надо создать собственный файл "распиновки" по образцу arhat_pins2560.h И добавить в условную компиляцию в файл "arhat.h" также по образцу в нем. Константу наименования вашего процессора смотрите в файле "io.h" компилятора (у меня это avr-gcc) или его дочерних файлах.
Блок "автоматного программирования" подключается отдельно #include "tsc.h" (название от Time State Controller - Автомат, управляемый состояниями во времени). Его файлы - библиотеки автоматов будут пополняться далее.
Совместимость библиотеки с кодом Wiring - далее НЕ ПОДДЕРЖИВАЕТСЯ за ненадобностью.