i.MX6ULL终结者SRTC实时时钟例程程序设计

it2026-01-30  4

本实验的源码工程在开发板光盘资料的:i.MX6UL终结者光盘资料\04_裸机例程源码\15_rtc 目录下。我们在Ubuntu系统下使用命令“mkdir 15_rtc”建立“15_rtc”文件夹,如图1所示:

图 1

然后使用“cd 15_rtc”命令进入到15_rtc文件夹,如图 2所示:

图 2

然后使用命令“cp -r …/14_lcd/* ./”将上一章试验中的所有内容拷贝到刚刚新建的“15_rtc”里面,如图 3所示:

图 3

首先我们需要添加SNVS_Type结构体定义,打开“core/MCIMX6Y2.h”文件,在36308行添加下面的代码:

36308 /** SNVS - Register Layout Typedef */ 36309 /* zuozhongkai 2018/12/13 */ 36310 typedef struct { 36311 __IO uint32_t HPLR; /**< SNVS_HP Lock register, offset: 0x0 */ 36312 __IO uint32_t HPCOMR; /**< SNVS_HP Command register, offset: 0x4 */ 36313 __IO uint32_t HPCR; /**< SNVS_HP Control register, offset: 0x8 */ 36314 __IO uint32_t HPSICR; /**< SNVS_HP Control register, offset: 0x8 */ 36315 __IO uint32_t HPSVCR; 36316 __IO uint32_t HPSR; 36317 __IO uint32_t HPSVSR; 36318 __IO uint32_t HPHACIVR; 36319 __IO uint32_t HPHACR; 36320 __IO uint32_t HPRTCMR; 36321 __IO uint32_t HPRTCLR; 36322 __IO uint32_t HPTAMR; 36323 __IO uint32_t HPTALR; 36324 __IO uint32_t LPLR; 36325 __IO uint32_t LPCR; 36326 __IO uint32_t LPMKCR; 36327 __IO uint32_t LPSVCR; 36328 __IO uint32_t LPTGFCR; 36329 __IO uint32_t LPTDCR; 36330 __IO uint32_t LPSR; 36331 __IO uint32_t LPSRTCMR; 36332 __IO uint32_t LPSRTCLR; 36333 __IO uint32_t LPTAR; 36334 __IO uint32_t LPSMCMR; 36335 __IO uint32_t LPSMCLR; 36336 }SNVS_Type;

然后在drivers目录下建立rtc文件夹,用来保存rtc的驱动文件,然后在“drivers/rtc”目录中新建rtc.h和rtc.c两个文件,然后在rtc.h文件输入下面的代码:

1 #ifndef _BSP_RTC_H 2 #define _BSP_RTC_H 3 4 #include "imx6ul.h" 5 6 /* 相关宏定义 */ 7 #define SECONDS_IN_A_DAY (86400) /* 一天86400秒 */ 8 #define SECONDS_IN_A_HOUR (3600) /* 一个小时3600秒 */ 9 #define SECONDS_IN_A_MINUTE (60) /* 一分钟60秒 */ 10 #define DAYS_IN_A_YEAR (365) /* 一年365天 */ 11 #define YEAR_RANGE_START (1970) /* 开始年份1970年 */ 12 #define YEAR_RANGE_END (2099) /* 结束年份2099年 */ 13 14 15 /* 时间日期结构体 */ 16 struct rtc_datetime 17 { 18 unsigned short year; /* 范围为:1970 ~ 2099 */ 19 unsigned char month; /* 范围为:1 ~ 12 */ 20 unsigned char day; /* 范围为:1 ~ 31 (不同的月,天数不同).*/ 21 unsigned char hour; /* 范围为:0 ~ 23 */ 22 unsigned char minute; /* 范围为:0 ~ 59 */ 23 unsigned char second; /* 范围为:0 ~ 59 */ 24 }; 25 26 27 /* 函数声明 */ 28 void rtc_init(void); 29 void rtc_enable(void); 30 void rtc_disable(void); 31 unsigned int rtc_coverdate_to_seconds(struct rtc_datetime *datetime); 32 unsigned int rtc_getseconds(void); 33 void rtc_setdatetime(struct rtc_datetime *datetime); 34 void rtc_getdatetime(struct rtc_datetime *datetime) 35 ; 36 37 #endif

第7行到第12行定义了一些和时间有关的宏,比如一天有多少秒,一小时有多少秒,一年有多少天等等。第16行定义了一个结构体,用来描述时间日期,然后28行到34行是一些函数的声明。 然后打开rtc.c文件,在里面输入下面的代码:

1 #include "rtc.h" 2 #include "stdio.h" 3 4 /* 5 * 描述:初始化RTC 6 */ 7 void rtc_init(void) 8 { 9 /* 10 * 设置HPCOMR寄存器 11 * bit[31] 1 : 允许访问SNVS寄存器,一定要置1 12 * bit[8] 1 : 此位置1,需要签署NDA协议才能看到此位的详细说明, 13 * 这里不置1也没问题 14 */ 15 SNVS->HPCOMR |= (1 << 31) | (1 << 8); 16 17 #if 0 18 struct rtc_datetime rtcdate; 19 20 rtcdate.year = 2018U; 21 rtcdate.month = 12U; 22 rtcdate.day = 13U; 23 rtcdate.hour = 14U; 24 rtcdate.minute = 52; 25 rtcdate.second = 0; 26 rtc_setDatetime(&rtcdate); //初始化时间和日期 27 #endif 28 29 rtc_enable(); //使能RTC 30 31 } 32 33 /* 34 * 描述: 开启RTC 35 */ 36 void rtc_enable(void) 37 { 38 /* 39 * LPCR寄存器bit0置1,使能RTC 40 */ 41 SNVS->LPCR |= 1 << 0; 42 while(!(SNVS->LPCR & 0X01));//等待使能完成 43 44 } 45 46 /* 47 * 描述: 关闭RTC 48 */ 49 void rtc_disable(void) 50 { 51 /* 52 * LPCR寄存器bit0置0,关闭RTC 53 */ 54 SNVS->LPCR &= ~(1 << 0); 55 while(SNVS->LPCR & 0X01);//等待关闭完成 56 } 57 58 /* 59 * @description : 判断指定年份是否为闰年,闰年条件如下: 60 * @param - year: 要判断的年份 61 * @return : 1 是闰年,0 不是闰年 62 */ 63 unsigned char rtc_isleapyear(unsigned short year) 64 { 65 unsigned char value=0; 66 67 if(year % 400 == 0) 68 value = 1; 69 else 70 { 71 if((year % 4 == 0) && (year % 100 != 0)) 72 value = 1; 73 else 74 value = 0; 75 } 76 return value; 77 } 78 79 /* 80 * @description : 将时间转换为秒数 81 * @param - datetime: 要转换日期和时间。 82 * @return : 转换后的秒数 83 */ 84 unsigned int rtc_coverdate_to_seconds(struct rtc_datetime *datetime) 85 { 86 unsigned short i = 0; 87 unsigned int seconds = 0; 88 unsigned int days = 0; 89 unsigned short monthdays[] = {0U, 0U, 31U, 90 59U, 90U, 120U, 91 151U, 181U, 212U, 92 243U, 273U, 304U, 334U}; 93 94 for(i = 1970; i < datetime->year; i++) 95 { 96 days += DAYS_IN_A_YEAR; /* 平年,每年365天 */ 97 if(rtc_isleapyear(i)) days += 1;/* 闰年多加一天 */ 98 } 99 100 days += monthdays[datetime->month]; 101 if(rtc_isleapyear(i) && (datetime->month >= 3)) 102 days += 1;/* 闰年,并且当前月份大于等于3月的话加一天 */ 103 104 days += datetime->day - 1; 105 106 seconds = days * SECONDS_IN_A_DAY + 107 datetime->hour * SECONDS_IN_A_HOUR + 108 datetime->minute * SECONDS_IN_A_MINUTE + 109 datetime->second; 110 111 return seconds; 112 } 113 114 /* 115 * @description : 设置时间和日期 116 * @param - datetime: 要设置的日期和时间 117 * @return : 无 118 */ 119 void rtc_setdatetime(struct rtc_datetime *datetime) 120 { 121 122 unsigned int seconds = 0; 123 unsigned int tmp = SNVS->LPCR; 124 125 rtc_disable(); /* 设置寄存器HPRTCMR和HPRTCLR的时候一定要先关闭RTC */ 126 127 128 /* 先将时间转换为秒 */ 129 seconds = rtc_coverdate_to_seconds(datetime); 130 131 SNVS->LPSRTCMR = (unsigned int)(seconds >> 17); /* 设置高16位 */ 132 SNVS->LPSRTCLR = (unsigned int)(seconds << 15); /* 设置地16位 */ 133 134 /* 如果此前RTC是打开的在设置完RTC时间以后需要重新打开RTC */ 135 if (tmp & 0x1) 136 rtc_enable(); 137 } 138 139 /* 140 * @description : 将秒数转换为时间 141 * @param - seconds : 要转换的秒数 142 * @param - datetime: 转换后的日期和时间 143 * @return : 无 144 */ 145 void rtc_convertseconds_to_datetime(unsigned int seconds, 146 struct rtc_datetime *datetime) 147 { 148 unsigned int x; 149 unsigned int secondsRemaining, days; 150 unsigned short daysInYear; 151 152 /* 每个月的天数 */ 153 unsigned char daysPerMonth[] = {0U, 31U, 28U, 154 31U, 30U, 31U, 155 30U, 31U, 31U, 156 30U, 31U, 30U, 31U}; 157 158 secondsRemaining = seconds; /* 剩余秒数初始化 */ 159 days = secondsRemaining / SECONDS_IN_A_DAY + 1; /* 根据秒数计算天数,加1是当前天数 */ 160 secondsRemaining = secondsRemaining % SECONDS_IN_A_DAY; /*计算天数以后剩余的秒数 */ 161 162 /* 计算时、分、秒 */ 163 datetime->hour = secondsRemaining / SECONDS_IN_A_HOUR; 164 secondsRemaining = secondsRemaining % SECONDS_IN_A_HOUR; 165 datetime->minute = secondsRemaining / 60; 166 datetime->second = secondsRemaining % SECONDS_IN_A_MINUTE; 167 168 /* 计算年 */ 169 daysInYear = DAYS_IN_A_YEAR; 170 datetime->year = YEAR_RANGE_START; 171 while(days > daysInYear) 172 { 173 /* 根据天数计算年 */ 174 days -= daysInYear; 175 datetime->year++; 176 177 /* 处理闰年 */ 178 if (!rtc_isleapyear(datetime->year)) 179 daysInYear = DAYS_IN_A_YEAR; 180 else /*闰年,天数加一 */ 181 daysInYear = DAYS_IN_A_YEAR + 1; 182 } 183 /*根据剩余的天数计算月份 */ 184 if(rtc_isleapyear(datetime->year)) /* 如果是闰年的话2月加一天 */ 185 daysPerMonth[2] = 29; 186 187 for(x = 1; x <= 12; x++) 188 { 189 if (days <= daysPerMonth[x]) 190 { 191 datetime->month = x; 192 break; 193 } 194 else 195 { 196 days -= daysPerMonth[x]; 197 } 198 } 199 200 datetime->day = days; 201 202 } 203 204 /* 205 * @description : 获取RTC当前秒数 206 * @param : 无 207 * @return : 当前秒数 208 */ 209 unsigned int rtc_getseconds(void) 210 { 211 unsigned int seconds = 0; 212 213 seconds = (SNVS->LPSRTCMR << 17) | (SNVS->LPSRTCLR >> 15); 214 return seconds; 215 } 216 217 /* 218 * @description : 获取当前时间 219 * @param - datetime: 获取到的时间,日期等参数 220 * @return : 无 221 */ 222 void rtc_getdatetime(struct rtc_datetime *datetime) 223 { 224 unsigned int seconds = 0; 225 seconds = rtc_getseconds(); 226 rtc_convertseconds_to_datetime(seconds, datetime); 227 }

rtc_init函数是初始化RTC的,主要完成使能RTC。 rtc_enable函数是使能RTC的。 rtc_disable函数是关闭RTC的。 rtc_isleapyear 函数用来判断某一年是否是闰年。 rtc_coverdate_to_seconds函数用于将给定的日期和时间转换为秒数。 rtc_setdatetime函数用于设置时间。 rtc_convertseconds_to_datetime函数用户将给定的秒数转换为时间。 rtc_getseconds函数用户获取RTC的秒数。 rtc_getdatetime函数用于获取时间信息。

然后我们打开main.c文件,在里面输入下面的内容:

1 #include "clk.h" 2 #include "delay.h" 3 #include "led.h" 4 #include "beep.h" 5 #include "key.h" 6 #include "int.h" 7 #include "uart.h" 8 #include "lcd.h" 9 #include "lcdapi.h" 10 #include "rtc.h" 11 #include "stdio.h" 12 13 /* 14 * @description : main函数 15 * @param : 无 16 * @return : 无 17 */ 18 int main(void) 19 { 20 unsigned char key = 0; 21 int i = 3, t = 0; 22 char buf[160]; 23 struct rtc_datetime rtcdate; 24 unsigned char state = OFF; 25 26 27 int_init(); /* 初始化中断(一定要最先调用!) */ 28 imx6u_clkinit(); /* 初始化系统时钟 */ 29 delay_init(); /* 初始化延时 */ 30 clk_enable(); /* 使能所有的时钟 */ 31 led_init(); /* 初始化led */ 32 beep_init(); /* 初始化beep */ 33 uart_init(); /* 初始化串口,波特率115200 */ 34 lcd_init(); /* 初始化LCD */ 35 rtc_init(); /* 初始化RTC */ 36 37 tftlcd_dev.forecolor = LCD_RED; 38 39 lcd_show_string(50, 10, 400, 24, 40 24, (char*)"i.MX6ULL RTC TEST"); /* 显示字符串 */ 41 42 tftlcd_dev.forecolor = LCD_BLUE; 43 44 memset(buf, 0, sizeof(buf)); 45 46 while(1) 47 { 48 if(t == 100) //1s时间到了 49 { 50 t=0; 51 printf("will be running %d s......\r", i); 52 53 lcd_fill(50, 90, 370, 110, tftlcd_dev.backcolor); /* 清屏 */ 54 sprintf(buf, "will be running %ds......", i); 55 lcd_show_string(50, 90, 300, 16, 16, buf); 56 i--; 57 if(i < 0) 58 break; 59 } 60 61 key = key_getvalue(); 62 if(key == KEY0_VALUE) 63 { 64 rtcdate.year = 2020; 65 rtcdate.month = 1; 66 rtcdate.day = 10; 67 rtcdate.hour = 10; 68 rtcdate.minute = 30; 69 rtcdate.second = 0; 70 rtc_setdatetime(&rtcdate); /* 初始化时间和日期 */ 71 printf("\r\n RTC Init finish\r\n"); 72 break; 73 } 74 75 delayms(10); 76 t++; 77 } 78 tftlcd_dev.forecolor = LCD_RED; 79 lcd_fill(50, 90, 370, 110, tftlcd_dev.backcolor); /* 清屏 */ 80 lcd_show_string(50, 90, 200, 16, 16, 81 (char*)"Current Time:"); /* 显示字符串 */ 82 tftlcd_dev.forecolor = LCD_BLUE; 83 84 while(1) 85 { 86 rtc_getdatetime(&rtcdate); 87 sprintf(buf,"%d/%d/%d %d:%d:%d",rtcdate.year, 88 rtcdate.month, rtcdate.day, 89 rtcdate.hour, rtcdate.minute, 90 rtcdate.second); 91 lcd_fill(50,110, 300,130, tftlcd_dev.backcolor); 92 lcd_show_string(50, 110, 250, 93 16, 16,(char*)buf); /* 显示字符串 */ 94 95 state = !state; 96 led_switch(LED0,state); 97 delayms(1000); /* 延时一秒 */ 98 } 99 return 0; 100 }

第35行调用rtc_init函数,初始化RTC时钟。 第48行到第77行倒数秒,如果在3秒内按下了按键,就会调用rtc_setdatetime函数设置RTC的时间,如果3秒内没有按下按键,那就不需要设置时间,跳出while(1)循环继续执行后面的程序。 第84行到第98行是while(1)主循环,每隔1秒钟调用一次rtc_getdatetime获取时间信息,通过串口发送出来,同时显示在LCD屏幕上。

最新回复(0)