Правильное выполнение арифметических операций?

Обсуждаем контроллеры компании Atmel.
Ответить
Это не хвост, это антенна
Аватара пользователя
Сообщения: 1455
Зарегистрирован: Ср сен 03, 2008 21:42:17
Откуда: Ленинградская область,пос.Красный Остров

Сообщение WatchCat »

Оно и работает, а если оставить просто 500, то уже нет.
Тем сайтом пользоваться не умею ибо только что узнал о его существовании,но у меня в vmlab такое же поведение.
Или вместо того чтобы суффиксы к 500 дописывать можно дописать unsigned long к первому сомножителю. Так тоже работает.
Я не стал искать истинную причину странного поведения, так как думать надо было о решении самой задачи проекта, а не о различных мелочах и неоднозначных особенностях инструментов, которые через неделю снова забудутся. В итоге просто обложил выражение скобками и типами, явно обозначив то, что компилятор должен сделать. Да, некрасиво. Но это дает стабильный результат и экономит время.
Полностью признаю такой подход правомерным в случае коммерческого проекта. Понимаю что далеко не все имеют неограниченное количество свободного времени как я,чтобы глубоко вникать в тонкости поведения и особенности используемых инструментов.
Возможность явных указаний для того и существует чтобы программист мог четко сформулировать свои пожелания к компилятору,не надеясь что тот догадается сам чего от него хотели. К тому же наличие этих явных указаний упрощает чтение сложных мест в коде - не приходится задумываться о порядке выполнения действий и приведений типов.

Хотя тут тоже есть свои странности в gcc. Например по умолчанию тип char - знаковый. И написание в моем примере char buf[40] компилятор воспринимает молча. Если же ему явно сказать signed char buf[40] - то он начинает сыпать предупреждениями при вызове
функций,ожидающий "просто char". Вот как хотите - так такие странности поведения и понимайте.
Создается впечатление что для компилятора три большие разницы - "просто char",signed char и unsigned char :-)
Реклама
Друг Кота
Аватара пользователя
Сообщения: 4468
Зарегистрирован: Вс янв 24, 2010 19:19:52
Откуда: Главный Улей России (Moscow)

Сообщение DX168B »

WatchCat На счет char, signed, unsigned могу согласиться с компилятором, потому что в параметрах сборки даже у IAR можно указать, чтобы char по умолчанию был unsigned.
Reflector писал(а): Причина в том, что UL для AVR дает 16-ти битные константы. Хотя я не спец по AVR, но если добавить такой суффикс чтобы константа стала 32-х битной, то больше ничего не нужно.
Вот и истинная причина. Я тоже не спец конкретно по avr-gcc, но когда знаешь,
что тип long при конкретной конфигурации сборки имеет ширину в 32 бита,
совсем не ожидаешь, что это не касается суффиксов в константах.
I am DX168B and this is my favourite forum on internet!
Контактная информация:
Реклама
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18675
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

Reflector писал(а):Нет в gcc для AVR 64-х битных типов, по крайней мере раньше не было
со времен WinAVR были, только на уровне базовой поддержки, т.е. не оптимизированные. для всех остальных типов в avr-gcc есть специально оптимизированные варианты всех комбинаций умножения деления типа U32/U8, U32/U32 и т.д., а стоило только применить ULL - всё, используются "классические алгоритмы, в итоге код становится просто дико огромным и тормозным, и никакая оптимизация не помогала.
DX168B писал(а):Я не стал искать истинную причину странного поведения, так как думать надо было о решении самой задачи проекта, а не о различных мелочах и неоднозначных особенностях инструментов, которые через неделю снова забудутся. В итоге, убедившись, что я нигде не ошибся, просто обложил выражение скобками и типами, явно обозначив то, что компилятор должен сделать. Да, некрасиво. Но это дает стабильный результат и экономит время.
а, не дай бог, потом при помощи указателей что-то вычислять, то-то весело будет при вашем подходе!

когда надо вычислить что-то сложное и требующее определенных размеров промежуточных значений, я всегда разбиваю выражение на несколько составных, с использованием переменных явно указанного типа. компилятор при этом делает тот же самый код, но никакой этажерки принудительных приведений типов не требуется. имхо, так и код читается лучше, и результат 100% предсказуемый, и побочных эффектов 0.
WatchCat писал(а):Создается впечатление что для компилятора три большие разницы - "просто char",signed char и unsigned char
так оно и есть: три разных типа. первый имеет знак в зависимости от того, как пожелает компилятор, а остальные два - гарантированно имеют или не имеют. первый следует использовать только для того, для чего он и предназначался - для хранения ASCII-символов, а вместо других намного разумнее использовать uint8_t и int8_t (или другие) из stdint.h, размерность и знаковость которых гарантируется на любой платформе.
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Друг Кота
Аватара пользователя
Сообщения: 4468
Зарегистрирован: Вс янв 24, 2010 19:19:52
Откуда: Главный Улей России (Moscow)

Сообщение DX168B »

[uquote="ARV",url="/forum/viewtopic.php?p=4203873#p4203873"]а, не дай бог, потом при помощи указателей что-то вычислять, то-то весело будет при вашем подходе![/uquote]
Мой подход - тщательно все тестировать на каждом этапе.
Если даже компилятор сказал, что все ОК, все равно тест.
Потому большая часть неочевидных багов быстро обнаруживается по ходу работы.
Этой хорошей практике научила меня работа с ПЛИС. Там без тестбенчей никуда.

Вычисления указателей - лютейшее зло. Метод выстрелить себе в ногу и гарантированно попасть.
Особенно, если такой код попытаться перенести с условного AVR на условный STM32. :)))
I am DX168B and this is my favourite forum on internet!
Контактная информация:
Реклама
Эиком - электронные компоненты и радиодетали
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18675
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

DX168B писал(а):Мой подход - тщательно все тестировать на каждом этапе
это да, не спорю. только тестирование не отменяет вредность "грязного" кода
DX168B писал(а):Вычисления указателей - лютейшее зло
только без указателей программирование на Си превращается в бесконечные хелловорды... ничего толкового без указателей не сделать, даже строку нормально не вывести.
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Реклама
Друг Кота
Аватара пользователя
Сообщения: 4468
Зарегистрирован: Вс янв 24, 2010 19:19:52
Откуда: Главный Улей России (Moscow)

Сообщение DX168B »

Вы наверное не так поняли.
Я про это:

Код: Выделить всё

void* ptr = y * SIZE + x;
Любое изменение архитектуры или типа данных и нога пробита.
I am DX168B and this is my favourite forum on internet!
Контактная информация:
Реклама
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18675
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

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

Мой уютный бложик... заходите!
Контактная информация:
Это не хвост, это антенна
Аватара пользователя
Сообщения: 1455
Зарегистрирован: Ср сен 03, 2008 21:42:17
Откуда: Ленинградская область,пос.Красный Остров

Сообщение WatchCat »

ничего толкового без указателей не сделать, даже строку нормально не вывести.
Почему не вывести?
Объявляем строку именно как массив символов,а не "указатель на символ":

Код: Выделить всё

char buf[40];
Потом пишем

Код: Выделить всё

i = 0;
while (buf[i]!=0)
   {
     uart_putc(buf[i]);
     i++;
    }
Только что проверил в VMLAB - строка выводится.

А вот если работать с строкой так

Код: Выделить всё

 
/*предполагаем что место для строки где-то выделено и str указывает на его начало*/
  while (*str!=0)
    {
        uart_putc(*str);
        str++;
    }
то шансов выстрелить себе в ногу куда больше.
Именно при переносе кода между разными архитектурами и разными компиляторами.
А ну как где-то char окажется двухбайтным по последней юникодной моде?
Или при выделении память забудем привести возвращенный нам указатель void к типу указатель именно на char?
Насколько тогда str++ "шагать" будет?
Друг Кота
Аватара пользователя
Сообщения: 4468
Зарегистрирован: Вс янв 24, 2010 19:19:52
Откуда: Главный Улей России (Moscow)

Сообщение DX168B »

[uquote="ARV",url="/forum/viewtopic.php?p=4203905#p4203905"]и это тоже - всегда можно обойтись, но иной раз выходит огород... но речь не о том, а что при работе с указателями приводить явно типы - это еще страшнее, чем ранее показанный загадочный пример...[/uquote]
Приводить явно типы, особенно от void указателей - нормальная практика при условии, что программист точно знает, что присваиваться указателю будут адреса данных только конкретного типа. Вы еще не видели Windows API или другую системную жесть, где такое встречается повсеместно. Особенно у винды, где официально в документации пишется, что условный аргумент lParam такой-то функции можно использовать как для передачи значения, так и для передачи указателя на данные/функцию/объект класса.

Мой код с рабочего проекта. Дополнительная жесть заключается в том, что приведенный
ниже статический метод выполняется как отдельный процесс, параллельно основному.
И в проекте достаточно много "ужасных" вещей происходит с указателями.
В том числе и между процессами. И ничего. Просто нужно понимать, что делаешь,
сформировать структуру программы так, чтобы ничего постороннего не попало куда не следует.
Обезопасить присваивания и использования указателей.
Спойлер

Код: Выделить всё

DWORD WINAPI CProcess::ProcessFunction(LPVOID pParam)
{
	if (pParam != NULL)
	{
		CProcess* procObj = reinterpret_cast<CProcess*>(pParam);
		procObj->procState = ProcState::PROCESS_STARTING;
		// Begin initialization code
		
		// End initialization code
		procObj->procState = ProcState::PROCESS_RUNNING;
#ifdef CPROCESS_CONTROL_CEVENT
		while (WaitForSingleObject(procObj->procStop.m_hObject, 0) != WAIT_OBJECT_0)
#else
		while (procObj->procState != ProcState::PROCESS_STOP)
#endif
		{
			// Check for pointer to function is correct
			if (procObj->procCallback == NULL)
			{

				break;
			}
			else
			{
#ifdef CPROCESS_CRITICAL_ENABLE
				procObj->critSect.Lock();
#endif
				// Call function
				if (procObj->procCallback(procObj->procParam) == false)
				{
#ifdef CPROCESS_CRITICAL_ENABLE
					procObj->critSect.Unlock();
#endif
					break;
				}
#ifdef CPROCESS_CRITICAL_ENABLE
				procObj->critSect.Unlock();
#endif
			}

			// Callback delay
			Sleep(100);
		}

		// Exit from process
		procObj->procState = ProcState::PROCESS_IDLE;
#ifdef CPROCESS_CONTROL_CEVENT
		procObj->procStop.ResetEvent();
		procObj->procStopped.SetEvent();
#endif
		AfxEndThread(0);
		return 0;
	}
	else
	{
		AfxEndThread(-1);
		return -1;
	}
}
I am DX168B and this is my favourite forum on internet!
Контактная информация:
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18675
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

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

Мой уютный бложик... заходите!
Контактная информация:
Друг Кота
Аватара пользователя
Сообщения: 4468
Зарегистрирован: Вс янв 24, 2010 19:19:52
Откуда: Главный Улей России (Moscow)

Сообщение DX168B »

Короче, мы что-то далеко ушли от темы.

Коты. Не слушайте никого. Если инструмент просит матерно выражаться:

Код: Выделить всё

uint16_t voltage = (uint32_t)(ADC * (uint32_t)(VREF_VALUE + 1)) >> 10;
выражайтесь, если это решает проблему. Все остальное - придирки, попытки запугать указателями и обозвать друг-друга ламерами. :)))

WatchCat
Мною приведенная формула - это формула из даташита (Atmega328P).
24.7 ADC conversion result
Просто я ее преобразовал для вычисления напряжения, поступающего на вход АЦП и привел к такому виду:
U = ADC * (Uref + 1) / 1024 ,где
ADC = значение АЦП после измерения
Uref - величина опорного напряжения, поданного на ногу AREF или внутреннего источника опоры (если выбран внутренний).
Формула справедлива для любых AVR.
Тут всего два действия - умножение и деление.
Умножать таки придется либо аппаратно, если это Atmega, либо программно - если это Tiny. Все это - головная боль компилятора. И только отчасти - программиста, если с производительностью будут проблемы.
А с делением все просто. Число 1024 - это два в десятой степени.
Значит, результат умножения можно поделить на 2^10 логическим сдвигом вправо на 10 разрядов.
Это уже аппаратно на любом AVR. Правда, оба действия не выполнятся за такт, так как числа большие, а разрядность МК маленькая.
И все равно, на ATMega это будет быстрее, чем на ATTiny.

Теперь о десятичном выражении. Не нужен тут тип float совсем. Достаточно числа с фиксированной точкой.
Что это такое? Это просто большое десятичное число, часть младших разрядов которого нужно воспринимать как дробную часть.
В том примере, что я приводил - число 100 соответствует 1,00 вольт. А число 101 - это 1,01 вольт.
VREF_VALUE 500ul - это 5,00 вольт опорного напряжения. Соответственно, АЦП при такой величине опоры, будет измерять от 0 до 5 вольт. А дальше - искусство схемотехники. Если нужно измерять большие величины напряжений, значит строится делитель напряжения на резисторах, который приведет масштаб измеряемых напряжений к масштабу 0...Uref. VREF_VALUE в этом случае можно задать в программе таким, как будто мы подали на AREF самое большое напряжение. Например - VREF_VALUE 1000ul, если нам нужно мерить от 0 до 10 вольт. Всю остальную работу по промежуточным преобразованиям выполнит аналоговый тракт. Главное, чтобы делитель напряжения привел шкалу 0..10 вольт к шкале 0..Uref вольт.

Если нужно мерить меньшие напряжения. Например, 0...75мВ от стандартного токового шунта, то тут тоже все просто.
Формула та же, расчет усилителя на то, чтобы наш диапазон входных напряжений 0...75мВ был приведен к диапазону 0...Uref выходных напряжений. В качестве значения VREF_VALUE задаем номинал шунта. Если он на 50 ампер, значит значение будет равным 5000ul.
Все, теперь у нас амперметр на 0...50А

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

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

Все это простые методы. Если нужны очень высокие точности, то тут уже будет все сложнее.
I am DX168B and this is my favourite forum on internet!
Контактная информация:
Это не хвост, это антенна
Аватара пользователя
Сообщения: 1455
Зарегистрирован: Ср сен 03, 2008 21:42:17
Откуда: Ленинградская область,пос.Красный Остров

Сообщение WatchCat »

Если инструмент просит матерно выражаться - выражайтесь
Вот я и выражаюсь. Пишу свои вычисления,смотрю в VMLABe.

Есть еще два вопроса:
- если мы считаем всё в целых числах, то насколько прилично будет что-то типа

int16_t a;
int16_t b;

b = a * 1.25;
ну или
b = a / 2.5;

То есть умножать-делить целые переменные на дробные литералы и присваивать результат целым?
По факту после таких действий просто обрезается дробная часть,а как вообще теория на это смотрит?
Или надо в явном виде писать b = a * 125 / 100 и b = a / 25 * 10 чтобы всё было строго в целых числах?


- и второй вопрос: что вообще AVR делает если таки словил деление на ноль по рукожопию программиста?
Интелы вон прерывание вызывают. И бодро виснут если на него обработчик корректный не установлен.
А у нас тут есть какая-то возможность отследить деление на ноль? Если деление программное то может в таких случаях библитека
какое-нибудь errno выставляет и его можно проверять?
Друг Кота
Аватара пользователя
Сообщения: 4468
Зарегистрирован: Вс янв 24, 2010 19:19:52
Откуда: Главный Улей России (Moscow)

Сообщение DX168B »

[uquote="WatchCat",url="/forum/viewtopic.php?p=4203973#p4203973"]Или надо в явном виде писать b = a * 125 / 100 и b = a / 25 * 10 чтобы всё было строго в целых числах?[/uquote]
Именно так. Просто потом, при выводе в консоль или на индикатор, ставить точку перед последними двумя разрядами. Чтобы те же 125 выглядели как 1,25. Тогда вычислительная проблема превращается в чисто косметическую, что решается гораздо меньшими ресурсами.
Можно даже написать функцию, которая после преобразования числа в строку, при выводе поставит точку в обозначенном месте.

Грубо говоря:

Код: Выделить всё

void PutFixed(int value, uint8_t point)
{
	size = int_to_string(buff, value);
	uint8_t index = 0;
	
	while(buff[index] != '\0')
	{
		if((size - point) == index)
			SendByte('.');

		SendByte(buff[index]);
		index++;
	}
}
I am DX168B and this is my favourite forum on internet!
Контактная информация:
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18675
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

а для чего может быть нужна формула из даташита?! она там для понимания, а не для использования, т.к. всегда реальная измеряемая величина имеет отличный от 0--VREF диапазон... а в этом случае надо вычислить вес младшего бита АЦП заранее, причем, можно до начала компиляции вручную, а затем умножать на этот коэффициент результат АЦП, т.е. уйти от деления.
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Друг Кота
Аватара пользователя
Сообщения: 4468
Зарегистрирован: Вс янв 24, 2010 19:19:52
Откуда: Главный Улей России (Moscow)

Сообщение DX168B »

Если измеряемые величины в лежат в рамках 0...Uref или не сильно выходят за этот предел, то вес бита может стать слишком большим целым числом (точку придется смещать влево), а потом опять делить, чтобы получить вменяемый результат. Ну а если сильно выходит за эти пределы (когда величина веса бита становится не ниже необходимой точности), то можно обойтись и умножением. Это уже оптимизации. Я описал универсальное решение, где в большинстве случаев про эти нюансы можно вообще особо сильно не думать.
I am DX168B and this is my favourite forum on internet!
Контактная информация:
Друг Кота
Аватара пользователя
Сообщения: 7077
Зарегистрирован: Пт ноя 11, 2016 05:48:09
Откуда: Сердце Пармы

Сообщение Ivanoff-iv »

[uquote="Dimon456",url="/forum/viewtopic.php?p=4203774#p4203774"]фиксированная арифметика выдала
при 8 бит точности 1171.875
при 16 бит точности 1259.765
при 32 бит точности 1259.999[/uquote]А можно про это место поподробнее?
Я понимаю, при сложении при малой разряднолсти могло переполнение произойти, но 1 то куда делась?
Для тех, кто не учил магию мир полон физики :)
Безграмотно вопрошающим про силовую или высоковольтную электронику я не отвечаю, а то ещё посадят за участие в (само)убиении оболтуса...
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18675
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

DX168B писал(а):можно вообще особо сильно не думать.
можно. но можно и подумать:
если вы измеряете ток от 0 до 100 ампер при помощи 10-разрядного АЦП, то у вас вес младшего бита будет 97,66 мА, хоть ты как считай. какие-то приведения к VREF совершенно лишние: максимальное значение измеряемой величины делим на 2 в 10 степени и получаем переводной коэффициент АЦП-величина, требующий только умножения. где вы потом будете ставить точку - это уже ваше дело. и именно такой подход на самом деле универсален - градуировать АЦП сразу в единицах измеряемой величины. вы можете младший бит выразить в амперах, миллиамперах или микроамперах - это ничего не изменит в плане точности и сложности вычислений - деление будет не нужно. и если вы обрабатываете сигнал с тензодатчика весов, вам гораздо выгоднее оперировать сразу киограммами-граммами, чем сначала вычислять что-то в милливольтах, а потом снова умножать на коэффициент...
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Друг Кота
Аватара пользователя
Сообщения: 4468
Зарегистрирован: Вс янв 24, 2010 19:19:52
Откуда: Главный Улей России (Moscow)

Сообщение DX168B »

ARV еще раз. Я об этом уже писал выше.
Не нравится название VREF_VALUE (которое было всего лишь частным случаем в конкретном проекте), так если его переименовать в MAX_VALUE, то суть работы формулы не поменяется.
Вот Вы предлагаете сначала делить MAX_VALUE на 2^10 макросом или калькулятором и только потом программно умножать этот результат на отсчет АЦП.
Так давайте представим следующую ситуацию:
Нам нужно измерять 0...10В.
Опорное напряжение = 5В.
Точность = 0,01В (до сотых или два знака после запятой)
Выражение результата - число с фиксированной точкой.
Подаем измеряемое напряжение на вход АЦП через делитель напряжения.

Величина опорного нужна только для расчета аналогового тракта.
Div = Uin_max / Uref = 10 / 5 = 2
Получаем делитель напряжения с коэффициентом 0,5 (или Uin / 2)
Наш делитель в идеальном случае приводит шкалу 0...10В в шкалу 0...5В (разброс реальных сопротивлений опустим)
MAX_VALUE будем брать как максимальное значение нашей шкалы. То есть, 10 вольт.

Считаем по вашему методу:
Вес одного бита (Ubit) = MAX_VALUE / 2^10 = 1000 / 2^10 = 0,97 или просто НОЛЬ, если это int.
U = Ubit * ADC
И выходов у нас в этом случае только два:
1. Вводить float в код, что сделает его еще более тормозным и громоздким.
2. Увеличивать точность результата (сдвигать точку влево), которая по ТЗ нам не нужна. То есть, брать не 1000, 10000 и более.
И каждый раз нам придется делать манипуляции с этим числом при использовании (например, при выводе на дисплей) потому что это число банально неудобно для работы. Плюс с увеличением точности начнутся проблемы с шумами аналогового тракта и АЦП. Такое число будет постоянно дрожать в младших десятичных разрядах.

По моему методу:
U = ADC * MAX_VALUE / 2^10 (или ADC * MAX_VALUE >> 10 )
В моем варианте мы получаем сразу нужное число нужной точности, определенное значением MAX_VALUE и не требующее лишних действий в дальнейшем, а младшие шумящие биты отрежутся сдвигом вправо. Результат будет сразу более стабильным и все, что мы будем наблюдать - это нечастые переключения десятичного младшего разряда на +/- единицу в случаях, если измеряемое напряжение приблизится к значению, например 3,509В или всех разрядов, если будет к примеру 6,999В. Во других промежуточных значениях дрожания не будет. И чем выше разрешение АЦП, тем меньше джиттера. Такое проще фильтровать. Причем, даже не прибегая к рекурсивным фильтрам и прочей DSPшной кухне. Всегда склонялся к тому, что данные должны поступать в основную программу уже нормально подготовленными, что упрощает работу с этими данными. При работе в команде это понимание приходит очень быстро. Если моя функция выдает результат с нюансами, значит коллега 100% напортачит при работе с ней.

Впрочем, я все уже изложил. Дальше пусть каждый поступает по-своему и выбирает то, что ему больше подойдет.

ЗЫ: Если формула в даташите приведена для понимания, это не значит, что ее нельзя использовать.
I am DX168B and this is my favourite forum on internet!
Контактная информация:
Поставщик валерьянки для Кота
Сообщения: 2089
Зарегистрирован: Вс июн 19, 2016 09:32:03

Сообщение Reflector »

[uquote="DX168B",url="/forum/viewtopic.php?p=4204221#p4204221"]Считаем по вашему методу:
Вес одного бита (Ubit) = MAX_VALUE / 2^10 = 1000 / 2^10 = 0,97 или просто НОЛЬ, если это int.
U = Ubit * ADC
И выходов у нас в этом случае только два:
1. Вводить float в код, что сделает его еще более тормозным и громоздким.
2. Увеличивать точность результата (сдвигать точку влево), которая по ТЗ нам не нужна. То есть, брать не 1000, 10000 и более.[/uquote]
1000 / 2^10 = 0.9765625, для числа с фиксированной точкой получим Ubit = 65536 * 0.9765625 = 64000, если 65536 у нас единица. Далее умножаем это значение на ADC и сдвигаем вправо на 16 бит, что должно быть еще быстрее, чем сдвиг на 10. Если настолько большая точность не нужна, то можно за единицу принять 2^10, тогда Ubit будет равен 1024 * 0.9765625 = 1000. Итого получается ADC * 1000 / 1024, т.е. в точности что ты сам и предлагаешь - это лишь частный случай для фиксированной точки где под дробную часть выделено 10 бит :)
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18675
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

DX168B писал(а):Считаем по вашему методу:
Вес одного бита (Ubit) = MAX_VALUE / 2^10 = 1000 / 2^10 = 0,97 или просто НОЛЬ,
бред какой-то.


мы измеряем 0...10В через делитель, который при 10В на входе выдает 5В, т.е. величину, равную опорному. АЦП при максимуме на входе выдает 1023, т.е. цена разряда у нас 10/1024=0,009765625. это примерно 10 мв, т.е. вы можете измерять 10В с дискретностью в 10 мв. значит, чтобы узнать значение измеряемого входного напряжения в МИЛЛИВОЛЬТАХ, вы должны умножить результат АЦП на 10. при выводе вы должны отделить три правых разряда этого ЦЕЛОГО числа точкой, чтобы получить вольты с десятыми-сотыми. где тут деление?! зачем тут float?!

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

Мой уютный бложик... заходите!
Контактная информация:
Ответить

Вернуться в «AVR»