788|1

41

帖子

0

TA的资源

一粒金砂(中级)

楼主
 

雅特力车规级MCU-AT32A403A开发板评测 13 RTC外设测试 OELD模块时钟显示 RT-thread... [复制链接]

本帖最后由 みずじ 于 2024-3-6 22:10 编辑

雅特力车规级MCU-AT32A403A开发板评测 13 RTC外设测试 OELD模块时钟显示 RT-thread RTC设备驱动测试

1. 软硬件平台

  1. AT32A403A Board开发板
  2. MDK-ARM Keil
  3. RT-Thread V4.1版本源码
  4. RTC

2. RTC

​ RTC 计数逻辑位于电池供电域,内部为一个 32 位递增计数器,只要电池供电域有电,RTC 便会一 直运行,不受系统复位以及 VDD 掉电影响,RTC 主要具有以下功能:

  1. 日历功能:32 位计数器,通过转换得到年、月、日、时、分、秒
  2. 闹钟功能
  3. 入侵检测功能
  4. 校准功能

RTC日历功能

​ RTC 内部是一个 32 位的计数器,通常使用中该计数器 1 秒增加 1,也就是该计数器相当于秒钟,然 后根据当前的秒钟值,通过转换得到年、月、日、星期、时、分、秒,实现日历的功能,修改计数器 的值便可修改时间和日期。根据使用需要还可以产生秒中断:若秒中断使能(TSIEN=1),每隔一秒产生一个秒中断。

秒钟转换成日历 先规定一个起始时间,例如 1970-1-1 00:00:00 对应计数器为 0,现在比如计数值为 200000,那么 换算成时间为:

  • 天数:200000 / 86400 = 2
  • 小时:(200000 % 86400) / 3600= 7
  • 分钟:(200000 % 3600) / 60= 33
  • 秒钟:200000 % 60 = 20
设置日历值(日历转换成秒钟)
  1. /**
  2. * <a href="https://bbs.eeworld.com.cn/home.php?mod=space&uid=159083" target="_blank">@brief</a> set time. convert the input clock to a second.
  3. * the time basic : 1970.1.1
  4. * legitimate year: 1970 ~ 2099
  5. * @param calendar
  6. * @retval 0: set time right.
  7. * 1: set time failed.
  8. */
  9. uint8_t rtc_time_set(calendar_type *calendar)
  10. {
  11. uint32_t t;
  12. uint32_t seccount = 0;
  13. if(calendar->year < 1970 || calendar->year > 2099)
  14. {
  15. return 1;
  16. }
  17. for(t = 1970; t < calendar->year; t++)
  18. {
  19. if(is_leap_year(t))
  20. {
  21. seccount += 31622400;
  22. }
  23. else
  24. {
  25. seccount += 31536000;
  26. }
  27. }
  28. calendar->month -= 1;
  29. for(t = 0; t < calendar->month; t++)
  30. {
  31. seccount += (uint8_t)mon_table[t] * 86400;
  32. if(is_leap_year(calendar->year) && t == 1)
  33. {
  34. seccount += 86400;
  35. }
  36. }
  37. seccount += (uint8_t)(calendar->date - 1) * 86400;
  38. seccount += (uint8_t)calendar->hour * 3600;
  39. seccount += (uint8_t)calendar->min * 60;
  40. seccount += calendar->sec;
  41. /* enable pwc and bpr clocks */
  42. crm_periph_clock_enable(CRM_PWC_PERIPH_CLOCK, TRUE);
  43. crm_periph_clock_enable(CRM_BPR_PERIPH_CLOCK, TRUE);
  44. /* enable write access to bpr domain */
  45. pwc_battery_powered_domain_access(TRUE);
  46. /* set the rtc counter value */
  47. rtc_counter_set(seccount);
  48. /* wait for the register write to complete */
  49. rtc_wait_config_finish();
  50. return 0;
  51. }

3. 软件设计流程

  1. 开启 PWC、BPR 时钟
  2. 解锁电池供电域写保护
  3. 检查日历是否已经初始化,如果正确就跳过初始化,如果不正确就初始化日历以及闹钟 (初始化日历时间 2024-03-06 19:40:00)
  4. 主函数里每秒打印一次日历信息
  5. 在 2024-03-06 19:40:15 时刻发生闹钟,串口进行打印并且LED亮。
  1. #include "main.h"
  2. void oled_example(void)
  3. {
  4. //OLED 汉字显示 雅特力科技
  5. OLED_ShowCHinese(0,0,13);
  6. OLED_ShowCHinese(16,0,14);
  7. OLED_ShowCHinese(32,0,15);
  8. OLED_ShowCHinese(48,0,16);
  9. OLED_ShowCHinese(64,0,17);
  10. OLED_ShowString_08x16(96,0,"RTC");
  11. }
  12. char *weekday_table[7] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
  13. /**
  14. * @brief init alarm.
  15. * @param none
  16. * @retval none
  17. */
  18. void alarm_init(void)
  19. {
  20. calendar_type alarm_struct;
  21. /* clear alarm flag */
  22. rtc_flag_clear(RTC_TA_FLAG);
  23. /* wait for the register write to complete */
  24. rtc_wait_config_finish();
  25. /* configure and enable rtc interrupt */
  26. nvic_irq_enable(RTC_IRQn, 0, 0);
  27. /* enable alarm interrupt */
  28. rtc_interrupt_enable(RTC_TA_INT, TRUE);
  29. /* wait for the register write to complete */
  30. rtc_wait_config_finish();
  31. /* config alarm */
  32. alarm_struct.year = 2024;
  33. alarm_struct.month = 3;
  34. alarm_struct.date = 6;
  35. alarm_struct.hour = 19;
  36. alarm_struct.min = 40;
  37. alarm_struct.sec = 15;
  38. rtc_alarm_clock_set(&alarm_struct);
  39. }
  40. /**
  41. * @brief main function.
  42. * @param none
  43. * @retval none
  44. */
  45. int main(void)
  46. {
  47. // unsigned char count_num;
  48. calendar_type time_struct;
  49. /* config nvic priority group */
  50. nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);
  51. system_clock_config();
  52. at32_board_init();
  53. uart_print_init(115200);
  54. oled_init();
  55. /* config init calendar */
  56. time_struct.year = 2024;
  57. time_struct.month = 3;
  58. time_struct.date = 6;
  59. time_struct.hour = 19;
  60. time_struct.min = 40;
  61. time_struct.sec = 0;
  62. rtc_init(&time_struct);
  63. /* config alarm */
  64. alarm_init();
  65. printf("initial ok\r\n");
  66. printf("Hardware_Init [ok] \r\n");
  67. printf("at_start_a403a board testing 2024-03-06\r\n");
  68. printf("at_start_a403a board module softiic oled \r\n");
  69. OLED_Clear();
  70. printf("rtc_oled_example_test [ok] \r\n");
  71. oled_example();
  72. while(1)
  73. {
  74. if(rtc_flag_get(RTC_TS_FLAG) != RESET)
  75. {
  76. at32_led_toggle(LED3);
  77. /* get time */
  78. rtc_time_get();
  79. /* print time */
  80. printf("%d/%d/%d ", calendar.year, calendar.month, calendar.date);
  81. printf("%02d:%02d:%02d %s\r\n", calendar.hour, calendar.min, calendar.sec, weekday_table[calendar.week]);
  82. OLED_ShowNumber_UnsignedInteger_08x16(0,2,calendar.year,4);
  83. OLED_ShowString_08x16(32,2,"-");
  84. OLED_ShowNumber_UnsignedInteger_08x16(40,2,calendar.month,2);
  85. OLED_ShowString_08x16(56,2,"-");
  86. OLED_ShowNumber_UnsignedInteger_08x16(64,2,calendar.date,2);
  87. OLED_ShowNumber_UnsignedInteger_08x16(0,4,calendar.hour,2);
  88. OLED_ShowString_08x16(16,4,":");
  89. OLED_ShowNumber_UnsignedInteger_08x16(24,4,calendar.min,2);
  90. OLED_ShowString_08x16(40,4,":");
  91. OLED_ShowNumber_UnsignedInteger_08x16(48,4,calendar.sec,2);
  92. OLED_ShowString_08x16(32,6,weekday_table[calendar.week]);
  93. /* wait for the register write to complete */
  94. rtc_wait_config_finish();
  95. /* clear the rtc second flag */
  96. rtc_flag_clear(RTC_TS_FLAG);
  97. /* wait for the register write to complete */
  98. rtc_wait_config_finish();
  99. }
  100. }
  101. }

4.测试效果

  1. 闹钟定时效果
  1. 复位重启,时间继续进行(恢复原始状态,需要断电)
  1. OLED模块显示效果
播放器加载失败: 未检测到Flash Player,请到安装
at32-rtc

### 5.RT-Thread RTC设备驱动测试

RT-Thread 提供了一套简单的 I/O 设备模型框架,如下图所示,它位于硬件和应用程序之间,共分成三层,从上到下分别是 I/O 设备管理层、设备驱动框架层、设备驱动层。本质上设备驱动框架层、设备驱动层都是都是对于原本的库函数进行二次封装,最后提供一个统一的API接口共应用层开发。在本文使用了RTC设备。

RTC (Real-Time Clock)实时时钟可以提供精确的实时时间,它可以用于产生年、月、日、时、分、秒等信息。目前实时时钟芯片大多采用精度较高的晶体振荡器作为时钟源。有些时钟芯片为了在主电源掉电时还可以工作,会外加电池供电,使时间信息一直保持有效。

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

访问 RTC 设备

在开启 RTC 设备框架以及 RTC 驱动之后,用户可以 #include <sys/time.h> 用来引用标准的时间操作函数(例如 time、ctime、stime、mktime等,具体使用方法可以百度)。在Unix系统或者Windows系统下怎么使用 <time.h> 里边的函数,在RT-Thread下就怎么使用。建议用户采用标准库时间函数来操作 RTC。

  1. 在RT-Thread工程模板下,添加相关RTC驱动
  1. 添加相关头文件,重点是time.h的添加,如果不添加,会调用MDK提供的底层库,但是会报错
  1. 添加RTC测试案例代码
  1. #include <rtthread.h>
  2. #include <rtdevice.h>
  3. #include "board.h"
  4. #include "drv_gpio.h"
  5. #define RTC_NAME "rtc"
  6. int main()
  7. {
  8. rt_err_t ret = RT_EOK;
  9. time_t now;
  10. rt_device_t device = RT_NULL;
  11. /* 寻找设备 */
  12. device = rt_device_find(RTC_NAME);
  13. if (!device)
  14. {
  15. rt_kprintf("find %s failed!", RTC_NAME);
  16. return RT_ERROR;
  17. }
  18. /* 初始化RTC设备 */
  19. if(rt_device_open(device, 0) != RT_EOK)
  20. {
  21. rt_kprintf("open %s failed!", RTC_NAME);
  22. return RT_ERROR;
  23. }
  24. /* 设置日期 */
  25. ret = set_date(2024, 3, 6);
  26. if (ret != RT_EOK)
  27. {
  28. rt_kprintf("set RTC date failed\n");
  29. return ret;
  30. }
  31. /* 设置时间 */
  32. ret = set_time(17, 43, 00);
  33. if (ret != RT_EOK)
  34. {
  35. rt_kprintf("set RTC time failed\n");
  36. return ret;
  37. }
  38. /* 获取时间 */
  39. now = time(RT_NULL);
  40. rt_kprintf("%s\n", ctime(&now));
  41. /* 延时1秒 */
  42. rt_thread_mdelay(1000);
  43. /* 获取时间 */
  44. now = time(RT_NULL);
  45. rt_kprintf("%s\n", ctime(&now));
  46. return ret;
  47. }
  48. void user_alarm_callback(rt_alarm_t alarm, time_t timestamp)
  49. {
  50. rt_kprintf("user alarm callback function.\n");
  51. }
  52. void alarm_sample(void)
  53. {
  54. struct rt_alarm_setup setup;
  55. struct rt_alarm * alarm = RT_NULL;
  56. static time_t now;
  57. struct tm p_tm;
  58. if (alarm != RT_NULL)
  59. return;
  60. /* 获取当前时间戳,并把下5秒时间设置为闹钟时间 */
  61. now = time(NULL) + 5;
  62. gmtime_r(&now,&p_tm);
  63. setup.flag = RT_ALARM_ONESHOT;
  64. setup.wktime.tm_year = p_tm.tm_year;
  65. setup.wktime.tm_mon = p_tm.tm_mon;
  66. setup.wktime.tm_mday = p_tm.tm_mday;
  67. setup.wktime.tm_wday = p_tm.tm_wday;
  68. setup.wktime.tm_hour = p_tm.tm_hour;
  69. setup.wktime.tm_min = p_tm.tm_min;
  70. setup.wktime.tm_sec = p_tm.tm_sec;
  71. alarm = rt_alarm_create(user_alarm_callback, &setup);
  72. if(RT_NULL != alarm)
  73. {
  74. rt_alarm_start(alarm);
  75. }
  76. }
  77. /* export msh cmd */
  78. MSH_CMD_EXPORT(alarm_sample,alarm sample);
  1. 测试效果(利用FINSH进行调试)
查看精华帖全部内容,请登录或者注册
此帖出自汽车电子论坛

最新回复

RTC 计数逻辑位于电池供电域,内部为一个 32 位递增计数器,会自动进行年月日时分秒的功能区分吗   详情 回复 发表于 2024-3-7 17:22
点赞 关注

回复
举报

6876

帖子

10

TA的资源

版主

沙发
 

RTC 计数逻辑位于电池供电域,内部为一个 32 位递增计数器,会自动进行年月日时分秒的功能区分吗

此帖出自汽车电子论坛
个人签名

在爱好的道路上不断前进,在生活的迷雾中播撒光引

 
 

回复
您需要登录后才可以回帖 登录 | 注册

开源项目 更多>>
    随便看看
    查找数据手册?

    EEWorld Datasheet 技术支持

    相关文章 更多>>
      关闭
      站长推荐上一条 1/10 下一条
      中星联华&ADI明日直播
      直播主题:大咖面对面,轻松玩转高速ADC性能测试
      直播时间:3月25日(周二)14:00
      活动奖励:京东卡、双肩包

      查看 »

       
      EEWorld订阅号

       
      EEWorld服务号

       
      汽车开发圈

       
      机器人开发圈

      About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

      站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网 14

      北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

      电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
      快速回复 返回顶部 返回列表