Newer
Older
am2302 / am2302.c
/*
 * am2302.h
 *
 * Created on: 13.03.2013
 *     Author: Pascal Gollor
 *        web: http://www.pgollor.de
 *
 * Dieses Werk ist unter einer Creative Commons Lizenz vom Typ
 * Namensnennung - Nicht-kommerziell - Weitergabe unter gleichen Bedingungen 3.0 Deutschland zugänglich.
 * Um eine Kopie dieser Lizenz einzusehen, konsultieren Sie
 * http://creativecommons.org/licenses/by-nc-sa/3.0/de/ oder wenden Sie sich
 * brieflich an Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA.
 *
 *
 *
 *	AM2302/DHT22 (Temperatur und Luftfeuchtigkeitssensor)
 *  _________
 * |  -+-+-  |
 * | +-+-+-+ |
 * | +-+-+-+ |
 * |  -+-+-  |
 * | +-+-+-+ |
 * |_________|
 *   | | | |
 *   1 2 3 4
 *
 * 1. VCC (3 to 5V power)
 * 2. Data out (Pullup 4,7k)
 * 3. Not connected
 * 4. Ground
 *
 *
 * Error codes
 * 1: sensor sda bus not free
 * 2: timeout: bus master release time
 * 3: timeout: response to low time
 * 4: timeout: response to high time
 * 5: timeout: signal low timeout
 * 6: timeout: signal high timeout
 *
 * Read the datasheet for more information about the times.
 */


#include "am2302.h"

#include <avr/io.h>
#include <util/delay.h>

#include "main.h"
#include "uart.h"


#define SENSOR_sda_out		DDR_SENSOR |= (1 << SENSOR)
#define SENSOR_sda_in			DDR_SENSOR &= ~(1 << SENSOR) // release sda => hi in consequence of pullup
#define SENSOR_sda_low    PORT_SENSOR &= ~(1 << SENSOR)
#define SENSOR_is_hi			PIN_SENSOR & (1 << SENSOR)
#define SENSOR_is_low		!(PIN_SENSOR & (1 << SENSOR))


/**
 * @brief init avr for am2302 sda
 */
inline void am2302_init(void);
{
	DDR_SENSOR &= ~(1 << SENSOR); // define as input
	PORT_SENSOR &= ~(1 << SENSOR);  // disable pullup
}

uint8_t am2302(uint16_t *humidity, uint16_t *temp)
{
	if (SENSOR_is_low)
	{
		// bus not free
		return 1;
	}

	SENSOR_sda_out;
	SENSOR_sda_low;	// MCU start signal
	_delay_ms(20);	// start signal (pull sda down for min 0.8ms and maximum 20ms)
	SENSOR_sda_in;

	// Bus master has released time min: 20us, typ: 30us, max: 200us
	uint8_t timeout = 200;
	while(SENSOR_is_hi)
	{
		_delay_us(1);
		if (!timeout--)
		{
			return 2;
		}
	}

	// AM2302 response signal min: 75us typ:80us max:85us
	timeout = 85;
	while(SENSOR_is_low)
	{
		_delay_us(1);
		if (!timeout--)
		{
			return 3;
		}
	}  // response to low time

	timeout = 85;
	while(SENSOR_is_hi)
	{
		_delay_us(1);
		if (!timeout--)
		{
			return 4;
		}
	}  // response to high time


	/*
	 *            time in us: min typ max
	 *    signal 0 high time: 22  26  30     (bit=0)
	 *    signal 1 high time: 68  70  75     (bit=1)
	 *  signal 0,1 down time: 48  50  55
	 */

	uint8_t sensor_data[5]={0};

	for(uint8_t i = 0; i < 5; i++)
	{
		uint8_t sensor_byte = 0;
	
		// get 8 bits from sensor
		for(uint8_t j = 1; j <= 8; j++)
		{
			// wait for sensor response
			timeout = 55;
			while(SENSOR_is_low)
			{
				_delay_us(1);

				// if timeout == 0 => sensor do not response
				if (!timeout--)
				{
					return 5;
				}
			}

			// wait 30 us to check if bit is logical "1" or "0"
			_delay_us(30);
			sensor_byte <<= 1; // add new lower bit

			// If sda ist high after 30 us then bit is logical "1" else it was a logical "0"
			// For a logical "1" sda have to be low after 75 us.
			if (SENSOR_is_hi)
			{
				sensor_byte |= 1; // add logical "1"
				timeout = 45;  // 30us - 75us = 45us

				while(SENSOR_is_hi)
				{
					_delay_us(1);
				
					if (!timeout--)
					{
						return 6;
					}
				}
			}
		}

		sensor_data[i] = sensor_byte;
	}

	// checksum
	if ( ((sensor_data[0] + sensor_data[1] + sensor_data[2] + sensor_data[3]) & 0xff ) != sensor_data[4])
	{
		// debug output
		//printf("%b %b %b %b %b %b" CR, sensor_data[0], sensor_data[1], sensor_data[2], sensor_data[3], sensor_data[4], ((sensor_data[0]+sensor_data[1]+sensor_data[2]+sensor_data[3]) & 0xff ));
		return 7;
	}

	*humidity = (sensor_data[0] << 8) + sensor_data[1];
	*temp = (sensor_data[2] << 8) + sensor_data[3];

	return 0;
}