/*
程序名(program name) : 时钟芯片控制程序;
概述 : 基于本程序的开发板是本人自主研发的YF-A1 AVR MEGA16单片机芯片开发板,信号
辅助硬件工具有一部8通道的24兆赫兹的逻辑分析仪和一部万用表,通俗的讲,此程序就
是开发板的操作系统,以MEGA16A为中央控制芯片,来控制DS1302时钟芯片,使其时间数据
显示在1602液晶屏上;
我用业余时间大概写了三天左右....得以完善,这次我写的比较顺利,错误只有两点,
一是将写好的程序下载入芯片后,时间不走,错误在于写错了时序,用逻辑分析仪捕捉信号
后,修正,KO! 二是时间可以走,但是跑秒区并非从0走到59,问题在于显示时,直接显示
BCD码,并未将BCD码转换为十进制数,修正后OK!
此程序还用到了定时器模块,定时每100毫秒读一次时间数据,并显示在液晶屏上;
心得: 此次又突破了以往的程序量,这是我写的目前最大的一个程序,由于AVR芯片IO口
控制比较复杂,所以程序语句比较多,但是此程序还有精简的空间,IO口的初始化很重
要!
finish time: 2014年3月7日13:16:14
作者: 肖邦;
QQ: 14-545-07665 ;
TEL: SORRY 保密;
地址: 新疆昌吉回族自治州昌吉市;
梦想: 将 托尼*斯塔克成为现实;
*/
#include <avr/io.h>
#include <avr/interrupt.h>//此头文件用于中断服务,本程序中可以省略;
typedef unsigned char uint8;
typedef unsigned int uint16;
void init_io(void) //====初始化所有io口;
{
DDRA=0XFF;
PORTA=0XFF;
DDRB=0XFF;
PORTB=0X00;
DDRC=0XFF;
PORTC=0X00;
DDRD=0XFF;
PORTD=0X00;
}
//================IO口的位操作==================
void rs(uint8 a) //指令/数据位/ ===========DS1302时钟位;
{
if(a) { PORTB|=1<<3; }
else { PORTB&=~(1<<3); }
}
void rw(uint8 a) //读/写位/ ================DS1302数据位;
{
if(a) { PORTB|=1<<4; }
else { PORTB&=~(1<<4); }
}
void e(uint8 a) //1602传输时能位;
{
if(a) { PORTB|=1<<5; }
else { PORTB&=~(1<<5); }
}
void ds(uint8 a) //=========================DS1302传输使能位;
{
if(a) { PORTC|=1<<4; }
else { PORTC&=~(1<<4); }
}
//==============================================
/*/======================================================
模块名: 1602LCD液晶屏函数
功能: 负责将视觉需要的数据显示在屏幕上;
*///=====================================================
void pa7(uint8 a) //繁忙检测位=================
{
if(a) { DDRA|=0X80; PORTA|=0X80 ;} //输出高电平状态;
else { DDRA&=~(0X80); PORTA|=0X80; }//输入内部有上拉电阻;
}
void busy(void) //繁忙检测函数
{
pa7(0); // PA7引脚输入状态内部有上拉;
do
{
e(0);
rs(0);
rw(1);
e(1); //注意!此处和读写数据指令不同,要置1传输使能位,再读取,读取完成后再清零此位;
}
while(PINA&0X80);//读取PA7引脚是否是0;如果是0,表示液晶屏处于空闲状态,可以进行操作;
e(0);
pa7(1); //PA7引脚输出状态,输出1;
}
void pa(uint8 a) //====向PA口装载数据;
{
PORTA=a;
}
void lcd_dat(uint8 dat)//=========写数据==============
{
busy(); //繁忙检测;
rs(1); //数据;
rw(0); //写入;
e(1); //传输开始;
pa(dat); //装载数据;
e(0) ; //传输结束;
}
void lcd_cmd(uint8 cmd) //=============写指令=============
{
busy(); //繁忙检测;
rs(0); //指令;
rw(0); //写入;
e(1); //传输开始;
pa(cmd); //装载数据;
e(0); //传输结束;
}
void init_lcd() //初始化液晶屏指令;
{
lcd_cmd(0x3c); //显示数据,8位数据, 两行,5X10显示;
lcd_cmd(0x0c); //整屏显示光标不闪,字符不闪.
lcd_cmd(0x06); //写入一个数据时地址加一,整屏不移动;
lcd_cmd(0x01); //清屏指令;
}
void play(uint8 add,uint8 dat)//=======最终显示函数===================
{
lcd_cmd(add);
lcd_dat(dat);
}
void delay1(uint8 j,uint8 i)//====可编程延时函数========
{
uint16 a=0,s=10000;
s=s*j;
while(i--)
{
for(a=0;a<s;a++);
}
}
void buzz(void) //蜂鸣器=============
{
PORTC|=0X80;
delay1(3,4);
PORTC&=~(0X80);
}
//==========================================================
//==================DS1302模块===========
void reset_1302()
{
ds(0);
rs(0);
rw(0);
}
void delay2(void) //用于DS1302小延时函数;
{
uint8 j=0;
for(j=0;j<200;j++);
}
void ds_write(uint8 dat) //DS1302写数据函数;
{
uint8 j=0;
for(j=0;j<8;j++)
{
rw(dat&0x01);
rs(0);
rs(1);
dat>>=1;
}
}
uint8 ds_read(void) // DS1302读取函数;
{
uint8 j=0,temp=0;
DDRB&=~(1<<4); //rw引脚输入状态;
PORTB|=1<<4; //内部有上拉电阻;
rs(0);
for(j=0;j<8;j++)
{
temp>>=1;
if((PINB&0X10)==0X10)//如果PB4口读到1;
{
temp|=0x80;//装载数据;
}
rs(1); //下降沿有效;
delay2(); //时钟脉宽延时;
rs(0);
}
DDRB|=1<<4; // rw引脚输出状态;
PORTB|=1<<4; //rw引脚输出1;
rs(0);
rw(0);
return (temp);
}
void w_1302(uint8 addr,uint8 dat) //写一个地址,写一个数据;
{
ds_write(addr);
ds_write(dat);
}
void ds_wp(uint8 j) //j==1:不能够写入数据,j==0:可以写入数据;
{
reset_1302(); //初始化时钟,数据,片选线;
ds(1);
w_1302(0x8e,j?0x80:0); //三目运算;
ds(0);
}
void set_time(uint8 * dat) //设置时间;
{
uint8 r=0 ,t=0, ad=0x80;
for(r=0;r<7;r++) //BCD码的转换.
{
t=dat[r]%10;
dat[r]=dat[r]/10;
dat[r]=(dat[r]*16)+t;
}
ds_wp(0);//可以写入数据;
for(r=0;r<7;r++)
{
reset_1302(); //初始化1302;
ds(1); //传输使能开;
ds_write(ad); //写入"写"操作地址;
ds_write(dat[r]);//写入时间数据;
ds(0);
ad+=2;//地址加2;
}
ds_wp(1);//禁止写入数据;
}
void read_time(uint8 * dat)
{
uint8 j=0,addr=0x81 ;
for(j=0;j<7;j++)
{
reset_1302();
ds(1); //传输使能开;
ds_write(addr); //发送读取地址;
dat[j]=ds_read(); //接收读到的数据;
ds(0); //传输使能关;
addr+=2; //读取地址加2;
}
}
void timer0(void) //定时器模块函数;
{
TCNT0=22; //每5毫秒溢出一次;
TCCR0=0X04; //256分频;
}
int main(void)
{
uint8 j=0;
uint8 sj[]={20,23,13,7,3,5,14};
init_io(); //初始化所有io口;
init_lcd(); //初始化1602液晶屏;
play(0x82,'2');
delay1(3,2);
play(0x83,'0');
delay1(3,2);
play(0x86,'-');
delay1(3,2);
play(0x89,'-');
delay1(3,2);
play(0x8d,'(');
delay1(3,2);
play(0x8f,')');
delay1(3,2);
play(0xc4,':');
delay1(3,2);
play(0xc7,':');
delay1(3,2);
buzz(); //蜂鸣器叫;
delay1(3,2);
set_time(sj); //设置时间;
timer0(); //运行定时器;
while(1)
{
if(TIFR&0X01) //定时器溢出了;
{
j++;
TCNT0=22;
TIFR|=0X01;//溢出位置一清零;
}
if(j==20)
{
j=0;
read_time(sj);
play(0xc9,sj[0]%16+'0'); //秒个位;
play(0xc8,sj[0]/16+'0'); //秒十位;
play(0xc6,sj[1]%16+'0'); //分个位;
play(0xc5,sj[1]/16+'0'); //分十位;
play(0xc3,sj[2]%16+'0'); //时个位;
play(0xc2,sj[2]/16+'0'); //时十位;
play(0x8b,sj[3]%16+'0'); //号个位;
play(0x8a,sj[3]/16+'0'); //号十位;
play(0x88,sj[4]%16+'0'); //月个位;
play(0x87,sj[4]/16+'0'); //月十位;
play(0x8e,sj[5]+'0'); //星期;
play(0x85,sj[6]%16+'0'); //秒个位;
play(0x84,sj[6]/16+'0'); //秒十位;
}
}
}