Официальный сайт компании Arduino по адресу arduino.cc
Скорость работы программы - термометр
- Войдите или зарегистрируйтесь, чтобы получить возможность отправлять комментарии
Вс, 24/01/2016 - 19:04
#include <OneWire.h> OneWire ds(52); void setup(void) { Serial.begin (9600); } void loop(void) { byte i; byte present = 0; byte type_s; byte data[12]; byte addr[8]; float celsius, fahrenheit; if(!ds.search(addr)) { ds.reset_search(); //delay(250); return; } ds.reset(); ds.select(addr); ds.write(0x44,1); //delay(1000); present = ds.reset (); ds.select(addr); ds.write(0xBE); for (i = 0; i < 9; i++) data[i] = ds.read (); unsigned int raw = (data[1] << 8) | data[0]; if (type_s) { raw = raw << 3; if(data[7] == 0x10) raw = (raw & 0xFFF0) + 12 - data[6]; } else { byte cfg = (data[4] & 0x60); if(cfg == 0x00) raw = raw << 3; else if(cfg == 0x20) raw = raw << 2; else if(cfg == 0x40) raw = raw << 1; } celsius = (float)raw / 128.0; fahrenheit = celsius * 1.8 + 32.0; Serial.print (" Temperature ="); Serial.print (celsius); Serial.print (" C,"); Serial.print (fahrenheit); Serial.println (" F"); }
Это код термометра. Если результаты выводить на 4х сегментный индикатор, то заметны мигания, хотя в программе никаких принудительных задержек нет. Обнаружил, что ds.reset ds.select ds.write сильно тормозят выполнение программы, из-за чего и заметны мерцания индикатора. Может есть способ сделать код быстрее или другим способом получать данные от ds18b20?
По даташиту, ds18b20 в максимальном разрешении складывает температуру до 750 миллисекунд и ничего с этим не сделаешь.
Только вот чего я не понял, так это каким боком сюда притянут индикатор и как он зависит от скорости измерения температуры. Он не должен от неё зависеть, а если зависит - перепишеите выводл на него.
Если будете использовать один датчик и фаренгейты не нужны, то вместо строк 18-54 можно использовать следующий
Если результаты выводите на индикатор, то после отладки уберите вывод данных в серийный порт.
Также на индикатор можно выводить данные в том случае, если они изменились.
У меня динамическая индикация и если в программе к примеру поставить задержку 100 мс, то будут видны мерцания. Чтобы получить показатель термометра используются функции ds.reset ds.write ds.select, которые выполняются довольно долго, вот отсюда и задержка программы. Просто видел видео где на ардуино с помощью ds18b20 делают на 4х сегментном индикаторе термометр и мерцаний не видно. Думаю что нужно как то быстрее извлечь данные из ds18b20 чтобы добится желаемого результата. Вот только как?
строка 30 в приведённом коде нужна для работы датчика
но этот простой можно убрать применением millis()
раз в секунду вызывать блок кода
{ опросить датчик , т.е считать блокнот;
послать запрос на новое измерение;
}
...тогда ардуине не надо будет ждать данные от датчика
и эту секунду она сможет выполнять другие задачи
В основном теле программы (void loop) оставьте только вывод данных на индикатор. Считывание данных с датчика сделайте через функцию, к которой будет осуществляться переход через определнный промежуток времени.
Динамическая индикация ЗЛО ...
Можно конечно через прерывания работать - во время ожидания данных от датчика продолжать обрабатывать дисплей. Но работа с прерываниями требует вдумчивого чтения даташита.
В китае 74hc595 и uln2003 по 3...4 руб штука, у нас по 20 - тоесть максимум за 160 руб можно избавить себя от этого гемороя(если конечно дисплей не матричного типа).
Вот полноценный код. Тут немного по другому опрашивается датчик, но довольно долго тоже. Если делать опрос раз в 10000(незнаю сколько это по времени, но примерно 5сек), то каждые 5 сек цифры на индикаторе будут мерцать. Те кто писал программу скорее всего расчитовали на вывод в консоль, где не настолько важна частота работы кода. Думаю, что функция опроса датчика содержит дополнительные действия связанные с контрольной суммой или еще чем то подобным.
Попробуйте сделать прогу без библиотеки считывания температуры (когда только начал изучать ардуино, попробовал её, не понравилось и никогда не использую). Если периодичность чтения данных с датчика не критична, увеличьте интервал до 30 сек. или более.
Если просто нужно вывести число, то прога работает нормально. Скорости выполнения одной итерации хватает чтобы глаз не замечал мерцания. Периодичность считывания не поможет. Каждые 30сек все равно будет мерцать. Мне вообще интересно было бы самому написать функцию чтения, обмена данными с датчиком, но не могу найти подобного примера.
У меня динамическая индикация и если в программе к примеру поставить задержку 100 мс, то будут видны мерцания. Чтобы получить показатель термометра используются функции ds.reset ds.write ds.select, которые выполняются довольно долго, вот отсюда и задержка программы. Просто видел видео где на ардуино с помощью ds18b20 делают на 4х сегментном индикаторе термометр и мерцаний не видно. Думаю что нужно как то быстрее извлечь данные из ds18b20 чтобы добится желаемого результата. Вот только как?
Неправильно думаете, бцстрее нвозможно. Нужно просто грамотно написать динамическую индикацию. Вы же нам даже её кода не показали.
Ой, уже показали, ну, нет, так не пойдёт. Динамическая индикация делается по-другому. Сейчас поздно, завтра могу показать.
В 7м посту в главном цикле переменная K пробегает значения от [10,13], т.е. четыре разряда. Каждую итерацию загорается соответствующий индикатор(разряд). Пробовал код другой динамической индикации, такая же проблема с мерцанием, если использовать функции считования данных с датчика.
Мне вообще интересно было бы самому написать функцию чтения, обмена данными с датчиком, но не могу найти подобного примера.
Вам нужен пример? Без DallasTemperature и без OpenWire? Могу дать, только Вы не читаете постов. Я Вам ещё утром написал, что у этого датчика время 750мс по даташиту! И никак Вы его не ускорите. А Вы весь день талдычите "ускорить бы, но не знаю как" - никак. Нужно просто правильно написать динамическую индикацию. Если она написано правильно, датчик никак ей помешать не может.
Нужен пример работы с датчиком без библиотек?
В 7м посту в главном цикле переменная K пробегает значения от [10,13], т.е. четыре разряда. Каждую итерацию загорается соответствующий индикатор(разряд). Пробовал код другой динамической индикации, такая же проблема с мерцанием, если использовать функции считования данных с датчика.
Давайте, договоримся, сейчас я не могу, а завтра мы сделаем Вам правильную динамическую индикацию Окей?
есть небольшой опыт программирования, как я избавлялся от мерцания:
завожу переменную и потом сравниваю если значение плученое с датчика не = переменной то перезапишим ее туда и выведем на экран ил на дисплей..., все работает отлично.
ну а если равно то оставляемтак как есть. надеюсь помогло.
Не менее 750 мс необходимо для считывания данных с датчика при паразитном питании. За это время внутренний конденсатор успевает зарядиться. А если в термометре используется не паразитное питание, то эту временную задержку можно свести к минимуму. Я использую 50 мс. Все работает.
repeat, во 2-м посту я привел 10 строчек кода для считывания данных с датчика при условии, если он один. Попробуйте их использовать вместо библиотеки dallastemperature. Опытным путем выберите минимальную паузу в строке 4.
Все же вы правы, все дело в задержке 750мс. сам индикатор обрабатывается за 0-1 мс и + 750мс на считывания данных. Вот только не понял как уменьшить эти 750 до 50. Использую не паразитное.
repeat, попробуйте, используя функцию micros(), определить время, затрачиваемое на считывание данных с датчика и их преобразование в температуру с использованием библиотеки dallastemperature и без нее с минимальной паузой или без этой паузы. Если это сделаете, поделитесь, пжл, результатами.
Считование данных занимает(без delay(50)) 20мс + индикатор 1мс. Это много. Мерцания видны. Нужно чтобы вся программа занимала не более 2мс. Я думаю это возможно, если распараллелить код. НО для этого нужно полностью с нуля писать функции считвания данных, а не работать с готовыми.
Только, что проверил на Uno. На приведенный мной во 2-м посте код по считыванию и преобразованию температуры без задержки затрачивается примерно 5,5 мс. Без задержки температура считывается без проблем.
6мс и меньше на глаз смотрится нормально. Каждая из функций ds.reset ds.write занимает 1мс, поэтому как раз на пределе. НО есть еще вот такая вещь for (i = 0; i < 9; i++) data[i] = ds.read (); которая занимает тоже 5мс. Поэтому от дрожаний неизбавится. Нужно как-то распараллелить, или переделать сами эти функции(хотя не уверен что их можно ускорить).
repeat, ниже приведен код. Можете сами его проверить.
Я думаю, что быстрее на ардуино не получится. Если только вместо библиотеки OneWire сделать свое.
Чтобы уменьшить время затрачиваемое на выставление нужного сигнала на портах контроллера для динамической индикации вместо функции digitalWrite можно ими управлять через регистры. Я думаю, что здесь уже вопрос целесообразности.
Да, получилось. Спасибо.
Все же вы правы, все дело в задержке 750мс. сам индикатор обрабатывается за 0-1 мс и + 750мс на считывания данных. Вот только не понял как уменьшить эти 750 до 50. Использую не паразитное.
в ДШ DS18B20 указано время преобразования температуры в код , чем больше точность - тем дольше,
750 миллисекунд - это при максимальном разрешении
это время не зависит от питания ( паразитное или прямое )
ещё раз - через миллис() сделать вызов блока ...... каждые 750...1000 миллисекунд
и в блоке сначала считывать датчик ( это быстро , три байта надо переслать ) ,
потом запускать датчик на очередное преобразование и выходить из блока
.....первый опрос датчика вернёт 4095.8 градусов С , дальше всё нормально будет читаться
считывание датчика и запуск его для очередного замера Т забирает мало времени ,
не нужно ждать когда датчик произведёт конвертацию температуры
( он работает примерно как АЦП на сдвиговом регистре с последовательным приближением )
У датчика DS18B20 независимый регистр для хранения значений. 750мС - время в течении которого температура еще не перезаписана. Соответственно можете подать сигнал на конвертацию, а считывание результата производить хоть через час, но не ранее 750мС для 12бит режима. Постоянное дерганье датчика ведет к повышению его собственной температуры и как следствие искажению показаний (для медленных динамических процессов, типа контроля температуры помещения, достаточно интервала в 1-5минут).
Здесь у dimax интересно.
Никакой разницы по типам питания нет. Время записывания температуры зависит только от разрешения датчика.
Можно идти, как Вам здесь советуют: давать команду, а потом считывать. А можно настроить таймер, чтобы дисплей обновлялся всегда регулярно и тогда ему (дисплею) плевать на Ваши датчики и на всё остальное, хоть delay(100500) ставьте. Это самый правильный путь работы с дисплеем потому что, если с ним работать в общем loop, то не датчик, так ещё кто - всегда что-нибудь вылезет. Как обещал вчера, могу помочь с настройкой дисплея через таймер. Надо?
1 - У датчика DS18B20 независимый регистр для хранения значений. 750мС - время в течении которого температура еще не перезаписана. Соответственно можете подать сигнал на конвертацию, а считывание результата производить хоть через час, но не ранее 750мС для 12бит режима. Постоянное дерганье датчика ведет к повышению его собственной температуры и как следствие искажению показаний (для медленных динамических процессов, типа контроля температуры помещения, достаточно интервала в 1-5минут).
2 - Здесь у dimax интересно.
1 - ................
2 - спасибо ! не видел, не читал раньше :(
Как обещал вчера, могу помочь с настройкой дисплея через таймер. Надо?
не знаю как для ТС , а мне надо :)
SU-27-16, если достаточно описать "идейно" я могу хоть сейчас. Если же Вам нужен полностью работающий пример, то это вечером, сейчас у меня нет под рукой ни ардуины, ни дисплея. чтобы проверять. Давайте, может я идейно сейчас напишу, а Вы мне скажете, всё ли понятно и нужен ли полностью работающий пример. Пойдёт?
Если же Вам нужен полностью работающий пример, то это вечером, сейчас у меня нет под рукой ни ардуины, ни дисплея. чтобы проверять. Давайте, может я идейно сейчас напишу, а Вы мне скажете, всё ли понятно и нужен ли полностью работающий пример. Пойдёт?
"Можно и туда" - "Служебный роман"....
....мине ВСЁ пойдёт , лишь бы Вас не нагружать....
:)
...ни дисплея. чтобы проверять. Давайте, может я идейно сейчас напишу, а Вы мне скажете, всё ли понятно и нужен ли полностью работающий пример. Пойдёт?
пойдёт про датчик , про его опрос.....
про динамОтображ - не нано !!!!
SU-27-16, если достаточно описать "идейно" я могу хоть сейчас.
если у Вас есть время на такое - я готов слушать-читать....
Идейно, это делается вот так: показ готовых цифр на экране полностью отрывается от основной программы. Они просто независимы. Показ работает сам по себе с фиксированной частотой, а основная программа может делать что хочет, хоть в вечном делэе сидеть. Внимательно прочитайте код и все комментарии к нему.
Осталось два момента:
1.
Как организовать вызов DisplayRefresh каждые INTER_DIGIT_DELAY миллисекунд? Я вижу два способа: лучше бы через таймер, но здесь может быть засада - доступных таймеров всего два и многие библиотеки любят их исользовать - подерёмся. Второй способ - с использованием техники, которую я описал в одном из моих "этюдов". Там собственно тоже через таймер, но там мы его монопольно не занимаем.
2.
Несколько искусственно выглядят танцы с бубнами вокруг класса, глобальной переменной, да и ещё указателя на неё в строках 40-46. Да, можно было сделать гораздо проще. Намного проще. Но! Здесь это сделано "на вырост". Я сейчас не буду пудрить мозги, пока не скажете, что с кодом всё понятно и Вы запросто можете такое воспроизвести. А вот, когда Вам будет всё понятно, я дополнительно покажу как сделать пачку виртуальных экранов на базе одного реального. И вот там нам эти танцы с бубнами пригодятся ещё как. Т.е. это "на вырост".
Конечно хотелось бы узнать как реализуются параллельные вычисления через прерывания.
функция memet как я понял обнуляет массив?
что такое volatile?
классный прием: LogicalScreen->currentDigit = (LogicalScreen->currentDigit + 1) % TOTAL_DIGITS; возьму на заметку.
Я почему-то думал у ардуино язык С (где нет классов), а тут С++.
Евгений, на пальцах рассказать можете как делаеются эти прерывания, ну т.е. на примере индикатора?
repeat,
я могу доделать тот пример до работающего скетча, но не обещаю сегодня - точно на этой неделе.
А вот, что очень важно: в другой теме и по другому поводу у меня случайно вылез отличный пример, который убедительно показывает почему нельзя делать обновление экрана через loop и почему обязательно делать через прерывания (иначе всегда будет не мигать, так "подминивать иногда"). Вот почитайте здесь.
Вобщем сделал индикатор с прерыванием по таймеру. Заметил странную вещь:
Пусть переменная R=74 объявлена как глобальная(и инициализирована 1 раз).
У меня прерывание срабатывает каждую милисекунду, и если писать в loop'е так:
или так:
то число 74 нормально отображается, но если так:
то второй разряд иногда не загорается(частота его загорания примерно в 2 раза реже 1-го разряда +-500мкс)(как будто в функцию передалось не 74 а 4).
С чем это связано? Может адресс переменной R чем-то забивается, а потом возвращается на место(ну или как правильно это сказать)?
1. Не нужно каждую миллисекунду. Сколько у Вас цифр на индикаторе? Скажем N. Весь индикатор должен сменяться с частотой не менее 50Гц, т.е. за 20мс. Значит Вам нужно дергать таймер не каждую миллисекунду, а каждые 20/N миллисекунд. Т.е. каждые 20/N миллисекунд Вы должны погасить старую цифру и зажечь новую и так по кругу. Мерцания никакого не будет. А будете гасить чаще - они будут менее ярко выглядеть, т.к. горят меньше времени.
20/N равно: для трёх цифр - 6, для четырёх - 5, для пяти - 4 и т.д.
2. Про Вашу цифру ничего сказать не могу, т.к. не вижу скетча.
Даже специально передал в функцию значение R и все равно мерцает 2й разряд
Как-то всё сложно и мудрёно. Вечером посмотрю, а сейчас так сходу ничего особо и не скажешь. Ну, из того что бросается в глаза:
1. Все глобальные переменные, которые используются и в val и ISR (p, pp, N, ...) необходимо объяить с аттрибутом volatile. Проблема с цифрой вполне может быть оттуда. А вот cntr как раз не обязательно иметь этот аттрибут, т.к. она используется только в ISR. Кстати, а почему бы её прямо там и не объявить (static, конечно)
2. Для чего нужна нигде и никак неиспользуемая j в строке 136?
3. По хорошему подготовку надо проводить не в обработчике прерывания. Всё, что у Вас делает вызываемый из обработчика num(), должно быть готово заранее, а там только показать и всё.
Оказывается нельзя обновлять в главном цикле число на индикаторе, пока не покажет он все цифры. Вот и решилась проблема.
Достаточно написать перед так: if(K==30) val(R);
Оказывается нельзя обновлять в главном цикле число на индикаторе, пока не покажет он все цифры. Вот и решилась проблема.
Достаточно написать перед так: if(K==30) val(R);
да, нет, надо всё аккуратно объявить volatile и не считать в прерывании, как я Вам писал. И обновлять когда нужно. Это же идиотизм, чтобы у главной программы ещё болела голова о проблемах индикатора. Его дело показывать, а главная программа обновляет когда её надо.
Не совсем понял что Вы имеете ввиду? По подробнее можете обяснить с кодом обработчика?
num() только модифицирует порт, чтобы загорелось определенное число(на всех индикаторах), а ненужные разряды я гашу. как еще проше это сделать?
И обновлять когда нужно. Это же идиотизм, чтобы у главной программы ещё болела голова о проблемах индикатора. Его дело показывать, а главная программа обновляет когда её надо.
дело в том, что когда программа входит в функцию val(R) функция чето там себе считает, изменяет значения разрядов и в этот момент срабатывает прерывание и показывает черти что.
получается обработчик прерывания не должен знать о изменении какого либо разряда пока не покажет все 4 цифры.
Не совсем понял что Вы имеете ввиду? По подробнее можете обяснить с кодом обработчика?
num() только модифицирует порт, чтобы загорелось определенное число(на всех индикаторах), а ненужные разряды я гашу. как еще проше это сделать?
Функция Num делает выбор из 14 вариантов и только потом присваивает значение порту. Да ещё перед вызовом функцией num в обработчике у Вас дохрена чего делается.
Я уж не говорю, что Вам незачем гасить все пины порта А (стр. 136), если Вы всё равно в него потом пишете присваиванием (строка 45)! Для чего у Вас в программе строка 136? Чтобы во все биты порта А записать нули, так? А что они туда строкой 45 не запишутся? ОТлично запишутся куда надо нули, а куда надо - единицы.
Теперьт как можно было проще. Обработчик прерывания НЕ ДОЛЖЕН заниматься выбором масок и т.п. Для него всё уже должно быть готово. Вот как примерно должен выглядеть обработчик прерывания в Вашем случае:
Больше в обработчике не должно быть ничего! В digits должны быть уже готовые к загрузке в порт маски без необходимости что-то выбирать и вычислять. В массиве commonAnodes должны сидеть уже готовые номера пинов для каждой цифры, опять же ничего выбирать не надо.
Всеми делами, которыми у Вас занимается num, должна заниматься функция типа showNumber(), которая вызывается из основной программы и её дело сложить в digits ГОТОВЫЕ к загрузке в порт значения.
Обработчик должен вызываться (для четырёх знаков, как у Вас) раз в 5мс. Чаще не нужно. Всё будет отлично работать.
теперь по поводу того, что Вы говорите: во время записи числа происходит прерывание. Поставьте volatile и не бойтесь этого. Всё равно у Вас реально показывается только одна цифра в каждый момент времени. Запись цифры как таковой не прервётся (т.к. volatile) а чего ещё бояться?
Блин, моя ошибка в том что я думал, что программа подает последний сигнал на ножки в конце своей итерации главного цикла. Когда писал физику для игр такой логикой и пользовался: мол вначале задаем направления сил/импульсов а лишь в самом конце обрабатываем столкновения. Думал что если написать в лупе так:
то светодиод на 13 пине не загорится ни разу, т.к последняя команда записывает в ножку 0.
Кстати у меня индикатор загорается если подавать 0, а не 1.
Может просто сделать опрос датчика рас в секунду, а на дисплей выводить сохраненное значение. Сам так делал, никаких мерцаний.
И еще одну важную вещь понял: выражение PORTA=.... нужно писать только 1 раз в программе(ну в обработчике), иначе будет каша.
Опять же, это присваивание сразу после этой команды на ножки подает сигнал. Я же думал подается лишь последнее присваивание в конце функции/итерации.
Евгений, спасибо, разобрался.