Реклама на сайте DatasheetsDatasheets

KAZUS.RUN - Электронный портал. Принципиальные схемы, Datasheets, Форум по электронике

Новости электроники Новости Литература, электронные книги Литература Документация, даташиты Документация Поиск даташитов (datasheets)Поиск PDF
  От производителей
Новости поставщиков
В мире электроники

  Сборник статей
Книги и журналы
FAQ по электронике

  Datasheets
Поиск SMD
Онлайн справочник

Принципиальные схемы Схемы Каталоги программ, сайтов Каталоги Общение, форум Общение Ваш аккаунтАккаунт
  Каталог схем
Схемы и проекты
Принципиальные схемы
  Программы
Каталог сайтов
Калькуляторы
  Форумы по электронике
Помощь проекту


 
Опции темы

Как в Си определить выход за приделы кода (переполнение) результата операции ?

Непрочитано 24.04.2013, 16:25  
dosikus
Гуру портала
 
Аватар для dosikus
 
Регистрация: 20.11.2004
Сообщений: 9,979
Сказал спасибо: 928
Сказали Спасибо 2,261 раз(а) в 1,557 сообщении(ях)
dosikus на пути к лучшему
По умолчанию Re: Как в Си определить выход за приделы кода (переполнение) результата операции ?

Сообщение от SasaVitebsk Посмотреть сообщение
Могу привести пример, если надо.
Обязательно.
Реклама:
__________________
Осторожно , злой кот
dosikus вне форума  
Непрочитано 24.04.2013, 17:22  
electroTrash
Прописка
 
Регистрация: 05.01.2013
Сообщений: 195
Сказал спасибо: 11
Сказали Спасибо 24 раз(а) в 21 сообщении(ях)
electroTrash на пути к лучшему
По умолчанию Re: Как в Си определить выход за приделы кода (переполнение) результата операции ?

Сообщение от AJScorp Посмотреть сообщение
Тем не менее, любая помеха по АЦП будет сдвигать усреднённое значение на дельта/2, а потом плавно приближаться к среднему. Не гуд.
Да что там , при таком "методе" получения среднего случайная ошибка при любом количестве членов суммирования будет не меньше случайной ошибки среднего от всего лишь двух измерений. Это собственно показывает ущербность алгоритма ТС, лучше брать сумму с запасом разрядности (т.е. без проверки переполнения) и сдвигать, чем суммировать неопределенное количество значений и делить.
electroTrash вне форума  
Непрочитано 24.04.2013, 23:38  
SasaVitebsk
Гражданин KAZUS.RUN
 
Регистрация: 04.08.2006
Сообщений: 911
Сказал спасибо: 28
Сказали Спасибо 180 раз(а) в 139 сообщении(ях)
SasaVitebsk на пути к лучшему
По умолчанию Re: Как в Си определить выход за приделы кода (переполнение) результата операции ?

Сообщение от dosikus Посмотреть сообщение
Обязательно.
При использовании скользящего среднего, к примеру, с 16 выборками, при разрядности АЦП 10 бит вам понадобится 16*2+2 байта на 1 канал. Что не мало.
При фильтрации без использования плавучки я использую 2 метода. Первый алгоритмический. Он жёсткий. То есть не меняется в процессе исполнения. Но зато достаточно быстрый.
1. Строится фильтр. Потом корректируются коэффициенты с учётом двочного деления. Перепроверяется фильтр с учётом этих коэффициентов. Здесь воможно несколько итераций.
2. Конечная формула приводится к сумированиям и сдвигам.
Выглядит примерно так:
Код:
 int16_t	x2,x1,x0,y0,y1,y2,z0,z1,z2;
...
   x2=Adc[TekChan].X2;									// Загрузить предыдущее значение
   x1=Adc[TekChan].X2=Adc[TekChan].X1;					// Загрузить предыдущее значение
   x0=Adc[TekChan].X1=InpAdc[TekChan];					// Прочитать значение АЦП
   y2=Adc[TekChan].Y2;									// Загрузить предыдущее значение Y2
   y1=Adc[TekChan].Y2=Adc[TekChan].Y1;					// Загрузить предыдущее значение Y1, Переписать значение Y2
   y0=Adc[TekChan].Y1=((((((((((y1-y2)››1)+(y1-y2))››1)+(x0+x2))››1)+x1)››1)-y2)››2)+y1;				// Переписать значение Y1
   z2=Adc[TekChan].Z2;									// Загрузить предыдущее значение Z2
   z1=Adc[TekChan].Z2=Adc[TekChan].Z1;					// Загрузить предыдущее значение Z1
   z0=Adc[TekChan].Z1=((((((((((((y0+y2)››1)+y1)››1)+((y0+y2+z1)))››1)+((y1-z2)))››1)+z1)››1)-z2)››1)+z1; // Высчитать z0-результат
   if(z0›0)Adc[TekChan].bADCReal=z0;
   else Adc[TekChan].bADCReal=0;						// При малых отрицательных значениях, обусловленных работой фильтра приравнять к нулю
   Flag.fADCConv=0;										// Сбросить флаг
   Flag.fADCResult=1;									// Результат измерений получен
Второй метод основан на следующем простом свойстве. Например нам нужно число X умножить на 0.3478. Берём 2^15 = 32768 и умножаем на 0.3478 получаем 11397 округлённо (то есть реальный коэффициент будет 11397/32768 = 0,347808838 ). То есть если взять X, умножить на 11397 и разделить на 32768, то получим нужный результат. С точки зрения проца надо перемножить 2 16-битных числа и старших 2 байта сдвинуть на 1 бит. То есть значительно проще деления.

Прога выглядит так
Код:
// Фильтр 2-го порядка исходя из формулы y0^ = a0*(x0+x2)+a1*x1 + b1*y0 + b2*y1 + c1*y0
// где a и b - коэффициенты, c - целая часть y0
// После выполнения производится сдвиг y.
// y1 -› y2, y0 -› y1, y0^ -› y0;
//
// В процедуру передаётся 2 параметра
// 1 - указатель на значения, в последовательности: x0,x1,x2,y0,y1,y2
// 2 - указатель на значения коэффициентов в последовательности: a0,a1,b1,b2,c1
// По финишу результат делится на 32768

struct	data_s
{
  int16_t		x[3],y[3];								// Данные фильтра
};

struct	coef_s
{
  int16_t		a0, a1, b1, b2, c1;						// Коэффициенты фильтра
};

void iir2_16(uint8_t * data, uint8_t * coef)
{
  struct data_s	*d_s;
  struct coef_s	*c_s;
  int32_t		acc;
  
  d_s = (struct data_s *) data;
  c_s = (struct coef_s *) coef;
  acc = (int32_t)(d_s-›x[0] + d_s-›x[2]) * (int32_t)c_s-›a0;
  acc += (int32_t)d_s-›x[1] * (int32_t)c_s-›a1;
  acc += (int32_t)d_s-›y[0] * (int32_t)c_s-›b1;
  acc += (int32_t)d_s-›y[1] * (int32_t)c_s-›b2;
  acc ››= 15;
  acc += d_s-›y[0] * c_s-›c1;
  d_s-›y[2] = d_s-›y[1];
  d_s-›y[1] = d_s-›y[0];
  d_s-›y[0] = acc;
}
Вызывать примерно так
Код:
// Фильтр Баттерворта 4 порядка 1000 -› 100; 200  = -30 дб
//
// 1 звено y0 = 0,061885*(x0+x2) + 0,123770*x1 + 1,048600*y1 - 0,296140*y2
// Коэффициенты a0,a1,b1,b2,c1 = 2028, 4056, 1593, -9704, 1 
// 2 звено y0 = 0,077956*(x0+x2) + 0,155913*x1 + 1,320910*y1 - 0,632739*y2
// Коэффициенты a0,a1,b1,b2,c1 = 2554, 5109, 10516, -20734, 1
//
int16_t			fltr_c1[5] = {2028, 4056, 1593, -9704, 1},
				fltr_c2[5] = {2554, 5109, 10516, -20734, 1};
.....
// Каналы АЦП
struct	data_ADC
{
  int16_t		x[3],y[3],z[3];							// Данные фильтра
  uint16_t		tek_dempf,								// значение текущего демпфирования
  				dempf,									// заданное демпфирование
				chan;									// Канал АЦП
  uint16_t		min,									// Уровень, ниже которого меняется цвет
  				max;									// Уровень, выше которого меняется цвет
  int8_t		lvl;									// Уровень столбика светодиодов
  uint32_t		col;									// Цвет столбика светодиодов
//  uint8_t		nlin;									// Номер линейки
};

.....



	lin[cnt_line].x[2] = lin[cnt_line].x[1];			// Коэффициенты x переписать
	lin[cnt_line].x[1] = lin[cnt_line].x[0];			// Коэффициенты x переписать
	lin[cnt_line].x[0] = ADC;							// Прочитать АЦП
	iir2_16((uint8_t *)&lin[cnt_line].x[0],(uint8_t *)fltr_c1);// Первое звено фильтра
	iir2_16((uint8_t *)&lin[cnt_line].y[0],(uint8_t *)fltr_c2);// Второе звено фильтра

....
bright = lin[cnt_line].z[0];
На 32-ух битном процессоре ошибка при расчёте коэффициентов очень маленькая, что практически не требует плавучки на простых фильтрах. Скажу честно, что делал и режекторные и полосовые фильтра. Например DTMF разбирал и демодуляцию делал. Всё работает.

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

Попробую также скинуть прогу, очень простую, которая позволит фильтр построить и посмотреть его АЧХ. Хотя я и сам аналогичную делал.

Последний раз редактировалось SasaVitebsk; 24.04.2013 в 23:44.
SasaVitebsk вне форума  
Эти 2 пользователя(ей) сказали Спасибо SasaVitebsk за это сообщение:
akegor (24.04.2013), MaxiMuz79 (25.04.2013)
Непрочитано 24.04.2013, 23:47  
SasaVitebsk
Гражданин KAZUS.RUN
 
Регистрация: 04.08.2006
Сообщений: 911
Сказал спасибо: 28
Сказали Спасибо 180 раз(а) в 139 сообщении(ях)
SasaVitebsk на пути к лучшему
По умолчанию Re: Как в Си определить выход за приделы кода (переполнение) результата операции ?

прога. Удобнее конечно фильтра строить в QED2000 или в FiltrSolution, но для начинающих простую прогу кину.
Вложения:
Тип файла: zip ciirf1.zip (263.7 Кб, 58 просмотров)
SasaVitebsk вне форума  
Непрочитано 25.04.2013, 00:12  
alvadep
Почётный гражданин KAZUS.RUN
 
Регистрация: 05.05.2011
Адрес: Харьков
Сообщений: 5,042
Сказал спасибо: 589
Сказали Спасибо 3,967 раз(а) в 1,825 сообщении(ях)
alvadep на пути к лучшему
По умолчанию Re: Как в Си определить выход за приделы кода (переполнение) результата операции ?

Вот еще простой ФНЧ фильтр.
Значение рассчитывается после каждой выборки по формуле (A*NEW+B*OLD)/(A+B), где NEW - новое значение выборки, OLD - предыдущее рассчитанное значение, A и B - коэффициенты фильтра. Например, (1*NEW+7*OLD)/8. Меняя соотношение коэффициентов можно получать разную степень фильтрации. Очень просто и минимум ресурсов.
alvadep вне форума  
Непрочитано 25.04.2013, 02:18  
electroTrash
Прописка
 
Регистрация: 05.01.2013
Сообщений: 195
Сказал спасибо: 11
Сказали Спасибо 24 раз(а) в 21 сообщении(ях)
electroTrash на пути к лучшему
По умолчанию Re: Как в Си определить выход за приделы кода (переполнение) результата операции ?

Сообщение от Yurkin2007 Посмотреть сообщение
if (sumADC›~temp16)
Сообщение от Yurkin2007 Посмотреть сообщение
temp16 += sumADC; // новая сумма
if(sumADC›temp16)// сравнение старого и нового результата
Сообщение от alvadep Посмотреть сообщение
sumADC+=temp16;
if (sumADC‹temp16)
Все эти условия проверки имеют ошибки, так сказать "слабые точки".
Правильно так:
Код:
if(sumADC|temp16)›(temp16+sumADC) // условие проверки переполнения
// отключаем АЦП
ADCSRA&=~(1‹‹ADEN);

Последний раз редактировалось electroTrash; 25.04.2013 в 06:14.
electroTrash вне форума  
Непрочитано 25.04.2013, 07:11  
niXto
Почётный гражданин KAZUS.RUN
 
Аватар для niXto
 
Регистрация: 13.10.2007
Адрес: Беларусь
Сообщений: 8,026
Сказал спасибо: 60
Сказали Спасибо 3,941 раз(а) в 2,302 сообщении(ях)
niXto на пути к лучшему
По умолчанию Re: Как в Си определить выход за приделы кода (переполнение) результата операции ?

Сообщение от SasaVitebsk Посмотреть сообщение
Второй метод основан на следующем простом свойстве. Например нам нужно число X умножить на 0.3478. Берём 2^15 = 32768 и умножаем на 0.3478 получаем 11397 округлённо (то есть реальный коэффициент будет 11397/32768 = 0,347808838 ). То есть если взять X, умножить на 11397 и разделить на 32768, то получим нужный результат. С точки зрения проца надо перемножить 2 16-битных числа и старших 2 байта сдвинуть на 1 бит.
А почему бы не умножать на 22793? Тогда ничего сдвигать не надо
niXto вне форума  
Непрочитано 25.04.2013, 07:56  
alvadep
Почётный гражданин KAZUS.RUN
 
Регистрация: 05.05.2011
Адрес: Харьков
Сообщений: 5,042
Сказал спасибо: 589
Сказали Спасибо 3,967 раз(а) в 1,825 сообщении(ях)
alvadep на пути к лучшему
По умолчанию Re: Как в Си определить выход за приделы кода (переполнение) результата операции ?

Сообщение от electroTrash Посмотреть сообщение
Все эти условия проверки имеют ошибки, так сказать "слабые точки".
Примеры "слабых точек" в студию.
alvadep вне форума  
Непрочитано 25.04.2013, 10:19  
SasaVitebsk
Гражданин KAZUS.RUN
 
Регистрация: 04.08.2006
Сообщений: 911
Сказал спасибо: 28
Сказали Спасибо 180 раз(а) в 139 сообщении(ях)
SasaVitebsk на пути к лучшему
По умолчанию Re: Как в Си определить выход за приделы кода (переполнение) результата операции ?

Сообщение от niXto Посмотреть сообщение
А почему бы не умножать на 22793? Тогда ничего сдвигать не надо
Положительные коэффициенты умножяются на 32768, а отрицательные на -32767.
По большому счёту можно оптимизировать. Я специально привёл какие-то первые эксперименты в данной области. Там зато прямо по программе видно что к чему. Но IAR всё сделает правильно. Он естественно не будет на 15 бит сдвигать вправо ... Он сдвинет на 1 бит влево.

Тут надо ещё понимать некоторые вещи. Сума коэффициентов должна быть равна 1, если ты хочешь получить сигнал 1:1. Но так бывает не всегда. Например входной сигнал 10 или 12 бит. Увеличить его вдвое (на 1 бит) ничего плохого не произойдёт.
Так, например, при написании HART модема, я делал АРУ входного сигнала вместе с первым полосовым фильтром. А уж потом делал перемножение и вторичную фильтрацию.
SasaVitebsk вне форума  
Непрочитано 25.04.2013, 10:40  
alvadep
Почётный гражданин KAZUS.RUN
 
Регистрация: 05.05.2011
Адрес: Харьков
Сообщений: 5,042
Сказал спасибо: 589
Сказали Спасибо 3,967 раз(а) в 1,825 сообщении(ях)
alvadep на пути к лучшему
По умолчанию Re: Как в Си определить выход за приделы кода (переполнение) результата операции ?

Сообщение от SasaVitebsk Посмотреть сообщение
Тут надо ещё понимать некоторые вещи. Сума коэффициентов должна быть равна 1, если ты хочешь получить сигнал 1:1.
Это для ФНЧ. Для полосового и ФВЧ, если не ошибаюсь, сумма коэффициентов должна быть равна 0. Или нет?
alvadep вне форума  
 
Опции темы

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.

Быстрый переход

Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Измерение времени выполнения куска кода в Протеусе. Как? tems-ya Proteus 4 21.01.2010 21:41
ATmega128 PORTF как инициализировать на выход lzrvt Микроконтроллеры, АЦП, память и т.д 3 07.10.2007 17:07
Как уменьшить прошивку кода написанного на С? graham Микроконтроллеры, АЦП, память и т.д 30 14.01.2006 21:03
[Решено] Порт А неработает как выход LDA Коллекция глюков 2 06.12.2004 20:25
переполнение TMR2 ??? Picallka Микроконтроллеры, АЦП, память и т.д 2 11.02.2004 21:32


Часовой пояс GMT +4, время: 11:45.


Powered by vBulletin® Version 3.8.4
Copyright ©2000 - 2026, Jelsoft Enterprises Ltd.