Why "DDRC |= _BV(0);" for(?)

The following does work, mostly, to read a DHT11 humidity/ temperature sensor. That, I believe, returns 5 bytes: humidity: integer part of “%”, humidity: decimal part of “%”, temperature: integer part, temperature: decimal part, checksum

I’m wondering if it has to be so obscure?

The hardware connects via “Analog 0”. which I suspect (comments welome!) is being used as Digital 14.

I’ve said “works, mostly”. because the decimal part of both the tture and humidity is always zero. (I’ll solve that problem other ways… I only mention it in case you “stub your toe” on what the problem is there!)

#define DHT11_PIN 0      // ADC0

byte read_dht11_dat()
{
      byte i = 0;
      byte result=0;
      for(i=0; i< 8; i++){


            while(!(PINC & _BV(DHT11_PIN)));  // wait for 50us
            delayMicroseconds(30);

            if(PINC & _BV(DHT11_PIN))
                  result |=(1<<(7-i));
              while((PINC & _BV(DHT11_PIN)));  // wait '1' finish


      }
      return result;
}


void setup()
{
      DDRC |= _BV(DHT11_PIN);
      PORTC |= _BV(DHT11_PIN);

        Serial.begin(9600);

Serial.println("Ready");
      }

void loop()
{
      byte dht11_dat[5];
      byte dht11_in;
      byte i;
      // start condition
      // 1. pull-down i/o pin from 18ms  //(I believe that should be "FOR 18ms")
      PORTC &= ~_BV(DHT11_PIN);
      delay(18);
      PORTC |= _BV(DHT11_PIN);
      delayMicroseconds(40);

      DDRC &= ~_BV(DHT11_PIN);
      delayMicroseconds(40);

      dht11_in = PINC & _BV(DHT11_PIN);

      if(dht11_in){
            Serial.println("dht11 start condition 1 not met");
            return;
      }
      delayMicroseconds(80);

      dht11_in = PINC & _BV(DHT11_PIN);

      if(!dht11_in){
            Serial.println("dht11 start condition 2 not met");
            return;
      }
      delayMicroseconds(80);
      // now ready for data reception
      for (i=0; i<5; i++)
            dht11_dat[i] = read_dht11_dat();

      DDRC |= _BV(DHT11_PIN);
      PORTC |= _BV(DHT11_PIN);

        byte dht11_check_sum = dht11_dat[0]+dht11_dat[1]+dht11_dat[2]+dht11_dat[3];
      // check check_sum
      if(dht11_dat[4]!= dht11_check_sum)
      {
            Serial.println("DHT11 checksum error");
      }

      Serial.print("Current humdity = ");
      Serial.print(dht11_dat[0], DEC);
      Serial.print(".");
      Serial.print(dht11_dat[1], DEC);
      Serial.print("%  ");
      Serial.print("temperature = ");
      Serial.print(dht11_dat[2], DEC);
      Serial.print(".");
      Serial.print(dht11_dat[3], DEC);
      Serial.println("C  ");

      delay(2000);
}

I am asking “Does this have to be so obscure”. For instance, in setup we see…

DDRC |= _BV(DHT11_PIN);
PORTC |= _BV(DHT11_PIN);

DHT11_PIN has been “#defined” as zero, just to save you looking.

Is there any important difference between the above and…

pinMode(14,OUTPUT);
digitalWrite(14,HIGH);

(I’d put the “14” in a #defined constant. The alternative code, if is IS equivalent to the original, has the advantage(?) of allowing the user to put the sensor on a line other than Analog0-5, besides being easier to read! Of course, the PORTC part has to be worked on if the sensor is connected via, say, Digital 12.)

===
Turning to the “obscure” lines in loop()…

Is my guess on target? I’m guessing that the lines I don’t like “merely” twiddle bits (in the narrow, one-digit-of-a-binary-number sense) and read bits?

Maybe the lines in the code execute faster, or more consistently, than the “less obscure” alternatives? I realize that timing issues may be forcing the “obscure” code.

Help welcomed!

Tom

This code was written from a non-Arduino point of view, using #defines (macros) supplied with avr-gcc. Now, people used to assembly language programming might love it, and I don’t know about you, but it gives me a headache. See Footnote.

The good Arduino folks have supplied us with some #defines that make bit manipulation and port designation somewhat less obfuscatory.

Your observations about using setMode and digitalWrite appear to be correct.

I’ll do a few things to show how I might change the code in the read_dht11_dat() function to make it more user-friendly (from the Arduino point of view and from my personal point of view). I mention in my Footnote that it may or may not be as “efficient” from a time or code size point of view, but my objective in the first pass at such things is clarity to the human who might be maintaining/debugging/enhancing the code some day. (And that’s what you asked about.)

I have no way of testing, since I don’t have the hardware, but consider the following changes:

1. As you observed, Pin 0 of ATmega Port C can be used as a Arduino digital pin:

//#define DHT11_PIN 0      // ADC0
#define dht11_pin 14    // Digital pin 14 is Port C, bit 0

2. In addition to the non-Arduino terminology, the following is horribly commented, and stylistically misleading

        while(!(PINC & _BV(DHT11_PIN)));  // wait for 50us
        delayMicroseconds(30);

I might have put it like this:

        // Loop until it shows a 1
        while (dht11_pin == 0)
            ;
        delayMicroseconds(30);

The Arduino stuff is pretty clear. I always put the “empty loop” semicolon on its own separate line, since its significance can be lost if you put it at the end of the while(); (Significance lost to us mere humans, that is; the compiler never gets confused by the placement of the semicolon in this statement.)

3. The Arduino setBit macro makes things more apparent to me:

        //if(PINC & _BV(DHT11_PIN))
            //result |=(1<<(7-i));
        if (dht11_pin != 0) {
            setbit(result, 7-i);
        }

4. And finally (for this function) I would Arduinoize the last loop by changing

        while((PINC & _BV(DHT11_PIN)));  // wait '1' finish

To

        // Wait until the dht11 pin goes high
        while (dht11_pin != 0)
            ;

Now, many (too many, in my opinion) C and C++ programmers take delight in reducing keystrokes, so the statement might have been

        while (dht11_pin);

That’s completely equivalent to my previous code. Which do you prefer? (I like the way that I showed in the previous statements, since it might even be clear to non-expert programmers.) Chacun à son goût

Now, maybe you can see some of the great stuff the Arduino guys have contributed to our mental well-being.

I’m new to Arduino, and there may be others who are more capable of explaining and implementing things the Arduino way, but I’m getting pretty comfortable with it. And I am mightily impressed with the quality of their efforts.

Go through the rest of the code and see what you can come up with. If the original program works, then change one thing at a time and test (as thoroughly as you can) before going on to the next change. Save the original (so if you screw up somewhere along the way you can go back and start over). At each step, don’t delete the lines that you are currently changing; just comment out the old line(s) and put your new stuff there. That makes easier to revert in case your most recent “improvement” doesn’t work.

Regards,

Dave

Footnote:
Sometimes I think life would be simpler if there were only one way of doing things like this. You would ask; someone would tell you or show you the way to do it, and we would all move on to the really creative part of the project: Using the results from this function to do something useful (or, maybe, even to do something that’s fun).

The fact that there are (usually) lots of ways to do things like this in C and C++, and what is “simple and straightforward and elegant” from one person’s point of view is unnecessarily complicated and unclear and downright butt-ugly from another’s.

Just to make a point: When I want to build a byte a bit at a time as this function does, I usually shift them in rather than use a logical “or” to place each bit. The shift operation in the loop might look like

        //if(PINC & _BV(DHT11_PIN))
            //result |=(1<<(7-i));
        //if (dht11_pin != 0) {
            //setbit(result, 7-i);
        //}
        result = (result << 1) | dht11_pin;

See how it goes? We started with result equal to zero going into the loop. Each time through the loop, we shift the previous value left and “or” in the current reading (a zero or a one) into the least significant bit. That’s the way I look at and that’s the way I do it.

The way that I showed first is a transliteration of the original non-Arduino style. This last way is a functional equivalent that I like better.

For people not used to such things none of the three methods may be exactly “obvious” from the start (especially since there are no comments), so I say: Pick a way that works for you. If you later think of something (or see something in someone else’s code) that you like better, then do it that way.

My recommendation at this stage of the game is not to worry about which is more “efficient” from a code size or speed point of view. Choose a style that makes things more “obvious” to you and let’s get on with it! If it turns out that you have to squeeze the last nanosecond out of your code and you have to worry about the last byte, then you can, maybe, look at optimization.

My recommendation at this stage of the game is not to worry about which is more "efficient" from a code size or speed point of view.

Many thanks for your long and thought provoking post.

I agree with the point you make... but should explain:

The code I quoted is what is supplied by someone selling a DHT11 module. Before I clomped through their code in my size ten boots, I wanted to show a little humility. It is possible, I think, that the code HAD to be done the "clever", "obscure" way... It does, after all, access external hardware that MAY NEED to eschew the "obvious" coding... to achieve precise timing of something critical, for instance? On the other hand, as I publish "how to do it" pages for novices, I would love to re-cast the code, using just "basic" words, etc, WHERE POSSIBLE.

So! The question is... do readers think there are things in the "clever" code which would be lost if it were "converted"?

=== (PS, an aside: None of the above is meant to say that I think the original code is "bad". It DOES work, after all!! :-) I'm just trying to make it more beginner-friendly without "breaking" it!)

…long…post…

Sometimes I just can’t help myself. It’s a curse.

In the first place, an important part of my “code” was incorrect: The Arduino way to get the value of a signal on an input pin is, of course, by using the digitalRead() function. (That’s why I hate to post code that I can’t actually test: I just hate to get it wrong.)

Sorry.

…achieve precise timing of something critical…

Now, the digitalRead() function does have some overhead that you can eliminate by using the original code’s bit-value macro that you get when you include <avr/io.h> See Footnote. That’s one reason that I suggested you make changes one at a time and thoroughly test the results before going to the next. If you “have to” leave code that you consider unclear (or ugly), just put in a comment telling future readers why you did it that way (and, maybe, warning against changes to more “obvious” ways).

There may or may not be timing subtleties throughout program that “make you” use the raw bit code of the original post. I would isolate such things and make sure that comments show why things are done that way.

Regards,

Dave

**Footnote:**The overhead in the digitalRead() is there to check to see whether the pin is valid and to turn off the PWM timer if it turns out that the program has (somewhere) tried to use the pin as an analog output pin. For a self-contained program, eliminating the tests would seem to be save, but if you are supplying the function as part of a library, then eliminating the tests might lead to unpleasant surprises to people trying to use the library. On the other hand, if it doesn’t work (due to timing problems brought on by the overhead), it is also worthless, right?

As a followup, I note that the OneWire (Version 2.0) library available from http://www.pjrc.com/teensy/td_libs_OneWire.html uses low-level #defines for the bit manipulations. By making a library class out of it the user doesn't have to know about such intricacies. The user function declares an object of the OneWire class and calls object methods to initialize and read from and write to the hardware.

If you are writing a tutorial and don't want to get into classes and stuff, you can take look at OneWire.cpp and get some clues about how others define and use low-level bit access and avoid the overhead of digitalWrite() and digitalRead(), and...

Or, maybe for your tutorial you can delay introducing the user to code that requires extra considerations until you get to the "Advanced" section.

Regards,

Dave

A further followup. I don’t have any way to test that sensor, and I probably won’t. See Footnote.

I did investigate the effects of using digitalWrite() before and after calling delayMicroseconds() to create a pulse instead of the direct write to a port. I measured a lot of different pulse widths on a 'scope and got fairly consistent results.

Bottom line (repeated in the comments of the sketch below): digitalWrite() adds about four microseconds. You can compensate by subtracting four microseconds from the argument that you give delayMicroseconds()

Real bottom line: If you write your software very carefully (regardless of which method you use to create output pulses), you may be able to control timing to within a few microseconds. Hardware protocols such as 1-Wire are fairly tolerant, and there should be no problems getting things to work. See Footnote (again).

// Test the overhead of using digitalWrite instead of writing
// a particular bit pattern to an ATmega port.
//
// Tested on a Genuine Duemilanove with an ATmega328p.
//
// I wanted to create a pulse that is 30 usec in duration.
// For test purposes I use Arduino digital pin 13 and look
// at the output on a digital scope.  The following gives
// output pulses that appear to be 29.8 usec on my scope.
// If you tell me my scope time base is off by 0.2 usec
// in the range I am observing, I won't argue (much---maybe
// it's my tired old eyes).
//
// The important thing that I am testing is the difference
// between digitalWrite and writing directly to the port.
//
// Note that delayMicroseconds does not turn off interrupts, so
// the normal timer0 interrupt (used to keep track of time for
// millis() and micros(), among other things) can result in
// (slightly) longer delays from time to time.
//
// For me, regardless of this little test, the bottom line
// is:
//
// Attention to detail in software can result in pretty accurate
// timing of hardware signals.
//
// However, the real bottom line is:
//
// If your hardware can't tolerate a timing jitter of a small
// percentage or it can't tolerate delay inaccuracies on the
// order of a few microseconds, then you shouldn't count on the
// timers in the CPU to do the job without some external precision
// timing circuitry.
//
// Get it?  A little digital pun:
//   shouldn't COUNT on?
//   No?  Oh, well...
// 
//
// davekw7x
//

const int ledPin = 13;

void setup(){}
//
// Each time through the loop I create a pulse with
// digitalWrite and then I create a pulse by "brute
// force" output to bit D5 of PORTB (that's AVRspeak
// for Arduino digital pin 13).  I have independently
// determined that the overhead for calling digitalWrite
// plus the time to execute the code and return is
// slightly over four microseconds.  Therefore, I adjust
// the delay value down a little to compensate for that.

int delay_value = 30;
int overhead_value = 4;

void loop()
{
    // Create three pairs of pulses every second or so
    for (int i = 0; i < 3; i++) {
        
        digitalWrite(ledPin, HIGH);
        delayMicroseconds(delay_value-overhead_value);
        digitalWrite(ledPin, LOW);

        PORTB = 0x20;
        delayMicroseconds(delay_value);
        PORTB = 0;
    }
    
    delay(10000);
}

Regards,

Dave

Footnote:
I will be doing some 1-Wire stuff soon, and I’ll look at timing of code from the 1-Wire library. At first, I had the impression that your device was like 1-Wire, but, looking at the code, I now think that doesn’t seem to be the case.

I can’t exactly glean the required sequence of signals, let alone min/max timing, from the data sheets that I found for your device because I only read English. (How provincial!)

I didn’t try to actually follow the code as far as the precise timing that is created. Why did you say in your original post that the code, “does work, mostly” ??? Are there issues with the original code? If there are, then I wouldn’t try any modifications unless and until I understood the problems in the original.

What "_BV" is all about is explained at...

http://www.urbanhonking.com/ideasfordozens/2009/05/an_tour_of_the_arduino_interna.html

Basically: You start with 00000001, and shift the 1 left a number of times.

_BV(2) returns 00000100... or is it 00000010? Something like that.

The |= operator is explained at...

http://www.arduino.cc/playground/Code/BitMath

if you start with x=8; (0001000 in binary), after

x |= 3;

you;ll have 0000000000001011 - because 3 is 11 in binary.

There's now LOTS more on reading the DHT11 or DHT22 at...

http://sheepdogguides.com/arduino/ar3ne1humDHT11.htm

If you have one of these sensors, from whatever source, they are easily interfaced to Arduino or clone.... Vcc, ground, and a single data line. For now, it has to connect to analog 0-5 (aka D14-19), but I suspect that will change, that you can use any line with the right software.