The code is working just fine, but my problem is that I don't understand it. It is a sample code provided by seeedstudio and I have only managed to get it to work on a normal arduino, not any mega. The problem there might be a different pin layout, but I am not really sure, I then tried to look at how I could change it, and then ran into things like DDRC |= _BV(DHT11_PIN); and PORTC |= _BV(DHT11_PIN); and found that it was some port manipulation going on.
My wish is now to be able to remove the port manipulation, and get stuff like analogRead and such in instead, things that I and other mortals might be better to understand.
Assuming all that 11 stuff refers to port 11 (or pin 11). Anyway, whatever port you plugged it into.
DDRC == The Port C Data Direction Register
PORTC == The Port C Data Register
The "real" digitalWrite is slightly safer (allowing for interrupts during the operation) and the "real" digitalRead returns HIGH or LOW (1 or 0) rather than non-zero or zero which your original code did. For your purposes there should be no problem substituting.
You might be lucky, it looks like bit 0 of port C is in fact called pin 0, but that was a bit of a coincidence.
There code was referring to "bit 0 of port C" (where each port has 8 bits) not pin 0 on your board.
Looking at the schematic it looks like pin 23 of the Arduino which is Port C, bit 0, is in fact connected (on the Uno at least) to the pin marked A0. But that is really luck.
Assuming all that 11 stuff refers to port 11 (or pin 11).
This is not a safe assumption.
The Arduino pin assignments do not correlate to anything on the ATMega. Part of why the Arduino environment supports so many ATMega varients is because it maps the appropriate pin on the microcontroller to the pin on the Arduino board.
For hints to how Port pins correlate, read the PortManipulation document referenced in the OP's first post.
Obviously this is not the same as the original code. The original code includes a line that says "#define DHT11_PIN 0 // ADC0". None of the Arduino variants have a A/D pin on Pin 11.
I'm no expert, but here's my understanding of what's going on. Anyone that knows more than me, feel free to correct me so i can learn more.
/*
Please refer to the pin to register mapping for whatever version of arduino you are using.
This works for ATMega168 or ATMega328
http://arduino.cc/playground/uploads/Learning/Atmega168PinMap2.png
This is for ATMega1280
http://forum.jeelabs.net/files/PIN%20MAPPING%20ARDUINO%20MEGA.jpg
For my comments below i will assume a ATMega168/328
You can also learn more about bitwise math at
http://194.81.104.27/~brian/microprocessor/BVMacro.pdf
*/
#define DHT11_PIN 0 // ADC0
/*
Just to make it easier to follow, they have given a "friendly name" to the number 0 to be DHT11_PIN. This means when you change 0 to another integer, it will change everywhere DHT11_PIN is mentioned.
*/
void setup()
{
DDRC |= _BV(DHT11_PIN);
/* DDRC is the direction register (input or output)
Same as saying
DDRC = DDRC | _BV(DHT11_PIN);
(or in words, DDRC equals bitwise OR of DDRC and the 8 bit binary representation of DHT11_PIN (which is 0, so the _BV(DHT11_PIN) is 00000000))
The bits that DDRC is 1 (as opposed to 0) indicate an output
The binary value of DDRC is represented by 8 bits (00000000 through 11111111). I find that the binary version is easiest to read, as it's Register Pin 7 through 0 (left to right).
Doing a bitwise OR will only make a pin go to 1 if what you are "ORing" it with has a 1, else anything else will remain the same as it originally was.
for example:
10111100
OR 00001110
Equals 10111110
This is VERY important to understand the concept of. If you don't understand bitwise math, brush up on it before reading any more on this.
In this code, DHT11_PIN is 0, so:
Whatever DDRC is, leave everything the same except for the last digit of the 8 bit binary representation, change it to 1
XXXXXXXX
OR 00000001
Equals XXXXXXX1
so that means leave everything the same but PIN0 of that register
According to http://arduino.cc/playground/uploads/Learning/Atmega168PinMap2.png, PIN0 of Register C is Analog 0
Now that i have explained the logic on what it's doing, it's setting Chip pin 23 (Analog 0) to an output
*/
PORTC |= _BV(DHT11_PIN);
/* same logic with the OR as above
PORTC sets the pin(s) as high or low depending on the value (1 is high, 0 is low)
so... change PORTC and leave all pins the same except for DHT11_PIN (which as we explained above is Analog 0) and set that as High (5V output)
*/
Serial.begin(9600); //Enable the serial port at 9600 baud
Serial.println("Ready"); // Print "Ready" to the serial output
}
void loop()
{
DHT11(); //Call the function DHT11()
}
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 DHT11() //Function DHT11()
{
byte dht11_dat[5];
byte dht11_in;
byte i;
// start condition
// 1. pull-down i/o pin from 18ms
PORTC &= ~_BV(DHT11_PIN);
/* this time we are turning the pin down, rather than up.
Here's what's going on:
PORTC equals PORTC Bitwise AND the inverse of the binary value of DHT11_PIN
Now to give it a graphical representation:
non-related example:
11001010
AND 11110000
Equals 11000000
If you don't understand that, brush up on your bitwise math
Now, this is what it's doing (assuming DHT11_PIN = 0
_BV(DHT11_PIN) is the same as 00000001
the inverse of that is 11111110
now,
XXXXXXXX
AND 11111110
Equals XXXXXXX0
thus only bringing PortC Pin 0 low
*/
delay(18);
PORTC |= _BV(DHT11_PIN);
/* This (as explained above) brings it back high*/
delayMicroseconds(40);
DDRC &= ~_BV(DHT11_PIN);
/* This (as explained above) brings it back low*/
delayMicroseconds(40);
dht11_in = PINC & _BV(DHT11_PIN); //Set the value of the PINC registers AND _BV(DHT11_PIN) to dht11_in
if(dht11_in){ //if dht11_in is non-zero
Serial.println("dht11 start condition 1 not met");
return;
}
delayMicroseconds(80);
dht11_in = PINC & _BV(DHT11_PIN); //Set the value of the PINC registers AND _BV(DHT11_PIN) to dht11_in
if(!dht11_in){ //if dht11_in is zero
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); // for some reason this sets it as output again
PORTC |= _BV(DHT11_PIN); // this sets it high again
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(200);
}
Note: it is possible that the sensor may not work this way, due to the fact that digitalWrite is 100x slower than port manipulation. If this is the case, you will have to use port manipulation.
Explanation coming later.
Sorry that was a bit of an exaduration, the digitalread and write functions are about 20-50x slower based on some timing i did myself in the past, plus some reading from google, including this article: » Pin I/O performance » JeeLabs
That is what i was referring to as the problem, not so much the analog reads. I figured that the digital reads and writes were time sensitive because of the very small microsecond delays in there.
The screenshot shows short pulses (125ns) for the bit manipulation, and 3792 ns (3.7917us) for the digitalWrite. 3792/125 = 30.33, which demonstrates that the port manipulation is about 30x as fast. A check with a higher-bandwidth oscilloscope confirms the short pulse width is 125 ns, so there is no major measurement error there.
The reason digitalWrite is somewhat slower is that it does more stuff ...
It converts the pin number to a port/bit offset by a table lookup
It checks the requested pin is valid.
It turns off PWM on that pin if required.
It saves the interrupt mask state.
It turns off interrupts.
It does the bit manipulation with the discovered port/bit offset
It restores the interrupt mask state.
This is more convenient, but it takes time. Evidently 3.66 microseconds. Still a delay(1) delays you by 1000 microseconds, so in the code you showed that probably isn't too bad.
Fortunately one can choose which method to use for your Arduino application. Direct port if speed is important or the arduino abstraction method for ease of use and readability of code, and both methods can exist in the same sketch, if you understand all the possible conflicts involved like PWM, etc.
Keep in mind that direct port access is not portable across the mega8/168/328 Vs mega1280/2560 boards as port.pin mapping is different between these two AVR 'series'.
My concern wasn't in the reading, but in the initialisation, because the original author obviously thought timing was important, due to the very specific delayMicroseconds(). I guess we will just have to wait and see though.
That's an option, but it will be a pain If necessary. The serial interface looks like it could be a real pain with bad timings www.robotshop.com/PDF/dht11.pdf