因为90C52单片机没有硬件I I C,所以需要通过IO口来模拟I I C 协议
先要实现I I C协议功能之后
然后再和AHT10温度传感器通信获取温度和湿度数据
如果使用其他STM单片机的话可以直接使用自带的硬件I I C
这里先要来实现I I C协议
I I C 协议代码基本上都是通用的,可以参考别人的代码和I I C 的时序图来实现
我这里使用P2.0和P2.1来模拟SDA信号和SCL信号
这个协议看着比较简单,但是如果是自己第一次实现这个协议基本上都会失败
这里先要实现一个协议的起始信号
SCL在高电平的时候SDA信号由高到低代表开始,所以SCL先要为高电平,然后SDA由高到低跳变表示开始信号
然后是结束信号,和开始信号相反,SDA信号由低到高代表结束
完成开始信号之后两根信号线应该都是低电平的,表示准备发送数据
完成结束信号之后两根信号线都是高电平,表示总线空闲
信号保持高电平的时间需要大于4.7微秒
然后是发送一个字节
因为I I C 只能一位一位比特的发送,所以这里需要一个for循环
发送逻辑1需要在SCL为高电平期间SDA保持高电平,发送逻辑0则需要在SCL为高电平期间SDA保持为低电平
且需要注意SDA高低电平变化只能在SCL为低电平期间改变,不然就会变成开始信号或者结束信号导致通信错误
发送完8个字节之后在第九个时钟周期需要等待设备响应ACK
以下为参考链接
!a=https://www.cnblogs.com/aspirs/p/13081835.html
响应方式就是在SCL为低电平时先把SDA拉高,等待设备将它拉低表示响应,然后SCL变为高电平时看到SDA为低电平说明响应了ACK,否则就是不响应
然后就是读取数据
在设备响应ACK之后通过控制时钟线在高电平获取SDA数据
也是需要一个for循环来获取8位比特数据
因为响应ACK之后SCL线是低电平,此时设备可以将数据放入SDA线等待SCL拉高获取数据
如此循环直到读取完8位数据
然后还需要主动发送一个ACK响应给设备表示确认收到数据
确认ACK就是在接受到第8位之后将SDA线拉低
这样设备就会继续发送下一个字节的数据给我们
如果不发送ACK的话设备就会认为不需要继续发送数据了,然后停止发送,我们就可以发送一个停止信号结束本次通信了
这个协议都是先发送一个设备地址然后在发送指令或者读取数据的
然后就是读取AHT10温度传感器的数据了
读取方式为,首先需要初始化设备,然后发送测量数据,最后读取数据,一直循环,需要注意的是发送测量之后需要等待至少75ms等待数据测量再获取,设备初始化也是至少需要20ms
这里发送的是一个 38 地址加第8位0表示写(0x70),响应之后依次发送 ac 33 00 触发测量
等待75ms以上之后
然后发送一个 38 地址位加一个读取位1(0x71)等待设备响应准备读取
读取6次,第一字节是状态信息,如果返回 1c 说明正常
之后的五个字节是湿度和温度信息,各占用20位长度
前20位是速度信息,后20位是温度信息
然后根据提供的格式计算即可获取到温度和湿度的信息
湿度计算公式为 数据 / 1048576 * 100 = 湿度百分比
例如获取到的数据如下 80 87 35 61 5C
湿度部分为 0x80873 温度部分为 0x5615c
0x80873 = 526451 / 1048576 * 100 = 50.206%
0x5615c = 552604 / 1048576 * 200 - 50 = 17.25℃
以下代码IIC和AHT数据获取都是正常的可以参考,但是串口发送数据不对,等待后续研究
#include <reg52.h>
#define LCD P0
sbit RS = P2^6;
sbit RW = P2^5;
sbit LCDE = P2^7;
sbit SDA = P2^0;
sbit SCL = P2^1;
sbit SDA1 = P2^4;
sbit SCL1 = P2^3;
void Delay(unsigned int a);
unsigned char IIC_send(unsigned char data1);
void IIC_end_ack();
unsigned char IIC_read();
void IIC_stop();
void IIC_start();
unsigned char shidu[] = "shidu";
unsigned char wendu[] = "wendu";
unsigned char status = 0;
unsigned char k;
void main(void){
long data1;
unsigned char i;
unsigned char datas[6];
unsigned char data2[4];
float temp;//临时变量
//初始化串口
SCON = 0x50; //串口工作方式
TMOD = 0x20; //定时器模式2
PCON = 0x80; //波特率加倍
TH1=0XF3; //计数器初始值设置,注意波特率是4800的
TL1=0XF3;
TR1 = 1; //打开计数器1中断
ES = 1; //打开串口中断
EA = 1; //打开总中断
//初始化aht10
IIC_start();
IIC_send(0x70);
IIC_send(0xba);
IIC_stop();
Delay(3000);
while(1){
//请求测量
IIC_start();
IIC_send(0x70); //0x38
IIC_send(0xac);
IIC_send(0x33);
IIC_send(0x00);
IIC_stop();
Delay(5000); //延时82ms
//获取数据
IIC_start();
Delay(1); //延时82ms
IIC_send(0x71);
datas[0] = IIC_read();
IIC_end_ack();
datas[1] = IIC_read();
IIC_end_ack();
datas[2] = IIC_read();
IIC_end_ack();
datas[3] = IIC_read();
IIC_end_ack();
datas[4] = IIC_read();
IIC_end_ack();
datas[5] = IIC_read();
IIC_end_ack();
IIC_stop();
data1 = (datas[3] & 0xf) * 65536 + datas[4] * 256 + datas[5];
temp = data1 / 1048576 * 200 - 50;
data1 = temp * 1;
data2[0] = (data1 / 10) + 48;
data2[1] = (data1 % 10) + 48;
status = 0;
k=1;
while(status < 2){
if(k){
SBUF = data2[status]+48;
k = 0;
}
}
Delay(50000); //4000ms
}
}
//起始信号
void IIC_start(){
SCL = 0;
SCL1 = 0;
Delay(1); //6.33us左右,必须大于4.7us
SDA = 1;
SDA1 = 1;
Delay(1);
SCL = 1;
SCL1 = 1;
Delay(1);
SDA = 0;
SDA1 = 0;
Delay(1);
SCL = 0; //scl拉低准备发送数据
SCL1 = 0;
}
void IIC_stop(){
SCL = 0;
SCL1 = 0;
Delay(1);
SDA = 0;
SDA1 = 0;
Delay(1);
SCL = 1; //接收时将SCL拉高表示总线空闲
SCL1 = 1;
Delay(1);
SDA = 1;
SDA1 = 1;
}
unsigned char IIC_read(){
unsigned char i,data1;
SDA = 1; //这里设置为1应该是为了保险起见,因为一方发送完之后 SDA 和 SCL 都是高电平的
for(i=0; i<8; i++){
Delay(1);
SCL = 1;
SCL1 = 1;
Delay(1);
data1 <<= 1; //低位先接收
data1 |= SDA; //低位或运算获取数据
SDA1 = SDA;
Delay(1);
SCL = 0; //拉低等待对方设置数据吗?
SCL1 = 0;
Delay(1);
}
return data1;
}
void IIC_end_ack(){
SCL = 0;
SCL1 = 0;
Delay(1);
SDA = 0; //讲道理这里应该就是响应了
SDA1 = 0;
Delay(1);
SCL = 1;
SCL1 = 1;
Delay(1);
SCL = 0;
SCL1 = 0;
Delay(1);
}
unsigned char IIC_send(unsigned char data1){
unsigned char i,num;
SCL = 0;
SCL1 = 0;
Delay(1);
for(i=0; i<8; i++){
//因为SCL现在时低电平,所以可以设置SDA 高位先发
SDA = data1 >> 7; //第一位 12345678 >> 7 = 00000001 ←----┒
data1 = data1 << 1; // 12345678 << 1 = 23456780 ---------┘
SDA1 = SDA;
Delay(1);
SCL = 1;
SCL1 = 1;
Delay(1);
SCL = 0; //拉高再拉低
SCL1 = 0;
}
SDA = 1;
SDA1 = 1;
Delay(1);
SCL = 1; //发送完一个字节,等待从机响应 ACK
SCL1 = 1;
num = 0;
while(SDA){
num++;
Delay(2);
if(num > 200){//防止从机不响应死循环 1.几毫秒左右
SCL = 0;
SCL1 = 0;
return 2;
}
}
SCL = 0; //从机响应
SCL1 = 0;
Delay(1);
return 1;
}
void Delay(unsigned int a){ //6.33us
unsigned char b;
for(;a>0;a--)
{
for(b=3;b>0;b--);
}
}
//串口中断
void usart0() interrupt 4{
unsigned char date1,i;
if(RI){
RI = 0; //硬件中断后自动置1,软件值0防止多次响应 表示接收完毕
}
if(TI){
status++;
while(!TI);
TI = 0; //表示发送完毕
k=1;
}
}