文章目录[隐藏]
目录
本文概述:
学习I2C总线通信协议,使用STM32F103完成基于I2C协议的AHT20温湿度传感器的数据采集,并将采集的温度-湿度值通过串口输出。具体任务:
1)解释什么是“软件I2C”和“硬件I2C”? (阅读野火配套教材的第23章“I2C--读写EEPROM”原理章节)
2)阅读AHT20数据手册,编程实现:每隔2秒钟采集一次温湿度数据,并通过串口发送到上位机(win10)。
一、I2C通信协议
1.1 I2C协议简介
I2C 通讯协议(Inter-Integrated Circuit)是由 Phiilps 公司开发的,由于它引脚少,硬件实现简单,可扩展性强,不需要 USART、 CAN 等通讯协议的外部收发设备,所以被广泛使用。
1.2 I2C物理层
①物理层特点
(1)它是一个支持设备的总线。“总线”指多个设备共用的信号线。在一个 I2C 通讯总线中,可连接多个 I2C 通讯设备,支持多个通讯主机及多个通讯从机。
(2)一个 I2C 总线只使用两条总线线路,一条双向串行数据线(SDA) ,一条串行时钟线(SCL)。数据线即用来表示数据,时钟线用于数据收发同步。
(3)每一个连接总线的设备都有一个独立的地址,主机可以通过这个地址进行选择连接总线的设备与之通信。
(4)总线通过上拉电阻接到电源。当 I2C 设备空闲时,会输出高阻态,而当所有设备都空闲,都输出高阻态时,由上拉电阻把总线拉成高电平。
(5)多个主机同时使用总线时,为了防止多个设备发送数据冲突,会利用仲裁方式决定由哪个设备占用总线。
(6) 具有三种传输模式:标准模式传输速率为 100kbit/s ,快速模式为 400kbit/s ,高速模式下可达 3.4Mbit/s,但目前大多 I2C 设备尚不支持高速模式。
(7) 连接到相同总线的 IC 数量受到总线的最大电容 400pF 限制 。
备注:
仲裁:SDA线的仲裁也是建立在总线具有线“与”逻辑功能(线与逻辑,即两个以上的输出端直接互连就可以实现“AND”的逻辑功能。两个一出一,一个一出零、没有一出零)的原理上的。节点在发送1位数据后,比较总线上所呈现的数据与自己发送的是否一致。是,继续发送;否则,进行比较,输出低电平进行发送,输出高电平退出。SDA线的仲裁可以保证I2C总线系统在多个主节点同时企图控制总线时通信正常进行并且数据不丢失。总线系统通过仲裁只允许一个主节点可以继续占据总线。
②I2C通讯设备之间的常用连接方式见图
1.3 I2C协议层
S:由主机的 I2C 接口产生的传输起始信号(S),这时连接到 I2C 总线上的所有从机都会接收到这个信号。
SLAVE ADDRESS:从机地址信号。 在 I2C 总线上,每个设备的地址都是唯一的, 当主机广播的地址与某个设备地址相同时,这个设备就被选中了,没被选中的设备将会忽略之后的数据信号。从机地址一般是 7 位或 10 位。
R/W:是传输方向的选择位,该位为 0 时,表示后面的数据传输方向是由主机传输至从机,即主机向从机写数据。该位为 1 时,则相反,即主机由从机读数据。
A | A/:一个应答(ACK)或非应答(NACK)信号。
P:停止传输信。
图中背景有填充的矩形表示数据由主机传输至从机,白色部分表示数据由从机传输至主机
①写数据
讲得简单一点,即主机发送信息、从机阅读信息。配置为方向为“写数据”方向,接受的数据包大小为8位(一个byte),主机每发送完一个字节数据,都要等待从机的应答信号(ACK),重复这个过程,数据的多少可以理论是可以随便的。当数据传输结束时,主机向从机发送一个停止传输信号,表示不再传输数据。
②读数据
讲得简单一点,即主机阅读信息、从机发送信息。若配置的方向传输位为“读数据”方向,发送的数据包大小也为 8 位,从机每发送完一个数据,都会等待主机的应答信号(ACK),重复这个过程,数据的多少可以理论也是可以随便的。当主机希望停止接收数据时,就向从机返回一个非应答信号(NACK),则从机自动停止数据传输。
③读和写数据
除了基本的读写,I2C 通讯更常用的是复合格式,该传输过程有两次起始信号(S)。一般在第一次传输中,主机通过 SLAVE_ADDRESS 寻找到从设备后,发送一段“数据”,这段数据通常用于表示从设备内部的寄存器或存储器地址(注意区分它与 SLAVE_ADDRESS 的区别);在第二次的传输中,对该地址的内容进行读或写。也就是说,第一次通讯是告诉从机读写地址,第二次则是读写的实际内容。
1.4 硬件 I2C 和 软件 I2C
- 硬件(固件) I2C 对应芯片上的 I2C 外设,有相应 I2C 驱动电路,其所使用的 I2C 管脚也是专用的
- 软件(模拟) I2C 一般是用 GPIO 管脚,用软件控制管脚状态以模拟 I2C 通信波形
区别:
- 硬件 I2C 的效率要远高于软件的;软件 I2C 由于不受管脚限制,接口比较灵活
- 硬件 I2C 是直接调用内部寄存器进行配置;软件 I2C 是通过 GPIO ,软件模拟寄存器的工作方式
- 硬件 I2C 配置了 IO 口的功能(I2C 功能);软件 I2C 没有配置 IO 口的功能
- 硬件 I2C 的 I2C 写函数,有调用现成的函数或者给某个寄存器赋值;软件 I2C 传输数据的方式是一个 bit 一个 bit 模拟发生送的,肯定用到了循环
- 根据代码量判断,硬件 I2C 的代码量肯定比软件 I2C 的代码量小
- 硬件 I2C 用法比较复杂,软件 I2C 的流程更清楚一些
- 硬件 I2C 的速度比软件 I2C 的速度快,并且硬件 I2C 可以用 DMA
- 硬件 I2C 只能在固定管脚上;模拟 I2C 可以在任何管脚上
- 软件 I2C 是程序员使用程序控制 SCL、SDA 线输出高低电平,模拟 I2C 协议的时序,一般较硬件 I2C 稳定,但是程序较为繁琐,但不难
- 硬件 I2C 程序员只要调用 I2C 的控制函数即可,不用直接的去控制SCL、SDA 高低电平的输出,但是有些单片机的硬件 I2C 不太稳定,调试问题较多
二、AHT20 温湿度传感器
2.1 原理图
2.2 相关参数
详细数据请参考:https://pan.baidu.com/s/1wl42yLOiQU1zJktUczRpNA
提取码:qlo6
三、项目实现
3.1 实验准备
硬件:
- 温湿度传感器AHT20
- STM32开发板
- 杜邦线
软件:
- Keil5 MDK
- 串口调试助手
3.2 程序代码
部分代码如下:
AHT20芯片启动函数read_AHT20_once:
void read_AHT20_once(void)
{
printf("读取数据中");
//延时 10 微妙
delay_ms(10);
//传输数据前进行启动传感器和软复位
reset_AHT20();
delay_ms(10);
//查看使能位
init_AHT20();
delay_ms(10);
//触发测量
startMeasure_AHT20();
delay_ms(80);
//读数据
read_AHT20();
delay_ms(5);
}
AHT20芯片复位函数reset_AHT20:
void reset_AHT20(void)
{
//数据传输开始信号
I2C_Start();
//发送数据
I2C_WriteByte(0x70);
//接收 ACK 信号
ack_status = Receive_ACK();
//判断 ACK 信号
if(ack_status)
{
printf(">");
}
else
printf("×");
//发送软复位命令(重启传感器系统)
I2C_WriteByte(0xBA);
//接收 ACK 信号
ack_status = Receive_ACK();
//判断 ACK 信号
if(ack_status)
printf(">");
else
printf("×");
//停止 I2C 协议
I2C_Stop();
}
AHT20芯片读取数据函数read_AHT20:
void read_AHT20(void)
{
uint8_t i;
//初始化 readByte 数组
for(i=0; i<6; i++)
{
readByte[i]=0;
}
I2C_Start();
//通过发送 0x71 可以获取一个字节的状态字
I2C_WriteByte(0x71);
ack_status = Receive_ACK();
//接收 6 个 8 bit的数据
readByte[0]= I2C_ReadByte();
//发送 ACK 信号
Send_ACK();
readByte[1]= I2C_ReadByte();
Send_ACK();
readByte[2]= I2C_ReadByte();
Send_ACK();
readByte[3]= I2C_ReadByte();
Send_ACK();
readByte[4]= I2C_ReadByte();
Send_ACK();
readByte[5]= I2C_ReadByte();
//发送 NACK 信号
SendNot_Ack();
I2C_Stop();
//温湿度的二进制数据处理
//0x68 = 0110 1000
//0x08 = 0000 1000
if( (readByte[0] & 0x68) == 0x08 )
{
H1 = readByte[1];
//H1 左移 8 位并与 readByte[2] 相或
H1 = (H1<<8) | readByte[2];
H1 = (H1<<8) | readByte[3];
//H1 右移 4 位
H1 = H1>>4;
H1 = (H1*1000)/1024/1024;
T1 = readByte[3];
//与运算
T1 = T1 & 0x0000000F;
T1 = (T1<<8) | readByte[4];
T1 = (T1<<8) | readByte[5];
T1 = (T1*2000)/1024/1024 - 500;
AHT20_OutData[0] = (H1>>8) & 0x000000FF;
AHT20_OutData[1] = H1 & 0x000000FF;
AHT20_OutData[2] = (T1>>8) & 0x000000FF;
AHT20_OutData[3] = T1 & 0x000000FF;
}
else
{
AHT20_OutData[0] = 0xFF;
AHT20_OutData[1] = 0xFF;
AHT20_OutData[2] = 0xFF;
AHT20_OutData[3] = 0xFF;
printf("꧰üá?");
}
printf("完成!\n");
printf("----温度:%d%d.%d °C\n",T1/100,(T1/10)%10,T1%10);
printf("----湿度:%d%d.%d %%",H1/100,(H1/10)%10,H1%10);
printf("\n\n");
}
完整工程代码:
链接:https://pan.baidu.com/s/1JfFc0ot8qaNdLzmpmXRpeg
提取码:cqjt
3.3 硬件连接
这里注意下,VCC→3V3,GND→GND,SCL→PB6,SDA→PB7,别接错了。
连接实物图:
3.4 成果展示
将代码编译并烧录进板子中,并将ATH20芯片用手捂住哈一口气,可以明显的观察到温湿度在升高
四、逻辑分析仪分析协议
由图中绿色小点代表信号开始采集,红色代表结束
当 SCL 线是高电平时 SDA 线从高电平向低电平切换,这个情况表示通讯的起始。当 SCL 是高电平时 SDA线由低电平向高电平切换,表示通讯的停止。
对于I2C协议前面有所提到,当SCL为高电平时,SDA才有作用, 每一个上升沿读取一个bit,则第一个波形对应的二进制信号为01000001,而最后一个是I2C协议的一个应答信号,当SDA为低电平时表示ACK希望继续接收信号,当SDA为高电平表示NAK表示不接收信号了。
代码分析:
首先初始化SCL和SDA都为高电平,那么在SDA从高电平跳变到低电平时表示开始接收。
开始接收,SDA从1到0
该函数功能即将SCL为高电平时,存储SDA的电平值。
这两个函数定义了ACK和NAK应答信号的发送。
五、参考文献
I2C通信协议详细讲解_Hello_STM32的博客-CSDN博客_iic通信协议I2C协议讲解讲解流程我们为什么要学习I2C通信I2C协议简介:I2C物理层特点I2C协议层写数据读数据读和写数据通讯的起始和停止信号地址及数据方向讲解流程我们为什么要学习I2C通信Stm32的最常用的板间通信有很多,有I2C、SPI、CAN;I2C通信协议是我们stm32板间通信比较常用的、也是比较简单的。话不多说,那么我们就去了解学习一下简单的I2C通信吧。I2C协议简介:I2C 通讯协议(Inter-Integrated Circuit)是由 Phiilps 公司开发的,由于它引脚少,硬件实https://blog.csdn.net/Hello_STM32/article/details/111086472?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163740545116780357233054%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=163740545116780357233054&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_click~default-2-111086472.first_rank_v2_pc_rank_v29&utm_term=i2c%E9%80%9A%E4%BF%A1%E7%9A%84%E8%AF%A6%E7%BB%86%E8%AE%B2%E8%A7%A3&spm=1018.2226.3001.4187AHT20温度采集_hhhhhh277523的博客-CSDN博客AHT20温度采集1.“软件I2C”和“硬件I2C”2.stm32f103指南者外接AHT20进行温度采集。连接硬件代码实现1.“软件I2C”和“硬件I2C”所谓硬件I2C对应芯片上的I2C外设,有相应I2C驱动电路,其所使用的I2C管脚bai也是专用的;软件I2C一般是用GPIO管脚,用软件控制管脚状态以模拟I2C通信波形。硬件I2C的效率要远高于软件的,而软件I2C由于不受管脚限制,接口比较灵活。本次实验中使用的软件I2C2.stm32f103指南者外接AHT20进行温度采集。连接硬件AHhttps://blog.csdn.net/hhhhhh277523/article/details/111397514?ops_request_misc=%25257B%252522request%25255Fid%252522%25253A%252522160865684616780288282043%252522%25252C%252522scm%252522%25253A%25252220140713.130102334.pc%25255Fall.%252522%25257D&request_id=160865684616780288282043&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_v2~rank_v29-19-111397514.nonecase&utm_term=AHT20
版权声明:本文为CSDN博主「歪比巴不」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_56802444/article/details/121444161
一、I2C通信协议
1、I2C协议简介
I2C通讯协议(Inter-Integrated Circuit)是由Phiilps公司开发的一种简单、双向二线制同步串行总线。它只需要两根线即可在连接于总线上的器件之间传送信息。由于它引脚少,硬件实现简单,可扩展性强,不需要 USART、CAN 等通讯协议的外部收发设备,现在被广泛地使用在系统内多个集成电路(IC)间的通讯。
2、I2C的两种工作方式
硬件I2C
直接利用 STM32 芯片中的硬件 I2C 外设。
使用方式:只要配置好对应的寄存器,外设就会产生标准串口协议的时序。在初始化好 I2C 外设后,只需要把某寄存器位置 1,此时外设就会控制对应的 SCL 及 SDA 线自动产生 I2C 起始信号,不需要内核直接控制引脚的电平。
软件I2C
直接使用 CPU 内核按照 I2C 协议的要求控制 GPIO 输出高低电平,从而模拟I2C。
使用方式:需要在控制产生 I2C 的起始信号时,控制作为 SCL 线的 GPIO 引脚输出高电平,然后控制作为 SDA 线的 GPIO 引脚在此期间完成由高电平至低电平的切换,最后再控制SCL 线切换为低电平,这样就输出了一个标准的 I2C 起始信号。
二、代码实现
主要代码分析
AHT20芯片使用:
void read_AHT20_once(void)
{
delay_ms(10);
reset_AHT20();//重置AHT20芯片
delay_ms(10);
init_AHT20();//初始化AHT20芯片
delay_ms(10);
startMeasure_AHT20();//开始测试AHT20芯片
delay_ms(80);
read_AHT20();//读取AHT20采集的到的数据
delay_ms(5);
}
AHT20芯片读取数据
void read_AHT20(void)
{
uint8_t i;
for(i=0; i<6; i++)
{
readByte[i]=0;
}
I2C_Start();//I2C启动
I2C_WriteByte(0x71);//I2C写数据
ack_status = Receive_ACK();//收到的应答信息
readByte[0]= I2C_ReadByte();//I2C读取数据
Send_ACK();//发送应答信息
readByte[1]= I2C_ReadByte();
Send_ACK();
readByte[2]= I2C_ReadByte();
Send_ACK();
readByte[3]= I2C_ReadByte();
Send_ACK();
readByte[4]= I2C_ReadByte();
Send_ACK();
readByte[5]= I2C_ReadByte();
SendNot_Ack();
//Send_ACK();
I2C_Stop();//I2C停止函数
//判断读取到的第一个字节是不是0x08,0x08是该芯片读取流程中规定的,如果读取过程没有问题,就对读到的数据进行相应的处理
if( (readByte[0] & 0x68) == 0x08 )
{
H1 = readByte[1];
H1 = (H1<<8) | readByte[2];
H1 = (H1<<8) | readByte[3];
H1 = H1>>4;
H1 = (H1*1000)/1024/1024;
T1 = readByte[3];
T1 = T1 & 0x0000000F;
T1 = (T1<<8) | readByte[4];
T1 = (T1<<8) | readByte[5];
T1 = (T1*2000)/1024/1024 - 500;
AHT20_OutData[0] = (H1>>8) & 0x000000FF;
AHT20_OutData[1] = H1 & 0x000000FF;
AHT20_OutData[2] = (T1>>8) & 0x000000FF;
AHT20_OutData[3] = T1 & 0x000000FF;
}
else
{
AHT20_OutData[0] = 0xFF;
AHT20_OutData[1] = 0xFF;
AHT20_OutData[2] = 0xFF;
AHT20_OutData[3] = 0xFF;
printf("读取失败!!!");
}
printf("\r\n");
//根据AHT20芯片中,温度和湿度的计算公式,得到最终的结果,通过串口显示
printf("温度:%d%d.%d",T1/100,(T1/10)%10,T1%10);
printf("湿度:%d%d.%d",H1/100,(H1/10)%10,H1%10);
printf("\r\n");
}
三、结果
四、总结
通过使用I2C的方式对AHT20温湿度测量模块进行温湿度的读取,使我对于I2C通信协议的原理有了一定的理解,同时也让我更加熟悉使用I2C进行通信的过程。
五、参考资料
版权声明:本文为CSDN博主「白~云yi#朵#朵~~」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/m0_61824471/article/details/121488164
暂无评论