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

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

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

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

И вот, наконец, я таки собрался и написал некую «недобибилиотеку», которая это делает. Почему «недо-»? Ну, хотя бы потому, что в отличие от нормальной ардуиновской библиотеки, моя плевать хотела на всё, кроме 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 и отрицательным он не станет), просто повторить также. Чтобы задача решалась, подбирались оптимальные (а не первые попавшиеся) величины, и чтобы это не ело память от слова совсем?

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

MacSim пишет:

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

Для чего?

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

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

Для чего?

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

Green
Онлайн
Зарегистрирован: 01.10.2015

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

DetSimen
DetSimen аватар
Онлайн
Зарегистрирован: 25.01.2017

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

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

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

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

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

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

ua6em пишет:

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

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

Green
Онлайн
Зарегистрирован: 01.10.2015

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

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

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

DetSimen
DetSimen аватар
Онлайн
Зарегистрирован: 25.01.2017

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

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

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

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

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

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

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

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

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

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

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

DetSimen
DetSimen аватар
Онлайн
Зарегистрирован: 25.01.2017

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

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

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

DetSimen
DetSimen аватар
Онлайн
Зарегистрирован: 25.01.2017

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

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

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

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

public:

	static constexpr const char *ClassName { "TDS3231" };

	TDS3231() :TDS3231(DEV_DEFAULT_ADDRESS) {};

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

 

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

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

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

ua6em пишет:

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

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

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

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

DetSimen пишет:

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

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

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

#include <Printing.h>

//
//	Определяем суффиксы времени.
//	Результат - время в тиках контроллера
//
uint32_t operator "" _tick(const char * s) { return strtoul(s, NULL, 10); }
uint32_t operator "" _us(const char * s) { return operator "" _tick(s) * (F_CPU/1000000ul); }
uint32_t operator "" _ms(const char * s) { return operator "" _us(s) * 1000ul; }
uint32_t operator "" _sec(const char * s) { return operator "" _ms(s) * 1000ul; }

void setup(void) {
	Serial.begin(57600);
	Serial << "1 tick = " << 1_tick << " ticks\r\n";
	Serial << "1 us = " << 1_us << " ticks\r\n";
	Serial << "1 ms = " << 1_ms << " ticks\r\n";
	Serial << "1 sec = " << 1_sec << " ticks\r\n";

	Serial << "2 us = " << 2_us << " ticks\r\n";
	Serial << "4 ms = " << 4_ms << " ticks\r\n";
	Serial << "5 sec = " << 5_sec << " ticks\r\n";
}

void loop(void) {}

 

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

ua6em пишет:

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

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

DetSimen
DetSimen аватар
Онлайн
Зарегистрирован: 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?

  #ifdef CONSTANTS
    TCCR2B = 4;
    OCR2A = 250;
    TCCR1B = 1;
    OCR1B = 32000;
  #else
    TCCR2B = timerBits2;
    OCR2A = timerTicks2;
    TCCR1B = timerBits1;
    OCR1B = timerTicks1;
  #endif

 

#include "lgtx8p.h"
enum Prescalers {
   PRESCALER_STOP = 0,
   PRESCALER_1 = 1, 
   PRESCALER_8 = 2, 
// PRESCALER_32 = 3, 
   PRESCALER_64 = 3, 
// PRESCALER_128 = 5, 
   PRESCALER_256 = 4, 
   PRESCALER_1024 = 5
};

 

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

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

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

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

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

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

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

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

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 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 байт переменных, как и было.

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

 

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

wdrakula пишет:

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

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

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

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

Отчёт.

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

Эххх...

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

void setup(void) {
	Serial.begin(57600);
	long frequency = 16'000'000;
	Serial.print(frequency);
}

void loop(void) {}

 

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

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

DetSimen
DetSimen аватар
Онлайн
Зарегистрирован: 25.01.2017

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

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

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

DetSimen
DetSimen аватар
Онлайн
Зарегистрирован: 25.01.2017

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

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

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

Отчёт.

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

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

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

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

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

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

Arduino: 1.8.8 (Windows XP), Плата:"WAVGAT NANO 3.0"

Внимание: platform.txt из ядра 'Arduino AVR Boards' содержит устаревшие recipe.ar.pattern="{compiler.path}{compiler.ar.cmd}" {compiler.ar.flags} {compiler.ar.extra_flags} "{build.path}/{archive_file}" "{object_file}", автоматически преобразовано в recipe.ar.pattern="{compiler.path}{compiler.ar.cmd}" {compiler.ar.flags} {compiler.ar.extra_flags} "{archive_file_path}" "{object_file}". Ожидайте обновления ядра.
In file included from D:\000\Timer-ALL_LIB-WAVGAT\Timer-ALL_LIB-WAVGAT.ino:7:0:

ConstTimers.h:91:1: error: 'constexpr' does not name a type

 constexpr  int prescalers01[] = {1, 8, 64, 256, 1024 };

 ^

sketch\ConstTimers.h:91:1: note: C++11 'constexpr' only available with -std=c++11 or -std=gnu++11

ConstTimers.h:92:1: error: 'constexpr' does not name a type

 constexpr  int  prescalers2[] = {1, 8, 32, 64, 128, 256, 1024 };

 ^

sketch\ConstTimers.h:92:1: note: C++11 'constexpr' only available with -std=c++11 or -std=gnu++11

ConstTimers.h:94:1: error: 'constexpr' does not name a type

 constexpr STimerParameters timerParameters[3] = {

 ^

sketch\ConstTimers.h:94:1: note: C++11 'constexpr' only available with -std=c++11 or -std=gnu++11

ConstTimers.h:99:1: error: 'constexpr' does not name a type

 constexpr int8_t totalTimers = sizeof(timerParameters) / sizeof(timerParameters[0]);

 ^

sketch\ConstTimers.h:99:1: note: C++11 'constexpr' only available with -std=c++11 or -std=gnu++11

ConstTimers.h:129:1: error: 'constexpr' does not name a type

 constexpr uint32_t getPeriod(const uint32_t frequency) {

 ^

sketch\ConstTimers.h:129:1: note: C++11 'constexpr' only available with -std=c++11 or -std=gnu++11

ConstTimers.h:133:1: error: 'constexpr' does not name a type

 constexpr uint16_t prValue(const int8_t prescalerId, const int8_t nTimer) {

 ^

sketch\ConstTimers.h:133:1: note: C++11 'constexpr' only available with -std=c++11 or -std=gnu++11

ConstTimers.h:137:1: error: 'constexpr' does not name a type

 constexpr uint32_t getDesiredTicks(const uint32_t frequency, const int8_t prescalerId, const int8_t nTimer) {

 ^

sketch\ConstTimers.h:137:1: note: C++11 'constexpr' only available with -std=c++11 or -std=gnu++11

ConstTimers.h:141:1: error: 'constexpr' does not name a type

 constexpr uint32_t correctTicks(uint32_t dTicks, const uint32_t maxValue) {

 ^

sketch\ConstTimers.h:141:1: note: C++11 'constexpr' only available with -std=c++11 or -std=gnu++11

ConstTimers.h:145:1: error: 'constexpr' does not name a type

 constexpr uint32_t getTicks(const uint32_t frequency, const int8_t prescalerId, const int8_t nTimer) {

 ^

sketch\ConstTimers.h:145:1: note: C++11 'constexpr' only available with -std=c++11 or -std=gnu++11

ConstTimers.h:150:1: error: 'constexpr' does not name a type

 constexpr uint32_t getBits(const int8_t prescalerId, const int8_t nTimer) {

 ^

sketch\ConstTimers.h:150:1: note: C++11 'constexpr' only available with -std=c++11 or -std=gnu++11

ConstTimers.h:155:1: error: 'constexpr' does not name a type

 constexpr int32_t absError(const uint32_t frequency, const int8_t prescalerId, const int8_t nTimer) {

 ^

sketch\ConstTimers.h:155:1: note: C++11 'constexpr' only available with -std=c++11 or -std=gnu++11

ConstTimers.h:161:1: error: 'constexpr' does not name a type

 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) {

 ^

sketch\ConstTimers.h:161:1: note: C++11 'constexpr' only available with -std=c++11 or -std=gnu++11

ConstTimers.h:167:1: error: 'constexpr' does not name a type

 constexpr uint16_t getTimerTicks(const int8_t nTimer, const uint32_t freq) {

 ^

sketch\ConstTimers.h:167:1: note: C++11 'constexpr' only available with -std=c++11 or -std=gnu++11

ConstTimers.h:171:1: error: 'constexpr' does not name a type

 constexpr uint8_t getPrescalerBits(const int8_t nTimer, const uint32_t freq) {

 ^

sketch\ConstTimers.h:171:1: note: C++11 'constexpr' only available with -std=c++11 or -std=gnu++11

Timer-ALL_LIB-WAVGAT:12:3: error: 'constexpr' does not name a type

   constexpr uint8_t timerBits2 = getPrescalerBits(2, 1000);

   ^

D:\000\Timer-ALL_LIB-WAVGAT\Timer-ALL_LIB-WAVGAT.ino:12:3: note: C++11 'constexpr' only available with -std=c++11 or -std=gnu++11

Timer-ALL_LIB-WAVGAT:13:3: error: 'constexpr' does not name a type

   constexpr uint8_t timerTicks2 = getTimerTicks(2, 1000);

   ^

D:\000\Timer-ALL_LIB-WAVGAT\Timer-ALL_LIB-WAVGAT.ino:13:3: note: C++11 'constexpr' only available with -std=c++11 or -std=gnu++11

Timer-ALL_LIB-WAVGAT:17:3: error: 'constexpr' does not name a type

   constexpr uint8_t timerBits1 = getPrescalerBits(1, 500);

   ^

D:\000\Timer-ALL_LIB-WAVGAT\Timer-ALL_LIB-WAVGAT.ino:17:3: note: C++11 'constexpr' only available with -std=c++11 or -std=gnu++11

Timer-ALL_LIB-WAVGAT:18:3: error: 'constexpr' does not name a type

   constexpr uint16_t timerTicks1 = getTimerTicks(1, 500);

   ^

D:\000\Timer-ALL_LIB-WAVGAT\Timer-ALL_LIB-WAVGAT.ino:18:3: note: C++11 'constexpr' only available with -std=c++11 or -std=gnu++11

D:\000\Timer-ALL_LIB-WAVGAT\Timer-ALL_LIB-WAVGAT.ino: In function 'void setup()':

Timer-ALL_LIB-WAVGAT:30:14: error: 'timerBits1' was not declared in this scope

     TCCR3B = timerBits1;

              ^

Timer-ALL_LIB-WAVGAT:31:13: error: 'timerTicks1' was not declared in this scope

     OCR3A = timerTicks1;

             ^

Timer-ALL_LIB-WAVGAT:32:14: error: 'timerBits2' was not declared in this scope

     TCCR2B = timerBits2;

              ^

Timer-ALL_LIB-WAVGAT:33:13: error: 'timerTicks2' was not declared in this scope

     OCR2A = timerTicks2;

             ^

D:\000\Timer-ALL_LIB-WAVGAT\Timer-ALL_LIB-WAVGAT.ino: In function 'void loop()':

Timer-ALL_LIB-WAVGAT:39:20: error: 'timerBits1' was not declared in this scope

     Serial.println(timerBits1);

                    ^

Timer-ALL_LIB-WAVGAT:41:23: error: 'timerTicks1' was not declared in this scope

        Serial.println(timerTicks1);

                       ^

Timer-ALL_LIB-WAVGAT:44:20: error: 'timerBits2' was not declared in this scope

     Serial.println(timerBits2);

                    ^

Timer-ALL_LIB-WAVGAT:46:24: error: 'timerTicks2' was not declared in this scope

         Serial.println(timerTicks2);

                        ^

exit status 1
'constexpr' does not name a type

Этот отчёт будет иметь больше информации с
включенной опцией Файл -> Настройки ->
"Показать подробный вывод во время компиляции"

 

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

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

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

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

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

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

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

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

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

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

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

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

// Если define ниже закомментировать, то работает библиотека
// Если же оставить, то простое присваивание числовых констант
//#define CONSTANTS 
 #include "lgtx8p.h"

#ifndef CONSTANTS
  #include "ConstTimers.h"
  
  // Параметры для работы таймера/счётчика №3 на частоте 73Гц
  //
  constexpr uint8_t timerBits3 = getPrescalerBits(1, 73);
  constexpr uint16_t timerTicks3 = getTimerTicks(1, 73);
  //
  // Параметры для работы таймера/счётчика №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) {
  Serial.begin(115200);
  
  #ifdef CONSTANTS
    TCCR3B = 4;
    OCR3A = 250;
    TCCR2B = 1;
    OCR2A = 32000;
    TCCR1B = 1;
    OCR1A = 32000;
    
  #else
    TCCR3B = timerBits3;
    if(timerTicks3 > 256){
    OCR3AH = timerTicks3 / 256;
    OCR3AL = timerTicks3 % 256;
    }else{
    OCR3AH = 0x00;
    OCR3AL = timerTicks3;
    }
   
    TCCR2B = timerBits2;
    OCR2A = timerTicks2;
    TCCR1B = timerBits1;
    OCR1A = timerTicks1;
  #endif
}

void loop(void) {
   Serial.print("TCCR3B = ");
    Serial.println(timerBits3);
     Serial.print("OCR3A = ");
       Serial.println(timerTicks3);
        
  Serial.print("TCCR2B = ");
    Serial.println(timerBits2);
      Serial.print("OCR2A = ");
        Serial.println(timerTicks2);
        
    Serial.print("TCCR1B = ");
      Serial.println(timerBits1);
        Serial.print("OCR1A = ");
          Serial.println(timerTicks1);
          
Serial.print("Считанное из OCR3A = ");
Serial.println(OCR3A);
Serial.println();
delay(5000);          
    }

 

DetSimen
DetSimen аватар
Онлайн
Зарегистрирован: 25.01.2017

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

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

DetSimen пишет:

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

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

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

DetSimen
DetSimen аватар
Онлайн
Зарегистрирован: 25.01.2017

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

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

ua6em пишет:

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

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

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

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

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

ua6em пишет:

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

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

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

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


 if(timerTicks3 > 256){
	    OCR3AH = timerTicks3 / 256;
	    OCR3AL = timerTicks3 % 256;
	    }else{
	    OCR3AH = 0x00;
	    OCR3AL = timerTicks3;
}

 

Ворота
Ворота аватар
Онлайн
Зарегистрирован: 10.01.2016

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

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

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

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

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

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

ua6em пишет:

 if(timerTicks3 > 256){
	    OCR3AH = timerTicks3 / 256;
	    OCR3AL = timerTicks3 % 256;
	    }else{
	    OCR3AH = 0x00;
	    OCR3AL = timerTicks3;
}

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

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

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

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

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

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

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

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

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

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

ua6em пишет:

 if(timerTicks3 > 256){
	    OCR3AH = timerTicks3 / 256;
	    OCR3AL = timerTicks3 % 256;
	    }else{
	    OCR3AH = 0x00;
	    OCR3AL = timerTicks3;
}

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

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

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

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

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