Sigmastar_RTC使用参考

REVISION HISTORY

Revision No.
Description
Date
1.0
  • Initial release
  • 01/31/2024

    1. 概述

    Real Time Clock(实时时钟),简称RTC,主要是用于计时和闹钟设置。RTC驱动采用标准的LINUX框架,能够使用统一的接口来操作RTC,同时在LINUX框架外添加符合SigmaStar IP功能的PWC(Power Controller)功能。

    PWC(Power Controller)的功能是通过检测到唤醒源触发时控制PAD_RTC_IO4和PAD_RTC_IO5引脚的高低电平,PAD_RTC_IO4和PAD_RTC_IO5控制外部的电源芯片从而控制整板电源。

    2. 硬件介绍

    2.1. RTC晶振频率

    RTC目前使用32.768kHz频率的晶振

    2.2. PWC的系统框架

    图2-1 PWC电路硬件图

    PWC可以通过唤醒源触发从而控制PAD_RTC_IO4和PAD_RTC_IO5引脚的高低电平,PAD_RTC_IO4和PAD_RTC_IO5控制外部的电源芯片从而控制整板电源。

    RTC部分可控的唤醒源有:

    1. PAD_RTC_IO0 - PAD_RTC_IO3 组成的按键/波形唤醒

    2. RTC的Alarm中断唤醒

    3. sw ctrl(software control)

    系统的框架图如下:

    图2-2 PAD_RTC_IO4控制源电路图

    图2-3 PAD_RTC_IO4和PAD_RTC_IO5的控制源电路图

    注:图2-2中的power_on_st与图2-3的pwr_en在硬件上是处于相连状态

    结合图2-2和图2-3可以看到,PAD_RTC_IO0 - PAD_RTC_IO3的事件触发和RTC Alarm的事件触发都可以使power_on_st输出高电平,沿着power_on_st->pwr_en进而拉高PAD_RTC_IO4的电平,同时sw ctrl事件的触发也可以控制PAD_RTC_IO4电平。 从图2-3可以看到PAD_RTC_IO4的电平、RTC Alarm的事件触发和sw ctrl事件的触发都会影响到PAD_RTC_IO5的电平,因此可以灵活利用这些触发事件以及PAD_RTC_IO4和PAD_RTC_IO5的互相作用来实现电源控制,软件上的配置方法详见后文的Kernel用法介绍章节。

    2.3. PAD_RTC_IO0配置

    PAD_RTC_IO0支持配置成下拉模式和高阻态模式,默认为下拉模式,软件上的配置方法详见后文的Kernel用法介绍章节。

    图2-4 PAD_RTC_IO0内部框图

    2.4. PAD_RTC_IO1配置

    PAD_RTC_IO1 只有下拉(PD)的配置,不支持软件更改。内部框图为:

    图2-5 PAD_RTC_IO1内部框图

    2.5. PAD_RTC_IO2配置

    PAD_RTC_IO2可以支持配置内部高压/低压比较器的阈值,此外PAD_RTC_IO2还可以选择连接到内置的施密特触发器上以实现对不同信号捕捉。PAD_RTC_IO2的内部框图为:

    图2-6 PAD_RTC_IO2内部框图

    施密特触发器可以通过比较器实现相同的逻辑,目前的驱动没有实现将PAD_RTC_IO2切换为施密特触发器的功能。PAD_RTC_IO2可以支持配置的类型为:CMPHL(compare high and low)、CMPH(compare low)、CMPL(compare high)

    2.5.1. CMPHL

    PAD_RTC_IO2与寄存器Offset 0x12和PAD_RTC_IO⅘的关系:

    图2-7 CMPHL下PAD_RTC_IO2与PAD_RTC_IO⅘的关系1图

    图2-8 CMPHL下PAD_RTC_IO2与PAD_RTC_IO⅘的关系2图

    2.5.2. CMPL

    PAD_RTC_IO2与寄存器Offset 0x12和PAD_RTC_IO⅘的关系:

    图2-9 CMPL下PAD_RTC_IO2与PAD_RTC_IO⅘的关系1图

    图2-10 CMPL下PAD_RTC_IO2与PAD_RTC_IO⅘的关系2图

    2.5.3. CMPH

    PAD_RTC_IO2与寄存器Offset 0x12和PAD_RTC_IO⅘的关系:

    图2-11 CMPH下PAD_RTC_IO2与PAD_RTC_IO⅘的关系1图

    图2-12 CMPH下PAD_RTC_IO2与PAD_RTC_IO⅘的关系2图

    2.6. PAD_RTC_IO3配置

    PAD_RTC_IO3支持配置成下拉模式和上拉模式,默认为下拉模式:

    图2-13 PAD_RTC_IO3下拉模式电路图

    上拉模式内部框图:

    图2-14 PAD_RTC_IO3上拉模式电路图

    3. RTOS用法介绍

    3.1 DRIVER PATH

    sc/driver/sysdriver/rtcpwc/drv/pub/drv_rtcpwc.h
    sc/driver/sysdriver/rtcpwc/drv/src/drv_rtcpwc.c
    sc/driver/sysdriver/rtcpwc/drv/src/drv_rtcpwc_test.c
    sc/driver/sysdriver/rtcpwc/hal/chipname/src/hal_rtcpwc.c
    sc/driver/sysdriver/rtcpwc/hal/chipname/inc/hal_rtcpwc.h
    sc/driver/sysdriver/rtcpwc/hal/chipname/inc/hal_rtcpwc_cfg.h
    sc/driver/sysdriver/rtcpwc/hal/chipname/inc/hal_rtcpwcreg.h
    

    3.2 CONFIG配置

    #make menuconfig
    
    BSP Driver Options --->
        [*] Sstar RTC Driver
        [*]   Sstar RTC With PWC
        [*]   Sstar RTC With Alarm
        [*]   Sstar RTC With Offset
    

    3.3 SYSDESC配置

    chipname.sys文件位于sc/driver/sysdriver/sysdesc/hal/chipname/pub

        <rtcpwc>
            [reg_u32] 0x1F006800;
            [interrupts_u8] INT_IRQ_FLAG_POC;
            // [io0_hiz_u8] 1;
            // [io2_wos_u32] 1;
            // [io2_wos_v_u32] 2, 3;
            // [io3_pu_u8] 1;
            // [offset_count_u8] 100;
            // [offset_negative_u8] 1;
            [io4_enable_u8] 3;
            [io5_enable_u8] 3;
            [status_u8] 1;
    

    RTCPWC驱动中支持配置的属性如下表:

    属性 描述 设定值 备注
    reg_u32 指定bank的地址 不需要修改
    interrupts_u8 指定硬件中断号 1表示加入,0表示不加入 可根据需要修改
    io0_hiz_u8 配置PAD_RTC_IO0为高阻态模式 1表示设置为高阻态模式,0表示不设置为高阻态模式 根据需要配置,不配置默认是下拉模式
    io2_wos_u32 配置PAD_RTC_IO2比较器的类型 0:CMPHL 1:CMPHL 2:CMPL 3:CMPH 可根据需要修改,详细说明可见硬件介绍章节
    io2_wos_v_u32 配置PAD_RTC_IO2比较器的电压 0, 1, 2, 3, 4, 5, 6, 7 可根据需要修改,详细备注请见注释1
    io3_pu_u8 配置PAD_RTC_IO3为上拉模式 1表示设置为上拉模式,0表示设置为下拉模式 可根据需要配置,不配置默认是下拉模式
    offset_count_u8 配置频率偏移的补偿值 0~255 可根据需要修改,详细备注请见注释2
    offset_negative_u8 配置频率偏移的方向为提前补偿 1表示提前补偿,0表示延后补偿 根据需要配置,不配置是延后补偿
    io4_enable_u8 用于配置PAD_RTC_IO4的触发模式 0~7 可根据需要修改,详细备注请见注释3
    io5_enable_u8 用于配置PAD_RTC_IO5的触发模式 0~7 可根据需要修改,详细备注请见注释3
    status_u8 选择是否使能驱动 1表示enable;0表示disable 可根据需要修改

    注释1:io2-wos-v属性第一个值代表VL,第二个值代表VH,VL和VH可选的值为:

    属性值 VL VL电压值 VH VH电压值
    0 1/16 AVDD 0.206 15/16 AVDD 3.094
    1 2/16 AVDD 0.413 14/16 AVDD 2.888
    2 3/16 AVDD 0.619 13/16 AVDD 2.681
    3 4/16 AVDD 0.825 12/16 AVDD 2.475
    4 5/16 AVDD 1.031 11/16 AVDD 2.269
    5 6/16 AVDD 1.238 10/16 AVDD 2.063
    6 7/16 AVDD 1.444 9/16 AVDD 1.856
    7 8/16 AVDD 1.650 8/16 AVDD 1.650

    表中VL/VH电压值为AVDD取3.3V时的电压值

    硬件默认值为3,如果dtsi里指定了io2-wos属性没有指定io2-wos-v属性,则io2-wos-v的值将被设置为0。

    注释2:RTC计数功能可能会由于晶振容差、PCB容差等因素造成频率偏差,从而导致RTC计时出现偏差。为了补偿此类制造工艺造成的偏差,RTC增加了频率偏移的功能。频率偏移补偿误差可以在产品生产初期选取样品使用用户空间接口进行调试标定,然后通过DTS配置到驱动中,就能在最终产品中达到一定的矫正效果。 RTC的频率偏移功能会在每32个1Hz时钟来临的时候进行补偿,补偿的时间计算公式为:

    T = 1/f × count

    以目前使用的32.768kHz频率的晶振,补偿计数值(offset-count)为1计算,每32秒会纠正的时间为:

    T = 1/32768 × 1 × 1000000000 ≈ 30518ns

    其中补偿计数值(offset-count)的最大值为255,所以24小时下最大的补偿时间为:

    T = 30518ns × 255 × (24 × 60 × 60 ÷ 32) ≈ 21.0s

    补偿计数值(offset-count)为正数时,为时间延后计算出来的时间,计数补偿值(offset-count)为负值时,为时间提前计算出来的时间值。

    注释3:dtsi中的io4-enable对应硬件介绍章节图2-2和图2-3的H10[3:0]、H0f[1]、H49[12],例如当前io4-enable设置为3,则H0F[0]为1且H0F[1]也为1,代表此时PAD_RTC_IO4可以同时由PAD_RTC_IO0-3和RTC Alarm共同控制。PAD_RTC_IO4除了以上两种控制源,还可以通过SW Ctrl的方式,即io4-enable配置为'7',对应H49[12],代表此时PAD_RTC_IO4可以同时由PAD_RTC_IO0-3、RTC Alarm和SW Ctrl共同控制。

    io5-enable属性对应硬件介绍章节图2-3的H20[0]、H20[1]、H49[10],例如当前io5-enable设置为2,则H20[0]为0且H20[1]为1,代表PAD_RTC_IO5不受PAD_RTC_IO4控制,PAD_RTC_IO5的状态仅受RTC Alarm的控制。与PAD_RTC_IO4相同的是,PAD_RTC_IO5也可以通过SW Ctrl的方式控制,将io5-enable配置为‘7’即可实现,对应H49[10],则代表此时PAD_RTC_IO5可以同时由PAD_RTC_IO4、RTC Alarm和SW Ctrl共同控制 。

    按键唤醒的真值表如下:

    PAD Event Power On Remark PAD_RTC_IO4 PAD_RTC_IO5
    PAD_RTC_IO0 PD rise >VIH default rise rise
    HIZ rise >VIH   rise rise
    PAD_RTC_IO1 PD rise >VIH default rise rise
    PAD_RTC_IO2 CMPH rise >VIH   rise rise
    CMPL fall <VIL   rise rise
    CMPHL rise >VIH default rise rise
    schmitt rise >VIH   rise rise
    PAD_RTC_IO3 PD rise >VIH default rise rise
    PU fall <VIL   rise rise

    3.4 API说明

    头文件位于sc/driver/sysdriver/rtcpwc/drv/pub/drv_rtcpwc.h

    3.4.1. drv_rtcpwc_read_time

    • 目的

      读取时间。

    • 语法

      int drv_rtcpwc_read_time(struct tm *ptime);
      
    • 参数

      参数名称 描述
      ptime 用于存储时间信息的结构体,具体用法可参考下文demo
    • 返回值

      结果 描述
      成功 返回0
      失败 返回负数

    3.4.2. drv_rtcpwc_set_time

    • 目的

      设置时间。

    • 语法

      int drv_rtcpwc_set_time(struct tm *ptime);
      
    • 参数

      参数名称 描述
      ptime 用于设置时间信息的结构体,具体用法可参考下文demo
    • 返回值

      结果 描述
      成功 返回0
      失败 返回负数

    3.4.3. drv_rtcpwc_read_alarm

    • 目的

      读取闹钟时间。

    • 语法

      int drv_rtcpwc_read_alarm(struct rtc_wkalrm *alarm);
      
    • 参数

      参数名称 描述
      ptime 用于存储闹钟时间信息的结构体,具体用法可参考下文demo
    • 返回值

      结果 描述
      成功 返回0
      失败 返回负数

    3.4.4. drv_rtcpwc_set_alarm

    • 目的

      设置闹钟时间。

    • 语法

      int drv_rtcpwc_set_alarm(struct rtc_wkalrm *alarm);
      
    • 参数

      参数名称 描述
      ptime 用于设置闹钟时间信息的结构体,具体用法可参考下文demo
    • 返回值

      结果 描述
      成功 返回0
      失败 返回负数

    3.5 DEMO

    demo源码位于sc/driver/sysdriver/rtcpwc/drv/src/drv_rtcpwc_test.c

    static int rtcpwc_test(CLI_t *cli, char *p)
    {
        char  *cmd;
        u32 year                     = 0;
        u32 month                    = 0;
        u32 day                      = 0;
        u32 hour                     = 0;
        u32 minute                   = 0;
        u32 second                   = 0;
        struct tm *rtc_test          = NULL;
        struct rtc_wkalrm *rtc_alarm = NULL;
    
        rtc_test        = (struct tm *)CamOsMemAlloc(sizeof(struct tm));
        rtc_alarm       = (struct rtc_wkalrm *)CamOsMemAlloc(sizeof(struct rtc_wkalrm));
        rtc_alarm->time = (struct tm *)CamOsMemAlloc(sizeof(struct tm));
    
        cmd = CliTokenPop(cli);
        if (!cmd)
            return -eCLI_PARSE_INPUT_ERROR;
    
        if (strcmp(cmd, "gettime") == 0)
        {
            drv_rtcpwc_read_time(rtc_test);
            cliPrintf("[%d-%d-%d %d:%d:%d]\n", rtc_test->tm_year + 1900, rtc_test->tm_mon + 1,\
                rtc_test->tm_mday, rtc_test->tm_hour, rtc_test->tm_min, rtc_test->tm_sec);
        }
        else if (strcmp(cmd, "settime") == 0)
        {
            if (CliTokenCount(cli) == 6)
            {
                if (CliTokenPopNum(cli, &year, 0) != eCLI_PARSE_OK)
                    goto _RTCPWC_HELP_EXIT;
                if (CliTokenPopNum(cli, &month, 0) != eCLI_PARSE_OK)
                    goto _RTCPWC_HELP_EXIT;
                if (CliTokenPopNum(cli, &day, 0) != eCLI_PARSE_OK)
                    goto _RTCPWC_HELP_EXIT;
                if (CliTokenPopNum(cli, &hour, 0) != eCLI_PARSE_OK)
                    goto _RTCPWC_HELP_EXIT;
                if (CliTokenPopNum(cli, &minute, 0) != eCLI_PARSE_OK)
                    goto _RTCPWC_HELP_EXIT;
                if (CliTokenPopNum(cli, &second, 0) != eCLI_PARSE_OK)
                    goto _RTCPWC_HELP_EXIT;
    
                rtc_test->tm_year = year - 1900;
                rtc_test->tm_mon  = month - 1;
                rtc_test->tm_mday = day;
                rtc_test->tm_hour = hour;
                rtc_test->tm_min  = minute;
                rtc_test->tm_sec  = second;
    
                drv_rtcpwc_set_time(rtc_test);
                cliPrintf("[%d-%d-%d %d:%d:%d]\n", rtc_test->tm_year + 1900, rtc_test->tm_mon + 1, rtc_test->tm_mday,\
                    rtc_test->tm_hour, rtc_test->tm_min, rtc_test->tm_sec);
            }
        }
        else if (strcmp(cmd, "getalarm") == 0)
        {
            drv_rtcpwc_read_alarm(rtc_alarm);
            cliPrintf("[%d-%d-%d %d:%d:%d]\n", rtc_alarm->time->tm_year + 1900, rtc_alarm->time->tm_mon + 1,\
                rtc_alarm->time->tm_mday, rtc_alarm->time->tm_hour, rtc_alarm->time->tm_min, rtc_alarm->time->tm_sec);
        }
        else if (strcmp(cmd, "setalarm") == 0)
        {
            if (CliTokenCount(cli) == 6)
            {
                if (CliTokenPopNum(cli, &year, 0) != eCLI_PARSE_OK)
                    goto _RTCPWC_HELP_EXIT;
                if (CliTokenPopNum(cli, &month, 0) != eCLI_PARSE_OK)
                    goto _RTCPWC_HELP_EXIT;
                if (CliTokenPopNum(cli, &day, 0) != eCLI_PARSE_OK)
                    goto _RTCPWC_HELP_EXIT;
                if (CliTokenPopNum(cli, &hour, 0) != eCLI_PARSE_OK)
                    goto _RTCPWC_HELP_EXIT;
                if (CliTokenPopNum(cli, &minute, 0) != eCLI_PARSE_OK)
                    goto _RTCPWC_HELP_EXIT;
                if (CliTokenPopNum(cli, &second, 0) != eCLI_PARSE_OK)
                    goto _RTCPWC_HELP_EXIT;
    
                rtc_alarm->time->tm_year = year - 1900;
                rtc_alarm->time->tm_mon  = month - 1;
                rtc_alarm->time->tm_mday = day;
                rtc_alarm->time->tm_hour = hour;
                rtc_alarm->time->tm_min  = minute;
                rtc_alarm->time->tm_sec  = second;
                rtc_alarm->enabled       = 1;
    
                cliPrintf("[%d-%d-%d %d:%d:%d]\n", rtc_alarm->time->tm_year + 1900, rtc_alarm->time->tm_mon + 1,\
                    rtc_alarm->time->tm_mday, rtc_alarm->time->tm_hour, rtc_alarm->time->tm_min, rtc_alarm->time->tm_sec);
                drv_rtcpwc_set_alarm(rtc_alarm);
            }
        }
        else
        {
        _RTCPWC_HELP_EXIT:
            CamOsMemRelease(rtc_test);
            CamOsMemRelease(rtc_alarm);
            CamOsMemRelease(rtc_alarm->time);
            cliPrintf("Plz key in : rtcpwc [settime] [year] [month] [day] [hour] [minute] [second]\n");
            cliPrintf("Plz key in : rtcpwc [setalarm] [month] [day] [hour] [minute] [second]\n");
            cliPrintf("Plz key in : rtcpwc [gettime]\n");
            cliPrintf("Plz key in : rtcpwc [getalarm]\n");
            return eCLI_PARSE_INVALID_PARAMETER;
        }
    
        CamOsMemRelease(rtc_test);
        CamOsMemRelease(rtc_alarm);
        CamOsMemRelease(rtc_alarm->time);
    
        return eCLI_PARSE_OK;
    }