实现目标
1、掌握IIC总线基础知识;
2、会使用软件模拟IIC总线和使用STM32硬件IIC总线;
3、 学会STM32CubeMX软件关于IIC的配置;
4、掌握SHT20温湿度传感器的驱动;
5、具体目标:(1)用STM32硬件IIC驱动SHT20,上位机串口助手打印温湿度;(2)用软件模拟IIC驱动SHT20,上位机串口助手打印温湿度(只提供代码)。
一、IIC概述
1.1 IIC定义
IIC , 即 I²C,全称 Inter-Integrated Circuit,是I²C Bus简称,中文叫集成电路总线 ,它是一种串行通信总线,使用多主从架构,由飞利浦公司在1980年代为了让主板、嵌入式系统或手机用以连接低速周边设备而发展。
1.2 IIC通信连接
IIC总线只有两根双向信号线。一根是数据线SDA,另一根是时钟线SCL。IIC总线通过上拉电阻接正电源(上拉电阻常用是4.7K,一般在4.7k-10k之间),要求两根线都使用 开漏输出接上拉电阻 的配置。当总线空闲时,两根线均为高电平。连到总线上的任一器件输出的低电平,都将使总线的信号变低,即各器件的SDA及SCL都是线“与”关系。
1.3 IIC特点
- 简单和灵活的连接:占用空间小,芯片管脚的数量少,无需片选信号,价格低。。
- 总线的长度可达7.6m,传送速度:快速 400kbps ,标准 100kbps,高速3.4Mbps。
- 最大主设备数:无限制;最大从机数:理论上是127。
- 信方式为同步半双工,同一时间只可以单向通信。
- 双线制:I2C使用两根信号线进行通信,包括时钟线(SCL)和数据线(SDA)。
- 多从机:多个从机设备可以连接到同一条I2C总线上,每个从机都有唯一的地址。
- 多主机:多个主机设备可以通过I2C总线进行并行通信,通过仲裁机制实现多主机冲突的解决。
1.4 硬件IIC和软件IIC的区别
硬件IIC和软件IIC都是I2C总线通信的方式,但它们实现的方式有所不同:
硬件IIC:硬件IIC是通过专门的硬件模块实现的,通常是通过IIC控制器或者外部IIC芯片来实现的,具有高可靠性、传输速度快、占用CPU资源少等优点。硬件IIC协议实现比较简单,只需要在芯片上添加I2C控制器就可以实现IIC通信,适用于传输数据量较大、传输速度要求较高的场景。
软件IIC:软件IIC是通过软件模拟实现的,通常是在单片机的GPIO上实现的,具有低成本、可扩展性强等优点。软件IIC的实现需要占用CPU资源,因此传输速度相对较慢,可靠性也不如硬件IIC。软件IIC适用于传输数据量较小、传输速度要求不高的场景。
二、IIC协议层
在IIC总线上每一位数据都由一个同步时钟所对应,也就是在SCL串行时钟的配合下,数据在SDA上从高位向低位一次串行传送每一位数据。IIC通信时序图如下:
2.1 数据有效性
IIC 的数据读取动作都在 SCL为高 时产生,SCL为低时是数据改变的时期,无论SDA如何变化都不影响读取。所以,传输数据的过程中,当SCL为高时,数据应当保持稳定,避免数据的采集出错。
2.2 开始信号、结束信号
开始信号:SCL 为高电平时,SDA 由高电平向低电平跳变,开始传送数据。
结束信号:SCL 为高电平时,SDA 由低电平向高电平跳变,结束传送数据。
2.3 字节格式
SDA数据线上的每个字节必须是8位,对于每次传输的字节数没有限制。每个字节(8位)数据传送完后紧跟着应答信号(ACK,第9位)。数据的先后顺序为:高位在前 。
2.4 应答信号
应答信号:主/从机在之后的数据通信中,数据接收方(可能是主机也可能是从机)收到传输的一个字节数据后,需要给出响应,此时处在第九个时钟,发送端释放 SDA 线控制权,将 SDA 电平拉高,由接收方控制。应答信号分为两种:
1)当第9位(应答位)为 低电平 时,为 ACK (Acknowledge) 信号
2)当第9位(应答位)为 高电平 时,为 NACK(Not Acknowledge)信号
2.5 数据传输(以地址7bit为例)
一个完整的7-bit通信过程如图所示:
在START信号后的首字节由7位从机地址和1位读写位组成,结构如下图:
发送完这个字节之后,主机释放SDA总线等待从机给出ACK应答。如果从机给出了ACK应答,表示从机地址正确(有从机响应)并且已知晓是读还是写,便可以开始读写数据。如果从机没有给出ACK应答,则说明接收设备可能没有收到数据(如寻址的设备不存在或是设备正在忙碌)或无法解析收到的数据,如果是这样,则由master来决定如何处理(STOP或ReSTART)。
2.5.1. 主机写-从机收,传输方向不变
主机对从机发送数据时,主机对从机发送一个开始字节,然后即可一直发送数据。以示例来讲解,其第一帧数据为要操作的寄存器地址,所以为:“[1-Byte]开始字节(写) + [1-Byte]寄存器地址 + [1-Byte]寄存器数据”。
2.5.2. 主机读-从机发,传输方向改变
主机对向从机读取数据时,方式同发送数据有所不同,要多一次通信过程。
主机需要先向从机发送一次信号,告诉从机”我要读取数据“,然后重开一次通信,等待从机主动返回数据。以示例来讲解,发送 “[1-Byte]开始字节(写) + [1-Byte]要读取的寄存器的地址”,之后结束通信,或者重开始,来进入到第二次通信中,先发送 [1-Byte]开始字节(读),然后等待读取从机发送过来的 [1-Byte]数据 即可。
三、SHT20概述
3.1 SHT20简介
SHT20, 新一代 Sensirion 湿度和温度传感器。标准 I 2 C 格式。SHT20 配有一个全新设计的 CMOSens®芯片、一个经过改进的电容式湿度传感元件和一个标准的能隙温度传感元件。SHT20的分辨率可以通过输入命令进行改变(8/12bit乃至12/14bit的 RH/T),传感器可以检测到电池低电量状态,并且输出校验和,有助于提高通信的可靠性。
3.2 SHT20 引脚
3.3 SHT20采用的I2C通信,基本命令
开始传输后,随后先传输首字节包括I2C设备地址(7bit)和一个SDA方向位(R:1, W:0)。一个时钟发送一个位,在第8个下降沿之后,通过拉低SDA引脚(ACK位 为0),只是传感器数据接收正常。在发出测量命令之后(‘1110’‘0011’代表温度测量,‘1110’‘0101’代表相对湿度测量 ,这种为主机模式),MCU必须等待测量完成。
3.4 保持主模式测量时序图
测量过程中,SCL线被封锁(由传感器控制),在测量时,SHT2x将SCL拉低强制主机进入等待状态。当释放SCL线,表示传感器内部工作接收,可以继续进行数据传送。
首先发送写地址,再发送命令,然后是读地址,随后等待测量即可,测量时间如下图,传感器内部设置的默认分辨率为相对湿度 12位和温度 14位。45发送NACK时可取消校验。
3.5 无保持主模式测量时序图
量过程中,SCL线是开发状态,可进行其它通信,可以在总线上处理其它I2C总线通信任务。
3.6 软复位
软复位命令是用于无需关闭和再次打开电源的情况下,重新启动传感器系统,重新初始化并恢复默认设置状态。所需要的时间不超过15ms。由MCU发起软复位命令为1111 1110
3.7 温湿度计算
传感器内部设置的默认分辨率为相对湿度12位和温度14位。SDA的输出数据被转换成两个字节的数据包,高字节MSB在前(左对齐)。每个字节后面都跟随一个应答位。两个状态位,即 LSB的后两位在进行物理计算前须置0。
(1)湿度
(2)温度
四、原理图设计
五、STM32CubeMX 配置
5.1 串口USART1配置
USART1:异步模式,波特率115200,功能:用于打印调试信息
5.2 IIC1配置
配置1:主模式特性 和 从模式特性 配置
Master features 主模式特性
I2C Speed Mode: IIC模式设置 快速模式和标准模式。
I2C Clock Speed: I2C传输速率,默认为100KHz
Slave features 从模式特性
Clock No Stretch Mode: 时钟没有扩展模式
IIC时钟拉伸(Clock stretching)
clock stretching通过将SCL线拉低来暂停一个传输.直到释放SCL线为高电平,传输才继续进行.
clock stretching是可选的,实际上大多数从设备不包括SCL驱动,所以它们不能stretch时钟.
Primary Address Length selection: 从设备地址长度 设置从设备的地址是7bit还是10bit 大部分为7bit
Dual Address Acknowledged: 双地址确认
Primary slave address: 从设备初始地址
配置2:开启IIC中断
六、程序设计
6.1 串口重定向
#include "stdio.h"
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *) &ch, 1, 0xFFFF);
return ch;
}
6.2 移植sht20.h文件至工程
#ifndef _SHT20_H_
#define _SHT20_H_
/*SHT20 设备操作相关宏定义,详见手册*/
#define SHT20_ADDRESS 0X40
#define SHT20_Write_Add 0x80
#define SHT20_Read_Add 0x81
#define SHT20_Measurement_RH_HM 0XE5
#define SHT20_Measurement_T_HM 0XE3
#define SHT20_Measurement_RH_NHM 0XF5 //非主机模式地址
#define SHT20_Measurement_T_NHM 0XF3 //非主机模式地址
#define SHT20_READ_REG 0XE7
#define SHT20_WRITE_REG 0XE6
#define SHT20_SOFT_RESET 0XFE
typedef struct
{
float tempreture;
float humidity;
} SHT20_INFO;
extern SHT20_INFO sht20_info;
void SHT20_GetValue(void);
#endif
6.3 移植sht20.c文件至工程
//头文件
#include "stm32f1xx_hal.h"
#include "sht20.h"
#include "i2c.h"
const int16_t POLYNOMIAL = 0x131;
SHT20_INFO sht20_info;
// 函数功能: SHT20复位
void SHT20_reset(void)
{
HAL_I2C_Mem_Write(&hi2c1, SHT20_Write_Add, SHT20_SOFT_RESET, I2C_MEMADD_SIZE_8BIT,(void*)0, 1, 1000);
HAL_Delay(15);
}
//函数功能: SHT20读取用户寄存器
unsigned char SHT20_read_user_reg(void)
{
unsigned char val = 0;
HAL_I2C_Mem_Read(&hi2c1, SHT20_Read_Add, SHT20_READ_REG, I2C_MEMADD_SIZE_8BIT,&val, 1, 1000);
return val;
}
//函数功能: 检查数据正确性
char SHT2x_CheckCrc(char data[], char nbrOfBytes, char checksum)
{
char crc = 0;
char bit = 0;
char byteCtr = 0;
//calculates 8-Bit checksum with given polynomial
for(byteCtr = 0; byteCtr < nbrOfBytes; ++byteCtr)
{
crc ^= (data[byteCtr]);
for ( bit = 8; bit > 0; --bit)
{
if (crc & 0x80) crc = (crc << 1) ^ POLYNOMIAL;
else crc = (crc << 1);
}
}
if(crc != checksum)
return 1;
else
return 0;
}
//* 函数功能: 温度计算
float SHT2x_CalcTemperatureC(unsigned short u16sT)
{
float temperatureC = 0; // variable for result
u16sT &= ~0x0003; // clear bits [1..0] (status bits)
//-- calculate temperature [℃] --
temperatureC = -46.85 + 175.72 / 65536 * (float)u16sT; //T= -46.85 + 175.72 * ST/2^16
return temperatureC;
}
// 函数功能: 湿度计算
float SHT2x_CalcRH(unsigned short u16sRH)
{
float humidityRH = 0; // variable for result
u16sRH &= ~0x0003; // clear bits [1..0] (status bits)
//-- calculate relative humidity [%RH] --
//humidityRH = -6.0 + 125.0/65536 * (float)u16sRH; // RH= -6 + 125 * SRH/2^16
humidityRH = ((float)u16sRH * 0.00190735) - 6;
return humidityRH;
}
//函数功能: 测量温湿度
float SHT2x_MeasureHM(unsigned char cmd, unsigned short *pMeasurand)
{
char checksum = 0; //checksum
char data[2]; //data array for checksum verification
unsigned char addr = 0;
unsigned short tmp = 0;
float t = 0;
HAL_I2C_Mem_Read(&hi2c1, SHT20_Read_Add, cmd, I2C_MEMADD_SIZE_8BIT,data,2, 0xFFFF);
SHT2x_CheckCrc(data, 2, checksum);
tmp = (data[0] << 8) + data[1];
if(cmd == SHT20_Measurement_T_HM)
{
t = SHT2x_CalcTemperatureC(tmp);
}
else
{
t = SHT2x_CalcRH(tmp);
}
if(pMeasurand)
{
*pMeasurand = (unsigned short)t;
}
return t;
}
//函数功能: 获取温湿度数据
void SHT20_GetValue(void)
{
sht20_info.tempreture = SHT2x_MeasureHM(SHT20_Measurement_T_HM, (void *)0);
HAL_Delay(70);
sht20_info.humidity = SHT2x_MeasureHM(SHT20_Measurement_RH_HM, (void *)0);
HAL_Delay(25);
SHT20_reset();
}
6.4 在main.c中打印温湿度值
while (1)
{
SHT20_GetValue();
printf( "Temperature:%0.1fC, Humidity:%0.1f%%\r\n", sht20_info.tempreture,
sht20_info.humidity);
HAL_Delay(1000);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}