获取AHT20传感器数据
使用野火指南者开发板
一、iic协议
1、起始帧:当SCL处于高电平时,SDA从高到低。
//i2c 起始信号
void i2c_start(void)
{
I2C_SDA_OUT();
I2C_SDA_1();
I2C_SCL_1();
SysTick_Delay_Us(4);
I2C_SDA_0();
SysTick_Delay_Us(4);
I2C_SCL_0();
}
2、停止帧:当SCL处于高电平时,SDA从低到高。
//iic停止信号
void i2c_stop(void)
{
I2C_SDA_OUT();
I2C_SCL_0();
I2C_SDA_0();
SysTick_Delay_Us(2);
I2C_SCL_1();
I2C_SDA_1();
SysTick_Delay_Us(2);
}
3、主机的响应与变化:(当SCL处于高电平状态时,则维持SDA数据的稳定;而当SCL处于低电平状态时,则允许SDA发生变化)
//主机发送一个Ack 1 - ack
void i2c_sendAck(uint8_t ack)
{
I2C_SCL_0();
I2C_SDA_OUT();
if(ack)
{
I2C_SDA_0(); //ACK
}
else
{
I2C_SDA_1(); //NACK
}
SysTick_Delay_Us(2);
I2C_SCL_1();
SysTick_Delay_Us(2);
I2C_SCL_0();
}
4、等待从机ACK响应
//等待回复ack
uint8_t i2c_waitAck(void)
{
uint8_t time;
I2C_SDA_IN(); //注意要将SDA设置为输入
I2C_SDA_1();SysTick_Delay_Us(2);
I2C_SCL_1();SysTick_Delay_Us(2);
while(I2C_READSDA())
{
time++;
if(time>=250)
{
i2c_stop();
return 1;
}
}
I2C_SCL_0();
return 0;
}
5、发送一字节数据
//i2c 发送一个字节
void i2c_sendByte(uint8_t Byte)
{
int8_t t;
I2C_SDA_OUT();
I2C_SCL_0();
for(t = 7; t >= 0; t--)
{
if(Byte & (0x01 << t))
{
I2C_SDA_1();
}
else
{
I2C_SDA_0();
}
SysTick_Delay_Us(4);
I2C_SCL_1();
SysTick_Delay_Us(4);
I2C_SCL_0();
SysTick_Delay_Us(4);
}
}
6、接收一个字节
//i2c 读取一个字节
uint8_t i2c_readByte(void)
{
uint8_t i,Byte;
I2C_SDA_IN(); //注意要将SDA设置为输入
for(i = 0; i < 8; i++)
{
I2C_SCL_0();
SysTick_Delay_Us(2);
I2C_SCL_1();
Byte<<=1;
if(I2C_READSDA())
{
Byte++;
}
SysTick_Delay_Us(2);
}
return Byte;
}
7、发送一连串字节数据
//发送一连串字节
uint8_t i2c_sendBytes(uint8_t address,uint8_t addrByte,uint8_t *txBuff,uint8_t size)
{
uint8_t i;
i2c_start();
i2c_sendByte(address|i2c_wr);
if(i2c_waitAck())
{
i2c_stop();
return 1;
}
i2c_sendByte(addrByte);
if(i2c_waitAck())
{
i2c_stop();
return 2;
}
for(i = 0; i < size; i++)
{
i2c_sendByte(txBuff[i]);
if(i2c_waitAck())
{
i2c_stop();
return 3;
}
}
i2c_stop();
return 0;
}
依次读取一系列字节块:address(内存地址),addByte(目标字节地址),*rxBuff(数据缓存区域),size(数据长度)
//读取一连串字节
uint8_t i2c_readBytes(uint8_t address, uint8_t addByte,uint8_t *rxBuff,uint8_t size)
{
uint8_t i;
i2c_start();
i2c_sendByte(address | i2c_wr);
if(i2c_waitAck())
{
i2c_stop();
return 4;
}
i2c_sendByte(addByte);
if(i2c_waitAck())
{
i2c_stop();
return 5;
}
i2c_start();
i2c_sendByte(address | i2c_rd);
if(i2c_waitAck())
{
i2c_stop();
return 6;
}
for(i = 0; i < size; i++)
{
rxBuff[i] = i2c_readByte();
if(i < size -1)
{
i2c_sendAck(1);
}
else
{
i2c_sendAck(0); //最后一个字节发送一个Nack
}
}
i2c_stop();
return 0;
}
二、AHT20传感器
1、手册描写读取过程
1).上电后要等待不少于100ms,读取温湿度值之前,通过发送0x71获取一个字节的
状态寄存器的状态字,在满足以下条件时:当状态字与0x18进行逻辑与运算后结果不等于0x18时,则需设置0x1B、0x1C、0x1E这些寄存器以完成详细的初始化流程,请参见我司官网提供的示例程序;反之,则执行下一项操作。
2).等待10ms发送0xAC命令(触发测量),此命令参数有两个字节,第一个字节为0x33,
第二个字节为0x00。
3).等待80ms待测量完成,如果读取状态字Bit[7]为0,表示测量完成,然后可以连续
读取六个字节;否则继续等待。
当接收六个完整字节后,随后下一个字节用于 CRC 校验,用户可以根据具体情况读取该数据
在数据包中,在接收到第六个字节后会发送ACK应答以确认成功;如果未成功则会不成功时发送NACK并终止处理流程。
CRC初始值为0XFF,CRC8校验多项式为:
CRC[7:0]=1+x4+x5+x8。
设置... ... ...寄存器官方示例程序仅在读取状态字并进行按位与运算后结果非零时执行初始化操作。
void AHT20_Start_Init(void)
{
JH_Reset_REG(0x1b);
JH_Reset_REG(0x1c);
JH_Reset_REG(0x1e);
}
void JH_Reset_REG(uint8_t addr)
{
uint8_t Byte_first,Byte_second,Byte_third,Byte_fourth;
I2C_Start();
AHT20_WR_Byte(0x70);
Receive_ACK();
AHT20_WR_Byte(addr);
Receive_ACK();
AHT20_WR_Byte(0x00);
Receive_ACK();
AHT20_WR_Byte(0x00);
Receive_ACK();
Stop_I2C();
Delay_1ms(5);
I2C_Start();
AHT20_WR_Byte(0x71);
Receive_ACK();
Byte_first = AHT20_RD_Byte();
Send_ACK();
Byte_second = AHT20_RD_Byte();
Send_ACK();
Byte_third = AHT20_RD_Byte();
Send_NOT_ACK();
Stop_I2C();
Delay_1ms(10);
I2C_Start();
AHT20_WR_Byte(0x70);
Receive_ACK();
AHT20_WR_Byte(0xB0|addr);
Receive_ACK();
AHT20_WR_Byte(Byte_second);
Receive_ACK();
AHT20_WR_Byte(Byte_third);
Receive_ACK();
Stop_I2C();
Byte_second=0x00;
Byte_third =0x00;
}
3、初始化寄存器方式,简化其实就是:
第一步:传输一系列数据,并包含从机地址附加指令(操作码)71(即二进制形式为71)。接着发送目标寄存器地址addr。将被传输的数值数据为\{71, 49\}。
->第二步:读取该寄存器数据,一共三个字节数据。
第三步:读取至某寄存器的数据,并将其存储至位于{0xB0 | addr}位置上的另一个寄存器中;其中后两个寄存器的数据会被存储,并非全部都会被保留。
//官方文档中 表示 初次上电需要 500ms延时
//初次上电需要判断 传感器状态字 与0x18相与不等于0x18 后需要重新配置传感器寄存器
void reset_aht20()
{
uint8_t tx_Buff[3],rx_Buff[3];
i2c_readBytes(aht20_addr, 0,rx_Buff, 1);
//printf("重写寄存器处:0x%x\r\n",rx_Buff[0]);
if((rx_Buff[0] & 0x18) != 0x18)
{
printf("出现此条则需要重写寄存器\r\n");
//利用官方例程重写寄存器
tx_Buff[0] = 0x00;
tx_Buff[1] = 0x00;
i2c_sendBytes(aht20_addr,0 , tx_Buff, 2);
SysTick_Delay_Ms(5);
i2c_readBytes(aht20_addr,0,rx_Buff,3);
SysTick_Delay_Ms(10);
i2c_sendBytes(aht20_addr, 0xB0|0x1B, rx_Buff+1,2);
tx_Buff[0] = 0x00;
tx_Buff[1] = 0x00;
i2c_sendBytes(aht20_addr,0 , tx_Buff, 2);
SysTick_Delay_Ms(5);
i2c_readBytes(aht20_addr,0,rx_Buff,3);
SysTick_Delay_Ms(10);
i2c_sendBytes(aht20_addr, 0xB0|0x1C, rx_Buff+1,2);
tx_Buff[0] = 0x00;
tx_Buff[1] = 0x00;
i2c_sendBytes(aht20_addr,0 , tx_Buff, 2);
SysTick_Delay_Ms(5);
i2c_readBytes(aht20_addr,0,rx_Buff,3);
SysTick_Delay_Ms(10);
i2c_sendBytes(aht20_addr, 0xB0|0x1E, rx_Buff+1,2);
}
}
三、获取AHT20温湿度数据
1、iic通讯正常,重写AHT20传感器寄存器后开始读取传感器数据。
使用的野火的指南者开发板。
iic端口:SCL:PB6
SDA:PB7
和eeprom使用同一个端口。
在读取过程中参考:执行AC指令以触发测量过程。随后等待80ms后进行测量:当状态字bit7等于0时,则连续读取6个字节的数据。
//读取AHT20 并返回读到的数据
//
uint8_t Readaht20(float *rxBuffaht20)
{
uint8_t txBuff[2] = {0x33, 0x00}, rxBuff[6];
uint8_t error;
uint16_t cnt = 0;
uint32_t retudate = 0;
//float aht20[2];
//发送测量命令 0xac
error = i2c_sendBytes(aht20_addr, 0xac, txBuff, 2);
//printf("写AC命令是否成功 0--成功:%d\r\n",error);
//需要等待80ms 测量完成
SysTick_Delay_Ms(80);
//读取状态寄存器
i2c_readBytes(aht20_addr,0, rxBuff,1);
//printf("输出状态寄存器:0x%x\r\n",rxBuff[0]);
while((rxBuff[0] & 0x80) == 0x80) //官方表示 需要判断bit[7]是否为1;1表示忙需要等待,0表示测量完成
{
SysTick_Delay_Ms(10);
cnt++;
if(cnt >= 10)
{
break; //忙状态跳出
}
else
{
break;
}
}
//printf("是否跳出忙检\r\n");
//判断cnt是否因为没有检测到空闲状态跳出,
if(cnt < 10)
{
//官方计算方法
i2c_readBytes(aht20_addr, 0, rxBuff, 6);
retudate = (retudate|rxBuff[1]) <<8;
retudate = (retudate|rxBuff[2]) <<8;
retudate = (retudate|rxBuff[3]);
retudate = retudate>>4;
rxBuffaht20[0] = (retudate*100*10/1024.0/1024.0)/10.0; //除10 已获得正确的值 湿度
retudate = 0; //等于0 防止 数据混乱
retudate = (retudate|rxBuff[3]) <<8;
retudate = (retudate|rxBuff[4]) <<8;
retudate = (retudate|rxBuff[5]);
retudate = retudate&0xfffff;
rxBuffaht20[1] = (retudate*200*10/1024.0/1024.0-500)/10.0; //除10 已获得正确的值 温度
//txUsart_Buff(rxBuff,6);
// printf("\r\nwd == %f\r\n",rxBuffaht20[1]);
// printf("sd == %f\r\n",rxBuffaht20[0]);
return 1;
}
return 0;
//返回两个值,1=代表测量完成。0代表没有
}
3、主函数
int main(void)
{
float readAht20Buff[2];
LEDGPIO_Conflg();
Usart1_Conflg();
GPIO_I2C_Conflg();
//Aht20_Typedef aht;
//AHT20_Init();
printf("\r\n测试串口是否可以通讯\r\n");
// printf("测试器件是否存在:地址(0x%x)(0 - 存在)== %d\r\n",eeprom_addr,read_eeprom());
printf("测试器件是否存在:地址(0x%x)(0 - 存在)== %d\r\n",aht20_addr,read_aht20());
//初始化AHT20 寄存器
reset_aht20();
// i2c_readBytes(aht20_addr, 0, mrxBuff, 3);
// txUsart_Buff(mrxBuff,3);
while(1)
{
if(Readaht20(readAht20Buff))
{
printf("\r\n温度 == %f\r\n",readAht20Buff[1]);
printf("湿度 == %f\r\n",readAht20Buff[0]);
//LEDB_Overturn;
delay_ms(1000);
}
else
{
printf("读取失败!\r\n");
}
}
}
四、效果

完整程序:链接:https://pan.baidu.com/s/1xH6Z9PG6beTeXnBBfl7JEg
提取码:633v
