|
|
  |
Измеритель уровня топлива, Первый проект на AVR |
|
|
|
Mar 26 2007, 09:54
|
Участник

Группа: Новичок
Сообщений: 29
Регистрация: 16-03-07
Из: МО, г.Балашиха
Пользователь №: 26 210

|
Из буфера интегратора ничего выкидывать не надо. Фильтр мин/макс организуется дополнительно. Пример. Конечно коряво, но смысл видно: Код 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 - Mar 26 2007, 09:56
|
|
|
|
|
Mar 26 2007, 12:31
|
Частый гость
 
Группа: Свой
Сообщений: 135
Регистрация: 7-03-07
Из: г. Запорожье
Пользователь №: 25 945

|
Если резистор не трогать, то показания АЦП теперь стоят как вкопаные, но стоит его тронуть и значения начинают скакать  думаю из-за дребезга... Может просто поставить RC цепочку секунды на две? И еще остается открытым вопрос с калибровкой. Как её организовать?
|
|
|
|
|
Mar 26 2007, 13:13
|

Знающий
   
Группа: Свой
Сообщений: 648
Регистрация: 11-02-06
Из: Санкт-Петербург
Пользователь №: 14 237

|
Цитата(alex2103 @ Mar 26 2007, 13:31)  Если резистор не трогать, то показания АЦП теперь стоят как вкопаные, но стоит его тронуть и значения начинают скакать  думаю из-за дребезга... Может просто поставить RC цепочку секунды на две? Это можно сделать и программно. Программным аналогом RC-цепочки можно считать усреднение, он же фильтр низкой частоты, он же апериодическое звено первого порядка. Если постоянную времени RC-цепи принять за Т, то для аналогичного программного эффекта вам нужно усреднять все значения измерения на временном интервале примерно 3*T, то есть в вашем случае около 6 с. Чем больше показаний АЦП за этот интервал вы успеете накопить - тем точнее будет измерение. Чтобы не хранить огромный массив значений, каждый раз не считать сумму всех значений (если, допустим, вы усредняете по 1024 точкам) , ведь разрядности переменной может не хватить, есть простой программный приём. При усреднении по N точкам достаточно хранить только само СРЕДНЕЕ значение, и на каждом цикле съёма данных с АЦП к этому среднему прибавлять разницу нового и среднего, делённую на N. Код .......... Uaverage = Uaverage + (Ucurrent - Uaverage)/N .......... Если N точек у вас равномерно распределены по временному интервалу 6 секунд, вы получаете классический фильтр с постоянной времени 2 секунды. Цитата(alex2103 @ Mar 26 2007, 13:31)  И еще остается открытым вопрос с калибровкой. Как её организовать? Тоже просто. Составляем две таблицы равной длины. В одной храним показания АЦП, в другой "литры". Заполняем таблицу опытным путём - от пустого бака к полному. Дальше всё решает линейная интерполяция. Чем больше точек вы можете себе позволить хранить в программе - тем точнее будет измерение. Если же вы примените квадратичную интерполяцию - ну тогда вааще...
--------------------
Сделано в Китае. Упаковано в России.
|
|
|
|
|
Mar 27 2007, 14:30
|
Частый гость
 
Группа: Свой
Сообщений: 135
Регистрация: 7-03-07
Из: г. Запорожье
Пользователь №: 25 945

|
Цитата Составляем две таблицы равной длины. В одной храним показания АЦП, в другой "литры". Заполняем таблицу опытным путём - от пустого бака к полному. Прикрутил я к своему "девайсу" кнопочку и теперь если при включении питания удерживать кнопку, то попадаем в режим калибровки  в этом режиме заполняется массив в eeprom из N значений, которые соответствуют литрам (от 0 до N-1). Вроде все корректно заполняется. Только как теперь этот массив использовать? На индикаторе хочу получить целое кол-во литров. Еще нужно предусмотреть какое-то округление до целой части, т.е. если значение АЦП соответсвует 13,6 литрам, то на индикатор выводилось бы 14, а если 13,4 то 13. Поможете?
|
|
|
|
|
Mar 29 2007, 21:11
|
Частый гость
 
Группа: Свой
Сообщений: 135
Регистрация: 7-03-07
Из: г. Запорожье
Пользователь №: 25 945

|
Собрал я свой измеритель и окончательно убедился, что датчик в виде поплавка с резистором абсолютно не подходит  Наверное нужно сооружать в баке конденсатор и мерять его емкость. Подскажите наиболее простой вариант реализации.
|
|
|
|
|
Mar 30 2007, 01:18
|
Участник

Группа: Новичок
Сообщений: 29
Регистрация: 16-03-07
Из: МО, г.Балашиха
Пользователь №: 26 210

|
Цитата датчик в виде поплавка с резистором абсолютно не подходит По какому критерию не подходит? Мож я чего пропустил... Какой в программе период опроса АЦП (время между измерениями)? Какой период вывода инфы на индикатор? Цитата Если резистор не трогать, то показания АЦП теперь стоят как вкопаные, но стоит его тронуть и значения начинают скакать Конкретнее, что значит "скакать"? Вроде бы всё логично, двигаешь резистор, показания индикатора меняются. Что не так? Рекомендованный программный фильтр "мин/макс" делал?
|
|
|
|
|
Mar 30 2007, 08:26
|
Частый гость
 
Группа: Свой
Сообщений: 135
Регистрация: 7-03-07
Из: г. Запорожье
Пользователь №: 25 945

|
Вот так провожу измерение Код 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, но в силу своей неопытности ничего лучшего не придумал. Проблема в том, что 1 литр = 2 градуса поворота резистора. Испытание в баке еще не проводил, но что-то кажется что точность будет никакая... С пузырьковой сортировкой пробывал, но что-то не заработало.
|
|
|
|
|
Mar 30 2007, 09:31
|
Участник

Группа: Новичок
Сообщений: 29
Регистрация: 16-03-07
Из: МО, г.Балашиха
Пользователь №: 26 210

|
Я задавал вопросы про времена, ты привёл код. Как по этому коду мне получить ответы? Не зная тактовой частоты твоего камня, да и АВР я не знаю, поэтому участок кода работы с АЦП проверить не могу, может знатоки АВР проверят. Так что, мои вопросы остались без ответа. По проге. На первый взгляд вроде всё правильно. На данном этапе это главное, а красота придёт с опытом. Навскидку, в твоей проге 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); } >> С пузырьковой сортировкой пробывал, но что-то не заработало. Не заработало что? Я привёл полностью рабочий код. Надо было просто скопировать его в свою прогу и всё.
|
|
|
|
|
Mar 30 2007, 10:29
|
Частый гость
 
Группа: Свой
Сообщений: 135
Регистрация: 7-03-07
Из: г. Запорожье
Пользователь №: 25 945

|
Vladimir Chekin , спасибо за внимание к теме Clock frequency : 1,000000 MHz ADC Clock frequency: 125,000 kHz Зачем заводил массив не объясню...сам незнаю зачем  Обязательно исправлю. Цитата А это куда вывод? Это вывод на индикатор целого кол-ва литров. Если значение АЦП лежит в пределах n-0.25литра<n<n+0.25литра , то на индикатор выводится n. С пузірьковой сортировкой попробую еще. muravei, наступание на грабли хороший метод обучения  muravei, наступание на грабли хороший метод обучения
|
|
|
|
|
Mar 30 2007, 12:02
|
Частый гость
 
Группа: Свой
Сообщений: 135
Регистрация: 7-03-07
Из: г. Запорожье
Пользователь №: 25 945

|
Получилась у меня пузырьковая сортировка!  Код 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 - Mar 30 2007, 12:02
|
|
|
|
|
Mar 30 2007, 12:31
|
Частый гость
 
Группа: Свой
Сообщений: 135
Регистрация: 7-03-07
Из: г. Запорожье
Пользователь №: 25 945

|
muravei, ну так и раздел форума для "детей"  ОФФ: многим обитателям форума мои вопросы могут показаться очень простыми, на которые даже отвечать не хочется  Для меня любой ваш ответ - неоценимая помощь в освоении МК! Когда-то прогуливал лекции по МК, было неинтересно... А теперь сам пытаюсь обучиться, причем не потому, что "надо", а просто для себя...
|
|
|
|
|
Mar 30 2007, 12:40
|
Участник

Группа: Новичок
Сообщений: 29
Регистрация: 16-03-07
Из: МО, г.Балашиха
Пользователь №: 26 210

|
>> Clock frequency, АDC Clock frequency... Я оценил юмор  Мож заодно пришлёшь мне компилятор и симулятор или макет с эмулятором/дебагером, чтоб я смог откомпилить и прогнать твою прогу, чтоб узнать сколько времени между измерениями и выводом на экран на самом деле? >> Это вывод на индикатор целого кол-ва литров Хм, у тебя только 9 градаций? Странно, вроде изначально речь шла о 40-литровом. Чего-то я не пойму тогда изначальной затеи. Пока нет опыта сделать нормальную процедуру калибровки, можно пойти более долгим, менее универсальным, но более простым путём: сперва сделать вывод на индикатор значения отфильрованного АЦП, т.е. out. Наливать в бак по литру и записывать показания индикатора на бумажку. Затем в программе объявить массив, инициализированный данными с бумажки: uchar table [40] = {x0, x1,... x39}; В цикле путём сравнения значений таблицы с out находишь номер члена таблицы меньше или больше out, как удобнее, и уже его выводишь на экран. Кстати, а что за экран? Судя по "плаванию" в элементарном есть подозрение в корректном написании процедуры преобразования целого в ASCI и собственно вывод. Может поэтому показания "скачат"?
|
|
|
|
|
Mar 30 2007, 13:24
|
Частый гость
 
Группа: Свой
Сообщений: 135
Регистрация: 7-03-07
Из: г. Запорожье
Пользователь №: 25 945

|
Цитата Хм, у тебя только 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; } } Еще раз спасибо за содействие!
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|