本帖最后由 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函数即可。
- /*
- * Copyright (c) 2006-2018, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date Author Notes
- * 2018-01-30 armink the first version
- */
- #include <time.h>
- #include <string.h>
- #include <rtthread.h>
- #include <drivers/rtc.h>
- #ifdef RT_USING_SOFT_RTC
- /* 2018-01-30 14:44:50 = RTC_TIME_INIT(2018, 1, 30, 14, 44, 50) */
- #define RTC_TIME_INIT(year, month, day, hour, minute, second) \
- {.tm_year = year - 1900, .tm_mon = month - 1, .tm_mday = day, .tm_hour = hour, .tm_min = minute, .tm_sec = second}
- #ifndef SOFT_RTC_TIME_DEFAULT
- #define SOFT_RTC_TIME_DEFAULT RTC_TIME_INIT(2018, 1, 1, 0, 0 ,0)
- #endif
- static struct rt_device soft_rtc_dev;
- static rt_tick_t init_tick;
- static time_t init_time;
- static rt_err_t soft_rtc_control(rt_device_t dev, int cmd, void *args)
- {
- time_t *time;
- struct tm time_temp;
- RT_ASSERT(dev != RT_NULL);
- memset(&time_temp, 0, sizeof(struct tm));
- switch (cmd)
- {
- case RT_DEVICE_CTRL_RTC_GET_TIME:
- time = (time_t *) args;
- *time = init_time + (rt_tick_get() - init_tick) / RT_TICK_PER_SECOND;
- break;
- case RT_DEVICE_CTRL_RTC_SET_TIME:
- {
- time = (time_t *) args;
- init_time = *time - (rt_tick_get() - init_tick) / RT_TICK_PER_SECOND;
- break;
- }
- }
- return RT_EOK;
- }
- #ifdef RT_USING_DEVICE_OPS
- const static struct rt_device_ops soft_rtc_ops =
- {
- RT_NULL,
- RT_NULL,
- RT_NULL,
- RT_NULL,
- RT_NULL,
- soft_rtc_control
- };
- #endif
- int rt_soft_rtc_init(void)
- {
- static rt_bool_t init_ok = RT_FALSE;
- struct tm time_new = SOFT_RTC_TIME_DEFAULT;
- if (init_ok)
- {
- return 0;
- }
- /* make sure only one 'rtc' device */
- RT_ASSERT(!rt_device_find("rtc"));
- init_tick = rt_tick_get();
- init_time = mktime(&time_new);
- soft_rtc_dev.type = RT_Device_Class_RTC;
- /* register rtc device */
- #ifdef RT_USING_DEVICE_OPS
- soft_rtc_dev.ops = &soft_rtc_ops;
- #else
- soft_rtc_dev.init = RT_NULL;
- soft_rtc_dev.open = RT_NULL;
- soft_rtc_dev.close = RT_NULL;
- soft_rtc_dev.read = RT_NULL;
- soft_rtc_dev.write = RT_NULL;
- soft_rtc_dev.control = soft_rtc_control;
- #endif
- /* no private */
- soft_rtc_dev.user_data = RT_NULL;
- rt_device_register(&soft_rtc_dev, "rtc", RT_DEVICE_FLAG_RDWR);
- init_ok = RT_TRUE;
- return 0;
- }
- INIT_DEVICE_EXPORT(rt_soft_rtc_init);
- #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,配置编译运行即可,如下图。
根据上面分析,驱动实现RTC的初始化和control函数即可,关闭软甲模拟RTC,在bsp目录下添加\rt-thread-master\bsp\stm32l476-nucleo\driversdrv_rtc.c并重新编译。代码如下: - #include <rthw.h>
- #include <rtdevice.h>
- #include <board.h>
- #include <time.h>
- #include <string.h>
- #include <drivers/rtc.h>
- #ifndef RT_USING_SOFT_RTC
- /* RTC handler declaration */
- RTC_HandleTypeDef RtcHandle;
- /* Uncomment to enable the adaquate Clock Source */
- #define RTC_CLOCK_SOURCE_LSI
- /*#define RTC_CLOCK_SOURCE_LSE*/
- #ifdef RTC_CLOCK_SOURCE_LSI
- #define RTC_ASYNCH_PREDIV 0x7F
- #define RTC_SYNCH_PREDIV 0xF9
- #endif
- #ifdef RTC_CLOCK_SOURCE_LSE
- #define RTC_ASYNCH_PREDIV 0x7F
- #define RTC_SYNCH_PREDIV 0x00FF
- #endif
- void HAL_RTC_MspInit(RTC_HandleTypeDef *hrtc)
- {
- RCC_OscInitTypeDef RCC_OscInitStruct;
- RCC_PeriphCLKInitTypeDef PeriphClkInitStruct;
-
- /*##-1- Enables the PWR Clock and Enables access to the backup domain ###################################*/
- /* To change the source clock of the RTC feature (LSE, LSI), You have to:
- - Enable the power clock using __HAL_RCC_PWR_CLK_ENABLE()
- - Enable write access using HAL_PWR_EnableBkUpAccess() function before to
- configure the RTC clock source (to be done once after reset).
- - Reset the Back up Domain using __HAL_RCC_BACKUPRESET_FORCE() and
- __HAL_RCC_BACKUPRESET_RELEASE().
- - Configure the needed RTC clock source */
- __HAL_RCC_PWR_CLK_ENABLE();
- HAL_PWR_EnableBkUpAccess();
- /*##-2- Configure LSE/LSI as RTC clock source ###############################*/
- #ifdef RTC_CLOCK_SOURCE_LSE
-
- RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI | RCC_OSCILLATORTYPE_LSE;
- RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
- RCC_OscInitStruct.LSEState = RCC_LSE_ON;
- RCC_OscInitStruct.LSIState = RCC_LSI_OFF;
- if(HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
- {
- Error_Handler();
- }
-
- PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC;
- PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
- if(HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
- {
- Error_Handler();
- }
- #elif defined (RTC_CLOCK_SOURCE_LSI)
- RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI | RCC_OSCILLATORTYPE_LSE;
- RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
- RCC_OscInitStruct.LSIState = RCC_LSI_ON;
- RCC_OscInitStruct.LSEState = RCC_LSE_OFF;
- if(HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
- {
- //Error_Handler();
- }
- PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC;
- PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSI;
- if(HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
- {
- //Error_Handler();
- }
- #else
- #error Please select the RTC Clock source inside the main.h file
- #endif /*RTC_CLOCK_SOURCE_LSE*/
-
- /*##-3- Enable RTC peripheral Clocks #######################################*/
- /* Enable RTC Clock */
- __HAL_RCC_RTC_ENABLE();
-
- /*##-4- Configure the NVIC for RTC TimeStamp ###############################*/
- HAL_NVIC_SetPriority(TAMP_STAMP_IRQn, 0x0F, 0);
- HAL_NVIC_EnableIRQ(TAMP_STAMP_IRQn);
- }
- static rt_err_t l476_rtc_control(rt_device_t dev, int cmd, void *args)
- {
- //rt_kprintf("%s\n",__FUNCTION__);
- RTC_DateTypeDef sdatestructureget;
- RTC_TimeTypeDef stimestructureget;
- time_t *time;
-
- switch (cmd)
- {
- case RT_DEVICE_CTRL_RTC_GET_TIME:
- time = (time_t *)args;
- struct tm tm_new;
- //rt_kprintf("entry RT_DEVICE_CTRL_RTC_GET_TIME \n");
- /* Get the RTC current Time */
- HAL_RTC_GetTime(&RtcHandle, &stimestructureget, RTC_FORMAT_BIN);
- /* Get the RTC current Date */
- HAL_RTC_GetDate(&RtcHandle, &sdatestructureget, RTC_FORMAT_BIN);
- tm_new.tm_year = 2000 + sdatestructureget.Year -1900;
- tm_new.tm_mon = sdatestructureget.Month-1;
- tm_new.tm_mday = sdatestructureget.Date;
- tm_new.tm_wday = sdatestructureget.WeekDay;
-
- tm_new.tm_hour = stimestructureget.Hours;
- tm_new.tm_min = stimestructureget.Minutes;
- tm_new.tm_sec = stimestructureget.Seconds;
- *time = mktime(&tm_new);
- /* Display time Format : hh:mm:ss */
- //rt_kprintf("%.2d:%.2d:%.2d", stimestructureget.Hours, stimestructureget.Minutes, stimestructureget.Seconds);
- /* Display date Format : mm-dd-yy */
- //rt_kprintf("%.2d-%.2d-%.2d", sdatestructureget.Month, sdatestructureget.Date, 2000 + sdatestructureget.Year);
- break;
- case RT_DEVICE_CTRL_RTC_SET_TIME:
- {
- const struct tm* tm_now;
- time = (time_t *)args;
- tm_now = localtime(time);
- stimestructureget.Hours = tm_now->tm_hour;
- stimestructureget.Minutes= tm_now->tm_min;
- stimestructureget.Seconds = tm_now->tm_sec;
-
- sdatestructureget.Year = tm_now->tm_year + 1900 -2000;
- sdatestructureget.Month = tm_now->tm_mon+1;
- sdatestructureget.Date = tm_now->tm_mday;
- sdatestructureget.WeekDay = tm_now->tm_wday;
-
- HAL_RTC_SetTime(&RtcHandle,&stimestructureget, RTC_FORMAT_BIN);
- HAL_RTC_SetDate(&RtcHandle, &sdatestructureget, RTC_FORMAT_BIN);
- //rt_kprintf("entry RT_DEVICE_CTRL_RTC_SET_TIME \n");
- break;
- }
- }
- return RT_EOK;
- }
- const static struct rt_device_ops l476_rtc_ops =
- {
- RT_NULL,
- RT_NULL,
- RT_NULL,
- RT_NULL,
- RT_NULL,
- l476_rtc_control
- };
- static struct rt_device l476_rtc_dev;
- /**
- * @brief Configure the current time and date and activate timestamp.
- * @param None
- * @retval None
- */
- static void RTC_TimeStampConfig(void)
- {
- RTC_DateTypeDef sdatestructure;
- RTC_TimeTypeDef stimestructure;
- /*##-1- Configure the Time Stamp peripheral ################################*/
- /* RTC TimeStamp generation: TimeStamp Rising Edge on PC.13 Pin */
- HAL_RTCEx_SetTimeStamp_IT(&RtcHandle, RTC_TIMESTAMPEDGE_RISING, RTC_TIMESTAMPPIN_DEFAULT);
- /*##-2- Configure the Date #################################################*/
- /* Set Date: Monday April 14th 2014 */
- sdatestructure.Year = 0x14;
- sdatestructure.Month = RTC_MONTH_APRIL;
- sdatestructure.Date = 0x14;
- sdatestructure.WeekDay = RTC_WEEKDAY_MONDAY;
-
- if(HAL_RTC_SetDate(&RtcHandle,&sdatestructure,RTC_FORMAT_BCD) != HAL_OK)
- {
- /* Initialization Error */
- rt_kprintf("HAL_RTC_SetDate failed\n");
- }
-
- /*##-3- Configure the Time #################################################*/
- /* Set Time: 08:10:00 */
- stimestructure.Hours = 0x08;
- stimestructure.Minutes = 0x10;
- stimestructure.Seconds = 0x00;
- stimestructure.SubSeconds = 0x00;
- stimestructure.TimeFormat = RTC_HOURFORMAT12_AM;
- stimestructure.DayLightSaving = RTC_DAYLIGHTSAVING_NONE ;
- stimestructure.StoreOperation = RTC_STOREOPERATION_RESET;
-
- if(HAL_RTC_SetTime(&RtcHandle,&stimestructure,RTC_FORMAT_BCD) != HAL_OK)
- {
- /* Initialization Error */
- rt_kprintf("HAL_RTC_SetTime falied\n");
- }
- }
- static int rt_l476_rtc_hw_init(void)
- {
- /*##-1- Configure the RTC peripheral #######################################*/
- /* Configure RTC prescaler and RTC data registers */
- /* RTC configured as follow:
- - Hour Format = Format 12
- - Asynch Prediv = Value according to source clock
- - Synch Prediv = Value according to source clock
- - OutPut = Output Disable
- - OutPutPolarity = High Polarity
- - OutPutType = Open Drain */
- __HAL_RTC_RESET_HANDLE_STATE(&RtcHandle);
- RtcHandle.Instance = RTC;
- RtcHandle.Init.HourFormat = RTC_HOURFORMAT_12;
- RtcHandle.Init.AsynchPrediv = RTC_ASYNCH_PREDIV;
- RtcHandle.Init.SynchPrediv = RTC_SYNCH_PREDIV;
- RtcHandle.Init.OutPut = RTC_OUTPUT_DISABLE;
- RtcHandle.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
- RtcHandle.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
- if(HAL_RTC_Init(&RtcHandle) != HAL_OK)
- {
- rt_kprintf("HAL_RTC_Init failed rtc state [%d]\n",RtcHandle.State);
- }
- RTC_TimeStampConfig();
- return 0;
- }
- int rt_l476_rtc_init(void)
- {
- //static rt_bool_t init_ok = RT_FALSE;
- rt_kprintf("%s\n",__FUNCTION__);
- rt_l476_rtc_hw_init();
- l476_rtc_dev.type = RT_Device_Class_RTC;
- /* register rtc device */
- #ifdef RT_USING_DEVICE_OPS
- l476_rtc_dev.ops = &l476_rtc_ops;
- rt_kprintf("define RT_USING_DEVICE_OPS \n");
- #else
- l476_rtc_dev.init = RT_NULL;
- l476_rtc_dev.open = RT_NULL;
- l476_rtc_dev.close = RT_NULL;
- l476_rtc_dev.read = RT_NULL;
- l476_rtc_dev.write = RT_NULL;
- l476_rtc_dev.control = l476_rtc_control;
- rt_kprintf("not define RT_USING_DEVICE_OPS \n");
- #endif
- /* no private */
- l476_rtc_dev.user_data = RT_NULL;
- rt_device_register(&l476_rtc_dev, "rtc", RT_DEVICE_FLAG_RDWR);
- //init_ok = RT_TRUE;
- return 0;
- }
- INIT_DEVICE_EXPORT(rt_l476_rtc_init);
- #endif
复制代码下载运行如下:设置的默认时间为2014-04-14 08:10:00
运行结果和预期的一直,驱动加载log输出,说明RTC加载成功。
|
都差不多 把裸板程序放到驱动框架里面就行了
感谢支持