51单片机存储篇:EEPROM(I2C)

先认识I2C通信

基本概述 

IICInter-Integrated Circuit)其实是IICBus简称,所以中文应该叫集成电路总线,它是一种串行通信总线,使用多主从架构,由飞利浦公司在1980年代为了让主板、嵌入式系统或手机用以连接低速周边设备而发展。I²C的正确读法为“I平方C”("I-squared-C"),而“I二C”("I-two-C")则是另一种错误但被广泛使用的读法。自2006年10月1日起,使用I²C协议已经不需要支付专利费,但制造商仍然需要付费以获取I²C从属设备地址。

I2C总线是一种同步、半双工,带数据应答的二线制串行总线。它只需要两根线即可在连接于总线上的器件之间传送信息。

主器件用于启动总线传送数据,并产生时钟以开放传送的器件,此时任何被寻址的器件均被认为是从器件。

在总线上主和从、发和收的关系不是恒定的,而取决于此时数据传送方向。

如果主机要发送数据给从器件,则主机首先寻址从器件,然后主动发送数据至从器件,最后由主机终止数据传送;如果主机要接收从器件的数据,首先由主器件寻址从器件,然后主机接收从器件发送的数据,最后由主机终止接收过程。在这种情况下.主机负责产生定时时钟和终止数据传送。

IIC是为了与低速设备通信而发明的。

比如:本来高速设备一个周期发送1位,但是低速设备5个周期才能接收1位。两者速率无法同步,现在通过主设备控制1个时钟频率,使得主从设备能够在同一个时钟频率下工作。

IIC的传输速率比不上SPI。

通信速率一般(kbps级别),不适合语音、视频等信息类型。

主要用途:SoC和周边外设之间的通信(典型的如EEPROM、电容触摸IC、各种sensor等)

物理接口:SCL + SDA
SCL(serial clock):时钟线,传输CLK信号,一般是I2C主设备向从设备提供时钟的通道。
SDA(serial data):数据线,通信数据都通过SDA线传输。

通信特征:串行、同步、非差分、低速率

  • I2C属于串行通信,所有的数据以位为单位在SDA线上串行传输。
  • 同步通信就是通信双方工作在同一个时钟下,一般是通信的A方通过一根CLK信号线传输A自己的时钟给B,B工作在A传输的时钟下。所以同步通信的显著特征就是:通信线中有CLK。
  • 非差分。因为I2C通信速率不高,而且通信双方距离很近,所以使用电平信号通信。
  • 低速率。I2C一般是用在同一个板子上的2个IC之间的通信,而且用来传输的数据量不大,所以本身通信速率很低(一般几百Kbps,不同的I2C芯片的通信速率可能不同,具体在编程的时候要看自己所使用的设备允许的I2C通信最高速率,不能超过这个速率)

突出特征1:主设备+从设备
I2C通信的时候,通信双方地位是不对等的,而是分主设备和从设备。通信由主设备发起,由主设备主导,从设备只是按照I2C协议被动的接受主设备的通信,并及时响应。
谁是主设备、谁是从设备是由通信双方来定的(I2C协议并无规定),一般来说一个芯片可以只做主设备、也可以只做从设备、也可以既当主设备又当从设备(软件配置)。

突出特征2:可以多个设备挂在一条总线上(从设备地址)
I2C通信可以一对一(1个主设备对1个从设备),也可以一对多(1个主设备对多个从设备)。

在这里插入图片描述
主设备来负责调度总线,决定某一时间和哪个从设备通信。

注意:同一时间内,I2C的总线上只能传输一对设备的通信信息,所以同一时间只能有一个从设备和主设备通信,其他从设备处于“休眠”状态,不能出来捣乱,否则通信就乱套了(广播然后匹配的思路)。
每一个I2C从设备在通信中都有一个I2C从设备地址,这个设备地址是从设备本身固有的属性,然后通信时主设备需要知道自己将要通信的那个从设备的地址,然后在通信中通过地址来甄别是不是自己要找的那个从设备。(这个地址是一个电路板上唯一的,不是全球唯一的)

关于I2C的上拉电阻:I2C协议规定,总线空闲时两根线都必须为高。

小对比:

SPI是通过片选来一对多的(需要多根片选线),I2C是通过地址识别来一对多的(用SDA来实现即可,无需额外的地址线)。

时序:起始和结束

I2C总线上有2种状态;空闲态(所有从设备都未和主设备通信,此时总线空闲)和忙态(其中一个从设备在和主设备通信,此时总线被这一对占用,其他从设备必须歇着)。


整个通信分为一个周期一个周期的,两个相邻的通信周期是空闲态。每一个通信周期由一个起始位开始,一个结束位结束,中间是本周期的通信数据。


起始位并不是一个时间点,起始位是一个时间段,在这段时间内总线状态变化情况是:SCL线维持高电平,同时SDA线发生一个从高到低的下降沿。
与起始位相似,结束位也是一个时间段。在这段时间内总线状态变化情况是:SCL线维持高电平,同时SDA线发生一个从低到高的上升沿。

时序:数据传输

每一个通信周期的发起和结束都是由主设备来做的,从设备只有被动的响应主设备,没法自己自发的去做任何事情。
主设备在每个通信周期会先发8位的数据(其中7位是从设备地址,还有1位表示主设备下面要写入还是读出,所以最多能连接2^7即128个从设备)到总线。然后总线上的每个从设备都能收到这个地址,并且收到地址后和自己的设备地址比较看是否相等。如果相等说明主设备本次通信就是给我说话,如果不相等说明这次通信与我无关,不用听了不管了。

时序:ACK应答

ACK,Acknowledge character,确认字符


发送方发送一段数据后,接收方需要回应一个ACK。这个响应本身只有1个bit位,不能携带有效信息,只能表示2个意思(要么表示收到数据,即有效响应;要么表示未收到数据,无效响应)
在某一个通信时刻,主设备和从设备只能有一个在发(占用总线,也就是向总线写),另一个在收(从总线读)。

应答信号是可以被配置有或者没有的。

数据格式如下:


I2C通信时的基本数据单位也是以字节为单位的,每次传输的有效数据都是1个字节(8位)。


起始位及其后的8个clk都是主设备在发送(主设备掌控总线),此时从设备只能读取总线来得知主设备发给它的信息;然后到了第9周期,按照协议规定从设备需要发送ACK给主设备,所以此时主设备必须释放总线(也就是主设备把SDA总线置为高电平然后不要动),同时从设备试图拉低总线发出ACK。

如果从设备拉低总线失败,或者从设备根本就没有拉低总线,则主设备看到的现象就是总线在第9周期仍然一直保持高,这对主设备来说,意味着我没收到ACK,主设备就认为刚才给从设备发送的8字节不对(接收失败)

写数据和读数据

发送一个字节:

SCL低电平期间,主机将数据位依次放到SDA线上(高位在前),然后拉高SCL,从机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可发送一个字节。

接收应答:

在发送完一个字节之后,主机在下一个时钟接收一位数据,判断从机是否应答,数据0表示应答,数据1表示非应答。

接收一个字节:

SCL低电平期间,从机将数据位依次放到SDA线上(高位在前),然后拉高SCL,主机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可接收一个字节。

发送应答:

在接收完一个字节之后,主机在下一个时钟发送一位数据,数据0表示应答,数据1表示非应答

如果是读取数据,我们就会知道是否读到数据,所以一般无需发送应答。

数据传输顺序

SPI可以选择从最低位或者最高位开始发送,之前学DS1302的时候是从最低位开始发的,可是在学习SPI的时候又说是从最高位开始发送的。不知道哪个是对的。后面查资料,又说其实是可以配置的,具体情况具体对待的,看手册就知道了,不必纠结。


虽然很多器件都是用了某种接口协议,比如SPI I2C等等,但是对这些接口协议的封装会根据具体功能有所差异,不是完全一致的。另一方面,有些协议是要收费的,所以制造商只能借鉴,不能完全使用。只是说,用了那些接口协议的思想。

I2C一般是从最高位开始传输的。

关于释放总线

某个引脚如果接了地,那么无法将其拉高;如果引脚是高电平,那么可以控制其拉高或者拉低。因为接地的控制力比高电平高。

释放总线,就是将总线置1,为什么,因为只有置1,其他设备才有可能改变其状态。如果是置0,那么其它设备就无法将其从接地中改变状态。

关于边沿触发和电平触发

在之前学习SPI时,是边沿触发。这里的EEPROM,或者说I2C,是高电平触发。

关于触发方式,在硬件上来说,涉及到触发器和锁存器。如果器件处于锁存状态,那么输出就不受输入影响。如果想要某时刻的输入作用后能影响到输出,就要触发,那么,什么时候触发,就是个问题。

有些电路设计让电平改变的瞬间触发,过了这个点,就会进入锁存状态。这就是边沿触发,分为上升沿触发、下降沿触发以及双边触发。

有些电路设计,只要电平处于某种状态,比如高电平或者低电平,就可以一直触发。比如当处于高电平时,某电路只要输入一变,输出就会随之改变。

更多可以参考:下降沿触发和低电平触发的区别 - 知乎

我们进一步来考虑编程中的处理。

比如,上升沿触发,和高电平触发,在编程时有何区别?

这里很容易搞错,比如上升沿触发是SCL = 0;SCL = 1;高电平触发也是SCL = 0; SCL = 1;

这里,一个是关注瞬间,一个是关注阶段。

写数据时:

先将数据放到数据线上,然后边沿触发就会在改变的瞬间写入;高电平触发在高电平期间就会起作用。按照原理来看,高电平触发可以先放数据然后再将电平拉高,也可以先将电平拉高然后再放数据。有的设计中,要求高电平至少持续一段时间才能将数据写入。

读数据时:

主设备先拉高电平,在电平拉高的瞬间,从设备的数据就会被发送到总线上,然后主设备去获取该数据。如果是电平触发,那么,主设备要先拉高电平,在高电平期间从设备就可以将数据放到总线上,然后主设备就可以去读取。读取完就可以将电平拉低。有的设计中,要求高电平至少持续一段时间从设备才能将数据放到总线上。

在两种方式时,到达高电平之后,都会有一段延时。虽然都是延时,但是作用不同。

边沿触发后的延时是为了构建正常的时钟信号,对延时的时长没有具体要求。

电平触发的延时是为了构建起作用的条件,有时,还会对延时时间有要求。

//写入///
//边沿触发
SDA = 1;
SCL = 0;
SCL = 1;

//电平触发
SDA = 1;
SCL = 0;
SCL = 1;
Delay();
//或者
SCL = 0;
SCL = 1;
SDA = 1;
Delay();

//读数据///
//边沿触发
SCL = 0;
SCL = 1;
DAT = SDA;

//电平触发
SCL = 0;
SCL = 1;
DAT = SDA;
Delay();
SCL = 0;

具体用什么方式来触发,一般取决于从设备所使用的协议要求。

另外,是否需要延时,延时多久,都需要取决于从设备的协议要求,如果不延时,频率过快,从设备可能承受不了这么高的频率,从而导致数据获取错误。

EEPROM

ROM:Read-Only Memory,只读存储器,断电后也能保存数据。

EEPROM:Electrically Erasable Programmable Read-Only Memory,电可擦除可编程只读存储器,最小读写单位为字节,读写速度很慢。

EEPROM存在系统中的2种形式:内置在单片机内部,外部扩展。


EEPROM如何编程?

  • I2C接口底层时序
  • 器件定义的寄存器读写时序

24C02

相关内容查看数据手册,这里提供一篇参考文章:

24C02是一个2Kbit的串行EEPROM存储芯片,可存储256个字节数据。工作电压范围为1.8V到6.0V,具有低功耗CMOS技术,自定时擦写周期,1000000次编程/擦除周期,可保存数据100年。24C02有一个16字节的页写缓冲器和一个写保护功能。通过I2C总线通讯读写芯片数据,通讯时钟频率可达400KHz。

可以通过存储IC的型号来计算芯片的存储容量是多大,比如24C02后面的02表示的是可存储2Kbit的数据,转换为字节的存储量为2*1024/8 = 256Byte;又比如24C04后面的04表示的是可存储4Kbit的数据,转换为字节的储存量为4*1024/8 = 512Byte;以此来类推其它型号的存储空间。
24C02的管脚图如下:

在这里插入图片描述

VCC和VSS是芯片的电源和地,电压的工作范围为:+1.8V~+6.0V。
A0、A1、A2是IC的地址选择脚。
WP是写保护使能脚。
SCL是I2C通讯时钟引脚。
SDA是I2C通讯数据引脚。


下图为芯片从地址:

在这里插入图片描述
以看出对于不同大小的24Cxx,具有不同的从器件地址。由于24C02为2k容量,也就是说只需要参考图中第一行的内容。

芯片的寻址:
AT24C设备地址为如下:前四位固定为1010,A2~A0为由管脚电平。AT24CXX EEPROM Board模块中默认为接地。A2-A0=000,最后一位表示读写操作。所以AT24Cxx的读地址为0xA1,写地址为0xA0。

也就是说:
写24C02的时候,从器件地址为10100000(0xA0);
读24C02的时候,从器件地址为10100001(0xA1)。

片内地址寻址(注意从设备地址和内部寻址的区别):

芯片寻址可对内部256B中的任一个进行读/写操作,其寻址范围为00~FF,共256个寻址单位。
具体解释:
由于24C02只有256个字节的存储空间,所以只需要1个字节就可以寻址完24C02的存储空间,但是无法寻址完更大容量的存储IC,比如24C04的存储容量是512字节,需要9个bit的地址位才能寻址完。那怎么办呢?该芯片的解决方法是将A0作为其中一个地址位,由上图可以看到,24C04的设备地址内是没有A0参数的,也就是说24C04的A0引脚是不起作用的,这样也就造成了在I2C总线上只能同时挂载4个24C04芯片。其它存储器如24C08、24C16也可以这么类推。

24C02的WP引脚是写保护引脚,当WP引脚接高电平时,24C02只能进行读取操作,不能进行写操作。只有当WP引脚悬空或接低电平时,24C02才能进行写操作。

读写EEPROM

在写EEPROM的时候,要连续写入三个字节,第一个字节为从设备地址,第二个字节为要寻址的内存地址,第三个字节为要写入的数据;

同理,读的时候也要先写入从设备地址,然后就是寻址地址,然后开始读数据。

至于是读还是写,上面说了,用从设备地址的最后一位来表示,1表示读,0表示写。

页写:只发一次从设备地址和寻址首地址,之后直接写入多个数据,就会从首地址开始,按连续地址依次写入。

读写代码实现

注意,截至至2022年7月28日凌晨0点46分,此代码是有问题的,没法跑通,编译没问题,就是从设备怎么都没法应答成功,相对应的应该是数据没有写入到ROM中,不知道哪里出了问题,先放这,等查查资料再看看。

eeprommain.c

/************************************************************
*日期:2022年7月27日
*作者:星辰
*文件内容:eeprom功能程序入口
**************************************************************/

#include "uart.h"
#include "eeprom.h"
 
/*************************************************************
*
*函数入口
*
**************************************************************/
void main(void)
{
    uchar flag1 = 0, flag2 = 0;
    uchar uartArr[1] = {0};
    
    I2cStart();
    flag1 = I2cWrite(0xA0) && I2cWrite(0x22) && I2cWrite('A'); //写入数据
    I2cStop();

    if(flag1)
    {
        I2cStart();
        flag2 = I2cWrite(0xA0) && I2cWrite(0x22);
        if(flag2)
        {
            I2cStart();
            I2cWrite(0xA1);
            uartArr[0] = I2cRead();  //读出的数据
        }
        I2cStop();
    }
    
    UartInit();
    SendSomeChar(uartArr, 1);    
}

eeprom.c

/************************************************************
*日期:2022年7月27日
*作者:星辰
*文件内容:eeprom读写
**************************************************************/

#include "eeprom.h"
#include "somedelay.h"

sbit SDA = P2^0;
sbit SCL = P2^1;

/************************************************************
*
*开始标志
*
**************************************************************/
void I2cStart()
{
    SDA = 1;
    Delay10us();
    SCL = 1;
    Delay10us();
    SDA = 0;
    Delay10us();
    SCL = 0;
    Delay10us();
}

/************************************************************
*
*结束标志
*
**************************************************************/
void I2cStop()
{ 
    SDA = 0;
    Delay10us();
    SCL = 1;
    Delay10us();
    SDA = 1;
    Delay10us();
}   

/************************************************************
*
*按字节写入
*
**************************************************************/
uchar I2cWrite(uchar dataToWrite)
{
    uchar i = 0;
    SCL = 0;

    for(i; i < 8; i++)
    {
        SDA = dataToWrite >> 7;    //从最高位开始传输
        dataToWrite <<= 1;
        Delay10us();
        SCL = 1;
        Delay10us();
        SCL = 0;
        Delay10us();
    }
    
    SDA = 1;    //释放总线
    Delay10us();
    SCL = 1;
    Delay10us();
    
    if(SDA == 0)
    {
        SCL = 0;
        Delay10us(); 
        return 1;   //返回1表示写入成功
    }
    
    SCL = 0;
    Delay10us();   
    return 0;   //返回0表示失败		
}

/************************************************************
*
*按字节读取
*读之前也要先写入从设备地址和寻址地址
**************************************************************/
uchar I2cRead()
{
    uchar charGeted = 0;
    uchar i = 0;
    SCL = 0;

    for(i; i < 8; i++)
    {
        SCL = 1;
        Delay10us();
        charGeted |= SDA;
        if(i != 7)
        {
            charGeted  <<= 1;
        }  
        SCL = 0;
        Delay10us();
    }
    
    //读的时候不用发送ACK吧?我只要看有没有读出数据就可以了呀。
    return charGeted;
}

uart.c

/************************************************************
*日期:2022年7月27日
*作者:星辰
*文件内容:串口调试
**************************************************************/

#include "uart.h"
#include "somedelay.h"

/*************************************************************
*
*初始化串口
*
**************************************************************/
void UartInit()
{
    SCON = 0x50;        //设置使用模式1,波特率可变的8位UART,接收模式可用
    TMOD = 0x20;        //配置定时器1处于模式3,8位自动重载,用作波特率发生器
    PCON = 0x80;        //使用波特率加倍
    TH1 = TL1 = 243;    //设置波特率为4800Hz
    TR1 = 1;            //打开定时器1
}

/*************************************************************
*
*串口发送字符串
*
**************************************************************/
void SendSomeChar(uchar charArr[], int len)
{    
    while(1)
    {
        int i = 0;
        
        for(i; i < len; i++)
        {
            SBUF = charArr[i];      //直接把数据扔给硬件即可,之后的由硬件完成
            while(!TI);             //等待上一个数据发完再发下一轮
            TI = 0;                 //软件复位
            Delay100us();           //必须要延时,速度太快,会出错
        }
        
        SBUF = '\r';        //直接把数据扔给硬件即可,之后的由硬件完成
        while(!TI);         //等待上一个数据发完再发下一轮
        TI = 0;             //软件复位标志位
        Delay1s();          //目标对象之间的延时
    }
}

其他的一些延时代码就不放了~~~~~~~~~~~

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

代码的分层设计

经过SPI和I2C等协议的学习,可以看出,有些外设使用了这些协议来实现功能,那么,这里就涉及到了两个层次。

底层用的就是这些协议,涉及到这些协议的基本读写。

高层,也就是具体的外设,也有一些具体的协议,通过读写特定的数据,实现具体的功能。

所以,在编程时,通常底层时序单独一个文件,高层时序单独一个文件,然后在main函数中,使用这些封装好的高层函数,而不直接使用底层函数。

  • 12
    点赞
  • 93
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
第一章.... 4 【实例1】使用累加器进行简单加法运算:... 4 【实例2】使用B寄存器进行简单乘法运算:... 4 【实例3】通过设置RS1,RS0选择工作寄存器区1:... 4 【实例4】使用数据指针DPTR访问外部数据数据存储器:... 4 【实例5】使用程序计数器PC查表:... 4 【实例6】if语句实例:... 4 【实例7】switch-case语句实例:... 4 【实例8】for语句实例:... 4 【实例9】while语句实例:... 5 【实例10】do…while语句实例:... 5 【实例11】语句形式调用实例:... 5 【实例12】表达式形式调用实例:... 5 【实例13】以函数的参数形式调用实例:... 5 【实例14】函数的声明实例:... 5 【实例15】函数递归调用的简单实例:... 5 【实例16】数组的实例:... 6 【实例17】指针的实例:... 6 【实例18】数组与指针实例:... 6 【实例19】P1口控制直流电动机实例... 6 第二章.... 8 【实例20】用74LS165实现串口扩展并行输入口... 8 【实例21】用74LS164实现串口扩展并行输出口... 10 【实例22】P0 I/O扩展并行输入口... 12 【实例23】P0 I/O扩展并行输出口... 12 【实例24】用8243扩展I/O端口... 12 【实例25】用8255A扩展I/O口... 14 【实例26】用8155扩展I/O口... 19 第三章.... 26 【实例29】与AT24系列EEPROM接口及驱动程序... 26 【实例30】EEPROM(X5045)接口及驱动程序... 30 【实例31】与铁电存储器接口及驱动程序... 33 【实例32】与双口RAM存储器接口及应用实例... 35 【实例33】与NANDFLASH(K9F5608)接口及驱动程序... 35 第四章.... 43 【实例34】独立键盘控制... 43 【实例35】矩阵式键盘控制... 44 【实例36】改进型I/O端口键盘... 46 【实例37】PS/2键盘的控制... 49 【实例38】LED显示... 53 【实例39】段数码管(HD7929)显示实例... 54 【实例40】16×2字符型液晶显示实例... 55 【实例41】点阵型液晶显示实例... 61 【实例42】LCD显示图片实例... 63 第五章.... 70 【实例43】简易电子琴的设计... 70 【实例44】基于MCS-51单片机的四路抢答器... 71 【实例45】电子调光灯的制作... 76 【实例46】数码管时钟的制作... 81 【实例47】LCD时钟的制作... 96 【实例48】 数字化语音存储与回放... 103 【实例49】电子标签设计... 112 第六章.... 120 【实例50】指纹识别模块... 121 【实例51】数字温度传感器... 121 第七章.... 124 【实例53】超声波测距... 124 【实例54】数字气压计... 125 【实例55】基于单片机的电压表设计... 132 【实例56】基于单片机的称重显示仪表设计... 133 【实例57】基于单片机的车轮测速系统... 136 第八章.... 138 【实例58】电源切换控制... 138 【实例59】步进电机控制... 140 【实例60】单片机控制自动门系统... 141 【实例61】控制微型打印机... 144 【实例62】单片机控制的EPSON微型打印头... 144 【实例63】简易智能电动车... 145 【实例64】洗衣机控制器... 149 第九章.... 152 【实例65】串行A/D转换... 152 【实例66】并行A/D转换... 153 【实例67】模拟比较器实现A/D转换... 154 【实例68】串行D/A转换... 155 【实例69】并行电压型D/A转换... 156 【实例70】并行电流型D/A转换... 156 【实例71】file:///C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\msohtmlclip1\01\clip_image002.gif接口的A/D转换... 157 【实例72】file:///C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\msohtmlclip1\01\clip_image002.gif接口的D/A转换... 161 第十章.... 164 【实例73】单片机间双机通信... 164 【实例74】单片机间多机通信方法之一... 166 【实例75】单片机间多机通信方法之二... 171 【实例76】 PC与单片机通信... 176 【实例77】红外通信接口... 178 第十一章.... 180 【实例79】单片机实现PWM信号输出... 180 【实例80】实现基于单片机的低频信号发生器... 182 【实例81】软件滤波方法... 183 【实例82】FSK信号解码接收... 186 【实例83】单片机浮点数运算实现... 187 【实例84】神经网络在单片机中的实现... 192 【实例85】信号数据的FFT变换... 194 第十二章.... 198 【实例86】file:///C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\msohtmlclip1\01\clip_image005.gif总线接口的软件实现... 198 【实例87】SPI总线接口的软件实现... 200 【实例88】1-WIRE总线接口的软件实现... 205 【实例89】单片机外挂CAN总线接口... 207 【实例90】单片机外挂USB总线接口... 210 【实例91】单片机实现以太网接口... 214 【实例92】单片机控制GPRS传输... 221 【实例93】单片机实现TCP/IP协议... 223 第十三章.... 229 【实例94】读写U盘... 229 【实例95】非接触IC卡读写... 234 【实例96】SD卡读写... 238 【实例97】高精度实时时钟芯片的应用... 242 第十四章.... 247 【实例98】智能手机充电器设计... 247 【实例99】单片机控制门禁系统... 248
内包含具体程序,仿真图、论文、焊接过程及芯片资料等详细资料,部分内容如下: 设计的主要内容及技术指标 要求温度控制系统完成以下功能: 1.基本功能 1)具有声光报警功能; 2)使用液晶显示; 3)温度上、下限报警值设定;温度上、下限报警; 4)手动方式设定温度上下限; #include //头文件 #include #include"eeprom52.h" //调用STC89C52单片机EEPROM控制程序 #include "math.h" #define uchar unsigned char //宏定义 #define uint unsigned int //宏定义 #define LCD1602_dat P0 //LCD1602的数据传输IO口 sbit LCD1602_rs=P2^5;//LCD1602命令数据控制IO sbit LCD1602_rw=P2^6; //LCD1602读写IO控制 sbit LCD1602_e=P2^7; //数据脉冲输入 sbit beep=P2^0; //蜂鸣器IO sbit led_1=P1^5; //超上限指示灯 sbit led_2=P1^6;//超下限指示灯 sbit key_1=P3^5; //设置按键 sbit key_2=P3^6;//加按键 sbit key_3=P3^7;//减按键 sbit TCL2543_EOC = P1^0; //转换结束标志IO口 sbit TCL2543_CLK = P1^1; //I/O时钟输入 sbit TCL2543_ADIN = P1^2; //串行数据输入端 sbit TCL2543_DOUT = P1^3; //串行数据输出端 sbit TCL2543_CS = P1^4; //片选脚IO float zhi; //暂存读取的输入变量 int temp; //读取DS18B20的温度数据 char temp_h,temp_l; //温度上下限制存储变量 uchar state,ms; //系统设置项变量、50ms定时变量 bit s1,beep1; //设置闪烁标志位、报警标志位 void delay(uint T) //系统延时程序 { while(T--); } // 其中 port 为通道: 通道0:port = 0x01 通道1:port = 0x02 通道2:port = 0x04 ...

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值