RTC 介绍
地奇星的 RTC 外设本质是掉电后仍能运行的定时器,其独特之处在于掉电运行特性:主电源 VDD 断开时,需通过 VBAT 引脚接纽扣电池供电以维持日历计数器计时(VDD 有效时由 VDD 供电,两者均掉电则计时丢失),且 VBAT 供电时仅日历计数功能可用;对于日历计数模式,RTC有一个从2000年到2099年的100年日历,并自动调整闰年的日期。对于二进制计数模式,RTC计数秒数并将信息保留为串行值。二进制count模式可用于公历(西方)日历以外的日历。其计数源可选择子时钟振荡器或 LOCO,使用 128Hz 时钟(由计数源除以预分频器获得),能实现年、月、日等时间单位或 32 位二进制 1/128 秒精度的计数。
RTC 说明
类别 | 子项 | 详细说明 |
---|---|---|
计数模式 | 日历计数模式 | 年、月、日、星期、时、分、秒计数,BCD显示;12/24小时模式切换;30秒调整功能;自动调整闰年 |
二进制计数模式 | 32位二进制秒计数,保留信息为串行值;适用于非公历 | |
计数源 | - | 子时钟(XCIN)或LOCO |
时钟和日历功能 | 共享功能 | 支持启动/停止;亚秒位以二进制单位显示(1Hz、2Hz、4Hz、8Hz、16Hz、32Hz、64Hz);时钟误差校正;1Hz/64Hz时钟输出 |
中断 | 闹钟中断(RTC_ALM) | 日历计数模式:可选择与年、月、日、星期、时、分、秒比较触发;二进制计数模式:32位二进制计数器任意位比较触发 |
周期中断(RTC_PRD) | 可选中断周期:2秒、1秒、1/2秒、1/4秒、1/8秒、1/16秒、1/32秒、1/64秒、1/128秒、1/256秒 | |
进位中断(RTC_CUP) | 64Hz计数器向秒计数器进位时触发;64Hz计数器变化且R64CNT寄存器被读时触发(仅32-KHz计数模式) | |
唤醒功能 | 可通过闹钟中断或周期中断从软件待机/深度软件待机模式返回 | |
时间捕获功能 | - | 检测时间捕获事件输入引脚边沿时捕获时间;日历模式捕获月、日、时、分、秒,二进制模式捕获32位二进制计数器值;支持中断(与IRQ共享引脚) |
事件链接功能 | 周期事件输出(RTC_PRD) | - |
TrustZone过滤器 | 安全属性 | 可设置安全属性 |
RTC 系统框图
注:在日历计数模式下,闹钟寄存器的比较值使用 BCD 码。
RTC I/O pins
引脚名称 | I/O 类型 | 描述信息 |
---|---|---|
XCIN | Input | 连接32.768kHz晶体(与XCOUT配合使用) |
XCOUT | Output | 连接32.768kHz晶体(与XCIN配合使用) |
RTCOUT | Output | 用于输出1Hz或64Hz波形,在深度软件待机模式下不工作 |
RTCICn(n=0,1) | Input | 时间捕获事件输入引脚(n表示引脚序号,支持2个独立通道) |
注:XCIN 与 XCOUT 只能用于RTC的时钟输入无其他功能。
RTC
注:工程是 uart 的工程基础上进行开发的。
配置引脚 在 FSP Configuration -> Pins -> Peripherals -> Timers:RTC -> RTC 接下来我们需要配置 Operation Mode 选择 Custom ,如下图所示:
接下来在 FSP Configuration -> Stacks ->New Stack -> Timers -> Realtime(r_rtc) ,如下图所示:
接下来我们选择 g_rtc0 模块,然后点击属性,然后放大,需要修改的内容如下图所示:
注:
- Clock Source
- Sub-Clock:子时钟系统
- LOCO:内部低速时钟
- Frequency Comparision Value (LOCO):当选择LOCO时,RFRL是一个用于控制预分频器的寄存器。当LOCO频率为32.768 kHz时,RFRL寄存器应设置为0x00FF。也是就是32768/256=128HZ。
- Automatic Adjustment Mode:自动校正模式,用于补偿外部子时钟系统的误差。
- Automatic Adjustment Period:自动校正的时间,可选择 10 秒、1 分钟、NONE。
- Automatic Type(Plus-Minus):补偿时间校准是增加、减小、NONE。
- Error Adjustment Value:错误自定义整数补偿。
- Callback:中断回调函数。
- Alarm Interrupt Priority :闹钟警告优先级
- Period Interrupt Priority:周期计数优先级
- Carry Interrupt Priority:捕获比较优先级
日历程序编写
#include "Apply\app.h"
#include "rtc\bsp_rtc.h"
#include "uart/bsp_uart.h"
/* 用于获取时间 */
static rtc_time_t Get_Current_Time = {
.tm_sec = 0, // 秒
.tm_min = 0, // 分
.tm_hour = 0, // 小时
.tm_wday = 0, // 星期
.tm_mday = 0, // 日
.tm_mon = 0, // 月
.tm_year = 0 // 年份
};
void Run(void)
{
char time_str[33]={0};
UART0_Init();
RTC_Init();
printf("欢迎使用立创·地奇星开发板\r\n");
printf("接下来开始RTC实验:\r\n");
while(1)
{
/* 判断RTC获取时间函数状态 */
if (Rtc_GetTime(&Get_Current_Time))
{
/* 格式化并输出时间 */
FormatRtcTime(&Get_Current_Time, time_str, sizeof(time_str));
printf("当前RTC时间: %s\r\n", time_str);
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#ifndef __APP_H
#define __APP_H
#include "hal_data.h"
#include <stdio.h>
void Run(void);
#endif
2
3
4
5
6
7
8
9
#include "bsp_rtc.h"
const char* Convet_tsr[7] = {"一","二","三","四","五","六","天"};
/* 中断标志位 */
bool Rtc_Flag_Interr = false;
static rtc_time_t Get_Current_Time;
/* 初始化时间设置 */
static rtc_time_t Set_Init_Time = {
.tm_sec = 40, // 秒
.tm_min = 59, // 分
.tm_hour = 23, // 小时
.tm_wday = 2, // 星期
.tm_mday = 15, // 日
.tm_mon = 7, // 月
.tm_year = 2025 - YEAR_FIXED // 年份
};
/* RTC初始化函数 */
void RTC_Init(void)
{
/* 初始化RTC */
fsp_err_t err = R_RTC_Open(g_rtc0.p_ctrl, g_rtc0.p_cfg);
if (FSP_SUCCESS != err) {
printf("错误:RTC初始化中断设置失败\n");
}
/* 设置RTC计时 */
err = R_RTC_CalendarTimeSet(g_rtc0.p_ctrl, &Set_Init_Time);
if (FSP_SUCCESS != err) {
printf("错误:RTC计时设置失败\n");
}
/* 使能计时中断 */
err = R_RTC_PeriodicIrqRateSet(g_rtc0.p_ctrl, RTC_PERIODIC_IRQ_SELECT_1_SECOND);
if (FSP_SUCCESS != err) {
printf("错误:RTC中断设置失败\n");
}
}
/* RTC获取时间函数 */
bool Rtc_GetTime(rtc_time_t *Nne_time)
{
if (Rtc_Flag_Interr)
{
/* 复制当前时间 */
*Nne_time = Get_Current_Time;
/* 清除中断标志位 */
Rtc_Flag_Interr = false;
return true;
}
return false;
}
/* RTC中断函数 */
void rtc_callback(rtc_callback_args_t *p_args)
{
if (p_args->event = RTC_EVENT_PERIODIC_IRQ)
{
/* 获取当前时间 */
fsp_err_t err = R_RTC_CalendarTimeGet(g_rtc0.p_ctrl, &Get_Current_Time);
if (FSP_SUCCESS != err) {
printf("错误:获取时间失败\n");
}
/* 标记时间更新 */
Rtc_Flag_Interr = true;
}
}
/* 格式化RTC时间为字符串 */
void FormatRtcTime(const rtc_time_t *time, char *buffer, size_t size)
{
int wday = time->tm_wday - 1;
if (wday < 0 || wday > 7) wday = 0; // 超出范围时默认取第一个
if (time && buffer && size > 0) {
snprintf(buffer, size, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d 星期:%s",
time->tm_year + YEAR_FIXED,
time->tm_mon,
time->tm_mday,
time->tm_hour,
time->tm_min,
time->tm_sec,
Convet_tsr[wday]
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#ifndef BSP_RTC_H_
#define BSP_RTC_H_
#include "hal_data.h"
#include "uart/bsp_uart.h"
#include <stdio.h>
/* 定义年份修正常量 */
#define YEAR_FIXED 1900
void RTC_Init(void);
bool Rtc_GetTime(rtc_time_t *Nne_time);
void FormatRtcTime(const rtc_time_t *time, char *buffer, size_t size);
#endif
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include "hal_data.h"
#include "Apply\app.h"
FSP_CPP_HEADER
void R_BSP_WarmStart(bsp_warm_start_event_t event);
FSP_CPP_FOOTER
/*******************************************************************************************************************//**
* main() is generated by the RA Configuration editor and is used to generate threads if an RTOS is used. This function
* is called by main() when no RTOS is used.
**********************************************************************************************************************/
void hal_entry(void)
{
/* TODO: add your own code here */
Run();
#if BSP_TZ_SECURE_BUILD
/* Enter non-secure code */
R_BSP_NonSecureEnter();
#endif
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
实验现象
实物连接
注:需要供电。
输出结果
RTC 闹钟
我们选择 FSP Configuration -> Stacks -> g_rtc0 模块,然后点击属性,然后放大,这里我们只需要使能 RTC 的 Alarm 中断,修改的内容如下图所示:
设置完成后,按下 Ctrl + S 进行保存,然后点击 Generate Project Content 进行工程生成。如下图所示:
闹钟程序编写
#include "Apply\app.h"
#include "rtc\bsp_rtc.h"
#include "uart/bsp_uart.h"
/* 用于获取时间 */
static rtc_time_t Get_Current_Time = {
.tm_sec = 0, // 秒
.tm_min = 0, // 分
.tm_hour = 0, // 小时
.tm_wday = 0, // 星期
.tm_mday = 0, // 日
.tm_mon = 0, // 月
.tm_year = 0 // 年份
};
void Run(void)
{
char time_str[33]={0};
UART0_Init();
RTC_Init();
printf("欢迎使用立创·地奇星开发板\r\n");
printf("接下来开始 RTC 闹钟实验:\r\n");
while(1)
{
/* 判断Alarm状态 */
if (RTC_Alarm_GetTime(&Get_Current_Time))
{
/* 格式化并输出时间 */
FormatRtcTime(&Get_Current_Time, time_str, sizeof(time_str));
printf("闹钟已触发,当前RTC时间:%s\r\n", time_str);
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#ifndef __APP_H
#define __APP_H
#include "hal_data.h"
#include <stdio.h>
void Run(void);
#endif
2
3
4
5
6
7
8
9
#include "bsp_rtc.h"
const char* Convet_tsr[7] = {"一","二","三","四","五","六","天"};
/* 中断标志位 */
bool Rtc_Flag_Interr = false;
static rtc_time_t Get_Current_Time;
bool Rtc_Alarm_Flag = false;
/* 初始化时间设置 */
static rtc_time_t Set_Init_Time = {
.tm_sec = 40, // 秒
.tm_min = 59, // 分
.tm_hour = 23, // 小时
.tm_wday = 1, // 星期
.tm_mday = 15, // 日
.tm_mon = 7, // 月
.tm_year = 2025 - YEAR_FIXED // 年份
};
rtc_alarm_time_t Set_Alarm__Time = {
.time = {
.tm_sec = 00, // 秒
.tm_min = 00, // 分
.tm_hour = 00, // 小时
.tm_wday = 3, // 星期
.tm_mday = 16, // 日
.tm_mon = 7, // 月
.tm_year = 2025 - YEAR_FIXED // 年份
},
.year_match = 0,
.mon_match = 0,
.mday_match = 0,
.hour_match = 0,
.dayofweek_match = 0,
.min_match = 0,
.sec_match = 1,
};
/* RTC初始化函数 */
void RTC_Init(void)
{
/* 初始化RTC */
fsp_err_t err = R_RTC_Open(g_rtc0.p_ctrl, g_rtc0.p_cfg);
if (FSP_SUCCESS != err) {
printf("错误:RTC初始化中断设置失败\n");
}
/* 设置RTC计时 */
err = R_RTC_CalendarTimeSet(g_rtc0.p_ctrl, &Set_Init_Time);
if (FSP_SUCCESS != err) {
printf("错误:RTC计时设置失败\n");
}
/* 配置RTC闹钟 */
err = R_RTC_CalendarAlarmSet(g_rtc0.p_ctrl, &Set_Alarm__Time);
if (FSP_SUCCESS != err) {
printf("错误:RTC闹钟设置失败\n");
}
/* 使能计时中断 */
err = R_RTC_PeriodicIrqRateSet(g_rtc0.p_ctrl, RTC_PERIODIC_IRQ_SELECT_1_SECOND);
if (FSP_SUCCESS != err) {
printf("错误:RTC中断设置失败\n");
}
}
/* RTC获取时间函数 */
bool Rtc_GetTime(rtc_time_t *Nne_time)
{
if (Rtc_Flag_Interr)
{
/* 复制当前时间 */
*Nne_time = Get_Current_Time;
/* 清除中断标志位 */
Rtc_Flag_Interr = false;
return true;
}
return false;
}
bool RTC_Alarm_GetTime(rtc_time_t *Nne_time)
{
if(Rtc_Alarm_Flag)
{
/* 复制当前时间 */
*Nne_time = Get_Current_Time;
/* 清除中断标志位 */
Rtc_Alarm_Flag = false;
return true;
}
return false;
}
/* RTC中断函数 */
void rtc_callback(rtc_callback_args_t *p_args)
{
switch(p_args->event)
{
case RTC_EVENT_ALARM_IRQ:
Rtc_Alarm_Flag = true;
break;
case RTC_EVENT_PERIODIC_IRQ:
/* 获取当前时间 */
fsp_err_t err = R_RTC_CalendarTimeGet(g_rtc0.p_ctrl, &Get_Current_Time);
if (FSP_SUCCESS != err) {
printf("错误:获取时间失败\n");
}
/* 标记时间更新 */
Rtc_Flag_Interr = true;
break;
default:
break;
}
}
/* 格式化RTC时间为字符串 */
void FormatRtcTime(const rtc_time_t *time, char *buffer, size_t size)
{
int wday = time->tm_wday - 1;
if (wday < 0 || wday > 7) wday = 0; // 超出范围时默认取第一个
if (time && buffer && size > 0) {
snprintf(buffer, size, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d 星期:%s",
time->tm_year + YEAR_FIXED,
time->tm_mon,
time->tm_mday,
time->tm_hour,
time->tm_min,
time->tm_sec,
Convet_tsr[wday]
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#ifndef BSP_RTC_H_
#define BSP_RTC_H_
#include "hal_data.h"
#include "uart/bsp_uart.h"
#include <stdio.h>
/* 定义年份修正常量 */
#define YEAR_FIXED 1900
void RTC_Init(void);
bool Rtc_GetTime(rtc_time_t *Nne_time);
bool RTC_Alarm_GetTime(rtc_time_t *Nne_time);
void FormatRtcTime(const rtc_time_t *time, char *buffer, size_t size);
#endif
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include "hal_data.h"
#include "Apply\app.h"
FSP_CPP_HEADER
void R_BSP_WarmStart(bsp_warm_start_event_t event);
FSP_CPP_FOOTER
/*******************************************************************************************************************//**
* main() is generated by the RA Configuration editor and is used to generate threads if an RTOS is used. This function
* is called by main() when no RTOS is used.
**********************************************************************************************************************/
void hal_entry(void)
{
/* TODO: add your own code here */
Run();
#if BSP_TZ_SECURE_BUILD
/* Enter non-secure code */
R_BSP_NonSecureEnter();
#endif
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22