//端口配置是這樣的:液晶的DB0-DB7數據口連接在單片機的PA口。
//液晶直接定義成并口方式,所以也就的PSB直接接高電平,低電平是串行方式。
//頭文件包含
#include <avr/io.h> //io端口寄存器配置文件,必須包含
#include <util/delay.h> //GCC中的延時函數頭文件
#include <string.h>
//端口位定義
#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<<PD6;
PORTD |= 1<<PD6;
//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
}
}