你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【RT-Thread内核实现与应用开发实战指南】读书笔记二 RTC驱...

[复制链接]
andeyqi 发布时间:2018-12-2 09:19
本帖最后由 andeyqi 于 2018-12-2 09:18 编辑

# RT-Thread RTC 设备

## 1、介绍

RT-Thread 的 RTC (实时时钟)设备为操作系统的时间系统提供了基础服务。面对越来越多的 IoT 场景,RTC 已经成为产品的标配,甚至在诸如 SSL 的安全传输过程中,RTC 已经成为不可或缺的部分。

## 2、使用

应用层对于 RTC 设备一般不存在直接调用的 API ,如果使用到 C 标准库中的时间 API (目前主要是获取当前时间的  `time_t time(time_t *t)`),则会间接通过设备的 control 接口完成交互。

> 注意:目前系统内只允许存在一个 RTC 设备,且名称为 `"rtc"` 。

### 2.1  设置日期

```C
rt_err_t set_date(rt_uint32_t year, rt_uint32_t month, rt_uint32_t day)
```

|参数                                    |描述|
|:-----                                  |:----|
|year                                    |待设置生效的年份|
|month                                   |待设置生效的月份|
|day                                     |待设置生效的日|

### 2.2 设置时间

```C
rt_err_t set_time(rt_uint32_t hour, rt_uint32_t minute, rt_uint32_t second)
```

|参数                                    |描述|
|:-----                                  |:----|
|hour                                    |待设置生效的时|
|minute                                  |待设置生效的分|
|second                                  |待设置生效的秒|

### 2.3 使用 Finsh/MSH 命令 查看/设置 日期和时间

#### 2.3.1 查看日期和时间

输入 `date` 即可,大致效果如下:

```
msh />date
Fri Feb 16 01:11:56 2018
msh />
```

#### 2.3.2 设置日期和时间

同样使用 `date` 命令,在命令后面再依次输入 `年` `月` `日` `时` `分` `秒` (中间空格隔开, 24H 制),大致效果如下:

```
msh />date 2018 02 16 01 15 30    # 设置当前时间为 2018-02-16 01:15:30
msh />
```

### 2.4 启用 NTP 时间自动同步

如果 RT-Thread 已接入互联网,可启用 NTP 时间自动同步功能,定期同步本地时间。

在 menuconfig 中启用 `RTC_SYNC_USING_NTP` 配置。启用该功能后,会自动开启 [netutils package](http://github.com/RT-Thread-packages/netutils) 的 NTP 功能。同时务必确保 RT-Thread 网络访问正常。

启用该配置后,还有两个配置是用户可选配置:

- `RTC_NTP_FIRST_SYNC_DELAY`: 首次执行 NTP 时间同步的延时。延时的目的在于,给网络连接预留一定的时间,尽量提高第一次执行 NTP 时间同步时的成功率。默认时间为 30S;
- `RTC_NTP_SYNC_PERIOD`: NTP 自动同步周期,单位为秒,默认一小时(即 3600S)同步一次。

> 注意:如果没有使用组件自动初始化功能,则需手动调用 `int rt_rtc_ntp_sync_init(void)` ,完成该功能初始化。

### 2.5 启用 Soft RTC (软件模拟 RTC)

这个模式非常适用于对时间精度要求不高,没有硬件 RTC 的产品。

#### 2.5.1 使用方法

在 menuconfig 中启用 `RT_USING_SOFT_RTC` 配置。

> 注意:如果没有使用组件自动初始化功能,则需手动调用 `int rt_soft_rtc_init(void)` ,完成该功能初始化。
## 3、L476 RTC驱动移植

### 3.1 RTC驱动函数调用结构

根据上述的rtt RTC 文档描述,通过date命令的分析来理解,date如何调用到底层的驱动来。
static void date(uint8_t argc, char **argv)
/* date 命令获取系统时间参数 */
        ==》time() //调用lobc函数
                ==》__gettimeofday
                        ==》gettimeofday
                                ==》rt_device_find("rtc");
                                ==》rt_device_control(device, RT_DEVICE_CTRL_RTC_GET_TIME, &time);这最终回调用到驱动层向上注册的驱动函数

/* date 命令设定系统时间*/
        ==》set_time()
                ==》localtime()
                ==》mktime()
                ==》rt_device_find("rtc")
                ==》rt_device_control(device, RT_DEVICE_CTRL_RTC_SET_TIME, &now);
        ==》set_date()
                        ==》localtime()
                ==》mktime()
                ==》rt_device_find("rtc")
                ==》rt_device_control(device, RT_DEVICE_CTRL_RTC_SET_TIME, &now);

通过调用关系可以发现,最终会调用到驱动control函数,驱动层只要实现这个函数就可以,剩下的rtt的框架已经实现,不需要用户关心。
### 3.2 软件RTC使用

rtt 驱动组件中已经包含了RTC的软件RTC,通过系统的systick作为RTC时钟源是实现RTC,通过上述的分析软件RTC只要实现驱动的control函数即可。

  1. /*
  2. * Copyright (c) 2006-2018, RT-Thread Development Team
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. *
  6. * Change Logs:
  7. * Date           Author       Notes
  8. * 2018-01-30     armink       the first version
  9. */

  10. #include <time.h>
  11. #include <string.h>
  12. #include <rtthread.h>

  13. #include <drivers/rtc.h>

  14. #ifdef RT_USING_SOFT_RTC

  15. /* 2018-01-30 14:44:50 = RTC_TIME_INIT(2018, 1, 30, 14, 44, 50)  */
  16. #define RTC_TIME_INIT(year, month, day, hour, minute, second)        \
  17.     {.tm_year = year - 1900, .tm_mon = month - 1, .tm_mday = day, .tm_hour = hour, .tm_min = minute, .tm_sec = second}

  18. #ifndef SOFT_RTC_TIME_DEFAULT
  19. #define SOFT_RTC_TIME_DEFAULT                    RTC_TIME_INIT(2018, 1, 1, 0, 0 ,0)
  20. #endif

  21. static struct rt_device soft_rtc_dev;
  22. static rt_tick_t init_tick;
  23. static time_t init_time;

  24. static rt_err_t soft_rtc_control(rt_device_t dev, int cmd, void *args)
  25. {
  26.     time_t *time;
  27.     struct tm time_temp;

  28.     RT_ASSERT(dev != RT_NULL);
  29.     memset(&time_temp, 0, sizeof(struct tm));

  30.     switch (cmd)
  31.     {
  32.     case RT_DEVICE_CTRL_RTC_GET_TIME:
  33.         time = (time_t *) args;
  34.         *time = init_time + (rt_tick_get() - init_tick) / RT_TICK_PER_SECOND;
  35.         break;

  36.     case RT_DEVICE_CTRL_RTC_SET_TIME:
  37.     {
  38.         time = (time_t *) args;
  39.         init_time = *time - (rt_tick_get() - init_tick) / RT_TICK_PER_SECOND;
  40.         break;
  41.     }
  42.     }

  43.     return RT_EOK;
  44. }

  45. #ifdef RT_USING_DEVICE_OPS
  46. const static struct rt_device_ops soft_rtc_ops =
  47. {
  48.     RT_NULL,
  49.     RT_NULL,
  50.     RT_NULL,
  51.     RT_NULL,
  52.     RT_NULL,
  53.     soft_rtc_control
  54. };
  55. #endif

  56. int rt_soft_rtc_init(void)
  57. {
  58.     static rt_bool_t init_ok = RT_FALSE;
  59.     struct tm time_new = SOFT_RTC_TIME_DEFAULT;

  60.     if (init_ok)
  61.     {
  62.         return 0;
  63.     }
  64.     /* make sure only one 'rtc' device */
  65.     RT_ASSERT(!rt_device_find("rtc"));

  66.     init_tick = rt_tick_get();
  67.     init_time = mktime(&time_new);

  68.     soft_rtc_dev.type    = RT_Device_Class_RTC;

  69.     /* register rtc device */
  70. #ifdef RT_USING_DEVICE_OPS
  71.     soft_rtc_dev.ops     = &soft_rtc_ops;
  72. #else
  73.     soft_rtc_dev.init    = RT_NULL;
  74.     soft_rtc_dev.open    = RT_NULL;
  75.     soft_rtc_dev.close   = RT_NULL;
  76.     soft_rtc_dev.read    = RT_NULL;
  77.     soft_rtc_dev.write   = RT_NULL;
  78.     soft_rtc_dev.control = soft_rtc_control;
  79. #endif

  80.     /* no private */
  81.     soft_rtc_dev.user_data = RT_NULL;

  82.     rt_device_register(&soft_rtc_dev, "rtc", RT_DEVICE_FLAG_RDWR);

  83.     init_ok = RT_TRUE;

  84.     return 0;
  85. }
  86. INIT_DEVICE_EXPORT(rt_soft_rtc_init);

  87. #endif /* RT_USING_SOFT_RTC */
复制代码
通过上述的代码通过rt_device_register注册名为"rtc"的驱动函数,SOFT_RTC_TIME_DEFAULT设置默认时间为2018-1-1 00:00:00,soft_rtc_contro函数获取设置系统时间。

### 3.2 软件RTC编译验证使用
编译时开启RTC,和软件RTC,配置编译运行即可,如下图。

soft_rtc_config.PNG


date.PNG

### 3.2 使用L476RTC

根据上面分析,驱动实现RTC的初始化和control函数即可,关闭软甲模拟RTC,在bsp目录下添加\rt-thread-master\bsp\stm32l476-nucleo\driversdrv_rtc.c并重新编译。代码如下:
  1. #include <rthw.h>
  2. #include <rtdevice.h>
  3. #include <board.h>
  4. #include <time.h>
  5. #include <string.h>
  6. #include <drivers/rtc.h>

  7. #ifndef RT_USING_SOFT_RTC

  8. /* RTC handler declaration */
  9. RTC_HandleTypeDef RtcHandle;
  10. /* Uncomment to enable the adaquate Clock Source */
  11. #define RTC_CLOCK_SOURCE_LSI
  12. /*#define RTC_CLOCK_SOURCE_LSE*/

  13. #ifdef RTC_CLOCK_SOURCE_LSI
  14. #define RTC_ASYNCH_PREDIV    0x7F
  15. #define RTC_SYNCH_PREDIV     0xF9
  16. #endif

  17. #ifdef RTC_CLOCK_SOURCE_LSE
  18. #define RTC_ASYNCH_PREDIV  0x7F
  19. #define RTC_SYNCH_PREDIV   0x00FF
  20. #endif


  21. void HAL_RTC_MspInit(RTC_HandleTypeDef *hrtc)
  22. {
  23.   RCC_OscInitTypeDef        RCC_OscInitStruct;
  24.   RCC_PeriphCLKInitTypeDef  PeriphClkInitStruct;
  25.   
  26.   /*##-1- Enables the PWR Clock and Enables access to the backup domain ###################################*/
  27.   /* To change the source clock of the RTC feature (LSE, LSI), You have to:
  28.      - Enable the power clock using __HAL_RCC_PWR_CLK_ENABLE()
  29.      - Enable write access using HAL_PWR_EnableBkUpAccess() function before to
  30.        configure the RTC clock source (to be done once after reset).
  31.      - Reset the Back up Domain using __HAL_RCC_BACKUPRESET_FORCE() and
  32.        __HAL_RCC_BACKUPRESET_RELEASE().
  33.      - Configure the needed RTC clock source */
  34.   __HAL_RCC_PWR_CLK_ENABLE();
  35.   HAL_PWR_EnableBkUpAccess();

  36.   /*##-2- Configure LSE/LSI as RTC clock source ###############################*/
  37. #ifdef RTC_CLOCK_SOURCE_LSE
  38.   
  39.   RCC_OscInitStruct.OscillatorType =  RCC_OSCILLATORTYPE_LSI | RCC_OSCILLATORTYPE_LSE;
  40.   RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  41.   RCC_OscInitStruct.LSEState = RCC_LSE_ON;
  42.   RCC_OscInitStruct.LSIState = RCC_LSI_OFF;
  43.   if(HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  44.   {
  45.     Error_Handler();
  46.   }
  47.   
  48.   PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC;
  49.   PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
  50.   if(HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
  51.   {
  52.     Error_Handler();
  53.   }
  54. #elif defined (RTC_CLOCK_SOURCE_LSI)  
  55.   RCC_OscInitStruct.OscillatorType =  RCC_OSCILLATORTYPE_LSI | RCC_OSCILLATORTYPE_LSE;
  56.   RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  57.   RCC_OscInitStruct.LSIState = RCC_LSI_ON;
  58.   RCC_OscInitStruct.LSEState = RCC_LSE_OFF;
  59.   if(HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  60.   {
  61.     //Error_Handler();
  62.   }

  63.   PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC;
  64.   PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSI;
  65.   if(HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
  66.   {
  67.     //Error_Handler();
  68.   }
  69. #else
  70. #error Please select the RTC Clock source inside the main.h file
  71. #endif /*RTC_CLOCK_SOURCE_LSE*/
  72.   
  73.   /*##-3- Enable RTC peripheral Clocks #######################################*/
  74.   /* Enable RTC Clock */
  75.   __HAL_RCC_RTC_ENABLE();
  76.   
  77.   /*##-4- Configure the NVIC for RTC TimeStamp ###############################*/
  78.   HAL_NVIC_SetPriority(TAMP_STAMP_IRQn, 0x0F, 0);
  79.   HAL_NVIC_EnableIRQ(TAMP_STAMP_IRQn);
  80. }


  81. static rt_err_t l476_rtc_control(rt_device_t dev, int cmd, void *args)
  82. {
  83.     //rt_kprintf("%s\n",__FUNCTION__);
  84.     RTC_DateTypeDef sdatestructureget;
  85.     RTC_TimeTypeDef stimestructureget;
  86.     time_t *time;
  87.         
  88.     switch (cmd)
  89.     {
  90.     case RT_DEVICE_CTRL_RTC_GET_TIME:
  91.         time = (time_t *)args;
  92.         struct tm tm_new;
  93.         //rt_kprintf("entry RT_DEVICE_CTRL_RTC_GET_TIME \n");
  94.         /* Get the RTC current Time */
  95.         HAL_RTC_GetTime(&RtcHandle, &stimestructureget, RTC_FORMAT_BIN);
  96.         /* Get the RTC current Date */
  97.         HAL_RTC_GetDate(&RtcHandle, &sdatestructureget, RTC_FORMAT_BIN);
  98.         tm_new.tm_year = 2000 + sdatestructureget.Year -1900;
  99.         tm_new.tm_mon = sdatestructureget.Month-1;
  100.         tm_new.tm_mday = sdatestructureget.Date;
  101.         tm_new.tm_wday = sdatestructureget.WeekDay;
  102.         
  103.         tm_new.tm_hour = stimestructureget.Hours;
  104.         tm_new.tm_min = stimestructureget.Minutes;
  105.         tm_new.tm_sec = stimestructureget.Seconds;

  106.         *time = mktime(&tm_new);
  107.         /* Display time Format : hh:mm:ss */
  108.         //rt_kprintf("%.2d:%.2d:%.2d", stimestructureget.Hours, stimestructureget.Minutes, stimestructureget.Seconds);
  109.         /* Display date Format : mm-dd-yy */
  110.         //rt_kprintf("%.2d-%.2d-%.2d", sdatestructureget.Month, sdatestructureget.Date, 2000 + sdatestructureget.Year);        
  111.         break;

  112.     case RT_DEVICE_CTRL_RTC_SET_TIME:
  113.     {
  114.         const struct tm* tm_now;
  115.         time = (time_t *)args;
  116.         tm_now = localtime(time);

  117.         stimestructureget.Hours = tm_now->tm_hour;
  118.         stimestructureget.Minutes= tm_now->tm_min;
  119.         stimestructureget.Seconds = tm_now->tm_sec;
  120.         

  121.         sdatestructureget.Year = tm_now->tm_year + 1900 -2000;
  122.         sdatestructureget.Month = tm_now->tm_mon+1;
  123.         sdatestructureget.Date = tm_now->tm_mday;
  124.         sdatestructureget.WeekDay = tm_now->tm_wday;
  125.         
  126.         HAL_RTC_SetTime(&RtcHandle,&stimestructureget, RTC_FORMAT_BIN);
  127.         HAL_RTC_SetDate(&RtcHandle, &sdatestructureget, RTC_FORMAT_BIN);
  128.         //rt_kprintf("entry RT_DEVICE_CTRL_RTC_SET_TIME \n");
  129.         break;
  130.     }
  131.     }
  132.     return RT_EOK;
  133. }


  134. const static struct rt_device_ops l476_rtc_ops =
  135. {
  136.     RT_NULL,
  137.     RT_NULL,
  138.     RT_NULL,
  139.     RT_NULL,
  140.     RT_NULL,
  141.     l476_rtc_control
  142. };

  143. static struct rt_device l476_rtc_dev;


  144. /**
  145.   * @brief  Configure the current time and date and activate timestamp.
  146.   * @param  None
  147.   * @retval None
  148.   */
  149. static void RTC_TimeStampConfig(void)
  150. {
  151.   RTC_DateTypeDef sdatestructure;
  152.   RTC_TimeTypeDef stimestructure;

  153.   /*##-1- Configure the Time Stamp peripheral ################################*/
  154.   /*  RTC TimeStamp generation: TimeStamp Rising Edge on PC.13 Pin */
  155.   HAL_RTCEx_SetTimeStamp_IT(&RtcHandle, RTC_TIMESTAMPEDGE_RISING, RTC_TIMESTAMPPIN_DEFAULT);

  156.   /*##-2- Configure the Date #################################################*/
  157.   /* Set Date: Monday April 14th 2014 */
  158.   sdatestructure.Year    = 0x14;
  159.   sdatestructure.Month   = RTC_MONTH_APRIL;
  160.   sdatestructure.Date    = 0x14;
  161.   sdatestructure.WeekDay = RTC_WEEKDAY_MONDAY;
  162.   
  163.   if(HAL_RTC_SetDate(&RtcHandle,&sdatestructure,RTC_FORMAT_BCD) != HAL_OK)
  164.   {
  165.     /* Initialization Error */
  166.     rt_kprintf("HAL_RTC_SetDate failed\n");
  167.   }
  168.   
  169.   /*##-3- Configure the Time #################################################*/
  170.   /* Set Time: 08:10:00 */
  171.   stimestructure.Hours          = 0x08;
  172.   stimestructure.Minutes        = 0x10;
  173.   stimestructure.Seconds        = 0x00;
  174.   stimestructure.SubSeconds     = 0x00;
  175.   stimestructure.TimeFormat     = RTC_HOURFORMAT12_AM;
  176.   stimestructure.DayLightSaving = RTC_DAYLIGHTSAVING_NONE ;
  177.   stimestructure.StoreOperation = RTC_STOREOPERATION_RESET;
  178.   
  179.   if(HAL_RTC_SetTime(&RtcHandle,&stimestructure,RTC_FORMAT_BCD) != HAL_OK)
  180.   {
  181.     /* Initialization Error */
  182.     rt_kprintf("HAL_RTC_SetTime falied\n");
  183.   }
  184. }

  185. static int rt_l476_rtc_hw_init(void)
  186. {
  187. /*##-1- Configure the RTC peripheral #######################################*/
  188.   /* Configure RTC prescaler and RTC data registers */
  189.   /* RTC configured as follow:
  190.       - Hour Format    = Format 12
  191.       - Asynch Prediv  = Value according to source clock
  192.       - Synch Prediv   = Value according to source clock
  193.       - OutPut         = Output Disable
  194.       - OutPutPolarity = High Polarity
  195.       - OutPutType     = Open Drain */
  196.     __HAL_RTC_RESET_HANDLE_STATE(&RtcHandle);
  197.     RtcHandle.Instance = RTC;
  198.     RtcHandle.Init.HourFormat     = RTC_HOURFORMAT_12;
  199.     RtcHandle.Init.AsynchPrediv   = RTC_ASYNCH_PREDIV;
  200.     RtcHandle.Init.SynchPrediv    = RTC_SYNCH_PREDIV;
  201.     RtcHandle.Init.OutPut         = RTC_OUTPUT_DISABLE;
  202.     RtcHandle.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
  203.     RtcHandle.Init.OutPutType     = RTC_OUTPUT_TYPE_OPENDRAIN;

  204.     if(HAL_RTC_Init(&RtcHandle) != HAL_OK)
  205.     {
  206.         rt_kprintf("HAL_RTC_Init failed rtc state [%d]\n",RtcHandle.State);
  207.     }
  208.     RTC_TimeStampConfig();
  209.         return 0;
  210. }

  211. int rt_l476_rtc_init(void)
  212. {
  213.     //static rt_bool_t init_ok = RT_FALSE;
  214.     rt_kprintf("%s\n",__FUNCTION__);
  215.     rt_l476_rtc_hw_init();
  216.     l476_rtc_dev.type    = RT_Device_Class_RTC;

  217.     /* register rtc device */
  218. #ifdef RT_USING_DEVICE_OPS
  219.     l476_rtc_dev.ops     = &l476_rtc_ops;
  220.     rt_kprintf("define RT_USING_DEVICE_OPS \n");
  221. #else
  222.     l476_rtc_dev.init    = RT_NULL;
  223.     l476_rtc_dev.open    = RT_NULL;
  224.     l476_rtc_dev.close   = RT_NULL;
  225.     l476_rtc_dev.read    = RT_NULL;
  226.     l476_rtc_dev.write   = RT_NULL;
  227.     l476_rtc_dev.control = l476_rtc_control;
  228.     rt_kprintf("not define RT_USING_DEVICE_OPS \n");   
  229. #endif

  230.     /* no private */
  231.     l476_rtc_dev.user_data = RT_NULL;

  232.     rt_device_register(&l476_rtc_dev, "rtc", RT_DEVICE_FLAG_RDWR);

  233.     //init_ok = RT_TRUE;

  234.     return 0;   
  235. }

  236. INIT_DEVICE_EXPORT(rt_l476_rtc_init);
  237. #endif
复制代码
下载运行如下:设置的默认时间为2014-04-14 08:10:00
L476_RTC.PNG

运行结果和预期的一直,驱动加载log输出,说明RTC加载成功。




评分

参与人数 1 ST金币 +20 收起 理由
STMCU + 20

查看全部评分

收藏 评论4 发布时间:2018-12-2 09:19

举报

4个回答
一代睡神的崛起 回答时间:2018-12-2 09:34:40
我感觉我不是在看内核,而是在看时钟配置
andeyqi 回答时间:2018-12-2 10:58:57
一代睡神的崛起 发表于 2018-12-2 09:34
我感觉我不是在看内核,而是在看时钟配置

都差不多    把裸板程序放到驱动框架里面就行了
damiaa 回答时间:2018-12-4 12:11:34
感谢分享!!!
andeyqi 回答时间:2018-12-4 12:56:37
damiaa 发表于 2018-12-4 12:11
感谢分享!!!

感谢支持

所属标签

STM32团队

意法半导体微控制器和微处理器拥有广泛的产品线,包含低成本的8位单片机和基于ARM® Cortex®-M0、M0+、M3、M4、M33、M7及A7内核并具备丰富外设选择的32位微控制器及微处理器


最新内容

相似分享

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版