疯狂单片机

一个普普通通的单片机、嵌入式学习个人博客站

Atmega88V、1602、DS18B20简易时钟

Atmega88V、1602、DS18B20简易时钟

元件清单:

1.Atmega88V单片机        一个

2.DS18B20时钟芯片        一个

3.1602液晶(带背光)        一个

4.万用板(两联的)        一块

5.晶振        32.768K+4MHz

6.开关插座电阻电容若干

先上图:

朴素为本!

 

整体的东西就这么多,采用的是USB供电,方便!

程序:

//头文件包含
#include <avr/io.h> //io端口寄存器配置文件,必须包含
#include <util/delay.h> //GCC中的延时函数头文件
#include <string.h>
#include <avr/interrupt.h> //中断函数头

//端口位定义
#define RS PC0 //数据/命令控制端 0命令,1数据
#define RW PC1 //读/写选择控制端 0写,1读
#define E PC2 //使能端 下降沿读,高电平写
#define BKL PC3 //背光

//函数声明
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 LCD_Print(char *str,unsigned char line);

//常量声明
#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};

volatile unsigned char Counter=0;

//函数声明
void Delayus(unsigned int lus); //us延时函数
void Delayms(unsigned int lms); //ms延时函数
void Port_Init(void); //端口初始化配置

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); //关闭充电
void DS1302_ShowTime(void);

//从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 << PB2); //DS1302的SCLK端口拉高
Delayus(10); //
PORTB &= ~(1 << PB2); //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 << PB2); //DS1302的SCLK置低
if(dat & 0x01) //写数据位
{
PORTB |= (1 << PB1); //如果该位为1,则I/O口置高
}
else
{
PORTB &= ~(1 << PB1); //如果该位为0,则I/O口置低
}
Delayus(10); //
PORTB |= (1 << PB2); //DS1302的SCLK置高,产生上升沿
dat >>= 1; //数据右移1位
}
}

//从DS1302的指定地址读一个字节数据
unsigned char DS1302_ReadData(unsigned addr)
{
unsigned char data;

PORTB &= ~(1 << PB0); //拉低片选端
PORTB &= ~(1 << PB2);//拉低时钟端
Delayus(10);
PORTB |= (1 << PB0);//拉高片选端
Delayus(10);
DS1302_WriteByte(addr);//写入操作命令(地址)
Delayus(10);
data = DS1302_ReadByte();//读出数据
Delayus(10);
PORTB &= ~(1 << PB2); //拉低时钟端
PORTB &= ~(1 << PB0); //拉低片选端

return data;
}

//向DS1302的指定地址写一个字节数据
void DS1302_WriteData(unsigned char addr,unsigned data)
{
PORTB &= ~(1 << PB0); //拉低片选端
PORTB &= ~(1 << PB2);//拉低时钟端
Delayus(10);
PORTB |= (1 << PB0);//拉高片选端
Delayus(10);
DS1302_WriteByte(addr);//写入操作命令(地址)
Delayus(10);
PORTB &= ~(1 << PB2);//拉低时钟端
Delayus(10);
DS1302_WriteByte(data);//写入数据
PORTB &= ~(1 << PB2); //拉低时钟端

Delayus(10);
PORTB &= ~(1 << PB0); //拉低片选端
}

//对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 << PB0);
Delayus(10);
PORTB |= (1 << PB0);
Delayus(10);
DS1302_WriteByte(0xbf);
for(i = 0;i < 8;i++)
{
Get_Time[i] = DS1302_ReadByte();
}
PORTB &= ~(1 << PB0);
PORTB &= ~(1 << PB2);
}

//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;
}
}
/*
//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(1); //_delay_loop_2(1)是延时4个时钟周期,参数为3则延时12
}
}

//ms级别的延时函数
void Delayms(unsigned int lms)
{
while(lms--)
{
Delayus(1000); //延时1ms
}
}

//T/C0定时中断服务程序
ISR(TIMER0_OVF_vect )
{
cli();
TCNT0 = 0; //重装计数初值
if(++Counter >= 10) //定时时间到1S吗?定时中断溢出4次为1S
{
DS1302_ShowTime();
Counter = 0;
}
sei();
}

int main(void)
{
unsigned char Set_Time[7] = {0x00,0x38,0x15,0x11,0x02,0x04,0x10};
//秒分 时 日 月 星期年
Port_Init(); //端口初始化
LCD_Init(); //LCD初始化

cli(); //关中断

LCD_Print("LCD Init OK!",0);

PORTC |= 1<<BKL;

//DS1302_Init();
DS1302_DisCharge();
DS1302_GetTime();
if(Get_Time[2] == 0x00)
DS1302_SetTime(Set_Time);

//T/C0定时中断设置
TIMSK0 |= (1 << 0); //T/C0溢出中断允许
TCCR0A = 0;
TCCR0B |= (1 <<CS02) | (1 << CS00);
TCNT0 = 0; //定时初值设置,定时时间 = (256-12)/976.5625=249.856ms

DS1302_ShowTime();

sei();

while(1);
}

void Port_Init()
{
//LCD数据端口设置
PORTD = 0X00; //
DDRD = 0XFF; //配置端口PB全部为输出口,LCD数据端口

//LCD控制端口设置
PORTC &= ~0X0F; //
DDRC |= (1 << RS) | (1 << RW) | (1 << E) |(1<<BKL);

DDRB |= (1 << PB1) | (1 << PB2); //DS1302的I/O和SCLK引脚设为输出
DDRB |= (1 << PB0); //DS1302的RST引脚设为输出
}

void LCD_Init()
{
Write_Com(0X01); //清屏
_delay_ms(5);
Write_Com(0X38); //显示模式设置 16x2显示,5x7点阵,8位数据接口
_delay_ms(5);
//Write_Com(0X0f); //显示开关控制,开显示,光标显示,光标闪烁
Write_Com(0X0c); //显示开关控制,开显示,光标不显示,光标不闪烁
_delay_ms(5);
Write_Com(0X06); //光标设置,读或写一个字符后,地址指针加一,光标加一,整屏不移动
_delay_ms(5);
}

void LCD_Print(char *str,unsigned char line)
{
unsigned char Disp_Number=0;
if(line)
{
Write_Com(0X80+0X40);
}
else
{
Write_Com(0X80+0X00);
}
for(Disp_Number = 0;Disp_Number < strlen(str);Disp_Number++)
{
Write_Data(str[Disp_Number]);
_delay_ms(1);
}
}

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_ms(5);
PORTD = LCD_Com; //指令送数据端口
PORTC &= ~(1 << E); //E=0,停止写操作
_delay_ms(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_ms(5);
PORTD = LCD_Data; // 数据送数据端口
PORTC &= ~(1 << E); //E=0,停止写操作
_delay_ms(5);

}

void Check_Busy()
{

DDRD = 0X00; //PB口置为输入口,准备读取数据
PORTC &= ~(1 << RS); //RS=0,读命令
PORTC |= (1 << RW); //RW=1,读指令
PORTC |= (1 << E); //E=1,使能

while(0X80 & PIND); //监测忙信号,直到忙信号为0,才能进行读写操作
PORTC &= ~(1 << E); //E=0
DDRD = 0XFF; //PB口置为输出口,准备向端口发送数据
}

void DS1302_DisCharge(void)
{
DS1302_WriteData(CHARGE|WR,TC_DISABLED);
Delayus(10);
}

void DS1302_ShowTime(void)
{
char i=0;
char Time_Str[16]="\0";
char *Week_Data[] = {"Monday","Tuesday","Wednesday","Thursday","Friday","Saturday:-)","Sunday:-)"};
DS1302_GetTime();
sprintf(Time_Str,"20%02x-%02x-%02x %02x:%02x",Get_Time[6],Get_Time[4],Get_Time[3],Get_Time[2],Get_Time[1]);
LCD_Print(Time_Str,0);
LCD_Print(Week_Data[Get_Time[5]-1],1);
}
 文件工程文件:atmega88v_1602.rar

这是eeworld上找来的程序,经过自己捣鼓捣鼓~

« 上一篇 | 下一篇 »
  • quote 1.木子木南
  • 哈哈,来看看……版面不错……比原来的有品位多了……高档啊……哈哈!板子分布依然你的风格啊,厉害!Music
  • 2010-2-25 9:19:50 回复该留言
  • quote 2.木子木南
  • 拜读了您的程序啊,发现小问题:
    void DS1302_ShowTime(void)
    {
    char i=0;
    char Time_Str[16]="\0";
    char *Week_Data[] = {"Monday","Tuesday","Wednesday","Thursday","Friday","Saturday:-)","Sunday:-)"};
    DS1302_GetTime();
    sprintf(Time_Str,"20%02x-%02x-%02x %02x:%02x",Get_Time[6],Get_Time[4],Get_Time[3],Get_Time[2],Get_Time[1]);
    LCD_Print(Time_Str,0);
    LCD_Print(Week_Data[Get_Time[5]-1],1);
    for(i=0;i<(16-strlen(Week_Data[Get_Time[5]-1]));i++)


    }
    最后一for循环不对的吧??没用而且编译不过的吧??
    Richard 于 2010-2-25 17:56:38 回复
    void DS1302_ShowTime(void)
    {
    char i=0;
    char Time_Str[16]="\0";
    char *Week_Data[] = {"Monday","Tuesday","Wednesday","Thursday","Friday","Saturday:-)","Sunday:-)"};
    DS1302_GetTime();
    sprintf(Time_Str,"20%02x-%02x-%02x %02x:%02x",Get_Time[6],Get_Time[4],Get_Time[3],Get_Time[2],Get_Time[1]);
    LCD_Print(Time_Str,0);
    LCD_Print(Week_Data[Get_Time[5]-1],1);
    }
    木子木南 于 2010-2-26 16:39:15 回复
    整的那么复杂,直接说最后一句去掉不就行啦??看来是原来的程序想打印其他东西,这次忘注释了,哈哈!Cool
  • 2010-2-25 14:53:35 回复该留言

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

日历

最新评论及回复

最近发表

Powered By Z-Blog 1.8 Arwen Build 90619 and Theme By BokeZhuti.cn

Copyright 2009-2010 www.mculover.com All Rights Reserved. 京ICP备09098861号
站长:莫旭冬 联系邮箱:moxudong@yeah.net