基于nRF52810的实时时钟的显示(万年历)

  1. 简介:

NRF52832 中的 RTC 是 Real-time Counter 实时计数器,而不是 Real-time Clock 实时时间 。所以为了实现实时时钟,需要通过RTC0的比较事件产生一个1s的计时,在每个中断事件中更新时间结构体t。

实现万年历功能有三种方式:

  • 新建一个1秒定时的APP_TIMER。
  • 优点:创建方便。
  • 缺点:实时性太差。
  • APP_TIMER采用轮询执行而非抢占的方式,假如其它APP_TIMER耗时较长,例如有一个APP_TIMER在采集心率,万年历的APP_TIMER就必须等采集心率完成才能执行。
  • 在APP_TIMER的中断中插入计算时间戳的代码
  • 优点:修改代码较少。
  • 缺点:代码耦合性高,易出现未知问题。
  • 我们都知道APP_TIMER的实现就是使用了RTC1,那么我们就可以利用上RTC1的特点,适当修改代码来实现我们的需求,但这样容易造成代码耦合性高,不易维护和管理。
  • 用一个新的RTC来实现。
  • 优点:不会影响原来APP_TIMER的功能,模块独立,方便管理。
  • 缺点:需要深入了解芯片的RTC,根据RTC特性来实现自己的功能。

————————————————
版权声明:本文为CSDN博主「Leung_ManWah」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_36347513/article/details/103893124

设计思路:

一、由RTC0的比较事件产生一个1s的计时

void nrf_cal_init(void)
{
    //使用外部32.768 kHz 晶体振荡器作为低频时钟源
    NRF_CLOCK->LFCLKSRC = CLOCK_LFCLKSRC_SRC_Xtal << CLOCK_LFCLKSRC_SRC_Pos;
	  //LFCLK启动完成事件清零
    NRF_CLOCK->EVENTS_LFCLKSTARTED = 0;
	  //启动LFCLK
    NRF_CLOCK->TASKS_LFCLKSTART = 1;
	  //等待LFCLK启动完成
    while(NRF_CLOCK->EVENTS_LFCLKSTARTED == 0);
    
    //配置RTC每秒产生一次比较匹配事件
    CAL_RTC->PRESCALER = 4095;
    CAL_RTC->EVTENSET = RTC_EVTENSET_COMPARE0_Msk;
    CAL_RTC->INTENSET = RTC_INTENSET_COMPARE0_Msk;
    CAL_RTC->CC[0] = 8;     
    CAL_RTC->TASKS_START = 1;
    NVIC_SetPriority(CAL_RTC_IRQn, CAL_RTC_IRQ_Priority);
    NVIC_EnableIRQ(CAL_RTC_IRQn);  
}
//设置时间
void nrf_cal_set_time(uint32_t year, uint32_t month, uint32_t day, uint32_t hour, uint32_t minute, uint32_t second)
{
    t.tm_year = year - 1900;
    t.tm_mon = month;
    t.tm_mday = day;
    t.tm_hour = hour;
    t.tm_min = minute;
    t.tm_sec = second;   
    CAL_RTC->TASKS_CLEAR = 1;  
}

//使用strftime格式化时间
char *nrf_cal_get_time_string(void)
{
    static char cal_string[80];
	  strftime(cal_string, 80, "%Y-%m-%d-%H:%M:%S ", &t);
    return cal_string;
}
//判断是不是闰年:能整除4但不能整除100,能整除400
static char not_leap(void)     
{
	if (!(t.tm_year%100))
	{
		return (char)(t.tm_year%400);
	}
	else
	{
		return (char)(t.tm_year%4);
	}
}

void CAL_updata(void)
{
	TimeUpdataFlag = UPDATA_SEC;
	if (++t.tm_sec==60)        //keep track of time, date, month, and year
	{
		TimeUpdataFlag = UPDATA_HM;
		t.tm_sec=0;
		if (++t.tm_min==60)
		{
			t.tm_min=0;
			if (++t.tm_hour==24)
			{
				TimeUpdataFlag = UPDATA_DATE;
				t.tm_hour=0;
				if (++t.tm_mday==32)
				{
					t.tm_mon++;
					t.tm_mday=1;
				}
				else if (t.tm_mday==31)
				{
					//4,6,9,11是小月(2月单独处理),一月30天
					if ((t.tm_mon==(4-1)) || (t.tm_mon==(6-1)) || (t.tm_mon==(9-1)) || (t.tm_mon==(11-1)))
					{
						t.tm_mon++;
						t.tm_mday=1;
					}
				}
				else if (t.tm_mday==30)//2月是闰月,2月有29天
				{
					if(t.tm_mon==2)
					{
						t.tm_mon++;
						t.tm_mday=1;
					}
				}
				else if (t.tm_mday==29)//2月不是闰月,2月有28天
				{
					if((t.tm_mon==2) && (not_leap()))
					{
						t.tm_mon++;
						t.tm_mday=1;
					}
				}
				if (t.tm_mon==13)
				{
					t.tm_mon=1;
					t.tm_year++;
				}
			}
		}
	}	
}

//RTC中断服务函数
void CAL_RTC_IRQHandler(void)
{
    //如果是RTC0通道0的比较匹配事件
	  if(CAL_RTC->EVENTS_COMPARE[0])
    {
        //清零事件
			  CAL_RTC->EVENTS_COMPARE[0] = 0;
        //清零计数器
        CAL_RTC->TASKS_CLEAR = 1;
			  //更新时间
        CAL_updata();
    }
}

二、定义一个时间结构体的变量t,记录时间,同时定义一个时间标志位(时间标志位的作用是用来刷新函数时的判断标准)

struct tm {
    int tm_sec;   /* seconds after the minute, 0 to 60
                     (0 - 60 allows for the occasional leap second) */
    int tm_min;   /* minutes after the hour, 0 to 59 */
    int tm_hour;  /* hours since midnight, 0 to 23 */
    int tm_mday;  /* day of the month, 1 to 31 */
    int tm_mon;   /* months since January, 0 to 11 */
    int tm_year;  /* years since 1900 */
    int tm_wday;  /* days since Sunday, 0 to 6 */
    int tm_yday;  /* days since January 1, 0 to 365 */
    int tm_isdst; /* Daylight Savings Time flag */
    union {       /* ABI-required extra fields, in a variety of types */
        struct {
            int __extra_1, __extra_2;
        };
        struct {
            long __extra_1_long, __extra_2_long;
        };
        struct {
            char *__extra_1_cptr, *__extra_2_cptr;
        };
        struct {
            void *__extra_1_vptr, *__extra_2_vptr;
        };
    };
};

三、oled更新显示。

void oled_updata(uint8_t flag)
{
    uint8_t i;
	  char *p;
	  //获取格式化的时间字符串
	  p = nrf_cal_get_time_string();
	  //根据时间更新标志更新OLED显示内容
	  switch(flag)
	  {
			//更新秒显示:只需更新秒显示即可
			case UPDATA_SEC:
				//更新秒显示		
				LCD_timestr(48,2,(uint8_t*)(p+17));
				LCD_timestr(56,2,(uint8_t*)(p+18));
				break;
				//更新时和分显示:需要更新秒显示和时分显示
			case UPDATA_HM:
				//更新秒显示
				LCD_timestr(48,2,(uint8_t*)(p+17));
				LCD_timestr(56,2,(uint8_t*)(p+18));
			  //更新时分显示
				LCD_timestr(0,2,(uint8_t*)(p+11));
				LCD_timestr(8,2,(uint8_t*)(p+12));
				LCD_timestr(16,2,(uint8_t*)(p+13));
				LCD_timestr(24,2,(uint8_t*)(p+14));
				LCD_timestr(32,2,(uint8_t*)(p+15));
//				LCD_timestr(40,2,(uint8_t*)(p+16));
				break;
			//更新年月日显示:需要更新年月日和时分秒显示
			case UPDATA_DATE:
			  //更新秒显示
			  LCD_timestr(48,2,(uint8_t*)(p+17));
			  LCD_timestr(56,2,(uint8_t*)(p+18));	
			  //更新时分显示
			  LCD_timestr(0,2,(uint8_t*)(p+11));
	          LCD_timestr(8,2,(uint8_t*)(p+12));
			  LCD_timestr(16,2,(uint8_t*)(p+13));
	          LCD_timestr(24,2,(uint8_t*)(p+14));
	          LCD_timestr(32,2,(uint8_t*)(p+15));
				//更新年月日显示		
			  for(i=0;i<10;i++)
	      {
		        LCD_timestr(0+8*i,0,(uint8_t*)(p+i));		
	      }
			//	break;
			
			default:
				break;		
	  } 	
}

 主函数:

void main
{	 
    while (true)
	{
		    //更新OLED显示
			//检查时间更新标志
			if(TimeUpdataFlag)
			{
				 oled_updata(TimeUpdataFlag);
				 TimeUpdataFlag = 0;
			}
	}
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值