C++ в микроконтроллерах, на примере типичной задачи
- Войдите на сайт для отправки комментариев
Всякий раз, когда приходится подбирать делитель частоты и количество тиков таймера, чтобы обеспечить нужную частоту ногодрыга (или нужную задержку до переполнения таймера), я пользуюсь калькулятором таймеров и вписываю в программу рассчитанные им значения. Когда вдруг приспичивает частоту изменить, опять запускаю калькулятор.
Давно хотелось иметь возможность вычислять конфигурационные биты делителя и количество тиков прямо в программе, чтобы я только частоту задавал, но всё как-то было «не до сук».
И вот, наконец, я таки собрался и написал некую «недобибилиотеку», которая это делает. Почему «недо-»? Ну, хотя бы потому, что в отличие от нормальной ардуиновской библиотеки, моя плевать хотела на всё, кроме ATmega328P. Думаю, в ближайшее время расширю на ATtiny85 и ATmega32U, но и всё, т.к. ничем другим я почти не пользуюсь.
Итак, задача:
по номеру таймера и требуемой частоте ногодрыга, подобрать такие делитель частоты таймера и количество тиков, чтобы получившаяся частота ногодрыга была наиболее точной из всех возможных на данном таймере. Делитель частоты выдать в виде готовых конфигурационных битов для записи в TCCRхB.
Речь идёт именно о наилучшем приближении, т.к. некоторые частоты невозможно получить точно (например, 3кГц). Там, где точное решение возможно, разумеется должно выдаваться именно оно.
Ну, вроде, написал и написал, а вам то зачем мозги пудрю?
А вот зачем. Недобиблиотеку я писал на самом что ни на есть богомерзком, гнусном С++ (даже с возможностями, появившимися только в 11-ом году), который, как всем известно: «неприменим на микроконтроллерах и на котором для микроконтроллеров пишут только дураки и преподы». И вот, решил я проверить сколько памяти отжирает моя библиотека. Какова, так сказать, цена вопроса?
Для проверки я написал такой в меру бессмысленный скетч. В нём производится «частичная» инициализация таймеров, причём если #define в строке №3 закомментировать, то работает моя библиотека, а если оставить на месте, то всё, связанное с библиотекой, выбрасывается нафиг, а в порты пишутся голимые числовые константы. Вот этот скетч, можете сами убедиться, что так оно и есть.
// Если define ниже закомментировать, то работает библиотека // Если же оставить, то простое присваивание числовых констант //#define CONSTANTS #ifndef CONSTANTS #include <ConstTimers.h> // // Параметры для работы таймера/счётчика №2 на частоте 1кГц // constexpr uint8_t timerBits2 = getPrescalerBits(2, 1000); constexpr uint8_t timerTicks2 = getTimerTicks(2, 1000); // // Параметры для работы таймера/счётчика №1 на частоте 500Гц // constexpr uint8_t timerBits1 = getPrescalerBits(1, 500); constexpr uint16_t timerTicks1 = getTimerTicks(1, 500); #endif void setup(void) { #ifdef CONSTANTS TCCR2B = 4; OCR2A = 250; TCCR1B = 1; OCR1B = 32000; #else TCCR2B = timerBits2; OCR2A = timerTicks2; TCCR1B = timerBits1; OCR1B = timerTicks1; #endif } void loop(void) {}
Пробуем компилировать (IDE 1.8.9, опции «из коробки»). И что же видим?
С константами: 474 байт (1%) памяти устройства ... 9 байт (0%) динамической памяти
С библиотекой: 474 байт (1%) памяти устройства ... 9 байт (0%) динамической памяти
Найдите отличия!
А вот и сама недобиблиотека. Как видите, если убрать огромный комментарий в начале, то она состоит всего из 60 строк – десять функций, самая большая из которых – 5 строк.
#ifndef CONSTTIMERS_H #define CONSTTIMERS_H /////////////////////////////////////////////////////////////////////////////// // // Недобиблиотека вычисления битов конфигурации делителя частоты и // количества тиков таймера по заданной частоте. // // Предназначена для использования с микроконтроллером ATmega328P // (например, в Ардуино или в AVR-студии). Модификация для других // микроконтроллеров возможна и несложна, как будет показано ниже. // // Основная особенность: ВСЕ ВЫЧИСЛЕНИЯ ПРОИСХОДЯТ ВО ВРЕМЯ КОМПИЛЯЦИИ. // В РЕЗУЛЬТИРУЮЩИЙ КОД НЕ ПОПАДАЕТ НИ ОДНОГО БАЙТА НИ КОДА, НИ ДАННЫХ. // // замечание о точности: // гарантировать точность произвольной частоты невозможно, т.к. требуемый период // может элементарно не являться делителем тактовой частоты микроконтроллера или // его, может быть, невозможно подобрать имеющимися делителями частоты. // Недобиблиотека ВСЕГДА выдаёт наилучший из возможных результатов для заданной частоты // на заданном таймере. Но при этом нет никакой возможности узнать насколько хорош // результат и какова погрешность. Если требуется инструмент для «зрячего» выбора // частот, следует взять «калькулятор таймеров» (их много, например, есть и у автора). // конец замечания о точности. // // ИСПОЛЬЗОВАНИЕ // // Для использования необходимо включить файл «ConstTimers.h» и определить константы с // модификатором constexpr для нужных конфигурационных битов и количества тиков, которым // присвоить значения, возвращаемые функциям getPrescalerBits и getTimerTicks соответсвенно. // Обе функции принимают два параметра – номер таймера и требуемую частоту. Функция // getPrescalerBits возвращает результат типа uint8_t. Функция getTimerTicks возвращает результат // типа uint16_t, т.к. таймер может быть 16-битным, для 8-битных таймеров значение не превышает 255. // // Например: // // #include <ConstTimers.h> // // // // Параметры для работы таймера/счётчика №2 на частоте 1кГц // // // constexpr uint8_t timerBits2 = getPrescalerBits(2, 1000); // constexpr uint8_t timerTicks2 = getTimerTicks(2, 1000); // // // // Параметры для работы таймера/счётчика №1 на частоте 500Гц // // // constexpr uint8_t timerBits1 = getPrescalerBits(1, 500); // constexpr uint16_t timerTicks1 = getTimerTicks(1, 500); // ............. // // // // Конфигурация таймеров // // таймер №2 // TCCR2B = timerBits2; // OCR2A = timerTicks2; // // таймер №1 // TCCR1B = timerBits1; // OCR1B = timerTicks1; // //Результат работы будет точно такой же, как если написать константы вместо вызовов функций: // // TCCR2B = 4; // OCR2A = 250; // TCCR1B = 1; // OCR1B = 32000; // // Ни на один байт код не изменится. // // КОФИГУРАЦИЯ: // // 1. расчёт производится для текущей тактовой частоты микроконтроллера. Если нужно // считать для какой-то другой частоты, измените константу ниже: // #define FCPU F_CPU // // 2. ниже приведён массив конфигурации таймеров. Количество элементов массива // соответсвует количеству таймеров. Нулевой элемент описывает нулевой таймер, // первый элемент – первый таймер и т.д. Если требуется расширить недобиблиотеку для других // микроконтроллеров, нужно изменить именно этот массив и больше изменять ничего не надо. // // В массиве для каждого таймера указано: // 1) разрядность таймера в виде максимально возможного значения количества тиков // (для 8-разрадных таймеров – 0xFF, для 16-разрядных – 0xFFFF. // 2) указатель на массив делителей частоты. Делители начинаются с 1 (делитель 0 писать не нужно) // 3) количество делителей частоты у данного таймера. // struct STimerParameters { const uint32_t maxValue; const int * prescalers; const uint8_t totalPrescalers; }; constexpr int prescalers01[] = {1, 8, 64, 256, 1024 }; constexpr int prescalers2[] = {1, 8, 32, 64, 128, 256, 1024 }; constexpr STimerParameters timerParameters[3] = { {0x000000FFul, prescalers01, sizeof(prescalers01) / sizeof(prescalers01[0])}, {0x0000FFFFul, prescalers01, sizeof(prescalers01) / sizeof(prescalers01[0])}, {0x000000FFul, prescalers2, sizeof(prescalers2) / sizeof(prescalers2[0])}, }; constexpr int8_t totalTimers = sizeof(timerParameters) / sizeof(timerParameters[0]); // // // ЛИЦЕНЗИЯ // // Данный код поставляется по лицензии ПНХ. // // 1. Вы можете свободно использовать или не использовать его в коммерческих, // некоммерческих, и любых иных, не запрещённых законом, целях. // // 2. Автор не несёт решительно никакой ответственности за любые положительные // или отрицательные результаты использования или неиспользования данного кода. // // 3. Если Вам таки хочется сделать автору предъяву, то Вы знаете, куда // Вам следует обратиться. А если не знаете, то см. название лицензии. // // 4. Если данный код вдруг Вам пригодился (как учебник или ещё как что) и Вам // почему-либо (ну, приболели, может) захотелось отблагодарить автора рублём, // то это всегда пожалуйста – WebMoney, кошелёк № R626206676373 // // 5. Возникновение или невозникновение у Вас желаний, обозначенных в п.4 // настоящей лицензии никак не связано с п.1, который действует безусловно // и независимо от п.4. // // 6. Если данный код нужен Вам с какой-либо другой лицензией, например, с // сопровождением или Вы нуждаетесь во внесении изменений, свяжитесь с автором // на предмет заключения договора гражданско-правового характера. // /////////////////////////////////////////////////////////////////////////////// constexpr uint32_t getPeriod(const uint32_t frequency) { return (FCPU + frequency / 2) / frequency; } constexpr uint16_t prValue(const int8_t prescalerId, const int8_t nTimer) { return timerParameters[nTimer].prescalers[prescalerId]; } constexpr uint32_t getDesiredTicks(const uint32_t frequency, const int8_t prescalerId, const int8_t nTimer) { return (getPeriod(frequency) + prValue(prescalerId, nTimer) / 2) / prValue(prescalerId, nTimer); } constexpr uint32_t correctTicks(uint32_t dTicks, const uint32_t maxValue) { return dTicks > maxValue ? maxValue : dTicks; } constexpr uint32_t getTicks(const uint32_t frequency, const int8_t prescalerId, const int8_t nTimer) { return prescalerId >= timerParameters[nTimer].totalPrescalers ? 0x1FFFFFFF : correctTicks(getDesiredTicks(frequency, prescalerId, nTimer), timerParameters[nTimer].maxValue); } constexpr uint32_t getBits(const int8_t prescalerId, const int8_t nTimer) { return prescalerId >= timerParameters[nTimer].totalPrescalers ? timerParameters[nTimer].totalPrescalers : prescalerId + 1; } constexpr int32_t absError(const uint32_t frequency, const int8_t prescalerId, const int8_t nTimer) { return abs(prescalerId >= timerParameters[nTimer].totalPrescalers ? 0x1FFFFFFF : static_cast<int32_t>(getTicks(frequency, prescalerId, nTimer) * prValue(prescalerId, nTimer)) - static_cast<int32_t>(getPeriod(frequency))); } constexpr uint8_t getPrescalerId(const uint32_t error, const uint32_t newError, const uint8_t prId, const uint8_t candidate, const uint32_t frequency, const int8_t nTimer) { return (prId >= timerParameters[nTimer].totalPrescalers) ? candidate : getPrescalerId(newError, absError(frequency, prId+1, nTimer), prId+1, (error <= newError) ? candidate : prId, frequency, nTimer); } constexpr uint16_t getTimerTicks(const int8_t nTimer, const uint32_t freq) { return getTicks(freq, getPrescalerId(0x1FFFFFul, absError(freq, 0, nTimer), 0, 0, freq, nTimer), nTimer); } constexpr uint8_t getPrescalerBits(const int8_t nTimer, const uint32_t freq) { return getBits(getPrescalerId(0x1FFFFFul, absError(freq, 0, nTimer), 0, 0, freq, nTimer), nTimer); } #endif // CONSTTIMERS_H
Модификация на другой микроконтроллер – всего лишь изменение одного трёхстрочного массива констант.
Ну, а теперь вопрос к апологетам идеи неприменимости С++ для микроконтроллеров: слабо повторить на чистом и кошерном?
Я не говорю сделать лучше (расход ресурсов – 0 и отрицательным он не станет), просто повторить также. Чтобы задача решалась, подбирались оптимальные (а не первые попавшиеся) величины, и чтобы это не ело память от слова совсем?
Кстати, чтобы не быть неправильно понятым, я вовсе не говорю, что это невозможно. Возможно, конечно (я - точно смогу), препроцессор – великая вещь. Но вот будет ли это также просто и элегантно? Или там гороху придётся скушать столько, что никакая диета не позволит? Попробуйте, чем голословно на С++ бочкотару катать. Задачка-то типично-микроконтроллерная - покажите неприменимость богомерзкого и ляпоту кошерного!
Ага, constexpr, изящно ;) Только инструмент не для начинающих, далеко не для. Спасибо, Евгений.
Так тут и нет заголовка "этюды для начинающих". Те же, к кому обращёны последние три абзаца, обычно считают себя гуру :)
Евгений, вы как человек науки всё ещё пытаетесь с верующими разговаривать на языке логики? "Богомерзкость" - это вполне достаточный для отрицания эффективности критерий.
Да, что Вы. Я не с ними разговариваю. Они - это так, к слову пришлись. Скорее, я разговариваю сам с собой.
Почему я стал писать это через constexpr? Как раз захотелось самому пощупать собственными пальцами, насколько это удобно/неудобно именно для такого типа задач. Вроде ничего, получилось, на препроцессоре было бы сложнее и менее удобно в изменениях. Честно говоря, давно не писал на функциональных языках - тяжеловато было мыслить в этой парадигме с непривычки.
А сюда выложил, когда "заметил", что памяти не ест вообще и на код никак не влияет. Знать-то я это (что не ест) и раньше, разумеется, знал (на то он и constexpr), но пока не написал как-то "неосознанно знал". А как увидел, так сразу - "Ух ты, блин! надо бы народу показать!" :) Вроде ж здесь на форуме про эту технику раньше не писали (или мимо меня прошло).
-------------
Кстати, в С++ есть ещё одна забавная и незаслуженно малораспространённая техника - "пользовательские литералы". Вот всё пытаюсь придумать под них "нативно-ардуиновскую" (не из пальца высосанную) задачу. Если придумаю - сделаю и выложу - покажу что это такое.
а я визард юзаю в cvavr для этого, собственно там и пишу. одно время помер, но щас возродился, добавили камней.
а я визард юзаю в cvavr для этого,
Для чего?
Для чего?
В СV есть vizard, в котором задаешь тики, предделители, время, тип прерывания , и он пишет заготовку кода. Типа Cube для STM32, но интегрирован в IDE CV. Кроме таймеров там можно все конфиги задать - прерывания, usart (с буфером), типы и ddr пинов, частоту, разгон (и он пишет фьюзы как надо), в общем весь конфиг фрагмент мк.
Генераторы кода есть и в других семействах. Правда их результат несколько каличный (не наглядный). Но, иногда бывает полезно сравнить со своим кодом, особенно когда свой не хочет как надо работать.)
Жалка, што я нихрена не понял.... :(
Захотели экспресии? Получайте )))
ЕвгенийП!
А для WAVGAT для третьего таймера, мне что, опять самому всё доделывать? )))
DetSemen!
Я тоже ничего не понял, но это же не мешает влезть своими кривыми ручками и поправить, всё же задокументировано! )
А для WAVGAT для третьего таймера, мне что, опять самому всё доделывать? )))
Я такого зверя и не видел никогда. Так что доделывайте, там делов-то - добавить элемент в массив timerParameters :)
Паша Хайдук - наш человек (соотечественник), создатель C компилятора CODEVISIONAVR с визардном для инициализации периферии. Одно время даже посещал наш известный российский форум.)
DetSimen, не прибедняйся :)
DetSimen, не прибедняйся :)
Ну, видима, я не до конца Страуса дочитал
Дед, думаю, ты меня троллишь, но так, на всякий случай, если вдруг, это мимо тебя прошло.
В 11-ом году появилось такое ключевое слово constexpr. Если не вдаваться в тонкости и детали, то ...
Применяется к переменным (на самом деле константам), функциям, статическим членам класса и конструкторам. В применении к переменным означает, что это на самом деле константа (в частности, обязана быть проинициализирована). В применении к функциям, конструкторам и т.д. - по большому счёту означает, что вызов такой функции может использоваться при инициализации constexpr-переменной.
Главная идея - constexpr-переменная должна быть вычислена на этапе компиляции и заменена в коде на своё значение (т.е. в коде она используется как готовый литерал). Отсюда и многочисленные ограничения не constexpr-функции (там много чего нельзя).
Собственно, всё. В моём примере четыре constexpr-переменные вычисляются на этапе компиляции и заменяются на вычисленные значения. Потому, результрующий код с использованием вычислений ничем (от слова "совсем") не отличается от кода с использованием готовых констант.
В принципе такое можно сделать и препроцессором, но там язык довольно специфический, нетривиальные вещи на нём тяжело писать. например, с циклом там столько гороху нажрёшься, а здесь пишешь на том же языке, что и всю программу.
К сожалению, наш компилятор поддерживает только С++11, а в нём constexpr-функция не имеет права содержать никаких исполняемых операторов, кроме return (глянь на мои функции). Уже в C++14 это органичение снято, но наш компилятор пока этого не поддерживает.
да неть, я constexpr то использовал, но только статически унутре класса, вместо чесного const, например, чтоб отдать ClassName (ну вот была такая задумка, щас от нее отказался). Но чтобы функции отдавали constexpr - до такова я не додумался бы, не гигант. Кстати, Страус тоже про это ничо не писал (памойму).
Слушай, а нет идей красивой задачи для "самогонного" литерала? О чём я в последнем абзаце #4 писал. Я бы примерчик сделал. Хочется, но задачу не могу придумать.
Слушай, а нет идей красивой задачи для "самогонного" литерала?
Хоть и название красивое, но я, к стыду своему, первый раз об этом слышу.
UPD. Нашол у себя constexpr. Вот так я это понимал
Это кусок из класса. А если б я разобрался, как это сразу во флэш встраивать...
UPD. UPD. Почитал за пользовательские литералы. Это не для моего скудоумия.
А для WAVGAT для третьего таймера, мне что, опять самому всё доделывать? )))
Я такого зверя и не видел никогда. Так что доделывайте, там делов-то - добавить элемент в массив timerParameters :)
Ан нет, у WAVGAT не все делители доступны для третьего таймера...
Посмотрел код, дополнить - это выше моих возможностей )))
UPD. UPD. Почитал за пользовательские литералы. Это не для моего скудоумия.
да, ладно. Все же пользуются литералами типа 100UL или там 100L. Здесь тоже самое, только свои хвосты определяем.
Вот пример для задания единиц времени в тиках контроллера. Т.е. я могу написать 1_sec - это будет 16000000 (при 16МГц) или там 2_us, в реальности это будет 32 (16*2). Т.е. перевод в тики спрятан за литералом.
Ан нет, у WAVGAT не все делители доступны для третьего таймера...
В смысле, есть пропущенные? Ну поставьте вместо них MAXINT, делов-то.Должно сработать
да я в общих чертах понял, канеш, про все эти ваши суффиксы, но сходу не могу придумать куда это можно применить, кто вот это вот всё придумал, и где сейчас эти люди.
Слушай, а нет идей красивой задачи для "самогонного" литерала? О чём я в последнем абзаце #4 писал. Я бы примерчик сделал. Хочется, но задачу не могу придумать.
Ну самый тупой из полезных примеров - это пересчет Вольт в целое, для использования с analogRead().
что-то типа: if (myAnalogValue > 2.35_V) ... Причем можно использовать рантайм вычисления и преобразовывать с учетом измерения питания... ну ты понял, что я имею ввиду? При обычном референсе Vcc, само значение референса уточнить через измерение своего питания. Оформить как библиотеку.
Больше, пока, ничего не приходит в голову....
Разве что: Если работаем с контроллером с достаточным к-вом памяти, то перекодировки символов.
Ан нет, у WAVGAT не все делители доступны для третьего таймера...
В смысле, есть пропущенные? Ну поставьте вместо них MAXINT, делов-то.Должно сработать
Не понимаю, у него вот так с делителями, значит брать как для таймера 1?
И вот тут не пойму, почему OCR1B а не OCR1A?
Ну, тогда вообще никаких проблем! Также точно как в моём массиве prescalers01.
Ну, тогда вообще никаких проблем! Также точно как в моём массиве prescalers01.
Это ясно, а по OCR1A?
Это ясно, а по OCR1A?
А он тут причём? Он в библиотеке вообще никак не используется. Поставьте правильный массив делителей и всё должно работать.
К сожалению, наш компилятор поддерживает только С++11, а в нём constexpr-функция не имеет права содержать никаких исполняемых операторов, кроме return (глянь на мои функции). Уже в C++14 это органичение снято, но наш компилятор пока этого не поддерживает.
Женя!!! Дарагой! Зачем такой неправда говоришь?!! ;)))
Всё с нашим компилятором хорошо! В platform.txt поменять gnu++11 на gnu++14 и всё прекрасно собирается. Сам компилятор полностью поддерживет 14 стандарт.
Я поменял одну тернарку у тебя в коде на простой if и результат 474 байта кода и 9 байт переменных, как и было.
Оцени: я даже не поленился проверить, что для меня - уже-таки немного подвиг!
Женя!!! Дарагой! Зачем такой неправда говоришь?!! ;)))
Сам компилятор полностью поддерживет 14 стандарт.
Вау!!! Ахренеть! А я трахался с этими "только return" - по сути на лиспе писал :((((
Ща, пойду проверять! Мож он gnu++17 понимает? Тогда и "if constexpr" можно будет юзать! :)
Отчёт.
1. При опции gnu++14 - фишки 14-го понимает (не все проверял, но что проверил - работает)
2. При опции gnu++17 - фишки 17-го понимает, но не все. В частности "if constexpr" не компилируется.
Эххх...
Зато разделители разрядов (тоже фишка 17-го) отлично пашут!
Это уже будет IDE 1.8.10 ;-)
мне тоже чота наданапица.
Птис классный, правда, дед?
классный. И на мня похож.
Отчёт.
1. При опции gnu++14 - фишки 14-го понимает (не все проверял, но что проверил - работает)
2. При опции gnu++17 - фишки 17-го понимает, но не все. В частности "if constexpr" не компилируется.
Я вот не проверял, как при gnu++14 все это потом в контроллере работает. Если ты все равно этим увлекся, то проверь что-нибудь собранное в стандарте 14 залить в контроллер. Я поеду на моте катаццо! Как раз новый шлем прикупил и наушники в него вклеил.
Так, Влад, заливал же ж! И в котроллер, и в протеус - то, что компилируется, то адекватно работает!
Стоит только выбрать плату WAVGAT и перестаёт компилироваться, а с платой LGT8F328P-LQFP32 всё компилируется:
Внимательно прочитайте 12-ую строчку, там всё написано, что Вам нужно знать, и выставьте нормальные опции.
Внимательно прочитайте 12-ую строчку, там всё написано, что Вам нужно знать, и выставьте нормальные опции.
я не знаю как это сделать, а то, что надо изменить опции компилятора усёк )))
Опции живут в файле platform.txt, нужная строчка начинается с "compiler.cpp.flags=" без кавычек
Опции живут в файле platform.txt, нужная строчка начинается с "compiler.cpp.flags=" без кавычек
Спасибо, поправил, работает )))
Только третий таймер у него конечно 16 битный но, представлен двумя восьмибитными регистрами, грузить которые надо раздельно, попробовал так, что-то не срослось:
Как харошо, что есть люди, которые во всем этом шарют. И есть, которые уже вхлам плюшевые. Как я. :-)
Как харошо, что есть люди, которые во всем этом шарют. И есть, которые уже вхлам плюшевые. Как я. :-)
В Москве еще без четверти 6, а я до 6 крепкое ни-ни! Так что пока вермутом разминаемся ;))) на льду.
Лыхаим! (то бишь: За жись!)
Лыхаим! Тяжолый был день, машину гавна растаскал по грядкам. Все болит. :-)
Только третий таймер у него конечно 16 битный но, представлен двумя восьмибитными регистрами, грузить которые надо раздельно, попробовал так, что-то не срослось:
И ещё, Вы обычные числа туда пихать умеете или нет? Мож там с какими сдвигами или масками? Так timerTicks1 - такое же обычное число.
Если хотите от меня какой-то разумной деятельности, то напишите словами что куда (в какой регистр) пихать надо, а ято я понятия не имею.
Только третий таймер у него конечно 16 битный но, представлен двумя восьмибитными регистрами, грузить которые надо раздельно, попробовал так, что-то не срослось:
И ещё, Вы обычные числа туда пихать умеете или нет? Мож там с какими сдвигами или масками? Так timerTicks1 - такое же обычное число.
Если хотите от меня какой-то разумной деятельности, то напишите словами что куда (в какой регистр) пихать надо, а ято я понятия не имею.
так пытаюсь, таймер 16 битный, но загружать его надо 8 битными значениями (регистры, их два (восьмибитных), OCR3AH и OCR3AL и загружать их надо сначала верхний, затем нижний
На самом деле, ради фразы
опции «из коробки»
можно и потрахаться. Это правильная фраза.
Условие в строке №1 не нужно. Достаточно написать ТОЛЬКО строки №№ 2 и 3.
А если уж хочется условие, то пишите правильно (255, а не 256).
Обычно у таких таймеров есть и общий 16-битный регистр. Например, в 329З есть и OCR1AH/OCR1AL, и общий OCR1A.
Так как Вы написали (с учётом ощшибки) должно работать. Если не работает, то виновата не библиотека. отладьте с голимыми числами и когда заработает, перейдёит на библиотеку. Разницы никакой.
можно и потрахаться.
Особенно, когда трахается кто-то другой :)
Условие в строке №1 не нужно. Достаточно написать ТОЛЬКО строки №№ 2 и 3.
А если уж хочется условие, то пишите правильно (255, а не 256).
Обычно у таких таймеров есть и общий 16-битный регистр. Например, в 329З есть и OCR1AH/OCR1AL, и общий OCR1A.
Так как Вы написали (с учётом ощшибки) должно работать. Если не работает, то виновата не библиотека. отладьте с голимыми числами и когда заработает, перейдёит на библиотеку. Разницы никакой.
что значит его величество ОПЫТ, скосячил однако с 256 )))