单片机综合练习 -
[导读]结合前几天来写过的文章, 今天总算写了一个功能较多的应用 - 多功能时钟, 集时钟, 秒表, 温度计一体.基础文章:1.单片机练习 - DS18B20温度转换与显示2.用C51编写单片机延时函数3.单片机练习 - 定时器4.单片机练习 -
结合前几天来写过的文章, 今天总算写了一个功能较多的应用 - 多功能时钟, 集时钟, 秒表, 温度计一体.
基础文章:
1.单片机练习 - DS18B20温度转换与显示
2.用C51编写单片机延时函数
3.单片机练习 - 定时器
4.单片机练习 - 计时器
实验板:TX-1B实验板
6位数码管与单片机的连接电路图
按键S2, S3与单片机的连接电路图: 其中S2与P3.4连, S3与P3.5连接...
DS18B20与单片机连接电路图:
具体按键功能分配请看源代码注释部分:
本文引用地址: http://www.21ic.com/app/mcu/201807/783596.htm
![]()
1
//多功能时钟,精确到小数0.01秒,即10ms
2
//功能:时钟,秒表,温度计
3
4
/*
5
S5键为功能选择键,上电默认使用时钟功能
6
功能顺序为:时钟,温度计,秒表
7
8
mode=1.时钟(每次掉电后都要重新设置时间)
9
1)当选中时钟功能时,具体按键功能如下:
10
11
2)可设置时分秒,时利用发光二极管显示,分秒用数码管显示
12
13
3)时钟:采用定时器0计时,工作方式1
14
15
mode=2.时钟设置模式
16
当选中时钟设置模式
17
S2为位选,S3为增加选中位的值
18
S4确定更改,S5放弃更改,进入秒表模式
19
20
mode=3.秒表
21
1)当选中秒表功能时,具体按键功能如下:
22
S2为开始/暂停,S3为清零
23
24
2)采用定时器1计时,工作方式1
25
26
mode=4.温度计
27
1)利用DS18B20检测环境温度;
28
2)最小温度值为0.01℃,可表示温度范围:-55℃~+125℃
29
30
*/
31
32
#include
33
#include
34
#include
35
36
//0-F数码管的编码(共阴极)
37
unsignedcharcodetable[]={0x3f,0x06,0x5b,0x4f,0x66,
38
0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
39
//0-9数码管的编码(共阴极),带小数点
40
unsignedcharcodetableWidthDot[]={0xbf,0x86,0xdb,0xcf,0xe6,0xed,0xfd,
41
0x87,0xff,0xef};
42
43
sbitwela=P2^7;//数码管位选
44
sbitdula=P2^6;//数码管段选
45
sbitds=P2^2;
46
unsignedcharth,tl,mode=1;//mode存放功能模式,默认在模式1时钟
47
unsignedcharclockPosition=0;//时钟设置模式下,光标所在的位置;默认在0
48
unsignedcharclockTmp=0;//用于时钟模式下临时计数
49
bitclockTmpBit=0;//用于时钟模式下临时标志位
50
51
//秒4字节,分2字节,时1字节
52
unsignedchardatas[]={0,0,0,0,0,0,0};//保存计时器数据
53
unsignedcharclockDatas[]={0,0,0,0,0,0,0};//保存时钟数据
54
unsignedchar*values=clockDatas;//根据mode选择适当的数据数组指针
55
inttempValue;//存放温度值
56
unsignedchartempCount=0;//用于记录显示了多少次温度值,用于定时
57
58
sbitS2=P3^4;//键S2,作开始/暂停
59
sbitS3=P3^5;//键S3,清零
60
sbitS4=P3^6;//键S4
61
sbitS5=P3^7;//键S5
62
unsignedchartmpDatas[]={0,0,0,0,0,0,0};//存放临时设置值
63
unsignedcharicount;
64
65
//延时函数,对于11.0592MHz时钟,例i=5,则大概延时5ms.
66
voiddelay(unsignedinti)
67
{
68
unsignedintj;
69
while(i--)
70
{
71
for(j=0;j<125;j++);
72
}
73
}
74
75
/***********************温度计模式******************************/
76
77
//初始化DS18B20
78
//让DS18B20一段相对长时间低电平,然后一段相对非常短时间高电平,即可启动
79
voiddsInit()
80
{
81
//对于11.0592MHz时钟,unsignedint型的i,作一个i++操作的时间大于为8us
82
unsignedinti;
83
ds=0;
84
i=100;//拉低约800us,符合协议要求的480us以上
85
while(i>0)i--;
86
ds=1;//产生一个上升沿,进入等待应答状态
87
i=4;
88
while(i>0)i--;
89
}
90
91
voiddsWait()
92
{
93
unsignedinti;
94
while(ds);
95
while(~ds);//检测到应答脉冲
96
i=4;
97
while(i>0)i--;
98
}
99
100
//向DS18B20读取一位数据
101
//读一位,让DS18B20一小周期低电平,然后两小周期高电平,
102
//之后DS18B20则会输出持续一段时间的一位数据
103
bitreadBit()
104
{
105
unsignedinti;
106
bitb;
107
ds=0;
108
i++;//延时约8us,符合协议要求至少保持1us
109
ds=1;
110
i++;i++;//延时约16us,符合协议要求的至少延时15us以上
111
b=ds;
112
i=8;
113
while(i>0)i--;//延时约64us,符合读时隙不低于60us要求
114
returnb;
115
}
116
117
//读取一字节数据,通过调用readBit()来实现
118
unsignedcharreadByte()
119
{
120
unsignedinti;
121
unsignedcharj,dat;
122
dat=0;
123
for(i=0;i<8;i++)
124
{
125
j=readBit();
126
//最先读出的是最低位数据
127
dat=(j<<7)|(dat>>1);
128
}
129
returndat;
130
}
131
132
//向DS18B20写入一字节数据
133
voidwriteByte(unsignedchardat)
134
{
135
unsignedinti;
136
unsignedcharj;
137
bitb;
138
for(j=0;j<8;j++)
139
{
140
b=dat&0x01;
141
dat>>=1;
142
//写"1",将DQ拉低15us后,在15us~60us内将DQ拉高,即完成写1
143
if(b)
144
{
145
ds=0;
146
i++;i++;//拉低约16us,符号要求15~60us内
147
ds=1;
148
i=8;while(i>0)i--;//延时约64us,符合写时隙不低于60us要求
149
}
150
else//写"0",将DQ拉低60us~120us
151
{
152
ds=0;
153
i=8;while(i>0)i--;//拉低约64us,符号要求
154
ds=1;
155
i++;i++;//整个写0时隙过程已经超过60us,这里就不用像写1那样,再延时64us了
156
}
157
}
158
}
159
160
//向DS18B20发送温度转换命令
161
voidsendChangeCmd()
162
{
163
dsInit();//初始化DS18B20,无论什么命令,首先都要发起初始化
164
dsWait();//等待DS18B20应答
165
delay(1);//延时1ms,因为DS18B20会拉低DQ60~240us作为应答信号
166
writeByte(0xcc);//写入跳过序列号命令字SkipRom
167
writeByte(0x44);//写入温度转换命令字ConvertT
168
}
169
170
//向DS18B20发送读取数据命令
171
voidsendReadCmd()
172
{
173
dsInit();
174
dsWait();
175
delay(1);
176
writeByte(0xcc);//写入跳过序列号命令字SkipRom
177
writeByte(0xbe);//写入读取数据令字ReadScratchpad
178
}
179
180
//获取当前温度值
181
voidgetTempValue()
182
{
183
unsignedinttmpvalue;
184
floatt;
185
unsignedcharlow,high;
186
sendReadCmd();
187
//连续读取两个字节数据
188
low=readByte();
189
high=readByte();
190
//将高低两个字节合成一个整形变量
191
//计算机中对于负数是利用补码来表示的
192
//若是负值,读取出来的数值是用补码表示的,可直接赋值给int型的value
193
tmpvalue=high;
194
tmpvalue<<=8;
195
tmpvalue|=low;
196
tempValue=tmpvalue;
197
198
//使用DS18B20的默认分辨率12位,精确度为0.0625度,即读回数据的最低位代表0.0625度
199
t=tempValue*0.0625;
200
//将它放大100倍,使显示时可显示小数点后两位,并对小数点后第三进行4舍5入
201
//如t=11.0625,进行计数后,得到value=1106,即11.06度
202
//如t=-11.0625,进行计数后,得到value=-1106,即-11.06度
203
tempValue=t*100+(tempValue>0?0.5:-0.5);//大于0加0.5,小于0减0.5
204
}
205
206
//显示当前温度值,精确到小数点后一位
207
//若先位选再段选,由于IO口默认输出高电平,所以当先位选会使数码管出现乱码
208
voiddisplayTemp()
209
{
210
unsignedchari;
211
unsignedinttmp=abs(tempValue);
212
tmpDatas[0]=tmp/10000;
213
tmpDatas[1]=tmp%10000/1000;
214
tmpDatas[2]=tmp%1000/100;
215
tmpDatas[3]=tmp%100/10;
216
tmpDatas[4]=tmp%10;
217
if(tempValue<0)
218
{
219
//关位选,去除对上一位的影响
220
P0=0xff;
221
wela=1;//打开锁存,给它一个下降沿量
222
wela=0;
223
//段选
224
P0=0x40;//显示"-"号
225
dula=1;//打开锁存,给它一个下降沿量
226
dula=0;
227
228
//位选
229
P0=0xfe;
230
wela=1;//打开锁存,给它一个下降沿量
231
wela=0;
232
delay(1);
233
}
234
for(i=0;i!=5;i++)
235
{
236
//关位选,去除对上一位的影响
237
P0=0xff;
238
wela=1;//打开锁存,给它一个下降沿量
239
wela=0;
240
//段选
241
if(i!=2)
242
{
243
P0=table[tmpDatas[i]];//显示数字
244
}
245
else
246
{
247
P0=tableWidthDot[tmpDatas[i]];//显示带小数点数字
248
}
249
dula=1;//打开锁存,给它一个下降沿量
250
dula=0;
251
252
//位选
253
P0=_crol_(0xfd,i);//选择第(i+1)个数码管
254
wela=1;//打开锁存,给它一个下降沿量
255
wela=0;
256
delay(1);
257
}
258
tempCount++;
259
if(tempCount==100)
260
{
261
tempCount=0;
262
getTempValue();
263
sendChangeCmd();
264
}
265
}
266
267
//显示时钟和秒表的结果
268
voiddisplay()
269
{
270
unsignedchari;
271
if(mode==4)//显示温度
272
{
273
displayTemp();
274
}
275
else{//显示时间
276
for(i=0;i<6;i++)
277
{
278
//关位选,去除对上一位的影响
279
P0=0xff;
280
wela=1;//打开锁存,给它一个下降沿量
281
wela=0;
282
//段选
283
if(i==2||i==4)
284
{
285
P0=tableWidthDot[values[i]];//显示带小数点数字,作为分秒,秒与毫秒的分隔
286
}
287
else
288
{
289
P0=table[values[i]];//显示数字
290
}
291
if(mode==2&&i==clockPosition)//时钟设置模式下,光标所在位置,闪烁
292
{
293
clockTmp++;
294
if(clockTmp==20)
295
{
296
clockTmpBit=~clockTmpBit;
297
clockTmp=0;
298
}
299
if(clockTmpBit)
300
P0=0x00;
301
}
302
dula=1;//打开锁存,给它一个下降沿量
303
dula=0;
304
305
//位选
306
P0=_cror_(0xdf,i);//选择第(i+1)个数码管
307
wela=1;//打开锁存,给它一个下降沿量
308
wela=0;
309
delay(1);
310
}
311
if(mode==2&&6==clockPosition)//时钟设置模式下,光标所在位置,闪烁
312
{
313
clockTmp++;
314
if(clockTmp==20)
315
{
316
clockTmpBit=~clockTmpBit;
317
clockTmp=0;
318
}
319
if(clockTmpBit)
320
{
321
if(values[6]==0)
322
{
323
P1=0x0f;
324
}
325
else
326
{
327
P1=~values[6];
328
}
329
}
330
else
331
{
332
P1=0xff;
333
}
334
}
335
else
336
{
337
P1=~values[6];
338
}
339
}
340
}
341
342
//检测功能模式键是否被按下
343
voidcheckModeKey()
344
{
345
if(!S5)
346
{
347
delay(7);
348
if(!S5)
349
{
350
mode++;
351
switch(mode)
352
{
353
case2://定时器0是不会关闭的,即使在设置模式下
354
for(icount=0;icount<7;icount++)
355
{
356
tmpDatas[icount]=clockDatas[icount];//将设置之前的值暂存到临时数组
357
}
358
values=tmpDatas;
359
break;
360
case3://秒表模式
361
values=datas;
362
break;
363
case4://温度计模式
364
//启动温度转换
365
sendChangeCmd();
366
getTempValue();
367
break;
368
default:
369
values=clockDatas;
370
mode=1;
371
TR0=1;
372
}
373
while(!S5)//等待释放键
374
{
375
display();
376
}
377
}
378
}
379
}
380
381
//选中位数值增1,用于时钟模式的设置状态下
382
voidadd()
383
{
384
values[clockPosition]++;
385
switch(clockPosition)
386
{
387
case3:
388
case5:
389
if(values[clockPosition]>5)
390
values[clockPosition]=0;
391
break;
392
case6:
393
if(values[clockPosition]>23)
394
values[clockPosition]=0;
395
break;
396
default:
397
if(values[clockPosition]>9)
398
values[clockPosition]=0;
399
}
400
}
401
402
//检测是否有键按下,并执行相应功能
403
voidcheckKey()
404
{
405
checkModeKey();
406
switch(mode)
407
{
408
case2:
409
if(!S2)//左移光标位
410
{
411
delay(7);
412
if(!S2)
413
{
414
clockPosition++;
415
if(clockPosition>6)
416
{
417
clockPosition=0;
418
}
419
}
420
}
421
elseif(!S3)//选中位增1
422
{
423
delay(7);
424
if(!S3)
425
{
426
add();
427
}
428
}
429
elseif(!S4)//选中确定更改
430
{
431
delay(7);
432
if(!S4)
433
{
434
for(icount=0;icount<7;icount++)
435
{
436
clockDatas[icount]=tmpDatas[icount];//将设置的值存到clockDatas[]
437
}
438
values=clockDatas;
439
mode=1;//将模式变成时钟模式
440
}
441
}
442
break;
443
case3:
444
if(!S2)
445
{
446
delay(7);//延时大约10ms,去抖动
447
if(!S2)//开始/暂停键按下
448
{
449
TR1=~TR1;
450
}
451
}
452
453
elseif(!S3)
454
{
455
delay(7);//延时大约10ms,去抖动
456
if(!S3)//清零键按下
457
{
458
TR1=0;
459
TH1=th;
460
TL1=tl;
461
for(icount=0;icount<7;icount++)
462
{
463
datas[icount]=0;
464
}
465
}
466
}
467
break;
468
}
469
//等待键被释放
470
while(!S2||!S3||!S4)
471
{
472
display();//等待期间要显示
473
}
474
}
475
476
voidmain()
477
{
478
delay(1);
479
th=0xdb;//(65536-10000/1.085)/256;//定时10ms
480
tl=0xff;//(65536-10000/1.085)-th*256;
481
TH1=TH0=th;//初始化定时器1,0
482
TL1=TL0=tl;
483
EA=1;//开中断
484
ET1=ET0=1;//允许定时器1,0中断请求
485
TMOD=0x11;//定时器1,0都工作方式1
486
TR1=0;
487
TR0=1;//开始显示时间
488
while(1)
489
{
490
checkKey();//检测是否就键按下
491
display();//显示计时值
492
}
493
}
494
495
//定时器0中断响应函数,用于时钟计时
496
voidtime0()interrupt1
497
{
498
TH0=th;//重置计数值
499
TL0=tl;
500
501
clockDatas[0]++;//0.01秒
502
if(clockDatas[0]==10)
503
{
504
clockDatas[0]=0;
505
clockDatas[1]++;//0.1秒
506
if(clockDatas[1]==10)
507
{
508
clockDatas[1]=0;
509
clockDatas[2]++;//秒
510
if(clockDatas[2]==10)
511
{
512
clockDatas[2]=0;
513
clockDatas[3]++;//10秒
514
if(clockDatas[3]==6)
515
{
516
clockDatas[3]=0;
517
clockDatas[4]++;//分
518
if(clockDatas[4]==10)
519
{
520
clockDatas[4]=0;
521
clockDatas[5]++;//10分
522
if(clockDatas[5]==6)
523
{
524
clockDatas[5]=0;
525
clockDatas[6]++;//时
526
if(clockDatas[6]==24)
527
{
528
clockDatas[6]=0;
529
}
530
}
531
}
532
}
533
}
534
}
535
}
536
}
537
538
//定时器1中断响应函数,用于秒表计时
539
voidtime1()interrupt3
540
{
541
TH1=th;//重置计数值
542
TL1=tl;
543
544
datas[0]++;//0.01秒
545
if(datas[0]==10)
546
{
547
datas[0]=0;
548
datas[1]++;//0.1秒
549
if(datas[1]==10)
550
{
551
datas[1]=0;
552
datas[2]++;//秒
553
if(datas[2]==10)
554
{
555
datas[2]=0;
556
datas[3]++;//10秒
557
if(datas[3]==6)
558
{
559
datas[3]=0;
560
datas[4]++;//分
561
if(datas[4]==10)
562
{
563
datas[4]=0;
564
datas[5]++;//10分
565
if(datas[5]==6)
566
{
567
datas[5]=0;
568
datas[6]++;//时
569
if(datas[6]==24)
570
{
571
datas[6]=0;
572
}
573
}
574
}
575
}
576
}
577
}
578
}
579
}
查看评论 回复









