前言
本篇文章属于STC89C52单片机(以下简称单片机)的学习笔记,来源于B站教学视频。下面是这位up主的视频链接。本文为个人学习笔记,只能做参考,细节方面建议观看视频,肯定受益匪浅。
[1-1] 课程简介_哔哩哔哩_bilibili
一、存储器介绍
存储器介绍
易失性存储器/RAM(random access memory 随机存储器):掉电会丢失
SRAM:静态RAM速度较快
DRAM:速度不如静态RAM
非易失性存储器/ROM(read only memory 只读存储器):掉电不会丢失
Mask ROM:掩膜ROM,不能写入,只能读取
PROM:可编程ROM,只能写入一次
EPROM:可擦除可编程ROM,紫外线照射30分钟才能擦除数据
EEPROM:电可擦除可编程ROM,可以通过电来迅速擦除
存储器的简化模型
烧录一词的由来:通过烧穿二极管来实现导通
二、AT24C02介绍
引脚及应用电路
内部结构框图
三、I2C总线
1.I2C总线介绍
2.I2C电路规范
单片机的弱上拉模式
要输出低电平0时,闭合开关,无电阻经过,驱动能力较强
要输出高电平1时,断开开关,有电阻经过,驱动能力较弱
开漏输出
把上拉电阻去掉
输出低电平时,闭合开关即可
输出高电平时,断开开关,IO口处于一个浮空的状态
3.I2C时序结构
起始位与终止位
发送一个字节
接受一个字节
从机应答
I2C数据帧
发送
接受
复合格式
AT24C02数据帧
四、实例一(AT24C02数据存储)
I2C.c
#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 接受到的应答位
*/
unsigned char I2C_ReceiveAck(void)
{
unsigned char Ackbit;
I2C_SDA=1;
I2C_SCL=1;
Ackbit=I2C_SDA;
I2C_SCL=0;
return Ackbit;
}
AT24C02.c --继承I2C的字节帧形成数据帧
#include <REGX52.H>
#include "I2C.h"
// SLAVE ADDRESS+W为0xA0,SLAVE ADDRESS+R为0xA1
#define AT24C02_ADDRESS_READ 0xA0
#define AT24C02_ADDRESS_WRITE 0xA1
/**
* @brief AT24C02写入一个字节
* @param WordAddress 要写入字节的地址
* @param Data 要写入的数据
* @retval 无
*/
void AT24C02_WriteByte(unsigned char WordAddress,Data)
{
I2C_Start();
I2C_SendByte(AT24C02_ADDRESS_READ);
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_READ);
I2C_ReceiveAck();
I2C_SendByte(WordAddress);
I2C_ReceiveAck();
I2C_Start();
// 读地址
I2C_SendByte(AT24C02_ADDRESS_WRITE);
I2C_ReceiveAck();
Data=I2C_ReceiveByte();
I2C_SendAck(1);
I2C_Stop();
return Data;
}
注意:调用AT24C02_WriteByte时要延时5ms,让数据有充分的时间写入
五、实例二(秒表(定时器扫描按键数码管))
在定时器的中断函数中调用按键扫描和数码管扫描的函数,实现定时器的复用
Key.c
#include <REGX52.H>
#include "Delay.h"
unsigned char Key_KeyNum;
unsigned char Key()
{
unsigned char Temp=0;
Temp=Key_KeyNum;
Key_KeyNum=0;
return Temp;
}
unsigned char Key_GetState()
{
unsigned char KeyNumber=0;
if(P3_1==0){KeyNumber=1;}
if(P3_0==0){KeyNumber=2;}
if(P3_2==0){KeyNumber=3;}
if(P3_3==0){KeyNumber=4;}
return KeyNumber;
}
void Key_Loop(void)
{
static unsigned char NowState,LastState;
LastState=NowState;
NowState=Key_GetState();
//Last=1,Now=0检测的是松手,Last=0,Now=1检测的是按下
if (LastState==1 && NowState==0)
{
Key_KeyNum=1;
}
if (LastState==2 && NowState==0)
{
Key_KeyNum=2;
}
if (LastState==3 && NowState==0)
{
Key_KeyNum=3;
}
if (LastState==4 && NowState==0)
{
Key_KeyNum=4;
}
}
Nixie.c
#include <REGX52.H>
#include "Delay.h"
unsigned char Nixie_Buf[9]={0,10,10,10,10,10,10,10,10};
unsigned char NixieTable[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x00,0x40};
void Nixie_SetBuf(unsigned char Location,Number)
{
Nixie_Buf[Location]=Number;
}
void Nixie_Scan(unsigned char Location,Number)
{
P0=0x00;
switch(Location)
{
case 1:P2_4=1;P2_3=1;P2_2=1;break;
case 2:P2_4=1;P2_3=1;P2_2=0;break;
case 3:P2_4=1;P2_3=0;P2_2=1;break;
case 4:P2_4=1;P2_3=0;P2_2=0;break;
case 5:P2_4=0;P2_3=1;P2_2=1;break;
case 6:P2_4=0;P2_3=1;P2_2=0;break;
case 7:P2_4=0;P2_3=0;P2_2=1;break;
case 8:P2_4=0;P2_3=0;P2_2=0;break;
}
P0=NixieTable[Number];
}
void Nixie_Loop(void)
{
static unsigned char i=1;
Nixie_Scan(i,Nixie_Buf[i]);
i++;
if(i>8){i=1;}
}
main.c
#include <REGX52.H>
#include "Key.h"
#include "Nixie.h"
#include "Timer0.h"
#include "AT24C02.h"
#include "Delay.h"
unsigned char KeyNum;
unsigned char Temp;
unsigned char Min,Sec,MiniSec;
unsigned char RunFlag;
void main()
{
Timer0_Init();
while(1)
{
KeyNum=Key();
if(KeyNum==1)
{
RunFlag=!RunFlag;
}
if(KeyNum==2)
{
Min=0;
Sec=0;
MiniSec=0;
}
if(KeyNum==3)
{
AT24C02_WriteByte(0,Min);
Delay(5);
AT24C02_WriteByte(1,Sec);
Delay(5);
AT24C02_WriteByte(2,MiniSec);
Delay(5);
}
if(KeyNum==4)
{
Min=AT24C02_ReadByte(0);
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);
}
}
void Sec_Loop()
{
if (RunFlag)
{
MiniSec++;
if (MiniSec>=100)
{
MiniSec=0;
Sec++;
if (Sec>=60)
{
Sec=0;
Min++;
if (Min>=100)
{
Min=0;
}
}
}
}
}
void Timer0_Rountine() interrupt 1
{
static unsigned int T0Count1,T0Count2,T0Count3;
TH0=0xFC;
TL0=0x66;
T0Count1++;
if (T0Count1==20)
{
T0Count1=0;
Key_Loop();
}
T0Count2++;
if (T0Count2==2)
{
T0Count2=0;
Nixie_Loop();
}
T0Count3++;
if (T0Count3==10)
{
T0Count3=0;
Sec_Loop();
}
}