AlexGyver / GyverCore

Быстрое ядро для Arduino IDE
https://alexgyver.ru/gyvercore/
GNU Lesser General Public License v3.0
127 stars 13 forks source link

Облегченный HardwareSerial, delay без micros и build.extra_flags #2

Open neko-neko-nyan opened 5 years ago

neko-neko-nyan commented 5 years ago

1) Облегченный HardwareSerial. Запись - копипаст из uart.cpp, чтение почти как было в оригинале. В uart.cpp обычный буфер с перемещениями, будет тормозить на каждом uartRead, а в HardwareSerial циклический буфер, который лучше подходит для этих целей.

2) delay без micros. В avrlibc есть функции _delay_ms и _delay_us, которые делают ровно то, что и delay/delayMicroseconds. Только не зависят ни от чего. Конечно, стоит их потестить в железе, может использовать только если micros отключен.

3) build.extra_flags. Вместо variant. Так оно как-то проще + по аналогии можно напихать еще кучу опций.

AlexGyver commented 5 years ago

Про циклический буфер надо почитать... А вот AVR/delay не впечатлило, работает криво, мы измеряли. Стандартные костыли работают лучше всего, аналогов не нашли. Boards txt полностью переписан в версии 1.6, но за инфу про флаги огромное спасибо! Переделаю

neko-neko-nyan commented 5 years ago

Циклический буфер реализован в ардуиновом 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 и после выключения мк просто зависает.

AlexGyver commented 5 years ago

Всё верно, сейчас поковыряю. С циклическим буфером не работал, обычно работаю с таким не очень эффективным. А по скорости мой read всё равно быстрее =) Не нашёл способа ввести свой дефайн кроме variants, сейчас вроде разобрался. -D это команда для дефайна да? avr delay тогда воткну, вот это отличная идея реально!

neko-neko-nyan commented 5 years ago

build.extra_flags это флаги компилятора, добавляются во все команды компиляции. -D NAME аналогично #define NAME. Только стоит поменять название этого дефайна на что-нибудь вроде _GYVERCORE_NOMILLIS чтобы избежать конфликтов с библиотеками. Еще я бы при отключении millis удалял функции millis и micros (обернуть в #ifdef вместе с isrом, и переменные тоже), в Arduino.h заменял их заголовки на #define millis() (0) (аналогично для micros). Это должно сэкономить некоторое кол-во флеша и оптимизатор должен сильно ускорить циклы, использующие millis (превратив их в бесконечные).

neko-neko-nyan commented 5 years ago

Serial.read из ардуины все равно медленнее моего потому что там обращения к переменным через this. Твой быстрее потому что нет проверки пустого буфера. (Кстати, столько там разницы?) Вообще нет смысла держать два разных uartа, только больше места во флеше занимают. Если оптимизировать, то тот, что есть в ардуине (для совместимости с библиотеками).

А еще по поводу #1 . Так делают только библиотеки, идущие вместе с ядром (twi). Зачем - непонятно. Предположу, что для SDA и SCL. В принципе их тоже можно редактировать (а именно удалить #include <pins_arduino.h> и перенести все из него в Arduino.h. Ну и потестить, конечно.)

neko-neko-nyan commented 5 years ago

Еще чисто декоративный момент. Надо будет как-нибудь повыравнивать нормально код, поменять табы на пробелы, определиться со стилем и все привести к нему (Где ставить {, сколько пробелов на отступ, использовать ли bitSet/bitClear и т.д.), убрать все sbi/cbi (в avrlibc они помечены как устаревшие, но стандартное ядро ардуины их тупо копирует. И зачем?) и далее по мелочам. Ну это как-бы чисто для красоты:)

AlexGyver commented 5 years ago

pins arduino изначально убрали, но некоторые либы (FastLED вроде) пытаются его инклудить.... трэш Насчёт кода - я пишу в notepad++, он реиндентит с табами, мне нравится. Часть кода писал Егорка, писал не знаю каким местом =) Его код даже не реиндентится. И он не юзает макросы bitSet и прочие, а я их люблю.. ну это ладно, ерунда Тут такой вопрос: отдельно сейчас GyverUART поковырял, попытался добавить кольцевой буфер ровно как в коммите. И что-то не работает, не посмотришь плиз? Там я закомментил свои куски кода, и сразу под ними - всё что к кольцевому относится GyverUART.zip При попытке поиграть в эхо сыпет в меня обратными вопросительными знаками

#include <GyverUART.h>

void setup() {
  uartBegin();
}

void loop() {
  if (uartAvailable()) {
    uartWrite(uartRead());
  }
}
neko-neko-nyan commented 5 years ago

Не компилировал, но предположу, что надо поменять rx_buffer_index_t на uint8_t или byte.

AlexGyver commented 5 years ago

Не компилировал, но предположу, что надо поменять rx_buffer_index_t на uint8_t или byte.

я изначально так и писал, потом вернул rx_buffer_index_t. Ничего не изменилось

neko-neko-nyan commented 5 years ago

А можно сообщения компилятора? Или как оно вообще не работает?

Я пишу в visual studio code с плагином c/cpp и настройками под avr-gcc.

AlexGyver commented 5 years ago

Ошибок нет, просто вместо "эхо" выдаёт в порт обратные знаки вопроса. А должно быть эхо

neko-neko-nyan commented 5 years ago

Что-то со скоростью

AlexGyver commented 5 years ago

так в том и дело, что заменил только то, что касается буфера. В прерывании и функциях чтения/проверки

neko-neko-nyan commented 5 years ago

А понял

neko-neko-nyan commented 5 years ago
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;  // <= Добавить это
}
AlexGyver commented 5 years ago

блин)))))))))))) вот это провал

neko-neko-nyan commented 5 years ago

Вот по этому надо добавить опции компилятора -Wall -Werror (включить все предупреждения и считать любое предупреждение ошибкой). Где-то вроде в platform.txt это настраивалось. Только потом отключить, чтобы кривые библиотеки иногда работали:)

AlexGyver commented 5 years ago

Всё работает, спасибо огромное! Теперь юарт быстрее ровно в 2 раза =) Вот это пушка. Кстати, "очистку" буфера я правильно сделал? _UART_RX_BUFFER_HEAD = _UART_RX_BUFFER_TAIL;

neko-neko-nyan commented 5 years ago

Да, можно так, а можно и _UART_RX_BUFFER_HEAD = _UART_RX_BUFFER_TAIL = 0;. А можно _UART_RX_BUFFER_TAIL = _UART_RX_BUFFER_HEAD;. Можно собрать разные варианты и посмотреть дизассемблер на предмет количества инструкций:) Но это совсем дикие оптимизации

AlexGyver commented 5 years ago

Окей. А такой вопрос: какие еще есть полезные команды у boards.txt и где про это почитать? Насколько вариативные конструкции можно делать вообще? А то сейчас играем с фьюзами, и пришлось создавать отдельный набор плат под разные фьюзы грубо говоря, байт то один....

neko-neko-nyan commented 5 years ago

Ну как сказать. Я про 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.

Я особо не понял, зачем надо было делать разные бутлоадеры как отдельные платы.

AlexGyver commented 5 years ago

Это всё понятно.. А как было сделать разный бутлодер с выбором частоты для каждого? Там и фьюзы меняются, и сам файл бутлодера. В других ядрах я смотрел только так и делают, бутлодер выбирается как плата, а потом во вкладке уже частота

neko-neko-nyan commented 5 years ago

Типа выбрать плату, потом частоту, потом бутлоадер, а потом millis? Попробую покопаться, может чего получится

AlexGyver commented 5 years ago

Миллис не проблема, там ведь дефайн сидит. А вот выбрать бутлодер и частоту как то не получается, не создавая отдельно три платы

neko-neko-nyan commented 5 years ago

Я понял, как можно сделать. Минут 30:)

AlexGyver commented 5 years ago

Круто! Пойду тогда поем пока что =) А то проснулся сел и всё

neko-neko-nyan commented 5 years ago

Я нигде не нашел сильно разные фьюзы. Не хватает некоторых файлов загрузчиков. Надо правильно переименовать загрузчики. Для 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
neko-neko-nyan commented 5 years ago

Нашел тут весьма интересную статью (вики) по boards.txt и platforms.txt тыц. Можно вместе с ядром запихать не только библиотеки, но и avr-gcc последней версии. (В ардуине вроде 4.х, хотя уже есть 9.2)

AlexGyver commented 5 years ago

ух ты! Вот это круто реально, через {} и пути/имена, спасибо огромное! ПО поводу безбутлоадерной прошивки тоже думали, надо пробовать компилить. А так да, нужен пустой файл, ну или с хуями =)

AlexGyver commented 5 years ago

Кстати, не получается задефайнить вызов метода от объекта, например не даёт #define Serial.begin(x) uartBegin(x) сделать даже просто в скетче

neko-neko-nyan commented 5 years ago

не даёт #define Serial.begin(x) uartBegin(x)

Ну да, так и должно быть. Надо просто переносить код.

AlexGyver commented 5 years ago

Жалко(

AlexGyver commented 5 years ago

Добавили Clock Out, там меняется фьюз. Можно ли упростить это дело и вывести отдельной менюшкой вкл/выкл? Сейчас сделал просто дополнительные частоты с clock out boards.txt

neko-neko-nyan commented 5 years ago

Я правильно понял, что надо добавить менюшку с переключением отдельных фьюзов?

AlexGyver commented 5 years ago

В целом получается да

neko-neko-nyan commented 5 years ago

Вообще avrdude понимает фьюзы не только как 0xFF, но и как 0b11111111. Те можно наделать переменных под отдельные поля во фьюзах. Еще что-то не так с бутлоадерами

neko-neko-nyan commented 5 years ago

Я могу весь fusecalc на менюшки перенести:)

AlexGyver commented 5 years ago

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

AlexGyver commented 5 years ago

Вообще я кажется понял, значение фюза закинуть как значение меню, и потом присваивать это название меню?

neko-neko-nyan commented 5 years ago

Хорошо бы создать отдельную ветку (типа develop) в гите и там всем этим обмениваться, а то как-то не очень удобно.

Значение фьюза - это просто число, которое передается при запуске avrdude (см platfrom.txt). avrdude понимает числа не только в виде 0xFF, но и в виде 0b11111111. А их можно склеивать как путь к загрузчику.

neko-neko-nyan commented 5 years ago

Что по поводу этого. Я так понял, что чтобы освободить место во флеше (загрузчик его занимает, но не использует), надо настроить фьюзы и перезаписать загрузчик. Или его пересобирать надо?

## 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
neko-neko-nyan commented 5 years ago

Короче, пока так.

AlexGyver commented 5 years ago

окей, спасибо! Будем ковырять. По поводу места и загрузчика - да, надо менять. Но платы, выпущенные уже с такими настройками, работать будут некорректно. Проебались Ардуино в общем

neko-neko-nyan commented 5 years ago

выпущенные уже с такими настройками

Опять же, перезалив бутлоадера решает эти проблемы. Поэтому я добавил пункт OptiBoot (with fixed size).

Для меня не понятно, зачем вот это там (так изначально было?).

Еще хотелось бы более удобной настройки шима. У setPwmFreqnuency потерялись его 8KHZ и 31KHZ. При этом setPWM_20kHz выглядит странно рядом с ней. У меня есть некоторые наработки по шиму, но я их не тестил. Могу скинуть, если надо.

AlexGyver commented 5 years ago

С бутлодерами перекопали уже всё) вроде работает ВОТ ЭТО там было изначально, трогать страшно. Как и многое другое Не понял про потерялись 8 кГц и 31 кГц? Тут прикол в том, что 8 и 31 позволяют использовать все ноги, а 20 кГц (самая удачная частота для моторов и вообще) можно сделать либо костылями, из за чего потерялись вторые ноги 8 битных таймеров, либо понижением системной частоты клока. Собственно ядро заточено под 16 МГц плату, и оставили вот так. А какую более удобную работу с ШИМ ты видишь?

neko-neko-nyan commented 5 years ago

По обновлению: 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)
}

Примерно так.

AlexGyver commented 5 years ago
  1. Не ту версию дополнил, в оригинале там был F_CPU, спасибо) 2 и 3 допилил
  2. Правда потеряли! Видимо версии заменились, в первой всё работало)
  3. Вот это уже сложно для юзера) У нас для такого есть GyverPWM отдельно
neko-neko-nyan commented 5 years ago

На счет шима хз. Мне кажется, что не совсем очевидно, почему 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 занимают ровно по одной инструкции, поэтому смысла на них экономить особо нет.

neko-neko-nyan commented 5 years ago

Вроде так должно быть

#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();
}
AlexGyver commented 5 years ago

SetPWM20 работает только на пинах 3, 5, 9, 10, вторые каналы 8 битных таймеров теряются
setPwmFreqnuency работает на всех пинах, это чисто смена прескейлера и всё
setPWM_9_10_resolution устанавливает 8 или 10 бит для генерации SetPWM20 кГц для пинов 9 и 10
По аналогу - там часть кода заменить на вызов функции аналогГет? Это сейчас егору скину, он писал
С дефайнами я обосрался да, сам понял и уже выкатил новую версию...
Вот по поводу init у нас вообще магия какая-то. Пустой скетч со стандартным инитом весит в два раза больше, чем лайт. Причём менять имя стандартного инита нельзя, и убирать его из одного файла с таймерами тоже. Вообще магия какая то. Также на новом ините у меня не завёлся i2C, в общем кто то откуда то этот инит тянет, пока неизвестно