一、I2C总线协议
1. 什么是I2C协议
I2C 通讯协议(Inter-Integrated Circuit)是由 Phiilps 公司开发的,由于它引脚少,硬件实现简单,可扩展性强,不需要 USART、CAN 等通讯协议的外部收发设备现在被广泛地使用在系统内多个集成电路(IC)间的通讯。
2. I2C 协议的物理层和协议层
在计算机科学里,大部分复杂的问题都可以通过分层来简化。如芯片被分为内核层和片上外设,STM32 标准库则是在寄存器与用户代码之间的软件层。 对于通讯协议,我们也以分层的方式来理解,最基本的是把它分为物理层 和协议层 。
简单来说物理层规定我们用嘴巴还是用肢体来交流,协议层则规定我们用中文还是英文来交流。 2.1.物理层 物理层规定通讯系统中具有机械、电子功能部分的特性,确保原始数据在物理媒体的传输。 I2C 通讯设备之间的常用连接方式如图所示: 2.2.协议层 I2C 的协议定义了通讯的起始和停止信号、数据有效性、响应、仲裁、时钟同步和地址广播等环节,主要规定通讯逻辑,统一收发双方的数据打包、解包标准。
3. I2C 基本读写过程
3.1. 写数据 若配置的方向传输位为“写数据”方向,即下图的情况,广播完地址,接收到应答信号后,主机开始正式向从机传输数据(DATA),数据包的大小为 8 位,主机每发送完一个字节数据,都要等待从机的应答信号(ACK),重复这个过程,可以向从机传输 N 个数据,这个 N 没有大小限制。当数据传输结束时,主机向从机发送一个停止传输信号§,表示不再传输数据。 3.2. 读数据 若配置的方向传输位为“读数据”方向,即上图的情况,广播完地址,接收到应答信号后,从机开始向主机返回数据(DATA),数据包大小也为 8 位,从机每发送完一个数据,都会等待主机的应答信号(ACK),重复这个过程,可以返回 N 个数据,这个 N 也没有大小限制。当主机希望停止接收数据时,就向从机返回一个非应答信号(NACK),则从机自动停止数据传输。
3.3. 读和写数据 除了基本的读写,I2C 通讯更常用的是复合格式,该传输过程有两次起始信号(S)。一般在第一次传输中,主机通过 SLAVE_ADDRESS 寻找到从设备后,发送一段“数据”,这段数据通常用于表示从设备内部的寄存器或存储器地址(注意区分它与 SLAVE_ADDRESS 的区别);在第二次的传输中,对该地址的内容进行读或写。也就是说,第一次通讯是告诉从机读写地址,第二次则是读写的实际内容。
4. I2C的两种方式—— 软件 I2C 和硬件 I2C
4.1. 软件I2C 所谓软件模拟,即直接使用 CPU 内核按照 I2C 协议的要求控制 GPIO 输出高低电平。 如控制产生 I2C 的起始信号时,见图 24-5,先控制作为 SCL 线的 GPIO 引脚输出高电平,然后控制作为 SDA 线的 GPIO 引脚在此期间完成由高电平至低电平的切换,最后再控制SCL 线切换为低电平,这样就输出了一个标准的 I2C 起始信号。 4.2. 硬件I2C 硬件 I2C 是指直接利用 STM32 芯片中的硬件 I2C 外设,该硬件 I2C 外设跟 USART串口外设类似,只要配置好对应的寄存器,外设就会产生标准串口协议的时序。使用它的I2C 外设则可以方便地通过外设寄存器产生 I2C 协议方式的通讯,如初始化好 I2C 外设后,只需要把某寄存器位置 1,那么外设就会控制对应的 SCL 及 SDA 线自动产生 I2C 起始信号,而不需要内核直接控制引脚的电平。 4.3. 两者之间的区别 相对来说,硬件 I2C 直接使用外设来控制引脚,可以减轻 CPU 的负担。不过使用硬件I2C 时必须使用某些固定的引脚作为 SCL 和 SDA,软件模拟 I2C 则可以使用任意 GPIO 引脚,相对比较灵活。在本开发板中,由于 STM32RCT6 芯片引脚较少,资源比较紧张,在设计硬件时不方便使用硬件 I2C 指定的引脚连接外部设备(EEPROM 存储器芯片),所以在控制程序上只能使用软件模拟 I2C 的方式。若希望学习如何使用 STM32 的硬件 I2C 外设,可以参考我们其它开发板的教程,如 F103 指南者、F103 霸道等开发板。
二、基于I2C的AHT20温湿度数据采集
1. AHT20芯片相关信息的了解
具体信息请到官方下载对应产品介绍文档,资料链接如下:http://www.aosong.com/class-36.html
2. 代码添加过程
- 可以在野火提供的示例代码中,打开一个只包含固件库的空项目。向工程中添加相关代码,添加代码的具体内容请参考链接:https://blog.csdn.net/hhhhhh277523/article/details/111397514
- 也可以使用:https://gitcode.net/mirrors/Thee24LYJ/STM32_AHT20?utm_source=csdn_github_accelerator
下载相应的代码文件进行修改
小编这里采用第二种方法(完整工程代码下载详见参考链接): 将上述文件下载完成后,打开工程,将主函数main.c 中的代码修改为如下:
//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);
}
}
修改后main.c文件如图所示:
3.代码分析
3.1. 初始化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
}
}
3.2. 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; //温度
}
4. 管脚连接
AHT20 | STM32F10X |
---|
SCL | PB6 | SDA | PB7 | VCC | 3V3 | GND | GND |
加上面包板,最后连线如下:
5.编译并烧录
点击左上角的编译按钮进行编译,并生成相应的.hex文件 打开mcuisp软件进行.hex文件烧录
6. 运行结果
打开野火串口调试助手并打开串口,可以看到以下结果: 上位机不断发送显示湿度和温度,且当手放在AHT20传感器上时,温度有明显的升高,实验成功。
三、小结
本实验主要是针对I2C协议进行通信过程的理解,对于AHT20芯片读取数据的过程有了一个比较清晰的认识,才能看懂代码是如何进行读取数据的。这个过程还是花了一些时间才完成的,最开始,因为对代码的不熟悉导致工程建立不成功,但通过对I2C协议和芯片的了解后,最后得以完成实验。
四、参考链接
1.https://blog.csdn.net/weixin_45830968/article/details/111437865 2.https://blog.csdn.net/qq_43279579/article/details/111597278 3.完整工程代码 提取码:1234
|