Open neko-neko-nyan opened 5 years ago
Про циклический буфер надо почитать... А вот AVR/delay не впечатлило, работает криво, мы измеряли. Стандартные костыли работают лучше всего, аналогов не нашли. Boards txt полностью переписан в версии 1.6, но за инфу про флаги огромное спасибо! Переделаю
Циклический буфер реализован в ардуиновом serial. Только он там и на ввод, и на вывод. Это штука подходит по буфер ввода/вывода. У тебя в uartRead делается перемещение данных в буфере:
for (byte i = 0; i < _UART_RX_COUNTER; i++) _UART_RX_BUFFER[i] = _UART_RX_BUFFER[i + 1];
Получается, что при чтении одного символа происходит по 64 операции чтения и записи в памяти. Еще у тебя нет обработки отсутствия данных в буфере (по идее uartRead должна вернуть -1 если нет данных) и переполнения буфера (если отправить на ардуину больше 64 символов пока она занята чем-то другим). В циклическом буфере данные вообще не двигаются, только меняются индексы. Можешь померить скорость uartRead и Serial.read.
А хоть boards.txt переписан, та фигня с вариантами осталась. ИМХО держать 2 файла ради одного дефайна как-то слишком круто:)
Avrовский delay можно включать если выключен millis (все равно от стандартного delay не будет ни какого толку). Его вроде нельзя использовать с большими задержками (где-то в мануале к avrlibc было написано точно). Можно по этому поводу выводить предупреждение. Просто иногда хочется выключить millis (например, нужен таймер для шима или чего-то еще), но какая-то библиотека использует delay и после выключения мк просто зависает.
Всё верно, сейчас поковыряю. С циклическим буфером не работал, обычно работаю с таким не очень эффективным. А по скорости мой read всё равно быстрее =) Не нашёл способа ввести свой дефайн кроме variants, сейчас вроде разобрался. -D это команда для дефайна да? avr delay тогда воткну, вот это отличная идея реально!
build.extra_flags это флаги компилятора, добавляются во все команды компиляции. -D NAME аналогично #define NAME. Только стоит поменять название этого дефайна на что-нибудь вроде _GYVERCORE_NOMILLIS
чтобы избежать конфликтов с библиотеками. Еще я бы при отключении millis удалял функции millis и micros (обернуть в #ifdef вместе с isrом, и переменные тоже), в Arduino.h заменял их заголовки на #define millis() (0)
(аналогично для micros). Это должно сэкономить некоторое кол-во флеша и оптимизатор должен сильно ускорить циклы, использующие millis (превратив их в бесконечные).
Serial.read из ардуины все равно медленнее моего потому что там обращения к переменным через this. Твой быстрее потому что нет проверки пустого буфера. (Кстати, столько там разницы?) Вообще нет смысла держать два разных uartа, только больше места во флеше занимают. Если оптимизировать, то тот, что есть в ардуине (для совместимости с библиотеками).
А еще по поводу #1 . Так делают только библиотеки, идущие вместе с ядром (twi). Зачем - непонятно. Предположу, что для SDA и SCL. В принципе их тоже можно редактировать (а именно удалить #include <pins_arduino.h>
и перенести все из него в Arduino.h. Ну и потестить, конечно.)
Еще чисто декоративный момент. Надо будет как-нибудь повыравнивать нормально код, поменять табы на пробелы, определиться со стилем и все привести к нему (Где ставить {
, сколько пробелов на отступ, использовать ли bitSet/bitClear и т.д.), убрать все sbi/cbi (в avrlibc они помечены как устаревшие, но стандартное ядро ардуины их тупо копирует. И зачем?) и далее по мелочам. Ну это как-бы чисто для красоты:)
pins arduino изначально убрали, но некоторые либы (FastLED вроде) пытаются его инклудить.... трэш Насчёт кода - я пишу в notepad++, он реиндентит с табами, мне нравится. Часть кода писал Егорка, писал не знаю каким местом =) Его код даже не реиндентится. И он не юзает макросы bitSet и прочие, а я их люблю.. ну это ладно, ерунда Тут такой вопрос: отдельно сейчас GyverUART поковырял, попытался добавить кольцевой буфер ровно как в коммите. И что-то не работает, не посмотришь плиз? Там я закомментил свои куски кода, и сразу под ними - всё что к кольцевому относится GyverUART.zip При попытке поиграть в эхо сыпет в меня обратными вопросительными знаками
#include <GyverUART.h>
void setup() {
uartBegin();
}
void loop() {
if (uartAvailable()) {
uartWrite(uartRead());
}
}
Не компилировал, но предположу, что надо поменять rx_buffer_index_t
на uint8_t
или byte
.
Не компилировал, но предположу, что надо поменять
rx_buffer_index_t
наuint8_t
илиbyte
.
я изначально так и писал, потом вернул rx_buffer_index_t. Ничего не изменилось
А можно сообщения компилятора? Или как оно вообще не работает?
Я пишу в visual studio code с плагином c/cpp и настройками под avr-gcc.
Ошибок нет, просто вместо "эхо" выдаёт в порт обратные знаки вопроса. А должно быть эхо
Что-то со скоростью
так в том и дело, что заменил только то, что касается буфера. В прерывании и функциях чтения/проверки
А понял
char uartRead() {
if (_UART_RX_BUFFER_HEAD == _UART_RX_BUFFER_TAIL) return -1;
unsigned char c = _UART_RX_BUFFER[_UART_RX_BUFFER_TAIL];
_UART_RX_BUFFER_TAIL = (_UART_RX_BUFFER_TAIL + 1) % UART_RX_BUFFER_SIZE;
return c; // <= Добавить это
}
блин)))))))))))) вот это провал
Вот по этому надо добавить опции компилятора -Wall -Werror
(включить все предупреждения и считать любое предупреждение ошибкой). Где-то вроде в platform.txt это настраивалось. Только потом отключить, чтобы кривые библиотеки иногда работали:)
Всё работает, спасибо огромное! Теперь юарт быстрее ровно в 2 раза =) Вот это пушка.
Кстати, "очистку" буфера я правильно сделал? _UART_RX_BUFFER_HEAD = _UART_RX_BUFFER_TAIL;
Да, можно так, а можно и _UART_RX_BUFFER_HEAD = _UART_RX_BUFFER_TAIL = 0;
. А можно _UART_RX_BUFFER_TAIL = _UART_RX_BUFFER_HEAD;
. Можно собрать разные варианты и посмотреть дизассемблер на предмет количества инструкций:) Но это совсем дикие оптимизации
Окей. А такой вопрос: какие еще есть полезные команды у boards.txt и где про это почитать? Насколько вариативные конструкции можно делать вообще? А то сейчас играем с фьюзами, и пришлось создавать отдельный набор плат под разные фьюзы грубо говоря, байт то один....
Ну как сказать. Я про extra_flags понял чисто случайно. Сами команды компиляции и прошивки лежат в platform.txt, а именно recipe.X.Y.pattern
(X - расширение входного файла, а Y - выходного). Команды чем-то незримым похожи на makefile. В {}
подстановки и других файлов. А в boards.txt задаются варианты, которые в зависимости от выбора пользователя копируются с другим названием. Например:
nano.menu.clock.internal_8.bootloader.file=path/to/file
при выборе платы nano
и меню clock=internal_8 становится bootloader.file=path/to/file
. Дальше подстановки из platform.
Или
nanoOpti.build.mcu=atmega328p
при выборе платы nanoOpti
становится build.mcu=atmega328p
.
То есть чтобы понять, какие опции можно использовать, надо изучить platform.txt.
Я особо не понял, зачем надо было делать разные бутлоадеры как отдельные платы.
Это всё понятно.. А как было сделать разный бутлодер с выбором частоты для каждого? Там и фьюзы меняются, и сам файл бутлодера. В других ядрах я смотрел только так и делают, бутлодер выбирается как плата, а потом во вкладке уже частота
Типа выбрать плату, потом частоту, потом бутлоадер, а потом millis? Попробую покопаться, может чего получится
Миллис не проблема, там ведь дефайн сидит. А вот выбрать бутлодер и частоту как то не получается, не создавая отдельно три платы
Я понял, как можно сделать. Минут 30:)
Круто! Пойду тогда поем пока что =) А то проснулся сел и всё
Я нигде не нашел сильно разные фьюзы. Не хватает некоторых файлов загрузчиков. Надо правильно переименовать загрузчики. Для without bootloader надо (?) пустой файл загрузчика и защиту от прошивки без программатора (или нет?).
nano.name=ATmega328
nano.upload.tool=avrdude
nano.upload.protocol=arduino
nano.bootloader.tool=avrdude
nano.bootloader.unlock_bits=0x3F
nano.bootloader.lock_bits=0x0F
nano.bootloader.file={bootloader.dir}/bootloader_{bootloader.suffix}.hex
nano.build.board=AVR_NANO
nano.build.core=arduino
nano.build.mcu=atmega328p
menu.clock=Clock
nano.menu.clock.external_16=External 16 MHz
nano.menu.clock.external_16.bootloader.low_fuses=0xFF
nano.menu.clock.external_16.build.f_cpu=16000000L
nano.menu.clock.external_16.bootloader.suffix=16MHz
nano.menu.clock.external_8=External 8 MHz
nano.menu.clock.external_8.bootloader.low_fuses=0xFF
nano.menu.clock.external_8.build.f_cpu=8000000L
nano.menu.clock.external_8.bootloader.suffix=8MHz
nano.menu.clock.internal_8=Internal 8 MHz
nano.menu.clock.internal_8.bootloader.low_fuses=0xD2
nano.menu.clock.internal_8.build.f_cpu=8000000L
nano.menu.clock.internal_8.bootloader.suffix=8MHz
nano.menu.clock.internal_1=Internal 1 MHz
nano.menu.clock.internal_1.bootloader.low_fuses=0x52
nano.menu.clock.internal_1.build.f_cpu=1000000L
nano.menu.clock.internal_1.bootloader.suffix=1MHz
nano.menu.clock.internal_128=Internal 128 kHz
nano.menu.clock.internal_128.bootloader.low_fuses=0xD3
nano.menu.clock.internal_128.build.f_cpu=128000L
nano.menu.clock.internal_128.bootloader.suffix=128kHz
menu.boot=Bootloader
nano.menu.boot.optiboot=OptiBoot
nano.menu.boot.optiboot.upload.maximum_size=30720
nano.menu.boot.optiboot.upload.maximum_data_size=2048
nano.menu.boot.optiboot.upload.speed=115200
nano.menu.boot.optiboot.bootloader.high_fuses=0xDA
nano.menu.boot.optiboot.bootloader.extended_fuses=0xFD
nano.menu.boot.optiboot.bootloader.dir=optiboot
nano.menu.boot.old=Old bootloader
nano.menu.boot.old.upload.maximum_size=30720
nano.menu.boot.old.upload.maximum_data_size=2048
nano.menu.boot.old.upload.speed=57600
nano.menu.boot.old.bootloader.high_fuses=0xDA
nano.menu.boot.old.bootloader.extended_fuses=0xFD
nano.menu.boot.old.bootloader.dir=atmega
nano.menu.boot.no=No bootloader
nano.menu.boot.no.upload.maximum_size=32768
nano.menu.boot.no.upload.maximum_data_size=2048
nano.menu.boot.no.upload.speed=
nano.menu.boot.no.bootloader.high_fuses=0xDF
nano.menu.boot.no.bootloader.extended_fuses=0xFD
nano.menu.boot.no.bootloader.dir=atmega
# TODO: Создать пустой файл
menu.timers=System timer
nano.menu.timers.yes_millis=millis enabled
nano.menu.timers.no_millis=millis disabled
nano.menu.timers.no_millis.build.extra_flags=-D_GYVERCORE_NOMILLIS
Нашел тут весьма интересную статью (вики) по boards.txt и platforms.txt тыц. Можно вместе с ядром запихать не только библиотеки, но и avr-gcc последней версии. (В ардуине вроде 4.х, хотя уже есть 9.2)
ух ты! Вот это круто реально, через {} и пути/имена, спасибо огромное! ПО поводу безбутлоадерной прошивки тоже думали, надо пробовать компилить. А так да, нужен пустой файл, ну или с хуями =)
Кстати, не получается задефайнить вызов метода от объекта, например не даёт #define Serial.begin(x) uartBegin(x) сделать даже просто в скетче
не даёт #define Serial.begin(x) uartBegin(x)
Ну да, так и должно быть. Надо просто переносить код.
Жалко(
Добавили Clock Out, там меняется фьюз. Можно ли упростить это дело и вывести отдельной менюшкой вкл/выкл? Сейчас сделал просто дополнительные частоты с clock out boards.txt
Я правильно понял, что надо добавить менюшку с переключением отдельных фьюзов?
В целом получается да
Вообще avrdude понимает фьюзы не только как 0xFF, но и как 0b11111111. Те можно наделать переменных под отдельные поля во фьюзах. Еще что-то не так с бутлоадерами
Я могу весь fusecalc на менюшки перенести:)
Бутлодеры я переназвал в папках, могу кинуть актуальную версию. Фьюзкалк конечно круто, но наверное уже лишнее))) а вот из двух выбрать было бы в самый раз
Вообще я кажется понял, значение фюза закинуть как значение меню, и потом присваивать это название меню?
Хорошо бы создать отдельную ветку (типа develop) в гите и там всем этим обмениваться, а то как-то не очень удобно.
Значение фьюза - это просто число, которое передается при запуске avrdude (см platfrom.txt). avrdude понимает числа не только в виде 0xFF, но и в виде 0b11111111. А их можно склеивать как путь к загрузчику.
Что по поводу этого. Я так понял, что чтобы освободить место во флеше (загрузчик его занимает, но не использует), надо настроить фьюзы и перезаписать загрузчик. Или его пересобирать надо?
## See: http://code.google.com/p/arduino/wiki/Platforms
## почему у оптибута неправильно стоят фьюзы? https://github.com/arduino/ArduinoCore-avr/issues/10
## optiboot v4
###################################
## GyverCore on 328 based boards ##
###################################
## BOARD ##
nano.name=ATmega328 based
nano.upload.tool=avrdude
nano.upload.protocol=arduino
nano.upload.maximum_data_size=2048
nano.bootloader.tool=avrdude
nano.bootloader.unlock_bits=0x3F
nano.bootloader.lock_bits=0x0F
# !RSTDISBL !DWEN SPIEN !WDTON
nano.bootloader.high_fuses=0b1101{fuses.EESAVE}{fuses.BOOT}
nano.bootloader.low_fuses=0b{fuses.CKDIV8}{fuses.CKOUT}{fuses.CKSEL}
nano.bootloader.file={bootloader.dir}/{bootloader.suffix}.hex
nano.build.board=AVR_NANO
nano.build.core=arduino
nano.build.mcu=atmega328p
## CLOCK ##
menu.clock=Clock
nano.menu.clock.external_16=External 16 MHz
nano.menu.clock.external_16.fuses.CKDIV8=1
nano.menu.clock.external_16.fuses.CKSEL=111111
nano.menu.clock.external_16.build.f_cpu=16000000L
nano.menu.clock.external_16.bootloader.suffix=atmega328_16mhz
nano.menu.clock.external_8=External 8 MHz
nano.menu.clock.external_8.fuses.CKDIV8=1
nano.menu.clock.external_8.fuses.CKSEL=111111
nano.menu.clock.external_8.build.f_cpu=8000000L
nano.menu.clock.external_8.bootloader.suffix=atmega328_8mhz
nano.menu.clock.external_2=External 16 MHz on 2 MHz
nano.menu.clock.external_2.fuses.CKDIV8=0
nano.menu.clock.external_2.fuses.CKSEL=111111
nano.menu.clock.external_2.build.f_cpu=2000000L
nano.menu.clock.external_2.bootloader.suffix=emptyBoot
nano.menu.clock.external_1=External 8 MHz on 1 MHz
nano.menu.clock.external_1.fuses.CKDIV8=0
nano.menu.clock.external_1.fuses.CKSEL=111111
nano.menu.clock.external_1.build.f_cpu=1000000L
nano.menu.clock.external_1.bootloader.suffix=emptyBoot
nano.menu.clock.internal_8=Internal 8 MHz
nano.menu.clock.internal_8.fuses.CKDIV8=1
nano.menu.clock.internal_8.fuses.CKSEL=010010
nano.menu.clock.internal_8.build.f_cpu=8000000L
nano.menu.clock.internal_8.bootloader.suffix=atmega328_8mhz
nano.menu.clock.internal_1=Internal 1 MHz
nano.menu.clock.internal_1.fuses.CKDIV8=0
nano.menu.clock.internal_1.fuses.CKSEL=010010
nano.menu.clock.internal_1.build.f_cpu=1000000L
nano.menu.clock.internal_1.bootloader.suffix=emptyBoot
nano.menu.clock.internal_128=Internal 128 kHz
nano.menu.clock.internal_128.fuses.CKDIV8=1
nano.menu.clock.internal_128.fuses.CKSEL=010011
nano.menu.clock.internal_128.build.f_cpu=128000L
nano.menu.clock.internal_128.bootloader.suffix=emptyBoot
## CLOCKOUT
menu.co=Clockout
nano.menu.co.enabled=External Clock Out (D8)
nano.menu.co.enabled.fuses.CKOUT=0
nano.menu.co.disabled=No External Clock Out (D8)
nano.menu.co.disabled.fuses.CKOUT=1
## EESAVE
menu.co=Save EEPROM on flash
nano.menu.co.enabled=Save, enabled
nano.menu.co.enabled.fuses.EESAVE=0
nano.menu.co.disabled=Clear, disabled
nano.menu.co.disabled.fuses.EESAVE=1
## BOOT ##
menu.boot=Bootloader
nano.menu.boot.old=Old bootloader
nano.menu.boot.old.upload.maximum_size=30720
nano.menu.boot.old.upload.speed=57600
nano.menu.boot.old.fuses.BOOT=010
nano.menu.boot.old.bootloader.dir=atmega
nano.menu.boot.optiboot=OptiBoot
nano.menu.boot.optiboot.upload.maximum_size=30720
nano.menu.boot.optiboot.upload.speed=115200
nano.menu.boot.optiboot.fuses.BOOT=010
nano.menu.boot.optiboot.bootloader.dir=optiboot-fixed
nano.menu.boot.optiboot_fix=OptiBoot (with fixed size)
nano.menu.boot.optiboot_fix.upload.maximum_size=32256
nano.menu.boot.optiboot_fix.upload.speed=115200
nano.menu.boot.optiboot_fix.fuses.BOOT=110
nano.menu.boot.optiboot_fix.bootloader.dir=optiboot
nano.menu.boot.no=No bootloader
nano.menu.boot.no.upload.maximum_size=32768
nano.menu.boot.no.upload.speed=
nano.menu.boot.no.fuses.BOOT=111
nano.menu.boot.no.bootloader.dir=empty
## TIMER ##
menu.timers=System timer
nano.menu.timers.yes_millis=millis enabled
nano.menu.timers.no_millis=millis disabled
nano.menu.timers.no_millis.build.extra_flags=-D_GYVERCORE_NOMILLIS
## BOD ##
menu.bod=B.O.D.
nano.menu.bod.disable=disable
nano.menu.bod.disable.bootloader.extended_fuses=0xFF
nano.menu.bod.bod_1_8=1.8V
nano.menu.bod.bod_1_8.bootloader.extended_fuses=0xFE
nano.menu.bod.bod_2_7=2.7V (default)
nano.menu.bod.bod_2_7.bootloader.extended_fuses=0xFD
nano.menu.bod.bod_4_3=4.3V
nano.menu.bod.bod_4_3.bootloader.extended_fuses=0xFC
## INIT ##
menu.init=Initialization
nano.menu.init.default=enable (default)
nano.menu.init.no_init=disable
nano.menu.init.no_init.build.extra_flags=-D_GYVERCORE_NOINIT
Короче, пока так.
окей, спасибо! Будем ковырять. По поводу места и загрузчика - да, надо менять. Но платы, выпущенные уже с такими настройками, работать будут некорректно. Проебались Ардуино в общем
выпущенные уже с такими настройками
Опять же, перезалив бутлоадера решает эти проблемы. Поэтому я добавил пункт OptiBoot (with fixed size)
.
Для меня не понятно, зачем вот это там (так изначально было?).
Еще хотелось бы более удобной настройки шима. У setPwmFreqnuency потерялись его 8KHZ и 31KHZ. При этом setPWM_20kHz выглядит странно рядом с ней. У меня есть некоторые наработки по шиму, но я их не тестил. Могу скинуть, если надо.
С бутлодерами перекопали уже всё) вроде работает ВОТ ЭТО там было изначально, трогать страшно. Как и многое другое Не понял про потерялись 8 кГц и 31 кГц? Тут прикол в том, что 8 и 31 позволяют использовать все ноги, а 20 кГц (самая удачная частота для моторов и вообще) можно сделать либо костылями, из за чего потерялись вторые ноги 8 битных таймеров, либо понижением системной частоты клока. Собственно ядро заточено под 16 МГц плату, и оставили вот так. А какую более удобную работу с ШИМ ты видишь?
По обновлению: 1) uart, 11 строка. Почему так? F_CPU же может меняться. 2) рядом. Это можно убрать, а в заголовке поменять
void uartBegin(void);
void uartBegin(uint32_t baudrate);
на
void uartBegin(uint32_t baudrate = 9800);
3) еще чуть дальше. Обычно (не только в ардуино, но и на компах) available возвращает количество доступных символов (uint8_t или byte). Если у тебя uartAvailable возвращает true, если доступен хотя бы один символ (как сейчас), то его можно упростить до
return _UART_RX_BUFFER_HEAD != _UART_RX_BUFFER_TAIL;
4) > ВОТ ЭТО там было изначально, трогать страшно
Это трогать вполне безопасно. При прошивке по usb-serial нельзя поменять фюьзы или бутлоадер (для прошивки надо выбирать нужный бутлоадер только по тому, что у них разная скорость уарта). При прошивке через программатор надо просто перезаписать загрузчик после изменения конфига (хотя может и не надо). И даже если загрузчик не заработает можно все вернуть назад и перезаписать загрузчик. Хотя опять же, надо тестить.
5) > Не понял про потерялись 8 кГц и 31 кГц
Я ожидал увидеть что-то типа
#define PWM_8KHZ 1
#define PWM_31KHZ 2
Но что-то не увидел. (Может плохо смотрел, конечно)
6) > А какую более удобную работу с ШИМ ты видишь
void configurePWM(byte pin, uint16_t bits, byte flags);
void setup(){
// pin 3 - 8bit fast pwm
configurePWM(3, 8, PWM_FAST);
// pin 9 - 13bit phase correct pwm
configurePWM(9, 13, PWM_PHASE_CORRECT | PWM_MAP_VALUE | PWM_16BIT_MAP);
// or pin 9 - 0-99 fast pwm (20kHz)
configurePWM(9, 99, PWM_FAST | PWM_MAP_VALUE | PWM_16BIT_MAP | PWM_CUSTOM);
analogWrite(3, 127); // 50%
analogWrite(9, 255); // 50%, MAP_VALUE преобразует значение, 16BIT_MAP указывает, что на входе не 0-255, а 0-65535 (0xffff, только для пинов 9 и 10)
}
Примерно так.
На счет шима хз. Мне кажется, что не совсем очевидно, почему setPWM_20kHz отдельно от setPwmFreqnuency, ведь 20kHz тоже freqnuency. Да и они как-то не очевидно конфликтуют (я вообще не понял как работает setPWM_9_10_resolution (и работает ли?) и игнорирование _TMR1_HR_PWM
если _TMR1_HF_PWM == false
). Да и идею GyverPWM я не очень понял. Куча функций (пока, флеш), а функционала не особо много. И если уж делать отдельную библиотеку для шима, то из ядра лучше убрать дополнения для шима. Кому нужна регулировка частоты/разрядности шима - тот поставит библиотеку. Кому не нужен - тому будет больше памяти.
Еще кое-что нашел. Можно заменить на
analogStartConvert(pin);
return analogGet();
Тут как-то немного не логично работают макросы
Может >=
? #include <util/delay.h>
в ifdef оборачивать смысла нет. Мне логика не понятна. Может #ifdef
? И так далее.
init и lightInit желательно обернуть в соответствующий #ifdef (именно сами функции, а не только их вызов). Кстати, какая между ними разница? Кроме сброса uart (который делать желательно, хотя вроде-как можно и не делать) и cli/sei (которые (особенно sei) нужны. Все функции ардуины ожидают, что прерывания включены (был sei), но при подаче питания прерывания отключены и init должен сделать sei (иначе millis внезапно будет всегда возвращать 0). Но при программном сбросе (((void(*)()) 0)();
, или типо того. В ардуине обычно не применяется) прерывания могут быть как включены, так и выключены. Поэтому init в начале делает cli. Кстати cli и sei занимают ровно по одной инструкции, поэтому смысла на них экономить особо нет.
Вроде так должно быть
#include "Arduino.h"
#include <util/delay.h>
/* функции времени и инициализация таймеров , АЦП*/
#ifndef _GYVERCORE_NOMILLIS // millis включен
# define MICROSECONDS_PER_TIMER0_OVERFLOW (clockCyclesToMicroseconds(64 * 256)) // 1024 на 16 МГц / 2048 на 8 МГц / 16384 на 1 МГц / 128000 на 128 кГц
# define MILLIS_INC (MICROSECONDS_PER_TIMER0_OVERFLOW / 1000) // 1 на 16 МГц / 2 на 8 МГц
# define FRACT_INC ((MICROSECONDS_PER_TIMER0_OVERFLOW % 1000) >> 3) // 3 на 16 МГц / 6 на 8 МГц
# define FRACT_MAX (1000 >> 3) // 125 на 16 МГц / 125 на 8 МГц
# define MICROS_MULT (64 / clockCyclesPerMicrosecond())
volatile unsigned long timer0_overflow_count = 0;
volatile unsigned long timer0_millis = 0;
static unsigned char timer0_fract = 0;
ISR(TIMER0_OVF_vect){
timer0_millis += MILLIS_INC;
timer0_fract += FRACT_INC;
if (timer0_fract >= FRACT_MAX) {
timer0_fract -= FRACT_MAX;
timer0_millis++;
}
timer0_overflow_count++;
}
unsigned long millis() {
cli(); // остановить счет
unsigned long m = timer0_millis; // перехватить значение
sei(); // продолжить счет
return m; // вернуть миллисекунды
}
unsigned long micros() {
cli(); // остановить прерывания
unsigned long m = timer0_overflow_count; // счет переполнений
uint8_t t = TCNT0; // считать содержимое счетного регистра
if ((TIFR0 & _BV(TOV0)) && (t < 255)) //инкремент по переполнению
m++;
sei(); // продолжить счет
return (long)(((m << 8) + t) * MICROS_MULT); // вернуть микросекунды
}
#endif
void delay(unsigned long ms) {
#ifdef _GYVERCORE_NOMILLIS
_delay_ms(ms);
#else
uint32_t start = micros(); // запомнили время старта
while (ms > 0) { // ведем отсчет
yield();
while ( ms > 0 && (micros() - start) >= 1000) {
ms--;
start += 1000;
}
}
#endif
}
void delayMicroseconds(unsigned int us) {
#if defined(_GYVERCORE_NOMILLIS) || F_CPU < 1000000L
_delay_us(us);
#else
// работает на счете тиков
#if F_CPU >= 16000000L
if (us <= 1) return; // = 3 cycles, (4 when true)
us <<= 2; // x4 us, = 4 cycles
us -= 5;
#elif F_CPU >= 8000000L
if (us <= 2) return; // = 3 cycles, (4 when true)
us <<= 1; //x2 us, = 2 cycles
us -= 4; // = 2 cycles
#elif F_CPU >= 1000000L
if (us <= 16) return; //= 3 cycles, (4 when true)
if (us <= 25) return; //= 3 cycles, (4 when true)
us -= 22; // = 2 cycles
us >>= 2; // us div 4, = 4 cycles
#endif
// busy wait
__asm__ __volatile__ (
"1: sbiw %0,1" "\n\t" // 2 cycles
"brne 1b" : "=w" (us) : "0" (us) // 2 cycles
);
// return = 4 cycles
#endif
}
void init() // функция инициализации
{
cli();
/* timer 0 */
TCCR0A = 0b00000011; // fast pwm 8 bit
TCCR0B = 0b00000011; // делитель 64
#ifndef _GYVERCORE_NOMILLIS
TIMSK0 |= (1<<TOIE0); // ovf interrupt вкл
#endif
/* timer 1 */
TCCR1A = 0b00000001; // phasecorrect pwm 8 bit
TCCR1B = 0b00001011; // делитель 64
/* timer 2 */
TCCR2A = 0b00000001; // phasecorrect pwm 8 bit
TCCR2B = 0b00000100; // делитель 64
/* adc */
ADCSRA = 0b10000010; // делитель - 4 [0,1,2 bits - делитель]
/* ADC prescalers: 001 >> /2 010 >> /4 011 >> /8 100 >> /16 101 >> /32 110 >> /64 111 >> /128*/
/* UART */
UCSR0B = 0; // пока не вызван Serial.begin / uartBegin выводы 0/1 свободны для работы.
sei();
}
SetPWM20 работает только на пинах 3, 5, 9, 10, вторые каналы 8 битных таймеров теряются
setPwmFreqnuency работает на всех пинах, это чисто смена прескейлера и всё
setPWM_9_10_resolution устанавливает 8 или 10 бит для генерации SetPWM20 кГц для пинов 9 и 10
По аналогу - там часть кода заменить на вызов функции аналогГет? Это сейчас егору скину, он писал
С дефайнами я обосрался да, сам понял и уже выкатил новую версию...
Вот по поводу init у нас вообще магия какая-то. Пустой скетч со стандартным инитом весит в два раза больше, чем лайт. Причём менять имя стандартного инита нельзя, и убирать его из одного файла с таймерами тоже. Вообще магия какая то. Также на новом ините у меня не завёлся i2C, в общем кто то откуда то этот инит тянет, пока неизвестно
1) Облегченный HardwareSerial. Запись - копипаст из uart.cpp, чтение почти как было в оригинале. В uart.cpp обычный буфер с перемещениями, будет тормозить на каждом uartRead, а в HardwareSerial циклический буфер, который лучше подходит для этих целей.
2) delay без micros. В avrlibc есть функции _delay_ms и _delay_us, которые делают ровно то, что и delay/delayMicroseconds. Только не зависят ни от чего. Конечно, стоит их потестить в железе, может использовать только если micros отключен.
3) build.extra_flags. Вместо variant. Так оно как-то проще + по аналогии можно напихать еще кучу опций.