Как в Си определить выход за приделы кода (переполнение) результата операции ?
24.04.2013, 16:25
|
|
|
Гуру портала
Регистрация: 20.11.2004
Сообщений: 9,979
Сказал спасибо: 928
Сказали Спасибо 2,261 раз(а) в 1,557 сообщении(ях)
|
Re: Как в Си определить выход за приделы кода (переполнение) результата операции ?
Сообщение от SasaVitebsk
|
|
Могу привести пример, если надо.
|
Обязательно.
__________________
Осторожно , злой кот
|
|
|
|
24.04.2013, 17:22
|
|
|
Прописка
Регистрация: 05.01.2013
Сообщений: 195
Сказал спасибо: 11
Сказали Спасибо 24 раз(а) в 21 сообщении(ях)
|
Re: Как в Си определить выход за приделы кода (переполнение) результата операции ?
Сообщение от AJScorp
|
|
Тем не менее, любая помеха по АЦП будет сдвигать усреднённое значение на дельта/2, а потом плавно приближаться к среднему. Не гуд.
|
Да что там  , при таком "методе" получения среднего случайная ошибка при любом количестве членов суммирования будет не меньше случайной ошибки среднего от всего лишь двух измерений. Это собственно показывает ущербность алгоритма ТС, лучше брать сумму с запасом разрядности (т.е. без проверки переполнения) и сдвигать, чем суммировать неопределенное количество значений и делить.
|
|
|
|
24.04.2013, 23:38
|
|
|
Гражданин KAZUS.RUN
Регистрация: 04.08.2006
Сообщений: 911
Сказал спасибо: 28
Сказали Спасибо 180 раз(а) в 139 сообщении(ях)
|
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.
|
|
|
|
Эти 2 пользователя(ей) сказали Спасибо SasaVitebsk за это сообщение:
|
|
|
24.04.2013, 23:47
|
|
|
Гражданин KAZUS.RUN
Регистрация: 04.08.2006
Сообщений: 911
Сказал спасибо: 28
Сказали Спасибо 180 раз(а) в 139 сообщении(ях)
|
Re: Как в Си определить выход за приделы кода (переполнение) результата операции ?
прога. Удобнее конечно фильтра строить в QED2000 или в FiltrSolution, но для начинающих простую прогу кину.
|
|
|
|
25.04.2013, 00:12
|
|
|
Почётный гражданин KAZUS.RUN
Регистрация: 05.05.2011
Адрес: Харьков
Сообщений: 5,042
Сказал спасибо: 589
Сказали Спасибо 3,967 раз(а) в 1,825 сообщении(ях)
|
Re: Как в Си определить выход за приделы кода (переполнение) результата операции ?
Вот еще простой ФНЧ фильтр.
Значение рассчитывается после каждой выборки по формуле (A*NEW+B*OLD)/(A+B), где NEW - новое значение выборки, OLD - предыдущее рассчитанное значение, A и B - коэффициенты фильтра. Например, (1*NEW+7*OLD)/8. Меняя соотношение коэффициентов можно получать разную степень фильтрации. Очень просто и минимум ресурсов.
|
|
|
|
25.04.2013, 02:18
|
|
|
Прописка
Регистрация: 05.01.2013
Сообщений: 195
Сказал спасибо: 11
Сказали Спасибо 24 раз(а) в 21 сообщении(ях)
|
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.
|
|
|
|
25.04.2013, 07:11
|
|
|
Почётный гражданин KAZUS.RUN
Регистрация: 13.10.2007
Адрес: Беларусь
Сообщений: 8,026
Сказал спасибо: 60
Сказали Спасибо 3,941 раз(а) в 2,302 сообщении(ях)
|
Re: Как в Си определить выход за приделы кода (переполнение) результата операции ?
Сообщение от SasaVitebsk
|
|
Второй метод основан на следующем простом свойстве. Например нам нужно число X умножить на 0.3478. Берём 2^15 = 32768 и умножаем на 0.3478 получаем 11397 округлённо (то есть реальный коэффициент будет 11397/32768 = 0,347808838 ). То есть если взять X, умножить на 11397 и разделить на 32768, то получим нужный результат. С точки зрения проца надо перемножить 2 16-битных числа и старших 2 байта сдвинуть на 1 бит.
|
А почему бы не умножать на 22793? Тогда ничего сдвигать не надо
|
|
|
|
25.04.2013, 07:56
|
|
|
Почётный гражданин KAZUS.RUN
Регистрация: 05.05.2011
Адрес: Харьков
Сообщений: 5,042
Сказал спасибо: 589
Сказали Спасибо 3,967 раз(а) в 1,825 сообщении(ях)
|
Re: Как в Си определить выход за приделы кода (переполнение) результата операции ?
Сообщение от electroTrash
|
|
Все эти условия проверки имеют ошибки, так сказать "слабые точки".
|
Примеры "слабых точек" в студию.
|
|
|
|
25.04.2013, 10:19
|
|
|
Гражданин KAZUS.RUN
Регистрация: 04.08.2006
Сообщений: 911
Сказал спасибо: 28
Сказали Спасибо 180 раз(а) в 139 сообщении(ях)
|
Re: Как в Си определить выход за приделы кода (переполнение) результата операции ?
Сообщение от niXto
|
|
А почему бы не умножать на 22793? Тогда ничего сдвигать не надо
|
Положительные коэффициенты умножяются на 32768, а отрицательные на -32767.
По большому счёту можно оптимизировать. Я специально привёл какие-то первые эксперименты в данной области. Там зато прямо по программе видно что к чему. Но IAR всё сделает правильно. Он естественно не будет на 15 бит сдвигать вправо ...  Он сдвинет на 1 бит влево.
Тут надо ещё понимать некоторые вещи. Сума коэффициентов должна быть равна 1, если ты хочешь получить сигнал 1:1. Но так бывает не всегда. Например входной сигнал 10 или 12 бит. Увеличить его вдвое (на 1 бит) ничего плохого не произойдёт.
Так, например, при написании HART модема, я делал АРУ входного сигнала вместе с первым полосовым фильтром. А уж потом делал перемножение и вторичную фильтрацию.
|
|
|
|
25.04.2013, 10:40
|
|
|
Почётный гражданин KAZUS.RUN
Регистрация: 05.05.2011
Адрес: Харьков
Сообщений: 5,042
Сказал спасибо: 589
Сказали Спасибо 3,967 раз(а) в 1,825 сообщении(ях)
|
Re: Как в Си определить выход за приделы кода (переполнение) результата операции ?
Сообщение от SasaVitebsk
|
|
Тут надо ещё понимать некоторые вещи. Сума коэффициентов должна быть равна 1, если ты хочешь получить сигнал 1:1.
|
Это для ФНЧ. Для полосового и ФВЧ, если не ошибаюсь, сумма коэффициентов должна быть равна 0. Или нет?
|
|
|
|
Ваши права в разделе
|
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения
HTML код Выкл.
|
|
|
Часовой пояс GMT +4, время: 11:45.
|
|