文章
博客 网店

 AVR单片机操作DS1302实时时钟程序(全部功能均测试过)


主程序文件rtc.c

/********************************
  AVR单片机I/O口操作DS1302接口程序 
  文件名:rtc.c
  编译:WinAVR-20070525

  硬件:ATMEGA169PV  时钟:7372800 Hz
      SCLK <----   PE5
      CE   <----   PE3
      IO   <--->   PE4  
  此程序在硬件上调试通过!    
  
  芯艺设计室 2004-2007  版权所有 
  转载请保留本注释在内的全部内容
  WEB: http://www.chipart.cn
  Email: changfutong@sina.com
*******************************/

#include
#include

//#define BURST_MODE

#define DS1302_DDR   DDRE
#define DS1302_PORT  PORTE
#define DS1302_PIN   PINE
#define DS1302_CK    PE5
#define DS1302_IO   PE4
#define DS1302_CE   PE3

#define delay_bus(x) _delay_loop_1(x*2)

static inline void ds1302_select(void)
{
  DS1302_PORT|=_BV(DS1302_CE);
  delay_bus(2);
}

static inline void ds1302_unselect(void)
{
  DS1302_PORT&=~_BV(DS1302_CE);
  delay_bus(2);
}

//实现读一字节时序
static uint8_t ds1302_read_byte(void)
{
  uint8_t i,ret=0;
  
  for(i=0;i<8;i++)
  {
    ret>>=1;
    
    if(DS1302_PIN & _BV(DS1302_IO))
      ret|=0x80;
    
    //给一脉冲
    DS1302_PORT|=_BV(DS1302_CK);  
    delay_bus(1);  
    DS1302_PORT&=~_BV(DS1302_CK);
    
    delay_bus(1);
  }
  return ret;
}

//实现写一字节时序
static void ds1302_write_byte(uint8_t dat)
{
  uint8_t i;
  
  //IO口设置为输出
  DS1302_DDR|=_BV(DS1302_IO); 
  DS1302_PORT&=~_BV(DS1302_IO);

  for(i=0;i<8;i++)
  {
    //设置好数据口
    if(dat& 0x01)
      DS1302_PORT|=_BV(DS1302_IO);
    else
      DS1302_PORT&=~_BV(DS1302_IO);
      
    delay_bus(1);
    
    //给一脉冲
    DS1302_PORT|=_BV(DS1302_CK);
    delay_bus(1);  
    DS1302_PORT&=~_BV(DS1302_CK);
    
    dat>>=1;  
  }  
  //IO口恢复到输入状态
  DS1302_PORT&=~_BV(DS1302_IO);  
  DS1302_DDR&=~_BV(DS1302_IO);
  
  delay_bus(1);
}

//读寄存器 addr: 0~8
uint8_t rtc_read_reg(uint8_t addr)
{
  uint8_t ret;
  
  addr<<=1;
  addr|=0x81;
  
  ds1302_select();
  ds1302_write_byte(addr);
  ret=ds1302_read_byte();
  ds1302_unselect();
  
  return ret;
}

//写寄存器
void rtc_write_reg(uint8_t addr,uint8_t dat)
{
  addr<<=1;
  addr|=0x80;
  
  ds1302_select();
  ds1302_write_byte(addr);
  ds1302_write_byte(dat);
  ds1302_unselect();
}

//读RAM 
//在多字节(BURST)模式下读取 x 长度的数据到buf指向的存储区
//在单字节模式下从地址为x(0~31)的RAM中读一字节存储到buf处
void rtc_read_ram(uint8_t x,uint8_t *buf)
{
#ifdef BURST_MODE
  uint8_t i;
  
  ds1302_select();
  ds1302_write_byte(0xff); //read burts ram
  for(i=0;i    buf[i]=ds1302_read_byte();
  ds1302_unselect();
#else
  x<<=1;
  x|=0xc1;
  
  ds1302_select();
  ds1302_write_byte(x);
  *buf=ds1302_read_byte();
  ds1302_unselect();
#endif  
}

//写RAM
//在多字节(BURST)模式下写 x 长度的数据到DS1302内部RAM,从0地址开始写
//在单字节模式下在地址为x(0~31)的RAM片写入一字节数据
void rtc_write_ram(uint8_t x,uint8_t *buf)
{
#ifdef BURST_MODE
  uint8_t i;
#endif

  rtc_write_reg(7,0);//写使能

#ifdef BURST_MODE
  ds1302_select();
  ds1302_write_byte(0xfe); //write burts ram
  for(i=0;i    ds1302_write_byte(buf[i]);
  ds1302_unselect();
#else
  x<<=1;
  x|=0xc0;
  
  ds1302_select();
  ds1302_write_byte(x);
  ds1302_write_byte(*buf);
  ds1302_unselect();  
#endif
  
  rtc_write_reg(7,0x80);//写禁止
}

//读取时钟 将读取的时钟以小时,分钟,秒的顺序存入time指向的缓冲区
//读出的数据为二进制格式,而非BCD码
void rtc_get_time(uint8_t *time)
{
  uint8_t s,m=0,h;

  h=rtc_read_reg(2);  
  if(h & 0x80)//12小时制
  {
    if(h& _BV(4))
      m=10;
    else
      m=0;
    
    if(h&_BV(5))
      m+=12;
      
    h=m+(h&0x0f);
  }
  else    //24小时制
  {
    h&=0x3f;
    h= (h>>4)*10 + (h & 0x0f);
  }
  s=rtc_read_reg(0) & 0x7f;
  m=rtc_read_reg(1) & 0x7f;

  time[2]= (s>>4)*10 + (s & 0x0f);
  time[1]= (m>>4)*10 + (m & 0x0f);
  time[0]= h;
}

//设置时钟,time中的时钟格式为:时,分,秒二进制式
void rtc_set_time(uint8_t *time)
{
  uint8_t h,m,s;
  h=time[0]%24;
  m=time[1]%60;
  s=time[2]%60;
  
  //二进制转BCD
  h=((h/10)<<4) + (h%10);
  m=((m/10)<<4) + (m%10);
  s=((s/10)<<4) + (s%10);
  
  rtc_write_reg(7,0);//write enable
  rtc_write_reg(0,s);
  rtc_write_reg(1,m);
  rtc_write_reg(2,h);
  rtc_write_reg(7,0x80);//write disable
}

//读日历 将读取的日历以年,月,日的顺序存入date指向的缓冲区
//读出的数据为二进制格式,而非BCD码
void rtc_get_date(uint8_t *date)
{
  uint8_t y,m,d;
  
  y=rtc_read_reg(6);
  m=rtc_read_reg(4)& 0x1f;
  d=rtc_read_reg(3)& 0x3f;
  
  //BCD转二进制
  date[0]=(y>>4)*10 + (y&0x0f);
  date[1]=(m>>4)*10 + (m&0x0f);
  date[2]=(d>>4)*10 + (d&0x0f);  
}

//设置日历
void rtc_set_date(uint8_t *date)
{
  uint8_t y,m,d;
  
  //二进制转BCD码
  y=((date[0]/10)<<4) + (date[0]%10);
  m=((date[1]/10)<<4) + (date[1]%10);
  d=((date[2]/10)<<4) + (date[2]%10);
  
  //写入DS1302
  rtc_write_reg(7,0);
  rtc_write_reg(6,y);
  rtc_write_reg(4,m);
  rtc_write_reg(3,d);
  rtc_write_reg(7,0x80);
}

//读周
void rtc_get_day(uint8_t *day)
{
  *day=rtc_read_reg(5) & 0x07;
}

//设置周
void rtc_set_day(uint8_t *day)
{
  rtc_write_reg(7,0);
  rtc_write_reg(5,(*day)&0x07);
  rtc_write_reg(7,0x80);
}

//初始化接口和各寄存器
void rtc_init(void)
{
  //CE,CK口设置为输出,IO口设置为输入
  DS1302_DDR|=_BV(DS1302_CE)|_BV(DS1302_CK);
  DS1302_PORT&=~(_BV(DS1302_CE)|_BV(DS1302_CK)|_BV(DS1302_IO));
  DS1302_DDR&=~_BV(DS1302_IO);  

  if(rtc_read_reg(0) & 0x80)//如果处于暂停状态
  {
    rtc_write_reg(7,0);//写允许
    rtc_write_reg(0,0);//运行
    rtc_write_reg(7,0x80);//写保护
  }  

  //... ... 这里你可以检查涓流充电配置并设置寄存器
}


包含头文件 rtc.h


//rtc.h

#ifndef RTC_H
#define RTC_H

uint8_t rtc_read_reg(uint8_t addr);
void rtc_write_reg(uint8_t addr,uint8_t dat);

void rtc_read_ram(uint8_t x,uint8_t *buf);
void rtc_write_ram(uint8_t x,uint8_t *buf);

void rtc_get_time(uint8_t *time);
void rtc_set_time(uint8_t *time);

void rtc_get_date(uint8_t *date);
void rtc_set_date(uint8_t *date);

void rtc_get_day(uint8_t *day);
void rtc_set_day(uint8_t *day);

void rtc_init(void);

//注:此套接口函数中表示寄存器或RAM的地址为偏移地址而不是数据手册中的命令字
//如秒寄存器的地址为0 而不是0x80或0x81
#endif

芯艺工作室    蒙ICP备06005492号
Copyright© 2004-2023 ChipArt Studio All Rights Reserved