我的自己做的硬件时直接用STM32f103的最小板外界AM2302实现的温湿度采集,证实不用上拉电阻也可以实现温湿度采集功能。
但不知道为什么效验位≠湿度高8位+湿度低8位+温度高8位+温度低8位。
所以我的程序上并没有加效验位判断。
AM2302引脚图
单总线通信时序
主机和传感器之间的通信可通过下面四个步骤
一、 AM2302 上电后(AM2302 上电后要等待 2S 以越过不稳定状态,在此期间读取设备不能发送任何指令),测试环境温湿度数据,并记录数据,此后传感器自动转入休眠状态。AM2302 的 SDA 数据线由上拉电阻拉高一直保持高电平,此时 AM2302 的 SDA 引脚处于输入状态,时刻检测外部信号。
二、微处理器的 I/O 设置为输出,同时输出低电平,且低电平保持时间不能小于 800us,典型值是拉低 1MS,然后微处理器的 I/O 设置为输入状态,释放总线,由于上拉电阻,微处理器的 I/O 即 AM2302的 SDA 数据线也随之变高,等主机释放总线后,AM2302 发送响应信号,即输出 80 微秒的低电平作为应答信号,紧接着输出 80 微秒的高电平通知外设准备接收数据。
三、AM2302 发送完响应后,随后由数据总线 SDA 连续串行输出 40 位数据,微处理器根据 I/O 电平的变化接收 40 位数据。
位数据“0”的格式为: 50 微秒的低电平加 26-28 微秒的高电平;
位数据“1”的格式为: 50 微秒的低电平加 70 微秒的高电平;
连续读取五个数据
AM2302.C
#include "AM2302.h"
uint8_t Hum_H,Hum_L,Temp_H,Temp_L,Check;
uint16_t Hum,Temp;
void AM2302_DATA_OUT(uint8_t a)//输出模式下,输出高低电平
{
if(a==)
{
GPIO_SetBits(GPIOA, GPIO_Pin_9);
}
else if(a==)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_9);
}
}
uint8_t AM2302_DATA_IN(void)//输入模式下,读取引脚是高电平还是低电平
{
uint8_t a;
a=GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_9);
return a;
}
void AM2302_OUT(void)//设置为推挽输出模式
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
void AM2302_IN(void)//设置为上拉输入模式
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
uint8_t Read_Byte(void)
{
uint8_t i,temp=;
for(i=;i<;i++)
{
while(AM2302_DATA_IN()==);
Delay_us();//50us
if(AM2302_DATA_IN()==)
{
while(AM2302_DATA_IN()==);//直到高电平结束跳出
temp|=(uint8_t)(<<(-i)); //把7-i位置1
}
else
{
temp&=(uint8_t)~(<<(-i)); //把7-i位置0
}
}
return temp;
}
void AM2302_RESET(void)//2s的启动延时
{
uint16_t i;
AM2302_OUT();
AM2302_DATA_OUT();
for(i=;i<;i++)
{
Delay_us();
}
}
void Read_AM2302(void)
{
AM2302_OUT();
AM2302_DATA_OUT();
Delay_us();//1ms-2ms之间都可以,设置为1.8ms
AM2302_DATA_OUT();
Delay_us(); //30us
AM2302_IN();
if(AM2302_DATA_IN()==)//判断AM2302是否返回响应
{
while(AM2302_DATA_IN()==);//响应80ms低电平
while(AM2302_DATA_IN()==);//响应80ms高电平
Hum_H=Read_Byte(); //读取湿度高8位
Hum_L=Read_Byte(); //读取湿度低8位
Temp_H=Read_Byte(); //读取温度高8位
Temp_L=Read_Byte();//读取温度低8位
Check=Read_Byte();//读取效验位
AM2302_OUT();
AM2302_DATA_OUT();
Delay_us(); //50us
}
}
void AM2302(void)
{
Hum=(Hum_H*+Hum_L);//此处的湿度为实际湿度的10倍
Temp=Temp_H*+Temp_L;//此处的湿度为实际温度的10倍
}
主函数
#include "stm32f10x.h"
#include "Systick.h"
#include "AM2302.h"
int main(void)
{
SysTick_Init();
AM2302_RESET();
while()
{
Read_AM2302();
AM2302();//湿度、温度均为实际的10倍
}
}
AM2302.h
#ifndef __AM2302_H
#define __AM2302_H
#include "stm32f10x.h"
#include "SysTick.h"
void AM2302_DATA_OUT(uint8_t a);
uint8_t AM2302_DATA_IN(void);
void AM2302_IN(void);
void AM2302_OUT(void);
uint8_t Read_Byte(void);
void Read_AM2302(void);
void AM2302(void);
void AM2302_RESET(void);
#endif /* __AM2302_H */
滴答时钟函数,用来做延时(SysTick.c)
#include "SysTick.h"
#include "bsp_key.h"
static uint64_t TimingDelay;
void SysTick_Init(void)
{
if (SysTick_Config(SystemCoreClock / ))
{
while ();
}
SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk;
}
void Delay_us(__IO u32 nTime)//10us为一个单位,例如Delay—us(1)=10us
{
TimingDelay = nTime;
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
while(TimingDelay != );
}
void TimingDelay_Decrement(void)
{
if (TimingDelay != )
{
TimingDelay--;
}
}
SysTick.h
#ifndef __SYSTICK_H
#define __SYSTICK_H
#include "stm32f10x.h"
void SysTick_Init(void);
void Delay_us(__IO u32 nTime);
#define Delay_ms(x) Delay_us(100*x)
写到这,AM2302驱动程序就写完了。
单总线器件只要认真看时序图照着一步一步写就行了。