MSP430 LaunchPad и DHT11

в 12:58, , рубрики: DHT11, msp430 LaunchPad, Программинг микроконтроллеров, метки: ,

Здравствуйте!
Недавно приобрел MSP430 LaunchPad и стал изучать документацию в надежде когда-нибудь применить в быту. В этом сообщении опишу получение влажности и температуры с датчика DHT11.
MSP430 LaunchPad и DHT11

В документации на DHT11 оказалось все необходимое для подключения и написания программы.

Схема подключения
MSP430 LaunchPad и DHT11
В моем случае датчик был распаян на платке и сопротивление подтягивающее линию данных к высокому уровню 10кОм, а не 5кОм, как в документации.

Диаграмма поясняющая начало опроса датчика
MSP430 LaunchPad и DHT11
Чтобы запросить текущие данные нужно подтянуть линию данных к низкому уровню и удерживать 18 мс. Через некоторое время датчик сообщит о готовности к передаче данных подтягиванием линии данных к низкому уровню на 80мкс. Далее передаются 40 бит данных старшим битом вперед. Биты кодируются продолжительностью импульса.

Диаграмма импульса соответствующего 0
MSP430 LaunchPad и DHT11

Диаграмма импульса соответствующего 1
MSP430 LaunchPad и DHT11

Передача заканчивается подтягиванием датчиком линии данных к низкому уровню на 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

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js