IIC头文件
/*------------------------------------------------------------------------------
* @file I2C.H
* @author Byron (from3900@gmail.com)
* @version V1.0.0
* @date 05/12/2020
* @brief 51系列单片机I2C通信协议头文件
* -----------------------------------------------------------------------------
*/
#ifndef __I2C_H_
#define __I2C_H_
/* includes ------------------------------------------------------------------*/
#include <reg52.h>
/* variables -----------------------------------------------------------------*/
extern bit ack; //应答标志位
/* exported functions --------------------------------------------------------*/
extern void IIC_init(); //IIC总线初始化函数
extern void IIC_Start(); //起始信号函数
extern void IIC_Stop(); //终止信号函数
extern void IIC_Ack(bit a); //应答函数
//无子地址写字节数据函数
extern void SendByte(unsigned char c);
//无子地址读字节数据函数
extern unsigned char RcvByte();
//向没有子地址器件发送字节数据函数
extern bit ISendByte(unsigned char sla, unsigned char c);
//向有子地址发送多字节数据函数
extern bit ISendStr(unsigned char sla,unsigned char suba, \
unsigned char *s,unsigned char no);
//从无子地址器件读取字节数据函数
extern bit IRcvByte(unsigned char sla, unsigned char *c);
//从有子地址器件读取多字节数据函数
extern bit IRcvStr(unsigned char sla, unsigned char suba, \
unsigned char *s, unsigned char no);
#endif
/* END OF FILE ---------------------------------------------------------------*/
IIC.c
/*------------------------------------------------------------------------------
* @file I2C.c
* @author Byron (from3900@gmail.com)
* @version V1.0.0
* @date 05/12/2020
* @brief 51系列单片机I2C通信协议
* ---------
* @notes
* -----------------------------------------------------------------------------
*/
/* Includes ------------------------------------------------------------------*/
#include "I2C.H"
#include <intrins.h>
/* variables -----------------------------------------------------------------*/
sbit SCL = P3^6; //IIC模拟时钟传送位
sbit SDA = P3^7; //IIC模拟数据控制位
bit ack; //应答标志
/* function prototypes -------------------------------------------------------*/
/* functions -----------------------------------------------------------------*/
/*
* @brief 延时5us
* @param None
* @retval None
*/
void Delay5us(void)
{
_nop_(); _nop_(); _nop_(); _nop_(); _nop_();
}
/*
* @brief 总线初始化函数。
* 拉高SCL时钟信号;
* 拉高SDA数据信号。
* @param None
* @retval None
*/
void IIC_init()
{
SCL = 1;
_nop_();
SDA = 1;
Delay5us();
}
/*
* @brief 起始信号函数。
* SCL时钟信号为高电平;
* SDA数据信号拉高并至少保持4.7us,之后产生一个下降沿, 同时SDA产生的这个
* 低电平要至少保持4us;
* 之后拉低SCL。
* @param None
* @retval None
*/
void IIC_Start()
{
SCL = 1; //拉高SCL
_nop_();
SDA = 1; //拉高SDA
Delay5us(); //保证SDA建立时间>4.7us
SDA=0; //使SDA产生下降沿跳变
Delay5us(); //保持建立时间>4us
SCL=0; //拉低SCL
_nop_();
}
/*
* @brief 终止信号函数。
* SDA为低电平
* SCL产生一个上升沿,同时保证维持高电平时间>4us
* SDA产生一个上升沿,同时保证维持高电平时间>4.7us
* @param None
* @retval None
*/
void IIC_Stop()
{
SDA = 0; //SDA为低电平
_nop_();
SCL = 1; //SCL产生上升沿
Delay5us(); //建立时间>4us
SDA = 1; //SDA产生上升沿
Delay5us(); //建立时间>4.7us
}
/*
* @brief 应答函数
* 主机接收到从器件最后一个数据字节之后,需要给从器件发送一个非应答信号,
* 以使得从器件释放数据总线,以便主机发送终止信号,结束数据传送。
* @param a=0 应答,从机继续占据总线,传送数据
* a=1 非应答,从机释放总线
* @retval None
*/
void IIC_Ack(bit a)
{
if (a==0) SDA=0; //应答
else SDA=1; //非应答
_nop_(); //确保SDA信号已经稳定
SCL = 1;
Delay5us();
SCL = 0; //清时钟线,准备接收
Delay5us();
}
/*
* @brief 发送一个Byte.
* 在SCL时钟信号为高电平期间,保持发送信号SDA稳定
* 发送结束之后 SDA=1 & SCL=0
* @param c 待发送的数据或地址
* @retval 发送成功ack=1,否则ack=0
*/
void SendByte(unsigned char c)
{
unsigned char BitCnt;
for (BitCnt=0; BitCnt<8; BitCnt++) //传送一个Byte即8个bit
{
SCL = 0; //保证SCL处于低电平
if ((c<<BitCnt)&0x80) SDA=1; //判断发送的位状态
else SDA=0;
_nop_();
SCL = 1; //拉高SCL, 通知从机开始接收数据bit
Delay5us();
SCL = 0; //复位SCL,为下一次发送做准备
}
_nop_();
SDA = 1; //8位发送完后释放数据线,准备接收应答位
_nop_();
SCL = 1;
Delay5us();
if (SDA==1) ack = 0; //是否接收到应答信号
else ack = 1;
SCL = 0;
_nop_();
}
/*
* @brief 从从器件中读取一个Byte
* 完成之后 SDA=1 & SCL=0
* @param None
* @retval retc 接收到的数据
*/
unsigned char RcvByte()
{
unsigned char retc = 0;
unsigned char BitCnt;
SDA = 1; //置数据线为输入方式
for (BitCnt=0; BitCnt<8; BitCnt++)
{
_nop_();
SCL = 0; //置时钟线为低,准备接收数据位
Delay5us(); //时钟低电平周期大于4.7μs
SCL = 1; //置时钟线为高使数据线上数据有效
_nop_();
retc = retc<<1;
if (SDA==1) retc = retc+1; //读数据位,接收的数据位放入retc中
_nop_();
}
SCL=0;
_nop_();
return(retc);
}
/*
* @brief 向没有子地址器件发送字节数据函数
* !启用前总线已经结束
* @param sla: 器件地址
* c : 发送数据
* @retval 成功返回1,否则返回0
*/
bit ISendByte(unsigned char sla, unsigned char c)
{
IIC_Start(); //启动总线
SendByte(sla); //发送器件地址
if (ack==0) return(0);
SendByte(c); //发送数据
if (ack==0) return(0);
IIC_Stop(); //结束总线
return(1);
}
/*
* @brief 向有子地址器件发送多字节数据函数
* 启动总线->发送地址、子地址、数据->结束总线
* !启用前总线已经结束
* @param sla : 从器件地址
* suba: 子地址
* s :指向待发送的内容
* no :发送字节的个数
* @retval 成功返回1,否则返回0
*/
bit ISendStr(unsigned char sla, unsigned char suba, \
unsigned char *s, unsigned char no)
{
unsigned char i;
IIC_Start(); //启动总线
SendByte(sla); //发送器件地址
if (ack==0) return(0);
SendByte(suba); //发送器件子地址
if (ack==0) return(0);
for (i=0; i<no; i++)
{
SendByte(*s);
if (ack==0) return(0);
s++;
}
IIC_Stop(); //结束总线
return(1);
}
/*
* @brief 从无子地址器件读取字节数据函数
* 启动总线->发送地址->读取数据->结束总线
* !启用前总线已经结束
* @param sla:从器件地址
* c :读取的数据
* @retval 成功返回1,否则返回0
*/
bit IRcvByte(unsigned char sla, unsigned char *c)
{
IIC_Start(); //启动总线
SendByte(sla+1); //发送器件地址
if (ack==0) return(0);
*c = RcvByte(); //读取数据0
IIC_Ack(1); //发送非就答位
IIC_Stop(); //结束总线
return(1);
}
/*
* @brief 从有子地址器件读取多字节数据函数
* 启动总线->发送地址、子地址、数据->结束总线
* !启用前总线已经结束
* @param sla : 从器件地址
* suba: 子地址
* s :指向读取的内容存放区
* no :读取字节个数
* @retval 成功返回1,否则返回0
*/
bit IRcvStr(unsigned char sla, unsigned char suba, \
unsigned char *s, unsigned char no)
{
unsigned char i;
IIC_Start(); //启动总线
SendByte(sla); //发送器件地址
if (ack==0) return(0);
SendByte(suba); //发送器件子地址
if (ack==0) return(0);
IIC_Start(); //启动总线
SendByte(sla); //发送器件地址
if (ack==0) return(0);
for (i=0; i<no-1; i++)
{
*s = RcvByte();
IIC_Ack(0);
s++;
}
*s = RcvByte();
IIC_Ack(1); //发送非就答位
IIC_Stop(); //结束总线
return(1);
}
/* END OF FILE ---------------------------------------------------------------*/