[AT32F403/403A] AT32F403 的电池供电域入门指南

[复制链接]
Enthusiast 发表于 2025-8-12 18:42 | 显示全部楼层 |阅读模式
本帖最后由 Enthusiast 于 2025-8-12 18:48 编辑

AT32F403 的电池供电域入门指南
AT32F403 的电池供电域包含 RTC、电池供电数据寄存器等功能,RTC 接口用于实现日历、时钟功能,本文主要就 RTC 的基本功能进行讲解和案列解析。注:本应用笔记对应的代码是基于雅特力提供的V2.x.x 板级支持包(BSP)而开发,对于其他版本BSP,需要注意使用上的区别。

1 RTC 接口简介
RTC 计数逻辑位于电池供电域,内部为一个 32 位递增计数器,只要电池供电域有电,RTC 便会一直运行,不受系统复位以及 VDD 掉电影响,RTC 主要具有以下功能:
― 日历功能:32 位计数器,通过转换得到年、月、日、时、分、秒
― 闹钟功能
― 入侵检测功能
― 校准功能

2 RTC 功能
2.1 寄存器访问
寄存器写保护
上电复位后 RTC 寄4.2 sBPWEN 位操作注意事项在程序中不要去开关 BPWEN 位,若要操作 BPR 数据,在代码初始化时将 PWC、BPR 时钟和BPWEN 位开启:存器处于写保护状态,需要先解除写保护,才能写配置 RTC 寄存器。解锁步骤:
1) 使能 PWC 接口时钟
  1. crm_periph_clock_enable(CRM_PWC_PERIPH_CLOCK, TRUE);
2) 使能 BPR 接口时钟
  1. crm_periph_clock_enable(CRM_BPR_PERIPH_CLOCK, TRUE);
3) 解锁电池供电域写保护
  1. pwc_battery_powered_domain_access(TRUE);
RTC 寄存器同步
由于 RTC 由电池供电域的计数逻辑和 APB1 接口的寄存器组成,寄存器的读写存在同步逻辑。
― 寄存器写:需要等待上一次的 RTC 寄存器配置完成后(CFGF = 1),才能进行新的写操作。
― 寄存器读:当寄存器值从电池供电域更新到 APB1 接口时 UPDF 标志置 1。当在系统复位、电源复位、从待机、深度睡眠模式唤醒后,有可能寄存器还未完全同步,所以需要先软件将 UPDF标志清除,然后等待 UPDF 标志置 1,以读取正确的值。
RTC 同步相关函数
等待上一次 RTC 寄存器配置完成(写寄存器之前使用)
  1. void rtc_wait_config_finish(void);
等待 RTC 寄存器更新完成(读取寄存器之前使用)
  1. void rtc_wait_update_finish(void);
RTC 寄存器写
写 RTC_DIV、RTC_TA、RTC_CNT 寄存器需要先进入配置模式(CFGEN = 1),然后才能对寄存器进行写操作,当退出配置模式(CFGEN = 0)时,就会将寄存器值实际写到电池供电域,这个过程至少需要 3 个 RTCCLK 周期。
下表列举了 RTC 寄存器受写保护状态,以及写入的条件:


寄存器复位
RTC 寄存器处于电池供电域,可以 CRM_BPDC 的 BPDRST 进行电池供电域复位,也可以由提供的库函数对每个寄存器写默认值进行复位。
RTC 复位相关函数
电池供电域复位
  1. void crm_battery_powered_domain_reset(confirm_state new_state);
或者
  1. void bpr_reset(void);
两个函数功能一样,只是 bpr_reset()封装了前一个函数。
2.2 时钟设置
时钟源选择RTC 时钟源经过选择后输入到分频器,最终得到 1Hz 的时钟用来更新日历。


RTC 的时钟源共有 3 种可以选择:
― LEXT:外部低速晶振,通常为 32.768kHz
― LICK:内部低速晶振,通常典型值为 40kHz 范围(30~60kHz),详情请见各型号的 datasheet
― HEXT_DIV:外部高速晶振分频后得到的时钟,不同型号分频值请见下表

                         RTC 时钟源设置相关函数
选择对应时钟使能
  1. void crm_clock_source_enable(crm_clock_source_type source, confirm_state new_state)
选择 RTC 时钟
  1. void crm_rtc_clock_select(crm_rtc_clock_type value)
使能 RTC 时钟
  1. void crm_rtc_clock_enable(confirm_state new_state)
预分频器设置RTC_CLK 通过 20 位预分频器后获得 1Hz 时钟,计算公式如下:


RTC 分频设置相关函数
设置 RTC 预分频器
  1. void rtc_divider_set(uint32_t div_value);
获取 RTC 预分频器值
  1. uint32_t rtc_divider_get(void);
RTC 时钟初始化举例:
  1. /* 使能 LEXT 时钟 */
  2. crm_clock_source_enable(CRM_CLOCK_SOURCE_LEXT, TRUE);
  3. /* 等待 LEXT 时钟稳定 */
  4. while(crm_flag_get(CRM_LEXT_STABLE_FLAG) == RESET)
  5. {
  6. }
  7. /* 选择 LEXT 时钟作为 RTC 时钟 */
  8. crm_rtc_clock_select(CRM_RTC_CLOCK_LEXT);
  9. /* 使能 RTC 时钟 */
  10. crm_rtc_clock_enable(TRUE);
  11. /* 配置 RTC 分频器 DIV=32767 */
  12. rtc_divider_set(32767)
2.3 日历
RTC 内部是一个 32 位的计数器,通常使用中该计数器 1 秒增加 1,也就是该计数器相当于秒钟,然后根据当前的秒钟值,通过转换得到年、月、日、星期、时、分、秒,实现日历的功能,修改计数器的值便可修改时间和日期。
根据使用需要还可以产生秒中断:若秒中断使能(TSIEN=1),每隔一秒产生一个秒中断。
  

计数相关函数
设置 RTC 计数值
  1. void rtc_counter_set(uint32_t counter_value);
获取 RTC 计数值
  1. uint32_t rtc_counter_get(void);
秒钟转换成日历先规定一个起始时间,例如 1970-1-1 00:00:00 对应计数器为 0,现在比如计数值为 200000,那么换算成时间为:
― 天数:200000 / 86400 = 2
― 小时:(200000 % 86400) / 3600= 7
― 分钟:(200000 % 3600) / 60= 33
― 秒钟:200000 % 60 = 20
所以现在的时间对应为 1970-1-3 07:33:20,对应日历转换成秒钟也是相同的思路。
在 BSP 的例程 project\at_start_f403a\examples\rtc\calendar 中,我们提供了秒钟与日历的相互转换函数。
设置日历值(日历转换成秒钟)
  1. uint8_t rtc_time_set(calendar_type *calendar);
结构体 calendar_type 里面参数含义如下:
― year:年
― month:月
― day:日
― hour:时
― min:分
― sec:秒
― week:星期几
读取日历值(秒钟转换成日历)
  1. void rtc_time_get(void);
2.4 闹钟
RTC 闹钟是一个 32 位的值,当闹钟值和计数值相等时产生闹钟事件(TAF 置 1),当中断使能时,会产生中断。


闹钟相关函数
闹钟值设置函数
  1. void rtc_alarm_set(uint32_t alarm_value)
中断使能函数
  1. void rtc_interrupt_enable(uint16_t source, confirm_state new_state);
标志获取函数
  1. flag_status rtc_flag_get(uint16_t flag);
标志清除函数
  1. void rtc_flag_clear(uint16_t flag);
2.5 计数值溢出
由于计数值为 32 位,所以存在溢出问题,当计数值为 0xFFFFFFFF 溢出到 0x00000000 时,产生溢出事件,OVFF 标志置 1 当闹钟使能后,由于溢出后,秒与日历的相转换关系便不正确,所以用户需妥善处理溢出事件。
0xFFFFFFFF 所能代表的最大时间为 136 年,例程起始时间为 1975,所以能够到 2106 年不溢出。


2.6 中断
当发生闹钟、秒、溢出事件时,RTC 可产生中断。闹钟中断有两种配置模式:
― 不配置 EXINT 线使用 RTC_IRQn 中断向量,此种方式不能唤醒 DEEPSLEEP 和 STANDBY 模式;
― 配置 EXINT 线使用 RTCAlarm_IRQn 中断向量,此种方式可以唤醒 DEEPSLEEP 和 STANDBY模式。
要使能 RTC 闹钟(不需要唤醒低功耗模式)、秒、溢出中断可按以下操作配置:
― 使能 RTC 中断对应的 NVIC 通道。
― 使能对应的 RTC 中断控制位。
要使能 RTC 闹钟(需要唤醒低功耗模式)中断可按以下操作配置:
― EXINT 线 17 配置为中断模式并使能,有效沿选择上升沿。
― 使能 RTC 中断对应的 NVIC 通道。
― 使能对应的 RTC 中断控制位。
下表说明了 RTC 时钟源、事件以及中断对唤醒低功耗模式的影响:





中断、事件相关函数
中断使能函数
  1. void rtc_interrupt_enable(uint16_t source, confirm_state new_state);
标志获取函数
  1. flag_status rtc_flag_get(uint16_t flag);
标志清除函数
  1. void rtc_flag_clear(uint16_t flag);
中断配置示例 1:以闹钟为例,使用 RTCAlarm_IRQn 中断向量
  1. /* 配置 EXINT 线 */
  2. exint_default_para_init(&exint_init_struct);
  3. exint_init_struct.line_enable = TRUE; //使能
  4. exint_init_struct.line_mode = EXINT_LINE_INTERRUPUT; //中断模式
  5. exint_init_struct.line_select = EXINT_LINE_17;//EXINT 线 17
  6. exint_init_struct.line_polarity = EXINT_TRIGGER_RISING_EDGE;//上升沿触发
  7. exint_init(&exint_init_struct);

  8. /* 使能闹钟中断 NVIC 向量:RTCAlarm_IRQn */
  9. nvic_irq_enable(RTCAlarm_IRQn, 0, 1);
  10. /* 设置闹钟值 */
  11. rtc_alarm_set(seccount);
  12. /* 使能闹钟中断 */
  13. rtc_interrupt_enable(RTC_TA_INT, TRUE);
中断处理函数
  1. void RTCAlarm_IRQHandler(void)
  2. {
  3. if(rtc_flag_get(RTC_TA_FLAG) != RESET)
  4. {
  5. /* 清除闹钟标志 */
  6. rtc_flag_clear(RTC_TA_FLAG);

  7. /* 清除 EXINT 线 17 标志 */
  8. exint_flag_clear(EXINT_LINE_17);
  9. }
  10. }
中断配置示例 2:以闹钟为例,使用 RTC_IRQn 中断向量
  1. /* 使能闹钟中断 NVIC 向量:RTC_IRQn */
  2. nvic_irq_enable(RTC_IRQn, 0, 1);
  3. /* 设置闹钟值 */
  4. rtc_alarm_set(seccount);
  5. /* 使能闹钟中断 */
  6. rtc_interrupt_enable(RTC_TA_INT, TRUE);
中断处理函数
  1. void RTC_IRQHandler(void)
  2. {
  3. if(rtc_flag_get(RTC_TA_FLAG) != RESET)
  4. {
  5. /* 清除闹钟标志 */
  6. rtc_flag_clear(RTC_TA_FLAG);
  7. }
  8. }
3 电池供电域功能
3.1 电池供电数据寄存器
电池供电域一共提供了 42 个 16 位电池供电数据寄存器,可以在只由电池供电下保存数据,不会被系统复位所复位,只能通过电池供电域复位或入侵事件进行复位。在写电池供电数据寄存器时,需要先解除读保护,解锁方式同 2.1 章节相同。
电池供电域数据操作相关函数
写电池供电数据寄存器
  1. void bpr_data_write(bpr_data_type bpr_data, uint16_t data_value);
读电池供电数据寄存器
  1. uint16_t bpr_data_read(bpr_data_type bpr_data);
电池供电域复位
  1. void bpr_reset(void);
3.2 RTC 校准
电池供电域还提供了 RTC 校准功能,通过 RTC_CALVAL 寄存器进行配置。

当 RTC_CLK 为 32.768kHz 时,校准周期为 220 个 RTC_CLK 约 32 秒。CALVAL[7:0]值指定了 220个 RTC_CLK 中忽略的脉冲数,最多可忽略 127 个脉冲,这可以将时钟调慢,调慢范围为0~121ppm。
可以选择将校准前或校准后的 RTC 时钟 64 分频后输出到 PC13 脚。
校准设置相关函数
校准值设置函数
  1. void bpr_rtc_clock_calibration_value_set(uint8_t calibration_value);
校准时钟输出设置函数
  1. void bpr_rtc_output_select(bpr_rtc_output_type output_source);
3.3 入侵检测
电池供电域提供了 1 组入侵检测 TAMPER,当在发生入侵事件时,TPEF 标志位置 1,同时将自动清除电池供电数据寄存器(RTC_BPRx)的值;若已使能入侵中断,将产生入侵中断,同时 TPIF 标志位置 1。入侵检测引脚固定为 PC13。

入侵检测模式分为高电平检测和低电平检测。
入侵检测相关函数
入侵检测有效电平设置
  1. void bpr_tamper_pin_active_level_set(bpr_tamper_pin_active_level_type active_level);
入侵检测使能
  1. void bpr_tamper_pin_enable(confirm_state new_state);
入侵检测标志获取
  1. flag_status bpr_flag_get(uint32_t flag);
入侵检测标准清除
  1. void bpr_flag_clear(uint32_t flag);
入侵检测中断使能
  1. void bpr_interrupt_enable(confirm_state new_state);
3.4 事件输出功能
电池供电域提供了一组复用功能输出,在 PC13 脚可以输出以下事件:
― 校准输出:校准前 64 分频输出、校准后 64 分频输出。
― 事件输出:闹钟事件、秒事件

当输出模式为事件输出时(闹钟事件、秒事件),当发生相应事件时将会输出一个脉冲,脉冲的宽度为一个 RTC 时钟周期。
事件输出相关函数
事件输出设置并使能
  1. void bpr_rtc_output_select(bpr_rtc_output_type output_source);
4 RTC 使用注意事项
4.1 读写电池供电域寄存器前提
读写电池供电域寄存器(电池供电数据寄存器 BPR_DT、RTC 寄存器、CRM 的 BPDC 寄存器)之前,都需要将 PWC,BPR 时钟开启,解锁电池供电域写保护(BPWEN 位置 1),执行以下代码:
  1. /* 开启 PWC BPR 时钟 */
  2. crm_periph_clock_enable(CRM_PWC_PERIPH_CLOCK, TRUE);
  3. crm_periph_clock_enable(CRM_BPR_PERIPH_CLOCK, TRUE);
  4. /*解锁电池供电域写保护 */
  5. pwc_battery_powered_domain_access(TRUE);
例如以下程序:先开启 PWC、BPR 时钟,解锁电池供电域写保护(BPWEN 位置 1),然后读取BPR_DT 寄存器、写 CRM 的 BPDC 寄存器、写 RTC 寄存器。
  1. /* 开启 PWC BPR 时钟 */
  2. crm_periph_clock_enable(CRM_PWC_PERIPH_CLOCK, TRUE);
  3. crm_periph_clock_enable(CRM_BPR_PERIPH_CLOCK, TRUE);
  4. /*解锁电池供电域写保护 */
  5. pwc_battery_powered_domain_access(TRUE);
  6. /* 检测 RTC 是否初始化 */
  7. if(bpr_data_read(BPR_DATA1) != 0x1234)
  8. {
  9. /* 复位电池供电域寄存器 */
  10. bpr_reset();
  11. /* 使能 LEXT 时钟 */
  12. crm_clock_source_enable(CRM_CLOCK_SOURCE_LEXT, TRUE);
  13. /* 等待 LEXT 时钟稳定 */
  14. while(crm_flag_get(CRM_LEXT_STABLE_FLAG) == RESET);
  15. /* 选择 RTC 时钟源 */
  16. crm_rtc_clock_select(CRM_RTC_CLOCK_LEXT);
  17. /* 使能 RTC 时钟 */
  18. crm_rtc_clock_enable(TRUE);
  19. /* 等待 RTC 寄存器同步 */
  20. rtc_wait_update_finish();
  21. /* 等待上一次寄存器配置同步完成 */
  22. rtc_wait_config_finish();

  23. /* 配置 RTC 分频器 */
  24. rtc_divider_set(32767);

  25. /* 等待上一次寄存器配置同步完成 */
  26. rtc_wait_config_finish();
  27. /* 设置时间 */
  28. rtc_time_set(calendar);
  29. /* 写入标志到电池供电域寄存器 */
  30. bpr_data_write(BPR_DATA1, 0x1234);
  31. }
4.2 sBPWEN 位操作注意事项
在程序中不要去开关 BPWEN 位,若要操作 BPR 数据,在代码初始化时将 PWC、BPR 时钟和BPWEN 位开启:
  1. /* 开启 PWC BPR 时钟 */
  2. crm_periph_clock_enable(CRM_PWC_PERIPH_CLOCK, TRUE);
  3. crm_periph_clock_enable(CRM_BPR_PERIPH_CLOCK, TRUE);
  4. /*解锁电池供电域写保护 */
  5. pwc_battery_powered_domain_access(TRUE);
然后程序中,更改 BPR DT 数据时,直接更改即可。(不要去开关 BPWEN 位)
  1. bpr_data_write(BPR_DATA1, 0x1234);
5 案例 使用日历以及闹钟功能
5.1 功能简介
演示日历功能、闹钟功能的使用。
5.2 资源准备
1) 硬件环境:对应产品型号的 AT-START BOARD
2) 软件环境:project\at_start_f4xx\examples\rtc\calendar
注:所有project都是基于keil 5而建立,若用户需要在其他编译环境上使用,请参考AT32xxx_Firmware_Library_V2.x.x\project\at_start_xxx\templates中各种编译环境(例如IAR6/7,keil 4/5)进行简单修改即可。
5.3 软件设计
1) 配置流程
- 开启 PWC、BPR 时钟
- 解锁电池供电域写保护
- 检查日历是否已经初始化,如果正确就跳过初始化,如果不正确就初始化日历以及闹钟
- 主函数里每秒打印一次日历信息
- 在 21-05-01 12:00:05 时刻发生闹钟。
2) 代码介绍
main 函数代码描述
  1. int main(void)
  2. {
  3. calendar_type time_struct;

  4. /* 配置 NVIC 优先级组 */
  5. nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);

  6. /* 初始化系统时钟 */
  7. system_clock_config();

  8. /* 初始化 ATSTART 板子资源 */
  9. at32_board_init();

  10. /* 初始化串口 */
  11. uart_print_init(115200);
  12. /* RTC 初始化 */
  13. time_struct.year = 2021;
  14. time_struct.month = 5;
  15. time_struct.date = 1;
  16. time_struct.hour = 12;
  17. time_struct.min = 0;
  18. time_struct.sec = 0;
  19. rtc_init(&time_struct);

  20. /* 闹钟初始化 */
  21. alarm_init();

  22. printf("initial ok\r\n");
  23. while(1)
  24. {
  25. /* 秒更新了 */
  26. if(rtc_flag_get(RTC_TS_FLAG) != RESET)
  27. {
  28. at32_led_toggle(LED3);

  29. /* 获取当前日历 */
  30. rtc_time_get();
  31. /* 打印日期:年-月-日 */
  32. printf("%d/%d/%d ", calendar.year, calendar.month, calendar.date);

  33. /* 打印时间:时:分:秒 */
  34. printf("%02d:%02d:%02d %s\r\n", calendar.hour, calendar.min, calendar.sec,
  35. weekday_table[calendar.week]);
  36. /* 等待上一次寄存器配置同步完成 */
  37. rtc_wait_config_finish();

  38. /* 清除秒钟标志 */
  39. rtc_flag_clear(RTC_TS_FLAG);
  40. /* 等待寄存器配置同步完成 */
  41. rtc_wait_config_finish();
  42. }
  43. }
  44. }
RTC 初始化 rtc_init 函数代码描述
  1. uint8_t rtc_init(calendar_type *calendar)
  2. {
  3. /* 开启 PWC BPR 时钟 */
  4. crm_periph_clock_enable(CRM_PWC_PERIPH_CLOCK, TRUE);
  5. crm_periph_clock_enable(CRM_BPR_PERIPH_CLOCK, TRUE);
  6. /*解锁电池供电域写保护 */
  7. pwc_battery_powered_domain_access(TRUE);
  8. /* 检测 RTC 是否初始化 */
  9. if(bpr_data_read(BPR_DATA1) != 0x1234)
  10. {
  11. /* 复位电池供电域寄存器 */
  12. bpr_reset();
  13. /* 使能 LEXT 时钟 */
  14. crm_clock_source_enable(CRM_CLOCK_SOURCE_LEXT, TRUE);
  15. /* 等待 LEXT 时钟稳定 */
  16. while(crm_flag_get(CRM_LEXT_STABLE_FLAG) == RESET);
  17. /* 选择 RTC 时钟源 */
  18. crm_rtc_clock_select(CRM_RTC_CLOCK_LEXT);
  19. /* 使能 RTC 时钟 */
  20. crm_rtc_clock_enable(TRUE);
  21. /* 等待 RTC 寄存器同步 */
  22. rtc_wait_update_finish();
  23. /* 等待上一次寄存器配置同步完成 */
  24. rtc_wait_config_finish();

  25. /* 配置 RTC 分频器 */
  26. rtc_divider_set(32767);

  27. /* 等待上一次寄存器配置同步完成 */
  28. rtc_wait_config_finish();
  29. /* 设置时间 */
  30. rtc_time_set(calendar);
  31. /* 写入标志到电池供电域寄存器 */
  32. bpr_data_write(BPR_DATA1, 0x1234);

  33. return 1;
  34. }
  35. else
  36. {
  37. /* 等待 RTC 寄存器同步 */
  38. rtc_wait_update_finish();
  39. /* 等待上一次寄存器配置同步完成 */
  40. rtc_wait_config_finish();

  41. return 0;
  42. }
  43. }
闹钟中断函数代码描述
  1. void RTC_IRQHandler(void)
  2. {
  3. /*闹钟中断标志置起 */
  4. if(rtc_flag_get(RTC_TA_FLAG) != RESET)
  5. {
  6. at32_led_toggle(LED4);

  7. /* 清除闹钟中断标志 */
  8. rtc_flag_clear(RTC_TA_FLAG);
  9. }
  10. }
5.4 实验效果
- 信息通过串口打印出来,在电脑上通过串口助手观看打印信息。
- 主函数里每秒打印一次日历信息。
- 在 21-05-01 12:00:05 时刻发生闹钟,发生闹钟时 LED4 点亮。

6 案例 使用 LICK 时钟并校准
6.1 功能简介
使用 LICK 时钟作为 RTC 时钟,并通过定时器测量出 LICK 时钟频率,通过得到的频率值,调整RTC 分频,达到在一定范围内校准时间的效果。
6.2 资源准备
1) 硬件环境:对应产品型号的 AT-START BOARD
2) 软件环境:project\at_start_f4xx\examples\rtc\lick_calibration
注:所有project都是基于keil 5而建立,若用户需要在其他编译环境上使用,请参考AT32xxx_Firmware_Library_V2.x.x\project\at_start_xxx\templates中各种编译环境(例如IAR6/7,keil 4/5)进行简单修改即可。
6.3 软件设计
1) 配置流程
- RTC 初始化
- 配置测量 LICK 频率的定时器
- 根据测量到的频率重新配置 RTC 分频
2) 代码介绍
main 函数代码描述
  1. int main(void)
  2. {
  3. tmr_input_config_type tmr_ic_init_structure;
  4. /* 初始化系统时钟 */
  5. system_clock_config();
  6. /* 初始化 ATSTART 板子资源 */
  7. at32_board_init();

  8. /* 初始化串口 */
  9. uart_print_init(115200);
  10. /* RTC 初始化 */
  11. rtc_configuration();
  12. printf("\r\n\nstart calib\r\n\r\n");
  13. /* 获取系统时钟频率 */
  14. crm_clocks_freq_get(&crm_clocks);
  15. /* 使能定时器 */
  16. crm_periph_clock_enable(CRM_TMR5_PERIPH_CLOCK, TRUE);
  17. crm_periph_clock_enable(CRM_IOMUX_PERIPH_CLOCK, TRUE);
  18. /* 将 LICK 连接到定时器 */
  19. gpio_pin_remap_config(TMR5CH4_MUX, TRUE);
  20. /* 定时器初始化 */
  21. tmr_base_init(TMR5, 0xFFFF, 0);
  22. tmr_cnt_dir_set(TMR5, TMR_COUNT_UP);
  23. tmr_clock_source_div_set(TMR5, TMR_CLOCK_DIV1);
  24. /* 定时器初始化 */
  25. tmr_input_default_para_init(&tmr_ic_init_structure);
  26. tmr_ic_init_structure.input_channel_select = TMR_SELECT_CHANNEL_4;
  27. tmr_ic_init_structure.input_polarity_select = TMR_INPUT_RISING_EDGE;
  28. tmr_ic_init_structure.input_mapped_select = TMR_CC_CHANNEL_MAPPED_DIRECT;
  29. tmr_ic_init_structure.input_filter_value = 0;
  30. tmr_input_channel_init(TMR5, &tmr_ic_init_structure, TMR_CHANNEL_INPUT_DIV_1);
  31. /* 初始化变量 */
  32. operationcomplete = 0;
  33. /* 使能定时器输入捕获 */
  34. tmr_counter_enable(TMR5, TRUE);
  35. /* 清除定时器标志 */
  36. tmr_flag_get(TMR5, TMR_C4_FLAG);
  37. /* 使能定时器 */
  38. tmr_interrupt_enable(TMR5, TMR_C4_INT, TRUE);
  39. /* 初始化中断 */
  40. nvic_configuration();
  41. /* 等待测量完成 */
  42. while(operationcomplete != 2);
  43. /* 计算 LICK 频率 */
  44. if(periodvalue != 0)
  45. {
  46. lickfreq = (uint32_t)((uint32_t)(crm_clocks.apb1_freq * 2) / (uint32_t)periodvalue);
  47. }
  48. printf("apb1_freq = %d\r\n", crm_clocks.apb1_freq);
  49. printf("period_value = %d\r\n", periodvalue);
  50. printf("lick_freq = %d\r\n", lickfreq);
  51. /* 调整 RTC 分频 */
  52. rtc_divider_set((lickfreq - 1));
  53. /* 等待寄存器写完成 */
  54. rtc_wait_config_finish();
  55. /* turn on led2 */
  56. at32_led_on(LED2);
  57. while(1)
  58. {
  59. }
  60. }
6.4 实验效果
- 信息通过串口打印出来,在电脑上通过串口助手观看打印信息。
- 通串口打印出当前测量出的 LICK 的频率以及分频值。
- 每秒钟打印一次日历。

7 案例 读写电池供电数据寄存器
7.1 功能简介
对电池供电数据寄存器(RTC_BPRx)进行读写访问。
7.2 资源准备
1) 硬件环境:对应产品型号的 AT-START BOARD
2) 软件环境:project\at_start_f4xx\examples\bpr\bpr_data
注:所有project都是基于keil 5而建立,若用户需要在其他编译环境上使用,请参考AT32xxx_Firmware_Library_V2.x.x\project\at_start_xxx\templates中各种编译环境(例如IAR6/7,keil 4/5)进行简单修改即可。
7.3 软件设计
1) 配置流程
- 开启 PWC、BPR 时钟
- 解锁电池供电域写保护
- 检查电池供电域数据是否正确
- 关闭 PWC、BPR 时钟降低功耗
2) 代码介绍
main 函数代码描述
  1. int main(void)
  2. {
  3. /* 初始化系统时钟 */
  4. system_clock_config();

  5. /* 初始化串口 */
  6. uart_print_init(115200);
  7. /* 开启 PWC BPR 时钟 */
  8. crm_periph_clock_enable(CRM_PWC_PERIPH_CLOCK, TRUE);
  9. crm_periph_clock_enable(CRM_BPR_PERIPH_CLOCK, TRUE);
  10. /*解锁电池供电域写保护 */
  11. pwc_battery_powered_domain_access(TRUE);
  12. /* 清除入侵检测标志 */
  13. bpr_flag_clear(BPR_TAMPER_EVENT_FLAG);
  14. /* 检查电池供电域数据 */
  15. if(bpr_reg_check() == TRUE)
  16. {
  17. printf("bpr reg => none reset\r\n");
  18. }
  19. else
  20. {
  21. printf("bpr reg => reset\r\n");
  22. }
  23. /* 复位电池供电域寄存器 */
  24. bpr_reset();
  25. /*写电池供电域寄存器 */
  26. bpr_reg_write();
  27. /* 检查电池供电域数据 */
  28. if(bpr_reg_check() == TRUE)
  29. {
  30. printf("write bpr reg ok\r\n");
  31. }
  32. else
  33. {
  34. printf("write bpr reg fail\r\n");
  35. }
  36. while(1)
  37. {
  38. }
  39. }
7.4 实验效果
- 信息通过串口打印出来,在电脑上通过串口助手观看打印信息。
- 如果寄存器里数据正确打印 bpr reg => none reset。
- 如果寄存器里数据正确打印 bpr reg => reset。
- 主函数里每秒打印一次日历信息。

8 案例 入侵检测
8.1 功能简介
演示入侵检测功能使用,PC13 脚当检测到一个上升沿后将触发入侵检测,当入侵事件发生时,电池供电数据寄存器将会被清除。
8.2 资源准备
1) 硬件环境:对应产品型号的 AT-START BOARD
2) 软件环境:project\at_start_f4xx\examples\bpr\tamper
注:所有project都是基于keil 5而建立,若用户需要在其他编译环境上使用,请参考AT32xxx_Firmware_Library_V2.x.x\project\at_start_xxx\templates中各种编译环境(例如IAR6/7,keil 4/5)进行简单修改即可。
8.3 软件设计
1) 配置流程
- RTC 初始化
- 初始化入侵检测
- 初始化电池供电寄存器
2) 代码介绍
main 函数代码描述
  1. int main(void)
  2. {
  3. /* 配置 NVIC 优先级组 */
  4. nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);
  5. /* 初始化系统时钟 */
  6. system_clock_config();
  7. /* 初始化 ATSTART 板子资源 */
  8. at32_board_init();
  9. /* 入侵检测中断配置 */
  10. nvic_irq_enable(TAMPER_IRQn, 0, 0);
  11. /* 开启 PWC BPR 时钟 */
  12. crm_periph_clock_enable(CRM_PWC_PERIPH_CLOCK, TRUE);
  13. crm_periph_clock_enable(CRM_BPR_PERIPH_CLOCK, TRUE);
  14. /* 解锁电池供电域写保护 */
  15. pwc_battery_powered_domain_access(TRUE);
  16. /* 禁止入侵检测功能 */
  17. bpr_tamper_pin_enable(FALSE);
  18. /* 禁止入侵检测中断 */
  19. bpr_interrupt_enable(FALSE);
  20. /* 配置入侵检测为低电平检测 */
  21. bpr_tamper_pin_active_level_set(BPR_TAMPER_PIN_ACTIVE_LOW);
  22. /* 清除入侵检测标志 */
  23. bpr_flag_clear(BPR_TAMPER_EVENT_FLAG);
  24. /* 写数据到电池供电域寄存器 */
  25. bpr_reg_write();
  26. /* 使能入侵检测中断 */
  27. bpr_interrupt_enable(TRUE);
  28. /* 使能入侵检测 */
  29. bpr_tamper_pin_enable(TRUE);
  30. /* 检查电池供电域寄存器值 */
  31. if(bpr_reg_check() == TRUE)
  32. {
  33. at32_led_on(LED2);
  34. }
  35. else
  36. {
  37. at32_led_off(LED2);
  38. }
  39. while(1)
  40. {
  41. }
  42. }
入侵检测中断处理函数代码描述
  1. void TAMPER_IRQHandler(void)
  2. {
  3. if(bpr_flag_get(BPR_TAMPER_INTERRUPT_FLAG) != RESET)
  4. {
  5. /* 检查电池供电寄存器是否被清除掉 */
  6. if(bpr_reg_judge() == 0)
  7. {
  8. /* ok, bpr registers are reset as expected */
  9. at32_led_on(LED3);
  10. }
  11. else
  12. {
  13. /* bpr registers are not reset */
  14. at32_led_off(LED3);
  15. }
  16. /* 清除入侵检测中断标志 */
  17. bpr_flag_clear(BPR_TAMPER_INTERRUPT_FLAG);
  18. /* 清除入侵检测事件标志 */
  19. bpr_flag_clear(BPR_TAMPER_EVENT_FLAG);
  20. /* 禁止入侵检测 */
  21. bpr_tamper_pin_enable(FALSE);
  22. /* 使能入侵检测 */
  23. bpr_tamper_pin_enable(TRUE);
  24. }
  25. }
8.4 实验效果
- 信息通过串口打印出来,在电脑上通过串口助手观看打印信息。
- 当发生入侵事件时(PC13 出现低电平),LED3 亮表示电池供电寄存器被清除。

VelvetVoyag 发表于 2025-8-16 12:51 | 显示全部楼层
是一篇完整的分享,楼主讲的很详细了!
您需要登录后才可以回帖 登录 | 注册

本版积分规则

3

主题

5

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部