0%

扫描频率可改变的电子钟

扫描频率可以改变的电子钟

程序设计思路

数字钟通过计数模拟时钟,将计数值,转换成时间格式,以格式:“时-分-秒”在LED数码管上显示,并通过案件调整扫描频率。

img

相关寄存器设置

  • P0的八个位和P2.3设置成推挽输出。按键是输入,不需要推挽。设置寄存器配置值如下。

    P2以及P0的端口设置,和上一个数码管扫描实验是一样的。

    额外增加的考虑是P3端口的设置。0通道和1通道都是0,是传统的8051I/O口模式。

    1
    2
    3
    4
    5
    6
    P2M1=0x00;
    P2M0=0xff;
    P0M1=0x00;
    P0M0=0xff;
    P3M0=0x00;
    P3M1=0x00;
  • 通过定时器0,采集方式1,在定时器中断中进行计数累加

    1
    TMOD = 0x01;
  • 打开总的中断

    1
    EA = 1;
  • 开启定时器中断

    1
    ET0 = 1;
  • 计数寄存器初始化

    定时器0设置于模式1时,计数寄存器为16位模式,由高8位TH0和低8位TL0两个8位寄存器组成,当设定计算值为65536-50000=15536(D)时,转换为十六进制就是3CB0(H),此时,TH0=3C,TL0=B0分别装入即可,为了免除这些计算步骤,很多编程者采用“TH0=(65536-50000)/256;TL0=(65536-50000)%256“的编程方式,去让单片机自己去计算结果,那么为什么要介入256呢?其实并不难理解,做一下10——16进制的换算就知道了,256(D)=0100(H),这里01就是高8位的数据,00就是低8位的数据,通俗点说,15536(D)里有多少个256,就相当于高8位有多少数值,就是除的关系了,商存入高8位寄存器后余下的数存入低8位即可,取商计算就是TH0=(65536-50000)/256;而取余计算就是TL0=(65536-50000)%256 。

    1
    2
    TH0=(65535-1000)/256;
    TL0=(65535-1000)%256;
  • 启动定时器

    1
    TR0=1;
  • 中断1优先级置1,表示中断1设置为最高优先级

    1
    PT0=1;
  • 设置P2和P0为推挽输出模式

    1
    2
    3
    4
    P2M1=0x00;
    P2M0=0xff;
    P0M1=0x00;
    P0M0=0xff;

代码说明

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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
/* 引入端口定义头文件和中断定义头文件 */ 
#include "STC15F2K60S2.h"
#include "intrins.h"

/* 重命名,简写 */
#define uint unsigned int
#define uchar unsigned char

/* 中断1 */
#define i1 interrupt 1

sbit ledSel = P2 ^3; // LED或数码管选择信号(共用选择端信号)
sbit key1 = P3 ^2; // 按键控制数码管扫描的频率

/* 显示0-9对应的数字,段选选择 */
uchar baseSevenSegment[] = {
0x3f, 0x06, 0x5b, 0x4f, 0x66,
0x6d, 0x7d, 0x07, 0x7f, 0x6f
};
unsigned char const line = 0x40; // "-":8中中间横杠的段选信息
char timeAddOneFlag = 0; // 时间增加标志,为1,时间增加一秒
char key1ActionFlag = 0; // 按键操作标志,为1,按键操作需要响应
unsigned int ledOnFlag = 0; // LED亮起标志,为1,LED亮起
char tubeOnFlag = 0; // 数码管亮标志,为1,数码管亮起
int ledValue = 1; // LED显示值为多少
int myDisplay[8] = {0}; // 数码管显示值为多少,一共8个数码管
unsigned int timeCount = 1; // 计数器
unsigned int currHour = 0; // 当前小时
unsigned int currMinute = 0; // 当前分钟
unsigned int currSecond = 0; // 当前秒钟
unsigned int interruptCount = 0; // 中断计数器
unsigned int keyDownTime = 0; // 按键按下的时间
unsigned int scanTime = 1; // 扫描时间
unsigned int currBit = 0; // 当前显示的位

/* 延时函数 可以用ISP生成 */
void Delay5us() //@11.0592MHz
{
unsigned char i;
_nop_();
i = 11;
while (--i);
}

/* 设置ledSel,转为数码管显示 */
void switchToTube() {
ledSel = 0;
}

/* 设置ledSel,转为led设置 */
void switchToLed() {
P0 = 0;
ledSel = 1;
}

/**
* 改变其中一位的内容
*
* @param bitNum 位数(第几位)(例如最左那位,则调用1)
* @param value 改变后的数字,需确保大于0小于10
*/
void change1Bit(int bitNum, int value) {
myDisplay[bitNum - 1] = baseSevenSegment[value];
}

/**
* 改变其中一位的内容(设置七段码)
*
* @param bitNum 位数(第几位)(例如最左那位,则调用1)
* @param sevenSegCode 目标七段码
*/
void change1Bit_seven(int bitNum, int sevenSegCode) {
myDisplay[bitNum - 1] = sevenSegCode;
}

/**
* 数码管设置为显示指定的数值
*
* @param num 数字
*/
void changeAll(long num) {
int i;
for (i = 7; i >= 0; --i) {
int foo = num % 10;
myDisplay[i] = baseSevenSegment[foo];
num /= 10;
}
}

/**
* led亮
*/
void displayLed() {
if (ledOnFlag) {
switchToLed();
P0 = ledValue;
}
}

/**
* 显示数码管
*/
void showTube() {
if (tubeOnFlag) {
switchToTube();
P0 = 0;
P2 = currBit;
P0 = myDisplay[currBit];
Delay5us();
}
}

/**
* 初始化定时器
*/
void timer0Initialize() //0.1毫秒@12MHz
{
AUXR |= 0x80; // 定时器时钟1T模式
TMOD &= 0xF0; // 设置定时器模式
TL0 = 0xAE; // 设置定时器初值
TH0 = 0xFB; // 设置定时器初值
TF0 = 0; // 清除TF0标志
TR0 = 1; // 定时器0开始计时
EA = 1; // 打开总的中断
ET0 = 1; // 打开定时器0中断
}

/**
* 整体程序初始化函数
*/
void initialize() {
P0M0 = 0xFF;
P0M1 = 0x00;
P2M0 = 0x0f; //设置P2.0-3为推挽工作状态
P2M1 = 0x00;
P3M0 = 0x00;
P3M1 = 0x00;
P3M0 = 0x00;
P3M1 = 0x00;
ledSel = 0;

timer0Initialize();
changeAll(0);
/*
* 第三位和第六位设置为 '-'
*/
change1Bit_seven(3, line);
change1Bit_seven(6, line);
}

/**
* 时间自增1秒
*/
void addTime() {
++currSecond;
if (currSecond == 60) {
++currMinute;
currSecond = 0;
if (currMinute == 60) {
++currHour;
currMinute = 0;
if (currHour == 24)
currHour = 0;
change1Bit(2, currHour % 10);
change1Bit(1, currHour / 10);
}
change1Bit(5, currMinute % 10);
change1Bit(4, currMinute / 10);
}
change1Bit(8, currSecond % 10);
change1Bit(7, currSecond / 10);
}

/**
* 按下key1要做的事
*/
void key1Action() {
if (key1ActionFlag) {
if (ledValue == 0x80)
ledValue = 0x01;
else
ledValue <<= 1;
if (ledValue == 1)
scanTime = 1;
else if (ledValue == 2)
scanTime = 50;
else if (ledValue == 4)
scanTime = 100;
else if (ledValue == 8)
scanTime = 200;
else if (ledValue == 16)
scanTime = 500;
else if (ledValue == 32)
scanTime = 1000;
else if (ledValue == 64)
scanTime = 2000;
else if (ledValue == 128)
scanTime = 5000;
key1ActionFlag = 0;
}
}

/**
* 收到中断的信号(是时候自增时间了)
*/
void timeSignalHandler() {
if (timeAddOneFlag) {
addTime();
timeAddOneFlag = 0;
}
}

/**
* 单片机运行
*/
void run() {
while (1) {
timeSignalHandler();
displayLed();
showTube();
key1Action();
}
}

/**
* 每0.1毫秒进入一次定时器中断
*/
void interruptFunction() i1 {
static const int KEY_TIME_THRESHOLD = 500;
interruptCount = (interruptCount + 1) % 20000;
// led显示的频率应该低些才有好的显示效果
if (interruptCount % 12 > 10) {
ledOnFlag = 1;
tubeOnFlag = 0;
} else {
tubeOnFlag = 1;
ledOnFlag = 0;
}
// 数码管的扫描
if (interruptCount % scanTime == 0)
currBit = (currBit + 1) % 8;
// 读秒
if (interruptCount % 10000 == 0)
timeAddOneFlag = 1;
// 按键功能设置
if (key1 == 0) {
if (keyDownTime < KEY_TIME_THRESHOLD)
++keyDownTime;
} else {
if (keyDownTime >= KEY_TIME_THRESHOLD)
key1ActionFlag = 1;
keyDownTime = 0;
}
}

int main() {
initialize();
run();
return 0;
}