3
\$\begingroup\$

I'm trying to interface a PIC with the DHT11 sensor and it's not working. I feel I have tried everything. Below is a stripped down version of my code which should at least make the sensor send a response message.

Basically a LED should turn on as soon as I get a HIGH value from the sensor. But I'm not getting anything..

If someone could help to solve this it would be really great. I'm also attaching a picture of my set-up.

Thanks a lot!

#include <stdio.h>
#include <stdlib.h>
#include <delays.h>

// PIC18F4620 Configuration Bit Settings

#include <p18F4620.h>


#pragma config OSC = HSPLL      // Oscillator Selection bits (HS oscillator, PLL enabled (Clock Frequency = 4 x FOSC1))
#pragma config WDT = OFF 
#pragma config PWRT = OFF       // Power-up Timer Enable bit (PWRT disabled)
#pragma config BOREN = OFF      // Brown-out Reset Enable bits (Brown-out Reset disabled in #pragma config WDT = OFF        // Watchdog Timer Enable bit (WDT disabled (control is placed on the SWDTEN bit))

#define DHT11_TRIS  TRISDbits.RD1
#define DHT11_IO    PORTDbits.RD1  //READ USING THIS

#define LED0_TRIS   TRISDbits.RD0
#define LED0_IO     PORTDbits.RD0

int my_flag = 0,bit_counter=39,t1=0,t2=0;

int response[]={0,0,0,0,0,0,0,0,0,0,
                0,0,0,0,0,0,0,0,0,0,
                0,0,0,0,0,0,0,0,0,0,
                0,0,0,0,0,0,0,0,0,0};

void high_isr(void);
void low_isr(void);

#pragma code InterruptVectorHigh = 0x08
void interrupt_at_high_vector(void)
{
_asm GOTO high_isr _endasm
}

#pragma code low_vector=0x18
void interrupt_at_low_vector(void)
{
_asm GOTO low_isr _endasm
}
#pragma code /* return to the default code section */
#pragma interrupt high_isr
void high_isr(void)
{
// return from high priority interrupt

}

#pragma interruptlow low_isr
void low_isr (void)
{
// return from low priority interrupt        
}

#define GetSystemClock()    (32000000ul)
#define GetInstructionClock()   (GetSystemClock()/4)



void Delay10us(int us)  {
    Delay10TCYx(((GetInstructionClock()/1000000)*(us)));
}


void DelayMs(int ms){

    unsigned int _iTemp = (ms);     
    while(_iTemp--)         
    Delay1KTCYx((GetInstructionClock()+999999)/1000000);    

}


void main(void){
    int c=0;
    CMCON = 7;

    DHT11_TRIS = 0; //Set DATA OUTPUT
    DHT11_IO = 0;

    LED0_TRIS = 0; //Set LED as OUTPUT
    LED0_IO=0;


    DHT11_IO = 1;

    DelayMs(1000);
    DHT11_IO = 0; //Pull Low
    DelayMs(20);  //For 20ms
    DHT11_IO = 1; //Set HIGH
    Delay10us(3);// For 30us
    DHT11_IO = 0;

    DHT11_TRIS = 1; //Set as input

    while (DHT11_IO !=1){}
    LED0_IO = 1;
    DelayMs(5000);
}

Edit: I would post my set-up but I'm not allowed due to rep points!

\$\endgroup\$
  • \$\begingroup\$ post link to picture of your setup and somebody will put it in the post \$\endgroup\$ – justing Sep 10 '12 at 21:41
  • \$\begingroup\$ We also need link to sensor datasheet and an explanation from you on the way it's supposed to interface with the PIC would be nice too, just in case you happen to interpret datasheet differently from us. \$\endgroup\$ – AndrejaKo Sep 10 '12 at 21:53
  • \$\begingroup\$ Ok, so here is a link to my setup: rapidshare.com/files/3248153354/DSC_0361.JPG Here is the link to sensor datasheet: micropik.com/PDF/dht11.pdf \$\endgroup\$ – DimC Sep 11 '12 at 5:52
  • \$\begingroup\$ Basically I keep the data pin high for one second which should keep the sensor in idle state. Then I pull the pin low for 20ms, then high for 30us. Then I wait for a response from the sensor, which should be low at first (80us) and then high (80us). \$\endgroup\$ – DimC Sep 11 '12 at 5:55
1
\$\begingroup\$

The DHT11 sensor you are using appears to use a protocol similar to 1-wire, though the timings look a bit different at a glance. This was invented by Dallas (now Maxim) and is used in many of their devices, the DS18S20 is an example of a similar temperature sensor to the DHT11 made by them.

My experience with 1-wire (and the above maxim part) is that is a great protocol but can be a little fiddly setting up on a small micro due to the quite fast and not-so-tolerant timing, especially if you are using a lowish speed oscillator and C. However this 1-wire part seems to have more relaxed timings and data direction appears to be only one way, so things should be a bit simpler (although the maxim part can do more)

Anyway, the initial presence detect for your part is an easy one since you just have to hold the line down for at least 18ms, raise it high for 20-40us and then release it (set to high impedance input)
I'm not sure why it says to pull it high for the 20-40us, when releasing it and having the pullup do this (the Maxim parts do it this way, and this is standard for open drain communication) would have the same effect and prevent any potential bus conflicts, but it may be to do with the rise time needed. In any case as long as you release the line before 40us it should be okay.

I haven't checked everything, but in your start signal code it looks like you are doing things correctly (although I would avoid setting the output low before setting the pin to input) so it's likely it's to do with the micro settings or a connection issue (e.g. miswired pin, open circuit)

A few things to check:

  • Have you tried a simple blink LED routine to confirm your LED is setup correctly?
  • Are you absolutely sure your delay routines are producing the correct delay? Have you confirmed this on a scope?
  • Have you confirmed the PIC pin is actually outputting the high/low levels correctly to the DHT11? (i.e. pin is setup and connected correctly - test at DHT11 pin to make sure signal is reaching it)
  • Is the 5kΩ pullup resistor (recommended in the datasheet for lines <20m, if longer you may have to reduce the value) present on the line?
\$\endgroup\$
  • \$\begingroup\$ Hi Oli, Yes, I tried blinking the LED. Regarding the delay routines, I am 95% sure they work correctly. I can't check on the scope for 20us delay, but at least for 1 second/5 second test delays they work fine. For setup pls. check link posted above. I don't have a 5K resistor, but a 4.7K. Should work... \$\endgroup\$ – DimC Sep 11 '12 at 6:00
  • \$\begingroup\$ Okay - you have the resistor between pin 2 and V+? It's hard to tell in the picture. 4.7k is fine. What is curious here is that if the DHT11 is doing nothing, then the line should remain high if the resistor is correctly in place, but your check is for a high so it must be staying low, which seems to indicate that either the DHT11 is pulling it low, or the resistor is not connected. \$\endgroup\$ – Oli Glaser Sep 11 '12 at 6:37
  • \$\begingroup\$ Yes, it's between PIN 2 and V+. Sorry, bad picture. You know what, I have also noticed exactly what you are saying. i.e. DHT11 is pulling the line low. I think this means that either the 'start' signal from the PIC is not correct, or the sensor is broken.. do you agree? Too bad there is no way to check that the sensor actually works. \$\endgroup\$ – DimC Sep 11 '12 at 6:50
1
\$\begingroup\$

Ok, found it, it was a (embarrassing I must say) problem with the power supply.

Just for the record, here is the complete code for the PIC18F4620 using the C18 compiler.

Note that I use TIMER_3 to measure the pulse width, CCP2 in capture mode to trigger an interrupt on the falling edge and have multiplexed it with PIN RB3.

Crystal Oscillator at 8MHz, with PLL (so PIC running at 40MHz).

Also note about the programme: The PIC keeps HIGH for one second before sending the 'start' signal to the sensor. It will turn on the LED if the sensor values are received correctly.

#include <stdio.h>
#include <stdlib.h>
#include <delays.h>

// PIC18F4620 Configuration Bit Settings

#include <p18F4620.h>

// CONFIG1H
#pragma config OSC = HSPLL      // Oscillator Selection bits (HS oscillator, PLL enabled (Clock Frequency = 4 x FOSC1))

// CONFIG2L
#pragma config PWRT = OFF       // Power-up Timer Enable bit (PWRT disabled)
#pragma config BOREN = OFF      // Brown-out Reset Enable bits (Brown-out Reset disabled in hardware and software)

// CONFIG2H
#pragma config WDT = OFF        // Watchdog Timer Enable bit (WDT disabled (control is placed on the SWDTEN bit))

// CONFIG3H
#pragma config CCP2MX = PORTBE   // CCP2 MUX bit (CCP2 input/output is multiplexed with RC1)
#pragma config PBADEN = OFF      // PORTB A/D Enable bit (PORTB<4:0> pins are configured as analog input channels on Reset)


#define DHT11_TRIS  TRISBbits.RB3
#define DHT11_IO    PORTBbits.RB3  //READ USING THIS

#define LED0_TRIS   TRISDbits.RD0
#define LED0_IO     PORTDbits.RD0

int bit_counter=39;

int response[]={0,0,0,0,0,0,0,0,0,0,
                0,0,0,0,0,0,0,0,0,0,
                0,0,0,0,0,0,0,0,0,0,
                0,0,0,0,0,0,0,0,0,0};

int state;
unsigned char HH=0,HL=0,TH=0,TL=0,CS=0;

void high_isr(void);
void low_isr(void);
void DelayMs(int ms);
void Delay10us(int us);
void init_interrupts(void);


#pragma code InterruptVectorHigh = 0x08
void interrupt_at_high_vector(void)
{
_asm GOTO high_isr _endasm
}

#pragma code low_vector=0x18
void interrupt_at_low_vector(void)
{
_asm GOTO low_isr _endasm
}
#pragma code /* return to the default code section */
#pragma interrupt high_isr
void high_isr(void)
{
// return from high priority interrupt

}

#pragma interruptlow low_isr
void low_isr (void)
{

    if (PIR2bits.CCP2IF && PIR2bits.TMR3IF !=1 ){ // on CCP2 interrupt
    switch(state){

        case 1 :
            PIR2bits.CCP2IF = 0;
            PIR2bits.TMR3IF = 0;
            TMR3H = 0xF6;         // preset for timer3 MSB register
            TMR3L = 0x80;         // preset for timer3 LSB register
            state++;
            break;

        case 2:
            PIR2bits.CCP2IF = 0;
            PIR2bits.TMR3IF = 0;

           if (CCPR2H <= 248) {
               response[bit_counter]= 0;   //Value is Zero

           }

            else if (CCPR2H > 248) {
                response[bit_counter]= 1;

                if (bit_counter>=32) HH |= 1<<(bit_counter-32);
                if (bit_counter<32 && bit_counter>=24) HL |= 1<<(bit_counter-24);
                if (bit_counter<24 && bit_counter>=16) TH |= 1<<(bit_counter-16);
                if (bit_counter<16 && bit_counter>=8)  TL |= 1<<(bit_counter-8);
                if (bit_counter<8 && bit_counter>=0)  CS |= 1<<(bit_counter-0);

            }

            bit_counter--;

           TMR3H = 0xF6;         // preset for timer3 MSB register
           TMR3L = 0x80;         // preset for timer3 LSB register
            break;
    }
    }

    if (PIR2bits.TMR3IF == 1){ //an error occured

        PIR2bits.TMR3IF = 0;
        state = 3;
    }

}

#define GetSystemClock()    (32000000ul)
#define GetInstructionClock()   (GetSystemClock()/4)



void Delay10us(int us)  {
    Delay10TCYx(((GetInstructionClock()/1000000)*(us)));
}


void DelayMs(int ms){

    unsigned int _iTemp = (ms);     
    while(_iTemp--)         
    Delay1KTCYx((GetInstructionClock()+999999)/1000000);    

}


void main(void){

    int c=0;
    HH=0,HL=0,TH=0,TL=0,CS=0;
    CMCON = 7;

    DHT11_TRIS = 0; //Set DATA OUTPUT
    DHT11_IO = 0;

    LED0_TRIS = 0; //Set LED as OUTPUT
    LED0_IO=0;


    DHT11_IO = 1;
    DelayMs(1000);
    DHT11_IO = 0; //Pull Low
    DelayMs(18);  //For 20ms
    DHT11_IO = 1; //Set HIGH
    Delay10us(2);// For 20us

    DHT11_TRIS = 1; //Set as input
    Delay10us(2);// For 20us

    state = 1; //Receive Response
    init_interrupts(); //Initialize Interrupts


    do {} while(bit_counter>=0 && state !=3); //Wait for all bits to be received. If state == 3, it means an error occured

    if (state!=3 &&((HH+HL+TH+TL)&0xFF) == CS){
        LED0_IO = 1;
        DelayMs(1000); //If data received OK blink LED for 1s
        LED0_IO = 0;
    }
    else {
        //Error Occured
         }


}

void init_interrupts(void){
     INTCON = 0b11000000; //Set INTCON <7> for global interrupts, <6> for peripheral interrupts
     RCONbits.IPEN = 1;    // Interrupt Priority Enable
     RCONbits.SBOREN = 0;  // Brown Out Detect Disable

     /*****CCP2 Set to Capture mode******/
     IPR2bits.CCP2IP = 0;  // Set CCP2 Low Priority Interrupt
     CCP2CON = 0b00000100; //Set CCP2 to capture on falling edge
     PIE2bits.CCP2IE = 1;   // CCP2 Interrupt Enable bit
     PIR2bits.CCP2IF = 0;

     /*****TMR3 Set to count to 130us******/
    IPR2bits.TMR3IP = 0;  // Set TMR3 Low Priority Interrupt


    T3CONbits.T3CCP2 = 1;  // bits 6,3  Timer3 is the capture/compare clock source for CCP2;
    T3CONbits.T3CCP1 = 1;
    T3CONbits.T3CKPS1 = 0;  // bits 5-4  Prescaler Rate Select bits
    T3CONbits.T3CKPS0 = 0;  // bit 4
    T3CONbits.T3SYNC  = 1;  // bit 2 Timer3 External Clock Input Synchronization Control bit: 1=Do not synchronize external clock input
    T3CONbits.TMR3CS  = 0;  // bit 1 Timer3 Clock Source Select bit: 0=Internal clock (FOSC/4)

    TMR3H = 0xF6;         // preset for timer3 MSB register
    TMR3L = 0x80;         // preset for timer3 LSB register
    //With these settings, interrupt/overflow occurs every 130us

    PIR2bits.TMR3IF = 0;
    PIE2bits.TMR3IE = 1;    //enable timer interrupt on overflow
    T3CONbits.TMR3ON  = 1;  // bit enables timer


}
\$\endgroup\$
  • \$\begingroup\$ Aha, so was the power to the DHT11 not present? If I recall the picture the DHT was using the rails on the opposite side of the breadboard. Glad it's fixed anyway :-) \$\endgroup\$ – Oli Glaser Sep 11 '12 at 17:28
  • \$\begingroup\$ Yes, damn it!!! Exactly that, I still can't believe I did such a newbie mistake :) Many thanks for your responses Oli! \$\endgroup\$ – DimC Sep 12 '12 at 8:58
  • \$\begingroup\$ @DimC - You can accept your answer (the check mark under the up/downvote arrows), so that the question won't bump the list anymore as unanswered. \$\endgroup\$ – stevenvh Oct 11 '12 at 13:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.