?? rtc.c
字號:
#include "sys.h"
#include "rtc.h"
#include "delay.h"
#include "usart.h"
//////////////////////////////////////////////////////////////////////////////////
//本程序只供學習使用,未經作者許可,不得用于其它任何用途
//Mini STM32開發板
//RTC實時時鐘 驅動代碼
//正點原子@ALIENTEK
//技術論壇:www.openedv.com
//修改日期:2010/12/30
//版本:V1.1
//版權所有,盜版必究。
//Copyright(C) 正點原子 2009-2019
//All rights reserved
//********************************************************************************
//V1.1修改說明
//修改了RTC_Init函數分頻設置無效的bug
//修改了RTC_Get函數的一個bug
//////////////////////////////////////////////////////////////////////////////////
//Mini STM32開發板
//RTC實時時鐘 驅動代碼
//正點原子@ALIENTEK
//2010/6/6
tm timer;//時鐘結構體
//實時時鐘配置
//初始化RTC時鐘,同時檢測時鐘是否工作正常
//BKP->DR1用于保存是否第一次配置的設置
//返回0:正常
//其他:錯誤代碼
u8 RTC_Init(void)
{
//檢查是不是第一次配置時鐘
u8 temp=0;
if(BKP->DR1!=0X5050)//第一次配置
{
RCC->APB1ENR|=1<<28; //使能電源時鐘
RCC->APB1ENR|=1<<27; //使能備份時鐘
PWR->CR|=1<<8; //取消備份區寫保護
RCC->BDCR|=1<<16; //備份區域軟復位
RCC->BDCR&=~(1<<16); //備份區域軟復位結束
RCC->BDCR|=1<<0; //開啟外部低速振蕩器
while((!(RCC->BDCR&0X02))&&temp<250)//等待外部時鐘就緒
{
temp++;
delay_ms(10);
};
if(temp>=250)return 1;//初始化時鐘失敗,晶振有問題
RCC->BDCR|=1<<8; //LSI作為RTC時鐘
RCC->BDCR|=1<<15;//RTC時鐘使能
while(!(RTC->CRL&(1<<5)));//等待RTC寄存器操作完成
while(!(RTC->CRL&(1<<3)));//等待RTC寄存器同步
RTC->CRH|=0X01; //允許秒中斷
while(!(RTC->CRL&(1<<5)));//等待RTC寄存器操作完成
RTC->CRL|=1<<4; //允許配置
RTC->PRLH=0X0000;
RTC->PRLL=32767; //時鐘周期設置(有待觀察,看是否跑慢了?)理論值:32767
Auto_Time_Set();
//RTC_Set(2009,12,2,10,0,55); //設置時間
RTC->CRL&=~(1<<4); //配置更新
while(!(RTC->CRL&(1<<5))); //等待RTC寄存器操作完成
BKP->DR1=0X5050;
//BKP_Write(1,0X5050);;//在寄存器1標記已經開啟了
//printf("FIRST TIME\n");
}else//系統繼續計時
{
while(!(RTC->CRL&(1<<3)));//等待RTC寄存器同步
RTC->CRH|=0X01; //允許秒中斷
while(!(RTC->CRL&(1<<5)));//等待RTC寄存器操作完成
//printf("OK\n");
}
MY_NVIC_Init(0,0,RTC_IRQChannel,2);//RTC,G2,P2,S2.優先級最低
RTC_Get();//更新時間
return 0; //ok
}
//RTC中斷服務函數
//const u8* Week[2][7]=
//{
//{"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"},
//{"日","一","二","三","四","五","六"}
//};
//RTC時鐘中斷
//每秒觸發一次
void RTC_IRQHandler(void)
{
if(RTC->CRL&0x0001)//秒鐘中斷
{
RTC_Get();//更新時間
//printf("CRL:%d\n",RTC->CRL);
}
if(RTC->CRL&0x0002)//鬧鐘中斷
{
//printf("Alarm!\n");
RTC->CRL&=~(0x0002);//清鬧鐘中斷
//鬧鐘處理
}
RTC->CRL&=0X0FFA; //清除溢出,秒鐘中斷標志
while(!(RTC->CRL&(1<<5)));//等待RTC寄存器操作完成
}
//判斷是否是閏年函數
//月份 1 2 3 4 5 6 7 8 9 10 11 12
//閏年 31 29 31 30 31 30 31 31 30 31 30 31
//非閏年 31 28 31 30 31 30 31 31 30 31 30 31
//輸入:年份
//輸出:該年份是不是閏年.1,是.0,不是
u8 Is_Leap_Year(u16 year)
{
if(year%4==0) //必須能被4整除
{
if(year%100==0)
{
if(year%400==0)return 1;//如果以00結尾,還要能被400整除
else return 0;
}else return 1;
}else return 0;
}
//設置時鐘
//把輸入的時鐘轉換為秒鐘
//以1970年1月1日為基準
//1970~2099年為合法年份
//返回值:0,成功;其他:錯誤代碼.
//月份數據表
u8 const table_week[12]={0,3,3,6,1,4,6,2,5,0,3,5}; //月修正數據表
//平年的月份日期表
const u8 mon_table[12]={31,28,31,30,31,30,31,31,30,31,30,31};
u8 RTC_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec)
{
u16 t;
u32 seccount=0;
if(syear<1970||syear>2099)return 1;
for(t=1970;t<syear;t++) //把所有年份的秒鐘相加
{
if(Is_Leap_Year(t))seccount+=31622400;//閏年的秒鐘數
else seccount+=31536000; //平年的秒鐘數
}
smon-=1;
for(t=0;t<smon;t++) //把前面月份的秒鐘數相加
{
seccount+=(u32)mon_table[t]*86400;//月份秒鐘數相加
if(Is_Leap_Year(syear)&&t==1)seccount+=86400;//閏年2月份增加一天的秒鐘數
}
seccount+=(u32)(sday-1)*86400;//把前面日期的秒鐘數相加
seccount+=(u32)hour*3600;//小時秒鐘數
seccount+=(u32)min*60; //分鐘秒鐘數
seccount+=sec;//最后的秒鐘加上去
//設置時鐘
RCC->APB1ENR|=1<<28;//使能電源時鐘
RCC->APB1ENR|=1<<27;//使能備份時鐘
PWR->CR|=1<<8; //取消備份區寫保護
//上面三步是必須的!
RTC->CRL|=1<<4; //允許配置
RTC->CNTL=seccount&0xffff;
RTC->CNTH=seccount>>16;
RTC->CRL&=~(1<<4);//配置更新
while(!(RTC->CRL&(1<<5)));//等待RTC寄存器操作完成
return 0;
}
//得到當前的時間
//返回值:0,成功;其他:錯誤代碼.
u8 RTC_Get(void)
{
static u16 daycnt=0;
u32 timecount=0;
u32 temp=0;
u16 temp1=0;
timecount=RTC->CNTH;//得到計數器中的值(秒鐘數)
timecount<<=16;
timecount+=RTC->CNTL;
temp=timecount/86400; //得到天數(秒鐘數對應的)
if(daycnt!=temp)//超過一天了
{
daycnt=temp;
temp1=1970; //從1970年開始
while(temp>=365)
{
if(Is_Leap_Year(temp1))//是閏年
{
if(temp>=366)temp-=366;//閏年的秒鐘數
else break;
}
else temp-=365; //平年
temp1++;
}
timer.w_year=temp1;//得到年份
temp1=0;
while(temp>=28)//超過了一個月
{
if(Is_Leap_Year(timer.w_year)&&temp1==1)//當年是不是閏年/2月份
{
if(temp>=29)temp-=29;//閏年的秒鐘數
else break;
}
else
{
if(temp>=mon_table[temp1])temp-=mon_table[temp1];//平年
else break;
}
temp1++;
}
timer.w_month=temp1+1;//得到月份
timer.w_date=temp+1; //得到日期
}
temp=timecount%86400; //得到秒鐘數
timer.hour=temp/3600; //小時
timer.min=(temp%3600)/60; //分鐘
timer.sec=(temp%3600)%60; //秒鐘
timer.week=RTC_Get_Week(timer.w_year,timer.w_month,timer.w_date);//獲取星期
return 0;
}
//獲得現在是星期幾
//功能描述:輸入公歷日期得到星期(只允許1901-2099年)
//輸入參數:公歷年月日
//返回值:星期號
u8 RTC_Get_Week(u16 year,u8 month,u8 day)
{
u16 temp2;
u8 yearH,yearL;
yearH=year/100; yearL=year%100;
// 如果為21世紀,年份數加100
if (yearH>19)yearL+=100;
// 所過閏年數只算1900年之后的
temp2=yearL+yearL/4;
temp2=temp2%7;
temp2=temp2+day+table_week[month-1];
if (yearL%4==0&&month<3)temp2--;
return(temp2%7);
}
//比較兩個字符串指定長度的內容是否相等
//參數:s1,s2要比較的兩個字符串;len,比較長度
//返回值:1,相等;0,不相等
u8 str_cmpx(u8*s1,u8*s2,u8 len)
{
u8 i;
for(i=0;i<len;i++)if((*s1++)!=*s2++)return 0;
return 1;
}
const u8 *COMPILED_DATE;//獲得編譯日期
const u8 *COMPILED_TIME;//獲得編譯時間
const u8 Month_Tab[12][3]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
//自動設置時間為編譯器時間
void Auto_Time_Set(void)
{
u8 temp[3];
u8 i;
u8 mon,date;
u16 year;
u8 sec,min,hour;
for(i=0;i<3;i++)temp[i]=COMPILED_DATE[i];
for(i=0;i<12;i++)if(str_cmpx((u8*)Month_Tab[i],temp,3))break;
mon=i+1;//得到月份
if(COMPILED_DATE[4]==' ')date=COMPILED_DATE[5]-'0';
else date=10*(COMPILED_DATE[4]-'0')+COMPILED_DATE[5]-'0';
year=1000*(COMPILED_DATE[7]-'0')+100*(COMPILED_DATE[8]-'0')+10*(COMPILED_DATE[9]-'0')+COMPILED_DATE[10]-'0';
hour=10*(COMPILED_TIME[0]-'0')+COMPILED_TIME[1]-'0';
min=10*(COMPILED_TIME[3]-'0')+COMPILED_TIME[4]-'0';
sec=10*(COMPILED_TIME[6]-'0')+COMPILED_TIME[7]-'0';
RTC_Set(year,mon,date,hour,min,sec) ;
//printf("%d-%d-%d %d:%d:%d\n",year,mon,date,hour,min,sec);
}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -