打印
[AVR单片机]

多功能时钟 (转)

[复制链接]
2498|15
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
huamunv|  楼主 | 2009-12-10 21:20 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
一个功能较多的应用 - 多功能时钟, 集时钟, 秒表, 温度计一体.

实验板: TX-1B实验板

6位数码管与单片机的连接电路图

按键S2, S3与单片机的连接电路图: 其中S2与P3.4连, S3与P3.5连接...

DS18B20与单片机连接电路图:

具体按键功能分配请看源代码注释部分:

相关帖子

沙发
huamunv|  楼主 | 2009-12-10 21:20 | 只看该作者
1//多功能时钟, 精确到小数0.01秒, 即10ms
  2//功能: 时钟, 秒表, 温度计
  3
  4/**//*
  5S5键为功能选择键, 上电默认使用时钟功能
  6功能顺序为: 时钟, 温度计, 秒表
  7
  8mode = 1. 时钟(每次掉电后都要重新设置时间)
  91)当选中时钟功能时, 具体按键功能如下:
10
112)可设置时分秒, 时利用发光二极管显示, 分秒用数码管显示
12
133)时钟: 采用定时器0计时, 工作方式1
14
15mode = 2. 时钟设置模式
16当选中时钟设置模式
17S2为位选, S3为增加选中位的值
18S4确定更改, S5放弃更改, 进入秒表模式
19
20mode = 3. 秒表
211)当选中秒表功能时, 具体按键功能如下:
22S2为开始/暂停, S3为清零
23
242)采用定时器1计时, 工作方式1
25
26mode = 4. 温度计
271)利用DS18B20检测环境温度;
282)最小温度值为0.01℃, 可表示温度范围: -55℃~+125℃
29
30*/
31
32#include <reg52.H>
33#include <intrins.H>
34#include <math.h>
35
36//0-F数码管的编码(共阴极)
37unsigned char code table[]={0x3f,0x06,0x5b,0x4f,0x66,
38    0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
39//0-9数码管的编码(共阴极), 带小数点
40unsigned char code tableWidthDot[]={0xbf, 0x86, 0xdb, 0xcf, 0xe6, 0xed, 0xfd,
41    0x87, 0xff, 0xef};
42
43sbit wela = P2^7;  //数码管位选
44sbit dula = P2^6;  //数码管段选
45sbit ds = P2^2;
46unsigned char th, tl, mode = 1; //mode存放功能模式, 默认在模式1 时钟
47unsigned char clockPosition = 0;  //时钟设置模式下, 光标所在的位置; 默认在0
48unsigned char clockTmp = 0; //用于时钟模式下临时计数
49bit clockTmpBit = 0; //用于时钟模式下临时标志位
50
51//秒4字节, 分2字节, 时1字节
52unsigned char datas[] = {0, 0, 0, 0, 0, 0, 0};//保存计时器数据
53unsigned char clockDatas[] = {0, 0, 0, 0, 0, 0, 0};//保存时钟数据
54unsigned char * values = clockDatas;  //根据mode选择适当的数据数组指针
55int tempValue; //存放温度值
56unsigned char tempCount = 0; //用于记录显示了多少次温度值, 用于定时
57
58sbit S2 = P3^4;  //键S2, 作开始/暂停
59sbit S3 = P3^5;     //键S3, 清零
60sbit S4 = P3^6;     //键S4
61sbit S5 = P3^7;     //键S5
62unsigned char tmpDatas[] = {0, 0, 0, 0, 0, 0, 0};  //存放临时设置值
63unsigned char icount;
64
65//延时函数, 对于11.0592MHz时钟, 例i=5,则大概延时5ms.
66void delay(unsigned int i)
67{
68    unsigned int j;
69    while(i--)
70    {
71        for(j = 0; j < 125; j++);
72    }
73}
74

使用特权

评论回复
板凳
huamunv|  楼主 | 2009-12-10 21:21 | 只看该作者
75/**//***********************温度计模式******************************/
76
77//初始化DS18B20
78//让DS18B20一段相对长时间低电平, 然后一段相对非常短时间高电平, 即可启动
79void dsInit()
80{
81    //对于11.0592MHz时钟, unsigned int型的i, 作一个i++操作的时间大于为8us
82    unsigned int i;  
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
91void dsWait()
92{
93     unsigned int i;
94     while(ds);  
95     while(~ds);  //检测到应答脉冲
96     i = 4;
97     while(i > 0) i--;
98}
99
100//向DS18B20读取一位数据
101//读一位, 让DS18B20一小周期低电平, 然后两小周期高电平,
102//之后DS18B20则会输出持续一段时间的一位数据
103bit readBit()
104{
105    unsigned int i;
106    bit b;
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    return b;
115}
116
117//读取一字节数据, 通过调用readBit()来实现
118unsigned char readByte()
119{
120    unsigned int i;
121    unsigned char j, dat;
122    dat = 0;
123    for(i=0; i<8; i++)
124    {
125        j = readBit();
126        //最先读出的是最低位数据
127        dat = (j << 7) | (dat >> 1);
128    }
129    return dat;
130}
131
132//向DS18B20写入一字节数据
133void writeByte(unsigned char dat)
134{
135    unsigned int i;
136    unsigned char j;
137    bit b;
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发送温度转换命令
161void sendChangeCmd()
162{
163    dsInit();    //初始化DS18B20, 无论什么命令, 首先都要发起初始化
164    dsWait();   //等待DS18B20应答
165    delay(1);    //延时1ms, 因为DS18B20会拉低DQ 60~240us作为应答信号
166    writeByte(0xcc); //写入跳过序列号命令字 Skip Rom
167    writeByte(0x44); //写入温度转换命令字 Convert T
168}
169
170//向DS18B20发送读取数据命令
171void sendReadCmd()
172{
173    dsInit();
174    dsWait();
175    delay(1);
176    writeByte(0xcc); //写入跳过序列号命令字 Skip Rom
177    writeByte(0xbe); //写入读取数据令字 Read Scratchpad
178}
179
180//获取当前温度值
181void getTempValue()
182{
183    unsigned int tmpvalue;
184    float t;
185    unsigned char low, 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口默认输出高电平, 所以当先位选会使数码管出现乱码
208void displayTemp()
209{
210    unsigned char i;
211    unsigned int tmp = 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//显示时钟和秒表的结果
268void display()
269{
270    unsigned char i;
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

使用特权

评论回复
地板
huamunv|  楼主 | 2009-12-10 21:21 | 只看该作者
342//检测功能模式键是否被按下
343void checkModeKey()
344{
345    if(!S5)
346    {
347        delay(7);
348        if(!S5)
349        {
350            mode++;
351            switch(mode)
352            {
353                case 2: //定时器0是不会关闭的, 即使在设置模式下
354                    for(icount = 0; icount < 7; icount++)
355                    {
356                        tmpDatas[icount] = clockDatas[icount]; //将设置之前的值暂存到临时数组
357                    }
358                    values = tmpDatas;
359                    break;
360                case 3: //秒表模式
361                    values = datas;
362                    break;
363                case 4: //温度计模式
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, 用于时钟模式的设置状态下
382void add()
383{
384    values[clockPosition]++;
385    switch(clockPosition)
386    {
387        case 3:
388        case 5:
389            if(values[clockPosition] > 5)
390                values[clockPosition] = 0;
391            break;
392        case 6:
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//检测是否有键按下, 并执行相应功能
403void checkKey()
404{
405    checkModeKey();
406    switch(mode)
407    {
408        case 2:
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            else if(!S3)  //选中位增1
422            {
423                delay(7);
424                if(!S3)
425                {
426                    add();
427                }
428            }
429            else if(!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        case 3:
444            if(!S2)
445            {
446                delay(7);  //延时大约10ms, 去抖动
447                if(!S2)  //开始/暂停键按下
448                {
449                    TR1 = ~TR1;
450                }
451            }
452        
453            else if(!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
476void main()
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中断响应函数, 用于时钟计时
496void time0() interrupt 1
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中断响应函数, 用于秒表计时
539void time1() interrupt 3
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}

使用特权

评论回复
5
huamunv|  楼主 | 2009-12-10 21:22 | 只看该作者
效果图:
时钟模式: 当前时间为11: 52: 56,98


秒表: 已计时34分13.88秒


温度计: 当前室内温度

使用特权

评论回复
6
lovelyegle| | 2009-12-10 22:26 | 只看该作者
楼主,你强!

使用特权

评论回复
7
wangwo| | 2009-12-11 21:36 | 只看该作者
好长,看你这么辛苦,顶下!

使用特权

评论回复
8
suoma| | 2009-12-12 23:05 | 只看该作者
为人民服务!

使用特权

评论回复
9
laslison| | 2009-12-13 22:22 | 只看该作者
赞!

使用特权

评论回复
10
huamunv|  楼主 | 2009-12-18 22:41 | 只看该作者
个人觉得非常不错:)

使用特权

评论回复
11
god_like| | 2009-12-19 00:53 | 只看该作者
很赞

使用特权

评论回复
12
love_life| | 2009-12-19 00:59 | 只看该作者
好东西

使用特权

评论回复
13
linhai2009| | 2009-12-19 10:03 | 只看该作者
不错,学习了

使用特权

评论回复
14
OUCHANGHUA| | 2010-8-4 16:27 | 只看该作者
不错,学习了

使用特权

评论回复
15
无名刀客| | 2010-8-7 07:49 | 只看该作者
不错,顶

使用特权

评论回复
16
weibestww| | 2011-4-10 22:52 | 只看该作者




好!

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

88

主题

730

帖子

1

粉丝