Vladimir Chekin 0 26 марта, 2007 Опубликовано 26 марта, 2007 (изменено) · Жалоба Из буфера интегратора ничего выкидывать не надо. Фильтр мин/макс организуется дополнительно. Пример. Конечно коряво, но смысл видно: 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 искомое Изменено 26 марта, 2007 пользователем Vladimir Chekin Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
alex2103 0 26 марта, 2007 Опубликовано 26 марта, 2007 · Жалоба Если резистор не трогать, то показания АЦП теперь стоят как вкопаные, но стоит его тронуть и значения начинают скакать :( думаю из-за дребезга... Может просто поставить RC цепочку секунды на две? И еще остается открытым вопрос с калибровкой. Как её организовать? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Demeny 0 26 марта, 2007 Опубликовано 26 марта, 2007 · Жалоба Если резистор не трогать, то показания АЦП теперь стоят как вкопаные, но стоит его тронуть и значения начинают скакать :( думаю из-за дребезга... Может просто поставить RC цепочку секунды на две? Это можно сделать и программно. Программным аналогом RC-цепочки можно считать усреднение, он же фильтр низкой частоты, он же апериодическое звено первого порядка. Если постоянную времени RC-цепи принять за Т, то для аналогичного программного эффекта вам нужно усреднять все значения измерения на временном интервале примерно 3*T, то есть в вашем случае около 6 с. Чем больше показаний АЦП за этот интервал вы успеете накопить - тем точнее будет измерение. Чтобы не хранить огромный массив значений, каждый раз не считать сумму всех значений (если, допустим, вы усредняете по 1024 точкам) , ведь разрядности переменной может не хватить, есть простой программный приём. При усреднении по N точкам достаточно хранить только само СРЕДНЕЕ значение, и на каждом цикле съёма данных с АЦП к этому среднему прибавлять разницу нового и среднего, делённую на N. .......... Uaverage = Uaverage + (Ucurrent - Uaverage)/N .......... Если N точек у вас равномерно распределены по временному интервалу 6 секунд, вы получаете классический фильтр с постоянной времени 2 секунды. И еще остается открытым вопрос с калибровкой. Как её организовать? Тоже просто. Составляем две таблицы равной длины. В одной храним показания АЦП, в другой "литры". Заполняем таблицу опытным путём - от пустого бака к полному. Дальше всё решает линейная интерполяция. Чем больше точек вы можете себе позволить хранить в программе - тем точнее будет измерение. Если же вы примените квадратичную интерполяцию - ну тогда вааще... :tort: Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
alex2103 0 27 марта, 2007 Опубликовано 27 марта, 2007 · Жалоба Составляем две таблицы равной длины. В одной храним показания АЦП, в другой "литры". Заполняем таблицу опытным путём - от пустого бака к полному. Прикрутил я к своему "девайсу" кнопочку и теперь если при включении питания удерживать кнопку, то попадаем в режим калибровки :) в этом режиме заполняется массив в eeprom из N значений, которые соответствуют литрам (от 0 до N-1). Вроде все корректно заполняется. Только как теперь этот массив использовать? На индикаторе хочу получить целое кол-во литров. Еще нужно предусмотреть какое-то округление до целой части, т.е. если значение АЦП соответсвует 13,6 литрам, то на индикатор выводилось бы 14, а если 13,4 то 13. Поможете? :cheers: Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
alex2103 0 29 марта, 2007 Опубликовано 29 марта, 2007 · Жалоба Собрал я свой измеритель и окончательно убедился, что датчик в виде поплавка с резистором абсолютно не подходит :( Наверное нужно сооружать в баке конденсатор и мерять его емкость. Подскажите наиболее простой вариант реализации. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Vladimir Chekin 0 30 марта, 2007 Опубликовано 30 марта, 2007 · Жалоба датчик в виде поплавка с резистором абсолютно не подходит По какому критерию не подходит? Мож я чего пропустил... Какой в программе период опроса АЦП (время между измерениями)? Какой период вывода инфы на индикатор? Если резистор не трогать, то показания АЦП теперь стоят как вкопаные, но стоит его тронуть и значения начинают скакать Конкретнее, что значит "скакать"? Вроде бы всё логично, двигаешь резистор, показания индикатора меняются. Что не так? Рекомендованный программный фильтр "мин/макс" делал? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
alex2103 0 30 марта, 2007 Опубликовано 30 марта, 2007 · Жалоба Вот так провожу измерение 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 градуса поворота резистора. Испытание в баке еще не проводил, но что-то кажется что точность будет никакая... С пузырьковой сортировкой пробывал, но что-то не заработало. :unsure: Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Vladimir Chekin 0 30 марта, 2007 Опубликовано 30 марта, 2007 · Жалоба Я задавал вопросы про времена, ты привёл код. Как по этому коду мне получить ответы? Не зная тактовой частоты твоего камня, да и АВР я не знаю, поэтому участок кода работы с АЦП проверить не могу, может знатоки АВР проверят. Так что, мои вопросы остались без ответа. По проге. На первый взгляд вроде всё правильно. На данном этапе это главное, а красота придёт с опытом. Навскидку, в твоей проге 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); } >> С пузырьковой сортировкой пробывал, но что-то не заработало. Не заработало что? Я привёл полностью рабочий код. Надо было просто скопировать его в свою прогу и всё. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
muravei 3 30 марта, 2007 Опубликовано 30 марта, 2007 · Жалоба Собрал я свой измеритель и окончательно убедился, что датчик в виде поплавка с резистором абсолютно не подходит :( Наверное нужно сооружать в баке конденсатор и мерять его емкость. Подскажите наиболее простой вариант реализации. А "Схемотехника" чем не угодила? Или надо самостоятельно на грабли наступать? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
alex2103 0 30 марта, 2007 Опубликовано 30 марта, 2007 · Жалоба Vladimir Chekin , спасибо за внимание к теме :) Clock frequency : 1,000000 MHz ADC Clock frequency: 125,000 kHz Зачем заводил массив не объясню...сам незнаю зачем :blink: Обязательно исправлю. А это куда вывод? Это вывод на индикатор целого кол-ва литров. Если значение АЦП лежит в пределах n-0.25литра<n<n+0.25литра , то на индикатор выводится n. С пузірьковой сортировкой попробую еще. muravei, наступание на грабли хороший метод обучения :) muravei, наступание на грабли хороший метод обучения :) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
alex2103 0 30 марта, 2007 Опубликовано 30 марта, 2007 (изменено) · Жалоба Получилась у меня пузырьковая сортировка! :) 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. Изменено 30 марта, 2007 пользователем alex2103 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
muravei 3 30 марта, 2007 Опубликовано 30 марта, 2007 · Жалоба muravei, наступание на грабли хороший метод обучения :) Главное, чтобы грабли не были детскими... ;) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
alex2103 0 30 марта, 2007 Опубликовано 30 марта, 2007 · Жалоба muravei, ну так и раздел форума для "детей" :) ОФФ: многим обитателям форума мои вопросы могут показаться очень простыми, на которые даже отвечать не хочется :) Для меня любой ваш ответ - неоценимая помощь в освоении МК! Когда-то прогуливал лекции по МК, было неинтересно... А теперь сам пытаюсь обучиться, причем не потому, что "надо", а просто для себя... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Vladimir Chekin 0 30 марта, 2007 Опубликовано 30 марта, 2007 · Жалоба >> Clock frequency, АDC Clock frequency... Я оценил юмор :) Мож заодно пришлёшь мне компилятор и симулятор или макет с эмулятором/дебагером, чтоб я смог откомпилить и прогнать твою прогу, чтоб узнать сколько времени между измерениями и выводом на экран на самом деле? >> Это вывод на индикатор целого кол-ва литров Хм, у тебя только 9 градаций? Странно, вроде изначально речь шла о 40-литровом. Чего-то я не пойму тогда изначальной затеи. Пока нет опыта сделать нормальную процедуру калибровки, можно пойти более долгим, менее универсальным, но более простым путём: сперва сделать вывод на индикатор значения отфильрованного АЦП, т.е. out. Наливать в бак по литру и записывать показания индикатора на бумажку. Затем в программе объявить массив, инициализированный данными с бумажки: uchar table [40] = {x0, x1,... x39}; В цикле путём сравнения значений таблицы с out находишь номер члена таблицы меньше или больше out, как удобнее, и уже его выводишь на экран. Кстати, а что за экран? Судя по "плаванию" в элементарном есть подозрение в корректном написании процедуры преобразования целого в ASCI и собственно вывод. Может поэтому показания "скачат"? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
alex2103 0 30 марта, 2007 Опубликовано 30 марта, 2007 · Жалоба Хм, у тебя только 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: Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться