Понадобилось мне добавить в устройство датчик влажности. Купил на али. Написал код. Но не работает. Несколько дней бился, но так и не смог добиться результата.
Имею вот такую программу Спойлер
Код:
////////////////////////////////////////////////////////// // ПАРАМЕТРЫ UART // ////////////////////////////////////////////////////////// #define BAUD 9600UL // Скорость обмена по UART #define SPEED ((F_CPU+BAUD*8)/(BAUD*16)-1) #define F_CPU 8000000UL
/********************************************************* НАБОР ФУНКЦИЙ ДЛЯ РАБОТЫ ЧЕРЕЗ UART *********************************************************/ void init_uart(void){ UCSRB = (1<<TXEN|1<<RXEN|0<<RXCIE); // Настройка UART UCSRC = (1<<URSEL|1<<UCSZ1|1<<UCSZ0); UBRRL = (uint8_t)(SPEED & 0xFF); UBRRH = (uint8_t)(SPEED >> 8); }
void out_uart(uint8_t data){ // Передача байта через UART while(!(UCSRA&(1<<UDRE))); // Ожидание готовности UART к передаче UDR = data; // Запись в регистр UDR байта данных начинает процесс передачи }
uint8_t in_uart(void){ // Прием байта из UART while(!(UCSRA&(1<<RXC))); // Ожидание прихода байта return UDR; // Возвращение принятого байта
//Инициализация датчика DDR_SENSOR |=(1<<PIN_DHT11); // выход PORT_SENSOR &=~(1<<PIN_DHT11); //в низкий уровень _delay_ms (18);
//Переводим порт в режим чтения PORT_SENSOR |=(1<<PIN_DHT11); //отпускаем линию _delay_us (40); DDR_SENSOR &=~(1<<PIN_DHT11); // вход, режим чтения
//Ждем сигнал готовности датчика к передаче while (PIN_SENSOR & (1<<PIN_DHT11)){} //ждем, датчик должен ответить 0 _delay_us (60); while (!(PIN_SENSOR & (1<<PIN_DHT11))){} //Ждем отпускания линии датчиком _delay_us (60);
//Начало приема данных while (PIN_SENSOR & (1<<PIN_DHT11)){} //эжем начала передачи, начинается с нуля, примерно через 80 мс
for (uint8_t byte = 0; byte < 5; byte++) { datadht[byte]=0; for (uint8_t bit=0; bit<8; bit++) { cli (); // запрещаем прерывания while (!(PIN_SENSOR & (1<<PIN_DHT11))){} //ждем когда датчик отпустит шину _delay_us (30); //задержка высокого уровня при 0 30 мкс if ((PIN_SENSOR & (1<<PIN_DHT11))){ //если по истечению времени сигнал на линии высокий, значит передается 1 datadht[byte]|=1<<(7-bit); //тогда i-й бит устанавливаем 1 } while (PIN_SENSOR & (1<<PIN_DHT11)); // ждем окончание 1 sei ();// разрешаем общее прерывание } } humi = datadht[0];
return 0; }
int main(void) {
//===Инициализация портов==== //Порт на выход DDR_LOAD |= (1<<PIN_LAMP|1<<PIN_VENT| 1<<PIN_DEBUG1|1<<PIN_DEBUG2); PORT_LOAD &= ~(1<<PIN_LAMP|1<<PIN_VENT| 1<<PIN_DEBUG1|1<<PIN_DEBUG2); //обнуляем выход
//Вход с подтяжкой DDR_SENSOR &= ~(1<<PIN_DET1|1<<PIN_DET2|1<<PIN_PIR|1<<PIN_DHT11); PORT_SENSOR |= (1<<PIN_DET1|1<<PIN_DET2|1<<PIN_PIR|1<< PIN_DHT11);
На УАРТ ничего не идет. Вот что показывает логический анализатор, подключенный на ножку данных датчика Только передний "провал" показывает длительностью 18ms. Потом все импульсы и паузы, либо длительностью 50, либо 100 us. Причем, посылка делается один раз. Потом просто высокий уровень стоит. Не пойму, то ли с программой что то, то ли датчик неисправный какой то пришел. Может кто то сталкивался с таким? Спасибо.
Спасибо. Попробую ещё раз с этими документами сверить.
А можно вопрос? Это выложено в педагогических целях (то есть вы там увидели явную ошибку) или все таки это как документ, могущий помочь. Просто вроде не одну диаграмму посмотрел. Ну и делал согласно тем диаграммам.
Да какая разница на чем писать, в данном случае.. это ж просто временные диаграммы. есть подозрение, что с датчиком просто что то.. хотелось бы это опровергнуть или подтвердить как то..
Я делал и работает. На захвате по таймеру, с внутренним RC генератором. А посему ввёл толеранцию, с которой пришлось поиграться. Сейчас оставил 5uS, но не всегда получаю результат, буду увеличивать. Асм, мега8.
_________________ Я всё-всё узнAю и стану профессором.
Объяснить что именно? Алгоритм? Так по даташиту всё и делал. Таймер1 - 1 тик=1uS. Генерируем стартовый импульс >1000uS - влетаем в прерывание. В обработчике перенастраиваем таймер на захват. Получаем в регистрах ICR длительность импульсов в uS. А дальше разгребаем, пришёл 0 или 1. Это сдвигом пихаем в байт. Да, у меня DHT22, но протокол тот же.
_________________ Я всё-всё узнAю и стану профессором.
Объяснить что именно? Алгоритм? Так по даташиту всё и делал. Таймер1 - 1 тик=1uS. Генерируем стартовый импульс >1000uS - влетаем в прерывание. В обработчике перенастраиваем таймер на захват. Получаем в регистрах ICR длительность импульсов в uS. А дальше разгребаем, пришёл 0 или 1. Это сдвигом пихаем в байт. Да, у меня DHT22, но протокол тот же.
А, отличие только что вы на таймерах делали?.. У меня боюсь таймеров не хватит. Или с ними как то извращаться нужно, чтобы комбинировать под разные задачи, что я наверно не осилю.
А посему ввёл толеранцию, с которой пришлось поиграться.
Их бы объяснить. Догадываюсь о чем, но хотелось бы точно.
Добавлено after 4 hours 36 minutes 21 second: На другом форуме проверили мой код в протеусе. Код рабочий. Скорее всего проблема с датчиками. Китайцы наверно фуфло прислали.
На таймерах... можно конечно.... Интервал единицы принимаем за целое Делим на 8. Таймер настраиваем на 1/8. Подсчет переполнений за время приема активной части импульса и табличный селектор для записи значения бита данных, соответствующего значению количества переполнений в текущий байт данных а там 1/2 +/-1/8 это 0, а все, что более - 1. Как-то так... Только длительность интервалов весьма маахонькая - проще количество программных задержек использовать.
С таймерами наверно может и хорошо. Вроде как точнее. Хотя мне кажется для такого датчика точность временных задержек не важна. Но и, как минимум, два у меня уже используются для отсчета времени. Третий тоже под вопросом. Да и сложнее это как то. Постоянно переконфигурировать таймер.
В моем проекте на программных циклах сделано было. МК не сильно шустрый - 1МГц (0,000001 секунда на команду). Воть такой варьянт Спойлер
Код:
; ; "dht11.txt" файл обработчика шины датчика влажности/температуры ; DHT-11 ; ;---------- ; заготовка программного модуля для обработки данных датчика DHT-11 ; формат данных пять байт : ; влажность RH целая часть ; влажность RH дробная часть (у DHT-11=0) ; температура Т целая часть ; температура Т дробная часть (у DHT-11=0) ; контрольная сумма (сумма первых четырех байт ; ; данные передаются старшими битами вперед по однопроводной ; шине подобно протокола микроLAN, разрыв приёма пакета недопустим. ; используются монопольно все регистры текущего регистрового банка. ; ; ---------- константы ---------- ; ; ; btrg equ 40 ; количество бит в пакете (5байт*8бит=40) ; DHTD equ 5 ; позиционный номер линии данных DHT-11 ; port_dht equ P3 ; переприсвоение порта ; ; ---------- регистры ---------- ; ; .DSEG ; rhdt equ mlans ; целая часть значения влажности в двоичном коде ; thdt equ mlans+1 ; целая часть значения температуры в двоичном коде ; регистры текущего банка - ; ; R7 - байт целых значений влажности ; R6 - байт дробных значений влажности ; R5 - байт целых значений температуры ; R4 - байт дробных значений температуры ; R3 - байт контрольной суммы ; R2 - счётчик задержки внутренний ; R1 - счётчик бит пакета ; R0 - счётчик задержки внешний ; ; ---------- флаги ---------- ; ; .BSEG ; dht_sf: .dbit 1 ; флаг семафора запроса/квитирования исполнения ; программы чтнеия данных с DHT-11 (flags2.0) ; dht_rdd: .dbit 1 ; flags2.1 флаг семафора переключения задач обработчиков ; "запрос чтения данных с DHT-11" ; dht_ptr: .dbit 1 ; flags2.2 флаг семафора переключения задач обработчиков ; "обработка/вывод на дисплей данных с DHT-11" ; ; ----- собственно утилиты ----- ; .CSEG dhtrd: setb port_dht.DHTD ; страховка clr a mov r7,a mov r6,a mov r5,a mov r4,a mov r3,a mov r1,#btrg ; предподготовка РБ quest: mov r0,#180 clr port_dht.DHTD tm_dht0: mov r2,#48 djnz r2,$ djnz r0,tm_dht0 mov r2,#88 djnz r2,$ ;генерация импульса запроса датчика 18mS setb port_dht.DHTD ; переводим линию данных на ввод с подтяжкой к 1 quest1: mov r2,#17 djnz r2,$ ; ждём ~34uS wt_dht0: ; error control mov a,port_dht jb ACC.DHTD,wt_dht0 ; ждем начало ответа датчика mov r2,#20 djnz r2,$ ; ждём ~40uS wt_dht1: ; error control mov a,port_dht jnb ACC.DHTD,wt_dht1 ; ждем окончание состояния 0 импульса ответа mov r2,#35 djnz r2,$ ; ждём ~70uS wt_dht2: ; error control mov a,port_dht jb ACC.DHTD,wt_dht2 ; ждем окончание состояния 1 импульса ответа mov r2,#23 djnz r2,$ ; ждём ~46uS wt_dht3: ; error control mov a,port_dht jnb ACC.DHTD,wt_dht3 ; ждем окончание состояния 0 первого импульса строба clr c first: mov r2,#8 djnz r2,$ ; задержка 16uS paket_dht: mov a,port_dht ; ждем окончание импульса значения текущего бита данных jnb ACC.DHTD,mount ; 3uS mov a,port_dht jnb ACC.DHTD,mount ; 3uS mov a,port_dht jnb ACC.DHTD,mount ; 3uS mov a,port_dht jnb ACC.DHTD,mount ; 3uS mov a,port_dht jnb ACC.DHTD,mount ; 3uS mov a,port_dht jnb ACC.DHTD,mount ; 3uS итоговое ожидание 1 в пределах 19-31uS setb c ; если окончание уровня 1 прошло в пределах 19-31uS обрабатывается 0, иначе это 1 wt_dht4: mov a,port_dht jb ACC.DHTD,wt_dht4 ; ждем окончание состояния 1 импульса ответа mount: mov a,r3 rlc a mov r3,a mov a,r4 rlc a mov r4,a mov a,r5 rlc a mov r5,a mov a,r6 rlc a mov r6,a mov a,r7 rlc a mov r7,a clr c ; страховой сброс С ; обработка текущего бита завершена (16uS) mov r2,#14 djnz r2,$ ; ждём ~28uS (в итоге вместе с обработкой массива вписались в 44uS) wt_dht5: ; error control mov a,port_dht jnb ACC.DHTD,wt_dht5 ; ждем окончание состояния 0 очередного импульса строба (~47-54uS) djnz r1,first ; декремент счетчика бит в пакете и если не равно 0 повтор от first ; последующее в задержке опроса 16+2=18uS (в сумме интервал 21-34uS) ;---------- ; храним результат в буфер mov mlans,r7 ; значение RH mov (mlans+1),r6 ; десятые RH (вероятен 0) mov (mlans+2),r5 ; значение Т mov (mlans+3),r4 ; десятые Т (вероятен 0) mov (mlans+4),r3 ; контрольная сумма предыдущего ;---------- ret ; обработка пакета, включая импульс завершения завершена ; ; error control - обработчик "сторожевой собаки" ошибки протокола/физического интерфейса датчика ; ;---------- ;
это "чистый ассемблер" для MCS51. Под АВР ку в "чистом асме" переделать не проблема, а вот с Сишным ассемблером (ассемблерные вставки) я не работаю - там несколько своя специфика.
Объясняю про толеранцию. В начале библы определяем, что толеранция= допустим,10uS. Когда произошёл захват, т.е. в обработчике прерывания по захвату впихиваем в память содержимое регистра ICR. В основном цикле берём из памяти младший байт - это длительность полученного от датчика импульса в микросекундах. Допустим, ловим импульс 80uS. Проверяем, если длительность <80-10 -это ошибка, если >80+10 - тоже ошибка. Т. е. Фактически ловим импульс 80+-10uS. Может датчик и выдаёт твёрдые интервалы, но с точки зрения внутреннего RC генератора эти интервалы очень растяжимое понятие. PS. кстати, почему для отсчёта времени надо 2 таймера? Всю работу с временем может делать один самый глупый таймер на борту. У меня таймер0 считает время, таймер1 занимается датчиком влажности, таймер2 молотит шим.
На таймерах... можно конечно.... Интервал единицы принимаем за целое Делим на 8. Таймер настраиваем на 1/8. Подсчет переполнений за время приема активной части импульса и табличный селектор для записи значения бита данных, соответствующего значению количества переполнений в текущий байт данных а там 1/2 +/-1/8 это 0, а все, что более - 1. Как-то так... Только длительность интервалов весьма маахонькая - проще количество программных задержек использовать.
Да нет, я я запускаю таймер на захват(после старта) 1 тик= 1uS. И выпрыгиваю в главный цикл, где проверяю флаг, который устанавливается в прерывании по захвату. Соответственно, достаю из RAMа число (оно в микросекундах) и решаю 0 это или 1. И опять в главный цикл. Т.е. не зависаю на 4 mS на датчике. Т.к.полученное число в микросекундах, просто по ДШ сравниваем (а чё это было) - 0, 1, или ещё какая херь.
_________________ Я всё-всё узнAю и стану профессором.
О PS. кстати, почему для отсчёта времени надо 2 таймера? Всю работу с временем может делать один самый глупый таймер на борту.
Таймеры считаю время для разных функций. Почему? - потому что мне так было проще. Наверно можно и на одном, но не хотел заморачиваться этим, т.к. вроде и так хватало)
Добавлено after 7 hours 1 minute 29 seconds: dgrett, перечитал еще ваше сообщение несколько раз. Возможно позже и попробую на таймере замутить. Но позже.. Пока хочется хоть как то замутить
Добавлено after 1 hour 13 minutes 8 seconds: Но! Т.к. протокол основан на измерении длительности импульсов(1-Wire), то сам атмел велел использовать таймер1 в режиме захвата, имхо.
Добавлено after 11 minutes 31 second: tux, я в сях ноль полный, но я вижу, что стартовый импульс у вас 1 секунда, а это в 1000 раз больше чем надо. И в других местах тоже mS вместо uS. Подтяжка к плюсу, надеюсь, имеется?
_________________ Я всё-всё узнAю и стану профессором.
1 секунда это не стартовый импульс. Это просто задержка после включения. В мс потом только стартовый импульс, который по даташиту равен 18мс. Потом везде микросекунды.
Добавлено after 2 minutes 12 seconds: Ну и как я уже писал выше - программу проверили в симуляторе, алгоритм рабочий, проблема с вероятностью 99% где то в железе
Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 11
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения