找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 11083|回复: 10
收起左侧

我的小制作基于AVR单片机的电子时钟

[复制链接]
ID:72519 发表于 2015-1-21 17:42 | 显示全部楼层 |阅读模式
通过几天的AVR单片机学习,用AVR系列单片机中的ATMEGA16,LCD液晶屏LC1602,温度采集模块DS18B20,时钟模块DS1302,5V升压IC,和三个按键,以及一些电子元件完成了数字时钟系统的设计。在一块液晶显示屏上实现动态,时、分、秒、年、月、日、星期、温度数据的显示, 通过三个按键,S1调时键、S2时间加键、S3时间减键、实现时钟的年、月、日、星期、时、分、秒、的调整。本系统设计大部分功能由软件来实现,使电路简单明了。这次制作源代码,是从我已前的一个小制作基于51单片机的DS1302时钟上移植过来的所以制作没用多少时间。一开始用51单片机做这电子钟在调试时还是出了不少问题如,没考虑到51单片机P0口没有内至上拉电阻,结果在硬件上加了8个10K的电阻造成硬件焊接的复杂性。因为锂电池供电的只有3.6V左右而各种芯片要5V才正常工作,根据多年的数码产品维修经验用平板电板中的USB升压电路将3.6V升成了5V。因为没考虑这个自举升电路的频率很高1M到3M左右用了普通二极管做升压中的倍压管造成了电压升不到5V的问题最后改用了快恢复二极管才正常工作,本次制做中的按键调日期功能还没完善好,因每个月的天数都不一样最后决定为到32时把日期变为1。通过这次的制作让我全面的了解了单片机
相关产品的开发流程,及一些常用芯片驱动程序的编写。始我在单片机上的学习更上一层!
  1. 附原代码

  2. #include<iom16v.h>
  3. #include<macros.h>          //头文件
  4. #include"delay.h"

  5. #define uchar unsigned char
  6. #define uint unsigned int

  7. #define S3_R PINA&BIT(0)
  8. #define S2_R PINA&BIT(3)
  9. #define S1_R PINA&BIT(6)

  10. #define RS_L PORTD&=~BIT(0)
  11. #define RS_H PORTD|=BIT(0)
  12. #define RS_OUT DDRD|=BIT(0)   //宏定义单片机与其它芯片的连接方便IO口的操作

  13. #define RW_L PORTD&=~BIT(1)
  14. #define RW_H PORTD|=BIT(1)
  15. #define RW_OUT DDRD|=BIT(1)

  16. #define EN_L PORTD&=~BIT(2)
  17. #define EN_H PORTD|=BIT(2)
  18. #define EN_OUT DDRD|=BIT(2)

  19. #define IO_L PORTB&=~BIT(5)
  20. #define IO_H PORTB|=BIT(5)
  21. #define IO_OUT DDRB|=BIT(5)
  22. #define IO_R PINB&BIT(5)
  23. #define IO_IN DDRB&=~BIT(5)

  24. #define SCK_L PORTB&=~BIT(6)
  25. #define SCK_H PORTB|=BIT(6)
  26. #define SCK_OUT DDRB|=BIT(6)

  27. #define RST_L PORTB&=~BIT(7)
  28. #define RST_H PORTB|=BIT(7)    //宏定义单片机与其它芯片的连接方便IO口的操作
  29. #define RST_OUT DDRB|=BIT(7)

  30. #define DQ_L PORTD&=~BIT(7)
  31. #define DQ_H PORTD|=BIT(7)
  32. #define DQ_OUT DDRD|=BIT(7)
  33. #define DQ_R PIND&BIT(7)
  34. #define DQ_IN DDRD&=~BIT(7)

  35. uchar tab4[]={0x82,0x85,0x88,0x8f,0xc0,0xc3,0xc6};//用来存放年月日星期时分秒的地址
  36. uchar write_add[7]={0x8c,0x88,0x86,0x8a,0x84,0x82,0x80}; //写寄存器地址
  37. uchar read_add[7]={0x8d,0x89,0x87,0x8b,0x85,0x83,0x81};//存放从1302中读数寄存器地址
  38. uchar tab1[]="20  -  -   week  ";  //屏上要固定显示的字符
  39. uchar tab2[]="  :  :  ";//屏上要固定显示的字符
  40. uchar tab3[];
  41. uchar tab[]={12,2,4,5,5,5,5};//年 月 日 周 时 分 秒的初值
  42. char tung;//根据tung确定按键位置不能定成uchar型
  43. void write_1602com(uchar com);//1602写地址函数
  44. void init_1602(void);//1602初始化
  45. void write_1302bvte(uchar);//1302写数据函数
  46. uchar read_1302(uchar);//1302读数据函数
  47. void write_1302(uchar,uchar);//1302指定地址定数据函数
  48. void write_ds1302_1();//时间显示函数
  49. void read_1302_1();//一次读出年月日 时分秒
  50. void flx(uchar j);  //时间转显示函数
  51. void key(void); //按键函数
  52. void set_rtc(void); //一次设置初始时间年月日函数
  53. void init_18b20(void);//18b20初始化函数
  54. void write_18b20(uchar value);//ds18b20写数据函数
  55. uchar read_18b20(void);//读温度函数
  56. uchar readtemperature(void);//一次性读出温度
  57. void xianshi(void);//温度显示函数

  58. void init_18b20(void)
  59. {
  60.    DQ_OUT;
  61.    DQ_L;
  62.    delay_100us(5);
  63.    DQ_H;                    //初始化18B20
  64.    delay_100us(5);
  65. }
  66. void write_18b20(uchar value)
  67. {
  68.   uchar i;
  69.   for(i=0;i<8;i++)
  70.   {
  71.      DQ_OUT;
  72.      DQ_L;           //写一位函数
  73.   delay_10us();
  74.   if(value&0x01)//
  75.   {
  76.      DQ_H;
  77.   }
  78.   }
  79.   delay_100us(1);
  80.   DQ_H;
  81.   value=value>>1;
  82. }
  83. uchar read_18b20(void)
  84. {
  85.   uchar i,value;
  86.   for(i=0;i<8;i++)
  87. {
  88. value=value>>1;
  89. DQ_OUT;
  90. DQ_L;
  91. delay_10us();//读一位函数
  92. DQ_H;
  93. DQ_IN;
  94. if(DQ_R)
  95. {
  96.    value|=0x80;
  97. }
  98.    delay_50us();
  99. }
  100.   return value;
  101. }

  102. uchar readtemperature(void)//一次性读出温度
  103. {
  104. uint i,j;//一定要用int型j大于256
  105. init_18b20;
  106. write_18b20(0xcc);//跳过ROM
  107. write_18b20(0x44);//启动温度测量
  108. delay_100us(20);
  109. init_18b20();
  110. write_18b20(0xcc);
  111. write_18b20(0xbe);//读取温度
  112. i=read_18b20();//低位
  113. j=read_18b20();//高位
  114. i=i>>4;    //因不用小数部分所以移4位
  115. j=j*16+i; //将存放低8位的和高8位合到一块
  116.   return j ;
  117. }
  118. void xianshi(void)
  119. {
  120.    uint temp,k,f;
  121.    temp=readtemperature();//将温度保存到temp中
  122.    f=temp/10;  //分离出十位跟个位
  123.    k=temp%10;
  124.    write_1602com(0x80+0x40+10);//温度显示的位置
  125.    write_1602dat(f+0x30);
  126.    write_1602dat(k+0x30); //将温度写到1602中

  127. }
  128. void set_rtc(void) //一次设置初始时间年月日函数
  129. {
  130.    uchar i,j;
  131.    for(i=0;i<7;i++)
  132.    {
  133.      j=tab[i]/10; //将设置的年月日时分解出十位放入J中
  134.      tab3[i]=tab[i]%10; //将设置的年月日时间分解出个位再放入time_data[]数组中
  135.      tab3[i]=tab3[i]+j*16;//将十进制的个位和十位转换为BCD码
  136.    }
  137.    write_1302(0x8e,0x00);//去除写保护
  138.    for(i=0;i<7;i++)
  139.    {
  140.      write_1302(write_add[i],tab3[i]); //写要设置初始时间日期的操作寄存器的地址
  141.    }
  142.    write_1302(0x8e,0x80);//加写保护
  143. }
  144. void key(void)
  145. {  
  146.    uchar s1,s2,s3;
  147.    if(~S1_R) //如果S1按下
  148.    {
  149.      delay_1ms(3); //延时去抖
  150.      if(~S1_R)  
  151.      {
  152. while(~S1_R);   //
  153.         tung=0;  //tung用来确定光标的位置
  154. while(tung<=6) //循环7次   时分秒年月日星期
  155. {  
  156.   if(~S2_R)
  157.   {
  158.     delay_1ms(3);
  159.   if(~S2_R)//去抖动延时
  160.   {
  161. while(~S2_R);    // 检测按键是否放开
  162. tab[tung]++; //如果+键按下加1
  163.       }
  164. }
  165. if(~S3_R)
  166. {
  167.    delay_1ms(3); // 检测按键是否放开
  168.    if(~S3_R)
  169. {
  170. while(~S3_R);  //按键是否松手
  171. tab[tung]--;  //如果—键按下减1
  172. }
  173. }  

  174. switch(tung)
  175. { //通过tung的值确定光标的位置
  176. case 0:
  177.   if(tab[tung]<3)  //如果tung=0;则表示正在调年让年在2004到2098这个范围内
  178.   tab[tung]=98;
  179.   if(tab[tung]>98)
  180.   tab[tung]=4;
  181.   break;
  182.   
  183. case 1:
  184.   if(tab[tung]<1)
  185.    tab[tung]=12;
  186.   if(tab[tung]>12)  //如果tung=1;则表示正在调月让月在1到12这个范围内
  187.     tab[tung]=1;
  188.    break;
  189. case 2:
  190.   if(tab[tung]==0)
  191.    tab[tung]=31;
  192.   if(tab[tung]==32)   //如果tung=2;则表示正在调日让在1到31这个范围内
  193.    tab[tung]=1;
  194.   break;
  195. case 3:
  196. if(tab[tung]<1)
  197.   tab[tung]=7;
  198. if(tab[tung]>7)   //如果tung =3则表示下在调星期让在1到7这个范围内
  199.   tab[tung]=1;
  200.   break;
  201. case 4:
  202. if(tab[tung]<0)
  203.      tab[tung]=23;
  204. if(tab[tung]>23) //如果tung=4则表示在调时让时在1到23这个范围内
  205.   tab[tung]=0;
  206.    break;
  207. case 5:
  208. if(tab[tung]<0)
  209.   tab[tung]=59;
  210. if(tab[tung]>59)   //如果tung=5;则表示正在调分让分在1到59这个范围内
  211.   tab[tung]=0;
  212.    break;
  213. default:
  214. if(tab[tung]<0)
  215.   tab[tung]=59;
  216. if(tab[tung]>59)   //如果tung=6;则表示正在调秒让秒在1到59这个范围内
  217.   tab[tung]=0;
  218.    break;
  219. }
  220. write_1602com(tab4[tung]); //显示正在调的位置  
  221. flx(tab[tung]);    //将数据分离成1602的显示码并送到1602显示屏


  222. if(~S1_R)
  223. {
  224. while(~S1_R);  //判断是否调好了时间如果好了则跳出key
  225. tung++; //每按一下S1调式模式键则加一次如果到了7次跳出KEY函数
  226. }

  227. }

  228. }
  229. write_1602com(0x0c);//调完了时间关闭光标
  230. set_rtc(); //把调好的时间数据送到DS1302芯片中
  231. }
  232. }


  233. void flx(uchar j)
  234. {  
  235. uchar k,a;
  236. write_1602com(0x0f);  //开光标显示指令
  237. k=j/10;   //分离十位跟个位
  238. a=j%10;
  239. if(tung!=3) //年月日时分秒分离
  240. {

  241.      write_1602dat(k+0x30);  //将分离的十位跟个位换成LC1602能显示的码
  242. write_1602com(0x0c);//关光标指令   
  243.      write_1602dat(a+0x30);

  244. }
  245. else

  246. write_1602dat(a+0x30); //星期分离
  247.   
  248. }

  249. void read_1302_1(void)//一次读出年月日 时分秒函数
  250. {
  251. uchar i,shi,ge,tun;
  252. for(i=0;i<7;i++)
  253. {
  254. tun=read_1302(read_add[i]);  //一次将年月日从DS1302中读出送到数组中
  255. ge=tun%16;
  256. shi=tun/16;    //将BCD码分成十位和个位
  257. tab[i]=shi*10+ge; //再换成十进制存到数组tab中

  258. }
  259. }
  260. void write_ds1302_1()
  261. {
  262.     read_1302_1();//一次读出年月日 时分秒函数  
  263.    write_1602com(0x80+0x40+6);//秒的位置
  264. write_1602dat(tab[6]/10+0x30);
  265. write_1602dat(tab[6]%10+0x30);

  266. write_1602com(0x80+0x40+3);//
  267. write_1602dat(tab[5]/10+0x30); //分的位置
  268. write_1602dat(tab[5]%10+0x30);
  269.   //将十进制的数据换成1602的显示码送到显示屏
  270. write_1602com(0x80+0x40+0);// //时的地址
  271. write_1602dat(tab[4]/10+0x30);   //显示屏的第二排要加0x80+40
  272. write_1602dat(tab[4]%10+0x30);

  273. write_1602com(0x80+2);//
  274. write_1602dat(tab[0]/10+0x30);//年的地址
  275. write_1602dat(tab[0]%10+0x30);

  276. write_1602com(0x80+5);// 显示屏 的第一排地址要加0x80;
  277. write_1602dat(tab[1]/10+0x30);
  278. write_1602dat(tab[1]%10+0x30); //月的地址

  279. write_1602com(0x80+8);//   日的地址
  280. write_1602dat(tab[2]/10+0x30);
  281. write_1602dat(tab[2]%10+0x30);

  282. write_1602com(0x80+15);    //星期的地址
  283. write_1602dat(tab[3]%10+0x30);
  284. }
  285. void init_1302(void)
  286. {
  287.   SCK_OUT;
  288.   RST_OUT;//1302IO口初始化
  289. }

  290. void write_1302(uchar dat ,uchar add)
  291. {
  292.    RST_L;
  293.    SCK_L;
  294.    RST_H;
  295.    write_1302bvte(dat);//指定地址写数据函数
  296.    write_1302bvte(add);
  297.    RST_L;
  298.    SCK_H;
  299. }
  300. void write_1302bvte(uchar add)
  301. {
  302.   uchar i;
  303.   RST_L;
  304.   SCK_L;
  305.   IO_OUT;
  306.   delay_1ms(2);
  307.   RST_H;
  308.   for(i=0;i<8;i++)
  309.   {
  310.    if(add&0X01)
  311.    {
  312.     IO_H;        //1302写函数
  313.    }
  314.    else
  315.    {
  316.     IO_L;
  317.    }
  318.    SCK_H;
  319.    SCK_L;
  320.    add=add>>1;
  321.    }
  322.   }
  323. uchar read_1302(uchar dat)
  324. {
  325.   uchar i,byte;
  326.   write_1302bvte(dat);
  327.   IO_IN;
  328.   for(i=0;i<8;i++)
  329.   {
  330.     byte=byte>>1;
  331.     if(IO_R)
  332.     {                  //1302读数据函数

  333.      byte=byte|0x80;
  334.     }
  335.     SCK_H;
  336.     SCK_L;
  337.   }
  338.   RST_L;
  339. return byte;
  340. }


  341. void write_1602com(uchar com)
  342. {
  343. RS_L;
  344. RW_L;    //写指令函数
  345. PORTC=com;
  346. EN_H;
  347. delay_1ms(4);
  348.     EN_L;
  349. }
  350. void write_1602dat(uchar dat)
  351. {
  352. RS_H;
  353. RW_L;
  354.     PORTC=dat;
  355.     EN_H;                //写数据函数
  356. delay_1ms(4);
  357. EN_L;

  358. }
  359. void init_1602(void)
  360. {  
  361.    uchar tou;
  362.     DDRC=0XFF;
  363. PORTC=0;
  364. RS_OUT;
  365. RW_OUT;
  366. EN_OUT;
  367. EN_L;
  368. write_1602com(0x38);//
  369. write_1602com(0x0C);//  //1602初始化函数
  370. write_1602com(0x06);
  371. write_1602com(0x01);
  372. write_1602com(0x80);
  373.     write_1602com(0x80);  //第一行显示的位置地址
  374.     for(tou=0;tou<16;tou++)
  375. {
  376. write_1602dat(tab1[tou]);  //显示固定字符
  377. }
  378.     write_1602com(0x80+0x40); //第二行显示的位置地址
  379.     for(tou=0;tou<8;tou++)
  380.     {
  381. write_1602dat(tab2[tou]);
  382. }
  383.     write_1602com(0x80+0x40+12);   
  384.     write_1602dat(0xdf);//显示温度的小圆圈符号,0xdf是液晶屏字符库的该符号地址码
  385.     write_1602dat(0x43); //显示"C"符号,0x43是液晶屏字符库里大写C的地址码 */
  386. }

  387. void main(void)
  388. {
  389.   DDRA&=~0X49;//按键初始化
  390.   PORTA|=0X49;

  391.   init_1602();
  392.   init_1302();
  393. while(1)
  394. {
  395.     xianshi();//温度显示函数
  396.    write_ds1302_1();//时间显示函数
  397.    key();//按键函数
  398. }
  399. }

  400. =0X49;

  401.   init_1602();
  402.   init_1302();
  403. while(1)
  404. {
  405.     xianshi();//温度显示函数
  406.    write_ds1302_1();//时间显示函数
  407.    key();//按键函数
  408. }
  409. }

复制代码



回复

使用道具 举报

ID:81954 发表于 2015-6-3 20:36 | 显示全部楼层
为何不能运行呢
回复

使用道具 举报

ID:82588 发表于 2015-6-10 10:19 | 显示全部楼层
收藏了,正打算也练下手,来一个,做完和你的对比一下
回复

使用道具 举报

ID:87088 发表于 2015-7-28 17:19 | 显示全部楼层
程序已经保存下来了,稍后学习一下,谢谢楼主分享。
回复

使用道具 举报

ID:210328 发表于 2017-6-14 16:49 | 显示全部楼层
为啥运行有错误?
回复

使用道具 举报

ID:82765 发表于 2017-6-17 12:33 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
回复

使用道具 举报

ID:44037 发表于 2017-6-18 14:35 | 显示全部楼层
谢谢楼主分享。学习一下
回复

使用道具 举报

ID:210328 发表于 2017-6-18 23:40 | 显示全部楼层
cjjcjj1 发表于 2017-6-17 12:33
具体错误内容是什么,贴出来看看


D:\ICCAVR\ICCAVR安装\bin\imakew -f 222.mak
    iccavr -c -ID:\ICCAVR\ICCAVR安装\include -e -D__ICC_VERSION=722 -DATMega16  -l -g -MLongJump -MHasMul -MEnhanced  2222.c
!E F:\期末单片机作业\程序\2222\2222.c(3): cannot include source file "delay.h"; file not found
D:\ICCAVR\ICCAVR安装\bin\imakew.exe: Error code 1
Done: there are error(s). Exit code: 1. Sun Jun 18 23:39:26 2017
回复

使用道具 举报

ID:275398 发表于 2018-5-21 22:23 | 显示全部楼层
666666
回复

使用道具 举报

ID:413383 发表于 2018-10-23 22:22 | 显示全部楼层
AVR学习中,为何不能运行呢
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

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