Перейти к содержанию
    

Измеритель уровня топлива

Из буфера интегратора ничего выкидывать не надо. Фильтр мин/макс организуется дополнительно.

 

Пример. Конечно коряво, но смысл видно:

 

uint8_t i, k, temp;
uint8_t filt_res[3];
uint16_t integrator;

for (i=0; i < 16; i++)
{
  for (k=0; k < 3; k++)
  {
    filt_res[k] =  read_adc(0); // читаем АЦП
    delay (xxx);
  }

  // Пузырьковая сортировка
  if (filt_res[0] > filt_res[1])
  {
    temp = filt_res[1];
    filt_res[1] = filt_res[0];
    filt_res[0] = temp;
  }

  if (filt_res[1] > filt_res[2])
  {
    temp = filt_res[2];
    filt_res[2] = filt_res[1];
    filt_res[1] = temp;
  }

  if (filt_res[0] > filt_res[1])
  {
    temp = filt_res[1];
    filt_res[1] = filt_res[0];
    filt_res[0] = temp;
  }

  // В filt_res[1] - среднее (не путать с усреднённым) по величене значение
  integrator += filt_res[1];
}

  temp = (uint8_t)(integrator >> 4); // В temp искомое

Изменено пользователем Vladimir Chekin

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Если резистор не трогать, то показания АЦП теперь стоят как вкопаные, но стоит его тронуть и значения начинают скакать :( думаю из-за дребезга... Может просто поставить RC цепочку секунды на две?

И еще остается открытым вопрос с калибровкой. Как её организовать?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Если резистор не трогать, то показания АЦП теперь стоят как вкопаные, но стоит его тронуть и значения начинают скакать :( думаю из-за дребезга... Может просто поставить RC цепочку секунды на две?

Это можно сделать и программно. Программным аналогом RC-цепочки можно считать усреднение, он же фильтр низкой частоты, он же апериодическое звено первого порядка. Если постоянную времени RC-цепи принять за Т, то для аналогичного программного эффекта вам нужно усреднять все значения измерения на временном интервале примерно 3*T, то есть в вашем случае около 6 с. Чем больше показаний АЦП за этот интервал вы успеете накопить - тем точнее будет измерение.

Чтобы не хранить огромный массив значений, каждый раз не считать сумму всех значений (если, допустим, вы усредняете по 1024 точкам) , ведь разрядности переменной может не хватить, есть простой программный приём. При усреднении по N точкам достаточно хранить только само СРЕДНЕЕ значение, и на каждом цикле съёма данных с АЦП к этому среднему прибавлять разницу нового и среднего, делённую на N.

..........
Uaverage = Uaverage + (Ucurrent - Uaverage)/N
..........

Если N точек у вас равномерно распределены по временному интервалу 6 секунд, вы получаете классический фильтр с постоянной времени 2 секунды.

 

И еще остается открытым вопрос с калибровкой. Как её организовать?

Тоже просто. Составляем две таблицы равной длины. В одной храним показания АЦП, в другой "литры". Заполняем таблицу опытным путём - от пустого бака к полному. Дальше всё решает линейная интерполяция. Чем больше точек вы можете себе позволить хранить в программе - тем точнее будет измерение.

Если же вы примените квадратичную интерполяцию - ну тогда вааще... :tort:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Составляем две таблицы равной длины. В одной храним показания АЦП, в другой "литры". Заполняем таблицу опытным путём - от пустого бака к полному.

 

Прикрутил я к своему "девайсу" кнопочку и теперь если при включении питания удерживать кнопку, то попадаем в режим калибровки :) в этом режиме заполняется массив в eeprom из N значений, которые соответствуют литрам (от 0 до N-1). Вроде все корректно заполняется.

Только как теперь этот массив использовать? На индикаторе хочу получить целое кол-во литров. Еще нужно предусмотреть какое-то округление до целой части, т.е. если значение АЦП соответсвует 13,6 литрам, то на индикатор выводилось бы 14, а если 13,4 то 13. Поможете? :cheers:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Собрал я свой измеритель и окончательно убедился, что датчик в виде поплавка с резистором абсолютно не подходит :(

Наверное нужно сооружать в баке конденсатор и мерять его емкость.

 

Подскажите наиболее простой вариант реализации.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

датчик в виде поплавка с резистором абсолютно не подходит

 

По какому критерию не подходит? Мож я чего пропустил... Какой в программе период опроса АЦП (время между измерениями)? Какой период вывода инфы на индикатор?

 

Если резистор не трогать, то показания АЦП теперь стоят как вкопаные, но стоит его тронуть и значения начинают скакать

 

Конкретнее, что значит "скакать"? Вроде бы всё логично, двигаешь резистор, показания индикатора меняются. Что не так?

 

Рекомендованный программный фильтр "мин/макс" делал?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вот так провожу измерение

 

unsigned char read_adc(unsigned char adc_input)
{
unsigned char out=0;
unsigned char n=0;
unsigned char i=0;
unsigned int sum=0;

for(i=0;i<16;i++)
{
for(n=0;n<16;n++)
        {
        ADMUX=adc_input | (ADC_VREF_TYPE & 0xff);
        // Start the AD conversion
        ADCSRA|=0x40;
        // Wait for the AD conversion to complete
        while ((ADCSRA & 0x10)==0);
        ADCSRA|=0x10;
        t[n]=ADCH; 
        delay_us(30);
        }   
for(n=0;n<16;n++)
        {
        sum+=t[n];
        }
sum = sum>>4;
t1[i]=(unsigned char)sum;
}
for(n=0;n<16;n++)
        {
        sum+=t1[n];
        }
        sum = sum>>4;
        out=(unsigned char)sum;


return out;}

 

Вот так вывожу:

 
while (1)
      {
unsigned char out=0;
unsigned char n=0;
out=read_adc(0);

for (n=9;n>0;n--)
{
if (out>=kalibr[n]-((kalibr[n]-kalibr[n-1])>>2))
if (out<=kalibr[n]+((kalibr[n+1]-kalibr[n-1])>>2))
leds(n);
}
     }

kalibr хранит значения полученные при калибровке. Не нравится мне конструкция из if, но в силу своей неопытности ничего лучшего не придумал. :biggrin:

 

Проблема в том, что 1 литр = 2 градуса поворота резистора. Испытание в баке еще не проводил, но что-то кажется что точность будет никакая...

С пузырьковой сортировкой пробывал, но что-то не заработало. :unsure:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Я задавал вопросы про времена, ты привёл код. Как по этому коду мне получить ответы? Не зная тактовой частоты твоего камня, да и АВР я не знаю, поэтому участок кода работы с АЦП проверить не могу, может знатоки АВР проверят. Так что, мои вопросы остались без ответа.

 

По проге. На первый взгляд вроде всё правильно. На данном этапе это главное, а красота придёт с опытом. Навскидку, в твоей проге

delay_us(30);

Сделай побольше порядка на 3, не микросекунды, а мили. Тогда и появится нужная плавность в показаниях.

 

Зачем ты заводишь массивы? В данной проге они не нужны совсем. Достаточно накапливать сумму в одном unsigned int sum с последующим делением, как было в моём примере выше.

 

Показалось мало 16 раз, ты сделал 256. Пусть так. 16 * 16 = 256. Не нужно 2 цикла. Т.к. ты нигде кроме этой процедуры не используешь промежуточный результат после 16 первых "оборотов", то достаточно одного цикла 256 раз с последующим делением на 256 (сдвиг вправо на 8 разрядов). Смысл не изменится.

 

А это куда вывод?

for (n=9;n>0;n--)
{
if (out>=kalibr[n]-((kalibr[n]-kalibr[n-1])>>2))
if (out<=kalibr[n]+((kalibr[n+1]-kalibr[n-1])>>2))
leds(n);
}

 

>> С пузырьковой сортировкой пробывал, но что-то не заработало.

Не заработало что? Я привёл полностью рабочий код. Надо было просто скопировать его в свою прогу и всё.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Собрал я свой измеритель и окончательно убедился, что датчик в виде поплавка с резистором абсолютно не подходит :(

Наверное нужно сооружать в баке конденсатор и мерять его емкость.

 

Подскажите наиболее простой вариант реализации.

А "Схемотехника" чем не угодила? Или надо самостоятельно на грабли наступать?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Vladimir Chekin , спасибо за внимание к теме :)

Clock frequency : 1,000000 MHz

ADC Clock frequency: 125,000 kHz

 

Зачем заводил массив не объясню...сам незнаю зачем :blink: Обязательно исправлю.

 

А это куда вывод?

Это вывод на индикатор целого кол-ва литров.

Если значение АЦП лежит в пределах n-0.25литра<n<n+0.25литра , то на индикатор выводится n.

 

С пузірьковой сортировкой попробую еще.

 

muravei, наступание на грабли хороший метод обучения :)

 

muravei, наступание на грабли хороший метод обучения :)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Получилась у меня пузырьковая сортировка! :)

unsigned char i, k, temp;
unsigned char filt_res[3];
unsigned int integrator=0;

for (i=0; i < 16; i++)
{
  for (k=0; k < 3; k++)
  {
    filt_res[k] =  read_adc(0); // ÷èòàåì ÀÖÏ
    delay_ms (10);
  }

  // Ïóçûðüêîâàÿ ñîðòèðîâêà
  if (filt_res[0] > filt_res[1])
  {
    temp = filt_res[1];
    filt_res[1] = filt_res[0];
    filt_res[0] = temp;
  }

  if (filt_res[1] > filt_res[2])
  {
    temp = filt_res[2];
    filt_res[2] = filt_res[1];
    filt_res[1] = temp;
  }

  if (filt_res[0] > filt_res[1])
  {
    temp = filt_res[1];
    filt_res[1] = filt_res[0];
    filt_res[0] = temp;
  }

  //  filt_res[1] - ñðåäíåå (íå ïóòàòü ñ óñðåäí¸ííûì) ïî âåëè÷åíå çíà÷åíèå
  integrator += filt_res[1];
}

  temp = integrator >> 4; // Â temp èñêîìîå

 

сразу неработало из-за того, что перед началом нового отсчета нужно было обнулить integtator.

Изменено пользователем alex2103

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

muravei, наступание на грабли хороший метод обучения :)

Главное, чтобы грабли не были детскими... ;)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

muravei, ну так и раздел форума для "детей" :)

 

ОФФ: многим обитателям форума мои вопросы могут показаться очень простыми, на которые даже отвечать не хочется :) Для меня любой ваш ответ - неоценимая помощь в освоении МК!

Когда-то прогуливал лекции по МК, было неинтересно... А теперь сам пытаюсь обучиться, причем не потому, что "надо", а просто для себя...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

>> Clock frequency, АDC Clock frequency...

Я оценил юмор :) Мож заодно пришлёшь мне компилятор и симулятор или макет с эмулятором/дебагером, чтоб я смог откомпилить и прогнать твою прогу, чтоб узнать сколько времени между измерениями и выводом на экран на самом деле?

 

>> Это вывод на индикатор целого кол-ва литров

Хм, у тебя только 9 градаций? Странно, вроде изначально речь шла о 40-литровом. Чего-то я не пойму тогда изначальной затеи.

 

Пока нет опыта сделать нормальную процедуру калибровки, можно пойти более долгим, менее универсальным, но более простым путём: сперва сделать вывод на индикатор значения отфильрованного АЦП, т.е. out. Наливать в бак по литру и записывать показания индикатора на бумажку.

 

Затем в программе объявить массив, инициализированный данными с бумажки: uchar table [40] = {x0, x1,... x39};

 

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

 

Кстати, а что за экран? Судя по "плаванию" в элементарном есть подозрение в корректном написании процедуры преобразования целого в ASCI и собственно вывод. Может поэтому показания "скачат"?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Хм, у тебя только 9 градаций? Странно, вроде изначально речь шла о 40-литровом. Чего-то я не пойму тогда изначальной затеи.

9 градаций сделано на время отладки. Потом будет 40.

 

Наливать в бак по литру и записывать показания индикатора на бумажку.

Алгоритм у меня такой же. Только индикатор у меня на 2 символа и out для калибровки я записываю в епром. Налил литр - нажал кнопку, налил еще - нажал. :)

 

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

Вот как это красиво организовать?

 

Кстати, а что за экран? Судя по "плаванию" в элементарном есть подозрение в корректном написании процедуры преобразования целого в ASCI и собственно вывод.

 

"Экран" от старого системника...на 2 с половиной разряда :)

Вот так с ним работаю:

void leds(char a) 
{
unsigned char digits[2];
if(a>=99) // если больше 99, то на индикаторе "Er"-типа ошибка.
{
PORTD=0x86;
PORTB=0x5F;
return;
}
digits[0] =a % 10;
digits[1] =a / 10 % 10;
switch(digits[1])  // в led[] и led1[] храняться коды индикаторов.
{
case 0: PORTD=led[0]; break;
case 1: PORTD=led[1]; break;
case 2: PORTD=led[2]; break;
case 3: PORTD=led[3]; break;
case 4: PORTD=led[4]; break;
case 5: PORTD=led[5]; break;
case 6: PORTD=led[6]; break;
case 7: PORTD=led[7]; break;
case 8: PORTD=led[8]; break;
case 9: PORTD=led[9]; break;
default: PORTD=0xBF;break;
}
switch(digits[0])
{
case 0: PORTB=led1[0]; break;
case 1: PORTB=led1[1]; break;
case 2: PORTB=led1[2]; break;
case 3: PORTB=led1[3]; break;
case 4: PORTB=led1[4]; break;
case 5: PORTB=led1[5]; break;
case 6: PORTB=led1[6]; break;
case 7: PORTB=led1[7]; break;
case 8: PORTB=led1[8]; break;
case 9: PORTB=led1[9]; break;
default: PORTB=0xBF;break;
        }  
}

 

Еще раз спасибо за содействие! :a14:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...