目录
一、I2C总线协议
1.1 I2C 通讯协议简介
I2C 通讯协议(Inter-Integrated Circuit)是由 Phiilps 公司开发的,由于它引脚少,硬件实现简单,可扩展性强,不需要 USART、CAN 等通讯协议的外部收发设备,现在被广泛地使用在系统内多个集成电路(IC)间的通讯。
在计算机科学里,大部分复杂的问题都可以通过分层来简化。如芯片被分为内核层和片上外设;STM32 标准库则是在寄存器与用户代码之间的软件层。
对于通讯协议,我们也以分层的方式来理解,最基本的是把它分为物理层和协议层。
- 物理层规定通讯系统中具有机械、电子功能部分的特性,确保原始数据在物理媒体的传输。
- 协议层主要规定通讯逻辑,统一收发双方的数据打包、解包标准。
简单来说物理层规定我们用嘴巴还是用肢体来交流,协议层则规定我们用中文还是英文来交流。
1.2 I2C的物理层与协议层
a) 物理层
I2C 通讯设备之间的常用连接方式见下图。
b) 协议层
I2C 的协议定义了通讯的起始和停止信号、数据有效性、响应、仲裁、时钟同步和地址广播等环节。
1.3 I2C 基本读写过程
-
写数据
若配置的方向传输位为“写数据”方向,广播完地址,接收到应答信号后,主机开始正式向从机传输数据(DATA),数据包的大小为 8 位,主机每发送完一个字节数据,都要等待从机的应答信号(ACK),重复这个过程,可以向从机传输 N 个数据,这个 N 没有大小限制。当数据传输结束时,主机向从机发送一个停止传输信号§,表示不再传输数据。 -
读数据
若配置的方向传输位为“读数据”方向,广播完地址,接收到应答信号后,从机开始向主机返回数据(DATA),数据包大小也为 8 位,从机每发送完一个数据,都会等待主机的应答信号(ACK),重复这个过程,可以返回 N 个数据,这个 N 也没有大小限制。当主机希望停止接收数据时,就向从机返回一个非应答信号(NACK),则从机自动停止数据传输。 -
读和写数据
除了基本的读写,I2C 通讯更常用的是复合格式,该传输过程有两次起始信号(S)。一般在第一次传输中,主机通过 SLAVE_ADDRESS 寻找到从设备后,发送一段“数据”,这段数据通常用于表示从设备内部的寄存器或存储器地址(注意区分它与 SLAVE_ADDRESS 的区别);在第二次的传输中,对该地址的内容进行读或写。也就是说,第一次通讯是告诉从机读写地址,第二次则是读写的实际内容。
1.4 I2C的两种方式
a) 软件I2C
所谓软件模拟,即直接使用 CPU 内核按照 I2C 协议的要求控制 GPIO 输出高低电平。
如控制产生 I2C 的起始信号时,见图 24-5,先控制作为 SCL 线的 GPIO 引脚输出高电平,然后控制作为 SDA 线的 GPIO 引脚在此期间完成由高电平至低电平的切换,最后再控制SCL 线切换为低电平,这样就输出了一个标准的 I2C 起始信号。
b) 硬件I2C
硬件 I2C 是指直接利用 STM32 芯片中的硬件 I2C 外设,该硬件 I2C 外设跟 USART串口外设类似,只要配置好对应的寄存器,外设就会产生标准串口协议的时序。使用它的I2C 外设则可以方便地通过外设寄存器产生 I2C 协议方式的通讯,如初始化好 I2C 外设后,只需要把某寄存器位置 1,那么外设就会控制对应的 SCL 及 SDA 线自动产生 I2C 起始信号,而不需要内核直接控制引脚的电平。
c) 二者区别
硬件 I2C 直接使用外设来控制引脚,可以减轻CPU 的负担。不过使用硬件I2C 时必须使用某些固定的引脚作为SCL 和 SDA;
软件模拟 I2C 则可以使用任意 GPIO 引脚,相对比较灵活。在本开发板中,由于 STM32RCT6 芯片引脚较少,资源比较紧张,在设计硬件时不方便使用硬件 I2C 指定的引脚连接外部设备(EEPROM 存储器芯片),所以在控制程序上只能使用软件模拟 I2C 的方式。
简单来说,硬件I2C对应芯片上的I2C外设,有相应I2C驱动电路,其所使用的I2C管脚也是固定的;软件I2C一般用GPIO管脚,用软件控制管脚状态,以模拟I2C通信波形。
硬件I2C的效率要远高于软件的,而软件I2C由于不受管脚限制,接口比较灵活。
二、AHT20温湿度数据采集
2.1 AHT20芯片
官方产品介绍:软件下载-温湿度传感器
2.2 添加代码
可以在野火提供的示例代码中,打开一个只包含固件库的空项目。
向工程中添加相关代码,添加代码的具体内容请参考链接:
https://blog.csdn.net/hhhhhh277523/article/details/111397514
这里下载相应的代码文件进行修改,链接如下
https://gitcode.net/mirrors/Thee24LYJ/STM32_AHT20?utm_source=csdn_github_accelerator
将上述文件下载完成后,打开工程,进行main.c
函数的修改
#include "delay.h"
#include "temhum.h"
#include "sys.h"
#include "usart.h"
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);
delay_ms(1000);
}
}
2.3 主要代码分析
a) 初始化AHT20
void AHT20_Init(void)
{
IIC_Init();
IIC_Start();
IIC_Send_Byte(0x70);
IIC_Wait_Ack();
IIC_Send_Byte(0xa8);//0xA8进入NOR工作模式
IIC_Wait_Ack();
IIC_Send_Byte(0x00);
IIC_Wait_Ack();
IIC_Send_Byte(0x00);
IIC_Wait_Ack();
IIC_Stop();
delay_ms(10);//延时10ms左右
IIC_Start();
IIC_Send_Byte(0x70);
IIC_Wait_Ack();
IIC_Send_Byte(0xbe);//0xBE初始化命令,AHT20的初始化命令是0xBE, AHT10的初始化命令是0xE1
IIC_Wait_Ack();
IIC_Send_Byte(0x08);//相关寄存器bit[3]置1,为校准输出
IIC_Wait_Ack();
IIC_Send_Byte(0x00);
IIC_Wait_Ack();
IIC_Stop();
delay_ms(10);//延时10ms左右
}
void temphum_init()
{
delay_ms(40);//刚上电,延时40ms才可以读取状态
//首先发0x71读取状态字bit[3],如果=1,为校准输出,无须初始化!!!正常情况下读回来的状态是0x1C或者是0x18,读回来是0x80表示忙状态;
if(!((AHT20_Read_Status()&0x08)==0x08))
{
AHT20_Init(); //初始化AHT20
}
}
b) AHT20芯片读取温度和湿度数据
//没有CRC校验,直接读取AHT20的温度和湿度数据
void AHT20_Read_CTdata(u32 *ct)
{
volatile u8 Byte_1th=0,Byte_2th=0,Byte_3th=0;
volatile u8 Byte_4th=0,Byte_5th=0,Byte_6th=0;
u32 RetuData = 0;
u16 cnt = 0,flag;
AHT20_SendAC();//向AHT20发送AC命令
delay_ms(80); //大约延时80ms
while(((AHT20_Read_Status()&0x80)==0x80))//直到状态bit[7]为0,表示为空闲状态,若为1,表示忙状态
{
delay_ms(1);
if(cnt++>=100) break;
}
IIC_Start();
IIC_Send_Byte(0x71);
flag=IIC_Wait_Ack();
Byte_1th = IIC_Read_Byte(flag);//状态字
Byte_2th = IIC_Read_Byte(flag);//湿度,发送ACK(继续发送)
Byte_3th = IIC_Read_Byte(flag);//湿度
Byte_4th = IIC_Read_Byte(flag);//湿度/温度
Byte_5th = IIC_Read_Byte(flag);//温度
Byte_6th = IIC_Read_Byte(!flag);//温度,发送NACK(停止发送)
IIC_Stop();
//保存得到的数据到RetuData中
RetuData = (RetuData|Byte_2th)<<8;
RetuData = (RetuData|Byte_3th)<<8;
RetuData = (RetuData|Byte_4th);
RetuData =RetuData >>4;
ct[0] = RetuData;//湿度
RetuData = 0;
RetuData = (RetuData|Byte_4th)<<8;
RetuData = (RetuData|Byte_5th)<<8;
RetuData = (RetuData|Byte_6th);
RetuData = RetuData&0x0fffff;
ct[1] =RetuData; //温度
}
2.4 管脚连接
AHT20 | STM32F10X |
---|---|
SCL | PB6 |
SDA | PB7 |
VCC | 3V3 |
GND | GND |
接入面包板如下所示:
2.5 编译并烧录
点击左上角的编译按钮进行编译,并生成相应的.hex文件
打开mcuisp软件进行程序烧录
2.6 运行结果
打开野火串口调试助手并打开串口,可以看到以下结果:
上位机不断发送显示湿度和温度,且当手放在AHT20传感器上时,温度和湿度都有明显的升高。
三、小结
本次实验通过了解I2C协议的通信过程后,再理解AHT20芯片读取数据的代码比较方便;通过串口连接传感器在调试助手上显示数据,有了几次实验的基础后,对于硬件部分上手操作简单了许多,也能够熟练掌握STM32面包板的使用了。