51单片机IIC通信协议

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 ---------------------------------------------------------------*/
  • 2
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值