STC51
发表于:2021-04-05 |
字数统计: 6.4k | 阅读时长: 27分钟 | 阅读量:

1、硬件(BST-M51)

1、51最小系统

cQwm6A.png

2、流水灯

cQwM0P.png

3、CH340烧录模块

cQw1k8.png

4、USB供电

cQwYlj.png

5、数码管、WIFI、红外发送模块

cQwHcd.md.png

6、红外接收、蜂鸣器、4位按键

cQwz4S.png

7、温度传感器

cQ0kBq.png

8、复位电路

cQ0nCF.png

9、供电

cQ01D1.png

2、示例代码

1
2
3
4
5
6
7
8
9
/***************************
1、51单片机点灯(IO口控制)
****************************/
#include "reg52.h" //<reg52.h>在当前工程文件夹下搜索;“reg52.h"先在当前工程文件夹下搜索,没有则到软件安装目录找
sbit led1 = P1^0; //P1口最低位;sfr P0 = 0X80;(寄存器重命名);sbit CY = PSW^7 (PSW最高位重命名)
void main()
{
led1 = 0; //3~20mA
}
1
2
3
4
5
6
7
8
/***************************
2、51单片机点灯(总线操作法)
****************************/
#include "reg52.h"
void main()
{
P1 = 0; //0xaa = 10101010 = 170
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/***************************
3、延时语句精确计算
****************************/
#include "reg52.h"
#define uint unsigned int //#define 宏定义(#define 新名称 原内容)只能定义一次
sbit led1 = P1^0;
uint i,j;
void main()
{
while(1)
{
led1 = 0;
for(i=1000;i>0;i--)
for(j=110;j>0;j--);
led1 = 1;
for(i=1000;i>0;i--)
for(j=110;j>0;j--);
}
}
1
2
3
4
5
6
7
8
9
/****************************
4、带参数函数写法及调用
*****************************/
void delayms(unsigned int xms)
{
uint i,j;
for(i=xms;i>0;i--)
for(j=110;j>0;j--); //i=xms即延时xms毫秒
}
1
2
3
4
5
/*****************************
5、利用C51库函数实现流水灯(leil\C51\HIP\C51Lib)
*******************************/
#include<intrins.h>
_cro_(aa,1); //将aa循环左移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
28
29
30
31
/*******************************
6、数码管静态显示,BST51第一个数码管显示字符“b”(共阴数码管)
********************************/
#include "reg52.h"
#define duan P0
sbit wei1 = P2^4; //定义第一位数码管
sbit wei2 = P2^5; //定义第二位数码管
sbit wei3 = P2^6; //定义第三位数码管
sbit wei4 = P2^7; //定义第四位数码管

void main()
{
wei1 = 1;
wei2 = 0;
wei3 = 0;
wei4 = 0;
duan = 0x7c;
//0111 1100 "b" 0x3f , 0x06 , 0x5b , 0x4f , 0x66 , 0x6d ,0x7d , 0x07 , 0x7f , 0x6f , 0x77 , 0x7c ,
//0x39 , 0x5e , 0x79 , 0x71 , 0x00(0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,不显示)

while(1);
}
/***********补充************************
unsigned char code table[]={
0x3f , 0x06 , 0x5b , 0x4f ,
0x66 , 0x6d ,0x7d , 0x07 ,
0x7f , 0x6f , 0x77 , 0x7c ,
0x39 , 0x5e , 0x79 , 0x71 , 0x00}
*/
//table[0] = 0x3f

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
/**************************************
7、74HC573锁存器 段选、位选(数码管静态显示)(共阴数码管)第一个数码管显示字符“8”
74H573锁存器:20引脚,-OE脚:三态允许控制端(低电平有效)(即输出使能端)
INPUTS OUTPUT 1D~8D:数据输入端;1Q~8Q;数据输出端
-OE LE D Q LE:锁存允许端(锁存控制端)
L H H H
L H L L
L L X Q0(上一次的数据状态)
H X X Z(高阻态)
(真值表)
***************************************/
#include "reg52.h"
sbit duan = P2^6; //申明U1锁存器的锁存端
sbit wela = P2^7; //申明U2锁存器的锁存端
void main()
{
wela = 1; //打开U2锁存器
P0 = 0xFE; //送入位选信号11111110
wela = 0; //关闭U2锁存端(Q0)

duan = 1; //打开U1锁存器
P0 = 0x7F; //送入段选信号01111111
duan = 0; //关闭U1锁存器(Q0)
while(1); //程序停在这里
}
/*************补充***************************
6个数码管同时点亮,依次显示0~F,时间间隔为0.5s,循环下去
void main()
{
wela = 1; //打开U2锁存器
P0 = 0xc0; //送入位选信号11000000
wela = 0; //关闭U2锁存端(Q0)
while(1)
{
for(num=0;num<16;num++)
{
duan = 1; //打开U1锁存器
P0 = table[num]; //送入段选信号
duan = 0; //关闭U1锁存端
delayms(500); //延时0.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
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
/********************************************
8、数码管动态显示(逐渐缩短延时时长,则同时显示)(实际上是轮流显示的)
*********************************************/
void main()
{
while(1)
{
duan = 1;
P0 = table[1]; //送段选信号
duan = 0;

P0 = 0xff; //11111111送位选数据前关闭所有显示,防止打开位选锁存时(消影)
//原来段选数据通过位选锁存器造成混乱
wela = 1; //11111110送位选数据
P0 = 0xfe;
wela = 0;
delayms(500);

duan = 1;
P0 = table[2];
duan = 0;
P0 = 0xff;
wela = 1;
P0 = 0xfd; //11111101送位选数据
wela = 0;
delayms(500);

duan = 1;
P0 = table[3];
duan = 0;
P0 = 0xff;
wela = 1;
P0 = 0xfb; //11111011送位选数据
wela = 0;
delayms(500);

duan = 1;
P0 = table[4];
duan = 0;
P0 = 0xff;
wela = 1;
P0 = 0xff; //11110111送位选数据
wela = 0;
delayms(500);

duan = 1;
P0 = table[5];
duan = 0;
P0 = 0xff;
wela = 1;
P0 = 0xef; //11101111送位选数据
wela = 0;
delayms(500);

duan = 1;
P0 = table[6];
duan = 0;
P0 = 0xff;
wela = 1;
P0 = 0xdf; //11011111送位选数据
wela = 0;
delayms(500);
}
}
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
/**************************************************
*实验名:数码管动态显示(逐渐缩短延时时长,则同时显示)(实际上是轮流显示的)
***************************************************/

#include<reg52.h>
#define duan P0
#define uchar unsigned char
sbit wei1 = P2^4; //定义第一位数码管
sbit wei2 = P2^5; //定义第二位数码管
sbit wei3 = P2^6; //定义第三位数码管
sbit wei4 = P2^7; //定义第四位数码管

uchar code sz[]={0x3f , 0x06 , 0x5b ,0x4f , 0x66 , 0x6d ,0x7d ,
0x07 , 0x7f , 0x6f ,0x77 , 0x7c , 0x39 , 0x5e , 0x79 , 0x71 , 0x00};

void delay(unsigned int xms)
{
unsigned int i,j;
for(i=xms;i>0;i--) //i=xms即延时约xms毫秒
for(j=112;j>0;j--);
}

void main()
{
while(1)
{
duan = sz[4];
wei1 = 0;
wei2 = 0;
wei3 = 0;
wei4 = 1;
delay(1000);
duan = sz[3];
wei1 = 0;
wei2 = 0;
wei3 = 1;
wei4 = 0;
delay(1000);
duan = sz[2];
wei1 = 0;
wei2 = 1;
wei3 = 0;
wei4 = 0;
delay(1000);
duan = sz[1];
wei1 = 1;
wei2 = 0;
wei3 = 0;
wei4 = 0;
delay(1000);
}
}

3、中断

cGZAG6.png

1、中断源(中断嵌套,中断优先级)

​ 51有5个,52有6个(多一个T2定时器/计数器2);

​ 默认优先级(由高到低):

  1. INT0——外部中断0,P3.2口,低电平或下降沿引起(序号:0;入口地址:0003H);
  2. INT1——外部中断1,P3.3口,低电平或下降沿引起(序号:1;入口地址:000BH);
  3. T0——定时器/计数器0中断,由T0计数器计满回零引起(序号:2;入口地址:0013H);
  4. T1——定时器/计数器1中断,由T1计数器计满回零引起(序号:3;入口地址:001BH);
  5. T2——定时器/计数器2中断,由T2计数器计满回零引起(序号:4;入口地址:0023H);
  6. TI/IR——串行口中断,串行端口完成一帧字符发送/接收后引起(序号:5;入口地址:002BH);

2、中断允许寄存器IE:

​ 设定各个中断源的打开和关闭,是特殊功能寄存器,字节地址:A8H,位地址:A8H~AFH,可进行位寻址(即可对寄存器的每一位进行单独操作,复位时,IE全被清零。

cGnTS0.md.png

EA:全局中断允许位;

**–**:无效位;

ET2:定时器/计数器2中断允许位(为1时打开,为0时关闭);

ES:串行口中断允许位;

ET1:定时器/计数器1中断允许位;

EX1:外部中断1中断允许位;

ET0:定时器/计数器0中断允许位;

EX0:外部中断0中断允许位。

3、中断优先级寄存器IP:

特殊功能寄存器,B8H~BFH。可位寻址,复位时,全清零

cGug91.md.png

**–**:无效位;

PS:串行口中断优先级控制位(为1定义为高优先级中断);

PT1:定时器/计数器1中断优先级控制位;

PX1:外部中断1中断优先级控制位;

PT0:定时器/计数器0中断优先级控制位;

PX0:外部中断0中断优先级控制位;

按优先级可形成中断嵌套。

4、单片机的定时器中断

​ 定时器/计数器实质是加1计数器(16位),由高8位和低8位两个寄存器组成:TMOD(定时器/计数器工作方式寄存器)

TCON(控制寄存器,控制T0、T1的启动停止及设置溢出标志)

cGMmsf.md.png

加1计数器输入计数脉冲两个来源;由系统的时钟振荡器输出脉冲经12分频后送来;T0或T1引脚输入的外部脉冲源,每来一个脉冲计数器加1,加到全1时,在输入一个脉冲,计数器回零,且计数器的溢出使TCON寄存器中TF0或TF1置1,向CPU发送中断请求(定时器/计数器中断允许时)

5、定时器/计数器工作方式寄存器TMOD

​ 字节地址:89H,不能位寻址,用来确定定时器的工作方式及功能选择,复位时全清零

cUZZHU.md.png

TMOD高四位设置定时器1,低四位用于设置定时器0;

GATE:门控制位

GATE=0,定时器/计数器启动与停止仅受TCON寄存器中的TRX(X=0,1)来控制;

GATE=1,定时器/计数器启动与停止由TCON寄存器中TRX和外部中断引脚(INT0或INT1)上的电平状态来共同控制。

C/T:定时器模式(=0)和计数器模式(=1)选择位;

M1M0:工作方式选择位

(每个定时器/计数器都有4种工作方式)

cUeNLV.md.png

6、定时器/计数器控制寄存器TCON

​ 字节地址:88H~8FH,可位寻址,TCON寄存器用来控制定时器的启、停,标志定时器溢出和中断情况。复位时全清零。

cUmK61.md.png

TF1、TR1、TF0和TR0位用于定时器/计数器;IE1、IT1、IE0和IT0位用于外部中断。

TF1:定时器1溢出标志(当定时器1溢出时,由硬件使TF1置1,并且申请中断。进入中断服务程序后,由硬件自动清0。)(如果使用定时器的中断,则该位完全不用人为去操作,但是如果用软件查询方式,当查询到该位置1后,就需要用软件清0。)

TR1:定时器1运行控制位。(由软件清0关闭定时器1.当GATE=1,且INT1为高电平时,TR1置1启动定时器1;当GATE=0 时,TR1置1启动定时器1。

TF0:定时器0溢出标志,功能及操作方法同TF1。

TR0:定时器0运行控制位,同TR1。

IE1:外部中断1请求标志。

。。。。。。。。。

**计算定时器的初值问题 **

默认TH0TL0为0,当用定时器的方式1时,机器周期为Tcy(一个机器周期为12个时钟周期),定时器产生一次中断的时间为t,则需计数的个数N=t/Tcy,装入THX和TLX中的数分别为;

​ THX = (65536-N)/256 TLX = (65536-N)%256

7、示例

补充:中断服务程序的写法(C51)

1
2
3
4
5
6
7
8
9
10
形式:
void 函数名 () interrupt 中断号 using 工作组

{

中断服务程序内容

}
说明:中断号:中断源的序号
using 工作组:指这个中断函数使用单片机内存中4组工作寄存器中的哪一组,一般不写,会自动分配。

写单片机的定时器程序时,再开始处需对定时器及中断寄存器做初始化配置:

1、对TMOD赋值,确定T0和T1的工作方式;

2、计算初值,并将初值写入TH0、TL0或TH1、TL1;

3、中断方式时,则对IE赋值,开放中断;

4、使TR0或TR1置位,启动定时器/计数器定时或计数。

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
/************************************************************
9、利用定时器0工作方式1,LED 1S闪烁
*/
#include "reg52.h"
#define uchar unsigned char
#define uint unsigned int
sbit led1 = P1^0;
uchar num;
void main()
{
//定时器初始化
TMOD = 0x01; //设置定时器0为工作方式1(M1M0为01)
TH0 = (65536-45872)/256; //装初值11.0592M晶振定时50ms数为45872
TL0 = (65536-45872)%256;
EA = 1; //开总中断
ET0 = 1; //开定时器0中断
TR0 = 1; //启动定时器0
while(1); //程序停止在这里等待中断发生
}
void T0_time()interrupt 1 //中断服务程序
{
TH0 = (65536-45872)/256; //重装初值
TL0 = (65536-45872)%256;
num++; //num每加1次判断一次是否到20次
if(num==20) //如果到了20次,说明1秒时间到
{
num = 0; //然后把num清0重新再计20次
led1 = ~led1;
}
}

能在主程序中完成的功能就不在中断函数中写,中断函数中要高效简洁:

优化后:

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
/************************************************************
9、利用定时器0工作方式1,LED 1S闪烁
*/
#include "reg52.h"
#define uchar unsigned char
#define uint unsigned int
sbit led1 = P1^0;
uchar num;
void main()
{
//定时器初始化
TMOD = 0x01; //设置定时器0为工作方式1(M1M0为01)
TH0 = (65536-45872)/256; //装初值11.0592M晶振定时50ms数为45872
TL0 = (65536-45872)%256;
EA = 1; //开总中断
ET0 = 1; //开定时器0中断
TR0 = 1; //启动定时器0
while(1)
{
if(num==20) //如果到了20次,说明1秒时间到
{
num = 0; //然后把num清0重新再计20次
led1 = ~led1;
}
} //程序停止在这里等待中断发生
}
void T0_time()interrupt 1 //中断服务程序
{
TH0 = (65536-45872)/256; //重装初值
TL0 = (65536-45872)%256;
num++; //num每加1次判断一次是否到20次

}
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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
/*********************************************************
10、中断(TX-1C)定时器0的方式1实现第一个发光管以200ms间隔闪烁
用定时器1的方式1实现数码管前两位59s循环计时
*/
#include "reg52.h"
#define uchar unsigned char
#define uint unsigned int
sbit dula = P2^6;
sbit wela = P2^7;
sbit led1 = P1^0;
uchar code table[]={
0x3f , 0x06 , 0x5b , 0x4f ,
0x66 , 0x6d ,0x7d , 0x07 ,
0x7f , 0x6f , 0x77 , 0x7c ,
0x39 , 0x5e , 0x79 , 0x71 };
void delayms(uint);
void display(uchar,uchar);
uchar num,num1,num2,shi,ge;
void main()
{
TMOD = 0x11; //设置定时器0和1为工作方式1(0001 0001)
TH0 = (65535-45872)/256; //装初值
TL0 = (65536-45872)%256;
TH1 = (65536-45872)/256; //装初值
TL1 = (65536-45872)%256;
EA = 1; //开总中断
ET0 = 1; //开定时器0中断
ET1 = 1; //开定时器1中断
TR0 =1; //启动定时器0
TR1 = 1; //启动定时器1
while(1)
{
display(shi,ge);
}
}
void display(uchar shi,uchar ge) //显示子函数
{
dula = 1;
P0 = table[shi]; //送段选数据
dula = 0;
P0 = 0xff; //送位选数据前关闭所有显示,防止打开位选锁存时原来的段选数据通过位选锁存器造成混乱
wela = 1;
P0 = 0xfe; //送位选数据
wela = 0;
delayms(5);

dula = 1;
P0 = table[ge];
dula = 0;
P0 =0xff;
wela = 1;
P0 = 0xfd;
wela = 0;
delayms(5);
}

void delayms(uint xms)
{
uint i,j;
for(i=xms;i>0;i--)
for(j=110;j>0;j--);
}
void T0_time() interrupt 1
{
TH0=(65536-45872)/256; //重装初值
TL0=(65536-45872)%256;
num1++;
if(num1==4) //4次中断=200ms
{
num1 = 0; //清0
led1 = ~led1;
}
}

void T1_time() interrupt 3
{
TH1=(65536-45872)/256; //重装初值
TL1=(65536-45872)%256;
num2++;
if(num2==20) //20次中断=1秒
{
num2=0; //清0
num++;
if(num==60) //该数用来送数码管显示,到60后归0
num=0;
shi=num/10; //把一个2位数分离后分别送数码管显示
ge=num%10; //十位和个位
}
}
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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
/*********************************************************
10、中断(BST-M51)定时器0的方式1实现第一个发光管以200ms间隔闪烁
用定时器1的方式1实现数码管前两位59s循环计时
*/
#include "reg52.h"
#define uchar unsigned char
#define uint unsigned int
sbit wei1 = P2^4;//定义第一位数码管
sbit wei2 = P2^5;//定义第二位数码管
sbit wei3 = P2^6;//定义第三位数码管
sbit wei4 = P2^7;//定义第四位数码管
sbit led1 = P1^0;
uchar code table[]={
0x3f , 0x06 , 0x5b , 0x4f ,
0x66 , 0x6d ,0x7d , 0x07 ,
0x7f , 0x6f , 0x77 , 0x7c ,
0x39 , 0x5e , 0x79 , 0x71 };
void delayms(uint);
void display(uchar,uchar);
uchar num,num1,num2,shi,ge;
void main()
{
TMOD = 0x11; //设置定时器0和1为工作方式1(0001 0001)
TH0 = (65535-45872)/256; //装初值
TL0 = (65536-45872)%256;
TH1 = (65536-45872)/256; //装初值
TL1 = (65536-45872)%256;
EA = 1; //开总中断
ET0 = 1; //开定时器0中断
ET1 = 1; //开定时器1中断
TR0 =1; //启动定时器0
TR1 = 1; //启动定时器1
while(1)
{
display(shi,ge);
}
}
void display(uchar shi,uchar ge) //显示子函数
{

P0 = table[shi]; //送段选数据
wei1 = 1;
wei2 = 0;
wei3 = 0;
wei4 = 0;
delayms(5);

P0 = table[ge];
wei1 = 0;
wei2 = 1;
wei3 = 0;
wei4 = 0;
delayms(5);
}

void delayms(uint xms)
{
uint i,j;
for(i=xms;i>0;i--)
for(j=110;j>0;j--);
}
void T0_time() interrupt 1
{
TH0=(65536-45872)/256; //重装初值
TL0=(65536-45872)%256;
num1++;
if(num1==4) //4次中断=200ms
{
num1 = 0; //清0
led1 = ~led1;
}
}

void T1_time() interrupt 3
{
TH1=(65536-45872)/256; //重装初值
TL1=(65536-45872)%256;
num2++;
if(num2==20) //20次中断=1秒
{
num2=0; //清0
num++;
if(num==60) //该数用来送数码管显示,到60后归0
num=0;
shi=num/10; //把一个2位数分离后分别送数码管显示
ge=num%10; //十位和个位
}
}

4、按键检测原理及应用实现

​ 键盘分为编码键盘和非编码键盘(是否有编码器);非编码键盘分为独立键盘和行列式(矩阵式)键盘。

1、独立键盘检测

​ 抖动现象(5~10ms),一般在检测按下时加入去抖延时,检测松手时不需。

cXXarj.jpg

目标:1、数码管前两位显示一个十进制数,变化范围00~59,开始时显示00,

​ 2、每按下S2键一次,数值加1;每按下S3键一次,数值减1;每按下S4键一次,数值归零;

​ 3、每按下S5键一次,利用定时器功能使数值开始自动每秒加1,再次按下S5键,数值暂停。

代码分析:(省略版)

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
#include "reg52.h"
#define uchar.....
sbit....
uchar code table[]={};

void display(uchar numdis){
shi=numdis/10;
ge=numdis%10;
} //显示子函数
void delayms(uint xms){} //延时子函数
void init(){ //初始化函数
//定时器初始化
TMOD = 0x01; //设置定时器0为工作方式1(M1M0为01)
TH0 = (65536-45872)/256; //装初值11.0592M晶振定时50ms数为45872
TL0 = (65536-45872)%256;
EA = 1; //开总中断
ET0 = 1; //开定时器0中断(通过 TR0 = 1;开启)
}
void keyscan(){ //按键扫描子函数
if(key1==0)
{
delayms(10); //消抖
if(key1==0)
{
num++;
if(num==60)
num=0;
while(!key1); //等待按键释放
}
}
if(key2==0){}
if(key3==0){}
if(key4==0){ while(!key4); TR0=~TR0;}
}
void main()
{
init();
while(1)
{
keyscan();
display(num);
}
}
void T0_time()interrupt 1
{
TH0 = (65536-45872)/256; //重装初值
TL0 = (65536-45872)%256;
numt0++;
if(numt0==20) //1秒时间到
{
numt0=0; //时间清零重计
num++; //每秒加1
if(num==60)
num=0;
}
}


2、矩阵键盘检测

cjpVPI.md.jpg

目标:TX-1C:上电,数码管不显示,顺序按下矩阵键盘后,数码管上依次显示0~F,6个数码管同时静态显示即可

代码分析:(省略版)

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
void main()
{
P0=0; //关闭所有数码管段选
dula=1;
dula=0;
P0=0xc0; //位选中所有数码管(以后直接送段选信号)
wela=1;
wela=0;
while(1)
{
matrixkeyscan(); //不停调用键盘扫描程序
}
}

void display(uchar num)
{
P0=table[num]; //显示函数只送段选数据
dula=1;
dula=0;
}
void matrixkeyscan()
{
uchar temp,key;
P3=0xfe; //1111 1110 第一行线置低电平,其余为高电平
temp=P3; //P3状态赋给临时变量,用于计算
temp=temp&0xf0; //1111 0000 若temp高四位有0,则结果不为0xf0; 若无,则一直为0xf0
if(temp!=0xf0) //说明有键按下
{
delayms(10); //消抖
temp=P3; //重读
temp=temp&0xf0; //重&
if(temp!=0xf0) //说明确实有键按下
{
temp=P3; //再读一下P3状态(行列交叉)
switch(temp)
{
case 0xee: //1110 1110
key=0;
break;
case 0xde: //1101 1110
key=1;
break;
case 0xbe: //1011 1110
key=2;
break;
case 0x7e: //0111 1110
key=3;
break;
}
while(temp!=0xf0) //等待按键释放
{
temp=P3; //不断读取
temp=temp&0xf0; //不断相&,只要结果不为0xf0,就说明按键没有被释放
}
display(key); //显示
}

}
P3=0xfd; //1111 1101第二行置低电平,其余为高电平
。。。。。。。。。。。。。
P3=0xfb;
.......................
P3=0xf7;
.......................

}

5、模/数转换

1、转换原理及参数指标

​ 采样保持、量化、编码。

采样定理:$f_s>=2f_{ i max }$,通常取3-5倍(量化需要时间),$f_s$为采样频率,$f_{i max}$为输入信号的最高频率分量的频率。在满足采样定理的条件下可以使用一个低通滤波器将信号还原出来,该低通滤波器电压传输系数在低于$f_{imax}$的范围内保持不变,在$f_s$-$f_{imax}$以前迅速下降为0

​ 量化和编码:

​ 量化:化成一个最小数量单位(量化单位$\bigtriangleup$)的整数倍。(会产生量化误差,最大为$\bigtriangleup$,解决:取中点,缩小为$\bigtriangleup$/2)

​ 编码:把量化的数值用二进制代码表示,即输出信号。

补充:采样保持电路(N沟道MOS管可做采样开关用,高电平T导通)

2、直接A/D转换器

1、并行比较型

​ (电压比较器+寄存器+代码转换器)

​ 单片集成并行比较型A/D转换器如:AD9012(8位)、AD9002(8位)、AD9020(10位)等

​ 特点:①并行,转换时间只受比较器、触发器和编码电路延迟时间限制,转换速度快;②随着分辨率提高,元件数目按几何级数增加;③这种含寄存器的并行A/D转换电路,可以不用附加采样-保持电路(比较器和寄存器兼有采样-保持功能)。

​ 缺点:需要很多的电压比较器和触发器。

2、反馈比较型

​ 原理:取一个数字量加到D/A转换器上,得到一个对应的输出模拟电压,将这个模拟电压与输入的模拟电压信号比较,如果两者不相等,则调整所取的数字量,直至两个模拟电压相等为止,最后所取的数字量就是转换结果。

3、计数型

​ ( 比较器C+D/A转换器+计数器+脉冲源+控制门G+输出寄存器等)

​ 使用寄存器原因:转换过程计数器中数字不停变化,不宜将计数器状态直接作为输出信号。

​ 缺点:转换速度低。

4、逐次比较型

​ (提高转换速度,在计数型基础上,采用类似天平称重的思路:将输入模拟信号与不同的参考电压做多次比较,使转换所得的数字量在树数值上逐次逼近输入模拟量的对应值)

​ 一次转换时间与其位数和时钟脉冲频率相关。位数越少,时钟频率越高,转换时间越短。

​ 优点:转换速度快,精度高等。

​ 集成逐次比较型A/D转换器如:8位:ADC0804、ADC0808、ADC0809系列;10位:AD575;12位:AD574A等

3、间接A/D转换器(主要两种)

1、时间-电压变换型(V-T变换型)

​ 首先把输入的模拟电压信号转换成与之成正比的时间宽度信号,然后在这个时间宽度里对固定频率的时钟脉冲计数,计数结果就是正比于输入模拟电压的数字信号。

2、电压-频率变换型(V-F变换型)

​ 首先把输入的模拟电压信号转换成与之成正比的频率信号,然后在一个固定的时间间隔里对得到的频率信号计数,所得到的结果就是正比于输入模拟电压的数字量。

4、A/D转换器的参数指标

  1. 分辨率:对输入信号的分辨能力,以输出二进制的位数表示。eg:输出8位二进制数,输入信号最大值5V,分辨最小电压:$5V×1/2^8≈19.53mv$;
  2. 转换误差:理论与实际输出数字量之间的差别,一般用最低有效位表示;
  3. 转换精度:A/D转换器的最大量化误差和模拟部分精度的共同体现;
  4. 最大量化误差:应为分辨率数值的一半(实际还要低些);
  5. 模拟处理(如积分器、比较器等)部分的误差:一般与数字转换误差应尽量处于同一数量等级。eg:10位A/D转换器用其中9位计数;
  6. 总误差:累加和;
  7. 最大相对量化误差:2^9×0.5≈0.1%,模拟部分也为0.1%,总精度0.2%;
  8. 转换时间:转换控制信号来,到输出端得到稳定的数字信号所经过的时间。并行比较A/D转换器(8位单片集成50ns)< 逐次比较型A/D转换型(10-50us,也有几百纳秒的)< 间接A/D转换器(双积分A/D转换器时间几十毫秒~几百毫秒)

5、小结:

​ ①高速:并行;

​ ②高精度:双积分A/D转换器;

​ ③逐次比较型A/D转换器兼有,普遍使用;

​ ④主要技术参数:转换精度、转换速度(决定系统)

6、ADC0804工作原理及其实现方法

RiZqCF.md.jpg

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
/*********************************************************
11、TX-IC上ADC0804的数模转换使用(逐次比较型)
*********************************************************/
#include "reg52.h"
#include "intrins.h"
#define uchar unsigned char
#define uint unsigned int
sbit dula = P2^6; //U1锁存端
sbit wela = P2^7; //U2锁存端
sbit adwr = P3^6; //A/D的WR端口
sbit adrd = P3^7; //A/D的RD端口

uchar code table[]={
0x3f , 0x06 , 0x5b , 0x4f ,
0x66 , 0x6d ,0x7d , 0x07 ,
0x7f , 0x6f , 0x77 , 0x7c ,
0x39 , 0x5e , 0x79 , 0x71 };
void delayms(uint xms)
{
uint i,j;
for(i=xms;i>0;i--)
for(j=110;j>0;j--);
}
void display(uchar bai,uchar shi,uchar ge)
{
dula = 1;
P0 = table[bai];
dula = 0;
P0 = 0xff; //送位选数据前关闭所有显示,防止打开位选锁存时,原来的段选数据通过位选锁存器造成混乱
wela = 1;
P0 = 0x7e; //送位选数据 0111 1110 (始终保持最高位为0,是下方CS端一直被选中)
wela = 0;
delayms(5);

dula = 1;
P0 = table[shi];
dula = 0;
P0 = 0xff;
wela = 1;
P0 = 0x7d; //0111 1101
wela = 0;
delayms(5);

dula = 1;
P0 = table[ge];
dula = 0;
P0 = 0xff;
wela = 1;
P0 = 0x7b; //0111 1011
wela = 0;
delayms(5);
}
void main()
{
uchar a,A1,A2,A3,adval;
wela = 1;
P0 = 0x7f; //CSAD为0,选通ADCS后不再管ADCS
wela = 0;
while(1)
{
adwr = 1;
_nop_(); //延迟一个机器周期
adwr = 0; //AD启动
_nop_();
adwr = 1;
for(a=10;a>0;a--) //此处A/D工作频率较低,所以启动后要多留时间用来转换(将显示部分放在这里)
{
display(A1,A2,A3);
}

P1 = 0xff; //读取P1口之前全赋1
adrd = 1; //选通ADCS
_nop_();
adrd = 0; //A/D读使能
_nop_();
adval = P1; //将A/D数据赋给P1口
adrd = 1;

A1 = adval/100;
A2 = adval%100/10;
A3 = adval%10;
}

6、数/模转换

上一篇:
通信课设--基于STM32与OneNet的温室大棚
下一篇:
魔方闲玩