Здравствуйте!
Недавно приобрел 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=%sn",
data[0] + data[1] + data[2] + data[3] == data[4] ?
"Ok" : "Error");
sendString(buf); //отправляем буфер по UART
memset(buf, 0, 30);
//Выводим в буфер значение влажности
sprintf(buf, "RH=%dn", data[0]);
sendString(buf);
memset(buf, 0, 30);
//Выводим в буфер значение температуры
sprintf(buf, "T=%dn", 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]; // Отправляем очередной символ из строки
}
}
Автор: constv