Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

neko-neko-nyan
Copy link

  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
Copy link
Owner

AlexGyver commented Aug 30, 2019

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

@neko-neko-nyan
Copy link
Author

Циклический буфер реализован в ардуиновом 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
Copy link
Owner

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

@neko-neko-nyan
Copy link
Author

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

@neko-neko-nyan
Copy link
Author

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

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

@neko-neko-nyan
Copy link
Author

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

@AlexGyver
Copy link
Owner

AlexGyver commented Aug 30, 2019

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

#include <GyverUART.h>

void setup() {
  uartBegin();
}

void loop() {
  if (uartAvailable()) {
    uartWrite(uartRead());
  }
}

@neko-neko-nyan
Copy link
Author

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

@AlexGyver
Copy link
Owner

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

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

@neko-neko-nyan
Copy link
Author

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

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

@AlexGyver
Copy link
Owner

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

@neko-neko-nyan
Copy link
Author

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

@AlexGyver
Copy link
Owner

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

@neko-neko-nyan
Copy link
Author

А понял

@neko-neko-nyan
Copy link
Author

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
Copy link
Owner

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

@neko-neko-nyan
Copy link
Author

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

@AlexGyver
Copy link
Owner

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

@neko-neko-nyan
Copy link
Author

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

@AlexGyver
Copy link
Owner

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

@neko-neko-nyan
Copy link
Author

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

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

@neko-neko-nyan
Copy link
Author

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

@AlexGyver
Copy link
Owner

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

@neko-neko-nyan
Copy link
Author

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

@AlexGyver
Copy link
Owner

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

@neko-neko-nyan
Copy link
Author

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

@AlexGyver
Copy link
Owner

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

@neko-neko-nyan
Copy link
Author

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

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

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

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

@AlexGyver
Copy link
Owner

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

@neko-neko-nyan
Copy link
Author

По обновлению:

  1. uart, 11 строка. Почему так? F_CPU же может меняться.
  2. рядом. Это можно убрать, а в заголовке поменять
void uartBegin(void);
void uartBegin(uint32_t baudrate);

на

void uartBegin(uint32_t baudrate = 9800);
  1. еще чуть дальше. Обычно (не только в ардуино, но и на компах) available возвращает количество доступных символов (uint8_t или byte). Если у тебя uartAvailable возвращает true, если доступен хотя бы один символ (как сейчас), то его можно упростить до
return _UART_RX_BUFFER_HEAD != _UART_RX_BUFFER_TAIL;
  1. ВОТ ЭТО там было изначально, трогать страшно

Это трогать вполне безопасно. При прошивке по usb-serial нельзя поменять фюьзы или бутлоадер (для прошивки надо выбирать нужный бутлоадер только по тому, что у них разная скорость уарта). При прошивке через программатор надо просто перезаписать загрузчик после изменения конфига (хотя может и не надо). И даже если загрузчик не заработает можно все вернуть назад и перезаписать загрузчик. Хотя опять же, надо тестить.

  1. Не понял про потерялись 8 кГц и 31 кГц

Я ожидал увидеть что-то типа

#define PWM_8KHZ 1
#define PWM_31KHZ 2

Но что-то не увидел. (Может плохо смотрел, конечно)

  1. А какую более удобную работу с ШИМ ты видишь

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
Copy link
Owner

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

@neko-neko-nyan
Copy link
Author

На счет шима хз. Мне кажется, что не совсем очевидно, почему 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
Copy link
Author

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

#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
Copy link
Owner

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

@neko-neko-nyan
Copy link
Author

Вот по поводу init у нас вообще магия какая-то

C light init пропадает обработчик прерывания таймер, потому и размер меньше.
Нашел. опять boards.txt.

# в начало добавить
nano.build.extra_flags={extra_flags.timer} {extra_flags.init}
# ...
nano.menu.timers.yes_millis=enable
nano.menu.timers.yes_millis.extra_flags.timer=
nano.menu.timers.no_millis=disable
nano.menu.timers.no_millis.build.extra_flags=-D_GYVERCORE_NOMILLIS
# ...
nano.menu.init.default=default 
nano.menu.init.default.extra_flags.init=-D_GYVERCORE_DEF_INIT
nano.menu.init.light_init=light (beta)
nano.menu.init.light_init.extra_flags.init=-D_GYVERCORE_LIGHT_INIT
nano.menu.init.no_init=disable
nano.menu.init.no_init.extra_flags.init=

Потому что если просто писать build.extra_flags, то флаги перезаписываются.

Как оказалось, это починит неработающий переключатель system timer, но light init продолжает выкидывать обработчик прерывания.

Причём менять имя стандартного инита нельзя

Вот тут я совсем не понял. Поменял, ничего не изменилось.

убирать его из одного файла с таймерами тоже.

Вынес init в отдельный файл. Теперь с ним тоже пропадает обработчик.

Если использовать millis, то обработчик не пропадает ни со стандартным, ни с light init. Разница между ними 10 байт.

Вывод: стандартный init перенести в отдельный файл (а лучше в main.cpp и сделать static), light init убрать совсем. Проверить размеры на пустом скетче и на скетче, использующем millis.

@AlexGyver
Copy link
Owner

Погоди, ничего не понял. Насколько я помню, инит и лайт инит отличаются в разы, и на лайт ините у меня не заводится некоторое железо.
Также выносил инит в отдельный файл, и выдавало ошибку....

@neko-neko-nyan
Copy link
Author

Также на новом ините у меня не завёлся i2C
Из-за того, что нет sei.

выкатил новую версию

#if defined (_GYVERCORE_NOMILLIS)
#include <util/delay.h>
#endif

Ну зачем??
2. Почему бы не использовать это?

#define MICROSECONDS_PER_TIMER0_OVERFLOW (clockCyclesToMicroseconds(64 * 256))
#define MILLIS_INC (MICROSECONDS_PER_TIMER0_OVERFLOW / 1000)
#define FRACT_INC ((MICROSECONDS_PER_TIMER0_OVERFLOW % 1000) >> 3)
#define FRACT_MAX (1000 >> 3)
  1. Так == или >= (строка 35)?
#elif F_CPU == 128000L
  1. Так > или >= (строка 120, на самом деле должно быть F_CPU >= 1000000L)?
#if F_CPU > 128000L

@neko-neko-nyan
Copy link
Author

раз, два. Там и 5 отличий-то нет:) Всего 3

@neko-neko-nyan
Copy link
Author

Хотя нет, 4. cli, TCCR1B, UCSR0B и sei

@neko-neko-nyan
Copy link
Author

Попробуй опять вынести инит в отдельный файл и покажи, что пишет.

@neko-neko-nyan
Copy link
Author

Про шим. Я даташит читал и про вторые каналы знаю. Про setPWM_9_10_resolution так и не понял, она же не изменяет ни одного регистра. Только включает умножение val на какое-то число. Как это поменяет разрешение? Да и не очень очевидно, что setPWM_9_10_resolution работает только вместе с setPWM_20kHz.

По аналогу - там весь код analogRead заменить на analogStartConvert(pin); return analogGet(); (вызов двух функций, да). Потому что там один и тот же код.

@AlexGyver
Copy link
Owner

AlexGyver commented Aug 31, 2019

Оееей, много всего, буду разбираться.
Такой вопрос: define kek 25*25
Он задефайнит как 25*25 или посчитает?
Это к тому, зачем я закомментил макросы в таймерс

@neko-neko-nyan
Copy link
Author

Задефайнит, да. Но у нас есть оптимизатор. Он счиает все, что можно просчитать. И не только в дефайнах. Любое выражение без использования переменных. Вообще оптимизатор очень умный и даже удаляет ненужные обработчики прерываний (хотя я раньше об этом не знал).

@neko-neko-nyan
Copy link
Author

И кстати, оптимизатор удаляет const переменные, поэтому можно спокойно писать

const byte PIN_LED = 5;

вместо

#define PIN_LED 5

@AlexGyver
Copy link
Owner

Ух ты! А я парюсь всегда по этому поводу) надо урок накидать на эту тему, потому что в ардуино-рилейтед контенте я про такое не находил. Есть ещё какие по памяти моменты про компилятор/препроцессор/оптимизатор важные?

@neko-neko-nyan
Copy link
Author

Мое любимое: в заголовках можно вместо #ifndef LIBRARY_H #define LIBARY_H ... #endif можно писать просто #pragma once в начале. Вместо кучи несвязанных дефайнов можно использовать enum (погугли). На новых версиях gcc можно использовать constexpr функции вместо дефайнов. Это функции, которые вычисляются на этапе компиляции. Очень удобная штука, но древнем gcc из ардуины она не работает.

Совсем не связанное со всем этим: можно объединить функции библиотеки в namespace. Например

// library.h
namespace miniUart {
  void begin(uint16_t baud = 9600);
  char read();
  int write(char c);
 // ...
  int print(const char *s);
}

// main code
#include <libaray.h>
void setup(){
  miniUart::begin();
  miniUart::println("Hello");
}

Эта фитча работает как переименование. Размер итогового кода не меняется.

@AlexGyver
Copy link
Owner

Прo enum и namespace я писал уроки, это ладно. А enum разве не занимает места? Там вроде что то вроде 1 байт если до 255 значение у нумерации, и 2 байта если до 65к, и всё такое

@neko-neko-nyan
Copy link
Author

Enum занимает место, но не сам enum, а значение. Но при использовании define тоже есть значение, которое занимает место.

#define MODE_A 0
#define MODE_B 1
#define MODEC 2

void setMode(byte mode);

setMode(MODE_A);
enum Mode {
  MODE_A, MODE_B, MODE_C
};

void setMode(Mode mode);

setMode(MODE_A);

После компиляции будет одно и то же. Только во втором случае нельзя будет сделать setMode(15) (вернее можно setMode(static_cast<Mode>(15)), но случайно так сделать сложно). Еще не будет проблем с добавлением новых значений (в define часто копируют варианты и забывают поменять значение).

Еще enum дружит с namespace, а дефайны могут вызывать конфликты библиотек.

@AlexGyver
Copy link
Owner

выкатил релиз, поправил всё что обсуждалось. На неделе попробую погонять Егора, чтобы он залил в камень и проверил частоты и время

@AlexGyver
Copy link
Owner

при переноси init в отдельный файл перестаёт работать штатный Serial =) вот такая срань

@neko-neko-nyan
Copy link
Author

  1. nano.menu.timers.no_millis.build.extra_flags=-D_GYVERCORE_NOMILLIS
nano.menu.timers.no_millis.extra_flags.timer=-D_GYVERCORE_NOMILLIS
  1. перестаёт работать штатный Serial

У меня работает

@AlexGyver
Copy link
Owner

Очень странно, у меня println Начинает бесконечно срать в порт первой буквой отправленного текста. Бордс поправил, спасибо) Ещё потыкаем и выкачу версию. И правда надо было dev сделать...

@neko-neko-nyan
Copy link
Author

Поймал. Не знаю в чем причина, но такое будет только если println сразу за begin. delayMicroseconds(1) сразу после begin решает. Я сначала не заметил потому, что у меня printnl далеко от begin.

@AlexGyver
Copy link
Owner

Ого) забавная бага

@neko-neko-nyan
Copy link
Author

Я где-то видел что-то про ожидание после установления соединения по uart. Но очень странно, что оно на стандартном ядре работает нормально. А uartPrint при этом нормально работает?

@AlexGyver
Copy link
Owner

Да, юарт рабоает без проблем

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants