《单片机原理及运用》开课了(_)!!要好好学习,搞钱啊!!!

感谢B站UP猪知弦的教学指导

文章中安装的Keil软件和Proteus软件在我的个人网盘,这里要感谢吕老师上传的教学文件以及孜孜不倦的教导

文章中涉及的源码,均以我自己的单片机为准,例如我的LED灯有总开关,如果你没有请自行修改代码,祝你学有所成(_),我会注明实际和理论的代码

一、单片机开发与仿真环境搭建

简单的部分我就直接照搬吕老师的课件了,部分需要注意的地方我会做GIF演示,侵删

1.1 Keil 软件

1.1.1 Keil软件的简介

大家安装完Keil软件肯定是要学习使用的,这里不建议将软件汉化,软件本来原生态的样子就很看,要学会适应英化的软件。

Keil C51 是德国 Keil Software 公司(现已被 ARM 收购)推出的 8051 系列的 IDE(Integrated Development,集成开发环境)。它不仅支持汇编语言开发,更支持 C/C++等高级语言开发单片机。其可以完成从工程建立和管理、编译、链接、目标代码生成、软件仿真调试等完整的开发流程。

1.1.2 Keil C51的工作界面

1

1.1.3 Keil C51建立工程

首先你需要在电脑文件资源管理器中新建一个文件夹用来存放你的工程,下面是新建工程的GIF演示。注意的AT89C52是吕老师要求的,如果不一样请换成你们的,如果直接找AT89C52是找不到的,要先找对应的厂商然后点击+就可以看到相对应的芯片型号了。

1.1.4 建立/编辑C语言源程序文件

下面是GIF演示,比较简单,一定要记得后缀是.c

以上,我们就完成了最基本的软件使用,下面是一些进阶使用。

1.1.5 工程的设置

在工程建立后,还需要对工程进行设置。在 μVision5 的上方工具栏中,右击工程名 Target 1 框旁的魔术棒 ,即打开工程设置对话框。啊,好多我也不懂,看老师PPT吧,这些你说重要吧它也不是那么重要,看一遍过去吧。





1.1.6 Keil C51 的Debug

大家都知道Debug在调试程序中重要地位,所以我将Keil C51的Debug单独拿出来学习。

源程序编写完毕后还需要编译和链接才能够进行软件和硬件仿真。① 编译;②排错;在程序的编译/链接中,如果用户程序出现错误,还需要修正错误后重新编译/链接,重新烧录程序查看错误现象,十分消耗时间。因此需要单步调试,一步一步的查看代码运行效果来加快对错误的排查速度。

进入DEBUG模式后,黄色箭头为汇编程序运行位置光标,蓝黄三角形为当前 C 语言运行位置,指向当前等待运行程序行。其界面如下,其中单个黄色箭头为汇编程序运行位置光标(汇编一生之痛)。

在 μVision5 中,有 4 种DEBUG运行方式 :单步跟踪(Step Into),单步运行(Step Over),运行到光标处(Run to Cursor line),全速运行(Go)。

下面是使用Debug进行调试的GIF录像,测试用例如下:

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
// 理论
#include<reg52.h>

sbit led0 = P0^0;
sbit led1 = P0^1;
sbit led2 = P0^2;
sbit led3 = P0^3;


sbit led4 = P0^4;
sbit led5 = P0^5;
sbit led6 = P0^6;
sbit led7 = P0^7;


void main ()
{
led0 = 0;
led1 = 0;
led2 = 0;
led3 = 0;


led4 = 0;
led5 = 0;
led6 = 0;
led7 = 0;


}

1.1.7 存储空间资源的查看和修改

这一部分对我来说还是太早了,先看PPT吧



1.2 Proteus软件

1.2.1 Proteus软件的简介

① Proteus 是英国 Lab Center Electronics 公司推出的用于仿真单片机及其外围设备的EDA工具软件。

② Proteus 与 Keil C51 配合使用,可以在不需要硬件投入的情况下,完成单片机 C 语言应用系统的仿真开发。

③ Proteus 具有高级原理布图(proteus)、混合模式仿真(PROSPICE)、PCB 设计以及自动布线(ARES)等功能。

1.2.2 Proteus简单使用

继续看PPT吧,一定要看啊,我是太懒了就不想打字😀









1.2.3 Proteus 8 与 Keil C51 的联合使用

下面进行基本演示:① 将源程序编译、链接生成HEX文件,这里需要将工程设置中的Output设置中的Create HEX File勾选上。

1.2.4 Proteus 画出单片机的最小系统

使用Proteus选择元器件的方法如下

电气连接方法如下

1.2.5 实战教学:流水灯的实现

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
// 实际
#include <reg52.h>

sbit LED=P0^0;
sbit ADDR0 = P1^0;
sbit ADDR1 = P1^1;
sbit ADDR2 = P1^2;
sbit ADDR3 = P1^3;
sbit ENLED = P1^4;

void delay(unsigned int t) {
unsigned int i, j;
for(i=0;i<t;i++)
for(j=0;j<120;j++);
}

void led()
{
int i=0;
for(i=0;i<8;i++)
{
P0=~(0x01<<i);
delay(50);
}
}

void main(){

ENLED = 0;
ADDR3 = 1;
ADDR2 = 1;
ADDR1 = 1;
ADDR0 = 0;

while(1){
led();
}
}

搭建如图所示仿真电路,这里采用共阳极二极管接法

然后对C源码进行编译生成HEX文件,之后导入芯片中

1.3 STC-ISP软件

1.3.1 STC-ISP软件的简介

STC-ISP是一款又 STC 研发的单片机程序下载烧录软件,是针对 STC 系列单片机而设计的,可下载 STC89 系列、STC12 系列和 STC15 等系列的 STC 单片机,使用简便。

1.3.2 STC-ISP软件的使用

安装好STC-ISP和驱动后,当我们电脑插上板子之后,打开设备管理器,查看端口,我这里是COM12

1.3.3 实战教学: 点亮第一个LED灯

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 实际
#include<reg52.h>

sbit LED = P2^0;
sbit ADDR0 = P1^0;
sbit ADDR1 = P1^1;
sbit ADDR2 = P1^2;
sbit ADDR3 = P1^3;
sbit ENLED = P1^4;


void main() {
ENLED = 0;
ADDR3 = 1;
ADDR2 = 1;
ADDR1 = 1;
ADDR0 = 0;

LED = 0;
while(1);
}

二、实战教学以及作品展示

2.1 数码管

2.1.1 数码管原理

pass

2.1.2 静态数码管显示

1
2
3
4
5
6
7
8
9
10
11
12
// 显示单个数字 0
// 理论
#include<reg52.h>

void seg() {
P2 = 0x3F; //0011 1111 -> 0x3F
}

void main(){
seg();
while(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
// 显示零到九的循环
// 理论
unsigned char s[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F};

void delay(unsigned int n) {
unsigned int i = 0,j = 0;
for (i=0; i<n; i++)
for(j=0; j<120; j++);
}


void seg() {
int i=0;
for(i=0; i<10; i++)
{
P2 = s[i];
delay(800);
}
}

void main() {
while(1) {
seg();
}

}

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
// 实际
// 0~F显示
#include<reg52.h>

unsigned char code LedChar[] = {0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E}; // 0~F的真值表,使用code关键字使char类型LedChar放入Flash而不放入RAM,之所以放入Flash是因为我们不需要改变该真值表的值

sbit ADDR0 = P1^0;
sbit ADDR1 = P1^1;
sbit ADDR2 = P1^2;
sbit ADDR3 = P1^3;
sbit ENLED = P1^4;

void main() {
unsigned char cnt = 0; // 记录T0中断次数
unsigned char sec = 0; // 记录经过的秒数

ENLED = 0; //使能 U3,选择数码管 DS1
ADDR3 = 1; //为 T0 赋初值 0xB800
ADDR2 = 0;
ADDR1 = 0;
ADDR0 = 0; //启动 T0

TMOD = 0x01; //设置 T0 为模式 1
TH0 = 0xB8;
TL0 = 0x00;
TR0 = 1;

while(1) {
if (TF0 == 1) { //判断 T0 是否溢出
TF0 = 0; //T0 溢出后,清零中断标志
TH0 = 0XB8; //并重新赋初值
TL0 = 0x00;
cnt++; //计数值自加 1
if (cnt >= 50){ //判断 T0 溢出是否达到 50 次
cnt = 0; //达到 50 次后计数值清零
P0 = LedChar[sec]; //当前秒数对应的真值表中的值送到 P0 口
sec++; //秒数记录自加 1
if (sec >= 16){ //当秒数超过 0x0F(15)后,重新从 0 开始
sec = 0;
}
}
}
}
}

2.1.3 动态数码管显示

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
#include<reg52.h>

unsigned char str[] = {0x76, 0x79, 0x38, 0x38, 0x3F}; //HELLO 的段选
unsigned char wei[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}; //共阴极的线选

void delay(unsigned int n) {
unsigned int i = 0,j = 0;
for (i=0; i<n; i++)
for(j=0; j<120; j++);
}


void seg() {
int i=0;
for(i=0; i<5; i++) // 线选为5
{
P3 = ~wei[i];
P2 = str[i];
delay(5); // 调小点是为了视觉残留的效果
}
}

void main() {
while(1) {
seg();
}

}

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
// 实际
#include<reg52.h>

sbit ADDR0 = P1^0;
sbit ADDR1 = P1^1;
sbit ADDR2 = P1^2;
sbit ADDR3 = P1^3;
sbit ENLED = P1^4;

unsigned char code LedChar[] = {0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E}; // 数码管显示字符转换表表
unsigned char LedBuff[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // 数码管显示缓冲区,初值0xFF确保启动时都不亮
void main() {
unsigned char i = 0; //动态扫描索引
unsigned int cnt = 0; // 记录T0中断次数
unsigned long sec = 0; //记录经过的秒数

ENLED = 0;
ADDR3 = 1;

TMOD = 0x01; // 设置T0模式为1 -> 0x01
TH0 = 0xFC; //为T0设置初值为0xFC67,定时为1ms,TH高
TL0 = 0x67; // TL低
TR0 = 1; // 启动T0

while(1) {
if (TF0 == 1) { // 判断T0是否溢出
TF0 = 0;
TH0 = 0xFC; //为T0重新赋值0xFC67
TL0 = 0x67;
cnt++; //计数器自加1
if (cnt >= 1000) { // 判断T0溢出是否达到1000次
cnt = 0; // 达到1000次后计数值清零
sec++; // 秒计数加1
// 以下代码将sec按十进制位从低到高依次提取并转为数码管显示字符
LedBuff[0] = LedChar[sec%10];
LedBuff[1] = LedChar[sec/10%10];
LedBuff[2] = LedChar[sec/100%10];
LedBuff[3] = LedChar[sec/1000%10];
LedBuff[4] = LedChar[sec/10000%10];
LedBuff[5] = LedChar[sec/100000%10];
}
// 以下代码完成数码管动态扫描刷新
if (i == 0){
ADDR2 = 0; ADDR1 = 0; ADDR0 = 0; i++; P0 = LedBuff[0];
}
else if (i == 1) {
ADDR2 = 0; ADDR1 = 0; ADDR0 = 1; i++; P0 = LedBuff[1];
}
else if (i == 2) {
ADDR2 = 0; ADDR1 = 1; ADDR0 = 0; i++; P0 = LedBuff[2];
}
else if (i == 3) {
ADDR2 = 0; ADDR1 = 1; ADDR0 = 1; i++; P0 = LedBuff[3];
}
else if (i == 4) {
ADDR2 = 1; ADDR1 = 0; ADDR0 = 0; i++; P0 = LedBuff[4];
}
else if (i == 5) {
ADDR2 = 1; ADDR1 = 0; ADDR0 = 1; i = 0; P0 = LedBuff[5];
}
}
}
}
### 2.2