51单片机

型号:STC89C52RC/LE52RC这个型号

Keil上选型号:Atmel-AT89C52

Lecture 2 LED

2.1 点亮一个LED

贴片二极管:绿色为负极

电阻读数:

image-20250901181105938

单片机里CPU通过程序控制寄存器,寄存器再通过驱动器实现对应功能

image-20250901181600015

进制转换

image-20250901183337379

LED 0xFE 11111110(末位D1亮)

2.2 可控时长流水灯

延时操作:stc-isp中软件延时计算器

image-20250901190647834

可控延时函数 延时1ms

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <INTRINS.H>
void Delay(unsigned int xms) //@12.000MHz
{
unsigned char i, j;
while(xms--)
{
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
}

Lecture 3 独立按键

3 独立按键控制LED移位

消抖操作:

1
2
3
4
5
6
7
8
9
10
11
12
int main(){
while(1)
{
if(P3_1==0)
{
Delay(20);
while(P3_1==0);
Delay(20);
P2--;
}
}
}

其中的Delay(20)用于消抖延时

while(P3_1==0);按下状态时始终执行空语句,用于等待按键释放的操作

否则按键按下时LED会一直闪

if语句和P2–构成了按键控制LED亮灭

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
int main()
{
char lednum=0x00;
P2=~0x01;
while(1)
{
if(P3_1==0)
{
Delay(20);
while(P3_1==0);
Delay(20);
lednum++;
if(lednum>=8)lednum=0;
P2=~(0x01<<lednum);
}

if(P3_0==0){
Delay(20);
while(P3_0==0);
Delay(20);
lednum--;
if(lednum<0)lednum=7;
P2=~(0x01<<lednum);
}
}
}

两个对于lednum的操作是用于处理二进制溢出的问题,实现满8归0

取反是因为P2为低电平(0)时才亮

Lecture 4 数码管

4.1 静态数码管显示

image-20250902110711424

上面为共阴极接地,下面为共阳极接VCC

原理图分析:

image-20250902113227203

P22 P23 P24均为输入端 8个LED端口为输出端

image-20250902113436698

C端为高端 通过CBA的十进制数保证Yx为低电平

现在目标让第三个输出6

得先让LED6为0 也就是上面74LS138 LED6为输出 也就是Y5 也就是CBA二进制输入为5

image-20250902115420847

4.2 动态数码管显示

核心思想:利用极短暂延时 人眼无法察觉使得同时显示多个数字

要注意消影问题

利用清零操作

位选 段选 **清零 ** 位选 段选

image-20250902151836231

上面在Nixie函数中的延时+归零 避免重影

下面直接的调用

Lecture 5 调试

Lecture 5.1 模块化编程

模块化编程:记得放.c和.h

根目录下加两个文件:Delay.c Delay.h

小心不要写成Dealy了!!!!

Delay.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14

void Delay(unsigned int xms) //@12.000MHz
{
unsigned char i, j;
while(xms--)
{
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
}#include "Delay.h"

Delay.h文件里面的调用

1
2
3
4
5
6
7
#ifndef __DELAY_H__
#define __DELAY_H__

void Delay(unsigned int xms); // 函数声明

#endif

main函数里面记得引用回来 #include "Delay.h"

Lecture 5.2 LCD1602调试工具

image-20250902152326398

Lecture 6 矩阵键盘

6.1矩阵键盘的读取

6.1.1扫描

数码管扫描(输出扫描) 原理:显示第1位→显示第2位→显示第3位→……,然后快速循环这个过程,最终实现所有数码管同时显示的效果矩阵键盘扫描(输入扫描) 原理:读取第1行(列)→读取第2行(列) →读取第3行(列) → ……,然后快速循环这个过程,最终实现所有按键同时检测的效果以上两种扫描方式的共性:节省I/O口

image-20250902174712091

矩阵扫描利用按列扫描方式

错误调用

1
2
3
4
5
6
7
void main(){
LCD_Init();
while(1)
{
LCD_ShowNum(1,1,Matrixkey(),3);
}

  • 每微秒调用一次 Matrixkey()(非常频繁)
  • 即使没有按键,也会在屏幕上显示”000”
  • 屏幕疯狂闪烁,根本看不清内容
  • 当真的有按键时,可能因为刷新太快而错过显示

正确调用

1
2
3
4
5
6
7
8
while(1)
{
char key = Matrixkey();
if(key)
{
LCD_ShowNum(2,1,key,2);
}
}

要有一个 if(key)的判断 避免速度过快 疯狂调用 显示00 if的目的是锁住显示函数

不加判断的话 他会迅速变成00

6.2 矩阵键盘密码锁

贴一下源码 注意一下几个条件判断以及结果的复位清零操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include <REGX52.H>
#include "Delay.h"
#include "LCD1602.h"
#include "Matrixkey.h"

// 1-9为基础密码 10为密码"0" 11为确认 12为取消 13-16无用处
void main(){
int k, num = 0;
int Password = 1206;
char key;
int inputComplete = 0; // 添加标志位,表示输入是否完成

LCD_Init();

while(1)
{
if(!inputComplete) // 如果输入未完成,显示密码输入界面
{
LCD_ShowString(1, 1, "Password: "); // 使用空格清除整行
LCD_ShowString(2, 1, " "); // 清除第二行

key = Matrixkey();

if(key)
{
if(key <= 10)
{
k = key % 10;
num = num * 10 + k;
LCD_ShowNum(2, 1, num, 4);
}
else if(key == 11) // 确认键
{
inputComplete = 1; // 标记输入完成
}
else if(key == 12) // 取消键
{
num = 0; // 清零
LCD_ShowNum(2, 1, num, 4);
}

Delay_ms(100);
}
}
else // 输入完成后显示验证结果
{
// 清除整个屏幕
LCD_ShowString(1, 1, " "); // 第一行清空
LCD_ShowString(2, 1, " "); // 第二行清空

if(num == Password)
{
LCD_ShowString(1, 1, "***Congratulations!***");
}
else
{
LCD_ShowString(1, 1, "***Don't give up!***");
}

// 添加延时,然后重置状态
Delay_ms(2000);
num = 0;
inputComplete = 0;
}
}
}

Lecture 7 定时

7.1 定时器原理

一、作用:

1、计时系统c2、替代长时间的delay

二、工作模式

T1: 16位定时器/计数器

三、中断系统

image-20250906203723434

image-20250906203741075

四、定时器相关寄存器

定时器功能的关键是控制寄存器的状态

image-20250906205541837

1、TCON寄存器的每一位

image-20250907140100129

2、TMOD寄存器

image-20250907140247406

image-20250907140318909

一开始配置M1=0 M0=1使得定时器按照模式1工作

GATE 门控端给0 由TR0单独控制是否计数(结合与非门)

3、中断寄存器

image-20250907142228928

image-20250907140940357

EA:enable all

4、定时器功能

image-20250906222725762

Ti是外部引脚 外部给的脉冲实现计数器功能

OSC为系统时钟,晶振周期为12Mhz

7.2 定时器的应用(代码:按键控制LED流水灯)

一、编程步骤:

1、初始化

(1)配置M1=0 M0=1使得定时器按照模式1工作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <REGX52.H>

void Timer0Init(void) //1微秒@12.000MHz
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0xFF; //设置定时初值
TH0 = 0xFF; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0=1;
EA=1;
PT0=0;
}

/*void Timer0_Routine() interrupt 1
{
static unsigned int T0count=0;
TH0=(65535-1000)/256;
TL0=(65535-1000)%256;//重置初值
T0count++;
if(T0count>=1000)//1ms*1000=1s 用于实现led 1s的反复闪烁
{
T0count=0;//复原
P2_0=~P2_0;//用于检验是否中断
}
}

Lecture 8 串口

8.1 串口通信

1、串口是一种应用十分广泛的通讯接口,串口成本低、容易使用、通信线路简单,可实现两个设备的互相通信。单片机的串口可以使单片机与单片机、单片机与电脑、单片机与各式各样的模块互相通信,极大的扩展了单片机的应用范围,增强了单片机系统的硬件实力。

2、51自带UART 进行单品机的串口通信

3、

简单双向串口通信有两根通信线(发送端TXD和接收端RXD)

TXD与RXD要交叉连接

当只需单向的数据传输时,可以直接一根通信线

当电平标准不一致时,需要加电平转换芯片

image-20250906210828538

若设备2无法自我供电 利用VCC来供电

image-20250906211324726

通信接口

image-20250906211907417

串行通信分类

(1)按传输方向

全双工:打电话

半双工:对讲机

单工:遥控器与电脑

(2)按同步时钟信号区分

同步通信:有CLK引脚 时钟同步 格式为信息帧

异步通信:无 格式为固定的数据帧 要求有相同的波特率(码元的传输速率) 比特率:每秒传输了多少二进制位数

image-20250906212305846

三、51单片机的串口:异步全双工的UART串口

image-20250908160250571

image-20250908162504834

串口模式

image-20250908154601734

3、串口控制寄存器SCON

1、SMO、SM1:控制工作方式

SM2:多机通信 若只有两个设备 设置为0即可

REN:允许接受位

​ 为1时表示允许该设备接收数据

​ 0:禁止

TB8:发送校验位

RB8:接受校验位

​ 模式1暂时不需要校验 设置为0 用到串口校验再说

TI: 发送中断

RI:接受中断

image-20250908160621633

image-20250908160648442

4、串口数据缓冲寄存器SBUF

用于接收串口收发的数据

自动处理起始位和停止位

5、初值寄存器TH1 TL1

T1 波特率发生器 串口中使用模式2双八位自动重装

image-20250908163132435

所以此时要注意定时器中选择的01(模式1)波特率发生器时10(模式2)

image-20250908161318540

不用给T1开中断 因为定时器可以自己赋初值

6、T1初值设置(波特率相应的计算)

image-20250908161528061

代码

1
2
3
4
5
6
7
8
9
TMOD &= 0x0F;		//控制定时器1 定时器0为0000
TMOD |= 0x20; //定时器1为0010(模式2)
TL0 = 0xFF; //设置定时初值
TH0 = 0xFF; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0=1;
EA=1;
PT0=0;

不过初始化可以考v了 stc-isp中的波特率计算器

注意这里的每一步配置

image-20250908163823124

7、单片机通过串口发送数据

main函数里面 无需用中断

image-20250908171055022

8、单片机通过串口接收数据

串口接收数据是需要打开REN(接收)并且打开串口中断的

image-20250908165846600

控制ES EA即可

1
2
3
4
5
6
7
8
9
10
11
12
13
void Uart_Init(void)		//4800bps@11.0592MHz
{
PCON |= 0x80; //使能波特率倍速位SMOD
SCON = 0x50; //8位数据,可变波特率
TMOD &= 0x0F; //清除定时器1模式位
TMOD |= 0x20; //设定定时器1为8位自动重装方式
TL1 = 0xF4; //设定定时初值
TH1 = 0xF4; //设定定时器重装值
ET1 = 0; //禁止定时器1中断
TR1 = 1; //启动定时器1
EA=1;//打开串口中断控制 这是新加的!!!
ES=1;//打开串口中断控制 这是新加的!!!
}
1
2
3
4
5
6
7
8
9
10
11
12
13
void UART_Routine(void) 	interrupt 4
{
if(RI==1)
{
P2=~SBUF;
UART_SendByte(SBUF);
RI=0;
}
if(TI==1)
{
TI=0;
}
}

9、数据显示模式

跟ASCII码值有关

HEX模式:底层传输的二进制/十六进制数据

文本模式:ASCII码值编码后的实际数据

Lecture 9 LED点阵屏

9.1 LED点阵屏理论知识

![image-20250908202705909](C:\Use rs\fluorine\AppData\Roaming\Typora\typora-user-images\image-20250908202705909.png)

image-20250908181439965

扫描方式:可逐行或逐列

image-20250908182605841

行控制阳极(看74HC595的三个输入端):DP 列控制阴极:P0

image-20250908201437276

image-20250908200525791

右边列为输出缓存

QH’会被接到其他74HC595 不断套娃

不过要先推前面的再推后面的

每次传输SER SERCLK上升沿时向下移位 只到满8后RCLK上升沿时锁存

9.2 制作图片显示以及动画

1、符号说明

1
2
3
sbit RCK=P3^5;//RCLK
sbit SCK=P3^6;//SRCLK
sbit SER=P3^4;//SER

这里取位很妙

SER=Byte&0x80;这一步取最高位的位 要么是1000 0000=256 (非0即1)或0000 0000(0即0)

实现取位的目的

取位操作

1
2
3
4
5
6
7
8
9
10
11
12
void _74HC595_WriteByte(unsigned char Byte)
{
unsigned char i;
for(i=0;i<8;i++)
{
SER=Byte&(0x80>>i);//这里很妙取位可以保证 依次& 1000 0000 0100 0000 ...
SCK=1;//上升沿移位
SCK=0;//SCK复位操作
}
RCK=1;
RCK=0;
}

记得消影

段选 位选 延时 位清0 段选 位选…

Lecture 10 DS1302

10.1 DS1302实时时钟

单片机时钟芯片:计时精度高 会占用芯片 无法断电

时钟芯片会带有备用电池 休眠状态下依然工作显示时间

RTC:实时时钟,是一种集成电路,通常称为时钟芯片

数据手册内容

image-20250909145353921

image-20250909145408284

**VCC2是主电源 VCC1是备用电源!!!**需要注意

命令字用于负责操作在哪里读出或者写入

image-20250909150707242

实际上只需要看时钟相关的寄存器地址定义即可

image-20250909150911500

时序定义

读写操作要先传输命令字再传输数据

每次先传最低位

image-20250909151419962

10.2 代码篇:DS1302时钟&可调时钟

1、对DS1302时钟模块的输入进行控制: SCLK I/O CE

image-20250909152221562

image-20250909160110237

要注意内部寄存器利用BCD码来记数

也就是将低四位表示个位 高四位表示十位..以此类推

所以最低位1010是10 个位不可能是10

所以写入数据时 要加十进制转化位bcd码 而读取时要把BCD码转化为十进制


51单片机
http://example.com/2025/09/01/51单片机/
发布于
2025年9月1日
许可协议