精华内容
下载资源
问答
  • <p>i have column register on table jadwal: <pre><code>CREATE TABLE IF NOT EXISTS 'jadwal' ('register' int(5) zerofill NOT NULL AUTO_INCREMENT, PRIMARY KEY ('register') </code></pre> ...
  • STM32F1xx官方资料: 《STM32中文参考手册V10》-第16章 实时时钟(RTC)   RTC实时时钟 RTC实时时钟简介 ... RTC模块和时钟配置系统(RCC_BDCR寄存器)处于后备区域,即在系统复位或从待机模式唤醒后,RT...

    STM32F1xx官方资料:

    《STM32中文参考手册V10》-第16章  实时时钟(RTC)

     

    RTC实时时钟

    RTC实时时钟简介

    实时时钟是一个独立的定时器。RTC模块拥有一组连续计数的计数器,在相应软件配置下,可提供时钟日历的功能。修改计数器的值可以重新设置系统当前的时间和日期。 RTC模块和时钟配置系统(RCC_BDCR寄存器)处于后备区域,即在系统复位或从待机模式唤醒后,RTC的设置和时间维持不变。 

    BKP备份寄存器简介

    备份寄存器是42个16位的寄存器,可用来存储84个字节的用户应用程序数据。他们处在备份域里,当Vdd电源被切断,他们仍然由Vbat维持供电。当系统在待机模式下被唤醒,或系统复位或电源复位时,他们也不会被复位。

    此外,BKP控制寄存器用来管理侵入检测和RTC校准功能。在本文中,使用备份寄存器来存储RTC的相关信息(标记时钟是否已经经过了配置)。

    复位后,对备份寄存器和RTC的访问被禁止,并且备份域被保护以防止可能存在的意外的写操作。执行以下操作可以使能对备份寄存器和RTC的访问。

    • 通过设置寄存器RCC_APB1ENR的PWREN和BKPEN位来打开电源和后备接口的时钟;
    • 电源控制寄存器(PWR_CR)的DBP位来使能对后备寄存器和RTC的访问。

    RTC实时时钟的主要特征

    • 可编程的预分频系数:分频系数最高为220;
    • 32位的可编程计数器,可用于较长时间段的测量;
    • 2个分离的时钟:用于APB1接口的PCLK1和RTC时钟(RTC时钟的频率必须小于PCLK1时钟频率的四分之一以上);
    • 可以选择以下三种RTC的时钟源:
    1. HSE时钟除以128;
    2. LSE振荡器时钟;
    3. LSI振荡器时钟;
    • 2个独立的复位类型:
    1. APB1接口由系统复位;
    2. RTC核心(预分频器、闹钟、计数器和分频器)只能由后备域复位;
    • 3个专门的可屏蔽中断:
    1. 闹钟中断,用来产生一个软件可编程的闹钟中断;
    2. 秒中断,用来产生一个可编程的周期性中断信号(最长可达1秒);
    3. 溢出中断,指示内部可编程计数器溢出并回转为0的状态。

     

    RTC工作原理

    RTC工作原理框图

    由工作框图可以看出,RTC由两部分组成:

    • APB1接口:用来和APB1总线相连。通过APB1接口可以访问RTC的相关寄存器(预分频值,计数器值,闹钟值)
    • RTC核心:由一组可编程计数器组成,分两个主要模块:
    1. RTC预分频模块,它可以编程产生最长1秒的RTC时间基TR_CLK。如果设置了秒中断允许位,可以产生秒中断;
    2. 32位的可编程计数器,可被初始化为当前时间。系统时间按TR_CLK周期累加并与存储在RTC_ALR寄存器中的可编程时间相比,当匹配时候如果设置了闹钟中断允许位,可以产生闹钟中断;如果溢出,可以产生溢出中断。

    复位过程

    • 除了RTC_PRL、RTC_ALR、RTC_CNT和RTC_DIV寄存器外,所有的系统寄存器都由系统复位或电源复位进行异步复位。
    • RTC_PRL、RTC_ALR、RTC_CNT和RTC_DIV寄存器仅能通过备份域复位信号复位。

    读RTC寄存器

    RTC内核完全独立于APB1接口,软件通过APB1接口对RTC相关寄存器访问。但是相关寄存器只在RTC APB1时钟进行重新同步的RTC时钟的上升沿被更新。所以软件必须先等待寄存器同步标志位(RTC_CRL的RSF位)被硬件置1才读。

    这意味着,如果APB1接口曾经被关闭,而读操作又是在刚刚重新开启APB1之后,则在第一次的内部寄存器更新之前,从APB1上读出的RTC寄存器数值可能被破坏了(通常读到0)。简单地讲,在APB1接口被禁止(复位、无时钟或断电)的情况下,RTC核仍保持运行状态。接着,重新打开APB1接口,此时必须等待RTC_CRL寄存器中的RSF位(寄存器同步标志)被硬件置1,同步之后,读RTC寄存器的值才不会有误。

    因此,若在读取RTC寄存器时,RTC的APB1接口曾经处于禁止状态,则软件首先必须等待RTC_CRL寄存器的RSF位被硬件置1。

    写RTC寄存器

    必须设置RTC_CRL寄存器中的CNF位,使RTC进入配置模式后,才能写入RTC_PRL、RTC_CNT、RTC_ALR寄存器。

    另外,对RTC任何寄存器的写操作,都必须在前一次写操作结束后进行。可以通过查询RTC_CR寄存器中的RTOFF状态位,判断RTC寄存器是否处于更新中。仅当RTOFF状态位是1时,才可以写入RTC寄存器。

     

    RTC相关配置寄存器

    RTC控制寄存器高位(RTC_CRH)

    作用:配置3个专门的可屏蔽中断(溢出中断、闹钟中断、秒中断)使能。

    RTC控制寄存器低位(RTC_CRL)

    作用:RTC操作是否完成判断、配置模式判断、寄存器同步判断、3个中断的标志位。

    这个寄存器尤其重要(尤其是位5、位4、位3):

    • 写任何寄存器之前,必须判断上一次写操作已经结束,也就是判断RTOFF位是否置1;
    • 写CNT、ALR、PRL寄存器,必须先配置CNF位进入配置模式,修改完之后,设置CNF位为0退出配置模式;
    • 读任何寄存器,必须先判断RSF位,确定已经同步。

    RTC预分频装载寄存器(RTC_PRLH、RTC_PRLL)

    作用:配置RTC预分频装载值,这个值是20bit长度。

    根据这个寄存器的值可以确定,TR_CLK和RTCCLK之间的关系公式:

    fTR_CLK=fRTCCLK/(PRL+1)

    如果输入时钟频率是32.768kHz(fRTCCLK,也就是以LSE作为时钟源),这个寄存器中写入7FFFh(32767)可获得周期为1秒钟的信号。

    RTC预分频器余数寄存器(RTC_DIVH、RTC_DIVL)

    作用:获得预分频计数器的当前值,也就是从RTC预分频装载寄存器倒数到0之间的一个值(以RTCCLK为时钟)。

    RTC计数器寄存器(RTC_CNTH、RTC_CNTL)

    作用:存放计数器内的计数值(以TR_CLK为时钟)。

    注意:由于RTC预分频器余数寄存器以RTCCLK为时钟,而RTC计数器寄存器以TR_CLK为时钟,而RTCCLK的时钟通常远远大于TR_CLK,所以利用RTC预分频器余数寄存器可以获得更准确的控制。比如,RTC计数器寄存器存储当前时间,精确到秒;但是利用由于RTC预分频器余数寄存器,可以在RTC预分频装载寄存器倒数到0的平均数处停下,从而达到0.5秒的更精确时间。

    RTC闹钟寄存器(RTC_ALRH、RTC_ALRL)

    作用:当RTC计数器寄存器的值与RTC闹钟寄存器的值相等的时候,触发一个闹钟事件,产生一个闹钟中断。

     

    读写RTC寄存器的步骤

    读RTC寄存器

    • 查询RSF位(寄存器同步标志位),直至RSF的值变成1;
    • 对一个或多个RTC寄存器进行读操作。

    写RTC寄存器

    • 查询RTOFF位(RTC操作关闭位),直到RTOFF的值变为1 ;
    • 置CNF位(配置标志位)值为1,进入配置模式(仅仅PRL、CNT、ALR寄存器);
    • 对一个RTC寄存器进行写操作;
    • 清除CNF标志位,退出配置模式(仅仅PRL、CNT、ALR寄存器);
    • 查询RTOFF,直至RTOFF位变为1以确认写操作已经完成。

    也就是说:对寄存器的写操作,无论是设置中断使能等等,每操作一次就需要查询一次RTOFF位。而对于PRL、CNT、ALR寄存器,还需要进入配置模式,这个就没有必要每操作一次就退出配置模式,可以等都配置完成了再退出。

     

    RTC相关配置库函数

    • 2个时钟源操作函数
    void RCC_RTCCLKConfig(uint32_t RCC_RTCCLKSource);
    void RCC_RTCCLKCmd(FunctionalState NewState);

    作用:确定RTC的时钟源,使能RTC时钟(通常选用LSE时钟源)。

    • 3个参数配置函数
    void RTC_SetCounter(uint32_t CounterValue);
    void RTC_SetPrescaler(uint32_t PrescalerValue);
    void RTC_SetAlarm(uint32_t AlarmValue);

    作用:配置预分频装载寄存器的值、计数器的值、闹钟配置。

    • 1个中断配置函数
    void RTC_ITConfig(uint16_t RTC_IT, FunctionalState NewState);

    作用:配置RTC中断的选择和使能。

    • 2个配置模式函数
    void RTC_EnterConfigMode(void);
    void RTC_ExitConfigMode(void);

    作用:前者允许RTC配置,后者退出配置模式。

    • 2个同步函数
    void RTC_WaitForLastTask(void);
    void RTC_WaitForSynchro(void);

    作用:前者等待上次操作完成(CRL寄存器的RTOFF位),后者等待时钟同步(CRL寄存器的RSF位)。

    • 4个状态位函数
    FlagStatus RTC_GetFlagStatus(uint16_t RTC_FLAG);
    void RTC_ClearFlag(uint16_t RTC_FLAG);
    ITStatus RTC_GetITStatus(uint16_t RTC_IT);
    void RTC_ClearITPendingBit(uint16_t RTC_IT);

    作用:前两者获取(或清除)状态标志位,后两者为获取(或清除)中断状态标志位。

    • 其他的相关函数
    void PWR_BackupAccessCmd(FunctionalState NewState);
    void RCC_APB1PeriphClockCmd(uint32_t RCC_APB1Periph, FunctionalState NewState);
    void RCC_LSEConfig(uint8_t RCC_LSE);

    作用:第一个函数使能BKP后备区域访问使能,第二个函数使能PWR和BKP时钟,第三个函数开启LSE时钟(这里为什么使用这几个函数?是在上文:BKP备份寄存器简介中讲到)。

    void BKP_WriteBackupRegister(uint16_t BKP_DR, uint16_t Data);
    uint16_t BKP_ReadBackupRegister(uint16_t BKP_DR);

    作用:上面的PWR_BackupAccessCmd()函数使能BKP后备区域使能之后,就可以通过这两个函数来读BKP的寄存器,写BKP的寄存器。

     

    RTC一般步骤

    • 使能PWR和BKP时钟。调用函数:RCC_APB1PeriphClockCmd();
    • 使能后备寄存器访问。调用函数:PWR_BackupAccessCmd();
    • 配置RTC时钟源,使能RTC时钟。调用函数:RCC_RTCCLKConfig();RCC_RTCCLKCmd();
    • 如果使用LSE,要打开LSE:RCC_LSEConfig(RCC_LSE_ON);
    • 设置RTC预分频系数。调用函数:RTC_SetPrescaler();
    • 设置时间。调用函数:RTC_SetCounter();
    • 开启相关中断(如果需要)。调用函数:RTC_ITConfig();
    • 编写中断服务函数。调用函数:RTC_IRQHandler();
    • 部分操作要等待写操作完成和同步。调用函数:RTC_WaitForLastTask();RTC_WaitForSynchro()。

    下面按照这个一般步骤来进行一个简单的RTC程序:

    //时间结构体
    typedef struct 
    {
    	vu8 hour;
    	vu8 min;
    	vu8 sec;			
    	//公历日月年周
    	vu16 w_year;
    	vu8  w_month;
    	vu8  w_date;
    	vu8  week;		 
    }_calendar_obj;	
    _calendar_obj calendar;//时钟结构体 
     
    static void RTC_NVIC_Config(void)
    {	
        NVIC_InitTypeDef NVIC_InitStructure;
    	NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;		//RTC全局中断
    	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;	//先占优先级1位,从优先级3位
    	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;	//先占优先级0位,从优先级4位
    	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;		//使能该通道中断
    	NVIC_Init(&NVIC_InitStructure);		//根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
    }
    
    //实时时钟配置
    //初始化RTC时钟,同时检测时钟是否工作正常
    //BKP->DR1用于保存是否第一次配置的设置
    //返回0:正常
    //其他:错误代码
    
    u8 RTC_Init(void)
    {
    	//检查是不是第一次配置时钟
    	u8 temp=0;
    	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);	//使能PWR和BKP外设时钟   
    	PWR_BackupAccessCmd(ENABLE);	//使能后备寄存器访问  
    	
    	if (BKP_ReadBackupRegister(BKP_DR1) != 0x5050)		//从指定的后备寄存器中读出数据:读出了与写入的指定数据不相乎
    		{	 			
    		BKP_DeInit();	//复位备份区域 	
    			
    		RCC_LSEConfig(RCC_LSE_ON);	//设置外部低速晶振(LSE),使用外设低速晶振
    		while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET&&temp<250)	//检查指定的RCC标志位设置与否,等待低速晶振就绪
    			{
    			temp++;
    			delay_ms(10);
    			}
    		if(temp>=250)return 1;//初始化时钟失败,晶振有问题	    
    			
    		RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);		//设置RTC时钟(RTCCLK),选择LSE作为RTC时钟    
    		RCC_RTCCLKCmd(ENABLE);	//使能RTC时钟  
    			
    		RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成
    		RTC_WaitForSynchro();		//等待RTC寄存器同步  
    		RTC_ITConfig(RTC_IT_SEC, ENABLE);		//使能RTC秒中断
    		RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成
    			
    		RTC_EnterConfigMode();/// 允许配置	
    		RTC_SetPrescaler(32767); //设置RTC预分频的值
    		RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成
    			
    		RTC_Set(2015,1,14,17,42,55);  //设置时间	
    		RTC_ExitConfigMode(); //退出配置模式  
    			
    		BKP_WriteBackupRegister(BKP_DR1, 0X5050);	//向指定的后备寄存器中写入用户程序数据
    		}
    	else//系统继续计时
    		{
    		RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成
    		RTC_ITConfig(RTC_IT_SEC, ENABLE);	//使能RTC秒中断
    		RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成
    		}
    		
    	RTC_NVIC_Config();//RCT中断分组设置		    				     
    	RTC_Get();//更新时间	
    	return 0; //ok
    
    }		 				    
    //RTC时钟中断
    //每秒触发一次  
    //extern u16 tcnt; 
    void RTC_IRQHandler(void)
    {		 
    	if (RTC_GetITStatus(RTC_IT_SEC) != RESET)//秒钟中断
    	{							
    		RTC_Get();//更新时间   
     	}
    	if(RTC_GetITStatus(RTC_IT_ALR)!= RESET)//闹钟中断
    	{
    		RTC_ClearITPendingBit(RTC_IT_ALR);		//清闹钟中断	  	
    	  RTC_Get();				//更新时间   
      	printf("Alarm Time:%d-%d-%d %d:%d:%d\n",calendar.w_year,calendar.w_month,calendar.w_date,calendar.hour,calendar.min,calendar.sec);//输出闹铃时间	
    		
      	} 				  								 
    	RTC_ClearITPendingBit(RTC_IT_SEC|RTC_IT_OW);		//清闹钟中断
    	RTC_WaitForLastTask();	  	    						 	   	 
    }
    //判断是否是闰年函数
    //月份   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_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);	//使能PWR和BKP外设时钟  
    	PWR_BackupAccessCmd(ENABLE);	//使能RTC和后备寄存器访问 
    	RTC_SetCounter(seccount);	//设置RTC计数器的值
    
    	RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成  	
    	return 0;	    
    }
    
    //初始化闹钟		  
    //以1970年1月1日为基准
    //1970~2099年为合法年份
    //syear,smon,sday,hour,min,sec:闹钟的年月日时分秒   
    //返回值:0,成功;其他:错误代码.
    u8 RTC_Alarm_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_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);	//使能PWR和BKP外设时钟   
    	PWR_BackupAccessCmd(ENABLE);	//使能后备寄存器访问  
    	//上面三步是必须的!
    	
    	RTC_SetAlarm(seccount);
     
    	RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成  	
    	
    	return 0;	    
    }
    
    
    //得到当前的时间
    //返回值:0,成功;其他:错误代码.
    u8 RTC_Get(void)
    {
    	static u16 daycnt=0;
    	u32 timecount=0; 
    	u32 temp=0;
    	u16 temp1=0;	  
        timecount=RTC_GetCounter();	 
     	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 {temp1++;break;}  
    			}
    			else temp-=365;	  //平年 
    			temp1++;  
    		}   
    		calendar.w_year=temp1;//得到年份
    		temp1=0;
    		while(temp>=28)//超过了一个月
    		{
    			if(Is_Leap_Year(calendar.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++;  
    		}
    		calendar.w_month=temp1+1;	//得到月份
    		calendar.w_date=temp+1;  	//得到日期 
    	}
    	temp=timecount%86400;     		//得到秒钟数   	   
    	calendar.hour=temp/3600;     	//小时
    	calendar.min=(temp%3600)/60; 	//分钟	
    	calendar.sec=(temp%3600)%60; 	//秒钟
    	calendar.week=RTC_Get_Week(calendar.w_year,calendar.w_month,calendar.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);
    }			  
    
     int main(void)
     {	 
     	u8 t=0;	
    	delay_init();	    	 //延时函数初始化	  
    	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
    	uart_init(115200);	 	//串口初始化为115200
     	LED_Init();			     //LED端口初始化
    	LCD_Init();			 	
    	usmart_dev.init(SystemCoreClock/1000000);	//初始化USMART	
    	RTC_Init();	  			//RTC初始化
    	POINT_COLOR=RED;//设置字体为红色 
    	LCD_ShowString(60,50,200,16,16,"WarShip STM32");	
    	LCD_ShowString(60,70,200,16,16,"RTC TEST");	
    	LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK");
    	LCD_ShowString(60,110,200,16,16,"2015/1/14");		
    	//显示时间
    	POINT_COLOR=BLUE;//设置字体为蓝色
    	LCD_ShowString(60,130,200,16,16,"    -  -  ");	   
    	LCD_ShowString(60,162,200,16,16,"  :  :  ");		    
    	while(1)
    	{								    
    		if(t!=calendar.sec)
    		{
    			t=calendar.sec;
    			LCD_ShowNum(60,130,calendar.w_year,4,16);									  
    			LCD_ShowNum(100,130,calendar.w_month,2,16);									  
    			LCD_ShowNum(124,130,calendar.w_date,2,16);	 
    			switch(calendar.week)
    			{
    				case 0:
    					LCD_ShowString(60,148,200,16,16,"Sunday   ");
    					break;
    				case 1:
    					LCD_ShowString(60,148,200,16,16,"Monday   ");
    					break;
    				case 2:
    					LCD_ShowString(60,148,200,16,16,"Tuesday  ");
    					break;
    				case 3:
    					LCD_ShowString(60,148,200,16,16,"Wednesday");
    					break;
    				case 4:
    					LCD_ShowString(60,148,200,16,16,"Thursday ");
    					break;
    				case 5:
    					LCD_ShowString(60,148,200,16,16,"Friday   ");
    					break;
    				case 6:
    					LCD_ShowString(60,148,200,16,16,"Saturday ");
    					break;  
    			}
    			LCD_ShowNum(60,162,calendar.hour,2,16);									  
    			LCD_ShowNum(84,162,calendar.min,2,16);									  
    			LCD_ShowNum(108,162,calendar.sec,2,16);
    			LED0=!LED0;
    		}	
    		delay_ms(10);								  
    	};  
     }

    STM32控制程序分析

    RTC_Init()函数:RTC初始化函数。

    按照之前的RTC一般步骤初始化RTC函数,这里需要注意的是,为了区分是否是第一次执行RTC_Init()函数,这里使用了一个flag(向BKP_DR1寄存器写入0x5050,当然写入其他的数字也都是可以的)。

    if (BKP_ReadBackupRegister(BKP_DR1) != 0x5050)		//从指定的后备寄存器中读出数据:读出了与写入的指定数据不相乎
    		{	 			
    		//第一次执行RTC_Init
    		BKP_WriteBackupRegister(BKP_DR1, 0X5050);	//向指定的后备寄存器中写入用户程序数据
    		}
    	else//系统继续计时
    		{
    		//不是第一次执行RTC_Init
    		}

    为什么要区分是否第一次执行RTC_Init呢?因为如果由于断电等因素,程序中断,但是RTC时钟却还是在执行中;等恢复供电,重新启动程序,这个时候就不需要再对RTC时钟进行初始化了。

    同时,设置外部低速晶振(LSE),使用外设低速晶振。需要检查指定的RCC标志位设置与否,等待低速晶振就绪。

    这里时间的设置是:距离1970年1月1日0点0分0秒的时间距离。其中,RTC_Get()、RTC_Set()等函数的内容涉及到时间距离转换的各种算法,就不在本文的讨论范围了。

     

    展开全文
  • 1、Interrupt Pending Register (INTP, R/W, Address = 0xE280_...中断挂起寄存器: You can clear specific bits of INTP register by writing 1’s to the bits that you want to clear regardless of RTCEN value.

    1、Interrupt Pending Register (INTP, R/W, Address = 0xE280_0030)

    中断挂起寄存器:

    You can clear specific bits of INTP register by writing 1’s to the bits that you want to clear regardless of RTCEN value.



    不论RTCEN的值是多少,如果你想清除INTP特定的位可以通过对相应的位写入1.

    2、Real Time Clock Control Register (RTCCON, R/W, Address = 0xE280_0040)

    RTC控制寄存器:
    The RTCCON register consists of 10 bits such as the RTCEN, which controls the read/ write enable of the BCDSEL, CNTSEL, CLKRST, TICCKSEL and TICEN for testing, and CLKOUTEN for RTC clock output control.
    RTCEN bit controls all interfaces between the CPU and the RTC, therefore it should be set to 1 in an RTC control routine to enable data read/ write after a system reset. To prevent inadvertent writing into BCD counter registers the RTCEN bit should be cleared to 0 before power off.
    CLKRST is counter reset for 2
    15 clock divider. Before RTC clock setting, 215 clock divider must be reset for exact RTC operation.
    RTCCON寄存器包含10个位,如下图所示。RTCEN控制CPU和RTC所有的接口,因此在系统复位后在RTC中使能相应的读写数据操作RTCEN应该被置1。为了防止因为数据意外写到BCD计数器寄存器时,应该在电源关闭前将RTCEN位置0。

    CLKRST是对2^15时钟分频器的计数器复位。在RTC时钟设置之前,为了RTC精确的运行,2^15时钟分频器必须复位。


    3、Tick Time Count Register (TICNT, R/W, Address = 0xE280_0044)

    这个寄存器暂时我们不用,它一般用在操作系统中。


    4、RTC Alarm Control Register (RTCALM, R/W, Address = 0xE280_0050)
    The RTCALM register determines the alarm enable and the alarm time. Note that the RTCALM register
    generates the alarm signal through both ALARM_INT and ALARM_WK in power down mode, but only through ALARM_INT in the normal operation mode. Enable ALMEN to use ALARM_INT and ALARM_WK.
    If compare value is year, ALMEN and YEAREN must be enabled. If compare values are year, month, day, hour,min and sec, ALMEN, YEAREN, MONEN, DAYEN, HOUREN, MINEN and SECEN must be enabled.
     

    RTC闹钟控制寄存器:
    RTCALM寄存器决定使能闹钟和设置闹钟时间。注意:RTCALM寄存器产生闹钟信号,在断电模式下通过闹钟中断和闹钟唤醒信号,在正常运行模式下只能通过闹钟中断信号。使能ALMEN后使用闹钟中断和闹钟唤醒信号。如果比较的是只是年份则必须同时ALMEN和YEAREN使能。如果是年月日时分秒则必须ALMEN, YEAREN, MONEN, DAYEN, HOUREN, MINEN and SECEN都使能。

    5、Alarm Second Data Register (ALMSEC, R/W, Address = 0xE280_0054)
    设置闹钟的秒数寄存器


    下面这几个也是设置闹钟相关日期/时间的寄存器:



    下面这几个是设置正常时实时时钟的时间寄存器:





    最后一个是设置TICK相关的寄存器,暂时我们还不用:




    展开全文
  • 这也是很多配置过程的第一步,可以通过RCC_APB1ENR寄存器来设置。在中文参考手册中是设置寄存器RCC_APB1ENR的PWREN和BKPEN位 寄存器方式:RCC_APB1ENR=1<<28; RCC_APB1ENR=1<<27; 库函数方式:RCC_APB1...

    参考资料:STM32中文参考手册;正点原子STM32开发指南

    RTC时钟简介

    实时时钟是一个独立的定时器。RTC模块拥有一组连续计数的计数器。修改计数器的值可以重新设置系统当前的时间和日期。 RTC模块和时钟配置系统(RCC_BDCR寄存器)处于后备区域,即在系统复位或从待机模式唤醒后,RTC的设置和时间维持不变。 系统复位后,对后备寄存器和RTC的访问被禁止,这是为了防止对后备区域(BKP)的意外写操作。执行以下操作将使能对后备寄存器和RTC的访问:
    ● 设置寄存器RCC_APB1ENR的PWREN和BKPEN位,使能电源和后备接口时钟
    ● 设置寄存器PWR_CR的DBP位,使能对后备寄存器和RTC的访问。

    主要特性

    ● 可编程的预分频系数:分频系数高为220。
    ● 32位的可编程计数器,可用于较长时间段的测量。
    ● 2个分离的时钟:用于APB1接口的PCLK1和RTC时钟(RTC时钟的频率必须小于PCLK1时钟 频率的四分之一以上)。
    ● 可以选择以下三种RTC的时钟源:
    ─ HSE时钟除以128;
    ─ LSE振荡器时钟;
    ─ LSI振荡器时钟
    ● 2个独立的复位类型:
    ─ APB1接口由系统复位;
    ─ RTC核心(预分频器、闹钟、计数器和分频器)只能由后备域复位
    ● 3个专门的可屏蔽中断:
    ─ 闹钟中断,用来产生一个软件可编程的闹钟中断。
    ─ 秒中断,用来产生一个可编程的周期性中断信号(长可达1秒)。
    ─ 溢出中断,指示内部可编程计数器溢出并回转为0的状态。

    RTC描述和框图

    RTC由两个主要部分组成(参见下图)。
    第一部分(APB1接口)用来和APB1总线相连。此单元还包 含一组16位寄存器,可通过APB1总线对其进行读写操作。APB1接口由APB1总线 时钟驱动,用来与APB1总线接口。
    另一部分(RTC核心)由一组可编程计数器组成,分成两个主要模块。第一个模块是RTC的预分频模块,它可编程产生长为1秒的RTC时间基准TR_CLK。RTC的预分频模块包含了一个20位的可编程分频器(RTC预分频器)。如果在RTC_CR寄存器中设置了相应的允许位,则在每个TR_CLK周期中RTC产生一个中断(秒中断)。第二个模块是一个32位的可编程计数器,可被初始 化为当前的系统时间。系统时间按TR_CLK周期累加并与存储在RTC_ALR寄存器中的可编程时 间相比较,如果RTC_CR控制寄存器中设置了相应允许位,比较匹配时将产生一个闹钟中断
    在这里插入图片描述

    在看配置步骤之前我自己是偏向于看寄存器版本的,更能理解实际的过程,但是我们常常使用库函数方式,因为进行了封装比较方便。

    RTC正常工作的一般配置步骤

    1.使能电源时钟和备份区域时钟
    这也是很多配置过程的第一步,可以通过RCC_APB1ENR寄存器来设置。在中文参考手册中是设置寄存器RCC_APB1ENR的PWREN和BKPEN位
    在这里插入图片描述
    寄存器方式:RCC_APB1ENR=1<<28; RCC_APB1ENR=1<<27;
    库函数方式:RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR |RCC_APB1Periph_BKP, ENABLE);

    2.取消备份区写保护
    要向备份区写入数据先要取消备份区写保护(写保护在每次硬复位之后被使能),否则是无法向备份区域写入数据的。我们需要用到向备份区域写入一个字节,来标记时钟已经配置过了,这样避免每次复位之后重新配置时钟。
    设置寄存器PWR_CR(电源控制寄存器)的DBP位,使能对后备寄存器和RTC的访问。
    在这里插入图片描述
    寄存器方式:PWR->CR|=1<<8;
    库函数方式:PWR_BackupAccessCmd(ENABLE);

    3.复位备份区域,开启外部低速振荡器
    在取消备份区域写保护之后,可以先对这个区域复位,可以清除前面的设置,然后可以使能外部低速振荡器,这里一般要先判断RCC_BDCR(备份域控制寄存器)的LSERDY位来确定低速振荡器已经就绪。

    在这里插入图片描述
    寄存器方式:
    RCC->BDCR|=1<<16; //备份区域软件复位
    RCC->BDCR&=~(1<<16); //备份区域软件复位结束
    RCC->BDCR|=1<<0;//开启外部低速振荡器
    RCC->BDCR|=0X02; //外部低速LSE就绪
    库函数方式:
    BKP_DeInit(); //复位备份区域
    RCC_LSEConfig(RCC_LSE_ON);//设置外部低速晶振
    RCC_GetFlagStatus(RCC_FLAG_LSERDY) = RESET; //外部低速LSE就绪
    说明:在最后一步外部低速LSE就绪,一般是在if中用于判断,用==号。

    4.选择RTC时钟,并使能
    这里我们将通过 RCC_BDCR 的 RTCSEL 来选择选择外部 LSE(32.768K 的外部晶振)作为 RTC 的时钟。然后通过 RTCEN 位使能 RTC 时钟,为什么选这个时钟??是通过时钟树决定的,RTC时钟可以有三个来源
    在这里插入图片描述
    在这里插入图片描述
    寄存器方式:
    RCC->BDCR|=1<<8;
    RCC->BDCR|=1<<15;
    库函数方式:
    RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
    RCC_RTCCLKCmd(ENABLE);

    5.设置RTC的分频,以及配置RTC时钟
    在开启了 RTC 时钟之后,我们要做的就是设置 RTC 时钟的分频数,通过 RTC_PRLH 和 RTC_PRLL (RTC预分频装载寄存器)来设置,但是在设置RTC时钟分频数时,要先检查RTC_CR寄存器的RTOFF位。

    预分频装载寄存器用来保存RTC预分频器的周期计数值。它们受RTC_CR寄存器的RTOFF位保护,仅当RTOFF值为’1’时允许进行写操作。

    然后等待 RTC 寄存器操作完成,并同步之后,设置秒钟中断。然后设置 RTC 的允许配置位,设置时间或者设置闹钟。
    ![RTC(https://img-blog.csdnimg.cn/20200408184248329.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ0NzA4NDI2,size_16,color_FFFFFF,t_70)
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    寄存器方式:
    while(RTC->CRL&=(1<<5));//检查RTC寄存器的RTOFF位
    while(!(RTC->CRL&(1<<3))); //等待 RTC 寄存器同步
    RTC->CRH|=0X01; //允许秒中断
    RTC->CRH|=0X02; //允许闹钟中断
    while(!(RTC->CRL&(1<<5)));//等待 RTC 寄存器操作完成
    RTC->CRL|=1<<4; //允许配置
    RTC->PRLH=0X0000;
    RTC->PRLL=32767; //时钟周期设置 理论值:32767

    库函数方式:
    RTC_WaitForLastTask(); //等待最近一次对 RTC 寄存器的写操作完成
    RTC_WaitForSynchro(); //等待 RTC 寄存器同步 RTC_ITConfig(RTC_IT_SEC, ENABLE); //使能 RTC 秒中断 RTC_WaitForLastTask(); //等待 RTC 寄存器操作完成 RTC_EnterConfigMode(); // 允许配置
    RTC_SetPrescaler(32767); //设置 RTC 预分频的值

    6.更新配置,设置 RTC 中断
    在设置完时钟之后,我们将配置更新,这里还是通过 RTC_CRH 的 CNF 来实现。在这之后 我们在备份区域 BKP_DR1 中写入 0X5050 代表我们已经初始化过时钟了,下次开机(或复位) 的时候,先读取 BKP_DR1 的值,然后判断是否是 0X5050 来决定是不是要配置,避免重复配置。接着我们配置 RTC 的秒钟中断,并进行分组。
    寄存器方式:
    RTC->CRL&=~(1<<4); //配置更新
    while(!(RTC->CRL&(1<<5))); //等待 RTC 寄存器操作完成
    BKP->DR1=0X5050; //标记已经配置过

    库函数方式:
    RTC_WaitForLastTask(); //等待 RTC 寄存器操作完成
    RTC_ExitConfigMode(); //退出配置模式 BKP_WriteBackupRegister(BKP_DR1, 0X5050); //向指定的后备寄存器中写入用户程序数据 0x5050

    在退出配置模式之前可以进行时间设置

    7.编写中断服务函数

    设置时间函数RTC_Set()
    该函数用于设置时间,把我们输入的时间,转换为以 1970 年 1 月 1 日 0 时 0 分 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_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);  //使能 PWR 和 BKP 外设时钟    
    	PWR_BackupAccessCmd(ENABLE);   //使能 RTC 和后备寄存器访问   
    	RTC_SetCounter(seccount);  //设置 RTC 计数器的值  
    	RTC_WaitForLastTask();  //等待最近一次对 RTC 寄存器的写操作完成    
    	return 0;      
    }
    

    用于获取时间和日期等数据函数RTC_Get()
    函数其实就是将存储在秒钟寄存器 RTC->CNTH 和 RTC->CNTL 中的秒钟数据转换为真正的时间和日期。该代码还用到了一个 calendar 的结构体。 因为 STM32 的 RTC 只有秒钟计数器,而年月日,时分秒这些需要我们自己软件计算。把计算好的值保存在 calendar 里面,方便其他程序调用。

    typedef struct 
    {
    	vu8 hour;
    	vu8 min;
    	vu8 sec;			
    	vu16 w_year;
    	vu8  w_month;
    	vu8  w_date;
    	vu8  week;		 
    }_calendar_obj;					 
    extern _calendar_obj calendar;	
    
    //得到当前的时间,结果保存在 calendar 结构体里面 
    //返回值: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++;     
    		}      
    		calendar.w_year=temp1;   //得到年份   
    		temp1=0;   
    		while(temp>=28)      //超过了一个月 
    		{    
    			if(Is_Leap_Year(calendar.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++;     
    		}   calendar.w_month=temp1+1; //得到月份   	
    		calendar.w_date=temp+1;   //得到日期   
    	}  
    	temp=timecount%86400;   //得到秒钟数         
    	calendar.hour=temp/3600;    //小时  
    	calendar.min=(temp%3600)/60;  //分钟   
    	calendar.sec=(temp%3600)%60;  //秒钟  
    	calendar.week=RTC_Get_Week(calendar.w_year,calendar.w_month,calendar.w_date);  //获取星期    
    	return 0; 
    } 
    

    秒钟中断服务函数

    //RTC 时钟中断 
    //每秒触发一次   
    void RTC_IRQHandler(void) 
    {     
    	if (RTC_GetITStatus(RTC_IT_SEC) != RESET)  //秒钟中断  
    	{          
    		RTC_Get();    //更新时间      
    	}  
    	if(RTC_GetITStatus(RTC_IT_ALR)!= RESET)    //闹钟中断  
    	{   
    		RTC_ClearITPendingBit(RTC_IT_ALR);   //清闹钟中断      
    		RTC_Get();    //更新时间    
    		printf("Alarm Time:%d-%d-%d %d:%d:%d\n",calendar.w_year,calendar.w_month, calendar.w_date,calendar.hour,calendar.min,calendar.sec);//输出闹铃时间
    	}                  		
    	RTC_ClearITPendingBit(RTC_IT_SEC|RTC_IT_OW); //清闹钟中断  
    	RTC_WaitForLastTask();                      
    }
    

    最后是想要用按键调整时间,后面再改成TFTLCD试试吧,main函数如下,按键写在外部中断里面:

     int main(void)
     {	 
     	u8 t=0;	
    	delay_init();	    	 //延时函数初始化	  
    	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
    	uart_init(115200);	 	//串口初始化为115200
     	LED_Init();			     //LED端口初始化
    	LCD_Init();		
    	EXTIX_Init();	 
    	usmart_dev.init(SystemCoreClock/1000000);	//初始化USMART	
    	RTC_Init();	  			//RTC初始化
    	//显示时间
    	POINT_COLOR=BLUE;//设置字体为蓝色
    	LCD_ShowString(60,130,200,16,16,"    -  -  ");	   
    	LCD_ShowString(60,170,200,16,16,"  :  :  ");		    
    	while(1)
    	{	 
    		if(t!=calendar.sec)
    		{
    			t=calendar.sec;
    			LCD_ShowNum(60,130,calendar.w_year,4,16);									  
    			LCD_ShowNum(100,130,calendar.w_month,2,16);									  
    			LCD_ShowNum(124,130,calendar.w_date,2,16);	 
    			switch(calendar.week)
    			{
    				case 0:
    					LCD_ShowString(60,148,200,16,16,"Sunday   ");
    					break;
    				case 1:
    					LCD_ShowString(60,148,200,16,16,"Monday   ");
    					break;
    				case 2:
    					LCD_ShowString(60,148,200,16,16,"Tuesday  ");
    					break;
    				case 3:
    					LCD_ShowString(60,148,200,16,16,"Wednesday");
    					break;
    				case 4:
    					LCD_ShowString(60,148,200,16,16,"Thursday ");
    					break;
    				case 5:
    					LCD_ShowString(60,148,200,16,16,"Friday   ");
    					break;
    				case 6:
    					LCD_ShowString(60,148,200,16,16,"Saturday ");
    					break;  
    			}
    			LCD_ShowNum(60,170,calendar.hour,2,16);									  
    			LCD_ShowNum(84,170,calendar.min,2,16);									  
    			LCD_ShowNum(108,170,calendar.sec,2,16);
    			LED0=!LED0;
    		}	
    		delay_ms(10);								  
    	}
     }
    
    u8 cnt=0;
    //外部中断0服务程序 
    void EXTI0_IRQHandler(void)
    {
    	delay_ms(10);//消抖
    	if(WK_UP==1)	 	 //WK_UP按键
    	{				 
    		if(cnt<4)
    		{
    			cnt++;
    		}
    		else
    		{
    			cnt=0;
    		}
    	}
    	EXTI_ClearITPendingBit(EXTI_Line0); //清除LINE0上的中断标志位  
    }
     
    
    //外部中断3服务程序
    void EXTI3_IRQHandler(void)
    {
    	delay_ms(10);//消抖
    	if(KEY1==0&&cnt==0)	 //年
    	{				 
    		RTC_Set(calendar.w_year+1,calendar.w_month,calendar.w_date,calendar.hour,calendar.min,calendar.sec);
    	}
    	else if(KEY1==0&&cnt==1)	//月
    	{
    		RTC_Set(calendar.w_year,calendar.w_month+1,calendar.w_date,calendar.hour,calendar.min,calendar.sec);
    	}
    	else if(KEY1==0&&cnt==2)	//日
    	{
    		RTC_Set(calendar.w_year,calendar.w_month,calendar.w_date+1,calendar.hour,calendar.min,calendar.sec);
    	}
    	else if(KEY1==0&&cnt==3)	//时
    	{
    		RTC_Set(calendar.w_year,calendar.w_month,calendar.w_date,calendar.hour+1,calendar.min,calendar.sec);
    	}
    	else if(KEY1==0&&cnt==4)	//分
    	{
    		RTC_Set(calendar.w_year,calendar.w_month,calendar.w_date,calendar.hour,calendar.min+1,calendar.sec);
    	}
    	EXTI_ClearITPendingBit(EXTI_Line3);  //清除LINE3上的中断标志位  
    }
    
    void EXTI4_IRQHandler(void)
    {
    	delay_ms(10);//消抖
    	if(KEY0==0&&cnt==0)	 //年
    	{				 
    		RTC_Set(calendar.w_year-1,calendar.w_month,calendar.w_date,calendar.hour,calendar.min,calendar.sec);
    	}	
    	else if(KEY0==0&&cnt==1)	//月
    	{
    		if(calendar.w_month>0)
    		{
    			RTC_Set(calendar.w_year,calendar.w_month-1,calendar.w_date,calendar.hour,calendar.min,calendar.sec);
    		}
    	}
    	else if(KEY0==0&&cnt==2)	//日
    	{
    		if(calendar.w_date>0)
    		{
    			RTC_Set(calendar.w_year,calendar.w_month,calendar.w_date-1,calendar.hour,calendar.min,calendar.sec);
    		}
    	}
    	else if(KEY0==0&&cnt==3)	//时
    	{
    		if(calendar.hour>0)
    		{
    			RTC_Set(calendar.w_year,calendar.w_month,calendar.w_date,calendar.hour-1,calendar.min,calendar.sec);
    		}
    	}
    	else if(KEY0==0&&cnt==4)	//分
    	{
    		if(calendar.min>0)
    		{
    			RTC_Set(calendar.w_year,calendar.w_month,calendar.w_date,calendar.hour,calendar.min-1,calendar.sec);
    		}
    	}
    	EXTI_ClearITPendingBit(EXTI_Line4);  //清除LINE4上的中断标志位  
    }
    

    因为在减时间的时候会出现bug,目前还没看出什么毛病,所以就不允许时间跨度递减,比如从2020-1-1把月份减一变成2019-12-31可能就会出现bug,导致时钟紊乱,其他日,时,分也是。

    在这里运用KEY0对时间减,KEY1对时间加,KEY_UP换位,这里用到了cnt这个标志位,初始是cnt=0,表示调整年,cnt=1,表示调整月,依此类推,但是不对秒进行调整,原因是当按键触发中断的时候要先运行中断函数对时间进行调整,导致秒中断被打断,所以秒会不准。

    实验结果

    在这里插入图片描述
    这是毕业设计里面的一小部分,后面再把毕业设计里面的东西再总结一遍吧,其实想用TFTLCD屏幕直接数字修改时间,后面再改改。

    有错误的话欢迎指出来呀

    展开全文
  • RTC_CRL寄存器中的RSF 位(寄存器同步标志)被硬件置’1’ 。 注: RTC的 APB1 接口不受WFI和WFE等低功耗模式的影响。 */ RTC_WaitForSynchro(); // 同步 RTC_WaitForLastTask(); RTC_ITConfig(RTC_IT_SEC,...

    在实际应用中,会出现许多复位或者掉电的情况,下面提供了一种方法使即使是在掉电和复位事件发生时,仍旧可以利用低功耗模式继续对于实时时钟进行供电,保证时钟的正常运行!


    //bsp_rtc.h
    #ifndef _BSP_RTC_H
    #define _BSP_RTC_H
    #include "misc.h"
    
    
    /*全局变量*/	   
     
    uint8_t RTCInterruptFlag=0;	 //RTC 中断标志
    
    uint32_t RTC_TimeNum=0;			  // 设置时间变量
    
    
    uint16_t Year;
    
    uint8_t Month;
    
    uint8_t Day;
    
    /* RTC hardware init*/
    void RTC_NVIconfigration(void);
    
    void RTC_configration(void);
    
    void RTC_Init(void);
    
    /*日历及时间输入*/
    uint8_t RTC_InputTime(uint32_t border);
    
    uint32_t RTC_TimeCollate(void);
    
    void RTC_RxIntHandler(void); 
    
     /*获得年月日*/
    uint16_t GetYear();
    
    uint8_t GetMonth();
    
    uint8_t GetDay();
    
    void CalenderSet(void);
    
    void  CalenderCount(void);
    
    static uint8_t Choice_MonthDay(uint16_t temp_year,uint8_t temp_month);
    
    /*显示*/
    void RTC_TimeDisplay(uint32_t TimeVar);
    
     /*测试*/
    void Text_RTC(void); 
    
    
    #endif/*_BSP_RTC_H*/
     


    bsp_rtc.c

    #include "bsp_rtc.h"
    
    #define RTCClockSource_LSE
    
    uint32_t TimeDisplay=0;	// 用于显示测试 每次进入中断改变  
    
    /***************************************************************************************************
    *\Function      RTC_NVIconfigration()
    *\Description   设置RTC中断优先级
    *\Parameter     void
    *\Return        void
    *\Note          
    *\Log          	2014年7月24日
    ***************************************************************************************************/
    void RTC_NVIconfigration(void)
    {
        NVIC_InitTypeDef NVIC_InitStructure;
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);	  //已经在bsp_usart.c进行了设置
    
        /* Enable the RTC Interrupt */
        NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;					//配置外部中断源(秒中断)
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&NVIC_InitStructure);
    }
    
    /***************************************************************************************************
    *\Function      RTC_configration(void)
    *\Description   RTC配置函数
    *\Parameter     void
    *\Return        void
    *\Note          
    *\Log          	2014年7月24日
    ***************************************************************************************************/
    void RTC_configration(void)
    {
        /* 使能 PWR 和 BKP 的时钟 */
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
    
        PWR_DeInit();
        /* 允许访问BKP区域 */
        PWR_BackupAccessCmd(ENABLE);
    
        /* 复位BKP */
        BKP_DeInit();
    
    #ifdef RTCClockSource_LSI
        /* 使能内部RTC时钟 */
        RCC_LSICmd(ENABLE);
        /* 等待RTC内部时钟就绪 */
        while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET)
        {
        }
        /* 选择RTC内部时钟为RTC时钟 */
        RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);
    #elif defined	RTCClockSource_LSE
        /* 使能RTC外部时钟 */
        RCC_LSEConfig(RCC_LSE_ON);
        /* 等待RTC外部时钟就绪 */
        while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET)
        {
        }
    
        /* 选择RTC外部时钟为RTC时钟 */
        RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
    #endif
        /* 使能RTC时钟 */
        RCC_RTCCLKCmd(ENABLE);
    
    
    #ifdef RTCClockOutput_Enable
        /* Disable the Tamper Pin */
        BKP_TamperPinCmd(DISABLE); /* To output RTCCLK/64 on Tamper pin, the tamper
                                   functionality must be disabled */
    
        /* 使能在TAMPER脚输出RTC时钟 */
        BKP_RTCCalibrationClockOutputCmd(ENABLE);
    #endif
    
        /* 等待RTC寄存器同步 */
        RTC_WaitForSynchro();
    
        /* 等待写RTC寄存器完成 */
        RTC_WaitForLastTask();
    
        /* 使能RTC秒中断 */
        RTC_ITConfig(RTC_IT_SEC, ENABLE);
    
        /* 等待写RTC寄存器完成 */
        RTC_WaitForLastTask();
    
        /* 设置RTC预分频 */
    #ifdef RTCClockSource_LSI
        RTC_SetPrescaler(31999);            /* RTC period = RTCCLK/RTC_PR = (32.000 KHz)/(31999+1) */
    #elif defined	RTCClockSource_LSE
        RTC_SetPrescaler(32767);            /* RTC period = RTCCLK/RTC_PR = (32.768 KHz)/(32767+1) */
    #endif
    
        /* 等待写RTC寄存器完成 */
        RTC_WaitForLastTask();
    }
    /***************************************************************************************************
    *\Function      RTC_Init(void)
    *\Description   RTC初始化
    *\Parameter     void
    *\Return        void
    *\Note          
    *\Log          	2014年7月24日
    ***************************************************************************************************/
    void RTC_Init(void)
    {
        if (BKP_ReadBackupRegister(BKP_DR1)!=0x0229)
        {
            //printf("\n RTC not yet configured....");
    
            RTC_configration();
    
            printf("\n RTC实时时钟设置...");
    		 /*日历设置操作*/
    		 CalenderSet();
    
    		 BKP_WriteBackupRegister(BKP_DR2,GetYear());
    
    	     BKP_WriteBackupRegister(BKP_DR3,GetMonth());  
    
    	     BKP_WriteBackupRegister(BKP_DR4,GetDay());
    
    
            /*实时时钟设置操作:*/
            RTC_WaitForLastTask();
    
            RTC_SetCounter(RTC_TimeCollate());	// 进行设置
    
            RTC_WaitForLastTask();
    
            BKP_WriteBackupRegister(BKP_DR1,0x0229);
    
    		
        }
        else
        {
            if (RCC_GetFlagStatus(RCC_FLAG_PORRST)!=RESET)
            {
    
                printf("\r\n\n 掉电重启事件....");
            }
            else  if (RCC_GetFlagStatus(RCC_FLAG_PINRST)!=RESET)
            {
    
                printf("\r\n\n 外部复位事件....");
            }
            printf("\r\n 等待时间同步....");
           
    		 /*在备份寄存器中重新读出年月日*/
    	   	 Year=BKP_ReadBackupRegister(BKP_DR2);
    
    		 Month=BKP_ReadBackupRegister(BKP_DR3);
    
    	   	 Day=BKP_ReadBackupRegister(BKP_DR4);
    
    	    /*在读取RTC寄存器时,RTC的APB1接口曾经处于禁止状态,则软件首先必须等待
             RTC_CRL寄存器中的RSF 位(寄存器同步标志)被硬件置’1’ 。
            注:    RTC的 APB1 接口不受WFI和WFE等低功耗模式的影响。 */
            RTC_WaitForSynchro(); //  同步
    
            RTC_WaitForLastTask();
    
            RTC_ITConfig(RTC_IT_SEC,ENABLE);// 使能秒中断
    
            RTC_WaitForLastTask();
    
        }
        RCC_ClearFlag();
    }
    /硬件初始化
    /***************************************************************************************************
    *\Function      GetYear()
    *\Description   获得年数
    *\Parameter     void
    *\Return        uint16_t Year
    *\Note          
    *\Log          2014年7月5日
    *     
    ***************************************************************************************************/
    uint16_t GetYear()
    {
    	 return Year;
    
    }
    /***************************************************************************************************
    *\Function      GetMonth()
    *\Description   获得月数
    *\Parameter     void
    *\Return        uint8_t Month
    *\Note          
    *\Log          2014年7月5日
    *     
    ***************************************************************************************************/
    uint8_t GetMonth()
    {
    	 return Month;
    
    }
    /***************************************************************************************************
    *\Function      GetDay()
    *\Description   获得月数
    *\Parameter     void
    *\Return        uint8_t Day
    *\Note          
    *\Log          2014年7月5日
    *     
    ***************************************************************************************************/
    uint8_t GetDay()
    {  
    	  return Day; 
    }
    /***************************************************************************************************
    *\Function      Choice_MonthDay(uint16_t temp_year,uint8_t temp_month)
    *\Description   选择月份天数
    *\Parameter     uint16_t temp_year  
    *\Parameter     uint8_t temp_month
    *\Return        Month_day
    *\Note          
    *\Log          2014年7月5日
    *     
    ***************************************************************************************************/
    static uint8_t Choice_MonthDay(uint16_t temp_year,uint8_t temp_month)
    {
    	uint8_t	Month_day=0;
    	switch(temp_month)
    	{
    	   	case 1:case 3:case 5:case 7:case 8:	case 10:case 12:
    		Month_day=31;
    		break;
    		case 4:	case 6:	case 9:	case 11:
    		Month_day=30;
    		break;
    		case 2:
    		if(temp_year%4==0&&temp_year%100!=0||temp_year%400==0)
    		Month_day=29;
    		else
    		Month_day=28;
    		default:
    		break;		 	
    	}
    	return Month_day;
    }
    
    /***************************************************************************************************
    *\Function      CalenderCount()
    *\Description   日历计数函数
    *\Parameter     void
    *\Return        void
    *\Note          
    *\Log          2014年7月5日
    *     
    ***************************************************************************************************/
    void  CalenderCount(void)
    {
    	if(Day>0&&Day<Choice_MonthDay(Year,Month))
    	{		 	
    	   Day++;
    	}
    	else
    	{
    	  Month++;
    	  if(Month>12)
    	  {
    	  	 Month=1;
    		 Year++;
    	  }
    	  Day=1;	
    	} 
    }
    /***************************************************************************************************
    *\Function      CalenderInput()
    *\Description   用户日历校对输入函数
    *\Parameter     void
    *\Return        void
    *\Note          
    *\Log          2014年7月5日
    *     
    ***************************************************************************************************/
    uint8_t CalenderInput()
    {
       
        RTCInterruptFlag=0;
        RTC_TimeNum=0;
        RTC_ITConfig(RTC_IT_SEC,DISABLE);   // 失能能秒中断
    
    	/* 防止第一个字符无法发出,关闭发送中断  */
        if (USART_GetITStatus(USART2,USART_IT_TXE)!=RESET)
        {
            USART_ITConfig(USART2,USART_IT_TXE,DISABLE);
        }
    	/*等待用户输入数据*/
        while (!RTCInterruptFlag)
        {
            printf("");	
        }   
     
        RTC_ITConfig(RTC_IT_SEC,ENABLE);// 失能能秒中断	
    
        return RTC_TimeNum;
    }		   
    /***************************************************************************************************
    *\Function      RTC_InputTime(uint32_t border)
    *\Description   用户时间校时输入函数
    *\Parameter     uint32_t border	   边界大小
    *\Return        uint8_t
    *\Note          
    *\Log          	2014年7月24日
    ***************************************************************************************************/
    uint8_t RTC_InputTime(uint32_t border)
    {
       
        RTCInterruptFlag=0;
        RTC_TimeNum=0;
        RTC_ITConfig(RTC_IT_SEC,DISABLE);   // 失能能秒中断
    
    	/* 防止第一个字符无法发出,关闭发送中断  */
        if (USART_GetITStatus(USART2,USART_IT_TXE)!=RESET)
        {
            USART_ITConfig(USART2,USART_IT_TXE,DISABLE);
        }
    	/*等待用户输入数据*/
        while (!RTCInterruptFlag)
        {
            printf("");	// 个人理解,因为RTC时间设置和数据输入都是用的USAET2 这样做是为了防止优先级被占用,不加的话while将不起作用
        }  
        printf("\n您键入的数值是:");   
        if (RTC_TimeNum > border)
        {
            printf("\n\r请键入0到%d之间的数字", border);
            return 0xFF;
        }
        RTC_ITConfig(RTC_IT_SEC,ENABLE);// 失能能秒中断	
    
        return RTC_TimeNum;
    }
    
    /***************************************************************************************************
    *\Function      void RTC_RxIntHandler()
    *\Description   RTC中断处理函数
    *\Parameter     void
    *\Return        void
    *\Note          
    *\Log          	2014年7月24日
    *               放在中断处理中
    ***************************************************************************************************/
    void RTC_RxIntHandler(void)
    {
    
        uint32_t temp_data;
        RTCInterruptFlag=1;
        if (USART_GetFlagStatus(USART2,USART_IT_RXNE)!=RESET)
        {
            temp_data=(USART_ReceiveData(USART2));
            RTC_TimeNum=(temp_data>>4)*10+(temp_data&0x0F);	// 设置时间变量
          //  printf("当前的temp_data %d",RTC_Num);
        }					   
        if (USART_GetITStatus(USART2,USART_IT_TXE)!=RESET)
        {
            USART_ITConfig(USART2,USART_IT_TXE,DISABLE);
        }			   
    }
    
    ///以上为底层函数//
    
    /***************************************************************************************************
    *\Function      CalenderSet(void)
    *\Description   日历设置函数
    *\Parameter     void
    *\Return        void
    *\Note         用于串口输入 
    *\Log          2014年7月5日
    *     
    ***************************************************************************************************/
    void CalenderSet(void)
    {
    	 printf("\n=========设置年月日=================:");
    	 printf("\n请输入年份:");
    	 Year=(uint16_t)CalenderInput()*100;
    	 Year=Year+CalenderInput();
    	 printf("\n您输入的年份是:%d",Year);
    	 printf("\n\n请输入月份:");
    	 Month=CalenderInput();
    	 printf("\n您输入的月份是:%d",Month);
    	 printf("\n\n请输入日期:");
    	 Day=CalenderInput();
    	 printf("\n您输入的日期是:%d",Day);
    }
    
    /****************************************************************************
    * 名    称:uint32_t RTC_TimeCollate(void)
    * 功    能:时间校正函数
    * 入口参数:无
    * 出口参数:uint32_t
    * 说    明:用于串口输入
    * 调用方法:
    ****************************************************************************/
    uint32_t RTC_TimeCollate(void)
    {
        uint32_t Tmp_HH = 0xFF, Tmp_MM = 0xFF, Tmp_SS = 0xFF; 
        printf("\r\n==============时间设置=========================");
        printf("\r\n  请输入小时:");  
        while (Tmp_HH == 0xFF)
        {
            Tmp_HH = RTC_InputTime(23);
        }
        printf(":  %d", Tmp_HH);
        printf("\r\n  请输入分钟:");
        while (Tmp_MM == 0xFF)
        {
            Tmp_MM = RTC_InputTime(59);
        }
        printf(":  %d", Tmp_MM);
        printf("\r\n  请输入秒数:");
        while (Tmp_SS == 0xFF)
        {
            Tmp_SS = RTC_InputTime(59);
        }
        printf(":  %d", Tmp_SS);		  
        /* 返回保存在RTC计数寄存器里的值 */
        return((Tmp_HH*3600 + Tmp_MM*60 + Tmp_SS));
    }	  
    
    
    
    /****************************************************************************
    * 名    称:void RTC_TimeDisplay(uint32_t TimeVar)
    * 功    能:显示当前时间
    * 入口参数:无
    * 出口参数:无
    * 说    明:
    * 调用方法:
    ****************************************************************************/
    void RTC_TimeDisplay(uint32_t TimeVar)
    {
        uint32_t THH = 0, TMM = 0, TSS = 0;
    
        /* 计算小时 */
        THH = TimeVar/3600;
        /* 计算分钟 */
        TMM = (TimeVar % 3600)/60;
        /* 计算秒 */
        TSS = (TimeVar % 3600)% 60;
        printf("\n%d年 %d月 %d日  ",Year,Month,Day);
        printf("Time: %0.2d:%0.2d:%0.2d\r\n",THH, TMM, TSS);
    }
    
    
     ///以上为应用层函数/
    
    
    
    
    
    /***************************************************************************************************
    *\Function      RTC_text(void)
    *\Description   时间显示
    *\Parameter     void
    *\Return        void
    *\Note          
    *\Log          	时间
    *              
    ***************************************************************************************************/
    void RTC_text(void)
    {
        printf("\n\r");
        while (1)
        {
            /* 秒更新发生 */
            if(TimeDisplay == 1)
            {
                /* 显示当前时间 */
                RTC_TimeDisplay(RTC_GetCounter());
                TimeDisplay = 0;
            }
        }
    }
    



    stm32f10x_it.c
    /*******************************************************************************
    * Function Name  : RTC_IRQHandler
    * Description    : This function handles RTC global interrupt request.
    * Input          : None
    * Output         : None
    * Return         : None
    *******************************************************************************/
    void RTC_IRQHandler(void)
    {
      if(RTC_GetITStatus(RTC_IT_SEC) != RESET)				 //读取秒中断状态
      {
        RTC_ClearITPendingBit(RTC_IT_SEC);					 //清除秒中断标志			    
    
        /* 时钟更新标志置位 */
        TimeDisplay = 1;	  
        RTC_WaitForLastTask();							     //等待上一次对RTC寄存器的写操作是否已经完成    
        if(RTC_GetCounter() == 0x0001517F)				     //当前时间是23:59:59时 复位为0:0:0 	    
        {
    	  RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
    	  PWR->CR|=1<<8;                  					 //取消备份区写保护
    
    	  RTC_EnterConfigMode();						     //允许配置 	  				
    	  RTC_WaitForLastTask();                             //等待上一次对RTC寄存器的写操作是否已经完成 
          RTC_SetCounter(0x0);								 //写入复位值
          RTC_WaitForLastTask();							 //等待上一次对RTC寄存器的写操作是否已经完成 
    	  CalenderCount();
    	  BKP_WriteBackupRegister(BKP_DR2,GetYear());
    	  BKP_WriteBackupRegister(BKP_DR3,GetMonth());  
    	  BKP_WriteBackupRegister(BKP_DR4,GetDay());
    	   
        }
    	else if(RTC_GetCounter() > 0x0001517F)				 //当再次上电后计数值超过0x00015180, 复位为当前值取模0x00015180。	    
        {
    	  RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
    	  PWR->CR|=1<<8;                                     //取消备份区写保护
    	  RTC_EnterConfigMode();			                 //允许配置 
    	  RTC_WaitForLastTask();                             //等待上一次对RTC寄存器的写操作是否已经完成    
          RTC_SetCounter(RTC_GetCounter()%0x0001517F);		 //写入复位值
          RTC_WaitForLastTask();							 //等待上一次对RTC寄存器的写操作是否已经完成 
    	   CalenderCount();
    	  BKP_WriteBackupRegister(BKP_DR2,GetYear());
    	  BKP_WriteBackupRegister(BKP_DR3,GetMonth());  
    	  BKP_WriteBackupRegister(BKP_DR4,GetDay());
    	  
    	     
        }
      }
            
    }


    展开全文
  •  直接操作寄存器中,可以自由设定这个时间戳起始的年份,RTC的32位寄存器存储的只是距离这个起始年份的总秒数,所以不会遇到这个问题。而且可以用无符号32位的二进制表示时间,这意味着此类系统的时间戳可以表示更...
  • 2) BKP备份寄存器特征与原理; 3) RTC常用寄存器+库函数介绍; 4) 相关实验代码解读。 官方资料:《STM32中文参考手册V10》第16章——实时时钟(RTC)和第5章——备份寄存器 1. RTC实时时钟特征与原理 1.1 RTC ...
  • 【这里尚未对当前操作位做特殊操作,比如年份闪烁表示正在对其进行操作,之后补充】 关键代码 秒表 /****************普通按键初始化函数******************** * 通用定时器中断初始化 * 这里时钟选择为APB1的...
  • 资源内容包括: RTC初始化;RTC设置和读取(RTC_Set,RTC_Get),RTC闹钟设置和读取(RTC_Alarm_Set)RTC周的计算(RTC_Get_Week),RTC中断配置(RTC_NVIC_Config,RTC_IRQHandler)等RTC综合灵活的应用方式 ...
  • (详情可以参照上一篇教程图片与文末链接) 第五行,把1赋值给P2,就是第一个数,(举个例子:二进制1111 最后一个数是1倒数第二个是2倒数第三个是4) 第六行,将累加器A中的数据传送到p1寄存器,也就是p1端口,就是...
  • (4)【◆题库问题◆】:[单选] ()包括运算器、控制器和寄存器。 A.算术逻辑单元 B.累加器 C.CPU D.主板 【◆参考答案◆】:C ·ℳ°.·※°∴ ╰☆╮ 。·ℳ°.·※°∴ ╰☆╮ 。·ℳ°.·※°∴ ╰☆╮ 。...
  • 打印任意年份的日历。 #include&lt;stdio.h&gt; int main() { int y; int a[2][13]={{0,31,28,31,30,31,30,31,31,30,31,30,31}, {0,31,29,31,30,31,30,31,31,30,31,30,31}}; int i,j; scanf(&...
  • 从键盘输入一个表示年份的正整数(1~65535),然后判断其是否为闰年。若是,则输出"Yes",否则,输出"No"。 思路: 主程序部分实现字符串数字的输入并输出提示信息。通过调用字符串转数字子程序string2num_2实现字符...
  • 1625-5 王子昂 总结《2017年4月7日》 【连续第188天总结】 A. 汇编第八章实验 100% ...慢慢理清思路,由于年份和总收入都占4个字节,而人数只占2个字节,因此不可能同用一个寄存器来表示(因为长度不
  • STM32F4 HAL库开发 -- RTC

    2021-07-19 17:00:10
    两个32位寄存器(TR和DR)包含二进码十进制格式(BCD)的秒、分钟、小时(12或24小时制)、星期、日期、月份和年份。此外,还可提供二进制格式的亚秒值。 STM32F429 的 RTC 可以自动将月份的天数补偿为 28、 29...
  • Linux Performance

    千次阅读 2020-10-08 13:46:53
    性能专家Brendan Gregg的网站。...在图像上检查年份(右下)以查看年份。 还有一个高分辨率图,它结合了可观察性,静态性能调整和perf-tools / bcc:png,svg(请参见讨论),但是它不如其他图完整。有关更多图表...
  • STM32程序风格: 采用寄存器方式开发,注释齐全,执行效率高,方便移植 手机APP: 采用QT设计,程序支持跨平台编译运行(Android、IOS、Windows、Linux都可以编译运行,对应平台上QT的环境搭建,之前博客已经发了文章...
  • 描述 德州仪器(TI)MSP430™系列的超低功耗微控器(MCU)包含多款器件,这些器件配备了不同的外设集以满足各类应用的需求。该架构可与五种低功耗模式配合使用,专...该器件具有功能强大的16位RISC CPU,16位寄存器...
  • 汇编学习--第七天

    2019-06-26 01:02:00
    1:数据寄存器,一般称之为通用寄存器组 8086 有8个8位数据寄存器, 这些8位寄存器可分别组成16位寄存器: AH&AL=AX:累加寄存器,常用于运算; BH&BL=BX:基址寄存器,常用于地址索引; CH&CL=CX...
  • 单片机 74HC595

    2019-04-20 15:27:23
    74HC595是带有存储寄存器和三态输出的8位串行移位寄存器,移位寄存器和存储寄存器有各自的时钟。 每当移位寄存器输入时钟SHCP上升沿来临之时,数据被移出。 每当存储寄存器输入时钟STCP上升沿来临之时,数据并行的...
  • 汇编语言一补充

    2016-09-21 01:08:16
    常见寄存器汇总 AH&AL=AX(accumulator):累加寄存器 BH&BL=BX(base):基址寄存器 CH&CL=CX(count):计数寄存器 DH&DL=DX(data):数据寄存器 SP(Stack Pointer):堆栈指针寄存器 BP(Base Pointer):基址...
  • STM32开发 -- RTC详解

    万次阅读 多人点赞 2018-08-04 12:31:26
    因此,若在读取RTC寄存器时, RTC的APB1接口曾经处于禁止状态,则软件首先必须等待RTC_CRL寄存器中的RSF位(寄存器同步标志)被硬件置’1’。 注: RTC的 APB1接口不受WFI和WFE等低功耗模式的影响 六、RTC相关...
  • 因为其中用到很多的寄存器,但是每个寄存器都有限制规则,所以要精心分配寄存器。 最先分配的是两个段寄存器,data用ds存放, table用es存放。 然后要确定循环使用的寄存器cx; 然后要精心分配指向table和data...
  • 如上图,身份证中第7~10这4位数表示的是出生年份,那么,我们可以这么执行: 检查想要进城的所有人的身份证号码的第7~10位数字,如果这个数字依次为1156则可以进入,否则则不可以,至于身份证号码的其他位则完全不...
  • (一)、8086汇编学习之基础知识、通用寄存器、CS/IP寄存器与Debug的使用 (二)、8086汇编学习之DS寄存器、SS/SP寄存器 (三)、8086汇编学习之[BX],CX寄存器与loop指令,ES寄存器等 (四)、 8086汇编学习之...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,985
精华内容 794
关键字:

年份寄存器