STM32F103ZET6+IIC+SHT20温湿度传感
IIC概述
IIC:两线式串行总线,它是由数据线SDA和时钟线SCL构成的串行总线,可发送和接收数据。
在CPU与被控IIC之间、IIC与IIC之间进行双向传送,高速IIC总线一般可达400kbs以上。总线上可以接多个从设备,从设备的地址必须不同。也可也接多个主设备,但同一时刻只能有一个主设备控制总线。最大设备数量受总线的最大负载电容400pf限制。每个从设备有自己的设备地址,主设备发送START信号后,紧跟着发送想要通信的从设备地址字节,7位器件地址+1位读写标志位(0为写,1为读)。
时钟线SCL:在通信过程起到控制作用。
数据线SDA:用来一位一位的传送数据。
IIC总共由五个核心函数,分别为:①起始信号②停止信号③应答信号④发送数据⑤接收数据,通过这五个核心基本函数就能于大多数的传感进行通信了。
起始信号
当SCL为高电平期间,SDA由高电平到低电平的跳变过程;起始信号是一种电平跳变时序信号,而不是一个电平信号,如图虚线框所示。
停止信号
当SCL为高电平期间,SDA由低电平到高电平的跳变过程;停止信号也是一种电平跳变时序信号,而不是一个电平信号,如图虚线框所示。
应答信号
IIC的数据字节定义为8位长,对于发送端每发送1个字节后,需要将数据线(SDA)释放,由接收端反馈一个应答信号(ACK)。应答信号为低电平时,则将其规定为有效信号(ACK简称应答位),表示接收端已经成功接收了该字节;应答位为高电平时,规定为非应答位(NACK),一般表示接收端没有成功接收该字节。
对于反馈有效应答位ACK的要求是,接收端在第9个时钟脉冲之前的低电平期间将SDA线拉低,并且确保在该时钟的高电平期间为稳定的低电平。如果接收端是主机,则在它接收到最后一个字节后,发送一个NACK信号,以通知发送端结束数据发送,并释放SDA线,以便主机接收端发送一个停止信号。
发送数据
在发送起始信号后开始通信,主机发送一个8位数据。然后,主机释放SDA线并等待从从机发出得确认信号(ACK)。
接收数据
在发送起始信号后开始通信,主机发送一个8位数据。然后,从机收到数据返回一个确认信号(ACK)给主机,这时候主机才开始接收数据,待主机接收数据完成后,发送一个NACK信号给从机,以通知接收端结束数据接收。
数据有效性
IIC总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。
IIC通信总过程
大概地了解了一下,IIC协议的基本情况,接下来,使用STM32驱动SHT20温湿度传感器为例,熟悉一下IIC协议的应用。
准备IIC协议
1、声明GPIO和IIC初始化
void SHT20_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(SHT20_SCL_GPIO_CLK | SHT20_SDA_GPIO_SDA, ENABLE);
GPIO_InitStructure.GPIO_Pin = SHT20_SCL_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(SHT20_SCL_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = SHT20_SDA_GPIO_PIN;
GPIO_Init(SHT20_SDA_GPIO_PORT, &GPIO_InitStructure);
}
2、IIC数据线的输出模式
因为在IIC数据线输出数据前,需要将数据线设置为输出模式才行
void SDA_OUT(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = SHT20_SDA_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(SHT20_SDA_GPIO_PORT, &GPIO_InitStructure);
}
3、IIC数据线的输入模式
同样在IIC数据线输入数据前,需要将数据线设置为输入模式才行
void SDA_IN(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = SHT20_SDA_GPIO_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(SHT20_SDA_GPIO_PORT, &GPIO_InitStructure);
}
4、IIC起始信号
void IIC_Start(void)
{
SDA_OUT();
SHT20_SDA = 1;
SHT20_SCL = 1;
delay_us(20);
SHT20_SDA = 0;
delay_us(20);
SHT20_SCL = 0;
}
5、IIC停止信号
void IIC_Stop(void)
{
SDA_OUT();
SHT20_SCL = 0;
SHT20_SDA = 0;
delay_us(20);
SHT20_SCL = 1;
SHT20_SDA = 1;
delay_us(20);
}
6、IIC等待应答信号
返回值:1,接收应答失败;0,接收应答成功。
u8 IIC_Wait_Ack(void)
{
u8 ucErrTime = 0;
SDA_IN();
SHT20_SDA = 1;
delay_us(10);
SHT20_SCL = 1;
delay_us(10);
while(SHT20_SDA_READ())
{
ucErrTime++;
if(ucErrTime > 250)
{
IIC_Stop();
return 1;
}
}
SHT20_SCL = 0;
return 0;
}
7、IIC产生应答信号
void IIC_Ack(void)
{
SHT20_SCL = 0;
SDA_OUT();
SHT20_SDA = 0;
delay_us(20);
SHT20_SCL = 1;
delay_us(20);
SHT20_SCL = 0;
}
8、IIC不产生应答信号
void IIC_NAck(void)
{
SHT20_SCL = 0;
SDA_OUT();
SHT20_SDA = 1;
delay_us(20);
SHT20_SCL = 1;
delay_us(20);
SHT20_SCL = 0;
}
9、IIC发送一个字节
返回从机有无应答,1,有应答;0,无应答。
void IIC_Send_Byte(u8 txd)
{
u8 t;
SDA_OUT();
SHT20_SCL = 0;
for(t = 0; t < 8; t++)
{
if((txd & 0x80) >> 7)
SHT20_SDA = 1;
else
SHT20_SDA = 0;
txd <<= 1;
delay_us(20);
SHT20_SCL = 1;
delay_us(20);
SHT20_SCL = 0;
delay_us(20);
}
delay_us(20);
}
10、IIC读取一个字节
读取字节时,ask=1,产生应答;ask=0,不产生应答
u16 IIC_Read_Byte(u8 ack)
{
u8 i;
u16 receive = 0;
SDA_IN();
for(i = 0; i < 8; i++ )
{
SHT20_SCL = 0;
delay_us(20);
SHT20_SCL = 1;
receive <<= 1;
if(SHT20_SDA_READ())
receive++;
delay_us(20);
}
if (!ack)
IIC_NAck();
else
IIC_Ack();
return receive;
}
温湿度传感器的驱动过程
有两种不同的操作模式与传感器通信:保持主模式或无保持主模式。在第一种情况下,SCL 线路在测量过程中阻塞(由传感器控制),而在后一种情况下,在传感器处理测量时,SCL线路保持打开状态,以进行其他通信。在传感器测量时,无保持主2模式允许处理总线上的其他 I 2 C 通信任务。
在保持主模式下,SHT20 在测量时拉下 SCL 线以等待状态。通过释放 SCL 线路 ,传感器指示内部处理已终止,并且传输可能继续。
在无保持主模式下,MCU 必须轮询,以终止 Sensor 的内部处理。这是通过发送开始条件后跟 II C 标头完成的,如果内部处理完成,传感器将确认 MCU 的轮询,MCU 可以读取数据。如果测量处理未完成,传感器应答无 ACK 位,并且必须再次发出"启动"条件。使用无保持主模式 mode时,建议在接收传感器的 ACK 位后包括 20 μs的等待时间,并处于"停止"状态之前。
驱动代码如下:
float ReadSht20(unsigned char whatdo)
{
float temp;
unsigned char MSB,LSB;
float Humidity,Temperature;
IIC_Start();
IIC_Send_Byte(0x80);
if(IIC_Wait_Ack()==0)
{
IIC_Send_Byte(whatdo);
if(IIC_Wait_Ack()==0)
{
do
{
delay_us(8);
IIC_Start();
IIC_Send_Byte(0x81);
}while(IIC_Wait_Ack()==1);
MSB = IIC_Read_Byte(1);
LSB = IIC_Read_Byte(1);
IIC_Read_Byte(0);
IIC_Stop();
LSB &= 0xfc;
temp = MSB*256 + LSB;
if (whatdo==((unsigned char)0xf5))
{
Humidity =(temp*125)/65536-6;
return Humidity;
}
else
{
Temperature = (temp*175.72)/65536-46.85;
return Temperature;
}
}
}
return 0;
}
在之前的USART工程上,新建两个文件,一个是SHT20.h文件,另一个是SHT20.c文件
SHT20.h文件的代码如下:
#ifndef __HX711_H
#define __HX711_H
#include "sys.h"
#include "delay.h"
#define SHT20_SCL PEout(11)
#define SHT20_SDA PEout(12)
// GPIO 引脚定义
#define SHT20_SCL_GPIO_CLK RCC_APB2Periph_GPIOE
#define SHT20_SCL_GPIO_PORT GPIOE
#define SHT20_SCL_GPIO_PIN GPIO_Pin_11
#define SHT20_SDA_GPIO_SDA RCC_APB2Periph_GPIOE
#define SHT20_SDA_GPIO_PORT GPIOE
#define SHT20_SDA_GPIO_PIN GPIO_Pin_12
#define SHT20_SDA_READ() GPIO_ReadInputDataBit(SHT20_SDA_GPIO_PORT, SHT20_SDA_GPIO_PIN)
//IIC 所有操作函数
void SHT20_GPIO_Init(void);
void SDA_OUT(void);
void SDA_IN(void);
void IIC_Start(void);
void IIC_Stop(void);
u8 IIC_Wait_Ack(void);
void IIC_Ack(void);
void IIC_NAck(void);
void IIC_Send_Byte(u8 txd);
u16 IIC_Read_Byte(u8 ack);
float ReadSht20(unsigned char whatdo);
#endif
SHT20.c文件的代码如下:
#include "SHT20.h"
void SHT20_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(SHT20_SCL_GPIO_CLK | SHT20_SDA_GPIO_SDA, ENABLE);
GPIO_InitStructure.GPIO_Pin = SHT20_SCL_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(SHT20_SCL_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = SHT20_SDA_GPIO_PIN;
GPIO_Init(SHT20_SDA_GPIO_PORT, &GPIO_InitStructure);
}
void SDA_OUT(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = SHT20_SDA_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(SHT20_SDA_GPIO_PORT, &GPIO_InitStructure);
}
void SDA_IN(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = SHT20_SDA_GPIO_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(SHT20_SDA_GPIO_PORT, &GPIO_InitStructure);
}
void IIC_Start(void)
{
SDA_OUT();
SHT20_SDA = 1;
SHT20_SCL = 1;
delay_us(20);
SHT20_SDA = 0;
delay_us(20);
SHT20_SCL = 0;
}
void IIC_Stop(void)
{
SDA_OUT();
SHT20_SCL = 0;
SHT20_SDA = 0;
delay_us(20);
SHT20_SCL = 1;
SHT20_SDA = 1;
delay_us(20);
}
u8 IIC_Wait_Ack(void)
{
u8 ucErrTime = 0;
SDA_IN();
SHT20_SDA = 1;
delay_us(10);
SHT20_SCL = 1;
delay_us(10);
while(SHT20_SDA_READ())
{
ucErrTime++;
if(ucErrTime > 250)
{
IIC_Stop();
return 1;
}
}
SHT20_SCL = 0;
return 0;
}
void IIC_Ack(void)
{
SHT20_SCL = 0;
SDA_OUT();
SHT20_SDA = 0;
delay_us(20);
SHT20_SCL = 1;
delay_us(20);
SHT20_SCL = 0;
}
void IIC_NAck(void)
{
SHT20_SCL = 0;
SDA_OUT();
SHT20_SDA = 1;
delay_us(20);
SHT20_SCL = 1;
delay_us(20);
SHT20_SCL = 0;
}
void IIC_Send_Byte(u8 txd)
{
u8 t;
SDA_OUT();
SHT20_SCL = 0;
for(t = 0; t < 8; t++)
{
if((txd & 0x80) >> 7)
SHT20_SDA = 1;
else
SHT20_SDA = 0;
txd <<= 1;
delay_us(20);
SHT20_SCL = 1;
delay_us(20);
SHT20_SCL = 0;
delay_us(20);
}
delay_us(20);
}
u16 IIC_Read_Byte(u8 ack)
{
u8 i;
u16 receive = 0;
SDA_IN();
for(i = 0; i < 8; i++ )
{
SHT20_SCL = 0;
delay_us(20);
SHT20_SCL = 1;
receive <<= 1;
if(SHT20_SDA_READ())
receive++;
delay_us(20);
}
if (!ack)
IIC_NAck();
else
IIC_Ack();
return receive;
}
float ReadSht20(unsigned char whatdo)
{
float temp;
unsigned char MSB,LSB;
float Humidity,Temperature;
IIC_Start();
IIC_Send_Byte(0x80);
if(IIC_Wait_Ack()==0)
{
IIC_Send_Byte(whatdo);
if(IIC_Wait_Ack()==0)
{
do
{
delay_us(8);
IIC_Start();
IIC_Send_Byte(0x81);
}while(IIC_Wait_Ack()==1);
MSB = IIC_Read_Byte(1);
LSB = IIC_Read_Byte(1);
IIC_Read_Byte(0);
IIC_Stop();
LSB &= 0xfc;
temp = MSB*256 + LSB;
if (whatdo==((unsigned char)0xf5))
{
Humidity =(temp*125)/65536-6;
return Humidity;
}
else
{
Temperature = (temp*175.72)/65536-46.85;
return Temperature;
}
}
}
return 0;
}
main.c文件的代码如下:
#include "stm32f10x.h"
#include <stdio.h>
#include "led.h"
#include "USART_Init_Config.h"
#include "SHT20.h"
int main(void)
{
float Humidity,Temperature; //定义温度和湿度变量
delay_init(); //延时函数初始化
LED_Init(); //LED灯初始化
USART_Init_Config(); //串口初始化
SHT20_GPIO_Init(); //SHT20初始化
while(1)
{
Humidity = ReadSht20(0xf3); //读取温度
printf("温度为:%0.2f",Humidity);
Temperature = ReadSht20(0xf5); //读取湿度
printf("湿度为:%0.2f",Temperature);
delay_ms(1000);
}
}
运行效果
打开串口调试助手
完整工程下载!!!