Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: нестабильность показаний АЦП
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
endasm
Вообщем нужно изменять некое напряжение (для примера я взял 5в). АЦП меряет, значение преобразуется для отображения на четырехразрядном семисегментрике и выводится на него. Но два последних разряда АЦП постоянно дают разные значения (крайние правые разряды индикатора постоянно показывают разные значения, при этом частота АЦП = 15 кГц). После этого, я добавил в схему повторитель на ОУ (т.к. думал что его малое выходное значение и большое входное положительно отобразится на стабильности измерений, но не помогло - значения по прежнему скачают). Подскажите что может исправить положения дел, что бы различные помехи наводки почти не влияли на результат измерений. Видео, упрощенная схема и код прилагаются. И, кстати, до нуля вход на АЦП тоже не доходит - там где минимум 1.2в, я думаю это из-за особенностей ОУ - так как он не rail-to-rail.

Демонстрация работы



CODE
#include <avr/io.h>
#include <avr/interrupt.h>

#define F_CPU 1000000UL
#include <util/delay.h>

int ADC_data;
unsigned char i;
unsigned char d0,d1,d2,d3;

unsigned char kod_simvola[10]= //коды цифр для семисегментного индикатора
{
0b00111111, //0
0b00000110, //1
0b01011011, //2
0b01001111, //3
0b01100110, //4
0b01101101, //5
0b01111101, //6
0b00000111, //7
0b01111111, //8
0b01101111 //9
};

//=====================ВЫВОД 4-ех ЦИФР НА СЕМИСЕГМЕНТНЫЙ ИНДИКАТОР=====================
void vivod_led(unsigned char digit0,unsigned char digit1,unsigned char digit2,unsigned char digit3)
{
cli();
unsigned char razr, digit;
DDRC=0b11111111; //PORTC0:7 подключаем к сегментам индикатора
DDRB=0b00001111; //PORTB0:3 подключаем к разрядам индикатора
for(razr=0;razr<4;razr++)
{
switch (razr)
{
case 0: digit=digit0; break;
case 1: digit=digit1; break;
case 2: digit=digit2; break;
case 3: digit=digit3; break;
}
switch (digit)
{
case 0:
PORTC=kod_simvola[0]; //выводим в порт нужную цифру
break;
case 1:
PORTC=kod_simvola[1];
break;
case 2:
PORTC=kod_simvola[2];
break;
case 3:
PORTC=kod_simvola[3];
break;
case 4:
PORTC=kod_simvola[4];
break;
case 5:
PORTC=kod_simvola[5];
break;
case 6:
PORTC=kod_simvola[6];
break;
case 7:
PORTC=kod_simvola[7];
break;
case 8:
PORTC=kod_simvola[8];
break;
case 9:
PORTC=kod_simvola[9];
break;
default: //если задан некорректный символ - выводится "_"
PORTC=0b0001000;
break;
}
if (razr==0) PORTC |=0b10000000; //зажигаем точку после первого рязряда
PORTB=1<<razr; //зажигаем нужный разряд индикатора
_delay_us(500); //менее 200 мкс - падает яркость, более 5 мс - заметно мерцание
PORTB=0<<razr; //гасим разряд индикатора
}
sei();
}

int main(void)
{
DDRA=0b00000000;
int temp; //для хранения результата преобразования АЦП

//====================ИНИЦИАЛИЗАЦИЯ ТАЙМЕРА T0===================================
TCCR0=(1<<CS00) | (1<<CS01);
/*Timer/Counter Control Register
устанавливаем коэффициент делителя таймера 64*/

OCR0=156;
/*Output Compare Register
число тактов котое будет сравниватся с числом тактов таймера*/

/*TIMSK=(1<<OCIE0);
/ *Timer/Counter Interrupt Mask Register
разрешаем прерывания по совпадению значения таймера T0 и OCR0=156* /*/

TIMSK=(1<<TOIE0);
/*Timer/Counter Interrupt Mask Register
разрешаем прерывания по переполнению таймера T0*/


//======================ИНИЦИАЛИЗАЦИЯ АЦП=======================================
ADMUX=(1<<REFS0) | (1<<ADLAR);
/*ADC Multiplexer Selection Register
REFS1:0=01 - за ИОН берем Vcc контроллера (AVcc)
MUX4:0=0000 - выбераем канал для АЦП - PORTA.0
ADLAR=1 - выравнивание результата преобразования АЦП по левому краю байтов результата*/


ADCSRA=(1<<ADEN) | (1<<ADSC) | (1<<ADPS1) | (1<<ADPS2) | (1<<ADATE);// | (1<<ADIE);
/*ADC Control and Status Register A
ADEN=1 - включаем АЦП
ADSC=1 - запускаем первое преобразование АЦП, дальше само идет автоматически
ADPS2:0=110 - делитель тактовой частоты для АЦП 1 Мгц/64 = 15.625 кГц
ADATE=1 - запускаем АЦП в режиме непрерывных последовательных преобразовний одно за другим
ADIE=0 - запрещаем прерывания по окончанию преобразования АЦП*/


SFIOR &=~(1<<ADTS0) | (1<<ADTS1) | (1<<ADTS2);
/*Special FunctionIO Register
ADTS2:0=000 - преобразование идет в непрерывном режиме с момента запуска*/


sei(); //разрешаем глобальные прерывания
while(1)
{
//ADC_data=(ADCL>>6) | (ADCH<<2); //байт ADCH всегда должен читатся последним
temp=ADC_data*0.004858*1000; //0.0049 - значение напряжение на одну ступень 4.97/1023
d3=temp%10;
temp /=10;
d2=temp%10;
temp /=10;
d1=temp%10;
temp /=10;
d0=temp%10;
vivod_led(d0,d1,d2,d3);

}
}

ISR(TIMER0_OVF_vect) //обработчик прерывания по переполнению T0
{
if(i==5)
{
ADC_data=(ADCL>>6) | (ADCH<<2); //байт ADCH всегда должен читатся последним
i=0;
}
else i++;
}
Snaky
Цитата(endasm @ Sep 25 2012, 07:56) *
АЦП меряет, значение преобразуется для отображения на четырехразрядном семисегментрике и выводится на него. Но два последних разряда АЦП постоянно дают разные значения (крайние правые разряды индикатора постоянно показывают разные значения, при этом частота АЦП = 15 кГц).


Установившего числа у вас никогда и не будет (разве что для самых крайних значений). Улучшением аппаратной (в основном) и программной части вы можете только приближаться к заявленным параметрам и получать все большую точность. У вас на видео видно что провода от ОУ до АЦП где-то с полметра в воздухе болтаются, чего ж вы хотели? Вот ваши несколько младших разрядов и прыгают из-за наловленного шума. Плюс контакты, разъемы, панельки... И, судя по плате, разводка аналоговых и цифровых земель скорее всего неоптимальная - плата, похоже, скорее для обучения, чем для реальной работы.

Ну и выполнять такие операции
Код
temp=ADC_data*0.004858*1000;        //0.0049 - значение напряжение на одну ступень 4.97/1023

где
Код
int temp;

как бы не совсем корректно :>

PS Кстати, угадайте сколько прилетит на вход АЦП у вас оторвется проводок идущий от потенциометра к входу "+" ОУ.
_Артём_
Цитата(endasm @ Sep 25 2012, 00:56) *
Вообщем нужно изменять некое напряжение (для примера я взял 5в). АЦП меряет, значение преобразуется для отображения на четырехразрядном семисегментрике и выводится на него. Но два последних разряда АЦП постоянно дают разные значения (крайние правые разряды индикатора постоянно показывают разные значения, при этом частота АЦП = 15 кГц).


Ну чо, усредняйте...
Иногда ещё так делают: первое преобразование после переключения MUX-а отбрасывают (может это и не помогает).
Или возьмите 12-бит АЦП и чтоб килогерц на 100-1000 мерял.

Ну и так к слову...
Цитата(endasm @ Sep 25 2012, 00:56) *
Код
int ADC_data;

ADC_data не volatile?...странно.

Цитата(endasm @ Sep 25 2012, 00:56) *
Код
        switch (digit)
        {
            case 0:
                    PORTC=kod_simvola[0];    //выводим в порт нужную цифру
                    break;
            case 1:

Не лень такую портянку писать? Читать точно тяжело...
Код
PORTC  = (digit<10) ? (kod_simvola[digit]) : (0b0001000);


Цитата(endasm @ Sep 25 2012, 00:56) *
Код
ADC_data=(ADCL>>6) | (ADCH<<2);        //байт ADCH всегда должен читатся последним


Код
ADC_data=ADC;// компилятор сам разберётся в каком порядке ему читать (или GCC не разберётся?)

И зачем вообще эти сдвиги? Поставьте ADLAR=0.
endasm
Пробовал в другом порядке считывать - после первого раза встает намертво и больше ацп не работает.

Ну а апаратно кроме как уменьшения длины проводов как-то же можно отфильтровать? И нужен ли там в действительности ОУ?
kovigor
Цитата(endasm @ Sep 25 2012, 07:31) *
И нужен ли там в действительности ОУ?

Нужен, если выходное сопротивление источника сигнала более 10 КОм, как у вас. См в даташите раздел об АЦП и там же упрощенную схему его входного каскада ...
MTh
Даже в идеальном случае - младший 1-2 разряда будут плавать, никуда от этого не уйдете. Берите среднее значение за период - будет Вам счастье.
uriy
Вот тут описан простой фильтр http://we.easyelectronics.ru/Theory/chestn...ovoy-filtr.html дает хорошие результаты.
TSerg
>Вот тут описан простой фильтр...

Откровения отца Иоанна простому народу sm.gif
Integral
Стабильность показаний АЦП зависит в первую очередь от стабильности источника опорного напряжения

Младшие разряды прыгают не просто так, прыгает напряжение на ноге АЦП, можете взять осциллограф и померять, вот как на осцилограме будет прыгать так и показания АЦП будут прыгать.

В основном все прыгает благодаря наводкам сетевой проводки 50Гц
Недавно значения показателя АЦП вывел по ЮАРТУ в терминал, сбросил все в ексель и нарисовал график, в результате получилась синусоида 50Гц

Как уменьшить влияние помех на АЦП?
Разделить аналоговую и сигнальную землю, поставить нормальный источник опорного напряжения, поставить малошумящий ОУ, экранировать провода

Иначе поднося руку к проводам будут уже другие показания

Обновляйте информацию о напряжении реже (раз в пол секунды)
В течении 0.5с делаейте 10 выборок с периодом 50мс, додавайте и делите на 10 (среднее значения напряжения за период 0.5с)
endasm
А как добится полного нуля на выходе ОУ, ведь у меня там 1,3 в когда на ОУ ноль подан. Это можно сделать без применения rail to rail ОУ?
Tanya
Цитата(endasm @ Sep 27 2012, 07:27) *
А как добится полного нуля на выходе ОУ,

Полного? Никак. Без второго питания.
RabidRabbit
Ну в качестве придирки цитата из даташита:
"By default, the successive approximation circuitry requires an input clock frequency between
50kHz and 200kHz to get maximum resolution. If a lower resolution than 10 bits is needed, the
input clock frequency to the ADC can be higher than 200kHz to get a higher sample rate."
Я бы поставил рекомендуемые 62,5 или 125кГц sm.gif
ReAl
Цитата(uriy @ Sep 25 2012, 19:45) *
Вот тут описан простой фильтр http://we.easyelectronics.ru/Theory/chestn...ovoy-filtr.html дает хорошие результаты.
«Ну кто так строит!»™
Цитата
Y(n) = (Na*Y(n-1) + Nb*X(n)) >> 8
Нельзя в целых числах просто так «взять и поделить»
Из-за отбрасывания при делении будет предельный цикл, после какого-то шага данный фильтр остановится не дойдя до нужного значения.
В данном случае при приближении снизу (при положительной ступеньке на входе фильтра). Вот выдача простой тестовой программки (исходник ниже) для Na из указанного в статье диапазона при начальном значении фильтра 0 и значении на входе 100.
Код
Na = 239: Y stabilised at  85 after step  36
Na = 240: Y stabilised at  85 after step  38
Na = 241: Y stabilised at  83 after step  39
...
Na = 247: Y stabilised at  72 after step  48
Na = 248: Y stabilised at  69 after step  50
Т.е. при Na = 248 после 50-го шага сколько не фильтруй, а на выходе фильтра будет 69 при входе 100.
При движении сверху (отрицательная ступенька) на 60-том шаге доходит до цели 100, тут всё нормально.
Причём симметричное округление, а не отбрасывание остатка, даст только симметризацию установившегося значения, при движении снизу при Na=248 остановится на 85, при движении сверху на 116.
Можно к фиксированной точке перейти, запас разрядности соответственно уменьшит отклонение предельного значения от правильного.
В простом варианте спасти может округление в сторону цели, т.е. если Y >= X то отбрасывать остаток, а если Y < X то перед делением добавлять (делитель-1).

Вот исходник тестовой программки для PC. Поскольку в тесте ступенькой вход - константа, для входа взята не какая-то переменная X, а константа YTARGET
CODE
#include <stdio.h>
#include <stdint.h>

//Y(n) = (Na*Y(n-1) + Nb*X(n)) >> 8
// 239 <= Na <= 248, Nb = 256 - Na

enum {
NA_FROM = 239,
NA_TO = 248,
YTARGET = 100,
};

typedef int (*calcfunc_t)(int,int);

int calc_original(int y, int na)
{
return (na * y + (256-na) * YTARGET) >> 8;
}

int calc_symm(int y, int na)
{
int temp = na * y + (256-na) * YTARGET;
temp += 128;
return temp / 256;
}

int calc_to_target(int y, int na)
{
int temp = na * y + (256-na) * YTARGET;
if (y < YTARGET) temp += 255;
return temp / 256;
}

void tester(const char *header, int initial, calcfunc_t func)
{
int y;

printf("\n%s\nY initial value %d, target %d\n",
header, initial, YTARGET);

for (int na = NA_FROM; na <= NA_TO; ++na) {
printf("Na = %3d: ", na);
int y = initial;
int yprev;
int step = 0;
do {
yprev = y;
y = func(y, na);
++step;
} while( y != yprev);
printf("Y stabilised at %3d after step %3d\n", y, step);
}
}

int main()
{
tester("Original calculation code", 0, calc_original);
tester("Original calculation code", 2*YTARGET, calc_original);

tester("Symmetric rounding", 0, calc_symm);
tester("Symmetric rounding", 2*YTARGET, calc_symm);

tester("Rounding to target", 0, calc_to_target);
tester("Rounding to target", 2*YTARGET, calc_to_target);
return 0;
}

И её выдача:
CODE
Original calculation code
Y initial value 0, target 100
Na = 239: Y stabilised at 85 after step 36
Na = 240: Y stabilised at 85 after step 38
Na = 241: Y stabilised at 83 after step 39
Na = 242: Y stabilised at 82 after step 40
Na = 243: Y stabilised at 81 after step 42
Na = 244: Y stabilised at 79 after step 43
Na = 245: Y stabilised at 77 after step 45
Na = 246: Y stabilised at 75 after step 47
Na = 247: Y stabilised at 72 after step 48
Na = 248: Y stabilised at 69 after step 50

Original calculation code
Y initial value 200, target 100
Na = 239: Y stabilised at 100 after step 38
Na = 240: Y stabilised at 100 after step 40
Na = 241: Y stabilised at 100 after step 42
Na = 242: Y stabilised at 100 after step 43
Na = 243: Y stabilised at 100 after step 45
Na = 244: Y stabilised at 100 after step 47
Na = 245: Y stabilised at 100 after step 50
Na = 246: Y stabilised at 100 after step 53
Na = 247: Y stabilised at 100 after step 56
Na = 248: Y stabilised at 100 after step 60

Symmetric rounding
Y initial value 0, target 100
Na = 239: Y stabilised at 93 after step 37
Na = 240: Y stabilised at 93 after step 39
Na = 241: Y stabilised at 92 after step 40
Na = 242: Y stabilised at 91 after step 42
Na = 243: Y stabilised at 91 after step 44
Na = 244: Y stabilised at 90 after step 46
Na = 245: Y stabilised at 89 after step 48
Na = 246: Y stabilised at 88 after step 50
Na = 247: Y stabilised at 86 after step 52
Na = 248: Y stabilised at 85 after step 56

Symmetric rounding
Y initial value 200, target 100
Na = 239: Y stabilised at 107 after step 37
Na = 240: Y stabilised at 108 after step 39
Na = 241: Y stabilised at 108 after step 40
Na = 242: Y stabilised at 109 after step 42
Na = 243: Y stabilised at 109 after step 44
Na = 244: Y stabilised at 110 after step 46
Na = 245: Y stabilised at 111 after step 48
Na = 246: Y stabilised at 112 after step 51
Na = 247: Y stabilised at 114 after step 52
Na = 248: Y stabilised at 116 after step 55

Rounding to target
Y initial value 0, target 100
Na = 239: Y stabilised at 100 after step 38
Na = 240: Y stabilised at 100 after step 40
Na = 241: Y stabilised at 100 after step 42
Na = 242: Y stabilised at 100 after step 43
Na = 243: Y stabilised at 100 after step 45
Na = 244: Y stabilised at 100 after step 47
Na = 245: Y stabilised at 100 after step 50
Na = 246: Y stabilised at 100 after step 53
Na = 247: Y stabilised at 100 after step 56
Na = 248: Y stabilised at 100 after step 60

Rounding to target
Y initial value 200, target 100
Na = 239: Y stabilised at 100 after step 38
Na = 240: Y stabilised at 100 after step 40
Na = 241: Y stabilised at 100 after step 42
Na = 242: Y stabilised at 100 after step 43
Na = 243: Y stabilised at 100 after step 45
Na = 244: Y stabilised at 100 after step 47
Na = 245: Y stabilised at 100 after step 50
Na = 246: Y stabilised at 100 after step 53
Na = 247: Y stabilised at 100 after step 56
Na = 248: Y stabilised at 100 after step 60
Конечно, если считать во всяких электронных таблицах или в самописных программах в плавучке, то этого не видно.
ViKo
Цитата(ReAl @ Sep 27 2012, 10:53) *
«Ну кто так строит!»™
Можно к фиксированной точке перейти, запас разрядности соответственно уменьшит отклонение предельного значения от правильного.

Можно просто хранить Y, сдвинутое <<8, т.е. в виде 0xmmff (целая часть, дробная часть).
И входное значение X сдвинуть <<8.
А потом уже оба складывать в своей пропорции.
А на вывод выдавать только целую часть Y, т.е. сдвинуть >>8.
ReAl
Цитата(ViKo @ Sep 27 2012, 11:28) *
Можно просто хранить Y, сдвинутое <<8, т.е. в виде 0xmmff (целая часть, дробная часть).
Ну так это и есть переход к фиксированной точке 8.8
Но большой запас разрядности плюс заметная фильтрация уже выталкивают в 32 бита даже при 8-битных входных значениях. Так что надо смотреть по месту, где-то я комбинировал и запас разрядности, и проверку куда округлять.
ViKo
Цитата(ReAl @ Sep 27 2012, 11:47) *
Ну так это и есть переход к фиксированной точке 8.8
Но большой запас разрядности плюс заметная фильтрация уже выталкивают в 32 бита даже при 8-битных входных значениях. Так что надо смотреть по месту, где-то я комбинировал и запас разрядности, и проверку куда округлять.

Для 8-разрядного входного числа и 8-разрядных коэффициентах размерности 8.8 хватит гарантированно. Можно проверить на вашей же программе. А промежуточные вычисления делать 24-битовыми. XX.XX * .NN = ZZ.ZZZZ

upd. добавил подчеркнутое
drvlas
Цитата(ReAl @ Sep 27 2012, 10:53) *
«Ну кто так строит!»™
Нельзя в целых числах просто так «взять и поделить»
...
Конечно, если считать во всяких электронных таблицах или в самописных программах в плавучке, то этого не видно.

Ну, если это камень в огород автора той заметки на Easyelectronics, то отвечу. Автор пользуется описанным подходом с середины 80-х и обычно как раз в целочисленной арифметике. Так что эффект потери точности при делении ему известен. И он легко устраняется сохранением неделенного результата.
В обсуждении той заметки была приведена даже реализация фильтра на Си.
Код
int filter(int x, int Na, int k){
static int y = 0, z = 0;
z += (x — y);
return y = Na * z >> k;
};

Увы, переделка статьи, в которой должен был быть учтен этот пробел, была отложена.
Так что спасибо за напоминание! Доработаем-с!
xemul
Даже с сохранением неделённого результата так
Код
return y = (Na * z + (1<<(k-1))) >> k;

будет корректнее.
drvlas
Да, конечно, симметричное округление. Это правильно.
Но я обычно в промежуточных вычислениях работаю с запасом по разрешающей способности. Поэтому там округлением не занимаюсь. А только в конце, может быть, проделаю такое добавление половинки единицы дискретности.
Например, с АЦП идет много разрядов, младшие пляшут (собсно, зачем и усредняем), мы пускаем все это в обработку, не заботясь о мелочи, полразряда. А когда уж на индикатор выводится значение, которое должно быть стабильным, которое имеет определенную дискретность (не взятую с потолка) - вот тогда и есть смысл прибавить половинку.

В статье поправил. Еще раз спасибо коллеге ReAl. Да, и оговорился там, что от диапазона входных величин x, Na и k зависит и выбор z. В указанном примере лучше сразу объявить long z. Что скажете?
xemul
Цитата(drvlas @ Sep 27 2012, 14:20) *
Что скажете?

Скажу, что при равномерном распределении (k-1)-го разряда методическая погрешность (y = Na * z >> k) за N вызовов filter() составит N*(1<<(k-1))/2.

UPD: нет, меньше, но лень предел считать в уме.
Tanya
Цитата(xemul @ Sep 27 2012, 14:32) *
Скажу...

Я тоже хочу сказать, что как бы ни были (при)увлекательны формулы и числа, надо сначала посмотреть на реальную правду в глазах.
Если имеется наводка, то от нее нужно избавляться в зародыше, чтобы потом не морочиться с числами.
Вот если есть наводка, а автор хочет измерять от нуля, то вблизи нуля никакие числовые фильтры не помогут.
И вблизи максимума... Поэтому нужно отфильтровать на входе аналоговым фильтром и сместить и сжать шкалу.
ViKo
Цитата(Tanya @ Sep 27 2012, 13:48) *
вблизи нуля никакие числовые фильтры не помогут.
И вблизи максимума...

Например, берем скользящее среднее. Если встречаются помехи-импульсы относительно сигнала-нуля, они тоже усреднятся. Не вижу причин для беспокойства. Ну, приподнимется чуть-чуть результат. Так надо же соображать, чтобы диапазон измеряемого сигнала (вместе с помехами, мы же не детектор-выпрямитель проектируем) укладывался в диапазон АЦП.
drvlas
Цитата(Tanya @ Sep 27 2012, 13:48) *
Если имеется наводка, то от нее нужно избавляться в зародыше, чтобы потом не морочиться с числами.

И верно, и нет.
Да, нужно подходить комплексно и не стараться вытянуть ЦОС грубые недоработки в проектировании платы.
Нет, ибо с числами можно не морочиться, а значительно улучшить результаты. Весь мир так делает. Почему нам отказываться?

Давайте конкретнее.
Цитата(Tanya @ Sep 27 2012, 13:48) *
Вот если есть наводка, а автор хочет измерять от нуля, то вблизи нуля никакие числовые фильтры не помогут.
Вблизи нуля помогут. Нужно правильно выбирать диапазон работы АЦП, вот и все. Впрочем, коллега ViKo уже сказал об этом.
ViKo
Можно и аналогию из аналоговой схемотехники привести. Если помехи у нас есть ниже нуля скачут, то фильтром на ОУ, запитанным от положительного питания, мы их тоже не отфильтруем.
endasm
А применение rail to rail ОУ даст мне на 10 битном АЦП полный ноль и полные 5 v ? ( шаг АЦП 0,0048 v)
ReAl
Цитата(drvlas @ Sep 27 2012, 12:39) *
В обсуждении той заметки была приведена даже реализация фильтра на Си.
...
Увы, переделка статьи, в которой должен был быть учтен этот пробел, была отложена.
Да вот я как-то, пардон, комментарии читать начал, но далеко не зашёл :-)
«Увы, ... отложена» близко и понятно :-(

Этот фильтр дотягивать будет и (с учётом замечания по симметризации округления) явно лучше.
Только такое впечатление, что, в терминологии исходной заметки, в нём не Na должно быть, а Nb.
Точнее, и ни то, и ни другое, так как фильтр уже другой, но Nb (малое по сравнению с делителем) по смыслу ближе.
По крайней мере если брать по статье Na=248 (30-кратное превышение влияния фильтрованного значения перед новым отсчетом), то в этот фильтр никак нельзя 248 ставить, он с таким коэффициентом при ступеньке даже в сотни квантов в несколько прыжков выйдет на заданное значение, а не в многие десятки.


Цитата(endasm @ Sep 27 2012, 16:44) *
А применение rail to rail ОУ даст мне на 10 битном АЦП полный ноль и полные 5 v ? ( шаг АЦП 0,0048 v)
Смотрите документацию на ОУ. RR может считаться уже когда он, скажем, милливольт пятдесят или сто от шины работает. А не пять.
Да и свой сигнал до нуля и опорного я чтараюсь и не рассчитывать, у АЦП свои ошибки есть. Если надо, подставку небольшую делаю. В том числе чтобы шумы/наводки при ограничении об шину не давали смещение сигнала после фильтрации.
drvlas
Цитата(ReAl @ Sep 27 2012, 20:09) *
Да вот я как-то, пардон, комментарии читать начал, но далеко не зашёл :-)
"но далеко не зашел" близко и понятно sm.gif

Цитата(ReAl @ Sep 27 2012, 20:09) *
Только такое впечатление, что, в терминологии исходной заметки, в нём не Na должно быть, а Nb.
Ай-ай-ай! Это была ошибка. Уже устранил. Огромное спасибо!
ViKo
Цитата(drvlas @ Sep 27 2012, 12:39) *
Так что эффект потери точности при делении ему известен. И он легко устраняется сохранением неделенного результата.
В обсуждении той заметки была приведена даже реализация фильтра на Си.
...

То есть, вместо хранения Y в форме целой и дробной частей (которые, в принципе, можно обе использовать для выдачи - получаем увеличение разрядности, о котором говорится на каждом микроконтроллерном сайте) вы будете хранить Y (целую часть? или не хранится?) и Z (целую и дробную части). И в чем выигрыш? В уменьшении умножений - вместо двух - одно? Так если брать Nb как степень двойки, можно одними сдвигами и сложениями обойтись.
А именно, например для Nb = 16 - из Y вычитаем 1/16 часть, а X уменьшаем в 16 раз, и добавляем к Y.
drvlas
Цитата(ViKo @ Sep 28 2012, 08:13) *
То есть, вместо хранения Y в форме целой и дробной частей (которые, в принципе, можно обе использовать для выдачи - получаем увеличение разрядности, о котором говорится на каждом микроконтроллерном сайте) вы будете хранить Y (целую часть?) и Z (целую и дробную части). И в чем выигрыш?

Спасибо, что заметили: сохранять Y вовсе не обязательно. Я поленился еще раз переделывать код, так как это мелочь, что она объявлена статической. Если уж Вам очень хочется, чтобы все используемые переменные имели одинаковую размерность, то считайте, что у Z есть целая и дробная часть. Мой скромный, но очень практический опыт работы с целочисленной арифметикой говорит о том, что нужно хорошенько разбираться с размерностью на этапе проектирования математики. Когда модель, формулы построены, то в реализации уже не так важно, где стоит запятая (точка). Поэтому я считаю все переменные целыми (как их и обрабатывает МК) и редко говорю "фиксированная точка", а чаще "целочисленная арифметика".

Пример: умножаем ЧИСЛО1 * ЧИСЛО2. Оба целые однобайтные. Результат ЧИСЛО3 - двухбайтное целое. Прекрасно.
ЧИСЛО1 - однобайтовое целое
ЧИСЛО2 - однобайтовое целое
ЧИСЛО3 - двухбайтовое целое
Можно объявить вообще все эти числа ИНТами или ЛОНГами. Никаких запятых.

Теперь нам захотелось результат иметь такого же масштаба, что и ЧИСЛО1. Хорошо, можете считать, что у ЧИСЛА2 запятая отделяет 8 бит дробной части. Тогда
ЧИСЛО1 - честно однобайтовое целое
ЧИСЛО2 - однобайтовое дробное
ЧИСЛО3 - двухбайтовое с целой и дробной частью.
Как на мой взгляд, то первый вариант проще для восприятия. Но на вкус и цвет...

Цитата(ViKo @ Sep 28 2012, 08:13) *
Так если брать Nb как степень двойки, можно одними сдвигами и сложениями обойтись.
А именно, например для Nb = 16 - из Y вычитаем 1/16 часть, а X уменьшаем в 16 раз, и добавляем к Y.

Вы немного не дочитали статью sm.gif Она, собственно, посвящена тому, как можно легко и просто найти Nb по заданным характеристикам фильтра (а именно: по времени отклика на ступенчатое воздействие). Поэтому уж такой свободы в выборе Nb у нас нет. Ну, частный случай может быть, что Nb = 16, но я считал более важным обеспечить Na+Nb=256 или иная степень двойки, чтобы деления не было.

ViKo
Цитата(drvlas @ Sep 28 2012, 09:14) *
Вы немного не дочитали статью sm.gif Она, собственно, посвящена тому, как можно легко и просто найти Nb по заданным характеристикам фильтра (а именно: по времени отклика на ступенчатое воздействие). Поэтому уж такой свободы в выборе Nb у нас нет. Ну, частный случай может быть, что Nb = 16, но я считал более важным обеспечить Na+Nb=256 или иная степень двойки, чтобы деления не было.

Я не читал вдумчиво, но ваш путь выбора фильтра разглядел. Однако, ради простоты фильтра я не стал бы заморачиваться получением точного времени отклика. Уверен, в большинстве случаев так и надо поступать.
Я пишу не про форму представления числа. А про лишнюю сущность - переменную Z.
Na + Nb = 256 можно (и нужно) обеспечивать и при Nb - степени двойки. Na мы вообще не будем использовать, я написал выше, как это получается.
muravei
Цитата(endasm @ Sep 27 2012, 16:44) *
А применение

Попробуйте "скользящее среднее" кратно 50 Гц , думаю, сильно облегчит вашу участь.
endasm
Я тогда попробую поставить такой rail-to-rail ОУ что бы у него диапазон выходных знечений был такой что бы он по краям обрезал менее 5 мв. Скоро допилю програмный фильтр и выложу результат.
Tanya
Цитата(endasm @ Sep 28 2012, 13:41) *
Я тогда попробую поставить такой rail-to-rail ОУ что бы у него диапазон выходных знечений был такой что бы он по краям обрезал менее 5 мв.

Если найдете такой, - срочно сообщите.
И, по ходу, по входу.
AndreyVN
Пользую с Мегами ОУ AD8602 (Rail-to-Rail), никаких "подрезаний" не обнаружено, работает от абсолютного нуля до питания (верх не проверял). Шум в 1 бит случается, но довольно редко, может до 1000 измерений идеально держать величину с точностью до МЗР.

У Rail-to-Rail ОУ, вроде, внутри схема вольтодобавки на переключаемых конденсаторах, с чего им 5mV подрезать? Или я не прав?

Питание 5В стабилизируется разными стабилизаторами для цифры и для аналоговой части и АЦП (MC78L05ACD), оно-же - опора с RC фильтром. Ну, само собой, земляной регион, разводка подальше от цифровых шин, конденсатооров по шинам питания побольше.

К стати, вычитываю данные вот так:
adc1 = ADCL; //Читаем результат преобразования сначала младший
adc2 = ADCH; //затем старший
V = (adc1 + adc2*256)*0.125;
И еще, не забыть включить ADC Nois Reduction Mode (регистр MCUCR, биты SM2,SM1,SM0).

Работаю в режиме одиночного преобразования, которое запукается от таймера, никакой стат обработки не использую.
К стати, у меня при оцифровке выше 5 кГц начинает падать точность, но, это, возможно, личные заморочки.
rx3apf
Вольтодобавка в ОУ - это больше похоже на кошмарный сон, приснившийся под утро. Бывает такое, знаю по себе. Я даже готов поверить, что такие изделия реально есть в природе, но это не типовое решение, точно. Потом, R2R это хорошо, конечно, но если мы работаем от Vcc в качестве опоры, то и этого в общем случае для полной шкалы недостаточно (ошибка смещения может быть больше трех единиц. Noise reduction - а sleep-то используете ?

Ну и по теме - вообще получить мерцание больше 1LSB при 10-битном ADC это тоже надо постараться. Даже на макетной плате с проводами....
Myron
Цитата(AndreyVN @ Sep 29 2012, 12:02) *
У Rail-to-Rail ОУ, вроде, внутри схема вольтодобавки на переключаемых конденсаторах, с чего им 5mV подрезать? Или я не прав?

В этом ОУ вольтодобавки еще нет и "подрезает" он по выходу исправно (см. пристегнутый файл).
У него относительно хороши входные характаристики за счет N- и P- MOS комбинированного входного дифф каскада. Но про этом появляется другая болезнь - ступенька в смещении и ступенька во входном токе. И, если с первой проблемой можно жить (иногда) за счет глубокой ОС, то ступенька в токе в середине уровня входного сигнала может привести к головной боли при использовании достаточно больших резисторов на ходе (например, в фильтрах и т.д.)
drvlas
Несколько старнно то, что автор не хочет пойти простым путем. Я имею в виду обычную двухполярную схему питания ОУ. Получить ее не сложно, хоть с того же MAX232 (пример схемы).

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

Так что, ИМХО, мы тут копья перья ломаем зря. Пусть автор определится с хотелкой.
endasm
Хорошая идея, но MAX232 довольно дорогая, если бы не это то я бы наверняка использовал бы её.
drvlas
Цитата(endasm @ Oct 1 2012, 08:23) *
Хорошая идея, но MAX232 довольно дорогая, если бы не это то я бы наверняка использовал бы её.
Ну, если речь идет о разовом изделии, то вопрос пары баксов кагбэ не вопрос, ИМХО. А в серии можно поискать и другие решения. Я назвал то, что на поверхности. Просто смысл убиваться в 5 мВ возле "рельсов" - сомнительно, по сравнению с классическим двухполярным (и даже не обязательно симметричным) питанием.
Да, сам себе нечаянно подсказал: если поставить источник питания всей системы на 5 В, и создать землю на диоде (0,7 В или добавить Шоттки для вольта), то "минус" этого источника будет около -1 В относительно земли. Вот и не надо так переживать за последние 5 мВ, запитываешь ОУ от -1...+5 В.
Честно скажу, сам так не делал. Но почему нет?

UPD
Вот изобразил, как это легко получить. Микроконтроллерная часть питается просто от 5В и "земли", а аналоговая - от 5В и источника "минус 700 мВ" на диоде. И, если ОУ хоть в какой-то мере rail-to rail (уж не имеет значения, до 5 мВ или до 50 мВ от земли) - все работает чики-пики.
В реальной схеме может еще понадобится запустить немного току в диод (резистор R1), если ток потребления МК очень прыгает. Дело в том, что диоду нужно немного выйти на режим - тогда и его напряжение более стабильно, и его динамическое сопротивление значительно падает (то есть "выходное сопротивление" источника минус 700 мВ)

Нажмите для просмотра прикрепленного файла
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.