https://blog.csdn.net/xue11232/article/details/108281049?utm_medium=distribute.pc_category.none-task-blog-hot-18.nonecase&depth_1-utm_source=distribute.pc_category.none-task-blog-hot-18.nonecase&request_id=
方案一:
//主函数
#include <reg52.h> //用52头文件编程
#include <iic.h> //iic头文件
sfr P4=0xc0; //定义P4管脚
sbit C1=P3^0; //矩阵按键相关
sbit C2=P3^1;
sbit C3=P3^2;
sbit C4=P3^3;
sbit R1=P3^4;
sbit R2=P3^5;
sbit R3=P4^2;
sbit R4=P4^4;
unsigned char s16=0; //按键s16输入按键
unsigned char s12=0; //按键s12密码修改按键
unsigned char s8=0; //按键s8清除按键
unsigned char a1; //修改密码时的参数
unsigned char a2;
unsigned char a3;
unsigned char a4;
unsigned char a5;
unsigned char a6;
unsigned char b1=0; //
unsigned char b2=0; //输入完成的标志
unsigned char b3=0; //密码输入正确的标志
unsigned char b4=0; //密码输入错误的标志
unsigned char b5=0; //输入密码达到6位时的标志
unsigned char b6=0; //密码输入正确时,修改密码的标志
unsigned char c1=8; //读取上次存储密码第一位
unsigned char c2=8; //读取上次存储密码第二位
unsigned char c3=8; //读取上次存储密码第三位
unsigned char c4=8; //读取上次存储密码第四位
unsigned char c5=8; //读取上次存储密码第五位
unsigned char c6=8; //读取上次存储密码第六位
unsigned char c7; //首次上电的密码存储相关
unsigned char moshi=0; //显示的模式
unsigned char count_1; //中断计数
unsigned long mima=888888;
unsigned long mima_1=21; //密码第一位
unsigned long mima_2=21; //密码第二位
unsigned long mima_3=21; //密码第三位
unsigned long mima_4=21; //密码第四位
unsigned long mima_5=21; //密码第五位
unsigned long mima_6=21; //密码第六位
unsigned long mima_7=0; //密码错误时显示 原数
unsigned char num; //按键代表的数
unsigned char i; //选择第几位密码输入
unsigned char dat; //AT24C02写入的数据
unsigned char smg_buff[8]; //数码管缓存
unsigned char code SMG_Duan[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,
0x88,0x80,0xc6,0xc0,0x86,0x8e,0xbf,0x7f,0x8c,0x86,0xc8,0xff};
//数码管段选0 1 2 3 4 5 6 7 8 9 a b c d e f - . P E N 关闭
void Read_mima();
void Delay(unsigned int t) //延时函数
{
while(t--);
}
void Init_system() //系统初始化函数
{
P2=(P2 & 0x1f)|0x80; //关闭LED灯
P0=0xff;
P2=(P2 & 0x1f)|0xa0; //关闭继电器和蜂鸣器
P0=0X00;
P2=0x00;
}
//***********************************数码管显示基础函数***************************************
void Display_SMG(unsigned char value,unsigned char pos)
{
P2=(P2 & 0x1f)|0xe0;
P0=value;
P2=(P2 & 0x1f)|0xc0;
P0=0x01<<pos;
P2=0x00;
}
void ALL_smg() //关闭所有数码管以防最后一个和其他亮度不一样
{
P2=(P2 & 0x1f)|0xe0;
P0=0xff;
P2=(P2 & 0x1f)|0xc0;
P0=0xff;
P2=0x00;
}
void SMG_Display()
{
Display_SMG(SMG_Duan[smg_buff[0]],0);
Delay(500);
Display_SMG(SMG_Duan[smg_buff[1]],1);
Delay(500);
Display_SMG(SMG_Duan[smg_buff[2]],2);
Delay(500);
Display_SMG(SMG_Duan[smg_buff[3]],3);
Delay(500);
Display_SMG(SMG_Duan[smg_buff[4]],4);
Delay(500);
Display_SMG(SMG_Duan[smg_buff[5]],5);
Delay(500);
Display_SMG(SMG_Duan[smg_buff[6]],6);
Delay(500);
Display_SMG(SMG_Duan[smg_buff[7]],7);
Delay(500);
//Display_SMG(0xff,0&1&2&3&4&5&6&7);
ALL_smg();
Delay(500);
//***********************************************************************************
//********************************AT24C02写函数************************************
void Write_AT24C02Aadd(unsigned char addr,unsigned char dat)
{
IIC_Start();
Delay(1000);
IIC_SendByte(0xa0);
Delay(1000);
IIC_WaitAck();
Delay(1000);
IIC_SendByte(addr);
Delay(1000);
IIC_WaitAck();
Delay(1000);
IIC_SendByte(dat);
Delay(1000);
IIC_WaitAck();
Delay(1000);
IIC_Stop();
Delay(1000);
//*****************************************************************************************
//*******************************AT24C02读函数*************************************
unsigned char Read_AT24C02_ADD(unsigned char addr)
{
unsigned char temp;
IIC_Start();
Delay(1000);
IIC_SendByte(0xa0);
Delay(1000);
IIC_WaitAck();
Delay(1000);
IIC_SendByte(addr);
Delay(1000);
IIC_WaitAck();
IIC_Start();
Delay(1000);
IIC_SendByte(0xa1);
Delay(1000);
IIC_WaitAck();
temp=IIC_RecByte();
IIC_SendAck(1);
IIC_Stop();
return temp;
}
//**********************************************************************************
//***********************************矩阵按键函数*************************************
void Kry_stat()
{
C1=0;C2=1;C3=1;C4=1; //0 1 2 3
if(R1==0)
{
Delay(500);
if(R1==0)
{
num=3;i++;b1++;
}
while(R1==0)
{
SMG_Display();
}
}
if(R2==0)
{
Delay(500);
if(R2==0)
{
num=2;i++;b1++;
}
while(R2==0)
{
SMG_Display();
}
}
if(R3==0)
{
Delay(500);
if(R3==0)
{
num=1;i++;b1++;
}
while(R3==0)
{
SMG_Display();
}
}
if(R4==0)
{
Delay(500);
if(R4==0)
{
num=0;i++;b1++;
}
while(R4==0)
{
SMG_Display();
}
}
C1=1;C2=0;C3=1;C4=1; //4 5 6 7
if(R1==0)
{
Delay(500);
if(R1==0)
{
num=7;i++;b1++;
}
while(R1==0)
{
SMG_Display();
}
}
if(R2==0)
{
Delay(500);
if(R2==0)
{
num=6;i++;b1++;
}
while(R2==0)
{
SMG_Display();
}
}
if(R3==0)
{
Delay(500);
if(R3==0)
{
num=5;i++;b1++;
}
while(R3==0)
{
SMG_Display();
}
}
if(R4==0)
{
Delay(500);
if(R4==0)
{
num=4;i++;b1++;
}
while(R4==0)
{
SMG_Display();
}
}
C1=1;C2=1;C3=0;C4=1; // 8 9 无效 无效
if(R1==0)
{
Delay(500);
if(R1==0)
{
}
while(R1==0)
{
SMG_Display();
}
}
if(R2==0)
{
Delay(500);
if(R2==0)
{
}
while(R2==0)
{
SMG_Display();
}
}
if(R3==0)
{
Delay(500);
if(R3==0)
{
num=9;i++;b1++;
}
while(R3==0)
{
SMG_Display();
}
}
if(R4==0)
{
Delay(500);
if(R4==0)
{
num=8;i++;b1++;
}
while(R4==0)
{
SMG_Display();
}
}
C1=1;C2=1;C3=1;C4=0; //无效 清除 修改 输入
if(R1==0)
{
Delay(500);
if(R1==0)
{
s16++;i=0;Read_mima();
}
while(R1==0)
{
SMG_Display();
}
}
if(R2==0)
{
Delay(500);
if(R2==0)
{
s12++;i=0;
}
while(R2==0)
{
SMG_Display();
}
}
if(R3==0)
{
Delay(500);
if(R3==0)
{
s8++;
}
while(R3==0)
{
SMG_Display();
}
}
if(R4==0)
{
Delay(500);
if(R4==0)
{
}
while(R4==0)
{
SMG_Display();
}
}
}
}
}
//***********************************************************************
//********************************按键处理函数***************************
void Key_Dispose()
{
if(s16>=1)
{
moshi=1;
switch(i)
{
case 1:
mima_1=num;a1=mima_1;
break;
case 2:
mima_2=a1;
mima_1=num;a2=mima_1;
break;
case 3:
mima_3=a1;mima_2=a2;mima_1=num;a3=mima_1;
break;
case 4:
mima_4=a1;mima_3=a2;mima_2=a3;mima_1=num;a4=mima_1;
break;
case 5:
mima_5=a1;mima_4=a2;mima_3=a3;mima_2=a4;mima_1=num;a5=mima_1;
break;
case 6:
mima_6=a1;mima_5=a2;mima_4=a3;mima_3=a4;mima_2=a5;mima_1=num;b2=1;
break;
}
if(i>=7) i=0;
}
}
//*****************************************************************************
//**********************************数据处理函数********************************
void Stat_Dispose()
{
if(s8==1)
{
s8=0;
i=0;
mima_1=21;
mima_2=21;
mima_3=21;
mima_4=21;
mima_5=21;
mima_6=21;
}
}
if(b2==1)
{
mima_7=mima_6*100000+mima_5*10000+mima_4*1000+mima_3*100+mima_2*10+mima_1;
if(c1==mima_1 && c2==mima_2 && c3==mima_3 && c4==mima_4 && c5==mima_5 && c6==mima_6)
{
moshi=2;
s16=0;
P2=(P2 & 0x1f)|0xa0;
P0=0x10;
P2=0x00;
b3=1;
s12=0;
b2=0;
mima_1=21;
mima_2=21;
mima_3=21;
mima_4=21;
mima_5=21;
mima_6=21;
}
else
{
moshi=3;
s16=0;
P2=(P2 & 0x1f)|0x80;
P0=0XFE;
P2=0x00;
b4=1;
b2=0;
}
i=0;
}
if(moshi==1)
{
P2=(P2 & 0x1f)|0x80;
P0=0xbf;
P2=0x00;
}
if(moshi==0)
{
P2=(P2 & 0x1f)|0x80;
P0=0xff;
P2=0x00;
}
if(moshi==4)
{
P2=(P2 & 0x1f)|0x80;
P0=0x7f;
P2=0x00;
}
//*******************************************************************************
//******************************密码修改函数**************************************
void xiugai()
{
if(b3==1)
{
if(s12>=1)
{
b3=0;
b6=1;
i=0;
}
}
if(b6==1)
{
}
}
方案二:
timer.c
#include <timer.h>
void Timer0Init(void) //1毫秒@11.0592MHz
{
AUXR |= 0x80; //定时器时钟1T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0xCD; //设置定时初值
TH0 = 0xD4; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
EA=ET0=1;
}
timer.h
#ifndef _TIMER_H_
#define _TIMER_H_
#include <STC15F2K60S2.H>
void Timer0Init(void);
#endif
smg.c
#include <smg.h>
unsigned char code smgduan[16]={0XC0,0XF9,0XA4,0XB0,0X99,0X92,0X82,
0XF8,0X80,0X90,0XBF,0XFF,0XC6,0X86,0XC8,0X8C};
//10:- 11:灭 12:C 13:E 14:N 15:P
unsigned char dspbuf[8];
void smg_display(void)
{
static unsigned char i=0;
P2=(P2&0X1F)|0XE0;
P0=0XFF;
P2&=0X1F;
P2=(P2&0X1F)|0XC0;
P0=1<<i;
P2&=0X1F;
P2=(P2&0X1F)|0XE0;
P0=smgduan[dspbuf[i]];
P2&=0X1F;
if(++i==8)
i=0;
}
keybord.c
#include <keybord.h>
unsigned char i,key_value,key_press,key_sum=0;
void key_read(void)
{
}
AT24C02模块
AT24C01/02/04/08/16是一个1K/2K/4K/8K/16K位(AT24C02大小为256字节)串行CMOS E2PROM内部含有128/256/512/1024/2048个8位字节,CATALYST公司的先进CMOS技术实质上减少了器件的功耗AT24C01有一个8字节页写缓冲器 AT24C02/04/08/16有一个16字节页写缓冲器该器件通过I2C总线接口进行操作有一个专门的写保护功能。
2、AT24C02特性
①、与 400KHz I2C 总线兼容
②、1.8 到 6.0 伏工作电压范围
③、低功耗 CMOS 技术
④、写保护功能 当 WP 为高电平时进入写保护状态
⑥、页写缓冲器
⑦、自定时擦写周期
⑧、1,000,000编程/擦除周期
⑨、可保存数据100年
硬件原理图
管脚描述
A0、A1、A2、三个脚不同的电平可以形成不同的地址,最多8种。原理图中A0、A1、A2、三个脚全部接地,也就是硬件决定不可更改,默认为A0=0、A1=0、A2=0。同时写保护脚WP(原理图中为HOLD),也是接地。因此默认也是关闭写保护的。
从器件地址
- A0、A1和A2对应器件的管脚1、2和3
- P0、P1、P2对应存储阵列地址字地址。
- R/W为读写方向位,1为读,0为写。
对应AT24C02硬件原理图而言,A0、A1、A2、三个脚被硬件接地,即A0=0、A1=0、A2=0。所以AT24C02读地址为1010 0001(0xA1),AT24C02写地址为1010 0000(0xA0)。
这里再简单的介绍一下P0、P1、P2的含义,对于AT24C02而言大小为256字节,而对于AT24C08而言大小为1024(4*256)字节。假如P0=0、P1=0,即代表选中第一个256字节的首地址,P0=0、P1=0,即代表选中第一个256的首地址,P0=1、P1=0,即代表选中第二个256的首地址,以此类推。
写周期限制
写周期时间是指从一个写时序的有效停止信号到内部编程/擦除周期结束的这一段时间。在写周期期间,总线接口电路禁能,SDA保持为高电平,器件不响应外部操作。
简单的说就是,当向AT24C02执行写操作时,从发出写命令到写完成最多10ms。因此在写程序时,每次写操作后都延迟10ms以保证稳定。
读写操作时序
向指定地址写一个字节
*
函数功能:向AT24c02指定地址写一字节数据
*/
void At24c02_Write_OneByte(u8 addr,u8 data)
{
IIC_Start();
IIC_Write_Byte(At24c02_Write_Addr);//发送写地址0XA0
if(IIC_Check_Ack())return ;
IIC_Write_Byte(addr);
if(IIC_Check_Ack())return ;
IIC_Write_Byte(data);
if(IIC_Check_Ack())return ;
IIC_Stop();
delay_ms(10);//确保写完成
}
向指定地址开始读数据
*
函数功能:向AT24c02指定地址开始读数据
*/
void At24c02_Read_Data(u8 addr,u8 *data,u8 len)
{
u8 i=0;
IIC_Start();
IIC_Write_Byte(At24c02_Write_Addr);//发送写地址0XA0
if(IIC_Check_Ack())return ;
IIC_Write_Byte(addr);//写数据的地址
if(IIC_Check_Ack())return ;
IIC_Start();
IIC_Write_Byte(At24c02_Read_Addr);//发送读地址0XA1
if(IIC_Check_Ack())return ;
for(i=0;i<len;i++)
{
data[i]=IIC_Read_Byte();
if(i==len-1)IIC_Send_Ack(1);
else IIC_Send_Ack(0);
}
IIC_Stop();
}
页写
用页写 AT24C01可一次写入8个字节数据 AT24C02/04/08/16可以一次写入16个字节的数据,页写操作的启动和字节写一样,不同在于传送了一字节数据后并不产生停止信号,主器件被允许发送停止信号P,AT24C01 P=7,AT24C02/04/08/16 P=15个额外的字节,每发送一个字节数据后AT24C01/02/04/08/16 产生一个应答位并将字节地址低位加1,高位保持不变。如果在发送停止信号之前主器件发送超过P+1个字节地址计数器将自动翻转,先前写入的数据被覆盖。
简单的说,AT24C01一页为8字节,AT24C01/02/04/08/16为16字节。对于AT24C02而言,在一页内,每写一个字节写指针+1,当大于16字节时,又回到第一个字节的位置,先前写入的数据将会被覆盖。
/*
函数功能:对AT24C02指定地址页内写数据
*/
void At24c02_Write_Page(u8 addr,u8 *data,u8 len)
{
u8 i=0;
IIC_Start();
IIC_Write_Byte(At24c02_Write_Addr);//发送写地址0XA0
if(IIC_Check_Ack())return ;
IIC_Write_Byte(addr);//写数据的地址
if(IIC_Check_Ack())return ;
for(i=0;i<len;i++)
{
IIC_Write_Byte(*data++);/
if(IIC_Check_Ack())return ;
}
IIC_Stop();
delay_ms(10);
}
向指定地址跨页写数据
/*
函数功能:对AT24C02指定地址跨页写数据
*/
void At24c02_Write_Data(u8 addr,u8 *data,u8 len)
{
u8 write_len=16-addr%16;//起始页剩下的空间
if(write_len>len)write_len=len;
while(1)
{
At24c02_Write_Page(addr,data,write_len);
if(write_len==len)break;
addr+=write_len;
data+=write_len;
len-=write_len;
if(len>16)write_len=16;
else write_len=len;
}
}
型号是AT24C02的EEPROM分为32页,每一页可以存储8个字节的数据,若在同一页写入超过8字节,则超过的部分会被写在该页的起始地址(也就是一开始写好的部分会被覆盖).
为了把连续的缓冲区数组按页写入到 EEPROM,就需要对缓冲区进行分页处理.I2C_EE_BufferWrite()是根据输入的缓冲区大小参数 NumByteToWrite,计算出需要写入多少页,计算写入位置。
分页处理好之后,调用 I2C_EE_PageWrite(),这个函数是与 EEPROM进行I2C通讯的最底层函数(里面都是调用STM32库函数)
EEPROM写入 I2C_EE_BufferWrite();
u8 I2c_Buf_Write[256];
void I2C_EE_BufferWrite(u8* pBuffer, u8 WriteAddr, u16 NumByteToWrite)
{
u8 NumOfPage = 0,NumOfSingle = 0,Addr =0,count = 0;
/*计算出要写的页数和分页*/
Addr = WriteAddr % I2C_PageSize;
count = I2C_PageSize - Addr;
NumOfPage = NumByteToWrite / I2C_PageSize;
if(Addr == 0)
{
if(NumOfPage == 0)
{
I2C_EE_PageWrite(pBuffer,WriteAddr,NumOfSingle);
I2C_EE_WaitEepromStandbyState(); //检测是否为Standby状态,才可以进行下一步操作
}
else
{
while(NumOfPage--)
{
I2C_EE_PageWrite(pBuffer,WriteAddr,I2C_PageSize);
I2C_EE_WaitEepromStandbyState();
WriteAddr += I2C_PageSize;
pBuffer += I2C_PageSize;
}
if(NumOfSingle!=0)
{
I2C_EE_PageWrite(pBuffer,WriteAddr,NumOfSingle);
I2C_EE_WaitEepromStandbyState();
}
}
}
else
{
if(NumOfPage == 0)
{
I2C_EE_PageWrite(pBuffer,WriteAddr,NumOfSingle);
I2C_EE_WaitEepromStandbyState();
}
else
{
NumByteToWrite -= count;
NumOfPage = NumByteToWrite / I2C_PageSize;
NumOfSingle = NumByteToWrite % I2C_PageSize;
if(count != 0)
{
I2C_EE_PageWrite(pBuffer,WriteAddr,count)
I2C_EE_WaitEepromStandbyState();
WriteAddr += count;
pBuffer += count;
}
while(NumOfPage--)
{
I2C_EE_PageWrite(pBuffer,WriteAddr,I2C_PageSize);
I2C_EE_WaitEepromStandbyState();
WriteAddr += I2C_PageSize;
pBuffer += I2C_PageSize;
}
if(NumOfSingle != 0)
{
I2C_EE_PageWrite(pBuffer,WriteAddr,NumOfSingle);
I2C_EE_WaitEepromStandbyState();
}
}
}
}
这里每次调用完 I2C_EE_PageWrite()后,都调用了一个I2C_EE_WaitEepromStandbyState();
void I2C_EE_WaitEepromStandbyState(void)
{
vu16 SR1_Tmp = 0;
do
{
I2C_GenerateSTART(I2C1, ENABLE);
SR1_Tmp = I2C_ReadRegister(I2C1, I2C_Register_SR1);
I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter);
}while(!(I2C_ReadRegister(I2C1, I2C_Register_SR1) & 0x0002));
I2C_ClearFlag(I2C1, I2C_FLAG_AF);
I2C_GenerateSTOP(I2C1, ENABLE);
}
这里是利用了 EEPROM在接收完数据后,启动了周期写入数据的时间内不会对主机的请求做出应答的特性,利用这个库函数循环发送起始信号,若检测到 EEPROM的应答,则说明 EEPROM已经完成上一步的数据写入,进入了Standby状态,可以进行下一步操作。
EEPROM进行I2C通讯的最底层函数I2C_EE_PageWrite()
void I2C_EE_PageWrite(u8* pBuffer, u8 WriteAddr, u8 NumByteToWrite)
{
//确保SDA总线空闲时再做I2C通讯/
while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));
//产生I2C的通讯起始信号S。
I2C_GenerateSTART(I2C1, ENABLE);
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
//把前面条件变异中赋值的变量 EEPROM_ADDRESS 地址通过I2C1接口发送出去,数据传输方向为ST32的 I2C发送数据
//这里的 EEPROM_ADDRESS 地址是 EEPROM作为挂载在I2C总线上设备的寻址,并不是 EEPROM 内存存储矩阵的地址
I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter);
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
//这里把数据传输到数据寄存器,再由I2C模块根据I2C协议发送出去,但是要注意这里的输入参数是WriteAddr,根据EEPROM的页写入时序,发送I2C的地址后的第一个数据,并不一定要写入EEPROM的数据,EEPROM的数据解释为将要对存储矩阵写入的地址,WRiteAddr是在I2C_EE_PageWrite()函数时作为参数输入的,在这个实列是由I2C_EE_BufferWrite()计算出来的.
I2C_SendData(I2C1, WriteAddr);
while(! I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
while(NumByteToWrite--)
{
//这里是向 EEPROM发送要写如的数据,根据 EEPROM 的页写入时序,这些数据被写入到前面发送的页地址中,如果连续写入超过一页的最大字节数(这里是8个),则多出来的数据会重新从该页的起始地址连续写入,覆盖前面的数据.
I2C_SendData(I2C1, *pBuffer);
pBuffer++;
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMIT)TED));
}
//产生I2C的传输结束信号,完成一次I2C通讯.
I2C_GenerateSTOP(I2C1, ENABLE);
}
I2C事件检测:
在 I2C_EE_PageWrite里面还有很多的事件检测,这些都是必须的,根据STM32参考手册的序列图可以看到,在I2C的通讯过程中,会产生一系列的事件,出现时间后相应的寄存器中会产生标志位.
这个 EEPROM 的页写入就是根据 EEPROM的页写入时序来写的:
在 I2C_EE_PageWrite()函数中,先不管while语句的循环检测,就可以很清晰的看到整个代码流程就是 EEPROM 的页写入时序流程
https://blog.csdn.net/linzhihan7410/article/details/52099051?biz_id=102&utm_term=i2cWriteBuffer&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-2-52099051&spm=1018.2118.3001.4449