[单片机芯片] CH32L103 后备寄存器(BKP)与循环冗余校验(CRC)应用

[复制链接]
7274|4
 楼主| L-MCU 发表于 2024-6-21 14:49 | 显示全部楼层 |阅读模式
本帖最后由 L-MCU 于 2024-6-21 14:51 编辑

1、后备寄存器(BKP)
关于BKP的介绍,在CH32L103应用手册第4章 后备寄存器(BKP)有详细介绍,在此不再赘述。
BKP在MCU应用中主要用于以下几个方面:
(1)VDD掉电保存。当VDD掉电后,由VBAT供电可存储20字节的用户数据;
(2)侵入检测功能。当检测到侵入事件,会清除后备数据寄存器保存的用户数据;
(3)RTC校准功能。可通过配合修改CAL[6:0]位来调整时钟对RTC 进行校准;
(4)RTC脉冲输出。 可将侵入检测引脚作为普通IO使用,用于输出秒脉冲或者闹钟脉冲。
CH32L103 EVT提供了BKP例程,演示数据保存以及侵入检测清除数据,例程打印信息如下:
468706675205f90965.png
由于EVT例程演示了数据保存以及侵入检测的应用,下面主要介绍一下RTC校准功能以及RTC脉冲输出的应用。关于RTC校准功能以及RTC脉冲输出的应用,基于使用开启RTC的前提下。
RTC校准功能:
启用RTC校准功能需要配置RTC校准寄存器(BKP_OCTLR)的位7为1,配置TEMPER引脚(即PC13引脚)输出经64分频的RTC时钟。可直接调用BKP_RTCOutputConfig()函数进行配置。BKP_RTCOutputConfig()函数代码如下:
  1. /*********************************************************************
  2. * @fn      BKP_RTCOutputConfig
  3. *
  4. * [url=home.php?mod=space&uid=247401]@brief[/url]   Select the RTC output source to output on the Tamper pin.
  5. *
  6. * @param   BKP_RTCOutputSource - specifies the RTC output source.
  7. *            BKP_RTCOutputSource_None - no RTC output on the Tamper pin.
  8. *            BKP_RTCOutputSource_CalibClock - output the RTC clock with
  9. *        frequency divided by 64 on the Tamper pin.
  10. *            BKP_RTCOutputSource_Alarm - output the RTC Alarm pulse signal
  11. *        on the Tamper pin.
  12. *            BKP_RTCOutputSource_Second - output the RTC Second pulse
  13. *        signal on the Tamper pin.
  14. *
  15. * [url=home.php?mod=space&uid=266161]@return[/url]  none
  16. */
  17. void BKP_RTCOutputConfig(uint16_t BKP_RTCOutputSource)
  18. {
  19.     uint16_t tmpreg = 0;

  20.     tmpreg = BKP->OCTLR;
  21.     tmpreg &= OCTLR_MASK;
  22.     tmpreg |= BKP_RTCOutputSource;
  23.     BKP->OCTLR = tmpreg;
  24. }
该函数主要是对RTC校准寄存器(BKP_OCTLR)进行配置,可配置TEMPER引脚输出经64分频的RTC时钟或秒脉冲或闹钟输出。此处配置TEMPER引脚输出经64分频的RTC时钟。
在RTC初始化完成后调用该函数就可以得到TEMPER引脚输出经64分频的RTC时钟,波形如下:
31299667520f9d35b8.png
此处RTC的时钟源为LSE,大小为32.768KHz,经过64分频后,大小为512Hz,与截图逻辑分析仪采样得到的频率差不多。
关于校准,注意RTC只能对RTC运行快了进行减慢,无法对RTC运行慢了进行加快。根据手册RTC校准寄存器(BKP_OCTLR)位[6:0]校准值寄存器的介绍,RTC时钟可以被减慢0-121ppm。此处单位ppm表示频率误差,定义为百万分之一,ppm误差的计算公式为:ppm误差=偏差/基准值*10的6次方。具体解释可参考百度百科解释,链接如下:
https://baike.baidu.com/item/ppm/14922800?fr=aladdin
关于校准值的计算:一般LSE的典型值为32.768KHz,但实际可能会偏小或偏大。比如我手上L103板子上LSE实测大小为32.765KHz,以该值为例,32765Hz经过64分频后输出大小应该为511.953Hz,但用逻辑分析仪测量的值为511.980Hz,那么根据ppm的误差计算公式,ppm误差大小为:(511.980-511.953)/511.953*1000000=52.739ppm
那么RTC的校准值应该是
52.739/1000000*2^20=55
根据该值,就可以调用BKP_SetRTCCalibrationValue函数对RTC进行校准,BKP_SetRTCCalibrationValue函数具体如下:
  1. /*********************************************************************
  2. * @fn      BKP_SetRTCCalibrationValue
  3. *
  4. * [url=home.php?mod=space&uid=247401]@brief[/url]   Sets RTC Clock Calibration value.
  5. *
  6. * @param   CalibrationValue - specifies the RTC Clock Calibration value.
  7. *            This parameter must be a number between 0 and 0x1F.
  8. *
  9. * [url=home.php?mod=space&uid=266161]@return[/url]  none
  10. */
  11. void BKP_SetRTCCalibrationValue(uint8_t CalibrationValue)
  12. {
  13.     uint16_t tmpreg = 0;

  14.     tmpreg = BKP->OCTLR;
  15.     tmpreg &= OCTLR_CAL_MASK;
  16.     tmpreg |= CalibrationValue;
  17.     BKP->OCTLR = tmpreg;
  18. }
该函数主要是对RTC校准寄存器(BKP_OCTLR)的位[6:0]校准值寄存器进行配置。
注意该种校准方式也是存在一定误差的。
RTC秒脉冲/闹钟脉冲
脉冲输出通过配置RTC校准寄存器(BKP_OCTLR)的AOSE位,开启RTC脉冲输出。通过设置ASOS位,可以选择输出秒脉冲或者闹钟脉冲,如下图。
9479266752156d7dde.png
秒脉冲:
直接调用BKP_RTCOutputConfig(BKP_RTCOutputSource_Second);函数即可输出秒脉冲,逻辑分析仪采样如下图。由脉宽可知,大约1s左右输出一次脉冲。
279516675216b5e72a.png
闹钟脉冲:
直接调用BKP_RTCOutputConfig(BKP_RTCOutputSource_Alarm);函数即可输出闹钟脉冲,逻辑分析仪采样如下图。注意输出闹钟脉冲要设置一下闹钟。
106586675217edda99.png



2、循环冗余校验(CRC)
关于CRC的介绍,在CH32L103应用手册第5章 循环冗余校验(CRC)有详细介绍,在此不再赘述。
CH32L103提供硬件CRC计算单元,其使用CRC32多项式(0x4C11DB7),可节省CPU和RAM资源,提高效率。
CH32L103 EVT提供了CRC例程,代码如下:
  1. /********************************** (C) COPYRIGHT *******************************
  2. * File Name          : main.c
  3. * Author             : WCH
  4. * Version            : V1.0.0
  5. * Date               : 2024/02/21
  6. * Description        : Main program body.
  7. *********************************************************************************
  8. * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
  9. * Attention: This software (modified or not) and binary are used for
  10. * microcontroller manufactured by Nanjing Qinheng Microelectronics.
  11. *******************************************************************************/

  12. /*
  13. *@Note
  14. CRC routine:
  15. Use CRC-32 polynomial 0x4C11DB7.

  16. */

  17. #include "debug.h"

  18. /* Global define */
  19. #define Buf_Size 32

  20. /* Global Variable */
  21. u32 SRC_BUF[Buf_Size] = {0x01020304, 0x05060708, 0x090A0B0C, 0x0D0E0F10,
  22.                          0x11121314, 0x15161718, 0x191A1B1C, 0x1D1E1F20, 0x21222324, 0x25262728,
  23.                          0x292A2B2C, 0x2D2E2F30, 0x31323334, 0x35363738, 0x393A3B3C, 0x3D3E3F40,
  24.                          0x41424344, 0x45464748, 0x494A4B4C, 0x4D4E4F50, 0x51525354, 0x55565758,
  25.                          0x595A5B5C, 0x5D5E5F60, 0x61626364, 0x65666768, 0x696A6B6C, 0x6D6E6F70,
  26.                          0x71727374, 0x75767778, 0x797A7B7C, 0x7D7E7F80};

  27. u32 CRCValue = 0;

  28. /*********************************************************************
  29. * @fn  RecalculateCRC
  30. *
  31. * [url=home.php?mod=space&uid=247401]@brief[/url] The function RecalculateCRC calculates the CRC value of a given buffer and sets the ID register to a
  32. *      specific value.
  33. *
  34. * @param SRC_BUF SRC_BUF is the source buffer, which is a pointer to the start of the data that needs
  35. *      to be used for CRC calculation.
  36. *        suze The parameter "suze" is likely a typo and should be "size". It represents the size of
  37. *      the source buffer in bytes.
  38. *
  39. * [url=home.php?mod=space&uid=266161]@return[/url] the calculated CRC value as a uint32_t.
  40. */
  41. uint32_t RecalculateCRC(uint32_t *SRC_BUF, uint32_t suze)
  42. {
  43.     uint32_t temp;
  44.     if (CRC_GetIDRegister() == 0xaa)
  45.         CRC_ResetDR();
  46.     temp = CRC_CalcBlockCRC((u32 *)SRC_BUF, suze);
  47.     CRC_SetIDRegister(0xaa);
  48.     return temp;
  49. }

  50. /**
  51. * @fn CRC_Is_Used
  52. *
  53. * [url=home.php?mod=space&uid=247401]@brief[/url] The function checks if the CRC module is being used by comparing the ID register value to 0xaa and
  54. * returns 1 if it is being used, otherwise it returns 0.
  55. *
  56. * [url=home.php?mod=space&uid=266161]@return[/url] a value of type uint8_t, which is an 8-bit unsigned integer. The function is checking if the
  57. * value returned by the CRC_GetIDRegister() function is equal to 0xaa. If it is, the function returns
  58. * 1. If it is not, the function returns 0.
  59. */
  60. uint8_t CRC_Is_Used()
  61. {
  62.     if (CRC_GetIDRegister() == 0xaa)
  63.         return 1;
  64.     else
  65.         return 0;
  66. }

  67. /*********************************************************************
  68. * @fn      main
  69. *
  70. * @brief   Main program.
  71. *
  72. * @return  none
  73. */
  74. int main(void)
  75. {
  76.     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
  77.     SystemCoreClockUpdate();
  78.     Delay_Init();
  79.     USART_Printf_Init(115200);
  80.     printf("SystemClk:%d\r\n", SystemCoreClock);
  81.     printf("ChipID:%08x\r\n", DBGMCU_GetCHIPID());
  82.     printf("CRC TEST\r\n");
  83.     RCC_HBPeriphClockCmd(RCC_HBPeriph_CRC, ENABLE);

  84.     CRC_Is_Used() ? printf("CRC is not clear\r\n") : printf("CRC is clear\r\n");
  85.     /* The code is calling the function `RecalculateCRC` with the arguments `SRC_BUF` and `Buf_Size`. */
  86.     CRCValue = RecalculateCRC(SRC_BUF, Buf_Size);

  87.     printf("CRCValue: 0x%08X\r\n", CRCValue); /* CRCValue should be 0x199AC3CA in this example */

  88.     CRC_Is_Used() ? printf("CRC is not clear\r\n") : printf("CRC is clear\r\n");
  89.     while (1)
  90.         ;
  91. }

程序中,使用CRC首先注意要开启CRC时钟,CRC是挂载在HB总线上的,如下图,调用RCC_HBPeriphClockCmd(RCC_HBPeriph_CRC, ENABLE);开启CRC时钟。
69272667521beb761d.png
程序中,CRC_Is_Used()函数主要用于判断CRC是否在使用中,该函数主要通过读取独立数据缓冲寄存器(CRC_IDATAR)的值判断CRC是否有被使用,若该寄存器值为0xAA,说明被使用。独立数据缓冲寄存器(CRC_IDATAR)是一个8位通用寄存器,具体介绍如下:
51189667521d9c5b39.png
程序中,RecalculateCRC()函数主要用于CRC计算,计算给定数组的CRC值,并往独立数据缓冲寄存器(CRC_IDATAR)写值0xAA,因此可以调用CRC_Is_Used()函数判断CRC是否在使用中。CRC计算主要往数据寄存器(CRC_DATAR)写入给定数组的值,会自动得到CRC计算结果。数据寄存器(CRC_DATAR)具体介绍如下:
57993667521f3846cd.png

附件例程为RTC校准以及秒脉冲、闹钟脉冲输出例程,可下载简单参考一下。

   

CH32L103 RTC校准.zip

664.55 KB, 下载次数: 7

tpgf 发表于 2024-10-11 15:27 | 显示全部楼层
后备寄存器(BKP)是一个关键组件,用于在电源故障或系统复位时保持重要数据和配置
wakayi 发表于 2024-10-14 21:58 | 显示全部楼层
当检测到侵入事件时,后备寄存器会自动清除存储的用户数据,以增加系统的安全性
wowu 发表于 2024-10-14 22:30 | 显示全部楼层
后备寄存器可用于调整实时时钟(RTC)的精度,通过修改CAL[6:0]位来校准RTC时钟
Bowclad 发表于 2024-10-27 12:34 来自手机 | 显示全部楼层
crc计算会不会占用大量资源啊?
您需要登录后才可以回帖 登录 | 注册

本版积分规则

17

主题

39

帖子

1

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