AT24C02介绍:
一种可以实现掉电不丢失的存储器,可用于保存单片机运行时想要永远保存的数据。
存储介质:E2PROM(电可擦除可编程 ROM)
通讯接口:I2C 总线
容量:256 字节
存储器介绍:
数据存储器(RAM):储存速度快,掉电丢失。
程序存储器(ROM):存储速度慢,掉电不丢失。
SRAM:靠触发器电路存储,不需要刷新/扫描。速度最快,成本高
DRAM:靠电容充放电存储,因为电容会漏电,所以需要不断扫描给电容补充电荷。
Mask ROM(掩膜 ROM):出厂固定,不能写入。
PROM(一次性可编程): ROM只能写 入1 次,不可擦除
EPROM:ROM紫外线整片擦除,可重复写入。
E²PROM: 电可擦 ROM电擦,按字节擦写(可重复写入)
Flash(只掌握这个):闪存,电擦,按扇区 / 块擦写
硬盘 / 光盘:外存
存储器简化模型:
这里只演示了存储器读的作用,写的作用没表示出来
地址总线:作用告诉存储器读哪一个存储单元。
CPU将地址编号放在地址总线,被地址译码器翻译,使某一行被选中接通。(每次只能选中一行)
数据总线:作用传输存储单元里的0/1数据。
CPU再从数据总线上拿到这一个存储单元的数据。
I2C总线:
是一种通用数据总线,特点:同步,半双工,数据应答。
作用:使各种设备的通信统一。
I2C总线规范:
1.所有 I2C 设备的 SCL 连在一起,SDA 连在一起
2.设备的 SCL 和 SDA 均要配置成开漏输出模式
3.SCL 和 SDA 各添加一个上拉电阻,阻值一般为 4.7KΩ 左右
对规范一的解释:总线的含义就是一根线给多个设备共用,SCL是串行时钟线,由单片机统一输出时钟,SDA是双向串行数据线,所有设备共用这条线收发数据。
对规范二解释:首先什么是开漏输出模式,如下图左边是开漏输出模式,接0时闭合,接1时浮空状态。右面是弱上拉,接0时闭合接到GND,输出0。接1时不闭合,输出1。
还可以解决多级通信干扰问题。
对规范三解释:开漏引脚都为1时,都是悬空高阻状态,没办法主动输出高电平,添加上拉电阻可以提供确定电平,实现线与逻辑。
I2C时序结构(I2C 模块运行逻辑):
起始条件:SCL高电平期间,SDA从高电平切换到低电平。
终止条件:SCL高电平期间,SDA从低电平切换到高电平。
发送一个字节:
在SCL低电平期间,主机将数据位依次放到SDA线上,然后拉高SCL,从机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述八次就能发送一个字节。
接收一个字节:
步骤大致相同,只不过是从机将数据位放在SDA线上,主机读取数据位。
发送应答:在接收完一个字节后,主机在下一时钟发送一位数据,数据0表示应答,数据1表示无应答。
接收应答:在发送完一个字节后,主机在下一时钟发送一位数据,判断从机是否应答,数据0表示应答,数据1表示无应答。
发送应答和接收应答工作原理和发送接收字符基本一致,可以看作循环的第九次。
I2C数据帧:(AT24C02模块运行逻辑)
接收一帧数据:
其中S:SLAVE ADDRESS + W 意义:发送从机地址+读写位。
这两个是固定值:SLAVE ADDRESS+W为0xA0,SLAVE ADDRESS+R为0xA1
发送一帧数据:
分为了两段:第一段(写阶段,目的:设置要读取的片内地址)因为只有写阶段才能发送片内地址
第二段:(读阶段,目的:读出刚才定位好地址里面的数据)
AT24C02数据存储代码:
建立AT24C02模块和I2C模块的作用:
I2C 模块 主要实现了 I2C 总线时序,负责 通用底层驱动。
AT24C02模块:可以在AT24C02 芯片要求的通信规则下,调用I2C底层函数。
I2C encapsulates 6 functions: (refer to the timing structure diagram for code implementation)
开始函数,结束函数 发送字节函数,接收字节函数
发送应答位函数,接收应答位函数
相关知识点:I2C_SDA=0时,无法读外部信息。只有I2C_SDA=1时才可以读到外部信息。
AT24C02中封装了2个函数:
AT24C02_WriteByte(WordAddress, Data)函数:往AT24C02指定存储地址写入1字节数据。
AT24C02_ReadByte(WordAddress)函数:在AT24C02中读取1字节数据并返回。
三个I2C_SendByte作用:
第一个:I2C 总线从机地址(找地址)
第二个:指定写到芯片里面哪个位置
第三个:Data:要保存进去的实际数据
每个I2CReceiveAck()作用:第 9 个 SCL 时钟,主机释放 SDA,等待从机拉低 SDA 返回 ACK(RA:0)
main主函数:
1.设置KeyNum用来设置按键编号,Key用来计数。
按键 1 加 1、按键 2 减 1、按键 3 保存数字到 AT24C02、按键 4 从 AT24C02 读出数字。
2.对于按下按键3代码解释:
因为1字节是8bit,256是2^8,二进制一共 16 位:[高8位][低8位],/256用来取高位,%256用来取低位。delay(5)等待5ms是因为AT24C02 芯片写完数据,需要一段时间把数据固化到 EEPROM 存储单元里面。
3.对于按下按键4代码解释:
AT24C02一次只能读取1字节(8Bit),所以分为2个地址,地址0存低8位,地址1存高8位。
AT24C02_ReadByte(0)拿到低 8 位。AT24C02_ReadByte(1) << 8拿到高八位。|是用来拼接高低八位的。
秒表(定时器扫描按键数码管)代码:
新的Key按键函数部分:
下降沿检测的按键驱动(松手触发)。
包含三个函数:
Key()给主函数读取按键编号,循环调用,读一次按键信号就自动清零,保证按键只响应一次。Temp作用:用Temp备份按键值,防止KeyNumber清零后丢失。
unsigned char Key_GetState()读取硬件引脚电平,获取当前哪个按键正在被按住(读取的是实时状态)。
void Key_Loop(void):每隔固定时间采样按键,对比前后状态识别松手动作产生按键事件给全局变量 Key_KeyNumber。
Nixie数码管部分:
数码管显示缓存区unsigned char Nixie_Buf[9]={0,10,10,10,10,10,10,10,10};
这个数组变量用来表示每一个数码管的编号:Nixie_Buf[1]就是第1个数码管
数码管段码表
unsigned char NixieTable[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x00,0x40};
这个数组变量是提供的段码(用来控制数码管显示什么数字)
Nixie_SetBuf函数:用来记录第几个数码管显示什么数字,为10,11的话还可以控制熄灭,横线
Nixie_Scan函数:一次只能点亮一个数码管,靠快速循环扫描调用它,实现多个数码管同时显示的视觉效果。
Nixie_Loop函数:是数码管循环扫描函数,静态变量i确定扫描位置。
Main主函数:有三个函数
Timer0_Init()初始化定时器,开启中断- main 进入 while (1) 死循环
- 定时器硬件每 1ms 溢出一次 → 进入中断
- 20ms 一次:扫描按键,检测 K1-K4
- 2ms 一次:数码管动态扫描
- 10ms 一次:Sec_Loop 判断 RunFlag,时间自增进位
- main 读取按键结果,处理:启停、清零、保存、读取时间
- main 调用
Nixie_SetBuf,刷新显示缓冲区 - 中断里面
Nixie_Loop轮流点亮数码管,屏幕看到分:秒:百分秒
Sec_Loop(秒表时间进位函数):定时器中断里调用的计时任务
Timer0_Routine()中断函数:有数码管扫描、按键消抖扫描、秒表计时三个任务
- 每2ms→ 跑数码管扫描
- 每10ms→ 跑秒表计时
- 每20ms→ 跑按键扫描