打印
[单片机芯片]

CH32L103 后备寄存器(BKP)与循环冗余校验(CRC)应用

[复制链接]
4091|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例程,演示数据保存以及侵入检测清除数据,例程打印信息如下:
由于EVT例程演示了数据保存以及侵入检测的应用,下面主要介绍一下RTC校准功能以及RTC脉冲输出的应用。关于RTC校准功能以及RTC脉冲输出的应用,基于使用开启RTC的前提下。
RTC校准功能:
启用RTC校准功能需要配置RTC校准寄存器(BKP_OCTLR)的位7为1,配置TEMPER引脚(即PC13引脚)输出经64分频的RTC时钟。可直接调用BKP_RTCOutputConfig()函数进行配置。BKP_RTCOutputConfig()函数代码如下:
/*********************************************************************
* @fn      BKP_RTCOutputConfig
*
* [url=home.php?mod=space&uid=247401]@brief[/url]   Select the RTC output source to output on the Tamper pin.
*
* @param   BKP_RTCOutputSource - specifies the RTC output source.
*            BKP_RTCOutputSource_None - no RTC output on the Tamper pin.
*            BKP_RTCOutputSource_CalibClock - output the RTC clock with
*        frequency divided by 64 on the Tamper pin.
*            BKP_RTCOutputSource_Alarm - output the RTC Alarm pulse signal
*        on the Tamper pin.
*            BKP_RTCOutputSource_Second - output the RTC Second pulse
*        signal on the Tamper pin.
*
* [url=home.php?mod=space&uid=266161]@return[/url]  none
*/
void BKP_RTCOutputConfig(uint16_t BKP_RTCOutputSource)
{
    uint16_t tmpreg = 0;

    tmpreg = BKP->OCTLR;
    tmpreg &= OCTLR_MASK;
    tmpreg |= BKP_RTCOutputSource;
    BKP->OCTLR = tmpreg;
}
该函数主要是对RTC校准寄存器(BKP_OCTLR)进行配置,可配置TEMPER引脚输出经64分频的RTC时钟或秒脉冲或闹钟输出。此处配置TEMPER引脚输出经64分频的RTC时钟。
在RTC初始化完成后调用该函数就可以得到TEMPER引脚输出经64分频的RTC时钟,波形如下:
此处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函数具体如下:
/*********************************************************************
* @fn      BKP_SetRTCCalibrationValue
*
* [url=home.php?mod=space&uid=247401]@brief[/url]   Sets RTC Clock Calibration value.
*
* @param   CalibrationValue - specifies the RTC Clock Calibration value.
*            This parameter must be a number between 0 and 0x1F.
*
* [url=home.php?mod=space&uid=266161]@return[/url]  none
*/
void BKP_SetRTCCalibrationValue(uint8_t CalibrationValue)
{
    uint16_t tmpreg = 0;

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



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

/*
*@Note
CRC routine:
Use CRC-32 polynomial 0x4C11DB7.

*/

#include "debug.h"

/* Global define */
#define Buf_Size 32

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

u32 CRCValue = 0;

/*********************************************************************
* @fn  RecalculateCRC
*
* [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
*      specific value.
*
* @param SRC_BUF SRC_BUF is the source buffer, which is a pointer to the start of the data that needs
*      to be used for CRC calculation.
*        suze The parameter "suze" is likely a typo and should be "size". It represents the size of
*      the source buffer in bytes.
*
* [url=home.php?mod=space&uid=266161]@return[/url] the calculated CRC value as a uint32_t.
*/
uint32_t RecalculateCRC(uint32_t *SRC_BUF, uint32_t suze)
{
    uint32_t temp;
    if (CRC_GetIDRegister() == 0xaa)
        CRC_ResetDR();
    temp = CRC_CalcBlockCRC((u32 *)SRC_BUF, suze);
    CRC_SetIDRegister(0xaa);
    return temp;
}

/**
* @fn CRC_Is_Used
*
* [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
* returns 1 if it is being used, otherwise it returns 0.
*
* [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
* value returned by the CRC_GetIDRegister() function is equal to 0xaa. If it is, the function returns
* 1. If it is not, the function returns 0.
*/
uint8_t CRC_Is_Used()
{
    if (CRC_GetIDRegister() == 0xaa)
        return 1;
    else
        return 0;
}

/*********************************************************************
* @fn      main
*
* @brief   Main program.
*
* @return  none
*/
int main(void)
{
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
    SystemCoreClockUpdate();
    Delay_Init();
    USART_Printf_Init(115200);
    printf("SystemClk:%d\r\n", SystemCoreClock);
    printf("ChipID:%08x\r\n", DBGMCU_GetCHIPID());
    printf("CRC TEST\r\n");
    RCC_HBPeriphClockCmd(RCC_HBPeriph_CRC, ENABLE);

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

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

    CRC_Is_Used() ? printf("CRC is not clear\r\n") : printf("CRC is clear\r\n");
    while (1)
        ;
}

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

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

   

CH32L103 RTC校准.zip

664.55 KB

使用特权

评论回复
沙发
tpgf| | 2024-10-11 15:27 | 只看该作者
后备寄存器(BKP)是一个关键组件,用于在电源故障或系统复位时保持重要数据和配置

使用特权

评论回复
板凳
wakayi| | 2024-10-14 21:58 | 只看该作者
当检测到侵入事件时,后备寄存器会自动清除存储的用户数据,以增加系统的安全性

使用特权

评论回复
地板
wowu| | 2024-10-14 22:30 | 只看该作者
后备寄存器可用于调整实时时钟(RTC)的精度,通过修改CAL[6:0]位来校准RTC时钟

使用特权

评论回复
5
Bowclad| | 2024-10-27 12:34 | 只看该作者
crc计算会不会占用大量资源啊?

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

17

主题

26

帖子

0

粉丝