51单片机中模拟IIC的代码编写
一、简介以及配置
主芯片:STC51W4K32S4
引脚配置:推挽输出
IIC从设备:SHT3X温湿度传感器
通信过程:
使用code代码为0x2c 0x06,发送一次命令采集一次温湿度。
注:IIC时序图、芯片以及温湿度传感器具体介绍内容,可自行百度搜索
二、代码展示
bsp_htsensors.h
#ifndef __BSP_HTSENSORS_H
#define __BSP_HTSENSORS_H
#include "reg51.h"
#include "config.h"
sbit SDA_PIN = P4^3;
sbit SCL_PIN = P4^4;
#define SDA_TYPE 0
#define SCL_TYPE 1
#define HIGHLEVEL 1
#define LOWLEVEL 0
#define I2C_ADDR 0x44 //addr pin connected logic low
void _IOConfig_SetOutput(char _io_pin);
void _IOConfig_SetInput(char _io_pin);
void _IO_SetDigtal(char _io_digtal, char _io_pin);
unsigned char _IO_GetDigtal();
char I2C2RcvStr(unsigned char sla,unsigned char suba, unsigned char *htdata, unsigned char num);
char Get_Enviroment_T_H(float *temp, float *humi);
#endif
bsp_htsensors.c
#include "bsp_htsensors.h"
#include "stdio.h"
static void IIC_nop(void)
{
int i;
for(i=0;i<500;i++);
}
//SDA P4.3 SCL P4.4 设置成推挽输出,需外接上拉电阻
void _IOConfig_SetOutput(char _io_pin)
{
if(_io_pin == SDA_TYPE)
{
P4M1 &= ~0x08; //清零
P4M0 |= 0x08; //置1
}
else if(_io_pin == SCL_TYPE)
{
P4M1 &= ~0x10; //清零
P4M0 |= 0x10; //置1
}
}
void _IOConfig_SetInput(char _io_pin)
{
if(_io_pin == SDA_TYPE)
{
P4M1 |= 0x08; //置1
P4M0 &= ~0x08; //清零
}
else if(_io_pin == SCL_TYPE)
{
P4M1 |= 0x10; //置1
P4M0 &= ~0x10; //清零
}
}
//设置输出引脚电平
void _IO_SetDigtal(char _io_digtal, char _io_pin)
{
if(_io_pin == SDA_TYPE)
{
SDA_PIN = _io_digtal;
}
else if(_io_pin == SCL_TYPE)
{
SCL_PIN = _io_digtal;
}
}
//读取引脚电平
unsigned char _IO_GetDigtal()
{
return SDA_PIN;
}
//test------------
static int b_AckForTHIIC,b_AckForTHIIC2;
static void TH_I2C2_Init(void)
{
_IOConfig_SetOutput(SCL_TYPE); //配置SCL为输出状态
_IO_SetDigtal(HIGHLEVEL, SCL_TYPE); //SCL输出高电平
IIC_nop();
_IOConfig_SetOutput(SDA_TYPE);
_IO_SetDigtal(HIGHLEVEL, SDA_TYPE);
IIC_nop();
}
static void TH_I2C2_Start(void)
{
TH_I2C2_Init(); //初始化I2C引脚
_IO_SetDigtal(LOWLEVEL, SDA_TYPE); //产生开始信号
IIC_nop();
_IO_SetDigtal(LOWLEVEL, SCL_TYPE); //SCL输出低电平
IIC_nop();
}
static void TH_I2C2_Write(char byte)
{
unsigned char t = 8;
_IOConfig_SetOutput(SDA_TYPE); /* SDA设置为输出模式*/
do
{
if(byte&0x80) //无论何时都要保证SDA数据变化时,SCL为低电平,
{
_IO_SetDigtal(HIGHLEVEL, SDA_TYPE);
}
else
{
_IO_SetDigtal(LOWLEVEL,SDA_TYPE);
}
byte<<=1;
IIC_nop();
_IO_SetDigtal(HIGHLEVEL, SCL_TYPE);
IIC_nop();
_IO_SetDigtal(LOWLEVEL, SCL_TYPE); //SCL由高变低
IIC_nop();
}while(--t!=0);
_IO_SetDigtal(HIGHLEVEL, SDA_TYPE); // 设置SDA为高,已释放SDA;
_IOConfig_SetInput(SDA_TYPE); // 设置SDA为输入;
IIC_nop();
_IO_SetDigtal(LOWLEVEL, SCL_TYPE); //SCL由低
IIC_nop();
_IO_SetDigtal(HIGHLEVEL, SCL_TYPE);
IIC_nop();
if(_IO_GetDigtal())
{
b_AckForTHIIC2=0;
}
else
{
b_AckForTHIIC2=1; //判断是否接收到应答信号
}
_IO_SetDigtal(LOWLEVEL, SCL_TYPE);
IIC_nop();
}
static char TH_I2C2_Read()
{
char dat = 0;
unsigned char t;
_IOConfig_SetInput(SDA_TYPE);/* SDA设置为输入模式*/
IIC_nop();
_IO_SetDigtal(LOWLEVEL, SCL_TYPE);/* SDA设置为输入模式*/
IIC_nop();
for(t=0;t<8;t++)
{
_IO_SetDigtal(HIGHLEVEL, SCL_TYPE);
dat=dat<<1;
if(_IO_GetDigtal())
dat=dat+1;
IIC_nop();
_IO_SetDigtal(LOWLEVEL, SCL_TYPE);
IIC_nop();
};
return dat;
}
static void TH_I2C2_PutAck(char ack)
{
_IOConfig_SetOutput(SDA_TYPE); /* SDA设置为输出模式*/
IIC_nop();
if(ack)
{
_IO_SetDigtal(HIGHLEVEL,SDA_TYPE);
}
else
{
_IO_SetDigtal(LOWLEVEL, SDA_TYPE);
}
IIC_nop();
_IO_SetDigtal(HIGHLEVEL, SCL_TYPE);
IIC_nop();
if(_IO_GetDigtal())
{
_IO_SetDigtal(HIGHLEVEL, SDA_TYPE);
}
IIC_nop();
_IO_SetDigtal(LOWLEVEL, SCL_TYPE);
IIC_nop();
}
static void TH_I2C2_Stop(void)
{
_IOConfig_SetOutput(SDA_TYPE); /* SDA设置为输出模式*/
_IO_SetDigtal(LOWLEVEL,SDA_TYPE);
IIC_nop();
_IO_SetDigtal(HIGHLEVEL, SCL_TYPE);
IIC_nop();
_IO_SetDigtal(HIGHLEVEL, SDA_TYPE);
IIC_nop();
IIC_nop(); //在下一次产生Start之前,要加一定的延时
IIC_nop();
}
char I2C2RcvStr(unsigned char sla,unsigned char suba, unsigned char *htdata, unsigned char num)
{
unsigned short i;
TH_I2C2_Start(); //启动总线
TH_I2C2_Write(sla); //发送器件地址
if(b_AckForTHIIC2==0)return(0);
TH_I2C2_Write(suba); //发送器件子地址
if(b_AckForTHIIC2==0)return(0);
//=====================================
TH_I2C2_Write(0x06); //发送命令
if(b_AckForTHIIC2==0)return(0);
//===============================================
TH_I2C2_Start();
TH_I2C2_Write(sla+1);
if(b_AckForTHIIC2==0)return(0);
//==============================
for(i=0;i<20;i++)
{
IIC_nop();
}
//==============================
for(i=0;i<num-1;i++)
{
*htdata=TH_I2C2_Read(); //发送数据
TH_I2C2_PutAck(0); //发送就答位
htdata++;
}
*htdata=TH_I2C2_Read();
TH_I2C2_PutAck(1); //发送非应位
TH_I2C2_Stop(); //结束总线
return(1);
}
/***************************************************************************
** 采集环境温湿度
** temp: 环境温度
** humi: 环境湿度
***************************************************************************/
char Get_Enviroment_T_H(float *temp, float *humi)
{
char ret = 0;
unsigned char ht_data[6]={0};
unsigned int temp_env = 0;
unsigned int humi_env = 0;
ret = I2C2RcvStr((I2C_ADDR<<1), 0x2c, &ht_data, 6);
if(ret == 1)
{
temp_env = ((unsigned short)ht_data[0]<<8) + ht_data[1];
humi_env = ((unsigned short)ht_data[3]<<8) + ht_data[4];
*temp = ((float)temp_env/65535)*172-45;
*humi = ((float)humi_env/65535)*100;
//printf("%.1f -- %.1f\r\n", *temp, *humi);
return 0;
}
return 1;
}
模拟IIC注意事项:
1、传输数据时,SDA需在SCL保持在低电平状态方可变换,SCL在高电平时需注意SDA电平稳定,延时的主要作用就是在于此点
2、接收与发送应答与非应答信号的时序需注意
3、总线空闲状态表示为SDA、SCL电平为高