通過幾天的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();//按鍵函數
- }
- }
復制代碼
|