MSP430 LaunchPad и DHT11

от автора

Здравствуйте!
Недавно приобрел 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/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *