×
单片机 > 单片机程序设计 > 详情

简易12684液晶和Atmega32的电子万年历

发布时间:2020-06-06 发布时间:
|

闲来无事,又逢刮风下雨,把以前买来驱动一下就扔一边的12864液晶做个小万年历。哈哈……房间真差个钟~

所用的原件不多:

1.Atmega32单片机一个(其实At89s51足够,手头Atmega32最多,所以高成本一下吧)

2.晶振,电容22pF

3.7805,电容470uF,104若干,DC2.1电源插头。

4.DS1302,32768Hz晶振,2012电池(座)(其他的电源都行,用作备用电源)

5.12864液晶,带背光。

6.万用板一块。

先上图吧:

液晶背面就是主板了,这样结构容易站立。也挺美观。

再来一张,关灯的效果,看上去很幽暗恐怖……:

以下是程序(包括了DS1302驱动和12864液晶驱动)[page]

//端口配置是这样的:液晶的DB0-DB7数据口连接在单片机的PA口。

//液晶直接定义成并口方式,所以也就的PSB直接接高电平,低电平是串行方式。

//头文件包含
#include         //io端口寄存器配置文件,必须包含
#include        //GCC中的延时函数头文件
#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< PORTD |= 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);
 }
}[page]

/**********************************************/

//从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
    }
}





『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
STM32单片机的复用端口初始化的步骤及方法