❤️ 专栏简介:本专栏记录了从零学习单片机的过程,其中包括51单片机和STM32单片机两部分;建议先学习51单片机,其是STM32等高级单片机的基础;这样再学习STM32时才能融会贯通。
☀️ 专栏适用人群 :适用于想要从零基础开始学习入门单片机,且有一定C语言基础的的童鞋。
🌙专栏目标:实现从零基础入门51单片机和STM32单片机,力求在玩好单片机的同时,能够了解一些计算机的基本概念,了解电路及其元器件的基本理论等。⭐️ 专栏主要内容: 主要学习51单片机的功能、各个模块、单片机的外设、驱动等,最终玩好单片机和单片机的外设,全程手敲代码,实现我们所要实现的功能。
🌴 专栏说明 :如果文章知识点有错误的地方,欢迎大家随时在文章下面评论,我会第一时间改正。让我们一起学习,一起进步。
💑专栏主页:http://t.csdn.cn/HCD8v
本学习过程参考:https://space.bilibili.com/383400717
单片机安装软件、各种资料以及源码的路径:
https://pan.baidu.com/s/1vDTN2o8ffvczzNQGfyjHng
提取码:gdzf
本节主要介绍学习AT24C02(I2C)的相关知识,包括AT24C02(I2C)基础知识介绍、本节目标等;基础知识包括存储器介绍、AT24C02介绍、I2C总线介绍、AT24C02数据帧;并利用两个小实验来写程序进行练习,分别是AT24C02数据存储以及秒表(定时器扫描按键数码管),最后附上相关代码。
文章目录
- 一、AT24C02(I2C)和本节目标
- 1.1 AT24C02(I2C)基础知识
- 1.1.1 存储器介绍
- 1.1.2 AT24C02介绍
- 1.1.3 I2C总线介绍
- 1.1.4 AT24C02数据帧
- 1.2 本节目标
- 二、AT24C02数据存储
- 三、秒表(定时器扫描按键数码管)
一、AT24C02(I2C)和本节目标
1.1 AT24C02(I2C)基础知识
1.1.1 存储器介绍
存储器分为RAM和ROM两种;RAM表示随机存储器;ROM表示只读存储器;
- RAM的特点是存储速度特别快,但是掉电会丢失;
- ROM的存储速度比较慢,但是特点是掉电后不会丢失。
所以一般都是两者结合使用,他们各自的种类如上图所示。
存储器的简化模型
存储器内部分为地址总线(横向)和数据总线(纵向);
当第一根地址总线被给到高电平1时,表示被选中,此时如果将纵向的第1,2,3根线连上,其他线不连,则表示第一根地址总线存储的数据为1110 0000
,如下图所示:
当然纵向的数据总线并不是直接连在地址总线上的,为了防止各个行之间的互相干扰,而是通过二极管的方式,如图中右侧的Mask ROM和PROM,二极管可以保证电流只是一个方向;
图中的蓝色二极管比较特殊,其很容易被击穿;当断开时,两个二极管都是断开;但是当有高电压过来时,蓝色二极管被击穿,也就成了右边的短路接通状态;因此PROM只能写入数据一次,就是因为蓝色二极管被击穿后无法恢复,只能被击穿一次;(所以单片机下载程序的过程也被成为烧录,就是烧毁的意思);
1.1.2 AT24C02介绍
AT24C02是一种掉电不丢失的存储器;其存储介质是E2PROM(电可擦除可编程ROM);使用的通讯接口是I2C总线;
引脚和应用电路
WP是写保护接口。
内部结构框图
1.1.3 I2C总线介绍
详细的介绍参考视频:https://www.bilibili.com/video/BV1Mb411e7re?p=27&vd_source=e2638d12685eef84cda913d9d67be0a9第45分钟开始,建议详细看一下这一部分。
I2C电路规范
I2C总线的时序图:
绿色代表发送一个字节;
紫色代表接收一个字节;
黄色表示发送应答;黑色表示接收应答;0表示收到;
发送一帧数据
接收一帧数据
复合格式
手册上的时序图:
1.1.4 AT24C02数据帧
1.2 本节目标
目标1:AT24C02数据存储
刚开始hi初始化后,LCD1602上显示0000,每按下K1按键,数字加一,即变成0001,再按下变成0002;按下K2时数字减1;0000减1是65535;按下K3按键是将当前数字保存下来;即使断电再上电后,按下K4按键也能恢复上次保存的数字。
目标2:秒表(定时器扫描按键数码管)
目标2实现一个秒表,按下K1按键,秒表开始工作:
再按下K1,秒表暂停;按下K2,秒表清零
按下K3,保存当前数字,
然后断电,再重新上电后,按下K4,恢复原来保存的数字
二、AT24C02数据存储
代码路径:51单片机入门教程资料\课件及程序源码\程序源码\KeilProject\12-1 AT24C02数据存储
具体代码:
#include <REGX52.H>
#include "LCD1602.h"
#include "Key.h"
#include "AT24C02.h"
#include "Delay.h"
unsigned char KeyNum;
unsigned int Num;
void main()
{
LCD_Init();
LCD_ShowNum(1,1,Num,5);
while(1)
{
KeyNum=Key();
if(KeyNum==1) //K1按键,Num自增
{
Num++;
LCD_ShowNum(1,1,Num,5);
}
if(KeyNum==2) //K2按键,Num自减
{
Num--;
LCD_ShowNum(1,1,Num,5);
}
if(KeyNum==3) //K3按键,向AT24C02写入数据
{
AT24C02_WriteByte(0,Num%256);
Delay(5);
AT24C02_WriteByte(1,Num/256);
Delay(5);
LCD_ShowString(2,1,"Write OK");
Delay(1000);
LCD_ShowString(2,1," ");
}
if(KeyNum==4) //K4按键,从AT24C02读取数据
{
Num=AT24C02_ReadByte(0);
Num|=AT24C02_ReadByte(1)<<8;
LCD_ShowNum(1,1,Num,5);
LCD_ShowString(2,1,"Read OK ");
Delay(1000);
LCD_ShowString(2,1," ");
}
}
}
AT24C02.c:AT24C02存储的相关操作
#include <REGX52.H>
#include "I2C.h"
#define AT24C02_ADDRESS 0xA0
/**
* @brief AT24C02写入一个字节
* @param WordAddress 要写入字节的地址
* @param Data 要写入的数据
* @retval 无
*/
void AT24C02_WriteByte(unsigned char WordAddress,Data)
{
I2C_Start();
I2C_SendByte(AT24C02_ADDRESS);
I2C_ReceiveAck();
I2C_SendByte(WordAddress);
I2C_ReceiveAck();
I2C_SendByte(Data);
I2C_ReceiveAck();
I2C_Stop();
}
/**
* @brief AT24C02读取一个字节
* @param WordAddress 要读出字节的地址
* @retval 读出的数据
*/
unsigned char AT24C02_ReadByte(unsigned char WordAddress)
{
unsigned char Data;
I2C_Start();
I2C_SendByte(AT24C02_ADDRESS);
I2C_ReceiveAck();
I2C_SendByte(WordAddress);
I2C_ReceiveAck();
I2C_Start();
I2C_SendByte(AT24C02_ADDRESS|0x01);
I2C_ReceiveAck();
Data=I2C_ReceiveByte();
I2C_SendAck(1);
I2C_Stop();
return Data;
}
I2C.c:I2C总线的相关操作
#include <REGX52.H>
sbit I2C_SCL=P2^1;
sbit I2C_SDA=P2^0;
/**
* @brief I2C开始
* @param 无
* @retval 无
*/
void I2C_Start(void)
{
I2C_SDA=1;
I2C_SCL=1;
I2C_SDA=0;
I2C_SCL=0;
}
/**
* @brief I2C停止
* @param 无
* @retval 无
*/
void I2C_Stop(void)
{
I2C_SDA=0;
I2C_SCL=1;
I2C_SDA=1;
}
/**
* @brief I2C发送一个字节
* @param Byte 要发送的字节
* @retval 无
*/
void I2C_SendByte(unsigned char Byte)
{
unsigned char i;
for(i=0;i<8;i++)
{
I2C_SDA=Byte&(0x80>>i);
I2C_SCL=1;
I2C_SCL=0;
}
}
/**
* @brief I2C接收一个字节
* @param 无
* @retval 接收到的一个字节数据
*/
unsigned char I2C_ReceiveByte(void)
{
unsigned char i,Byte=0x00;
I2C_SDA=1;
for(i=0;i<8;i++)
{
I2C_SCL=1;
if(I2C_SDA){Byte|=(0x80>>i);}
I2C_SCL=0;
}
return Byte;
}
/**
* @brief I2C发送应答
* @param AckBit 应答位,0为应答,1为非应答
* @retval 无
*/
void I2C_SendAck(unsigned char AckBit)
{
I2C_SDA=AckBit;
I2C_SCL=1;
I2C_SCL=0;
}
/**
* @brief I2C接收应答位
* @param 无
* @retval 接收到的应答位,0为应答,1为非应答
*/
unsigned char I2C_ReceiveAck(void)
{
unsigned char AckBit;
I2C_SDA=1;
I2C_SCL=1;
AckBit=I2C_SDA;
I2C_SCL=0;
return AckBit;
}
最终的效果就是目标1.
三、秒表(定时器扫描按键数码管)
代码路径:51单片机入门教程资料\课件及程序源码\程序源码\KeilProject\12-2 秒表(定时器扫描按键数码管)
具体代码:
#include <REGX52.H>
#include "Timer0.h"
#include "Key.h"
#include "Nixie.h"
#include "Delay.h"
#include "AT24C02.h"
unsigned char KeyNum;
unsigned char Min,Sec,MiniSec;
unsigned char RunFlag;
void main()
{
Timer0_Init();
while(1)
{
KeyNum=Key();
if(KeyNum==1) //K1按键按下
{
RunFlag=!RunFlag; //启动标志位翻转
}
if(KeyNum==2) //K2按键按下
{
Min=0; //分秒清0
Sec=0;
MiniSec=0;
}
if(KeyNum==3) //K3按键按下
{
AT24C02_WriteByte(0,Min); //将分秒写入AT24C02
Delay(5);
AT24C02_WriteByte(1,Sec);
Delay(5);
AT24C02_WriteByte(2,MiniSec);
Delay(5);
}
if(KeyNum==4) //K4按键按下
{
Min=AT24C02_ReadByte(0); //读出AT24C02数据
Sec=AT24C02_ReadByte(1);
MiniSec=AT24C02_ReadByte(2);
}
Nixie_SetBuf(1,Min/10); //设置显示缓存,显示数据
Nixie_SetBuf(2,Min%10);
Nixie_SetBuf(3,11);
Nixie_SetBuf(4,Sec/10);
Nixie_SetBuf(5,Sec%10);
Nixie_SetBuf(6,11);
Nixie_SetBuf(7,MiniSec/10);
Nixie_SetBuf(8,MiniSec%10);
}
}
/**
* @brief 秒表驱动函数,在中断中调用
* @param 无
* @retval 无
*/
void Sec_Loop(void)
{
if(RunFlag)
{
MiniSec++;
if(MiniSec>=100)
{
MiniSec=0;
Sec++;
if(Sec>=60)
{
Sec=0;
Min++;
if(Min>=60)
{
Min=0;
}
}
}
}
}
void Timer0_Routine() interrupt 1
{
static unsigned int T0Count1,T0Count2,T0Count3;
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
T0Count1++;
if(T0Count1>=20)
{
T0Count1=0;
Key_Loop(); //20ms调用一次按键驱动函数
}
T0Count2++;
if(T0Count2>=2)
{
T0Count2=0;
Nixie_Loop();//2ms调用一次数码管驱动函数
}
T0Count3++;
if(T0Count3>=10)
{
T0Count3=0;
Sec_Loop(); //10ms调用一次数秒表驱动函数
}
}
最终的效果是目标2。