简易12684液晶和Atmega32的电子万年历
2013-12-12 来源:eefocus
闲来无事,又逢刮风下雨,把以前买来驱动一下就扔一边的12864液晶做个小万年历。哈哈……房间真差个钟~
所用的原件不多:
1.Atmega32单片机一个(其实At89s51足够,手头Atmega32最多,所以高成本一下吧)
2.晶振,电容22pF
3.7805,电容470uF,104若干,DC2.1电源插头。
4.DS1302,32768Hz晶振,2012电池(座)(其他的电源都行,用作备用电源)
5.12864液晶,带背光。
6.万用板一块。
先上图吧:
液晶背面就是主板了,这样结构容易站立。也挺美观。
再来一张,关灯的效果,看上去很幽暗恐怖……:
以下是程序(包括了DS1302驱动和12864液晶驱动)
//端口配置是这样的:液晶的DB0-DB7数据口连接在单片机的PA口。
//液晶直接定义成并口方式,所以也就的PSB直接接高电平,低电平是串行方式。
//头文件包含
#include
#include
#include
//端口位定义
#define RS PC2 //数据/命令控制端 0命令,1数据
#define RW PC1 //读/写选择控制端 0写,1读
#define E PC0 //使能端 下降沿读,高电平写
//#define PSB PC7 //数据传输方式选择端,H,8位或4位并口方式;L,串口方式
//常量声明
#define BAUD 115200
#define TURE 1
#define FALSE 0
//时钟/日历寄存器
#define RD 0x01 //读
#define WR 0x00 //写
#define SECOND 0x80 //秒
#define MINUTE 0x82 //分
#define HOUR 0x84 //时
#define DAY 0x86 //日
#define MONTH 0x88 //月
#define WEEK 0x8A //星期 DATE
#define YEAR 0x8C //年
#define WR_PROTECT 0x8E //控制(写保护)
#define CHARGE 0x90 //涓流充电
#define BURST 0xBE //时钟多字节
//配置位
#define CLK_HALT 0x80 //停止时钟控制位 SECOND bit7
#define CLK_START 0x00 //启动时钟
#define M12_24 0x80 //12/24小时值选择位 HOUR bit7
#define PROTECT 0x80 //写保护控制位 CONTROL bit7
#define UPROTECT 0x00 //写保护控制位 CONTROL bit7
//涓流充电控制常量
#define TC_D1R2 0xA5 //充电时选择一个二极管和2K电阻
#define TC_D2R8 0xAB //充电时选择二个二极管和8K电阻
#define TC_DISABLED 0x00 //禁止充电功能
//RAM 命令
#define RAMBASE 0xC0 //RAM起始位为0XCO,RAM范围0-31
//全局变量声明
unsigned char Get_Time[7] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00};
//全局变量声明
unsigned char logo[]="==小电子万年历==";
//函数声明
void Delayus(unsigned int lus); //us延时函数
void Delayms(unsigned int lms); //ms延时函数
unsigned char DS1302_ReadByte(void); //从DS1302读一个字节数据
void DS1302_WriteByte(unsigned char dat); //向DS1302写一个字节数据
unsigned char DS1302_ReadData(unsigned addr); //从DS1302的指定地址读一个字节数据
void DS1302_WriteData(unsigned char addr,unsigned data);
//向DS1302的指定地址写一个字节数据
void DS1302_SetTime(unsigned char *time); //对DS1302设置时间
void DS1302_GetTime(void); //从DS1302读取时间
unsigned char DS1302_Check(void); //DS1302是否工作检测
void DS1302_Init(void); //DS1302初始化
void DS1302_DisCharge(void); //关闭涓流充电
unsigned char DS1302_Alarm(unsigned char hour,unsigned char min); //闹铃
void System_Beep(void);
void Port_Init(void); //端口初始化
void LCD_Init(void); //LCD初始化
void Write_Com(unsigned char LCD_Com); //LCD写指令
void Write_Data(unsigned char LCD_Data); //LCD写数据
void Check_Busy(void); //读写检测函数,每次对液晶操作前都要进行读写检测
void Put_CHS_String(unsigned char x,unsigned char y,char *STR);
int main(void)
{
unsigned char Disp_Number;
char str_time[64];
unsigned char Set_Time[7] = {0x30,0x57,0x20,0x12,0x04,0x01,0x10};
//设置秒,分,时,日,月,星期,年
char chs_week[7][6]={"星期一","星期二","星期三","星期四","星期五","星期六","星期日"};
_delay_ms(200);
Port_Init(); //端口初始化
LCD_Init(); //LCD初始化
DS1302_DisCharge();
DS1302_GetTime();
if(Get_Time[2] == 0x00)
DS1302_SetTime(Set_Time);
Put_CHS_String(0,0,logo);
DS1302_GetTime();
Put_CHS_String(0,1,"现在时间:");
while(1)
{
sprintf(str_time,"20%01x%01x年%01x%01x月%01x%01x日",Get_Time[6] / 16,Get_Time[6] % 16
,Get_Time[4] / 16,Get_Time[4] % 16
,Get_Time[3] / 16,Get_Time[3] % 16 );
Put_CHS_String(0,2,str_time);
sprintf(str_time,"星期%01x %01x%01x:%01x%01x:%01x%01x",Get_Time[5] % 16
,Get_Time[2] / 16,Get_Time[2] % 16
,Get_Time[1] / 16,Get_Time[1] % 16
,Get_Time[0] / 16,Get_Time[0] % 16 );
Put_CHS_String(0,3,str_time);
DS1302_GetTime();
_delay_ms(500);
}
}
void Port_Init()
{
//LCD数据端口设置
PORTA = 0X00; //
DDRA = 0XFF; //配置端口PA全部为输出口,LCD数据端口
PORTD = 0X00;
DDRD |= 1<
//LCD控制端口设置
PORTC = 0X00; //
DDRC |= (1 << RS) | (1 << RW) | (1 << E);
PORTB = 0XFF;
DDRB |= (1 << PB1) | (1 << PB0); //DS1302的IO和SCLK引脚设为输出
DDRB |= (1 << PB2); //DS1302的RST引脚设为输出
}
void LCD_Init()
{
Write_Com(0X01); //清屏
_delay_us(5);
Write_Com(0X38); //显示模式设置 16x2显示,5x7点阵,8位数据接口
_delay_us(5);
//Write_Com(0X0f); //显示开关控制,开显示,光标显示,光标闪烁
Write_Com(0X0c); //显示开关控制,开显示,光标不显示,光标不闪烁
_delay_us(5);
Write_Com(0X06); //光标设置,读或写一个字符后,地址指针加一,光标加一,整屏不移动
_delay_us(5);
}
void Write_Com(unsigned char LCD_Com)
{
Check_Busy();
PORTC &= ~(1 << RS); //RS=0,写命令
PORTC &= ~(1 << RW); //RW=0,写指令
PORTC |= (1 << E); //E=1,写操作
_delay_us(5);
PORTA = LCD_Com; //指令送数据端口
PORTC &= ~(1 << E); //E=0,停止写操作
_delay_us(5);
}
void Write_Data(unsigned char LCD_Data)
{
Check_Busy();
PORTC |= (1 << RS); //RS=1,写数据
PORTC &= ~(1 << RW); //RW=0,写指令
PORTC |= (1 << E); //E=1,写操作
_delay_us(5);
PORTA = LCD_Data; // 数据送数据端口
PORTC &= ~(1 << E); //E=0,停止写操作
_delay_us(5);
}
void Check_Busy()
{
DDRA = 0X00; //PA口置为输入口,准备读取数据
PORTC &= ~(1 << RS); //RS=0,读命令
PORTC |= (1 << RW); //RW=1,读指令
PORTC |= (1 << E); //E=1,使能
while(0X80 & PINA); //监测忙信号,直到忙信号为0,才能进行读写操作
PORTC &= ~(1 << E); //E=0
DDRA = 0XFF; //PA口置为输出口,准备向端口发送数据
}
void Put_CHS_String(unsigned char x,unsigned char y,char *STR)
{
unsigned char Disp_Number;
switch(y)
{
case 0:
Write_Com(0X80+x);
break;
case 1:
Write_Com(0X90+x);
break;
case 2:
Write_Com(0X88+x);
break;
case 3:
Write_Com(0X98+x);
break;
default:
break;
}
for(Disp_Number = 0;Disp_Number < strlen(STR);Disp_Number++)
{
Write_Data(STR[Disp_Number]);
_delay_us(5);
}
}
/**********************************************/
//从DS1302读一个字节数据
unsigned char DS1302_ReadByte(void)
{
unsigned char i,dat = 0; //dat存放读出的数据,初始化为0
PORTB &= ~(1 << PB1); //DS1302的I/O口上拉不使能,
DDRB &= ~(1 << PB1); //DS1302的I/O口设置为输入口,准备读数据
for(i = 0;i < 8;i++) //读8位,低位在前,右移
{
dat >>= 1; //读出的数据右移一位
PORTB |= (1 << PB0); //DS1302的SCLK端口拉高
Delayus(10); //
PORTB &= ~(1 << PB0); //DS1302的SCLK端口拉低,产生下降沿,
Delayus(10);
if(PINB & (1 << PB1)) //读数据端口状态
{
dat |= 0x80; //如果数据端口位高,相应数据位置1
}
}
DDRB |= (1 << PB1); //最后将数据端口设置为输出
return dat; //返回读出的数据
}
//向DS1302写一个字节数据
void DS1302_WriteByte(unsigned char dat)
{
unsigned char i;
for(i = 0;i < 8;i++) //写8位,低位在前
{
PORTB &= ~(1 << PB0); //DS1302的SCLK置低
if(dat & 0x01) //写数据位
{
PORTB |= (1 << PB1); //如果该位为1,则I/O口置高
}
else
{
PORTB &= ~(1 << PB1); //如果该位为0,则I/O口置低
}
Delayus(10); //
PORTB |= (1 << PB0); //DS1302的SCLK置高,产生上升沿
dat >>= 1; //数据右移1位
}
}
//从DS1302的指定地址读一个字节数据
unsigned char DS1302_ReadData(unsigned addr)
{
unsigned char data;
PORTB &= ~(1 << PB2); //拉低片选端
PORTB &= ~(1 << PB0);//拉低时钟端
Delayus(10);
PORTB |= (1 << PB2);//拉高片选端
Delayus(10);
DS1302_WriteByte(addr);//写入操作命令(地址)
Delayus(10);
data = DS1302_ReadByte();//读出数据
Delayus(10);
PORTB &= ~(1 << PB0); //拉低时钟端
PORTB &= ~(1 << PB2); //拉低片选端
return data;
}
//向DS1302的指定地址写一个字节数据
void DS1302_WriteData(unsigned char addr,unsigned data)
{
PORTB &= ~(1 << PB2); //拉低片选端
PORTB &= ~(1 << PB0);//拉低时钟端
Delayus(10);
PORTB |= (1 << PB2);//拉高片选端
Delayus(10);
DS1302_WriteByte(addr);//写入操作命令(地址)
Delayus(10);
PORTB &= ~(1 << PB0);//拉低时钟端
Delayus(10);
DS1302_WriteByte(data);//写入数据
PORTB &= ~(1 << PB0); //拉低时钟端
Delayus(10);
PORTB &= ~(1 << PB2); //拉低片选端
}
//对DS1302设置时间
void DS1302_SetTime(unsigned char *time)
{
unsigned char i;
unsigned char addr = 0x80;//写入地址从秒寄存器开始
DS1302_WriteData(WR_PROTECT | WR,UPROTECT);//控制命令,WP位为0,允许写操作
Delayms(5);
for(i = 0;i < 7;i++)
{
DS1302_WriteData(addr | WR,time[i]);// 秒 分 时 日 月 星期 年
addr += 2;
Delayms(1);
}
DS1302_WriteData(WR_PROTECT | WR,PROTECT);//控制命令,WP位为1,不允许写操作
}
//从DS1302读取时间
void DS1302_GetTime(void)
{
unsigned char i;
PORTB &= ~(1 << PB2);
Delayus(10);
PORTB |= (1 << PB2);
Delayus(10);
DS1302_WriteByte(0xbf);
for(i = 0;i < 8;i++)
{
Get_Time[i] = DS1302_ReadByte();
}
PORTB &= ~(1 << PB2);
PORTB &= ~(1 << PB0);
}
//DS1302是否工作检测
unsigned char DS1302_Check(void)
{
DS1302_WriteData(WR_PROTECT | WR,UPROTECT);
DS1302_WriteData(RAMBASE | WR,0x31);
if(DS1302_ReadData(RAMBASE | WR) == 0x31)
{
return TURE;
}
else
{
return FALSE;
}
}
void DS1302_DisCharge(void)
{
DS1302_WriteData(CHARGE|WR,TC_DISABLED);
Delayus(10);
}
unsigned char DS1302_Alarm(unsigned char hour,unsigned char min)
{
}
void System_Beep(void)
{
}
//DS1302初始化
void DS1302_Init(void)
{
DS1302_WriteData(WR_PROTECT | WR,UPROTECT); //写入写允许命令
DS1302_WriteData(SECOND | WR,CLK_START); //启动振荡器,DS1302开始工作
DS1302_WriteData( WR_PROTECT | WR,PROTECT); //控制命令,WP位为1,不允许写操作
}
//us级别的延时函数
void Delayus(unsigned int lus)
{
while(lus--)
{
_delay_loop_2(3); //_delay_loop_2(1)是延时4个时钟周期,参数为3则延时12
//个时钟周期,本实验用12M晶体,则12个时钟周期为12/12=1us
}
}
//ms级别的延时函数
void Delayms(unsigned int lms)
{
while(lms--)
{
Delayus(800); //延时1ms
}
}
下一篇:关于ATMEGA32的TWI使用