Недавно приобрел MSP430 LaunchPad и стал изучать документацию в надежде когда-нибудь применить в быту. В этом сообщении опишу получение влажности и температуры с датчика DHT11.
В документации на DHT11 оказалось все необходимое для подключения и написания программы.
Схема подключения
В моем случае датчик был распаян на платке и сопротивление подтягивающее линию данных к высокому уровню 10кОм, а не 5кОм, как в документации.
Диаграмма поясняющая начало опроса датчика
Чтобы запросить текущие данные нужно подтянуть линию данных к низкому уровню и удерживать 18 мс. Через некоторое время датчик сообщит о готовности к передаче данных подтягиванием линии данных к низкому уровню на 80мкс. Далее передаются 40 бит данных старшим битом вперед. Биты кодируются продолжительностью импульса.
Диаграмма импульса соответствующего 0
Диаграмма импульса соответствующего 1
Передача заканчивается подтягиванием датчиком линии данных к низкому уровню на 50 мкс.
Программа написана на Си в Code Composer Studio v5 и построена следующим образом, при нажатии кнопки на отладочной плате, отключается прерывание кнопки, ножка, к которой подключена линия данных (в моем случае P2.5) конфигурируется как выход, на нее подается 0 и запускается таймер на 20 мс. По прерыванию таймера, ножка конфигурируется как вход, разрешается прерывание при изменении уровня сигнала на ней с 1 на 0 и запускается таймер. Прерывание этого таймера при переполнении (65 мс) используется для завершения процесса считывания, а отсчеты для замера длительности между перепадами уровня сигнала с 1 на 0. В процедуре обработки прерывания входа в массив записывается значение таймера равное промежутку времени между перепадами уровня сигнала с 1 на 0 и перезапускается таймер. В первом элементе массива число, которое не имеет смысла, во втором промежуток времени от начала ответа датчика до начала передачи данных, в оставшихся 40 промежутки времени соответствующие 0(от 76 до 78 мкс, в моем случае оказалось меньше 70) и 1(120 мкс, также оказалось чуть меньше). Когда перепады уровня заканчиваются срабатывает прерывание таймера по переполнению. По этому прерыванию отключаем прерывания на входе, отключаем таймер, преобразуем интервалы времени в биты информации(1 байт — влажность, 2 — 0, 3 — температура, 4 — 0, 5 — контрольная сумма, должна быть равна сумме первых 4 байт), передаем по UART и разрешаем прерывание кнопки. В терминале на ПК видим примерно следующее
Serial port COM2 opened CheckSum=Ok RH=36 T=28 CheckSum=Ok RH=35 T=28 CheckSum=Ok RH=35 T=28 CheckSum=Ok RH=35 T=28 Serial port COM2 closed
Исходный код программы
#include <msp430g2553.h> #include <stdio.h> #include <string.h> //Массив для записи ответа датчика unsigned int signal[100]; //Декодированные данные unsigned short data[5]; //Номер элемента массива signal int signalElement = 0; //Количество отсчетов таймера для подавления //дребезга кнопки, 10*50000=0,5 с int debouncePause = 0; //Флаг статуса, равен 1 когда идет получение данных int isDataReading = 0; //Функция для отправки строки по UART void sendString(char*); void main(void) { //Останавливаем сторожевой таймер WDTCTL = WDTPW + //любые операции с регистром сторожевого таймера //должны выполнятся с установленным флагом WDTPW WDTHOLD; //флаг WDTHOLD используется для остановки отсчета //сторожевого таймера //Красный светодиод подключен к выводу P1.0 //Используем для индикации процесса получения данных //Настраиваем P1.0 как выход и подаем на него низкий уровень P1DIR |= BIT0; P1OUT &= ~BIT0; //Зеленый светодиод подключен к выводу P1.6 //Используем для индикации готовности устройства выполнить //попытку получить данные с датчика //Настраиваем P1.6 как выход и подаем на него высокий уровень P1DIR |= BIT6; P1OUT |= BIT6; //Кнопка подключена к выводу P1.3 //Используем ее для иницирования получения данных от датчика //Настраиваем P1.3 как вход, включаем подтягивающий //к высокому уровню резистор, очищаем флаг прерывания и //разрешаем прерывание для P1.3 при изменении сигнала на входе //с 1 на 0 P1DIR &= ~BIT3; P1OUT |= BIT3; P1REN |= BIT3; P1IFG &= ~BIT3; P1IES |= BIT3; P1IE |= BIT3; //Вывод P1.2 используется для передачи данных по UART //Настроим его для выполнения этой функции P1SEL |= BIT2; P1SEL2 |= BIT2; //Настройка UART //Предварительно сделал настройки с помощью GRACE в //тестовом проекте,т.к. пока не разобрался с регистрами //и флагами этой периферии //Baund 9600 UCA0CTL1 |= UCSSEL_2; UCA0BR0 = 104; UCA0BR1 = 0; UCA0MCTL = UCBRS0; UCA0CTL1 &= ~UCSWRST; //Настройка источников тактовых импульсов //Также настроил в Grace на 1МГц, т.к. пока еще // не совсем понимаю смысл некоторых флагов BCSCTL2 = SELM_0 + DIVM_0 + DIVS_0; if (CALBC1_1MHZ != 0xFF) { DCOCTL = 0x00; BCSCTL1 = CALBC1_1MHZ; DCOCTL = CALDCO_1MHZ; } BCSCTL1 |= XT2OFF + DIVA_0; BCSCTL3 = XT2S_0 + LFXT1S_2 + XCAP_1; //Запускаем бесконечный цикл программы while (1) { //Останавливаем ЦПУ и разрешаем прерывания //В этом месте выполнение программы останавливается //до момента очистки флага CPUOFF //(для понижения скорости разряда батареи) __bis_SR_register(CPUOFF + GIE); unsigned int i = 0; unsigned int j = 0; //Цикл для преобразования временного интервала захваченных //импульсов в биты данных for (j = 0; j < 5; j++) { //Очищаем байт данных от предыдущего значения data[j] = 0; for (i = 0; i < 8; i++) { int k = i + 2 + j * 8; //Если длительность между перепадами с 1 на 0 //больше 100 но меньше 120, то устанавливаем единичку //в соответствующий бит байта данных if (signal[k] > 100 && signal[k] < 120) { data[j] |= (1 << (7 - i)); } } } char buf[30]; //Буфер для символов memset(buf, 0, 30); //Заполняем его 0 //Выводим в буфер результат проверки контрольной суммы sprintf(buf, "CheckSum=%s\n", data[0] + data[1] + data[2] + data[3] == data[4] ? "Ok" : "Error"); sendString(buf); //отправляем буфер по UART memset(buf, 0, 30); //Выводим в буфер значение влажности sprintf(buf, "RH=%d\n", data[0]); sendString(buf); memset(buf, 0, 30); //Выводим в буфер значение температуры sprintf(buf, "T=%d\n", data[2]); sendString(buf); //Включаем зеленый светодиод, //устройство готово считывать температуру снова P1OUT |= BIT6; //Выключаем красный светодиод, //получение данных завершено P1OUT &= ~BIT0; //Очищаем флаг прерывания и разрешаем прерывание для ножки P1.3, к которой подключена //кнопка P1IFG &= ~BIT3; P1IE |= BIT3; //Инициализируем переменные для нового захвата данных isDataReading = 0; debouncePause = 0; signalElement = 0; } } //Процедура обработки прерывания №0 таймера #pragma vector=TIMER0_A0_VECTOR __interrupt void Timer0_A0(void) { //Если прошло 10*50000=0,5 с if (debouncePause == 9) { //Очищаем таймер TA0CTL = TACLR; //Настраиваем ножку P2.5 как выход //и подаем на нее низкий уровень P2DIR |= BIT5; P2OUT &= ~BIT5; //Очищаем таймер, и перезапускаем в режиме UP на 20 мс, чтобы дать понять датчику, //что нам нужны данные TA0CCR0 = 20000; TA0CTL = TASSEL_2 + MC_1; } //Через 20 мс попадаем в эту ветку if (debouncePause == 10) { //Очищаем таймер, //запрещаем прерывание №0 таймера, TA0CTL = TACLR; TA0CCTL0 &= ~CCIE; //Настраиваем P2.5 как //вход, очищаем флаг прерывания, разрещаем прерывания, P2DIR &= ~BIT5; P2IFG &= ~BIT5; P2IES |= BIT5; P2IE |= BIT5; //Перезапускаем таймер в режиме Continuous и разрешаем //прерывание №1, которое сработает при переполнении счетчика - //это будет сигналом, что данные получены или датчик не отвечает TA0CTL = TASSEL_2 + MC_2 + TAIE; } debouncePause++; } //Процедура обработки прерывания №1 таймера #pragma vector=TIMER0_A1_VECTOR __interrupt void Timer0_A1(void) { //По флагу переполнения таймера switch (TA0IV) { case TA0IV_TAIFG: //Запрещаем прерывания //для ножки P2.5 , очищаем таймер и включаем ЦПУ, //чтобы выполнить обработку полученных данных и //перадать их по UART P2IE &= ~BIT5; TA0CTL = TACLR; __bic_SR_register_on_exit(CPUOFF); break; default: break; } } //Процедура обработки прерывании кнопки, ножка P1.5 #pragma vector=PORT1_VECTOR __interrupt void Port_1(void) { //Очищаем флаг прерывания и запрещаем прерывания для кнопки, //чтобы избежать дребезга P1IFG &= ~BIT3; P1IE &= ~BIT3; //Отключаем диоды P1OUT &= ~BIT0; P1OUT &= ~BIT6; //Запускаем таймер в режиме UP и разрешаем прерывание №0 //оно срабатывает при достяжении значения 50000 // и отсчет начивается заново TA0CCR0 = 50000; TA0CCTL0 |= CCIE; TA0CTL = TASSEL_2 + MC_1; } //Процедура обработки прерывании линии данных датчика, ножка P2.5 #pragma vector=PORT2_VECTOR __interrupt void Port_2(void) { //Очищаем флаг прерывания P2IFG &= ~BIT5; //Копируем значение таймера в массив signal[signalElement] = TA0R; //Очищаем и перезапускаем таймер TA0CTL = TACLR; TA0CTL = TASSEL_2 + MC_2 + TAIE; //Моргаем красным диодом P1OUT ^= BIT0; //Увеличиваем на 1 номер элемента массива signalElement++; } void sendString(char * text) { int i = 0; for (i = 0; i < strlen(text); i++) { while (!(IFG2 & UCA0TXIFG)) ; // Если буфер для отправки готов UCA0TXBUF = text[i]; // Отправляем очередной символ из строки } }
ссылка на оригинал статьи http://habrahabr.ru/post/160769/
Добавить комментарий