通过几天的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。通过这次的制作让我全面的了解了单片机
相关产品的开发流程,及一些常用芯片驱动程序的编写。始我在单片机上的学习更上一层! - 附原代码
- #include<iom16v.h>
- #include<macros.h> //头文件
- #include"delay.h"
- #define uchar unsigned char
- #define uint unsigned int
- #define S3_R PINA&BIT(0)
- #define S2_R PINA&BIT(3)
- #define S1_R PINA&BIT(6)
- #define RS_L PORTD&=~BIT(0)
- #define RS_H PORTD|=BIT(0)
- #define RS_OUT DDRD|=BIT(0) //宏定义单片机与其它芯片的连接方便IO口的操作
- #define RW_L PORTD&=~BIT(1)
- #define RW_H PORTD|=BIT(1)
- #define RW_OUT DDRD|=BIT(1)
- #define EN_L PORTD&=~BIT(2)
- #define EN_H PORTD|=BIT(2)
- #define EN_OUT DDRD|=BIT(2)
- #define IO_L PORTB&=~BIT(5)
- #define IO_H PORTB|=BIT(5)
- #define IO_OUT DDRB|=BIT(5)
- #define IO_R PINB&BIT(5)
- #define IO_IN DDRB&=~BIT(5)
- #define SCK_L PORTB&=~BIT(6)
- #define SCK_H PORTB|=BIT(6)
- #define SCK_OUT DDRB|=BIT(6)
- #define RST_L PORTB&=~BIT(7)
- #define RST_H PORTB|=BIT(7) //宏定义单片机与其它芯片的连接方便IO口的操作
- #define RST_OUT DDRB|=BIT(7)
- #define DQ_L PORTD&=~BIT(7)
- #define DQ_H PORTD|=BIT(7)
- #define DQ_OUT DDRD|=BIT(7)
- #define DQ_R PIND&BIT(7)
- #define DQ_IN DDRD&=~BIT(7)
- uchar tab4[]={0x82,0x85,0x88,0x8f,0xc0,0xc3,0xc6};//用来存放年月日星期时分秒的地址
- uchar write_add[7]={0x8c,0x88,0x86,0x8a,0x84,0x82,0x80}; //写寄存器地址
- uchar read_add[7]={0x8d,0x89,0x87,0x8b,0x85,0x83,0x81};//存放从1302中读数寄存器地址
- uchar tab1[]="20 - - week "; //屏上要固定显示的字符
- uchar tab2[]=" : : ";//屏上要固定显示的字符
- uchar tab3[];
- uchar tab[]={12,2,4,5,5,5,5};//年 月 日 周 时 分 秒的初值
- char tung;//根据tung确定按键位置不能定成uchar型
- void write_1602com(uchar com);//1602写地址函数
- void init_1602(void);//1602初始化
- void write_1302bvte(uchar);//1302写数据函数
- uchar read_1302(uchar);//1302读数据函数
- void write_1302(uchar,uchar);//1302指定地址定数据函数
- void write_ds1302_1();//时间显示函数
- void read_1302_1();//一次读出年月日 时分秒
- void flx(uchar j); //时间转显示函数
- void key(void); //按键函数
- void set_rtc(void); //一次设置初始时间年月日函数
- void init_18b20(void);//18b20初始化函数
- void write_18b20(uchar value);//ds18b20写数据函数
- uchar read_18b20(void);//读温度函数
- uchar readtemperature(void);//一次性读出温度
- void xianshi(void);//温度显示函数
- void init_18b20(void)
- {
- DQ_OUT;
- DQ_L;
- delay_100us(5);
- DQ_H; //初始化18B20
- delay_100us(5);
- }
- void write_18b20(uchar value)
- {
- uchar i;
- for(i=0;i<8;i++)
- {
- DQ_OUT;
- DQ_L; //写一位函数
- delay_10us();
- if(value&0x01)//
- {
- DQ_H;
- }
- }
- delay_100us(1);
- DQ_H;
- value=value>>1;
- }
- uchar read_18b20(void)
- {
- uchar i,value;
- for(i=0;i<8;i++)
- {
- value=value>>1;
- DQ_OUT;
- DQ_L;
- delay_10us();//读一位函数
- DQ_H;
- DQ_IN;
- if(DQ_R)
- {
- value|=0x80;
- }
- delay_50us();
- }
- return value;
- }
- uchar readtemperature(void)//一次性读出温度
- {
- uint i,j;//一定要用int型j大于256
- init_18b20;
- write_18b20(0xcc);//跳过ROM
- write_18b20(0x44);//启动温度测量
- delay_100us(20);
- init_18b20();
- write_18b20(0xcc);
- write_18b20(0xbe);//读取温度
- i=read_18b20();//低位
- j=read_18b20();//高位
- i=i>>4; //因不用小数部分所以移4位
- j=j*16+i; //将存放低8位的和高8位合到一块
- return j ;
- }
- void xianshi(void)
- {
- uint temp,k,f;
- temp=readtemperature();//将温度保存到temp中
- f=temp/10; //分离出十位跟个位
- k=temp%10;
- write_1602com(0x80+0x40+10);//温度显示的位置
- write_1602dat(f+0x30);
- write_1602dat(k+0x30); //将温度写到1602中
- }
- void set_rtc(void) //一次设置初始时间年月日函数
- {
- uchar i,j;
- for(i=0;i<7;i++)
- {
- j=tab[i]/10; //将设置的年月日时分解出十位放入J中
- tab3[i]=tab[i]%10; //将设置的年月日时间分解出个位再放入time_data[]数组中
- tab3[i]=tab3[i]+j*16;//将十进制的个位和十位转换为BCD码
- }
- write_1302(0x8e,0x00);//去除写保护
- for(i=0;i<7;i++)
- {
- write_1302(write_add[i],tab3[i]); //写要设置初始时间日期的操作寄存器的地址
- }
- write_1302(0x8e,0x80);//加写保护
- }
- void key(void)
- {
- uchar s1,s2,s3;
- if(~S1_R) //如果S1按下
- {
- delay_1ms(3); //延时去抖
- if(~S1_R)
- {
- while(~S1_R); //
- tung=0; //tung用来确定光标的位置
- while(tung<=6) //循环7次 时分秒年月日星期
- {
- if(~S2_R)
- {
- delay_1ms(3);
- if(~S2_R)//去抖动延时
- {
- while(~S2_R); // 检测按键是否放开
- tab[tung]++; //如果+键按下加1
- }
- }
- if(~S3_R)
- {
- delay_1ms(3); // 检测按键是否放开
- if(~S3_R)
- {
- while(~S3_R); //按键是否松手
- tab[tung]--; //如果—键按下减1
- }
- }
- switch(tung)
- { //通过tung的值确定光标的位置
- case 0:
- if(tab[tung]<3) //如果tung=0;则表示正在调年让年在2004到2098这个范围内
- tab[tung]=98;
- if(tab[tung]>98)
- tab[tung]=4;
- break;
-
- case 1:
- if(tab[tung]<1)
- tab[tung]=12;
- if(tab[tung]>12) //如果tung=1;则表示正在调月让月在1到12这个范围内
- tab[tung]=1;
- break;
- case 2:
- if(tab[tung]==0)
- tab[tung]=31;
- if(tab[tung]==32) //如果tung=2;则表示正在调日让在1到31这个范围内
- tab[tung]=1;
- break;
- case 3:
- if(tab[tung]<1)
- tab[tung]=7;
- if(tab[tung]>7) //如果tung =3则表示下在调星期让在1到7这个范围内
- tab[tung]=1;
- break;
- case 4:
- if(tab[tung]<0)
- tab[tung]=23;
- if(tab[tung]>23) //如果tung=4则表示在调时让时在1到23这个范围内
- tab[tung]=0;
- break;
- case 5:
- if(tab[tung]<0)
- tab[tung]=59;
- if(tab[tung]>59) //如果tung=5;则表示正在调分让分在1到59这个范围内
- tab[tung]=0;
- break;
- default:
- if(tab[tung]<0)
- tab[tung]=59;
- if(tab[tung]>59) //如果tung=6;则表示正在调秒让秒在1到59这个范围内
- tab[tung]=0;
- break;
- }
- write_1602com(tab4[tung]); //显示正在调的位置
- flx(tab[tung]); //将数据分离成1602的显示码并送到1602显示屏
-
- if(~S1_R)
- {
- while(~S1_R); //判断是否调好了时间如果好了则跳出key
- tung++; //每按一下S1调式模式键则加一次如果到了7次跳出KEY函数
- }
- }
- }
- write_1602com(0x0c);//调完了时间关闭光标
- set_rtc(); //把调好的时间数据送到DS1302芯片中
- }
- }
- void flx(uchar j)
- {
- uchar k,a;
- write_1602com(0x0f); //开光标显示指令
- k=j/10; //分离十位跟个位
- a=j%10;
- if(tung!=3) //年月日时分秒分离
- {
- write_1602dat(k+0x30); //将分离的十位跟个位换成LC1602能显示的码
- write_1602com(0x0c);//关光标指令
- write_1602dat(a+0x30);
- }
- else
- write_1602dat(a+0x30); //星期分离
-
- }
- void read_1302_1(void)//一次读出年月日 时分秒函数
- {
- uchar i,shi,ge,tun;
- for(i=0;i<7;i++)
- {
- tun=read_1302(read_add[i]); //一次将年月日从DS1302中读出送到数组中
- ge=tun%16;
- shi=tun/16; //将BCD码分成十位和个位
- tab[i]=shi*10+ge; //再换成十进制存到数组tab中
- }
- }
- void write_ds1302_1()
- {
- read_1302_1();//一次读出年月日 时分秒函数
- write_1602com(0x80+0x40+6);//秒的位置
- write_1602dat(tab[6]/10+0x30);
- write_1602dat(tab[6]%10+0x30);
- write_1602com(0x80+0x40+3);//
- write_1602dat(tab[5]/10+0x30); //分的位置
- write_1602dat(tab[5]%10+0x30);
- //将十进制的数据换成1602的显示码送到显示屏
- write_1602com(0x80+0x40+0);// //时的地址
- write_1602dat(tab[4]/10+0x30); //显示屏的第二排要加0x80+40
- write_1602dat(tab[4]%10+0x30);
- write_1602com(0x80+2);//
- write_1602dat(tab[0]/10+0x30);//年的地址
- write_1602dat(tab[0]%10+0x30);
- write_1602com(0x80+5);// 显示屏 的第一排地址要加0x80;
- write_1602dat(tab[1]/10+0x30);
- write_1602dat(tab[1]%10+0x30); //月的地址
- write_1602com(0x80+8);// 日的地址
- write_1602dat(tab[2]/10+0x30);
- write_1602dat(tab[2]%10+0x30);
- write_1602com(0x80+15); //星期的地址
- write_1602dat(tab[3]%10+0x30);
- }
- void init_1302(void)
- {
- SCK_OUT;
- RST_OUT;//1302IO口初始化
- }
- void write_1302(uchar dat ,uchar add)
- {
- RST_L;
- SCK_L;
- RST_H;
- write_1302bvte(dat);//指定地址写数据函数
- write_1302bvte(add);
- RST_L;
- SCK_H;
- }
- void write_1302bvte(uchar add)
- {
- uchar i;
- RST_L;
- SCK_L;
- IO_OUT;
- delay_1ms(2);
- RST_H;
- for(i=0;i<8;i++)
- {
- if(add&0X01)
- {
- IO_H; //1302写函数
- }
- else
- {
- IO_L;
- }
- SCK_H;
- SCK_L;
- add=add>>1;
- }
- }
- uchar read_1302(uchar dat)
- {
- uchar i,byte;
- write_1302bvte(dat);
- IO_IN;
- for(i=0;i<8;i++)
- {
- byte=byte>>1;
- if(IO_R)
- { //1302读数据函数
- byte=byte|0x80;
- }
- SCK_H;
- SCK_L;
- }
- RST_L;
- return byte;
- }
- void write_1602com(uchar com)
- {
- RS_L;
- RW_L; //写指令函数
- PORTC=com;
- EN_H;
- delay_1ms(4);
- EN_L;
- }
- void write_1602dat(uchar dat)
- {
- RS_H;
- RW_L;
- PORTC=dat;
- EN_H; //写数据函数
- delay_1ms(4);
- EN_L;
- }
- void init_1602(void)
- {
- uchar tou;
- DDRC=0XFF;
- PORTC=0;
- RS_OUT;
- RW_OUT;
- EN_OUT;
- EN_L;
- write_1602com(0x38);//
- write_1602com(0x0C);// //1602初始化函数
- write_1602com(0x06);
- write_1602com(0x01);
- write_1602com(0x80);
- write_1602com(0x80); //第一行显示的位置地址
- for(tou=0;tou<16;tou++)
- {
- write_1602dat(tab1[tou]); //显示固定字符
- }
- write_1602com(0x80+0x40); //第二行显示的位置地址
- for(tou=0;tou<8;tou++)
- {
- write_1602dat(tab2[tou]);
- }
- write_1602com(0x80+0x40+12);
- write_1602dat(0xdf);//显示温度的小圆圈符号,0xdf是液晶屏字符库的该符号地址码
- write_1602dat(0x43); //显示"C"符号,0x43是液晶屏字符库里大写C的地址码 */
- }
- void main(void)
- {
- DDRA&=~0X49;//按键初始化
- PORTA|=0X49;
- init_1602();
- init_1302();
- while(1)
- {
- xianshi();//温度显示函数
- write_ds1302_1();//时间显示函数
- key();//按键函数
- }
- }
- =0X49;
- init_1602();
- init_1302();
- while(1)
- {
- xianshi();//温度显示函数
- write_ds1302_1();//时间显示函数
- key();//按键函数
- }
- }
复制代码
|