STC学习:非易失存储器

程序设计目标及程序运行效果说明
程序设计目标:本程序是对24C02存储页面的0x00地址写入可变化的数据,然后读取数据,并显示在数码管上。
程序运行效果说明:
位数码管默认显示0。按下key3,要写入数据的地址加1。按下key2要写入的数据减1。按下key1,向存储器写入数据并读取数据,并显示在数码管上。数码管左边2位(第一、第二位)是写入的地址,数码管中间两位(第四、第五位)是写入的数据,数码管右边两位(第七、第八位)是显示从非易失存储器读取的数据。

程序相关电路及工作原理说明
非易失性存储器(nonvolatile memory)是掉电后数据能够保存的存储器,它不用定期地刷新存储器内容。这包括所有形式的只读存储器(ROM),像是可编程只读存储器(PROM)、可擦可编程只读存储器(EPROM)、电可擦除只读存储器(EEPROM)和闪存。在许多常见的应用中,微处理器要求非易失存储器来存放其可执行代码、变量和其他暂态数据(例如采集到的温度、光照等数据)。
1.24C02工作电路及其工作原理
在这里插入图片描述
本实验采用24C02芯片, 24C02通过IIC_SCL和IIC_SDA与单片机相连,单片机以IIC总线的方式对24C02进行读写。24C02是一个2K位串行E2PROM,内部含有256个8位字节。
(1)管脚配置
在这里插入图片描述
(2)管脚描述
在这里插入图片描述
(3)寻址方式
寻址信号由一个字节构成,高7位为地址位,最低位为方向位,用以表明主机与从器件的数据传送方向。方向位位0,表明主机接下来对从器件进行写操作;方向位位1,表明主机接下来对从器件进行读操作。
在这里插入图片描述
A0,A1和A2对应器件的管脚1,2和3;

a8,a9和a10对应存储阵列地址字地址;
(4)读/写时序
写一个字节时序
在这里插入图片描述
读一个字节时序
在这里插入图片描述
如图,写一个字节时序,第一个DEV SEL是器件选择信号,器件选择的范围为(000~111),总共可以选择8个24C02芯片器件。但是本实验只用到了1个24C02芯片,所以对应的器件管脚地址A2A1A0为000。第二个信号BYTEADDR是地址信号,表示要对哪一个地址进行操作,第三个DATA IN则是写入的数据。而读操作则是多了一步,DEV SEL和BYTE ADDR后,还有一个DEV SEL,但此信号的最后一位为高,表示是读操作,随后从机会把相应地址的数据发送给主机。

2.I2C总线介绍
I2C(Inter-Integrated Circuit)总线是由PHILIPS公司开发的两线式串行总线,用于连接微控制器及其外围设备。是微电子通信控制领域广泛采用的一种总线标准。它是同步通信的一种特殊形式,具有接口线少,控制方式简单,器件封装形式小,通信速率较高等优点。I2C总线硬件结构图如下:
在这里插入图片描述
SCL是时钟线,SDA是数据线

I2C总线信号包括有,启始信号,停止信号和应答信号,在程序用分别用函数void start()、void stop()、void respons()表示。24C02的存储空间为2K,每一次写和读操作都只能操作已选定的对应24C02芯片的地址数据。要切换操作的芯片,需要重新发送寻址信号,在void write_add(uchar addr,uchar date)函数中,第一个寻址信号writebyte(0xa0),已经固定了本程序只能在第0个芯片进行操作(注:0xa0化为二进制为1010000,其中,前4位1010是固定不能改变的,最后一位0代表写操作,1代表读操作,而中间三位则是代表不同芯片地址的编号),若要改变需要操作的芯片,则只需改变中间三位即可。
(1)I2C位传输
数据传输:SCL为高电平时,SDA线若保持稳定,那么SDA上是在传输数据bit;若SDA发生跳变,则用来表示一个会话的开始或结束

数据改变:SCL为低电平时,SDA线才能改变传输的bit
在这里插入图片描述
(2)I2C开始和结束信号
开始信号:SCL为高电平时,SDA由高电平向低电平跳变,开始传送数据。
结束信号:SCL为高电平时,SDA由低电平向高电平跳变,结束传送数据。
在这里插入图片描述
(3)I2C应答信号
主设备每发送完8bit数据后等待从设备的ACK。
即在第9个clock,从IC发ACK,SDA会被拉低。
若没有ACK,SDA会被置高,这会引起Master发生RESTART或STOP流程。

测试方法
(1)用STC ISP打开并下载HEX文件;

(2)默认下载后数码管显示0;

(3)按下key3,要写入的数据的地址加1。按下key2要写入的数据加1。按下key1,向

存储器写入数据并读取数据,并显示在数码管上。数码管左边2位(第一、第二位)是写入的地址,数码管中间两位(第四、第五位)是写入的数据,数码管右边两位(第七、第八位)是显示从非易失存储器读取的数据。

代码如下:

#include<STC15F2K60S2.H>
#define uint unsigned int
#define uchar unsigned char
#define FOSC 11059200L  //晶振频率
#define T_ms 0.1        //定时时间为0.1ms
#define NMAX_KEY 10
sbit Key1=P3^2;     //按下key1,向存储器写入数据并读取该地址的数据,显示在数码管上。
sbit Key2=P3^3;     //按下key2,要写入的数据加1
sbit Key3=P1^7;     //按下key3,要写入的地址加1
sbit led=P2^3;      //LED灯与数码管切换引脚
sbit SDA=P4^0;      //I2C总线的数据线
sbit SCL=P5^5;      //I2C总线的时钟线
sbit Beep=P3^4;
uchar duan[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//数码管段选,显示0-f
uchar wei[]={0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07};//数码管位选
uchar flag1;                //数码管循环扫描变量
uchar write_addr;           //写入地址
uchar write_date;           //写入数据
uchar read_date;            //读出数据
uchar G_count;              //定时器0中断计数值
uint Key1_count;            //KEY1键在1ms内达到低电平的次数
uint Key2_count;            //KEY2键在1ms内达到低电平的次数
uint Key3_count;            //KEY3键在1ms内达到低电平的次数
uint Key_count;             //由100递减到0
bit flg_1ms;                //表示1ms时间到
bit Key1_C;/*key1当前的状态*/
bit Key1_P;/*key1前一个状态*/
bit Key2_C;/*key2当前的状态*/
bit Key2_P;/*key2前一个状态*/
bit Key3_C;/*key3当前的状态*/
bit Key3_P;/*key3前一个状态*/
void SMG1(uchar date1,uchar date2,uchar date3)//数码管显示函数
{
    flag1++;
    if(flag1==8)
        flag1=0;
    P0=0x00;
    P2=wei[flag1];
    switch(flag1)
    {
        case 0:
            P0=duan[date1/16];break;
        case 1:
            P0=duan[date1%16];break;
        case 2:
            P0=0x40;break;
        case 3:
            P0=duan[date2/16];break;
        case 4:
            P0=duan[date2%16];break;
        case 5:
            P0=0x40;break;
        case 6:
            P0=duan[date3/16];break;
        default:
            P0=duan[date3%16];break;
    }
}
void KEY_init() //按键消抖模块初始化
{
    G_count=0;
    flg_1ms=0;
    Key1_C=1; /*key1当前的状态*/
    Key1_P=1; /*key1前一个状态*/
    Key2_C=1; /*key2当前的状态*/
    Key2_P=1; /*key2前一个状态*/
    Key3_C=1; /*key3当前的状态*/
    Key3_P=1; /*key3前一个状态*/
    Key1_count=0x80+NMAX_KEY/3*2;
    Key2_count=0x80+NMAX_KEY/3*2;
    Key3_count=0x80+NMAX_KEY/3*2;
    Key_count=NMAX_KEY;
}
void delay()        //延时4us
{
    ;;
}
void IIC_init()     //I2C总线初始化
{
    SCL=1;
    delay();
    SDA=1;
    delay();
}
void start()        //主机启动信号
{
    SDA=1;
    delay();
    SCL=1;
    delay();
    SDA=0;
    delay();
}
void stop()         //停止信号
{
    SDA=0;
    delay();
    SCL=1;
    delay();
    SDA=1;
    delay();
}
void respons()      //从机应答信号
{
    uchar i=0;
    SCL=1;
    delay();
    while(SDA==1&&(i<255)) //表示若在一段时间内没有收到从器件的应答则
        i++;                //主器件默认从期间已经收到数据而不再等待应答信号。
    SCL=0;
    delay();
}
void writebyte(uchar date) //对24C02写一个字节数据
{
    uchar i,temp;
    temp=date;
    for(i=0;i<8;i++)
    {
        temp=temp<<1;
        SCL=0;
        delay();
        SDA=CY;
        delay();
        SCL=1;
        delay();
    }
    SCL=0;
    delay();
    SDA=1;
    delay();
}
uchar readbyte()            //从24C02读一个字节数据
{
    uchar i,k;
    SCL=0;
    delay();
    SDA=1;
    delay();
    for(i=0;i<8;i++)
    {
        SCL=1;
        delay();
        k=(k<<1)|SDA;
        delay();
        SCL=0;
        delay();
    }
    delay();
    return k;
}
void write_add(uchar addr,uchar date) //对24C02的地址addr,写入一个数据date
{
    start();
    writebyte(0xa0);
    respons();
    writebyte(addr);
    respons();
    writebyte(date);
    respons();
    stop();
}
uchar read_add(uchar addr)      //从24C02的addr地址,读一个字节数据
{
    uchar date;
    start();
    writebyte(0xa0);
    respons();
    writebyte(addr);
    respons();
    start();
    writebyte(0xa1);
    respons();
    date=readbyte();
    stop();
    return date;
}
void IO_init()       //IO口初始化,变量初始化
{
    P2M1=0x00;    //设置P0口和P2^3推挽输出
    P2M0=0x08;
    P0M1=0x00;
    P0M0=0xff;    //按键无需推挽
    led=0;        //关LED显示
    P0=0x00;
	Beep=0;            //确保蜂鸣器关
    write_addr=0x00;      //写入地址初始化
    write_date=0x00;      //写入数据初始化
}
void Timer0_Init()       //计时器0初始化
{
    TMOD=0x00;        //计时器0工作方式0,16位自动重装计数
    AUXR=0x80;        //1T模式,T0x12=1,
    EA=1;             //开总中断
    ET0=1;            //开定时器0中断
    TH0=(65536-T_ms*FOSC/1000)/256;  //给定时器赋初值
    TL0=(65536-T_ms*FOSC/1000);  
    TR0=1;                              //启动定时器
}
void Timer_T0()interrupt 1  //定时器0中断函数
{
    G_count++;
    if(G_count==10)
    {
        flg_1ms=1;       //1ms时间到,fla_1ms=1
        G_count=0x00;
    }
    SMG1(write_addr,write_date,read_date); //在定时器中短中调用数码管显示函数
}
int main()                 //主函数
{
    IO_init();            //IO口初始化
    Timer0_Init();        //定时器0初始化
    KEY_init();           //按键消抖模块初始化
    IIC_init();            //IIC总线初始化
    while(1)
    {
        while(flg_1ms)  //1ms时间到 判断按键状态
        {
            flg_1ms=0;
            read_date= read_add(write_addr); //读出地址为write_addr的数据
            if(Key1==0)
                Key1_count--;
            if(Key2==0)
                Key2_count--;
            if(Key3==0)           //按键是按下状态
                Key3_count--;
            Key_count--;           //总的次数减1
            if(Key_count==0)      //10次完了
            {
                if(Key1_count<0x80)
                {
                    Key1_C=0;
                    if(Key1_P==1)       //下降沿(按键做动作)
                    {
                        Key1_P=0;
                        write_add(write_addr,write_date); //向地址write_addr写入数据
                    }
                }
                if(Key1_count>=0x80)
                {
                    Key1_C=1;
                    if(Key1_P==0)
                        Key1_P=1;          //上升沿(假设不做动作那就继续)
                }
                if(Key2_count<0x80)
                {
                    Key2_C=0;
                    if(Key2_P==1)         //下降沿(按键做动作)
                    {
                        Key2_P=0;
                        write_date++;           //按键数据加1
                        if(write_date==0xff) //假如输入的数据大于0xff,则变为0x00。
                            write_date=0x00;
                    }
                }
                if(Key2_count>=0x80)
                {
                    Key2_C=1;
                    if(Key2_P==0)
                        Key2_P=1;  //上升沿(假设不做动作那就继续)
                }
                if(Key3_count<0x80)
                {
                    Key3_C=0;
                    if(Key3_P==1) //下降沿(按键做动作)
                    {
                        Key3_P=0;
                        write_addr++;           //按键写入地址+1
                        if(write_addr==0xff)
                            write_addr=0;
                    }
                }
                if(Key3_count>=0x80)
                {
                    Key3_C=1;
                    if(Key3_P==0)
                        Key3_P=1;  //上升沿(假设不做动作那就继续)
                }
                Key1_count=0x80+NMAX_KEY/3*2; //新一轮的判断
                Key2_count=0x80+NMAX_KEY/3*2;
                Key3_count=0x80+NMAX_KEY/3*2;
                Key_count=NMAX_KEY;
            }
        }//read_date=read_add(read_addr); 读数据函数放在此处数码管会无法及时显示
    }
}
尽管闪存和其他非易失性存储技术已广泛用于实现嵌入式文件系统,但对于某些嵌入式应用程序来说可能太复杂了。在许多情况下的内存可以最有效地用作已预先初始化的数据结构。这种方法需要对数据完整性进行某种管理。   NV-SRAM简介 在现代计算机系统中,存在大量内存。其中大多数是名称不合时宜的随机存取存储器(RAM)。这个名称意义不大,因为当今所有内存都是随机访问的。当工程师谈论RAM时,它们的意思是易失性半导体存储器,只要加电,就可以无限期地对其进行读写操作。并非总是这样。在计算机的早期,程序/数据存储的最常见形式是“核心内存”。按照现代标准,这是笨重的(更不用说昂贵了!),但是具有有用的特性:它是非易失性的。读取或写入数据需要电源,但不需要保留电源。在核心内存掉电的情况下,数据将无限期保持不变。有趣的是,掉落或振动的核心内存可能会破坏其内容,尽管现代计算机和大多数嵌入式系统的工作内存主要是RAM,但拥有大量可用的非易失性RAM(NV-SRAM)仍然很有用。这可以使用闪存或某些其他具有非易失性的存储技术(例如MRAM)来实现,也可以是带有受保护电源(例如电池)的常规RAM。嵌入式系统中NV-SRAM有许多可能的用途:•程序代码和常量数据的存储,在启动时会复制到RAM中。尽管通常可以选择执行NV-SRAM,但是某些NV-SRAM技术的速度(访问时间)使这种方法没有吸引力。•重启后保留设备设置参数。许多设备都是用户可配置的。此信息需要存储在某个地方。•长时间缓冲采集的数据,不受电源故障的影响。一个简单的例子可能是在数码相机中存储照片。
目前版本支持MCU设备: ■ARM7 32位处理器 【LPC2214、LPC2292、LPC2131、LPC2132、LPC2136、LPC2138】 ■ARM Cortex-M3 处理器【STM32F10x家族】 ■STC 8051 微处理器(没有讨厌的狗头金广告) 【STC89C51RC、STC89C52RC、STC89C55RD+、STC89C516RD+】 对于"ARM7 32位处理器"、"ARM Cortex-M3 处理器"、"STC 8051 微处理器" 三类中的 各个型号选择,在ISP下载时可以忽略型号,软件自动识别并控制相应的下载命令。 当前版本支持 Oscillator: 8.000M、11.0592M、12.00M、18.432M、22.1184M、24.00M、36.864M ! ComMagic 是一款让您爱不释手的工具软件,串口调试 + 常用MCU设 备ISP上载软件, 使用完全免费! 本软件可以在Win95/98、Win2000、 WinNT、WinXP、Vista下面运行.软件功能主要为: 1.接收从串口进来的数据并在窗口显示. 2.所接收到的数据数据显示方式可以选择为字符方式或者HEX方式 3.中文显示无乱码,且不影响速度 4.串口波特率可以选择为110bps-115200bps 5.可以选择“5、6、7、8”四种数据长度. 6.可以选择为“1、1.5、2”三种停止位.(1.5停止位需要硬件支持) 7.第9位数据可以选择为“无、奇校验、偶校验、1、0”四种方式. 8.串口设置和字符串操作等设置在程序关闭时自动保存,打开时自 动载入. 9.可以选择在发送窗口按键即发送该键值. 10.可以在字符串输入框输入您想发送的字符串,并发送. 11.可以在字符串输入框输入您想发送的HEX数据串,数据的值从00 到FF,没有任何限制. 12.可以定时重复发送数据,并可以设置发送时间间隔. 13.可以在发送字符串时选择发送新行,即自动加上回车换行. 14.可以自由控制当前串口的DTR、RTS信号线的输出状态. 15.可以打开一个文本文件或者一个二进制文件预览其内容,查看方 式可以是文本方式. 16.可以打开一个文本文件或者一个二进制文件并以当前波特率发送 到串口. 17.可以保存窗口内容到一个文本文件 18.可以即时显示发送的字节数和接收到的字节数,按清除窗口将会 清零. 19.带有常用MCU设备ISP上载功能. 20.这是个绿色软件,单个文件即可执行,不会给您的机器增加任何 负担.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

布布要成为最负责的男人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值