搜索
bottom↓
回复: 51

基于ATMEGN8的闹钟

[复制链接]

出0入0汤圆

发表于 2005-6-15 22:38:19 | 显示全部楼层 |阅读模式
一.硬件电路图仅供参考



二.软件实现

/*****************************************************************************************

本程序是用CODEVISIONAVR编写的闹钟程序。当前时间和闹钟时间可以自由设定,由于本程序使用了较少的

库函数,所以往不同的开发平台移植较容易。比如往WINAVR上移植,只需编写DELAY函数即可。

TARGET CHIP;ATMEGA8

PORTD作为LED的数据端,PORTC.5&PORTC.5   PORTB.7&PORTB.6作为4个LED的片选

PORTC.1.PORTC.2作为设定当前时间和闹铃键

作者:谢长才

QQ:363236498(随风往事)

联系方式:X_CHC@EYOU.COM OR X_CHC@YAHHOO.COM.CN

时间:2005年6月

北京信息工程学院信息与通信系 *****************************************************************************************/

#include <mega8.h>   

#include <delay.h>

static unsigned char g_bCount=0;    //计数值

const unsigned char segled [10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f} ; //LED字符表

unsigned char ledbuff[10]={0,1,2,3,4,5,6,7,8,9};  //LED缓冲

unsigned char hh=0,hl=0,mh=0,ml=0,sh=0,sl=0,sec=0,minu=0,hr=0,timeh=7,tihh=0,tihl=0,timem=30, //全局变量

timh=0,timl=0;

void ledtest (void)         //测试LED从0记到9

{

unsigned char i;  

DDRC=0xf1;

PORTC=0x00;

DDRD=0xff;

DDRB=0xf1;

PORTB =0xff;  

delay_ms(500);

PORTB =0x00;

for(i=1;i<=10;i++)  

{

  PORTD=segled[ledbuff[i-1]];

  delay_ms(400);

   } PORTD=0x00;

   PORTB=0xf1;  

   PORTC|=0xf0;

  }

void alarm(void) //音响提示1

{

unsigned char x,y,m;   

for(m=0;m<8;m++)  {

for(y=1;y<20;y++)

{

for(x=1;x<15;x++)

{

PORTC.0=1; PORTB.0 =1;

delay_us(75);

PORTC.0=0;  PORTB.0 =0;

delay_us(75);

}

}   

for(y=1;y<25;y++)

{

for(x=1;x<20;x++)

{

PORTC.0=1;

delay_us(400);

PORTC.0=0;

delay_us(400);

}

} }

}     

void alarml(void) //音响提示2

{

unsigned char x,y,m;   

for(m=0;m<8;m++)  {

for(y=1;y<35;y++)

{

for(x=1;x<25;x++)

{

PORTC.0=1; PORTB.0 =1;

delay_us(175);

PORTC.0=0;   PORTB.0 =0;

delay_us(705);

}

}   

for(y=1;y<35;y++)

{

for(x=1;x<25;x++)

{

PORTC.0=1;

delay_us(1000);

PORTC.0=0;

delay_us(900);

}

}

}

}   

void timedisplay (void)            //显示当前闹铃设定时间

{   unsigned int i,j;

     j=timem/10;   

   if(j>=6)  

   {    timh=j-6;

        timeh++;

        if(timeh>=24)

        timeh-=24;

        timem-=60;

        }

   else

   timh=j;

   timl=timem%10;

   tihh=timeh/10;

   tihl=timeh%10;

  for (i=0;i<100;i++){

   PORTB.7=0;

   PORTD=segled[ledbuff[tihh]];

   delay_ms(6);

   PORTB.7=1;

   PORTB.6=0;

   PORTD=segled[ledbuff[tihl]];

   delay_ms(6);

   PORTB.6=1;

   PORTC.5=0;

   PORTD=segled[ledbuff[timh]];

   delay_ms(6);

   PORTC.5=1;

   PORTC.4=0;

   PORTD=segled[ledbuff[timl]];

   delay_ms(6);

   PORTC.4=1;}  

}

void timeplay (void)                //显示当前时间

{  unsigned int i,j;

     j=minu/10;   

   if(j>=6)  

   {mh=j-6;

    hr+=1;

   if(hr>=24)

   hr-=24;

   minu-=60;

   }  

   else

   mh=j;

   ml=minu%10;

   hh=hr/10;

   hl=hr%10;

for (i=0;i<100;i++){

   PORTB.7=0;

   PORTD=segled[ledbuff[hh]];

   delay_ms(6);

   PORTB.7=1;

   PORTB.6=0;

   PORTD=segled[ledbuff[hl]];

   delay_ms(6);

   PORTB.6=1;

   PORTC.5=0;

   PORTD=segled[ledbuff[mh]];

   delay_ms(6);

   PORTC.5=1;

   PORTC.4=0;

   PORTD=segled[ledbuff[ml]];

   delay_ms(6);

   PORTC.4=1;}  

}



interrupt [TIM0_OVF] void timer0_ovf_isr(void)     //中断程序

{

TCNT0=0x00;

if(++g_bCount >14)

{

g_bCount=0;

sec++;

if(sec>=60) //秒判断

{

sec=0; minu++;

if(minu>=60) //分判断

{

minu=0;

hr++;

if(hr>=24) //小时判断

{

hr=0;

}

hh=hr/10;

hl=hr%10;

}

mh=minu/10;

ml=minu%10;

}

sh=sec/10;

sl=sec%10;

}

if((hr==timeh)&&(minu==timem))            //与闹铃设定的时间相同时报警

                 {alarm();

                  alarml();}

}





void main (void)  

{

OSCCAL=0xa0;                             //振荡校验

ledtest();                               //开机测试

DDRC=0xf1;

TCNT0=0;                                // T/C0开始值

TCCR0|=0x05;                            // 预分频 ck/1024 ,计数允许

TIMSK=0x01;

#asm("sei")

  timedisplay ();                        //显示闹铃设定时间



    while (1)                            //检测按键

  {  if (PINC.1==1)   

    { minu+=10;  timeplay();             //当前时间加

     delay_ms(70);  }

     if (PINC.2==1)

      { timem+=10;  timedisplay();      //当前闹铃加

     delay_ms(70);  }  

     else                               //动态显示

   {PORTB.7=0;

   PORTD=segled[ledbuff[hh]];

    delay_ms(7);

     PORTB.7=1;

    PORTB.6=0;

    PORTD=segled[ledbuff[hl]];

    delay_ms(7);

     PORTB.6=1;

    PORTC.5=0;

    PORTD=segled[ledbuff[mh]];

    delay_ms(7);

     PORTC.5=1;

    PORTC.4=0;

    PORTD=segled[ledbuff[ml]];

    delay_ms(7);

    PORTC.4=1;

    } }

}



-----此内容被x_chc于2005-06-15,22:47:58编辑过



-----此内容被x_chc于2005-06-15,22:55:15编辑过



-----此内容被x_chc于2005-06-16,23:04:00编辑过


-----此内容被x_chc于2005-06-16,23:15:32编辑过

出0入0汤圆

发表于 2005-6-15 23:54:00 | 显示全部楼层
用內部rc振盪做闹钟, 樓主果然nb!

建議在定時中斷中不要做太多的事,影響精度的.

出0入0汤圆

发表于 2005-6-16 07:57:42 | 显示全部楼层
这个东东误差大么?

出0入0汤圆

 楼主| 发表于 2005-6-16 14:26:46 | 显示全部楼层
基本没有误差,

出0入0汤圆

 楼主| 发表于 2005-6-16 14:27:50 | 显示全部楼层
哦,我是个初学者,知道了,中断里不写太多代码

出0入0汤圆

发表于 2005-6-16 15:10:21 | 显示全部楼层
你是如何修正 进入中断所消耗掉的时间呢?

出0入0汤圆

 楼主| 发表于 2005-6-16 15:52:48 | 显示全部楼层
我中断15次才算做1秒,其实此时才0.987秒(算过记不太清了)

这不就自动修正了吗,任何东西都有误差,在我们能容忍的范围内就行。

出0入0汤圆

发表于 2005-6-17 19:56:09 | 显示全部楼层
ok,顶一下

出0入0汤圆

发表于 2005-6-17 19:58:33 | 显示全部楼层
不錯, 不頂不行!!

出0入0汤圆

 楼主| 发表于 2005-6-17 22:45:33 | 显示全部楼层
这可是我花了2个下午才搞出来的东东!

出0入0汤圆

发表于 2006-2-22 17:18:56 | 显示全部楼层
那也到是哦,用个 I 可以计算是多少秒,用M16 可以用一个中断

大家看看一个我的代码!

出0入0汤圆

发表于 2006-2-22 17:21:08 | 显示全部楼层
/*******************************************************/

// 定时1ms

// 时间:2006.2.11

// QQ:136461278

// 作者:lychee

// 芯片:M16

/*******************************************************/

#include"iom16v.h"

#include"macros.h"

#include"M16_display.h"



unsigned int time = 0xffff;



/**************************************************************/

//  中断函数

/*************************************************************/

#pragma interrupt_handler timer1_ovf:9



void timer1_ovf(void)

{

    unsigned int i,n;

        TCNT1H = 0x85; //reload counter high value

    TCNT1L = 0xEE; //reload counter low value

        //PORTD = 0XFF;   // D 灯不亮

        time  = time + 1;

        //PORTD = 0X00;   

}

/********************************************************************/

//端口初始化

/********************************************************************/

void Init_Port(void)

{

    PORTA = 0x00;

    DDRA  = 0x00;

    PORTB = 0x00;

    DDRB  = 0x00;

    PORTC = 0x00; //m103 output only

    DDRC  = 0x00;

    PORTD = 0x00;

    DDRD  = 0xff; // D口灯亮

    //DDRD = 0XFF;

        //PORTD= 0X00;   

  

}

/*******************************************************************/

// 定时器初始化

/*******************************************************************/

void Init_timer1(void)

{

    TCCR1B = 0x00; //stop

    TCNT1H = 0x85; //setup

    TCNT1L = 0xEE;

    OCR1AH = 0x7A;

    OCR1AL = 0x12;

    OCR1BH = 0x7A;

    OCR1BL = 0x12;

    //OCR1CH = $OCR1CH$;

    //OCR1CL = $OCR1CL$;

    ICR1H  = 0x7A;

    ICR1L  = 0x12;

    TCCR1A = 0x00;

    TCCR1B = 0x04; //start Timer

}



void main(void)

{

    CLI();

       

    Init_Port();        //D 口灯亮

        Init_timer1();

        //TIMSK = 0X01;

        //PORTD = 0XFF;

    TIMSK = 0x04; //timer interrupt sources

    SEI(); //re-enable interrupts

        //while(1)



        while(1)

        {

           //time  = time + 1;

       M16_display(time);

        }   

       

            //;

}

/************** end ********************************************/

出0入0汤圆

发表于 2006-2-22 18:16:05 | 显示全部楼层
误差要累计的,10ppm的误差累计一年相当可观,RC振荡器能保证日误差小于1S吗

出0入0汤圆

发表于 2006-2-22 20:22:34 | 显示全部楼层
不知转到GCC或ICC容易吗?

出0入0汤圆

发表于 2006-2-28 19:11:33 | 显示全部楼层
如果改到icc的话,不用很麻烦的,我有一个类似的程序,可以显示年月日时分秒,而且可以有闰年和非闰年的区别。

出0入0汤圆

发表于 2006-2-28 23:38:53 | 显示全部楼层
同以共享吗?

出0入0汤圆

发表于 2006-3-4 11:52:28 | 显示全部楼层
ATmega8内部具有接32768手表晶振的异步定时器,为的就是时钟的应用,干嘛还用内部RC振荡?

出0入0汤圆

发表于 2006-3-4 20:59:56 | 显示全部楼层
不错,顶一下!!!

出0入0汤圆

发表于 2006-3-5 15:00:13 | 显示全部楼层
很好,最近想做一个时钟温度计练练手,正好参考

出0入0汤圆

发表于 2006-3-5 16:22:23 | 显示全部楼层
果然够牛,居然不用晶振,不知道一天会漂移几秒呢?
头像被屏蔽

出0入0汤圆

发表于 2006-3-6 20:15:42 | 显示全部楼层
应该是以分钟为单位。

出0入0汤圆

发表于 2006-3-7 00:20:58 | 显示全部楼层
走时误差的问题,对于做计时产品而言,是非常关键的一项指标;但作为编程学习的目的,倒不用太在意这个。毕竟能够将功能实现,能正常运行就达到学习单片机的目的了。

出0入0汤圆

发表于 2007-1-30 11:19:34 | 显示全部楼层
#include <mega8.h>     

#include <delay.h>  



请问楼主这两个头文件在哪里能找到?

请指点,我是初学者,先谢了!

出0入0汤圆

发表于 2007-4-11 19:55:39 | 显示全部楼层
收下了,谢谢

出0入0汤圆

发表于 2007-4-11 22:51:21 | 显示全部楼层
不计算日误差时间的能叫时钟?充其量算个可编程振荡器

日误差1s仅仅是相当低的要求呀

出0入0汤圆

发表于 2007-4-12 08:34:26 | 显示全部楼层
我中断15次才算做1秒,其实此时才0.987秒(算过记不太清了)

这不就自动修正了吗,任何东西都有误差,在我们能容忍的范围内就行。



请问楼主上述话是每秒钟误差在1-0.987=0.013秒吗?

若是这样的话,那么这个时钟1小时误差是0.013*3600=46.8秒

1天的误差是46.8*24=1123秒=18.72分钟

1个月的误差是18.72分钟*30=561分钟=9.36小时

1年的误差是9.36小时*12=112.32小时=4.68天



你1年误差4天半这个时钟还有什么意义?我见过1个时钟项目,要求是每月误差是1秒。



还有楼主有没有考虑功耗问题?

出0入0汤圆

发表于 2007-4-13 10:36:03 | 显示全部楼层
呵呵,大家别太较真,人家楼主没说要来当的时钟用呀,做为AVR学习例子,很不错了。



另,如果真的想做个好用的时钟,还是用DS1302之类,要降低成也,最少也要在ATmega8接个32768的手表晶振。

出0入0汤圆

发表于 2007-4-13 11:04:30 | 显示全部楼层
既然是当作时钟,还是要有个基本的样子和规格的.

ATmega8接个32768的手表晶振,有多少人能做到1s/天?

出0入0汤圆

发表于 2007-4-13 23:11:15 | 显示全部楼层
用内部RC很难做到计时精确。做时钟一定要用外部晶振,我用M8做过一个,用外部8M晶振,TIMER1中断,0.5秒中断一次。为了校准,24小时记录一次误差,调整参数,用了三四天时间,做到了一天误差小于1秒,再没有进一步校准,太麻烦。

出0入0汤圆

发表于 2007-4-13 23:25:53 | 显示全部楼层
曾经拆过一个很便宜的电子闹钟,不用晶振。

当接交流电时,使用外接的交流电60Hz,结果还相当准确;当用电池时,用内部振荡,惨不忍睹......

出0入0汤圆

发表于 2007-4-14 00:59:20 | 显示全部楼层
楼上的兄台,你大概没有见过有些地区的市电频率能下漂到47Hz,好恐怖啊……

出0入0汤圆

发表于 2007-4-14 01:14:20 | 显示全部楼层
这个充其量只能算是个玩具闹钟,或用来向幼儿园演示闹钟功能的仪器。不过还是要支持楼主,希望能更上一层楼。这个例子用来学习也不合适,方法是错的。

出0入0汤圆

发表于 2007-4-14 01:16:55 | 显示全部楼层
这种情况还是比较少的,总体平均下来还是接近50Hz的。我在电厂工作。如果没有联网的话就不能保证电网频率了。联网后的频率差的就不大了。尤其现在准备全国电网上一次调频功能。其实还是为了应付这个频率的问题。

出0入0汤圆

发表于 2007-7-19 09:09:09 | 显示全部楼层
很不错,以后大家多交流交流

出0入0汤圆

发表于 2007-7-21 10:59:05 | 显示全部楼层
327678配DS1302可以降低功耗,误差与不用DS1302差不多。一天做到小于1S就可以了。

出0入0汤圆

发表于 2007-8-13 15:23:53 | 显示全部楼层
初學者都喜歡在中斷程序里寫上一大堆,這樣很不明智,占用了系統大量的時間.可以把10ms的定時寫在中斷程序里,再在子程序中通過10ms的旗標進行秒,分,時的計時.

出0入0汤圆

发表于 2007-8-15 17:37:39 | 显示全部楼层
testcode 你知道为什么用交流电作为时基很准吗?



因为电网的电能考核里就有这个指标,每天晚上,调度中心(由最高级进行,以前是广东电网调度中心,现在是南方电网调度中心)都要进行一项工作,就是调节时钟,如果当天的时钟慢了(这个时钟就是以交流电的频率作为时钟的时基),就适当把电网的频率调高,反之调低.



当然这是指大电网,小电网就当然不准了.

出0入0汤圆

发表于 2007-8-21 11:06:24 | 显示全部楼层
误差应该比较大,呵呵。

出0入0汤圆

发表于 2007-9-5 17:32:48 | 显示全部楼层
用串口同步一下,不久准了,哈哈

出0入0汤圆

发表于 2009-4-21 08:50:42 | 显示全部楼层
很好,漂亮!能否用LCD1602显示呢,希望各位帮忙一下啊!!!程序和电路图啊

出70入0汤圆

发表于 2009-4-21 08:59:14 | 显示全部楼层
这个钟误差绝对大的,我用1302,一天都有几秒的误差

出0入0汤圆

发表于 2009-4-21 09:51:42 | 显示全部楼层
看看,可能误差较大

出0入0汤圆

发表于 2009-4-21 09:52:30 | 显示全部楼层
一般用时钟芯片会准些,不过这个成本低

出0入0汤圆

发表于 2009-4-21 16:31:43 | 显示全部楼层
我给一个建议:让M8使用32.768KHz的手表晶振,使用TC2的异步方式,这样保证计时的精度;另外你这里有四个数码管,所以调整TC2每秒溢出中断256次,每次中断就点亮一个数码管,即相当于4个数码管每秒刷新64次,然后在主程序中完成程序的其他功能。

我看你的程序中控制数码管显示是调用的函数,调用一次要占用很长的时间,而我的建议会让控制数码管显示所需要的时间用的最少,但是效果不会受到影响。

出0入0汤圆

发表于 2009-4-21 16:49:51 | 显示全部楼层
内部RC在常温还可以,温度稍变化就不行了。

出0入0汤圆

发表于 2009-4-21 19:51:12 | 显示全部楼层
误差;肯定是有的、、、、

出0入0汤圆

发表于 2009-4-21 21:02:34 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-5-25 07:25:29 | 显示全部楼层
好好啊 支持

出0入0汤圆

发表于 2009-10-14 15:21:15 | 显示全部楼层
不知道楼主有没有avr M16 用数码管做的电子表的程序呀,只要是显示年月日,还可以调时,并伴有闪烁。谢谢指教,初学者,如果可以就发到我的邮箱shiyuanzhangjin@163.com

出0入0汤圆

发表于 2009-10-18 16:17:05 | 显示全部楼层
支持

出0入0汤圆

发表于 2009-12-28 11:32:41 | 显示全部楼层
学习ing...

出0入0汤圆

发表于 2009-12-28 18:36:03 | 显示全部楼层
mark
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-7 20:42

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表