打印
[其他ST产品]

【正点原子K210连载】第十五章 RTC实验《DNK210使用指南-SDK版》

[复制链接]
1122|1
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
第十五章 RTC实验
本章将介绍Kendryte K210的RTC外设使用。通过本章的学习,读者将学习到SDK编程技术使用Kendryte K210的的RTC模块。
本章分为如下几个小节:
15.1 RTC介绍
15.2 硬件设计
15.3 程序设计
15.4 运行验证
15.1 RTC介绍
Kendryte K210RTC是用来计时的单元,在设置时间后具备计时功能:
1. 可使用外部高频晶振进行计时
2. 可配置外部晶振频率与分频
3. 支持万年历配置,可配置的项目包含世纪、年、月、日、时、分、秒与星期
4. 可按秒进行计时,并查询当前时刻
5. 支持设置一组闹钟,可配置的项目包含年、月、日、时、分、秒,闹钟到达时触发中断
6. 中断可配置,支持每日、每时、每分、每秒触发中断
7. 可读出小于1秒的计数器计数值,最小刻度单位为外部晶振的单个周期
8. 上电/复位后数据清零
实时时钟(RTC)能为系统提供一个准确的时间,Kendryte K210RTC模块功能强大,支持万年历配置、闹钟、时间中断,能够适用于多种场景。通过本章的学习,读者将学习到RTC的使用。
Kendryte K210官方SDK提供了多个操作RTC模块的函数,这里我们只讲述本实验用到的函数,这些函数介绍如下:
1, rtc_init函数
该函数主要用于RTC初始化,如下代码所示:
int rtc_init(void)
{
    /* Reset RTC */
    sysctl_reset(SYSCTL_RESET_RTC);
    /* Enable RTC */
    sysctl_clock_enable(SYSCTL_CLOCK_RTC);
    /* Unprotect RTC */
    rtc_protect_set(0);
    /* Set RTC clock frequency */
    rtc_timer_set_clock_frequency(
        sysctl_clock_get_freq(SYSCTL_CLOCK_IN0));
    rtc_timer_set_clock_count_value(1);
    /* Set RTC mode to timer running mode */
    rtc_timer_set_mode(RTC_TIMER_RUNNING);
    return 0;
}
首先我们要重新复位RTC,然后使能系统RTC时钟,撤销RTC的保护,让RTC能重新配置,为了保证RTC计时准确,我们需要根据系统时钟重新配置RTC时钟,最后配置RTC进入计时模式,RTC配置成功后该函数返回0,反之返回1。这里需要注意的是:RTC模块需要使能PLL0,并且CPU频率要大于30MHz
2rtc_timer_set函数
该函数用于设置RTC的日期和时间,如下代码所示:
int rtc_timer_set(int year, int month, int day, int hour, int minute, int second);
Kendryte K210RTC模块在上电/复位后数据会清零,所以我们每次启动RTC后要重新设置日期、时间数据,函数共有6个参数,参数分别对应年、月、日、小时、分钟和秒,设置成功函数返回0
3rtc_timer_get函数
该函数用来读取当前RTC的日期、时间数据,该函数原型及参数描述如下所示:
int rtc_timer_get(int *year, int *month, int *day, int *hour, int *minute, int *second);
函数共有6个参数,参数分别对应年、月、日、小时、分钟和秒,读取成功函数返回0,返回其他表示读取失败。
15.2 硬件设计
15.2.1 例程功能
1.程序编译时将当前系统的日期、时间设置为RTC的日期、时间,之后每秒打印输出RTC的时间数据。
15.2.2 硬件资源
1.UARTHSISP)
        UARTHS_TX – IO5
        UARTHS_RX – IO4
15.2.3 原理图
本章实验内容,主要讲解RTC模块的使用,无需关注原理图。
15.3 程序设计
15.3.1 RTC获取系统时间驱动代码
RTC获取系统时间源码包括两个文件:rtcdate.crtcdate.hrtcdate.h文件只包含了函数的声明,我们这里介绍下函数参数的结构体rtc_date_time_t。
typedef struct _rtc_date_time
{
    uint32_t sec : 6;
    uint32_t min : 6;
    uint32_t hour : 5;
    uint32_t week : 3;
    uint32_t day : 5;
    uint32_t month : 4;
    uint16_t year;
} rtc_date_time_t;
可以看到,这个结构体是用于存放RTC的时间和日期的,我们设置RTC时间的时候可以直接使用该结构体的参数,下面的们介绍rtcdate.c文件的内容。
void get_compile_time(rtc_date_time_t *compile_time)
{
    const char date[12] = {__DATE__};
    const char time[9] = {__TIME__};
    uint8_t length;
    const char *ptr;
    char buffer[100];
    uint8_t month_index;
    const char *month_list[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
    if (compile_time == NULL)
    {
        return;
    }
    /* Set to default */
    compile_time->sec = 0;
    compile_time->min = 0;
    compile_time->hour = 0;
    compile_time->week = 6;
    compile_time->day = 1;
    compile_time->month = 1;
    compile_time->year = 2000;
    /* Month */
    ptr = find_field(date, ' ', 0);
    length = (uint8_t)(strchr(ptr, ' ') - ptr);
    strncpy(buffer, ptr, length);
    buffer[length] = '\0';
    for (month_index=0; month_index<sizeof(month_list)/sizeof(month_list[0]); month_index++)
    {
        if (strcmp(buffer, month_list[month_index]) == 0)
        {
            compile_time->month = month_index + 1;
        }
    }
   
    /* Day */
    ptr = find_field(date, ' ', 1);
    length = (uint8_t)(strchr(ptr, ' ') - ptr);
    strncpy(buffer, ptr, length);
    buffer[length] = '\0';
    compile_time->day = atoi(buffer);
    /* Year */
    ptr = find_field(date, ' ', 2);
    length = (uint8_t)(strchr(ptr, ' ') - ptr);
    strncpy(buffer, ptr, length);
    buffer[length] = '\0';
    compile_time->year = atoi(buffer);
    /* Hour */
    ptr = find_field(time, ':', 0);
    length = (uint8_t)(strchr(ptr, ':') - ptr);
    strncpy(buffer, ptr, length);
    buffer[length] = '\0';
    compile_time->hour = atoi(buffer);
    /* Minute */
    ptr = find_field(time, ':', 1);
    length = (uint8_t)(strchr(ptr, ':') - ptr);
    strncpy(buffer, ptr, length);
    buffer[length] = '\0';
    compile_time->min = atoi(buffer);
    /* Second */
    ptr = find_field(time, ':', 2);
    length = (uint8_t)(strchr(ptr, '\0') - ptr);
    strncpy(buffer, ptr, length);
    buffer[length] = '\0';
    compile_time->sec = atoi(buffer);
    /* Week */
    compile_time->week = rtc_get_wday(compile_time->year, compile_time->month, compile_time->day);
}
首先我们介绍get_compile_time函数,函数主要功能就是获取系统时间,通过代码__DATE__和__TIME__就能读取到系统的日期和时间,这两个数据是以字符串的形式保存在数组中,我们不能直接使用,这时候就需要数据提取处理,接着我们对RTC的日期和时间结构体变量赋予一个默认值,完成后我们就对上面提取到的日期和时间数据处理了,在VSCode我们可以看到__DATE__和__TIME__这两个数据的宏定义分别为#define __DATE__ "Feb 21 2024"和#define __TIME__ "09:49:35",我们就可以根据这两个字符串的格式提取所需数据,这里我们还需要用到另外一个函数,函数的原型如下:
/**
* @brief   查找字符串中第n个分隔符后的地址
* @param   string   :需要查找的字符串
* @param   interval :分隔符
* @param   index    :n个分割符
* @retval  返回第n个分隔符后的地址
*/
static const char *find_field(const char *string, const char interval, uint8_t index)
{
    uint8_t index_loop = 0;
    const char *field = string;
    while (index_loop < index)
    {
        while (field[0] != interval)
        {
            field++;
        }
        while (field[0] == interval)
        {
            field++;       /* 偏移到分隔符地址后 */
        }
        index_loop++;
    }
    return field;
}
这个函数是根据我们日期和时间的数据格式编写的,比如日期数据中两个数据之间是以空格符号分隔,时间是以‘:’符号分隔,那我们就可以以这两个分隔符提取数据处理了,这里我们通过三个参数便可读取到字符串中第n个分隔符后的数据地址了,如日期第一个数据是月份,后面是空格符号,通过find_field(date, ' ', 0)这段代码是直接读取到日期数据的第一个字符的首地址(第三个参数为0地址未偏移直接跳出),接着我们用strchr函数读取下个空格符的位置,就能够确定月份这个数据的所占的字节数,进而提取到到月份的字符串数据,我们可以再稍微处理下再保存到RTC存储日期、时间数据的结构体变量中,其他几个数据读取方法都相似,这里不再叙述。
15.3.2 main.c代码
main.c中的代码如下所示:
int main(void)
{
    rtc_date_time_t time;
    int year;
    int month;
    int day;
    int hour;
    int minute;
    int second;
    int second_prev = 0;
    sysctl_pll_set_freq(SYSCTL_PLL0, 800000000); /* RTC模块需要开启PLL0,并且CPU频率要大于30MHz */
    get_compile_time(&time);    /* 获取系统时间 */
    rtc_init();
    rtc_timer_set(time.year, time.month, time.day, time.hour, time.min, time.sec);
    while (1)
    {
        msleep(100);
        rtc_timer_get(&year, &month, &day, &hour, &minute, &second);
        if (second_prev != second)
        {
            second_prev = second;
            printf("%4d-%02d-%02d %02d:%02d:%02d\n", year, month, day, hour, minute, second);
        }
    }
}
我们先定义6个日期、时间数据变量,然后开启PLL0时钟。RTC模块要求开启PLL0时钟,并且CPU频率大于30MHz,通过get_compile_time函数读取系统时钟存放在结构体变量中,然后初始化RTC,设置RTC时间。
最后在一个循环中每隔100毫秒读取一次RTC的实时时间,当秒钟变量second发生变化时,就通过串口打印输出RTC的日期、时间数据。
15.4 运行验证
DNK210开发板连接到电脑主机,通过VSCode将固件烧录到开发板中,此时,我们打开“串口终端”查看输出的数据,如下图所示:。
图15.4.1 串口终端输出时间

使用特权

评论回复
沙发
610u| | 2024-10-2 20:54 | 只看该作者
上电/复位后数据清零 实时时钟(RTC)能为系统提供一个准确的时间,Kendryte K210的RTC模块功能强大,支持万年历

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

93

主题

94

帖子

2

粉丝