0 卖盘信息
BOM询价
您现在的位置: 首页 > 电子资讯 >技术信息 > 80C51单片机模拟I2C总线的主机程序

80C51单片机模拟I2C总线的主机程序

来源: 电子发烧友
2018-12-06
类别:技术信息
eye 327
文章创建人 拍明

原标题:80C51单片机模拟I2C总线的主机程序

  

  I2C总线协议程序

  在使用的过程中一定要注意时序、时间的问题。

  i2c.c

  /*

  I2C.c

  标准80C51单片机模拟I2C总线的主机程序

  All rights reserved.

  */

  #include “I2C.h”

  //定义延时变量,用于宏I2C_Delay()

  unsigned char data I2C_Delay_t;

  /*

  宏定义:I2C_Delay()

  功能:延时,模拟I2C总线专用

  */

  #define I2C_Delay()

  {

  I2C_Delay_t = (I2C_DELAY_VALUE);

  while ( --I2C_Delay_t != 0 );

  }

  /*

  image.png

  函数:I2C_Init()

  功能:I2C总线初始化,使总线处于空闲状态

  说明:在main()函数的开始处,通常应当要执行一次本函数

  */

  void I2C_Init()

  {

  I2C_SCL = 1;

  I2C_Delay();

  I2C_SDA = 1;

  I2C_Delay();

  }

  /*

  函数:I2C_Start()

  功能:产生I2C总线的起始状态

  说明:

  SCL处于高电平期间,当SDA出现下降沿时启动I2C总线

  不论SDA和SCL处于什么电平状态,本函数总能正确产生起始状态

  本函数也可以用来产生重复起始状态

  本函数执行后,I2C总线处于忙状态

  */

  void I2C_Start()

  {

  I2C_SDA = 1;

  I2C_Delay();

  I2C_SCL = 1;

  I2C_Delay();

  I2C_SDA = 0;

  I2C_Delay();

  I2C_SCL = 0;

  I2C_Delay();

  }

  /*

  函数:I2C_Write()

  功能:向I2C总线写1个字节的数据

  参数:

  dat:要写到总线上的数据

  */

  void I2C_Write(char dat)

  {

  unsigned char t = 8;

  do

  {

  I2C_SDA = (bit)(dat & 0x80);

  dat 《《= 1;

  I2C_SCL = 1;

  I2C_Delay();

  I2C_SCL = 0;

  I2C_Delay();

  } while ( --t != 0 );

  }

  /*

  函数:I2C_Read()

  功能:从从机读取1个字节的数据

  返回:读取的一个字节数据

  */

  char I2C_Read()

  {

  char dat;

  unsigned char t = 8;

  I2C_SDA = 1; //在读取数据之前,要把SDA拉高

  do

  {

  I2C_SCL = 1;

  I2C_Delay();

  dat 《《= 1;

  if ( I2C_SDA ) dat |= 0x01;

  I2C_SCL = 0;

  I2C_Delay();

  } while ( --t != 0 );

  return dat;

  }

  /*

  函数:I2C_GetAck()

  功能:读取从机应答位

  返回:

  0:从机应答

  1:从机非应答

  说明:

  从机在收到每个字节的数据后,要产生应答位

  从机在收到最后1个字节的数据后,一般要产生非应答位

  */

  bit I2C_GetAck()

  {

  bit ack;

  I2C_SDA = 1;

  I2C_Delay();

  I2C_SCL = 1;

  I2C_Delay();

  ack = I2C_SDA;

  I2C_SCL = 0;

  I2C_Delay();

  return ack;

  }

  /*

  函数:I2C_PutAck()

  功能:主机产生应答位或非应答位

  参数:

  ack=0:主机产生应答位

  ack=1:主机产生非应答位

  说明:

  主机在接收完每一个字节的数据后,都应当产生应答位

  主机在接收完最后一个字节的数据后,应当产生非应答位

  */

  void I2C_PutAck(bit ack)

  {

  I2C_SDA = ack;

  I2C_Delay();

  I2C_SCL = 1;

  I2C_Delay();

  I2C_SCL = 0;

  I2C_Delay();

  }

  /*

  函数:I2C_Stop()

  功能:产生I2C总线的停止状态

  说明:

  SCL处于高电平期间,当SDA出现上升沿时停止I2C总线

  不论SDA和SCL处于什么电平状态,本函数总能正确产生停止状态

  本函数执行后,I2C总线处于空闲状态

  */

  void I2C_Stop()

  {

  unsigned int t = I2C_STOP_WAIT_VALUE;

  I2C_SDA = 0;

  I2C_Delay();

  I2C_SCL = 1;

  I2C_Delay();

  I2C_SDA = 1;

  I2C_Delay();

  while ( --t != 0 ); //在下一次产生Start之前,要加一定的延时

  }

  /*

  函数:I2C_Puts()

  功能:I2C总线综合发送函数,向从机发送多个字节的数据

  参数:

  SlaveAddr:从机地址(7位纯地址,不含读写位)

  SubAddr:从机的子地址

  SubMod:子地址模式,0-无子地址,1-单字节子地址,2-双字节子地址

  *dat:要发送的数据

  Size:数据的字节数

  返回:

  0:发送成功

  1:在发送过程中出现异常

  说明:

  本函数能够很好地适应所有常见的I2C器件,不论其是否有子地址

  当从机没有子地址时,参数SubAddr任意,而SubMod应当为0

  */

  bit I2C_Puts

  (

  unsigned char SlaveAddr,

  unsigned int SubAddr,

  unsigned char SubMod,

  char *dat,

  unsigned int Size

  )

  {

  //定义临时变量

  unsigned char i;

  char a[3];

  //检查长度

  if ( Size == 0 ) return 0;

  //准备从机地址

  a[0] = (SlaveAddr 《《 1);

  //检查子地址模式

  if ( SubMod 》 2 ) SubMod = 2;

  //确定子地址

  switch ( SubMod )

  {

  case 0:

  break;

  case 1:

  a[1] = (char)(SubAddr);

  break;

  case 2:

  a[1] = (char)(SubAddr 》》 8);

  a[2] = (char)(SubAddr);

  break;

  default:

  break;

  }

  //发送从机地址,接着发送子地址(如果有子地址的话)

  SubMod++;

  I2C_Start();

  for ( i=0; i

  {

  I2C_Write(a[i]);

  if ( I2C_GetAck() )

  {

  I2C_Stop();

  return 1;

  }

  }

  //发送数据

  do

  {

  I2C_Write(*dat++);

  if ( I2C_GetAck() ) break;

  } while ( --Size != 0 );

  //发送完毕,停止I2C总线,并返回结果

  I2C_Stop();

  if ( Size == 0 )

  {

  return 0;

  }

  else

  {

  return 1;

  }

  }

  /*

  函数:I2C_Gets()

  功能:I2C总线综合接收函数,从从机接收多个字节的数据

  参数:

  SlaveAddr:从机地址(7位纯地址,不含读写位)

  SubAddr:从机的子地址

  SubMod:子地址模式,0-无子地址,1-单字节子地址,2-双字节子地址

  *dat:保存接收到的数据

  Size:数据的字节数

  返回:

  0:接收成功

  1:在接收过程中出现异常

  说明:

  本函数能够很好地适应所有常见的I2C器件,不论其是否有子地址

  当从机没有子地址时,参数SubAddr任意,而SubMod应当为0

  */

  bit I2C_Gets

  (

  unsigned char SlaveAddr,

  unsigned int SubAddr,

  unsigned char SubMod,

  char *dat,

  unsigned int Size

  )

  {

  //定义临时变量

  unsigned char i;

  char a[3];

  //检查长度

  if ( Size == 0 ) return 0;

  //准备从机地址

  a[0] = (SlaveAddr 《《 1);

  //检查子地址模式

  if ( SubMod 》 2 ) SubMod = 2;

  //如果是有子地址的从机,则要先发送从机地址和子地址

  if ( SubMod != 0 )

  {

  //确定子地址

  if ( SubMod == 1 )

  {

  a[1] = (char)(SubAddr);

  }

  else

  {

  a[1] = (char)(SubAddr 》》 8);

  a[2] = (char)(SubAddr);

  }

  //发送从机地址,接着发送子地址

  SubMod++;

  I2C_Start();

  for ( i=0; i

  {

  I2C_Write(a[i]);

  if ( I2C_GetAck() )

  {

  I2C_Stop();

  return 1;

  }

  }

  }

  //这里的I2C_Start()对于有子地址的从机是重复起始状态

  //对于无子地址的从机则是正常的起始状态

  I2C_Start();

  //发送从机地址

  I2C_Write(a[0]+1);

  if ( I2C_GetAck() )

  {

  I2C_Stop();

  return 1;

  }

  //接收数据

  for (;;)

  {

  *dat++ = I2C_Read();

  if ( --Size == 0 )

  {

  I2C_PutAck(1);

  break;

  }

  I2C_PutAck(0);

  }

  //接收完毕,停止I2C总线,并返回结果

  I2C_Stop();

  return 0;

  }

  i2c.h

  /*

  I2C.h

  标准80C51单片机模拟I2C总线的主机程序头文件

  Copyright (c) 2005,广州周立功单片机发展有限公司

  All rights reserved.

  本程序仅供学习参考,不提供任何可靠性方面的担保;请勿用于商业目的

  */

  #ifndef _I2C_H_

  #define _I2C_H_

  #include

  //模拟I2C总线的引脚定义

  sbit I2C_SCL = P3^4;

  sbit I2C_SDA = P3^5;

  //定义I2C总线时钟的延时值,要根据实际情况修改,取值1~255

  //SCL信号周期约为(I2C_DELAY_VALUE*4+15)个机器周期

  #define I2C_DELAY_VALUE 12

  //定义I2C总线停止后在下一次开始之前的等待时间,取值1~65535

  //等待时间约为(I2C_STOP_WAIT_VALUE*8)个机器周期

  //对于多数器件取值为1即可;但对于某些器件来说,较长的延时是必须的

  #define I2C_STOP_WAIT_VALUE 120

  //I2C总线初始化,使总线处于空闲状态

  void I2C_Init();

  //I2C总线综合发送函数,向从机发送多个字节的数据

  bit I2C_Puts

  (

  unsigned char SlaveAddr,

  unsigned int SubAddr,

  unsigned char SubMod,

  char *dat,

  unsigned int Size

  );

  //I2C总线综合接收函数,从从机接收多个字节的数据

  bit I2C_Gets

  (

  unsigned char SlaveAddr,

  unsigned int SubAddr,

  unsigned char SubMod,

  char *dat,

  unsigned int Size

  );

  #endif //_I2C_H_

  【80C51单片机】

  80C51单片机属于MCS-51系列单片机,由Intel公司开发,其结构是8048的延伸,改进了8048的缺点,增加了如乘(MUL)、除(DIV)、减(SUBB)、比较(CMP)、16位数据指针、布尔代数运算等指令,以及串行通信能力和5个中断源。采用40引脚双列直插式DIP(Dual In Line Package),内有128Byte的RAM单元及4K的ROM。

  概述

  80C51有两个16位定时计数器,两个外中断,两个定时计数中断,及一个串行中断,并有4个8位并行输入口。80C51内部有时钟电路,但需要石英晶体和微调电容外接,本系统中采用12MHz的晶振频率。由于80C51的系统性能满足系统数据采集及时间精度的要求,而且产品产量丰富来源广,应用也很成熟,故采用来作为控制核心。

  选型表

  特性

  y 80C51 核心处理单元

  4k 字节FLASH 89C51X2

  8k 字节FLASH 89C52X2

  16k 字节FLASH 89C54X2

  32k 字节FLASH 89C58X2

  128 字节RAM 89C51X2

  256 字节RAM 89C52X2/54X2/58X2

  布尔处理器

  全静态操作

  y 12 时钟操作 可选6 个时钟 通过软件或并行编程器

  y 存储器寻址范围

  64K 字节ROM 和64K 字节RAM

  y 电源控制模式

  ―时钟可停止和恢复

  ―空闲模式

  ―掉电模式

  y 两个工作频率范围

  6 时钟模式时为0 到20MHz

  12 时钟模式时为0 到33MHz

  y LQFP, PLCC 或DIP 封装

  y 扩展温度范围

  y 双数据指针

  y 3 个加密位

  1

  y 4 个中断优先级

  y 6 个中断源

  y 4 个8 位I/O 口

  y 全双工增强型UART

  ―帧数据错误检测

  ―自动地址识别

  y 3 个16 位定时/计数器T0 T1 标准80C51 和增加的T2 捕获和比较

  y 可编程时钟输出

  y 异步端口复位

  y 低EMI (禁止ALE 以及6 时钟模式)

  y 掉电模式可通过外部中断唤醒

  订购信息

  P89C51X2 4K 字节FLASH

  类型编号 封装 温度范围( )

  P89C51X2BA PLCC44 0~+70

  P89C51X2BN DIP40 0~+70

  P89C51X2BBD LQFP44 0~+70

  P89C51X2FA PLCC44 -40~+85

  P89C52X2 8K 字节FLASH

  类型编号 封装 温度范围( )

  P89C52X2BA PLCC44 0~+70

  P89C52X2BN DIP40 0~+70

  P89C52X2BBD LQFP44 0~+70

  P89C52X2FA PLCC44 -40~+85

  P89C52X2FN DIP40 -40~+85

  P89C52X2FBD LQFP44 -40~+85

  P89C54X2 16K 字节FLASH

  类型编号 封装 温度范围( )

  P89C54X2BA PLCC44 0~+70

  P89C54X2BN DIP40 0~+70

  P89C54X2BBD LQFP44 0~+70

  P89C54X2FA PLCC44 -40~+85

  P89C58X2 32K 字节FLASH

  类型编号 封装 温度范围( )

  P89C58X2BA PLCC44 0~+70

  P89C58X2BN DIP40 0~+70

  P89C58X2BBD LQFP44 0~+70

  P89C58X2FA PLCC44 -40~+85


责任编辑:David

【免责声明】

1、本文内容、数据、图表等来源于网络引用或其他公开资料,版权归属原作者、原发表出处。若版权所有方对本文的引用持有异议,请联系拍明芯城(marketing@iczoom.com),本方将及时处理。

2、本文的引用仅供读者交流学习使用,不涉及商业目的。

3、本文内容仅代表作者观点,拍明芯城不对内容的准确性、可靠性或完整性提供明示或暗示的保证。读者阅读本文后做出的决定或行为,是基于自主意愿和独立判断做出的,请读者明确相关结果。

4、如需转载本方拥有版权的文章,请联系拍明芯城(marketing@iczoom.com)注明“转载原因”。未经允许私自转载拍明芯城将保留追究其法律责任的权利。

拍明芯城拥有对此声明的最终解释权。

相关资讯