51单片机 普通IO口模拟IIC(I2C)接口通讯的程序代码


I2C总线是Philips公司提出的一种集成电路IC器件之间相连接的总线协议,其目的是使电子系统(不只 限于单片机系统)各个IC器件之间的连线变得容易。因为使用传统的并行总线在IC器件之间连接,往往会使得IC之间连线较多,显得非常复杂。而I2C总线 则使IC器件之间只需SDA、SCL两条连线就可以传送数据,因而十分方便。由于I2C在印刷体中不容易书写(需要上标),所以实际书写时,还常见到 IIC、I2C等书写方法,本文采用IIC的写法,敬请注意。关于IIC总线的知识,请参阅相关书籍,此处不再做进一步介绍。

下面我们用一个使用IIC总线连接器件的例子来简单说明IIC总线的仿真。

例.EEPROM24C02是采用IIC接口的一种常用2Kbit(256×8bit)的存储器。编写程序使用AT89C51的IO口模拟实现IIC总线协议进行通信,并向24C02存储器内从字节0到字节FF写入数字0到FF。

51系列单片机本身没有IIC接口,但一些本身具有IIC接口的单片机往往是高端产品,一方面价格不菲,另一方面我们的系统也没有必要使用之。通常我们就使用软件通过51系列单片机的IO口来模拟实现IIC总线通信。

本例事实上比较简单,但需要对IIC总线时序有较好的理解。源文件如下图所示(采用C51语言编写):

 


 

在Keil中编辑好源文件以后,接下来就可以建立工程文件并生成相应的源代码了,然后我们来绘制电路图。
此例的电路图极其简单。只需两个IC,即AT89C51和24C02C,和两个上拉电阻,而且上拉电阻还可以省略。至于连接,就更为简单了。最后得到绘制好的电路图如下图所示:

 

绘制好电路图,我们就可以将前面刚刚生成的程序源代码装入单片机了,装入以后,下面我们就可以来进行仿真了。

首先点击仿真按钮,系统没有什么反映,只有高低电平变化的颜色。我们要想查看结果,还要用前文中仿真扩展 RAM存储器的方法,先点击暂停,然后点击“Debug”菜单下的“I2C Memory Internal Memory – U2”子菜单来打开U2即EEPROM存储器24C02C的内容窗口“I2C Memory Internal Memory – U2”,然后我们就看到了其中的内容,也就是我们仿真程序的结果。如下图所示:

 

从图中我们能清楚地看到我们的仿真结果,程序完全正确地执行了我们的命令。

当然,如果你过早地点击了暂停按钮,那么你得到的结果可能和上图略有不同,那可能是因为程序尚未执行完毕。此时你可以继续点击运行按钮,或者点击单步按钮来仔细查看程序执行过程中24C02C存储器内容的改变情况。


完整代码如下:

  • /*----------------------------------------------------------------

  • Acess the eeprom--24c04

  • ----------------------------------------------------------------*/

  • #include <intrins.h>

  • #ifndef INT8U

  • #define INT8U unsigned char

  • #endif

  • #ifndef INT8S

  • #define INT8S signed char

  • #endif

  • #ifndef INT16U

  • #define INT16U unsigned int

  • #endif

  • #define I2C_DELAY; _nop_();_nop_();_nop_();_nop_();_nop_(); // >=4.7uS

  • //----------------------------------------------------------------

  • // delay 100us

  • //----------------------------------------------------------------

  • void mDelay(INT8U k)

  • {

  • INT16U i ;

  • for(; k>0; k--)

  • {

  • for(i=0; i<93; i++)

  • ;

  • }

  • }

  • //----------------------------------------------------------------

  • //OK

  • //----------------------------------------------------------------

  • void I2C_Start(void)

  • {

  • SDA = 1;

  • I2C_DELAY;

  • SCL = 1;

  • I2C_DELAY;

  • SDA = 0;

  • I2C_DELAY;

  • I2C_DELAY;

  • }

  • //----------------------------------------------------------------

  • //OK

  • //----------------------------------------------------------------

  • void I2C_Stop(void)

  • {

  • SDA = 0 ;

  • I2C_DELAY;

  • SCL = 1 ;

  • I2C_DELAY;

  • SDA = 1 ;

  • I2C_DELAY;

  • I2C_DELAY;

  • }

  • //----------------------------------------------------------------

  • //

  • //----------------------------------------------------------------

  • void sendAck(void)

  • {

  • SCL = 0;

  • I2C_DELAY;

  • SDA = 0;

  • I2C_DELAY;

  • SCL = 1;

  • I2C_DELAY;

  • }

  • //----------------------------------------------------------------

  • //

  • //----------------------------------------------------------------

  • void sendNoAck(void)

  • {

  • SCL = 0;

  • I2C_DELAY;

  • SDA = 1;

  • I2C_DELAY;

  • SCL = 1;

  • I2C_DELAY;

  • }

  • //----------------------------------------------------------------

  • // 0 = noACK; 1 = ACK ;

  • //----------------------------------------------------------------

  • bit checkAck()

  • {

  • bit tempbit;

  • /*发送完一个字节后检验设备的应答信号*/

  • SDA = 1;

  • I2C_DELAY;

  • SCL = 0;

  • I2C_DELAY;

  • tempbit = SDA;

  • SCL = 1;

  • I2C_DELAY;

  • if(tempbit==1)

  • {

  • return 0; //noACK

  • }

  • else

  • {

  • return 1; //ACK

  • }

  • }

  • //----------------------------------------------------------------

  • //OK

  • // a positive clock edge clock a bit into the ROM

  • //----------------------------------------------------------------

  • void writeByte(INT8U datum)

  • {

  • INT8U bitCnt = 0 ;

  • for(bitCnt=0; bitCnt<8; bitCnt++)

  • {

  • SCL = 0 ;

  • I2C_DELAY;

  • if ((datum&0x80) == 0x80) //if the MSb is 1

  • SDA = 1 ;

  • else

  • SDA = 0 ;

  • I2C_DELAY;

  • SCL = 1 ;

  • I2C_DELAY;

  • datum<<=1 ;

  • }

  • }

  • //----------------------------------------------------------------

  • //OK

    ARM单片机采用了新型的32位ARM核处理器,使其在指令系统,总线结构,调试技术,功耗以及性价比等方面都超过了传统的51系列单片机,同时ARM单片机在芯片内部集成了大量的片内外设,所以功能和可靠性都大大提高。具有统一和固定长度的指令域,使指令集和指令译码都大大简化。具有一个大而统一的寄存器文件,大多数数据操作都在寄存器中完成,使指令执行速度更快。采用加载/存储结构,使数据处理时只对寄存器操作,而不直接对存储器操作。

  • //----------------------------------------------------------------

  • INT8U readByte(void)

  • {

  • bit tempbit = 1 ;

  • INT8U temp = 0 ;

  • INT8U bitCnt ;

  • SDA = 1 ; // release the bus,ready to receive byte??????????????

  • I2C_DELAY;

  • for(bitCnt=0; bitCnt<8; bitCnt++)

  • {

  • SCL = 0; //?????????????????????????huan???????????????

  • I2C_DELAY;

  • tempbit = SDA ;

  • if (tempbit)

  • temp |= 0x01 ;

  • else

  • temp &= 0xfe ;

  • SCL = 1 ;

  • I2C_DELAY;

  • if(bitCnt<7)

  • temp <<= 1 ;

  • }

  • return(temp) ;

  • }

  • /*~~~~~~~~~~~~~~~~~~~~~~~ API ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

  • /*-----------------------------------------------------------------

  • write some bytes to sequential address

  • -----------------------------------------------------------------*/

  • void writeToROM(INT8U datum[], INT8U address, INT8U num)

  • {

  • bit tempbit ;

  • INT8U i ;

  • INT8U *datum_P ;

  • datum_P = datum ;

  • I2C_Start() ;

  • writeByte(0xa0) ;

  • tempbit = checkAck();

  • writeByte(address) ;

  • tempbit = checkAck();

  • for(i=0; i<num; i++)

  • {

  • writeByte(*(datum_P+i)) ;

  • if(!checkAck())

  • {

  • I2C_Stop() ;

  • mDelay(100) ;

  • }

  • }

  • I2C_Stop() ;

  • }

  • /*-----------------------------------------------------------------

  • read some bytes from ROM`s sequential address

  • -----------------------------------------------------------------*/

  • void readFromROM(INT8U datum[], INT8U address, INT8U num)

  • {

  • bit tempbit ;

  • INT8U i ;

  • INT8U *datum_P ;

  • datum_P = datum;

  • I2C_Start() ;

  • writeByte(0xa0) ;

  • tempbit = checkAck();

  • writeByte(address) ;

  • tempbit = checkAck();

  • I2C_Start() ;

  • writeByte(0xa1) ;

  • tempbit = checkAck();

  • for(i=0; i<num; i++)

  • {

  • *(datum_P+i) = readByte() ;

  • if(i!=num-1)

  • {

  • sendAck() ;

  • }

  • else

  • {

  • sendNoAck() ;

  • }

  • }

  • I2C_Stop() ;

  • }

  • /*-----------------------------------------------------------------

  • wirte one byte to ROM --random write

  • -----------------------------------------------------------------*/

  • void writeOneByte(INT8U addr, INT8U datum)

  • {

  • bit tempbit ;

  • /*write a byte to mem*/

  • I2C_Start();

  • writeByte(0xa0);

  • tempbit = checkAck();

  • writeByte(addr); /*address*/

  • tempbit = checkAck();

  • writeByte(datum); /*the data*/

  • tempbit = checkAck();

  • I2C_Stop();

  • mDelay(100) ;

  • }

  • /*-----------------------------------------------------------------

  • read one byte from rom --random read

  • -----------------------------------------------------------------*/

  • INT8U readOneByte(INT8U addr)

  • {

  • bit tempbit = 1;

  • INT8U mydata;

  • /*read a byte from mem*/

  • I2C_Start();

  • writeByte(0xa0);

  • tempbit = checkAck();

  • writeByte(addr); /*address*/

  • tempbit = checkAck();

  • I2C_Start();

  • writeByte(0xa1);

  • tempbit = checkAck();

  • mydata = readByte();

  • tempbit = checkAck();

  • return (mydata) ;

  • I2C_Stop();

  • }

> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > >

既然单片机有着和计算机一样的功能,那他都用在哪里呢?平常我们都不会在意的。像以前我们玩的卡带游戏,洗衣机,电冰箱等家电上面都有使用。

生成海报
点赞 0

thePro

我还没有学会写个人说明!

相关推荐

stm32 IIC 协议控制PCA9685舵机驱动板

这篇教程与网上的大部分教程都不相同,着重讲述如何使用编程PCA9685,而不是着重讲述原理,最后你还是一头雾水。这是一篇教你如何如何输出固定舵机角度,如何移植程序,从PCA9

12. STM32——硬件IIC驱动OLED屏幕显示

OLED屏幕 OLED即有机发光管(Organic Light-Emitting Diode,OLED)。OLED显示技术具有自发光、广视角、几乎无穷高的对比度、较低功耗、极高反应速度、可用于绕曲性面板、使用温度范围广、构造及制