本例使用I2C协议读取AHT20温湿度传感器的值,并将读取到的数据通过串口打印出来。
I2C 通讯协议(Inter-Integrated Circuit)是由Phiilps公司开发的,由于它引脚少,硬件实现简单,可扩展性强,不需要USART、CAN等通讯协议的外部收发设备,现在被广泛地使用在系统内多个集成电路(IC)间的通讯。
I2C总线主要的优点是其简单性和有效性。由于接口直接在组件之上,因此I2C总线占用的空间非常小,减少了电路板的空间和芯片管脚的数量,降低了互联成本。
I2C总线的另一个优点是,它支持多主控(multimastering), 其中任何能够进行发送和接收的设备都可以成为主总线。一个主控能够控制信号的传输和时钟频率。当然,在任何时间点上只能有一个主控。
它是一个支持多设备的总线。“总线”指多个设备共用的信号线。在一个I2C通讯总线中,可连接多个I2C通讯设备,支持多个通讯主机及多个通讯从机。
一个I2C总线只使用两条总线线路,一条双向串行数据线(SDA) ,一条串行时钟线(SCL)。数据线即用来表示数据,时钟线用于数据收发同步。
每个连接到总线的设备都有一个独立的地址,主机可以利用这个地址进行不同设备之间的访问。
总线通过上拉电阻接到电源。当I2C设备空闲时,会输出高阻态,而当所有设备都空闲,都输出高阻态时,由上拉电阻把总线拉成高电平。
多个主机同时使用总线时,为了防止数据冲突,会利用仲裁方式决定由哪个设备占用总线。
具有三种传输模式:标准模式传输速率为100kbit/s ,快速模式为400kbit/s ,高速模式下可达3.4Mbit/s,但目前大多I2C设备尚不支持高速模式。
引用来源(野火参考资料)
I2C的协议定义了通讯的起始和停止信号、数据有效性、响应、仲裁、时钟同步和地址广播等环节。
其中,按照是否使用I2C基本外设可分为
软件模拟协议:使用CPU直接控制通讯引脚的电平,产生出符合通讯协议标准的逻辑。
硬件实现协议:由STM32的I2C片上外设专门负责实现I2C通讯协议,只要配置好该外设,它就会自动根据协议要求产生通讯信号,收发数据并缓存起来,CPU只要检测该外设的状态和访问数据寄存器,就能完成数据收发。这种由硬件外设处理I2C协议的方式减轻了CPU的工作,且使软件设计更加简单。
本例我们主要软件I2C为例,详细分析I2C的通讯格式。
CRC[7:0]=1+x4+x5+x8AHT20
本例是以AHT20温湿度传感器读取数据为例,主要分析在I2C通讯协议的起始与停止信号、应答/非应答信号,数据有效性、数据读写操作等。
AHT20例程
关于AHT20例程文件的移植的详细讲解,可参考
STM32基于IIC的温湿度采集(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的每个时钟周期传输一位数据。
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传感器写数据为例)
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 的所有硬件架构都是根据图中左侧 SCL 线和 SDA 线展开的 。 STM32 芯片有多个 I2C 外设,它们的 I2C 通讯信号引出到不同的 GPIO 引脚上,使用时必须配置到这些指定的引脚,见表 STM32F10x 的 I2C 引脚 。
关于I2C的GPIO引脚的复用功能,参考如下
SCL 线的时钟信号,由 I2C 接口根据时钟控制寄存器 (CCR) 控制,控制的参数主要为时钟频率。
配置 I2C 的 CCR 寄存器可修改通讯速率相关的参数:
选择不同的模式,可以配置I2C不同的通讯速率。
I2C 的 SDA 信号主要连接到数据移位寄存器上,数据移位寄存器的数据来源及目标是数据寄存器 (DR)、地址寄存器 (OAR)、 PEC 寄存器以及 SDA 数据线。当向外发送数据的时候,数据移位寄存器以“数据寄存器”为数据源,把数据一位一位地通过 SDA 信号线发送出去;当从外部接收数据的时候,数据移位寄存器把 SDA 信号线采样到的数据一位一位地存储到“数据寄存器”中。
若使能了数据校验,接收到的数据会经过 PCE 计算器运算,运算结果存储在“PEC 寄存器”中。 当 STM32 的 I2C 工作在从机模式的时候,接收到设备地址信号时,数据移位寄存器会把接收到 的地址与 STM32 的自身的“I2C 地址寄存器”的值作比较,以便响应主机的寻址。 STM32 的自 身 I2C 地址可通过修改“自身地址寄存器”修改,支持同时使用两个 I2C 设备地址,两个地址分 别存储在 OAR1 和 OAR2 中。
整体控制逻辑负责协调整个 I2C 外设,控制逻辑的工作模式根据我们配置的“控制寄存器(CR1/CR2)”的参数而改变。在外设工作时,控制逻辑会根据外设的工作状态修改“状态寄存器 (SR1 和 SR2)”,我们只要读取这些寄存器相关的寄存器位,就可以了解 I2C 的工作状态。
除此之外,控制逻辑还根据要求,负责控制产生 I2C 中断信号、 DMA 请求及各种 I2C 的通讯信号(起始、停止、响应信号等)。
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产品规格书》
基于I2C硬件协议的AHT20温湿度数据采集
STM32基于IIC的温湿度采集(AHT20)
IIC原理超详细讲解—值得一看
STM32下基于IIC协议的AHT20温湿度采集