嵌入式系统开发环境下,I2C总线通信协议的理解及通过I2C实现温湿度(AHT20)采集的过程分析

学习I2C总线通信协议,实现每隔2秒钟采集一次温湿度数据,并通过串口发送到上位机(win10)



前言

  • 我们为什么要学习I2C通信?
    1、Stm32的最常用的板间通信有很多,有I2C、SPI、CAN;I2C通信协议是我们stm32板间通信比较常用的、也是比较简单的;
    2、I2C通讯协议(Inter-Integrated Circuit)引脚少,硬件实现简单,可扩展性强,不需要USART、CAN等通讯协议的外部收发设备,现在被广泛地使用在系统内多个集成电路(IC)间的通讯

一、题目要求

  • 学习I2C总线通信协议,使用STM32F103完成基于I2C协议的AHT20温湿度传感器的数据采集,并将采集的温度-湿度值通过串口输出。具体任务:
    (1)解释什么是“软件I2C”和“硬件I2C”? (阅读野火配套教材的第23章“I2C–读写EEPROM”原理章节);
    (2)阅读AHT20数据手册,编程实现:每隔2秒钟采集一次温湿度数据,并通过串口发送到上位机(win10)

二、I2C总线通信协议

1. I2C总线简介

1.1 原理图

  • 原理图如下(注意上拉电阻):
    在这里插入图片描述

1.2 特点

  • 只需要一根数据线SDA和一根时钟线SCL,SDA(串行数据线)和SCL(串行时钟线)都是双向I/O线
    SCL(Serial Clock):串行时钟线,传输CLK信号,一般是主设备向从设备提供
    SDA(Serial Data) :串行数据线,传输通信数据

  • 实现真正的多主机总线,任何器件既可以作为主机又可以作为从机,但是同一时刻只能有一个主机

  • 可以通过外部连线检测,便于系统故障诊断和调试

  • 连接到相同总线上的IC数量只受总线最大电容的限制,串行的8位双向数据传输位速率在标准模式下可达100Kbit/s,快速模式下可达400Kbit/s,高速模式下可达3.4Mbit/s

  • 总线上消耗的电流很小,因此,总线上扩展的器件数量主要由电容负载来决定,抗高噪声干扰,增加总线驱动器可以使总线电容扩大10倍,传输距离达到15m;兼容不同电压等级的器件,工作温度范围宽

  • 接口电路为开漏输出,需通过上拉电阻接电源VCC,当总线空闲时,两根线都是高电平,连接总线的外同器件都是CMOS器件输出级也是开漏电路

1.3 数据传输

  • 发送到SDA线上的每个字节必须为8位,每次传输可以发送的字节数量不受限制;每个字节后必须跟一个响应位。首先传输的是数据的最高位(MSB),如果从机要完成一些其他功能后(例如一个内部中断服务程序)才能接收或发送下一个完整的数据字节,可以使时钟线SCL保持低电平,迫使主机进入等待状态,当从机准备好接收下一个数据字节并释放时钟线SCL后数据传输继续:
    在这里插入图片描述
  • 空闲时候:SDA数据线和SCL时钟线都是高电平
  • 开始信号:SCL处于高电平,SDA由高到低
  • 结束信号:SCL处于高电平,SDA由低到高
  • 应答信号:当传输完8位数据以后,在第9个SCL时钟周期
  • 主机释放SDA控制权交给从机,由于上拉电阻的作用,此时该电平为高,但是如果从机正确接受了数据,就会将SDA拉低
  • 发送数据:SDA上的数据必须在SCL高电平周期时保持稳定,数据的高低电平翻转变化发生在SCL低电平时期
  • 非应答信号:如果第9个SCL时钟周期,SDA保持高电平,则代表非应该信号
  • 非应答信号可能是主机发出的也可能是从机产生的
    有几种可能:
    1、I2C总线上没有主机所指定地址的从机设备
    2、从机正在执行一些操作,处于忙状态,还没有准备好与主机通讯
    3、主机发送的一些控制命令,从机不支持
    4、主机接收从机数据时,主机产生非应答信号,通知从机数据传输结束,不要再发数据了

1.4 通讯特征

  • 1)串行通信:所有的数据以位为单位在SDA线上串行传输
  • 2)同步通信:通过时钟同步
  • 3)非差分:I2C通信速率不高,且通信距离近,使用电平信号通信
  • 4)低速率:I2C一般是同一个板子上的两个IC芯片间通信,数据量不大,速率低;速率:几百KHz,速率可能不同,不能超过IC的最高速率

1.5 模式

  • 标准模式(Standard):100kbps
  • 快速模式(Fast):400kbps
  • 快速模式(Fast-Plus):1Mbps
  • 高速模式(High-speed):3.4Mbps
  • 超快模式(Ultra-Fast):5Mbps(单向传输)

2. I2C通信协议简介

2.1 起始位和结束位

  • I2C总线通讯由起始位开始通讯,由结束位停止通讯,并释放I2C总线。起始位和结束位都由主设备发出
    起始位(S):在SCL为高电平时,SDA由高电平变为低电平
    结束位(P):在SCL为高电平时,SDA由低电平变为高电平

    如下图所示:
    在这里插入图片描述

2.2 数据格式与应答

  • I2C数据以字节(即8bits)为单位传输,每个字节传输完后都会有一个ACK应答信号;应答信号的时钟是由主设备产生的

  • 应答(ACK):拉低SDA线,并在SCL为高电平期间保持SDA线为低电平
    非应答(NOACK):不要拉低SDA线(此时SDA线为高电平),并在SCL为高电平期间保持SDA线为高电平

  • 在传输期间,如果从设备来不及处理主设备发送的数据,从设备会保持SCL线为低电平,强迫主设备等待从设备释放SCL线,直到从设备处理完后,释放SCL线,接着进行数据传输

    如下图所示:
    在这里插入图片描述

2.3 传输通讯流程

  • 写数据
    开始数据传输后,先发送一个起始位(S),主设备发送一个地址数据(由7bit的从设备地址,和最低位的写标志位组成的8bit字节数据,该读写标志位决定数据的传输方向),然后,主设备释放SDA线,并等待从设备的应答信号(ACK)。每一个字节数据的传输都要跟一个应答信号位。数据传输以停止位(P)结束,并且释放I2C总线
  • 读数据
    开始通讯时,主设备先发送一个起始信号(S),主设备发送一个地址数据(由7bit的从设备地址,和最低位的写标志位组成的8bit字节数据),然后,主设备释放SDA线,并等待从设备的应答信号(ACK),从设备应答主设备后,主设备再发送要读取的寄存器地址,从设备应答主设备(ACK),主设备再次发送起始信号(Sr),主设备发送设备地址(包含读标志),从设备应答主设备,并将该寄存器的值发送给主设备
  • 读取单字节数据
    主设备要读取的数据,如果是只有一个字节的数值,就要结束应答,主设备要先发送一个非应答信号(NOACK),再发送结束信号(P)
  • 读取多字节数据
    主设备要读取的数据,如果是大于一个字节的多个数据,就发送ACK应答信号(ACK),而不是非应答信号(NOACK),然后主设备再次接收从设备发送的数据,依次类推,直到主设备读取的数值是最后一个字节数据后,需要主设备给从设备发送非应答信号(NOACK),再发送结束信号(P),结束I2C通讯,并释放I2C总线
    在这里插入图片描述
  • 注意:所有的数据传输过程中,SDA线的电平变化必须在SCL为低电平时进行,SDA线的电平在SCL线为高电平时要保持稳定不变
    如下图所示:
    在这里插入图片描述

2.4 I2C物理层

  • 物理层的特点:
    (1) 它是一个支持设备的总线。“总线”指多个设备共用的信号线。在一个 I2C 通讯总线中,可连接多个 I2C 通讯设备,支持多个通讯主机及多个通讯从机;
    (2) 一个 I2C 总线只使用两条总线线路,一条双向串行数据线(SDA) ,一条串行时钟线(SCL),数据线即用来表示数据,时钟线用于数据收发同步;
    (3) 每个连接到总线的设备都有一个独立的地址,主机可以利用这个地址进行不同设备之间的访问;
    (4) 总线通过上拉电阻接到电源,当 I2C 设备空闲时,会输出高阻态,而当所有设备都空闲,都输出高阻态时,由上拉电阻把总线拉成高电平;
    (5) 多个主机同时使用总线时,为了防止数据冲突,会利用仲裁方式决定由哪个设备占用总线;
    (6) 具有三种传输模式:标准模式传输速率为 100kbit/s ,快速模式400kbit/s ,高速模式下可达 3.4Mbit/s,但目前大多 I2C 设备尚不支持高速模式;
    (7) 连接到相同总线的 IC 数量受到总线的最大电容 400pF 限制

2.5 I2C协议层

  • I2C 的协议定义了通讯的起始和停止信号、数据有效性、响应、仲裁、时钟同步和地址广播等环节

  • I2C 通讯过程的基本结构
    在这里插入图片描述

  • 通讯的起始和停止信号
    在这里插入图片描述

  • 数据有效性
    在这里插入图片描述

  • 每次数据传输都以字节为单位,每次传输的字节数不受限制
    地址及数据方向
    在这里插入图片描述

  • 响应
    在这里插入图片描述

3. 硬件I2C和软件I2C

  • 所谓硬件I2C对应芯片上的I2C外设,有相应I2C驱动电路,其所使用的I2C管脚也是专用的;软件I2C一般是用GPIO管脚,用软件控制管脚状态以模拟I2C通信波形;

  • 硬件I2C的效率要远高于软件的,而软件I2C由于不受管脚限制,接口比较灵活;

  • 模拟I2C 是通过GPIO,软件模拟寄存器的工作方式,而硬件(固件)I2C是直接调用内部寄存器进行配置。如果要从具体硬件上来看,可以去看下芯片手册。因为固件I2C的端口是固定的,所以会有所区别;

  • 至于如何区分它们?
    1、可以看底层配置,比如IO口配置,如果配置了IO口的功能(I2C功能)那就是固件I2C,否则就是模拟;
    2、可以看I2C写函数,看里面有木有调用现成的函数或者给某个寄存器赋值,如果有,则肯定是固件I2C功能,没有的话肯定是数据一个bit一个bit模拟发生送的,肯定用到了循环,则为模拟;
    3、根据代码量判断,模拟的代码量肯定比固件的要大:
    1)硬件I2C用法比较复杂,模拟I2C的流程更清楚一些。
    2)硬件I2C速度比模拟快,并且可以用DMA
    3)模拟I2C可以在任何管脚上,而硬件只能在固定管脚上。

  • 软件i2c是程序员使用程序控制SCL,SDA线输出高低电平,模拟i2c协议的时序;一般较硬件i2c稳定,但是程序较为繁琐,但不难

  • 硬件i2c程序员只要调用i2c的控制函数即可,不用直接的去控制SCL,SDA高低电平的输出;但是有些单片机的硬件i2c不太稳定,调试问题较多

三、项目工程创建

1. 实验要求

  • 阅读AHT20数据手册,编程实现:每隔2秒钟采集一次温湿度数据,并通过串口发送到上位机(win10)

2. 核心代码分析

  • 可以根据其他公司提供的示例代码进行修改与代码添加。(如正点原子或者野火)
  • 相关的AHT20资料大家可以去官网进行查看与下载: http://www.aosong.com/class-36.html
  • AHT20芯片的使用过程read_AHT20_once函数:
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芯片读取数据 read_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");
}
  • 工程一览:
    在这里插入图片描述

3. 项目完整代码

  • 可跳转至本人的gitee代码仓库查看:
    https://gitee.com/baogu_xuxu/embedded-system-development
    在这里插入图片描述

四、实验结果展示

1. 温湿度采集器引脚一览表

  • 引脚切勿连接错误,以免发生危险:
    在这里插入图片描述

2. 硬件线路连接

2.1 usb to ttl ----> STM32F103C8T6核心开发板

  • 3V3 —> 3V3
  • GND —> GND
  • RXD —> A9
  • TXD —> A10
    在这里插入图片描述

2.2 STM32F103C8T6核心开发板 ----> 温湿度采集器

  • 3V3 —> 引脚1
  • GND —> 引脚3
  • PB6 —> SCL
  • PB7 —> SDA
    在这里插入图片描述

3. 工程文件编译及烧录

  • 工程烧录:
    在这里插入图片描述
  • 打开FlyMcu:
    在这里插入图片描述

4. 结果展示

  • 串口结果展示:
    在这里插入图片描述

  • 可以用手握住收集器,观察输出温湿度的变化:
    在这里插入图片描述

  • 视频整体效果展示:

    通过I2C实现温湿度(AHT20)采集


总结

通过本次实验课程,掌握了嵌入式系统开发环境下,对于I2C总线通信协议的理解,以及通过I2C协议实现温湿度(AHT20)采集的过程分析;
路途是艰辛的,前途是光明的,在实操过程中,由于代码是在开源基础上进行修正与改进的,所以出现了很多的问题,前前后后进行了不下五次的代码修改与调试,好在最终还是完满达到了实验要求!
同时也期待大家能够积极留言,指出我存在的问题,谢谢!

参考文献:
https://blog.csdn.net/u011727389/article/details/84858378
https://blog.csdn.net/qq_20017379/article/details/120635348

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值