AHT20温湿度采集
前言
本例使用I2C协议读取AHT20温湿度传感器的值,并将读取到的数据通过串口打印出来。
一、I2C简介
(一)I2C协议简介
I2C 通讯协议(Inter-Integrated Circuit)是由Phiilps公司开发的,由于它引脚少,硬件实现简单,可扩展性强,不需要USART、CAN等通讯协议的外部收发设备,现在被广泛地使用在系统内多个集成电路(IC)间的通讯。
I2C总线主要的优点是其简单性和有效性。由于接口直接在组件之上,因此I2C总线占用的空间非常小,减少了电路板的空间和芯片管脚的数量,降低了互联成本。
I2C总线的另一个优点是,它支持多主控(multimastering), 其中任何能够进行发送和接收的设备都可以成为主总线。一个主控能够控制信号的传输和时钟频率。当然,在任何时间点上只能有一个主控。
1.I2C物理层
-
它是一个支持多设备的总线。“总线”指多个设备共用的信号线。在一个I2C通讯总线中,可连接多个I2C通讯设备,支持多个通讯主机及多个通讯从机。
-
一个I2C总线只使用两条总线线路,一条双向串行数据线(SDA) ,一条串行时钟线(SCL)。数据线即用来表示数据,时钟线用于数据收发同步。
-
每个连接到总线的设备都有一个独立的地址,主机可以利用这个地址进行不同设备之间的访问。
-
总线通过上拉电阻接到电源。当I2C设备空闲时,会输出高阻态,而当所有设备都空闲,都输出高阻态时,由上拉电阻把总线拉成高电平。
-
多个主机同时使用总线时,为了防止数据冲突,会利用仲裁方式决定由哪个设备占用总线。
-
具有三种传输模式:标准模式传输速率为100kbit/s ,快速模式为400kbit/s ,高速模式下可达3.4Mbit/s,但目前大多I2C设备尚不支持高速模式。
引用来源(野火参考资料)
2.I2C协议层
I2C的协议定义了通讯的起始和停止信号、数据有效性、响应、仲裁、时钟同步和地址广播等环节。
- I2C基本读写过程
- 通讯的起始和停止信号
- 数据有效性
- 地址及数据方向
- 响应
其中,按照是否使用I2C基本外设可分为
软件模拟协议:使用CPU直接控制通讯引脚的电平,产生出符合通讯协议标准的逻辑。
硬件实现协议:由STM32的I2C片上外设专门负责实现I2C通讯协议,只要配置好该外设,它就会自动根据协议要求产生通讯信号,收发数据并缓存起来,CPU只要检测该外设的状态和访问数据寄存器,就能完成数据收发。这种由硬件外设处理I2C协议的方式减轻了CPU的工作,且使软件设计更加简单。
本例我们主要软件I2C为例,详细分析I2C的通讯格式。
3.AHT20简介
产品外型
引脚定义
传感器读取流程
- 上电后要等待不少于100ms, 读取温湿度值之前, 通过发送0x71获取一个字节的状态字,如果状态字和0x18相与后不等于0x18,初始化0x1B、 0x1C、 0x1E寄存器, 详细初始化流程请参照官网例程; 如果相等,执行下一步
- 等待10ms发送0xAC命令(触发测量), 此命令参数有两个字节, 第一个字节为0x33,第二个字节为0x00
- 等待80ms待测量完成,如果读取状态字Bit[7]为0, 表示测量完成, 然后可以连续读取六个字节; 否则继续等待
- 当接收完六个字节后, 紧接着下一个字节是CRC校验数据, 用户可以根据需要读出, 如果接收端需要CRC校验,则在接收完第六个字节后发ACK应答, 否则发NACK结束,CRC初始值为0XFF,CRC8校验多项式为:
CRC[7:0]=1+x4+x5+x8AHT20
- 计算温湿度值。 注: 在第一步的校准状态检验只需要上电时检查, 在正常采集过程无需操作。
- 原始数据处理
二、模拟I2C
本例是以AHT20温湿度传感器读取数据为例,主要分析在I2C通讯协议的起始与停止信号、应答/非应答信号,数据有效性、数据读写操作等。
AHT20例程
关于AHT20例程文件的移植的详细讲解,可参考
(一)起始/停止信号
起始信号
void IIC_Start(void)
{
SDA_OUT(); //配置SDA线输出为通用推挽输出,输出速度为50Mhz
IIC_SDA=1; //SDA线输出高电平
IIC_SCL=1; //SCL线输出高电平
delay_us(4);
IIC_SDA=0; //SCL线为高电平时,SDA线由高变低(下降沿),表示通信的开始
delay_us(4);
IIC_SCL=0; //钳住I2C总线,准备发送或接收数据
}
可以看到,当SCL线是高电平时,SDA线从高电平向低电平切换,表示通讯的开始。
停止信号
void IIC_Stop(void)
{
SDA_OUT(); //配置SDA线输出为通用推挽输出,输出速度为50Mhz
IIC_SCL=0; //SCL线输出低电平
IIC_SDA=0; //SDA线输出高电平
delay_us(4);
IIC_SCL=1; //SCL线输出高电平
IIC_SDA=1; //SCL线为高电平时,SDA线由低变高(上升沿),表示通信的结束
delay_us(4);
}
当SCL线是高电平时,SDA线从低电平向高电平切换,表示通讯的停止。
(二)数据有效性
I2C使用SDA信号线来传输数据,使用SCL线进行数据同步。
SDA数据线在SCL的每个时钟周期传输一位数据。
- SCL为高电平的时候,表示SDA的数据有效,即此时的SDA为高电平时表示数据“1”,低电平时表示数据“0”。
- 当SCL为低电平时,表示SDA的数据无效,一般在这个时候进行电平转换,为下一次传输数据做好准备。
void AHT20_WR_Byte(uint8_t Byte) //往AHT20写一个字节
{
uint8_t Data,N,i;
Data=Byte;
SDA_OUT(); //配置SDA线输出为通用推挽输出,输出速度为50Mhz
i = 0x80; //1000 0000
for(N=0;N<8;N++)
{
/*此时SCL线为低电平,表示SDA数据的交换 */
IIC_SCL=0;
delay_us(4);
if(i&Data)
{
IIC_SDA=1;
}
else
{
IIC_SDA=0;
}
/*此时SCL线为低电平,表示SDA数据的交换 */
IIC_SCL=1; //SCL线为高电平、表示数据有效
delay_us(4);
Data <<= 1; //移位发送,从高位向低位发送数据
}
IIC_SCL=0; //SCL线再次置低
delay_us(8);
}
我们用示波器观察发送以数据0x12
为例的波形
(三)应答/非应答
发送应答信号
//I2C应答信号
void IIC_Ack(void)
{
IIC_SCL=0; //SCL线输出低电平
SDA_OUT(); //配置SDA线输出为通用推挽输出,输出速度为50Mhz
IIC_SDA=0; //SDA线输出低电平
delay_us(2);
IIC_SCL=1; //SCL线输出高电平,当SCL线为高电平时,此时SDA为低电平,表示应答
delay_us(2);
IIC_SCL=0; //SCL线输出低电平
}
当SCL线为高电平时、SDA线为低电平表示响应。
发送非应答信号
//I2C非应答信号
void IIC_NAck(void)
{
IIC_SCL=0; //SCL线输出低电平
SDA_OUT(); //配置SDA线输出为通用推挽输出,输出速度为50Mhz
IIC_SDA=1; //SDA线输出高电平
delay_us(2);
IIC_SCL=1; //SCL线输出高电平,此时SCL线为高电平,SDA线也为高电平,表示不响应
delay_us(2);
IIC_SCL=0; //SCL线输出低电平
}
可以看到,当SCL线为高电平时、SDA线为高电平表示不响应。
因为I2C协议的规定,主机每接收(发送)一个字节的数据,主机(传感器)都要返回一个应答/非应答信号。
因为I2C协议的规定,主机每接收(发送)一个字节的数据,主机(传感器)都要返回一个应答/非应答信号。
以下是主机写多个字节数据的帧格式(我们以stm32向AHT20传感器写数据为例)
- 主机stm32产生起始信号
- 主机向总线发送7位设备(AHT20)地址和一个读/写位,构成一个字节数据
- AHT20判断主机发送的地址与自身设备地址相同,产生应答响应
- 主机发送一个数据,AHT20成功接收,并产生应答响应(表示AHT20还需要接收数据)
- 主机再次发送一个数据,AHT20成功接收,并产生非应答响应(表示AHT20不需要接收数据了)
- 主机收到非应答响应,产生停止信号,通信结束
IIC写字节代码
//IIC_WriteByte流程
void IIC_WriteByte(uint16_t addr,uint8_t data,uint8_t device_addr)
{
IIC_Start(); //产生起始信号
if(device_addr==0xA0)
IIC_Send_Byte(0xA0 + ((addr/256)<<1)); //写入设备“读”地址
else
IIC_Send_Byte(device_addr); //写入设备“写”地址
IIC_Wait_Ack(); //等待应答信号
IIC_Send_Byte(addr&0xFF); //写入要读/写操作的寄存器地址
IIC_Wait_Ack(); //等待应答信号
IIC_Send_Byte(data); //写入数据
IIC_Wait_Ack(); //等待应答信号
IIC_Stop(); //数据停止信号
if(device_addr==0xA0)
delay_ms(10);
else
delay_us(2);
}
注意
:只有最后希望通信停止的时候才发送NACK信号,而表示对数据正确接收到并希望继续接收数据,需要发送ACK信号。
三、硬件I2C
(一)I2C的功能框图
I2C 的所有硬件架构都是根据图中左侧 SCL 线和 SDA 线展开的 。 STM32 芯片有多个 I2C 外设,它们的 I2C 通讯信号引出到不同的 GPIO 引脚上,使用时必须配置到这些指定的引脚,见表 STM32F10x 的 I2C 引脚 。
关于I2C的GPIO引脚的复用功能,参考如下
(二)I2C硬件控制逻辑
1.时钟控制逻辑
SCL 线的时钟信号,由 I2C 接口根据时钟控制寄存器 (CCR) 控制,控制的参数主要为时钟频率。
配置 I2C 的 CCR 寄存器可修改通讯速率相关的参数:
选择不同的模式,可以配置I2C不同的通讯速率。
2.数据控制逻辑
I2C 的 SDA 信号主要连接到数据移位寄存器上,数据移位寄存器的数据来源及目标是数据寄存器 (DR)、地址寄存器 (OAR)、 PEC 寄存器以及 SDA 数据线。当向外发送数据的时候,数据移位寄存器以“数据寄存器”为数据源,把数据一位一位地通过 SDA 信号线发送出去;当从外部接收数据的时候,数据移位寄存器把 SDA 信号线采样到的数据一位一位地存储到“数据寄存器”中。
若使能了数据校验,接收到的数据会经过 PCE 计算器运算,运算结果存储在“PEC 寄存器”中。 当 STM32 的 I2C 工作在从机模式的时候,接收到设备地址信号时,数据移位寄存器会把接收到 的地址与 STM32 的自身的“I2C 地址寄存器”的值作比较,以便响应主机的寻址。 STM32 的自 身 I2C 地址可通过修改“自身地址寄存器”修改,支持同时使用两个 I2C 设备地址,两个地址分 别存储在 OAR1 和 OAR2 中。
3.整体控制逻辑
整体控制逻辑负责协调整个 I2C 外设,控制逻辑的工作模式根据我们配置的“控制寄存器(CR1/CR2)”的参数而改变。在外设工作时,控制逻辑会根据外设的工作状态修改“状态寄存器 (SR1 和 SR2)”,我们只要读取这些寄存器相关的寄存器位,就可以了解 I2C 的工作状态。
除此之外,控制逻辑还根据要求,负责控制产生 I2C 中断信号、 DMA 请求及各种 I2C 的通讯信号(起始、停止、响应信号等)。
四、演示实例
- main文件
int main(void)
{
u32 CT_data[2]={0};
volatile float hum=0,tem=0;
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
uart_init(115200); //串口初始化为115200
temphum_init(); //ATH20初始化
while(1)
{
AHT20_Read_CTdata(CT_data); //不经过CRC校验,直接读取AHT20的温度和湿度数据
hum = CT_data[0]*100*10/1024/1024; //计算得到湿度值(放大了10倍)
tem = CT_data[1]*200*10/1024/1024-500;//计算得到温度值(放大了10倍)
printf("湿度:%.1f%%\r\n",(hum/10));
printf("温度:%.1f度\r\n",(tem/10));
printf("\r\n");
delay_ms(1000);
}
}
- 接线方式
AHT20 | STM32F103C8T6 |
---|---|
SCL | PB6 |
SDA | PB7 |
VCC | 3V3 |
GND | GND |
- 演示效果如下
总结
硬件I2C和软件I2C各有其特点,对软件I2C代码分析让我对I2C的数据通讯格式有了更深刻的理解,但是也只是简单的了解一些通讯格式。原本是想尝试参考软件I2C的流程使用硬件I2C替代一下,,但是参考例程配置硬件I2C外设以以及数据的格式处理等,中途遇到了一些问题,现在尚未解决。
以上若有不当之处,敬请指教!
参考
《STM32库开发指南-基于野火指南者开发板PDF》
《AHT20产品规格书》