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[42];
    //Декодированные данные
    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.3
    #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]; // Отправляем очередной символ из строки
    	}
    }
    

    • +26
    • 22,4k
    • 6
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Комментарии 6
    • +2
      Чтобы понимать смысл некоторых и многих других флагов, советую 2 вещи:
      1) we.easyelectronics.ru/tag/MSP430/ — Уроки MSP430 LaunchPad.
      2) Книга СЕМЕЙСТВО МИКРОКОНТРОЛЛЕРОВ MSP430x2xx Архитектура Программирование Разработка приложений. 530 страниц где-то. Это русский перевод зарубежной книги.Там вся основа расписана.
      • +1
        Да, есть такая книжка, но не все еще успел прочитать. А из того что прочитал не все понимаю, например, флаг в ней описывают, а на что он влияет нет.
        «XCAPx — Выбор нагрузочной ёмкости. При XTS = 0 эти биты определяют ве
        личину нагрузочной ёмкости для резонатора, подключаемого к гене
        ратору LFXT1. Если XTS = 1 или LFXT1Sx = 11, то биты XCAPx
        должны быть равны 00.
        Величина нагрузочной ёмкости для кварцевого резонатора
        при работе генератора LFXT1 в режиме LF задаётся программно конфигурируе
        мыми битами XCAPx. Эта ёмкость может принимать значения из ряда: 1 пФ,
        6 пФ, 10 пФ или 12.5 пФ. При необходимости допускается подключение внешних
        конденсаторов.»
        Мне не понятно, что изменится если изменять этот флаг.
        • 0
          Собственно говорят если менять XCAPx при XTS = 0 то это выбор ёмкости, а смысл её выбора в том, что для корректной работы кварца с ним в связке должен стоять конденсаторы определенной ёмкости, иначе он может даже не заработать (величину ёмкость пишут в даташите на кварц). Если XTS = 1 или LFXT1Sx = 11, то биты XCAPx должны быть равны 00 — тут скорее всего из-за того чтобы внутренние емкости не мешали работе т.к. предполагается использование внешних.
          • 0
            «Нагрузочная емкость — любая внешняя емкость, включенная последовательно с резонатором, становится элементом, изменяющим частоту резонанса. Варьируя нагрузочную емкость, можно, в некоторых пределах, изменять резонансную частоту. Некоторые изготовители иногда заранее рекомендуют использовать стандартные значения нагрузочной емкости для точной настройки резонансной частоты.»
            И как измерить частоту, в среде разработки можно увидеть, как влияет изменение флагов на частоту?
            • 0
              В том то и дело что просто так не вычислить частоту. Для этого и подбираются кварцы и нагрузочные емкости, чтобы задать точные значения частоты генератора. А так чтобы вычислить частоту, необходимо иметь более точный генератор. Встроенный генератор на стандартной частоте по даташиту имеет большой разброс частоты 0.80 — 1.50 МГЦ, по этому его тоже нельзя использовать для точного измерения. По этому придется использовать или внешний генератор (с заранее известным точным значением) или осциллограф.
              • 0
                Изменения флагов сильно большого сдвига частоты не дадут, но как вариант — для грубых вычислений можно замерить 10 млн тактов и послать через UART сигнал, а потом еще раз измерить и послать, а на компе уже вычислять время за которое это всё произошло. Затем изменить емкость и повторить заново.

        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.