-
Notifications
You must be signed in to change notification settings - Fork 13
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
base: master
Are you sure you want to change the base?
Conversation
…ций uart* в Serial.* uart.cpp/uart.h по-хорошему надо бы удалить.
Про циклический буфер надо почитать... |
Циклический буфер реализован в ардуиновом serial. Только он там и на ввод, и на вывод. Это штука подходит по буфер ввода/вывода. У тебя в uartRead делается перемещение данных в буфере: for (byte i = 0; i < _UART_RX_COUNTER; i++) _UART_RX_BUFFER[i] = _UART_RX_BUFFER[i + 1]; Получается, что при чтении одного символа происходит по 64 операции чтения и записи в памяти. Еще у тебя нет обработки отсутствия данных в буфере (по идее uartRead должна вернуть -1 если нет данных) и переполнения буфера (если отправить на ардуину больше 64 символов пока она занята чем-то другим). А хоть boards.txt переписан, та фигня с вариантами осталась. ИМХО держать 2 файла ради одного дефайна как-то слишком круто:) Avrовский delay можно включать если выключен millis (все равно от стандартного delay не будет ни какого толку). Его вроде нельзя использовать с большими задержками (где-то в мануале к avrlibc было написано точно). Можно по этому поводу выводить предупреждение. Просто иногда хочется выключить millis (например, нужен таймер для шима или чего-то еще), но какая-то библиотека использует delay и после выключения мк просто зависает. |
Всё верно, сейчас поковыряю. С циклическим буфером не работал, обычно работаю с таким не очень эффективным. А по скорости мой read всё равно быстрее =) |
build.extra_flags это флаги компилятора, добавляются во все команды компиляции. -D NAME аналогично #define NAME. Только стоит поменять название этого дефайна на что-нибудь вроде |
Serial.read из ардуины все равно медленнее моего потому что там обращения к переменным через this. Твой быстрее потому что нет проверки пустого буфера. (Кстати, столько там разницы?) Вообще нет смысла держать два разных uartа, только больше места во флеше занимают. Если оптимизировать, то тот, что есть в ардуине (для совместимости с библиотеками). А еще по поводу #1 . Так делают только библиотеки, идущие вместе с ядром (twi). Зачем - непонятно. Предположу, что для SDA и SCL. В принципе их тоже можно редактировать (а именно удалить |
Еще чисто декоративный момент. Надо будет как-нибудь повыравнивать нормально код, поменять табы на пробелы, определиться со стилем и все привести к нему (Где ставить |
pins arduino изначально убрали, но некоторые либы (FastLED вроде) пытаются его инклудить.... трэш
|
Не компилировал, но предположу, что надо поменять |
я изначально так и писал, потом вернул 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; // <= Добавить это
} |
блин)))))))))))) вот это провал |
Вот по этому надо добавить опции компилятора |
Всё работает, спасибо огромное! Теперь юарт быстрее ровно в 2 раза =) Вот это пушка. |
Да, можно так, а можно и |
Окей. А такой вопрос: какие еще есть полезные команды у boards.txt и где про это почитать? Насколько вариативные конструкции можно делать вообще? А то сейчас играем с фьюзами, и пришлось создавать отдельный набор плат под разные фьюзы грубо говоря, байт то один.... |
Ну как сказать. Я про extra_flags понял чисто случайно. Сами команды компиляции и прошивки лежат в platform.txt, а именно
при выборе платы
при выборе платы То есть чтобы понять, какие опции можно использовать, надо изучить 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 |
окей, спасибо! Будем ковырять. |
Опять же, перезалив бутлоадера решает эти проблемы. Поэтому я добавил пункт Для меня не понятно, зачем вот это там (так изначально было?). Еще хотелось бы более удобной настройки шима. У setPwmFreqnuency потерялись его 8KHZ и 31KHZ. При этом setPWM_20kHz выглядит странно рядом с ней. У меня есть некоторые наработки по шиму, но я их не тестил. Могу скинуть, если надо. |
С бутлодерами перекопали уже всё) вроде работает |
По обновлению:
void uartBegin(void);
void uartBegin(uint32_t baudrate); на void uartBegin(uint32_t baudrate = 9800);
return _UART_RX_BUFFER_HEAD != _UART_RX_BUFFER_TAIL;
Это трогать вполне безопасно. При прошивке по usb-serial нельзя поменять фюьзы или бутлоадер (для прошивки надо выбирать нужный бутлоадер только по тому, что у них разная скорость уарта). При прошивке через программатор надо просто перезаписать загрузчик после изменения конфига (хотя может и не надо). И даже если загрузчик не заработает можно все вернуть назад и перезаписать загрузчик. Хотя опять же, надо тестить.
Я ожидал увидеть что-то типа #define PWM_8KHZ 1
#define PWM_31KHZ 2 Но что-то не увидел. (Может плохо смотрел, конечно)
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 (и работает ли?) и игнорирование Еще кое-что нашел. Можно заменить на analogStartConvert(pin);
return analogGet(); Тут как-то немного не логично работают макросы init и lightInit желательно обернуть в соответствующий #ifdef (именно сами функции, а не только их вызов). Кстати, какая между ними разница? Кроме сброса uart (который делать желательно, хотя вроде-как можно и не делать) и cli/sei (которые (особенно sei) нужны. Все функции ардуины ожидают, что прерывания включены (был sei), но при подаче питания прерывания отключены и init должен сделать sei (иначе millis внезапно будет всегда возвращать 0). Но при программном сбросе ( |
Вроде так должно быть #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 битных таймеров теряются |
C light init пропадает обработчик прерывания таймер, потому и размер меньше. # в начало добавить
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. |
Погоди, ничего не понял. Насколько я помню, инит и лайт инит отличаются в разы, и на лайт ините у меня не заводится некоторое железо. |
#if defined (_GYVERCORE_NOMILLIS)
#include <util/delay.h>
#endif Ну зачем?? #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)
#elif F_CPU == 128000L
#if F_CPU > 128000L |
Хотя нет, 4. cli, TCCR1B, UCSR0B и sei |
Попробуй опять вынести инит в отдельный файл и покажи, что пишет. |
Про шим. Я даташит читал и про вторые каналы знаю. Про setPWM_9_10_resolution так и не понял, она же не изменяет ни одного регистра. Только включает умножение val на какое-то число. Как это поменяет разрешение? Да и не очень очевидно, что setPWM_9_10_resolution работает только вместе с setPWM_20kHz. По аналогу - там весь код analogRead заменить на |
Оееей, много всего, буду разбираться. |
Задефайнит, да. Но у нас есть оптимизатор. Он счиает все, что можно просчитать. И не только в дефайнах. Любое выражение без использования переменных. Вообще оптимизатор очень умный и даже удаляет ненужные обработчики прерываний (хотя я раньше об этом не знал). |
И кстати, оптимизатор удаляет const переменные, поэтому можно спокойно писать const byte PIN_LED = 5; вместо #define PIN_LED 5 |
Ух ты! А я парюсь всегда по этому поводу) надо урок накидать на эту тему, потому что в ардуино-рилейтед контенте я про такое не находил. Есть ещё какие по памяти моменты про компилятор/препроцессор/оптимизатор важные? |
Мое любимое: в заголовках можно вместо Совсем не связанное со всем этим: можно объединить функции библиотеки в 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");
} Эта фитча работает как переименование. Размер итогового кода не меняется. |
Прo enum и namespace я писал уроки, это ладно. А enum разве не занимает места? Там вроде что то вроде 1 байт если до 255 значение у нумерации, и 2 байта если до 65к, и всё такое |
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); После компиляции будет одно и то же. Только во втором случае нельзя будет сделать Еще enum дружит с namespace, а дефайны могут вызывать конфликты библиотек. |
выкатил релиз, поправил всё что обсуждалось. На неделе попробую погонять Егора, чтобы он залил в камень и проверил частоты и время |
при переноси init в отдельный файл перестаёт работать штатный Serial =) вот такая срань |
nano.menu.timers.no_millis.extra_flags.timer=-D_GYVERCORE_NOMILLIS
У меня работает |
Очень странно, у меня println Начинает бесконечно срать в порт первой буквой отправленного текста. Бордс поправил, спасибо) Ещё потыкаем и выкачу версию. И правда надо было dev сделать... |
Поймал. Не знаю в чем причина, но такое будет только если println сразу за begin. delayMicroseconds(1) сразу после begin решает. Я сначала не заметил потому, что у меня printnl далеко от begin. |
Ого) забавная бага |
Я где-то видел что-то про ожидание после установления соединения по uart. Но очень странно, что оно на стандартном ядре работает нормально. А uartPrint при этом нормально работает? |
Да, юарт рабоает без проблем |
Облегченный HardwareSerial. Запись - копипаст из uart.cpp, чтение почти как было в оригинале. В uart.cpp обычный буфер с перемещениями, будет тормозить на каждом uartRead, а в HardwareSerial циклический буфер, который лучше подходит для этих целей.
delay без micros. В avrlibc есть функции _delay_ms и _delay_us, которые делают ровно то, что и delay/delayMicroseconds. Только не зависят ни от чего. Конечно, стоит их потестить в железе, может использовать только если micros отключен.
build.extra_flags. Вместо variant. Так оно как-то проще + по аналогии можно напихать еще кучу опций.