系列文章目录
文章目录
- 系列文章目录
- 前言
- 一、SHT11时序
-
- 1.传输时序
- 2.通信复位顺序
- 发送一个字节
- 接收一个字节
- 5.测量温度和湿度
- 6.温湿度补偿
- 二、实现仿真
-
- 三、代码
- 四、总结
前言
首先,为了写作,声明SHT11.我走了很多弯路,看了时间序列,参考了在线程序。最后,我花了大约两天的时间来了解如何实现它。虽然有错误,但我还是收获了很多。网上关于这个芯片的代码太少,要么不完整,要么花钱。今天分享以下内容。练习如何通过时间序列图自己写程序真的很难。
一、SHT11时序
1.传输时序
首先,数据线上升,时钟线下降。然后时钟线上升,数据线下降,时钟线下降,时钟线上升,数据线上升,时钟线下降。
void I2C_Start() {
I2C_SDA=1; I2C_SCL=0; I2C_SCL=1; I2C_SDA=0; I2C_SCL=0; I2C_SCL=1; I2C_SDA=1; I2C_SCL=0; }
2.通信复位顺序
这也很容易理解
void Reset() {
unsigned char i; I2C_SDA=1; I2C_SCL=0; for(i=0;i<9;i ) {
I2C_SCL=1; I2C_SCL=0; } I2C_Start(); }
发送一个字节
首先发送字节(8位数据),然后接收从机响应信号和通常I2C时间顺序相似,但最终响应不同。必须注意的是,当时钟线为低电时,数据线只能在平时更改,即当时钟线下降时,数据线准备数据(这里是主机准备数据)。当时钟线上升时,从机器上读取数据线上的数据
char I2C_SendByte(unsigned char byte) {
unsigned char i; char error=0; I2C_SCL=0; //拉低时钟线,目的是让主机准备数据,数据线只能在时钟为低电平时变化 for(i=0;i<8;i++) {
if(byte&(0X80>>i))//主机准备数据 I2C_SDA=1; else I2C_SDA=0; I2C_SCL=1;//从机读取数据,只有在时钟为高电平时,才会读取数据 I2C_SCL=0; } I2C_SDA=1;//主机释放SDA //从机准备应答数据 I2C_SCL=1; error=I2C_SDA;//主机读取应答数据 I2C_SCL=0;//这一点最特殊,这是第9个时钟下降沿 I2C_SDA=1;//从机释放SDA return error; }
4、接收一个字节
主机接收一个字节,然后主机发送应答信号。接收字节与发送同理。时钟线低电平时,从机准备数据,时钟线高电平时,主机读取数据。
unsigned char I2C_ReceiveByte(bit ACK)
{
unsigned char i,byte=0X00;
I2C_SCL=0;//可加可不加,加上容易理解
I2C_SDA=1;//主机释放总线,从机准备好数据
for(i=0;i<8;i++)
{
I2C_SCL=1;
if(I2C_SDA)//主机读取从机数据
byte|=(0X80>>i);
I2C_SCL=0;
}
I2C_SDA=ACK;//主机准备好应答数据
I2C_SCL=1;//从机读取应答数据
I2C_SCL=0;
I2C_SDA=1;//主机释放总线,可加可不加
return byte;
}
5、测量温度湿度
参考前人程序
char s_measure(unsigned char *p_value,unsigned char mode)
{
char error=0;
unsigned int i;
I2C_Start();
switch(mode)
{
//选择发送命令
case 0 : error+=I2C_SendByte(0X03); break;
case 1 : error+=I2C_SendByte(0X05); break;
default : break;
}
for (i=0;i<65535;i++)
if(I2C_SDA==0)
break; //等待测量结束,测量温湿度需要时间
if(I2C_SDA)
error+=1; // 如果长时间数据线没有拉低,说明测量错误
*(p_value) =I2C_ReceiveByte(0); //读第一个字节,高字节 (MSB)
*(p_value+1)=I2C_ReceiveByte(1); //读第二个字节,低字节 (LSB)
//这里发送非应答信号,不用CRC校验
return error;
}
6、温湿度补偿
传入两个指针,下面是数据手册公式
void calc_SHT11(float *p_humidity ,float *p_temperature) //const表示常量,不允许修改里面的内容
{
const float C1=-4.0; // 12位湿度精度 修正公式
const float C2=+0.0405; // 12位湿度精度 修正公式
const float C3=-0.0000028; // 12位湿度精度 修正公式
const float T1=+0.01; // 14位温度精度 5V条件 修正公式
const float T2=+0.00008; // 14位温度精度 5V条件 修正公式
float rh=*p_humidity; // rh: Humidity 12 Bit
float t=*p_temperature; // t: Temperature 14 Bit
float rh_lin; // rh_lin: Humidity linear
float rh_true; // rh_true: Temperature compensated humidity
float t_C; // t_C : Temperature [C]
t_C=t*0.01 - 40; //补偿温度,14位温度精度 5V条件 修正公式
rh_lin=C3*rh*rh + C2*rh + C1; //相对湿度非线性补偿
rh_true=(t_C-25)*(T1+T2*rh)+rh_lin-3; //相对湿度对于温度依赖性补偿
if(rh_true>100)rh_true=100; //湿度最大修正
if(rh_true<0.1)rh_true=0.1; //湿度最小修正
*p_temperature=t_C; //返回温度结果
*p_humidity=rh_true; //返回湿度结果
}
二、仿真实现
三、代码
采用模块化编程 //…main.c…//
#include <regx52.h> #include "LCD1602.h" #include "I2C.h" #include <intrins.h> #include <math.h> typedef union //定义了两个共用体:如果没有typedef那么就是普通的定义了匿名联合的一个变量value.加了typedef后, 定义的就是类型别名, 当类型一样用 { unsigned int i; //i表示测量得到的温湿度数据(int 形式保存的数据) float f; //f表示测量得到的温湿度数据(float 形式保存的数据) } value; void delay_n10us(unsigned int n) //延时n个10us@12M晶振 { unsigned int i; for(i=n;i>0;i--) { _nop_();_nop_();_nop_();_nop_();_nop_();_nop_(); } } void main(void) { value humi_val,temp_val; //185行,定义两个共同体,一个用于湿度,一个用于温度 unsigned char error; //用于检验是否出现错误 unsigned int wendu,shidu; //最终,一位小数温湿度的值 LCD_Init(); Reset(); while(1) { //int为两个字节,这里没看懂,高字节赋给int低字节什么操作,其他都没问题,看到网上程序全是这么写的 error=0; //初始化error=0,即没有错误 error+=s_measure((unsigned char*) &humi_val.i,1); //measure humidity error+=s_measure((unsigned char*) &temp_val.i,0); //measure temperature if(error!=0) Reset(); //in case of an error: connection reset else { humi_val.f=(float)humi_val.i; //converts integer to float temp_val.f=(float)temp_val.i; //converts integer to float calc_SHT11(&humi_val.f,&temp_val.f); //calculate humidity, temperature LCD_ShowString(1,1,"TE:"); LCD_ShowString(2,1,"RH:"); LCD_ShowString(1,7,"."); LCD_ShowString(2,7,"."); LCD_ShowString(1,9,"C "); LCD_ShowString(2,9,"% "); wendu=10*temp_val.f; //例如温度109.1→1091 LCD_ShowChar(1,4,abs(wendu)/1000+'0'); //显示温度百位,加“0”是为了将字符的ASCII码大于48(即字符0的ASCII值),一般是将数字0,1,2……,9转换为字符“0”,“1”……,“9”; LCD_ShowChar(1,5,abs(wendu)%1000/100+'0'); //显示温度十位 LCD_ShowChar(1,6,abs(wendu)%100/10+'0'); //显示温度个位 LCD_ShowChar(1,8,abs(wendu)%10+'0'); //显示温度小数点后第一位 shidu=10*humi_val.f; LCD_ShowChar(2,4,shidu/1000+'0'); //显示湿度百位 LCD_ShowChar(2,5,(shidu%1000)/100+'0'); //显示湿度十位 LCD_ShowChar(2,6,(shidu%100)/10+'0'); //显示湿度个位 LCD_ShowChar(2,8,(shidu%10)+'0'); //显示湿度小数点后第一位 } delay_n10us(800); } }
//…LCD1602.c…//
#include <reg52.h>
#include <intrins.h>
#define LCD_Bus P0
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
//函数定义:
/** * @brief LCD1602延时函数,12MHz调用可延时1ms * @param 无 * @retval 无 */
void LCD_Delay()
{
unsigned char i, j;
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
void LCD_WriteCommand(unsigned char command)
{
LCD_RS=0;
LCD_RW=0;
LCD_EN=0;
LCD_Bus=command;
LCD_EN=1;
LCD_Delay();
LCD_EN=0;
LCD_Delay();
}
void LCD_WriteData(unsigned char Data)
{
LCD_RS=1;
LCD_RW=0;
LCD_EN=0;
LCD_Bus=Data;
LCD_EN=1;
LCD_Delay();
LCD_EN=0;
LCD_Delay();
}
void LCD_Init()
{
LCD_WriteCommand(0x38);
LCD_WriteCommand(0x06);
LCD_WriteCommand(0x0c);
LCD_WriteCommand(0x01);
}
void LCD_SetAddress(unsigned char line,unsigned char column)
{
if(line==1)
LCD_WriteCommand(0X80+column-1);
else
LCD_WriteCommand(0X80+0X40+column-1);
}
void LCD_ShowChar(unsigned char line,unsigned char column,char Char)
{
LCD_SetAddress(line,column);
LCD_WriteData(Char);
}
void LCD_ShowString(unsigned char line,unsigned char column,char*String)
{
LCD_SetAddress(line,column);
while(*String!='\0')
{
LCD_WriteData(*String++);
}
}
int pow(int x,int y)
{
int result=1;
while(y-->0)
{
result*=x;
}
return result;
}
void LCD_ShowNumber(unsigned char line,unsigned char column,unsigned int number,unsigned char length)
{
unsigned char i;
LCD_SetAddress(line,column);
for(i=length;i>0;i--)
{
LCD_WriteData(number/pow(10,i-1)%10+'0');
}
}
//…LCD1602.h…//
#ifndef __LCD1602_H__
#define __LCD1602_H__
void LCD_Init();
void LCD_ShowChar(unsigned char line,unsigned char column,char Char);
void LCD_ShowString(unsigned char line,unsigned char column,char*String);
void LCD_ShowNumber(unsigned char line,unsigned char column,unsigned int number,unsigned char length);
#endif
//…I2C.c…//
#include <regx52.h>
#include <intrins.h>
sbit I2C_SCL=P2^1;
sbit I2C_SDA=P2^0;
#define MEASURE_TEMP 0X03
#define MEASURE_HUMI 0X05
void I2C_Start()
{
I2C_SDA=1;
I2C_SCL=0;
I2C_SCL=1;
I2C_SDA=0;
I2C_SCL=0;
I2C_SCL=1;
I2C_SDA=1;
I2C_SCL=0;
}
void Reset()
{
unsigned char i;
I2C_SDA=1;
I2C_SCL=0;
for(i=0;i<9;i++)
{
I2C_SCL=1;
I2C_SCL=0;
}
I2C_Start();
}
char I2C_SendByte(unsigned char byte)
{
unsigned char i;
char error=0;
I2C_SCL=0;
for(i=0;i<8;i++)
{
if(byte&(0X80>>i))//主机准备数据
I2C_SDA=1;
else
I2C_SDA=0;
I2C_SCL=1;//从机读取数据
I2C_SCL=0;
}
I2C_SDA=1;//主机释放SDA
//从机准备应答数据
I2C_SCL=1;
error=I2C_SDA;//主机读取应答数据
I2C_SCL=0;
I2C_SDA=1;//从机释放SDA
return error;
}
unsigned char I2C_ReceiveByte(bit ACK)
{
unsigned char i,byte=0X00;
I2C_SDA=1;//主机释放总线
for(i=0;i<8;i++)
{
I2C_SCL=1;
if(I2C_SDA
标签: te511温度lcd显示传感器