本帖最后由 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校准以及秒脉冲、闹钟脉冲输出例程,可下载简单参考一下。
|