手头有一块AM2302温湿度模块,之前没有用过,最近闲暇,就尝试写一下驱动,使用的STM32F103主控MCU,虽然协议很简单,但也零零散散花了3天时间,主要是一方面发现原先用的延时器不准,导致一直调试不成功,所以遇到调试不上,先尽量检查一下延时函数的精确度,不宜偏差过大。
现贴参考代码如下:
#ifndef _AM2302_H__
#define _AM2302_H__
#include "bsp.h"
#define AM_SDA_IN() {AM2302_PORT->CRH&=0xFFFFFF0F;AM2302_PORT->CRH|=((unsigned int)8<<4);}//PB9-SDA
#define AM_SDA_OUT() {AM2302_PORT->CRH&=0xFFFFFF0F;AM2302_PORT->CRH|=(unsigned int)(3<<4);}
#define AM_SDA_SET(x) (0==x)?(GPIO_ResetBits(AM2302_PORT,AM2302_PIN)):(GPIO_SetBits(AM2302_PORT,AM2302_PIN))
#define AM_SDA_READ() GPIO_ReadInputDataBit(AM2302_PORT,AM2302_PIN)
typedef struct
{
unsigned short value:15;
unsigned short polar:1;
}data_st;
typedef struct
{
data_st humt;
data_st temp;
}sensor_st;
extern sensor_st sensor;
void sens_start(void);
void get_byte(unsigned char *gbyte);
unsigned char get_data(sensor_st *sensor_me);
void read_th(void);
#endif
#include "am2302.h"
#include "delay.h"
#include "string.h"
#include "stdio.h"
#define LEV_HIGH (1) /*高电平*/
#define LEV_LOW (0) /*低电平*/
#define RET_OK (0) /*返回正常*/
#define RET_FAIL (1) /*返回失败*/
sensor_st sensor={0,0};
/*
1.微处理器把数据总线(SDA)拉低一段时间(至少 800µs)[1],通知传感器准备数据。
2.传感器把数据总线(SDA)拉低 80µs,再接高 80µs 以响应主机的起始信号
3.收到主机起始信号后,传感器一次性从数据总线(SDA)串出 40 位数据,高位先出
位数据“0”的格式为: 50 微秒的低电平加 26-28 微秒的高电平;
位数据“1”的格式为: 50 微秒的低电平加 70 微秒的高电平;
4.数据完毕后,传感器将继续输出50微妙作为结束信号
*/
//------------------------------------------
//发起准备
unsigned char sens_ready(void)
{
AM_SDA_OUT();//输出模式
AM_SDA_SET(0);
xDelay_ms(10);
printf(">> start ok\r\n");
AM_SDA_SET(1);
xDelay_us(40);
AM_SDA_IN();//输入模式
xDelay_us(40);//加延时,以保证信号上升
if(LEV_LOW==AM_SDA_READ())//响应信号
{
while(LEV_LOW==AM_SDA_READ());//等待80us低电平过去
while(LEV_HIGH==AM_SDA_READ());//等待80us高电平过去
return RET_OK;
}
else
{
return RET_FAIL;
}
}
//------------------------------------------
//获取单字节数据
void get_byte(unsigned char *gbyte)
{
unsigned char htime=0;
*gbyte=0;
for(unsigned char data_bit=0;data_bit<8;data_bit++)
{
htime=0;
xDelay_us(30);
if(LEV_LOW==AM_SDA_READ())//
{
while(LEV_LOW==AM_SDA_READ());//等待50us低电平结束
}
while(LEV_HIGH==AM_SDA_READ())//等待高电平结束,并判断当前信号特性值
{
++htime;
xDelay_us(10);
}
if(3<htime) //如果能计数到30us以上,则说明是电平 “1”
{
*gbyte|=(1<<(7-data_bit));
}
else
{
*gbyte&=~(1<<(7-data_bit));
}
}
}
//------------------------------------------
//获取完整数据
unsigned char get_data(sensor_st *sensor_me)
{
unsigned char gdata[5]={0};
if(RET_FAIL==sens_ready())
{
printf(">> commit fail\r\n");
return RET_FAIL;//发起启动
}
for(unsigned char i=0;i<5;i++)
{
get_byte(&gdata[i]);
}
xDelay_us(15);
while(LEV_LOW==AM_SDA_READ());//等待50us低电平结束(结束信号)
AM_SDA_OUT();//输出模式
AM_SDA_SET(0);
xDelay_ms(5);
RLED_SW(0);
//校验位=湿度高位+湿度低位+温度高位+温度低位
if(gdata[4]==(unsigned char)(gdata[0]+gdata[1]+gdata[2]+gdata[3]))//数据校验
{
if(0x01==(gdata[0]>>8))
{
gdata[0]&=~0x80;//清除极性位
sensor_me->temp.polar=1;
}
sensor_me->humt.value=((unsigned short)gdata[0]<<8)|gdata[1];
sensor_me->temp.value=((unsigned short)gdata[2]<<8)|gdata[3];
printf(">> crc ok\r\n");
return RET_OK;
}
for(unsigned char i=0;i<5;i++)
printf(">> gdata[%d]:0x%02x\r\n",i,gdata[i]);
printf(">> crc fail\r\n");
return RET_FAIL;
}
//------------------------------------------
//输出数据
void read_am2302(void)
{
if(RET_OK==get_data(&sensor))
{
if(0==sensor.temp.polar)//检查温度的极性(最高比特位是否为1)
{
printf(">> Temp:%d.%d;Hum:%d.%d\r\n",sensor.temp.value/10,sensor.temp.value%10,sensor.humt.value/10,sensor.humt.value%10);
}
else //条件限制,暂未验证
{
printf(">> Temp:-%d.%d;Hum:%d.%d\r\n",sensor.temp.value/10,sensor.temp.value%10,sensor.humt.value/10,sensor.humt.value%10);
}
}
else
{
printf(">> Data geted fail\r\n");
}
}
另外周期读取read_am2302()手册建议是2s以上,过快容易通信不成功。
调试输出截图如上。