C++ в микроконтроллерах, на примере типичной задачи

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

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

Давно хотелось иметь возможность вычислять конфигурационные биты делителя и количество тиков прямо в программе, чтобы я только частоту задавал, но всё как-то было «не до сук».

И вот, наконец, я таки собрался и написал некую «недобибилиотеку», которая это делает. Почему «недо-»? Ну, хотя бы потому, что в отличие от нормальной ардуиновской библиотеки, моя плевать хотела на всё, кроме ATmega328P. Думаю, в ближайшее время расширю на ATtiny85 и ATmega32U, но и всё, т.к. ничем другим я почти не пользуюсь.

Итак, задача:

по номеру таймера и требуемой частоте ногодрыга, подобрать такие делитель частоты таймера и количество тиков, чтобы получившаяся частота ногодрыга была наиболее точной из всех возможных на данном таймере. Делитель частоты выдать в виде готовых конфигурационных битов для записи в TCCRхB.

Речь идёт именно о наилучшем приближении, т.к. некоторые частоты невозможно получить точно (например, 3кГц). Там, где точное решение возможно, разумеется должно выдаваться именно оно.

Ну, вроде, написал и написал, а вам то зачем мозги пудрю?

А вот зачем. Недобиблиотеку я писал на самом что ни на есть богомерзком, гнусном С++ (даже с возможностями, появившимися только в 11-ом году), который, как всем известно: «неприменим на микроконтроллерах и на котором для микроконтроллеров пишут только дураки и преподы». И вот, решил я проверить сколько памяти отжирает моя библиотека. Какова, так сказать, цена вопроса?

Для проверки я написал такой в меру бессмысленный скетч. В нём производится «частичная» инициализация таймеров, причём если #define в строке №3 закомментировать, то работает моя библиотека, а если оставить на месте, то всё, связанное с библиотекой, выбрасывается нафиг, а в порты пишутся голимые числовые константы. Вот этот скетч, можете сами убедиться, что так оно и есть.

Пробуем компилировать (IDE 1.8.9, опции «из коробки»). И что же видим?

С константами: 474 байт (1%) памяти устройства ... 9 байт (0%) динамической памяти
С библиотекой: 474 байт (1%) памяти устройства ... 9 байт (0%) динамической памяти

Найдите отличия!

А вот и сама недобиблиотека. Как видите, если убрать огромный комментарий в начале, то она состоит всего из 60 строк – десять функций, самая большая из которых – 5 строк.

Модификация на другой микроконтроллер – всего лишь изменение одного трёхстрочного массива констант.

Ну, а теперь вопрос к апологетам идеи неприменимости С++ для микроконтроллеров: слабо повторить на чистом и кошерном?

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

Кстати, чтобы не быть неправильно понятым, я вовсе не говорю, что это невозможно. Возможно, конечно (я - точно смогу), препроцессор – великая вещь. Но вот будет ли это также просто и элегантно? Или там гороху придётся скушать столько, что никакая диета не позволит? Попробуйте, чем голословно на С++ бочкотару катать. Задачка-то типично-микроконтроллерная - покажите неприменимость богомерзкого и ляпоту кошерного!

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Ага, constexpr, изящно ;) Только инструмент не для начинающих, далеко не для. Спасибо, Евгений.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Так тут и нет заголовка "этюды для начинающих". Те же, к кому обращёны последние три абзаца, обычно считают себя гуру :)

sadman41
Offline
Зарегистрирован: 19.10.2016

Евгений, вы как человек науки всё ещё пытаетесь с верующими разговаривать на языке логики? "Богомерзкость" - это вполне достаточный для отрицания эффективности критерий.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Да, что Вы. Я не с ними разговариваю. Они - это так, к слову пришлись. Скорее, я разговариваю сам с собой.

Почему я стал писать это через constexpr? Как раз захотелось самому пощупать собственными пальцами, насколько это удобно/неудобно именно для такого типа задач. Вроде ничего, получилось, на препроцессоре было бы сложнее и менее удобно в изменениях. Честно говоря, давно не писал на функциональных языках - тяжеловато было мыслить в этой парадигме с непривычки.

А сюда выложил, когда "заметил", что памяти не ест вообще и на код никак не влияет. Знать-то я это (что не ест) и раньше, разумеется, знал (на то он и constexpr), но пока не написал как-то "неосознанно знал". А как увидел, так сразу - "Ух ты, блин! надо бы народу показать!" :) Вроде ж здесь на форуме про эту технику раньше не писали (или мимо меня прошло).

-------------

Кстати, в С++ есть ещё одна забавная и незаслуженно малораспространённая техника - "пользовательские литералы". Вот всё пытаюсь придумать под них "нативно-ардуиновскую" (не из пальца высосанную) задачу. Если придумаю - сделаю и выложу - покажу что это такое.

MacSim
Offline
Зарегистрирован: 28.11.2012

а я визард юзаю в cvavr  для этого, собственно там и пишу. одно время помер, но щас возродился, добавили камней.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

MacSim пишет:

а я визард юзаю в cvavr  для этого, 

Для чего?

Onkel
Offline
Зарегистрирован: 22.02.2016

ЕвгенийП пишет:

Для чего?

В СV есть vizard, в котором задаешь тики, предделители, время, тип прерывания , и он пишет заготовку кода. Типа Cube для STM32, но интегрирован в IDE CV. Кроме таймеров там можно все конфиги задать - прерывания, usart (с буфером), типы и ddr пинов, частоту, разгон (и он пишет фьюзы как надо), в общем весь конфиг фрагмент мк.

Green
Offline
Зарегистрирован: 01.10.2015

Генераторы кода есть и в других семействах. Правда их результат несколько каличный (не наглядный). Но, иногда бывает полезно сравнить со своим кодом, особенно когда свой не хочет как надо работать.)

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Жалка, што я нихрена не понял....  :( 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

Захотели экспресии? Получайте )))

ЕвгенийП!
А для WAVGAT для третьего таймера, мне что, опять самому всё доделывать?  )))

DetSemen!
Я тоже ничего не понял, но это же не мешает влезть своими кривыми ручками и поправить, всё же задокументировано! )

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

ua6em пишет:

А для WAVGAT для третьего таймера, мне что, опять самому всё доделывать?  ))) 

Я такого зверя и не видел никогда. Так что доделывайте, там делов-то - добавить элемент в массив timerParameters :)

Green
Offline
Зарегистрирован: 01.10.2015

Паша Хайдук - наш человек (соотечественник), создатель C компилятора CODEVISIONAVR с визардном для инициализации периферии. Одно время даже посещал наш известный российский форум.)

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

DetSimen, не прибедняйся :)

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

ЕвгенийП пишет:

DetSimen, не прибедняйся :)

Ну, видима, я не до конца Страуса дочитал

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Дед, думаю, ты меня троллишь, но так, на всякий случай, если вдруг, это мимо тебя прошло.

В 11-ом году появилось такое ключевое слово constexpr.  Если не вдаваться в тонкости и детали, то ...

Применяется к переменным (на самом деле константам), функциям, статическим членам класса и конструкторам. В применении к переменным означает, что это на самом деле константа (в частности, обязана быть проинициализирована). В применении к функциям, конструкторам и т.д. - по большому счёту означает, что вызов такой функции может использоваться при инициализации constexpr-переменной.

Главная идея - constexpr-переменная должна быть вычислена на этапе компиляции и заменена в коде на своё значение (т.е. в коде она используется как готовый литерал). Отсюда и многочисленные ограничения не constexpr-функции (там много чего нельзя).

Собственно, всё. В моём примере четыре constexpr-переменные вычисляются на этапе компиляции и заменяются на вычисленные значения. Потому, результрующий код с использованием вычислений ничем (от слова "совсем") не отличается от кода с использованием готовых констант.

В принципе такое можно сделать и препроцессором, но там язык довольно специфический, нетривиальные вещи на нём тяжело писать. например, с циклом там столько гороху нажрёшься, а здесь пишешь на том же языке, что и всю программу.

К сожалению, наш компилятор поддерживает только С++11, а в нём constexpr-функция не имеет права содержать никаких исполняемых операторов, кроме return (глянь на мои функции). Уже в C++14 это органичение снято, но наш компилятор пока этого не поддерживает.

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

да неть, я constexpr то использовал, но только статически унутре класса, вместо чесного const, например, чтоб отдать ClassName (ну вот была такая задумка, щас от нее отказался).  Но чтобы функции отдавали constexpr - до такова я не додумался бы, не гигант.  Кстати, Страус тоже про это ничо не писал (памойму). 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Слушай, а нет идей красивой задачи для "самогонного" литерала? О чём я в последнем абзаце #4 писал. Я бы примерчик сделал. Хочется, но задачу не могу придумать.

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

ЕвгенийП пишет:

Слушай, а нет идей красивой задачи для "самогонного" литерала?

Хоть и название красивое, но я, к стыду своему, первый раз об этом слышу. 

UPD. Нашол у себя constexpr.   Вот так я это понимал

1public:
2 
3    static constexpr const char *ClassName { "TDS3231" };
4 
5    TDS3231() :TDS3231(DEV_DEFAULT_ADDRESS) {};

Это кусок из класса.  А если б я разобрался, как это сразу во флэш встраивать...

 

UPD. UPD.  Почитал за пользовательские литералы.  Это не для моего скудоумия.  

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

ЕвгенийП пишет:

ua6em пишет:

А для WAVGAT для третьего таймера, мне что, опять самому всё доделывать?  ))) 

Я такого зверя и не видел никогда. Так что доделывайте, там делов-то - добавить элемент в массив timerParameters :)

Ан нет, у WAVGAT не все делители доступны для третьего таймера...
Посмотрел код, дополнить - это выше моих возможностей )))

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

DetSimen пишет:

UPD. UPD.  Почитал за пользовательские литералы.  Это не для моего скудоумия.  

да, ладно. Все же пользуются литералами типа 100UL или там 100L. Здесь тоже самое, только свои хвосты определяем.

Вот пример для задания единиц времени в тиках контроллера. Т.е. я могу написать 1_sec - это будет 16000000 (при 16МГц) или там 2_us, в реальности это будет 32 (16*2). Т.е. перевод в тики спрятан за литералом.

01#include <Printing.h>
02 
03//
04//  Определяем суффиксы времени.
05//  Результат - время в тиках контроллера
06//
07uint32_t operator "" _tick(const char * s) { return strtoul(s, NULL, 10); }
08uint32_t operator "" _us(const char * s) { return operator "" _tick(s) * (F_CPU/1000000ul); }
09uint32_t operator "" _ms(const char * s) { return operator "" _us(s) * 1000ul; }
10uint32_t operator "" _sec(const char * s) { return operator "" _ms(s) * 1000ul; }
11 
12void setup(void) {
13    Serial.begin(57600);
14    Serial << "1 tick = " << 1_tick << " ticks\r\n";
15    Serial << "1 us = " << 1_us << " ticks\r\n";
16    Serial << "1 ms = " << 1_ms << " ticks\r\n";
17    Serial << "1 sec = " << 1_sec << " ticks\r\n";
18 
19    Serial << "2 us = " << 2_us << " ticks\r\n";
20    Serial << "4 ms = " << 4_ms << " ticks\r\n";
21    Serial << "5 sec = " << 5_sec << " ticks\r\n";
22}
23 
24void loop(void) {}

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

ua6em пишет:

Ан нет, у WAVGAT не все делители доступны для третьего таймера...

В смысле, есть пропущенные? Ну поставьте вместо них MAXINT, делов-то.Должно сработать

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

да я в общих чертах понял, канеш, про все эти ваши суффиксы, но сходу не могу придумать куда это можно применить, кто вот это вот всё придумал, и где сейчас эти люди. 

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

ЕвгенийП пишет:

Слушай, а нет идей красивой задачи для "самогонного" литерала? О чём я в последнем абзаце #4 писал. Я бы примерчик сделал. Хочется, но задачу не могу придумать.

Ну самый тупой из полезных примеров - это пересчет Вольт в целое, для использования с analogRead().

что-то типа: if (myAnalogValue > 2.35_V) ...  Причем можно использовать рантайм вычисления и преобразовывать с учетом измерения питания... ну ты понял, что я имею ввиду? При обычном референсе Vcc, само значение референса уточнить через  измерение своего питания. Оформить как библиотеку.

Больше, пока, ничего не приходит в голову....

Разве что: Если работаем с контроллером с достаточным к-вом памяти, то перекодировки символов.

 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

ЕвгенийП пишет:

ua6em пишет:

Ан нет, у WAVGAT не все делители доступны для третьего таймера...

В смысле, есть пропущенные? Ну поставьте вместо них MAXINT, делов-то.Должно сработать

Не понимаю, у него вот так с делителями, значит брать как для таймера 1?

И вот тут не пойму, почему OCR1B а не OCR1A?

01#ifdef CONSTANTS
02  TCCR2B = 4;
03  OCR2A = 250;
04  TCCR1B = 1;
05  OCR1B = 32000;
06#else
07  TCCR2B = timerBits2;
08  OCR2A = timerTicks2;
09  TCCR1B = timerBits1;
10  OCR1B = timerTicks1;
11#endif

 

01#include "lgtx8p.h"
02enum Prescalers {
03   PRESCALER_STOP = 0,
04   PRESCALER_1 = 1,
05   PRESCALER_8 = 2,
06// PRESCALER_32 = 3,
07   PRESCALER_64 = 3,
08// PRESCALER_128 = 5,
09   PRESCALER_256 = 4,
10   PRESCALER_1024 = 5
11};

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Ну, тогда вообще никаких проблем! Также точно как в моём массиве prescalers01.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

ЕвгенийП пишет:

Ну, тогда вообще никаких проблем! Также точно как в моём массиве prescalers01.

Это ясно, а по OCR1A?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

wdrakula пишет:
можно использовать рантайм вычисления и преобразовывать с учетом измерения питания...
Мысль интересная. Я как-то (совершенно необоснованно) всегда воспринимал литералы, как константы. Но, ведь нет, они вполне могут в run-time вычисляться. Спасибо.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

ua6em пишет:

Это ясно, а по OCR1A?

А он тут причём? Он в библиотеке вообще никак не используется. Поставьте правильный массив делителей и всё должно работать.

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

ЕвгенийП пишет:

К сожалению, наш компилятор поддерживает только С++11, а в нём constexpr-функция не имеет права содержать никаких исполняемых операторов, кроме return (глянь на мои функции). Уже в C++14 это органичение снято, но наш компилятор пока этого не поддерживает.

Женя!!! Дарагой! Зачем такой неправда говоришь?!! ;)))

Всё с нашим компилятором хорошо! В platform.txt поменять gnu++11 на gnu++14 и всё прекрасно собирается. Сам компилятор полностью поддерживет 14 стандарт.

Я поменял одну тернарку у тебя в коде на простой if и результат 474 байта кода  и 9 байт переменных, как и было.

Оцени: я даже не поленился проверить, что для меня - уже-таки немного подвиг!

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

wdrakula пишет:

Женя!!! Дарагой! Зачем такой неправда говоришь?!! ;)))

Сам компилятор полностью поддерживет 14 стандарт.

Вау!!! Ахренеть! А я трахался с этими "только return" - по сути на лиспе писал :((((

Ща, пойду проверять! Мож он gnu++17 понимает? Тогда и "if constexpr" можно будет юзать! :)

Отчёт.

1. При опции gnu++14 - фишки 14-го понимает (не все проверял, но что проверил - работает)
2. При опции gnu++17 - фишки 17-го понимает, но не все. В частности "if constexpr" не компилируется.

Эххх...

Зато разделители разрядов (тоже фишка 17-го) отлично пашут!

1void setup(void) {
2    Serial.begin(57600);
3    long frequency = 16'000'000;
4    Serial.print(frequency);
5}
6 
7void loop(void) {}

 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

Это уже будет IDE 1.8.10  ;-)

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

мне тоже чота наданапица. 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Птис классный, правда, дед?

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

классный. И на мня похож. 

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

ЕвгенийП пишет:

Отчёт.

1. При опции gnu++14 - фишки 14-го понимает (не все проверял, но что проверил - работает)
2. При опции gnu++17 - фишки 17-го понимает, но не все. В частности "if constexpr" не компилируется.

Я вот не проверял, как при gnu++14 все это потом в контроллере работает. Если ты все равно этим увлекся, то проверь что-нибудь собранное в стандарте 14 залить в контроллер. Я поеду на моте катаццо! Как раз новый шлем прикупил и наушники в него вклеил.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Так, Влад, заливал же ж! И в котроллер, и в протеус - то, что компилируется, то адекватно работает!

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

Стоит только выбрать плату WAVGAT и перестаёт компилироваться, а с платой LGT8F328P-LQFP32 всё компилируется:
 

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Внимательно прочитайте 12-ую строчку, там всё написано, что Вам нужно знать,  и выставьте нормальные опции.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

ЕвгенийП пишет:

Внимательно прочитайте 12-ую строчку, там всё написано, что Вам нужно знать,  и выставьте нормальные опции.

я не знаю как это сделать, а то, что надо изменить опции компилятора усёк )))

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Опции живут в файле platform.txt, нужная строчка начинается с "compiler.cpp.flags=" без кавычек

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

ЕвгенийП пишет:

Опции живут в файле platform.txt, нужная строчка начинается с "compiler.cpp.flags=" без кавычек

Спасибо, поправил, работает )))
Только третий таймер у него конечно 16 битный но, представлен двумя восьмибитными регистрами, грузить которые надо раздельно, попробовал так, что-то не срослось:
 

 

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Как харошо, что есть люди, которые во всем этом шарют. И есть, которые уже вхлам плюшевые. Как я. :-)

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

DetSimen пишет:

Как харошо, что есть люди, которые во всем этом шарют. И есть, которые уже вхлам плюшевые. Как я. :-)

В Москве еще без четверти 6, а я до 6 крепкое ни-ни! Так что пока вермутом разминаемся ;))) на льду.

Лыхаим! (то бишь: За жись!)

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Лыхаим!  Тяжолый был день, машину гавна растаскал по грядкам. Все болит. :-)

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

ua6em пишет:

Только третий таймер у него конечно 16 битный но, представлен двумя восьмибитными регистрами, грузить которые надо раздельно, попробовал так, что-то не срослось:

И ещё, Вы обычные числа туда пихать умеете или нет? Мож там с какими сдвигами или масками? Так timerTicks1 - такое же обычное число.

Если хотите от меня какой-то разумной деятельности, то напишите словами что куда (в какой регистр) пихать надо, а ято я понятия не имею.

 
ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

ЕвгенийП пишет:

ua6em пишет:

Только третий таймер у него конечно 16 битный но, представлен двумя восьмибитными регистрами, грузить которые надо раздельно, попробовал так, что-то не срослось:

И ещё, Вы обычные числа туда пихать умеете или нет? Мож там с какими сдвигами или масками? Так timerTicks1 - такое же обычное число.

Если хотите от меня какой-то разумной деятельности, то напишите словами что куда (в какой регистр) пихать надо, а ято я понятия не имею.

так пытаюсь, таймер 16 битный, но загружать его надо 8 битными значениями (регистры, их два (восьмибитных), OCR3AH и OCR3AL и загружать их надо сначала верхний, затем нижний

1if(timerTicks3 > 256){
2        OCR3AH = timerTicks3 / 256;
3        OCR3AL = timerTicks3 % 256;
4        }else{
5        OCR3AH = 0x00;
6        OCR3AL = timerTicks3;
7}

 

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

ЕвгенийП пишет:
А я трахался с этими "только return" - по сути на лиспе писал :((((

На самом деле, ради фразы

ЕвгенийП пишет:

опции «из коробки»

можно и потрахаться. Это правильная фраза.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

ua6em пишет:

1if(timerTicks3 > 256){
2        OCR3AH = timerTicks3 / 256;
3        OCR3AL = timerTicks3 % 256;
4        }else{
5        OCR3AH = 0x00;
6        OCR3AL = timerTicks3;
7}

Условие в строке №1 не нужно. Достаточно написать ТОЛЬКО строки №№ 2 и 3.

А если уж хочется условие, то пишите правильно (255, а не 256).

Обычно у таких таймеров есть и общий 16-битный регистр. Например, в 329З есть и OCR1AH/OCR1AL, и общий OCR1A.

Так как Вы написали (с учётом ощшибки) должно работать. Если не работает, то виновата не библиотека. отладьте с голимыми числами и когда заработает, перейдёит на библиотеку. Разницы никакой.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Ворота пишет:

можно и потрахаться. 

Особенно, когда трахается кто-то другой :)

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

ЕвгенийП пишет:

ua6em пишет:

1if(timerTicks3 > 256){
2        OCR3AH = timerTicks3 / 256;
3        OCR3AL = timerTicks3 % 256;
4        }else{
5        OCR3AH = 0x00;
6        OCR3AL = timerTicks3;
7}

Условие в строке №1 не нужно. Достаточно написать ТОЛЬКО строки №№ 2 и 3.

А если уж хочется условие, то пишите правильно (255, а не 256).

Обычно у таких таймеров есть и общий 16-битный регистр. Например, в 329З есть и OCR1AH/OCR1AL, и общий OCR1A.

Так как Вы написали (с учётом ощшибки) должно работать. Если не работает, то виновата не библиотека. отладьте с голимыми числами и когда заработает, перейдёит на библиотеку. Разницы никакой.

что значит его величество ОПЫТ, скосячил однако с 256 )))