Последние мои покупки у добрых китайцев (или швейцарцев?) были в магазине electrodragon, и одна из них — датчик температуры и влажности воздуха (относительной, прошу заметить!) dht21, он же — AM2301, существуют так же его вариации вроде DHT22 (AM2302), непонятно правда, чем отличается от dht21, при оказии можно будет попробовать и другой.

Описание датчика DHT21/DHT22:

3-проводное подключение;
1-wire интерфейс обмена данными;
пониженное энергопотребление;
не нужно никаких дополнительных компонентов;
Высокая долговременная термостабильность, заводская калибровка;
Цифровой выход;
Передача сигнала на большое расстояние
Измерение температуры и влажности воздуха


Характеристики:

Тип: AM2301/AM2302;

Разрешение данных: 0,1;

Диапазон измерения влажности 0-100% с точностью  ±3%;

Диапазон измерения температуры: -40℃ ~ +80℃ с точностью ±0.5℃.

Для тех, кто забыл, что такое влажность воздуха и почему иногда важно знать её величину, напомню вкратце, что от количества влаги (или пара, воды) в воздухе может зависеть как общее самочувствие человека, состояние его кожи, а то и различные заболевания в редких случаях; бывает, что нужно по другим причинам знать влажность воздуха в помещении (лаборатории, парники…).

Все конечно же, знают, что в воздухе всегда присутствует кроме газов, из которого он состоит, некоторое количество воды в виде пара, который появляется в воздухе в результате испарения воды (ваш К.О.). Но, при разной температуре воздуха в нём может содержаться разное количество воды, — это как в стакане с чаем можно растворить определённое количество сахара, после чего он перестанет растворяться, тогда говорят, что раствор насыщен, а если нагреть чай, то в нём можно будет растворить больше сахара. Охладив чай, мы можем увидеть как «лишний» сахар выпадает в осадок. Так и с воздухом — чем он теплее, тем больше в нём может содержаться воды (пара). Максимальное содержание воды в воздухе при определённой температуре даёт нам насыщенный паром воздух, и эта величина принимается за 100%.

Относительная же влажность воздуха — это то количество воды (пара) которое содержится в воздухе на данный момент относительно полностью насыщенного паром воздуха при заданной температуре. Вот так. Для человека комфортная влажность воздуха нормируется и в общем случае составляет от 30 до 50%, зимой при проветривании она может сильно снижаться (воздух, пришедший с улицы, нагрелся, но содержание воды в нём не изменилось, а значит относительная влажность упала).

Итак, будем измерять относительную влажность воздуха с помощью нашего датчика dht21/dht22. Программа взята с официального форума msp430, немного подкорректирована и добавлен вывод на дисплей hd44780. Ничего сложного:

/*
* This program will interface with the DHT22 Temperature and Humidity sensor using propriatory 1-wire
* interface on Pin P1.0 and Timer A1
* When MCU send start signal, RHT03 change from standby-status to running-status. When MCU finish sending the
* start signal, RHT03 will send response signal of 40-bit data that reflect the relative humidity and
* temperature to MCU.
* MCU will pull low data-bus and this process must beyond at least 1~10ms to ensure RHT03 could detect MCU's signal,
* then MCU will pulls up and wait 20-40us for RHT03's response.
* When RHT03 detect the start signal, RHT03 will pull low the bus 80us as response signal, then RHT03 pulls up
* 80us for preparation to send data.
*
* DHT22 is connected to P2.0 to read it through TA1.0 capture mode
* Data from DHT22 flow MSB to LSB, so the High byte (MSB) first then the Low byte of the 16 bit value of
* Humidity and heat.
* The information we need is represented in the high pulse durations.
* Accordingly, we only care about measuring the high pulses duration only
* -----------------------------------------------------------------------------
* | This code runs at 8Mhz to ensure capturing the DHT22 pulse width correctly |
* -----------------------------------------------------------------------------
*/

#include «msp430g2553.h»
#include «hd44780.h»

#define ISR_DELAY 0 // Timer A1 ISR modes
#define ISR_DHT22_READ 1

#define DHT22_DATA_SIZE 5 // Size of the DHT22 data received in bytes (Hum 2, Temp 2 and checksum 1)
#define DHT22_HIGH_DATA_THRESHOLD 50 // dht22 data high pulse duration threshold to decide on data being 0 or 1
// The ZERO bit high pulse width is 26us and the ONE bit high pulse width
// is 73us. So, the decision to decide if this is one or zero can be taken
// at thresold 50us
#define TRUE 1
#define FALSE 0

typedef enum {START_LOW, START_HIGH, DATA_LOW, DATA_HIGH} DHT22_STATES;
DHT22_STATES dht22_state = START_LOW;
DHT22_STATES previous_dht22_state; // Store previous DHT22 data parsing state

unsigned char dht22[5];
volatile unsigned int new_cap=0;
volatile unsigned int old_cap=0;
volatile unsigned char TimerA1_ISR_mode = ISR_DELAY;
volatile unsigned char bit_counter; // keeps track of the data pulses parsed (maximum 40 bits)
volatile unsigned char byte_counter; // keeps track of the data pulses parsed (maximum 5 bytes)
volatile unsigned char pulse_duration; // duration in uS of the DHT22 generated pulse data

unsigned char delay_in_progress=FALSE;

float Temp, Humidity;

void usdelay(unsigned int interval){
TimerA1_ISR_mode = ISR_DELAY;
// Setup TimerA
TA1CCTL0 = CCIE; // interrupt enabled
TA1CCR0 = TA1R + 8*interval;// micro secs @ 8Mhz Clock
TA1CTL = TASSEL_2 + MC_2; // SMCLK, continuous mode.
delay_in_progress = TRUE; // Indicate to other ISRs that they should not wake the CPU when triggered as the
// CPU is currently sleeping waiting for a delay to expire
//__bis_SR_register(CPUOFF + GIE); // suspend CPU
LPM0;
}

/*
* This Timer A1 interrupt handles two functions:
* — Generate uS delays
* — Read DHT22 sensor data stream in capture mode and store it in an array for later parsing
*/
#pragma vector=TIMER1_A0_VECTOR
__interrupt void Timer_A1_ISR (void)
{
if (TimerA1_ISR_mode == ISR_DELAY) {
TA1CTL = 0; // Stop Timer_A0
delay_in_progress = FALSE; // Indicate to other ISRs that they can wake the CPU now if needed
//__bic_SR_register_on_exit(CPUOFF);
LPM0_EXIT; // Return active
}

if (TimerA1_ISR_mode == ISR_DHT22_READ) {
switch (dht22_state) {
case START_LOW: // we receive a low pulse start from the DHT22
dht22_state = START_HIGH; // be ready to receive a high pulse start
// Initialize DHT22 readings array
for (byte_counter = 0; byte_counter < DHT22_DATA_SIZE; byte_counter++) {
dht22[byte_counter]=0;
}
bit_counter = 0; // reset DHT22 data bit counter within a byte
byte_counter = 0; // reset DHT22 data byte counter
break;

case START_HIGH:
previous_dht22_state = dht22_state;
dht22_state = DATA_LOW; // be ready to receive a low pulse data
break;

case DATA_LOW:
if (previous_dht22_state == DATA_HIGH){ // if we were previously in a dht22 high data pulse, then parse it
new_cap = TA1CCR0; // Store current time stamp (timer value)
// store the length of the previous high pulse
pulse_duration = (new_cap — old_cap)/8;
dht22[byte_counter]<<=1; // shift left to leave a space for the new bit if (pulse_duration > DHT22_HIGH_DATA_THRESHOLD){
dht22[byte_counter] |= BIT0; // This is a ONE bit
} else {
dht22[byte_counter] &= ~BIT0; // This is a ZERO bit
}
bit_counter++; // We passed one date bit, then increment counter
if (bit_counter == 8) { // Finished filling one byte, then move to next byte
byte_counter++;
bit_counter = 0;
if (byte_counter == DHT22_DATA_SIZE){
// disable interrupt and wake processor
TA1CTL = 0; // Stop Timer_A1
dht22_state = START_LOW;// rest to the initial state
LPM0_EXIT; // Return active
break;
}
}
}
dht22_state = DATA_HIGH; // move to the next state
break;

case DATA_HIGH:
old_cap=TA1CCR0; // Store the start of the high data pulse
previous_dht22_state = dht22_state; // As this is what contains the information we need
dht22_state = DATA_LOW;
break;

default:
break;
}
}
}

void read_DHT22(void){
unsigned int T,H;
P2OUT &= ~BIT0; // Issue start signal, P2.0 output low
usdelay(1000); // Wait 1ms to ensure that DHT22 has detected it

// Now listen to the data coming from the DHT22
TimerA1_ISR_mode = ISR_DHT22_READ; // Set ISR mode as parsing DHT22 data
P2SEL |= BIT0; // P2.0 option select Timer_A1
P2DIR &= ~BIT0; // P2.0 as input.
TA1CTL = TASSEL_2 + MC_2 + TAIE; // SMCLK, continuous mode, enable overflow interrupt
TA1CCTL0 = CM_3 + SCS + CCIS_0 + CAP + CCIE;
// Rising and falling edges, capture synchronize, capture mode.
// Interrupt enabled

LPM0; // Sleep CPU (LPM0 as the DHT22 parser is using SMCLK)
// and let the Timer A1 ISR parse the data coming to it in capture mode
// Once all data is read, the ISR will wakeup the CPU to present
usdelay(1000); // the collected data.
TimerA1_ISR_mode = ISR_DELAY;
TA1CTL = TACLR;
TA1CCTL0 = 0;
P2SEL &= ~BIT0;
// See if checksum is correct
if (dht22[4]=dht22[0]+dht22[1]+dht22[2]+dht22[3]) {
H=dht22[0]<<8;
Humidity = H + dht22[1];
T=dht22[2]<<8;
Temp = T + dht22[3];
}
}

void main(void){

WDTCTL = WDTPW + WDTHOLD;
// WDTCTL = WDTPW + WDTTMSEL; // WDT as interval timer,
// f = SMCLK / 32768
//Calibrate DCO for 8MHz operation
BCSCTL1 = CALBC1_1MHZ;
DCOCTL = CALDCO_1MHZ;

HD44780_Init();
HD44780_Clear_Screen();
__enable_interrupt(); // Enable global interrupt

while(1) {

BCSCTL1 = CALBC1_8MHZ;
DCOCTL = CALDCO_8MHZ;

P2OUT |= BIT0; // P2.0 output high
P2DIR |= BIT0; // P2.0 as output
read_DHT22();

BCSCTL1 = CALBC1_1MHZ;
DCOCTL = CALDCO_1MHZ;

HD44780_SetPosition(0,0);
HD44780_PrintStr(«Temp:»);
HD44780_SetPosition(1,0);
HD44780_PrintStr(«Humidity:»);

HD44780_SetPosition(0, 6);
HD44780_outdec(Temp, 1);
HD44780_SetPosition(1, 10);
HD44780_outdec(Humidity, 1);

HD44780_SetPosition(0,10);
HD44780_PrintStr(«C»);
HD44780_SetPosition(1,14);
HD44780_PrintStr(«%»);

usdelay(1000);
// read_DHT22(); // Breakpoint on debugger, return from call, never advance to next call.
// read_DHT22();
// LPM3; // Enter LPM3 w/ interrupt
}
}

К сожалению, обнаружилась проблема, которую я так и не смог пока побороть, — это зависание программы при повторном вызове функции чтения информации с датчика. Пока что программа работает за счёт «костыля» в виде увеличенной задержки при работе с hd44780, но это неправильно и надо разобраться, как её починить. Если у кого-то есть мысли по этому поводу, — прошу поделиться!

 файлы проекта