电子说
CMU仅适用于S32K11x系列。S32K1xx系列中的其他器件没有CMU。S32K11x有两个CMU单元:CMU0和CMU1。它监控作为系统时钟主要来源的FIRC。CMU需要通过软件启用,因为默认情况下未启用。CMU0用于时钟丢失检测时产生复位,映射到RCM_SRS[4]。当FIRC低于编程阈值时,标识时钟丢失,将IER[FHAIE]和IER[FLLAIE]以及阈值编程设置为启用复位功能。当FIRC低于或高于编程阈值限制时,CMU1用于中断生成。在CMU1中设置IER[FHIE]和IER[FLLIE]以及阈值编程,以启用中断功能。下图显示了CMU与芯片的连接:
在禁用FIRC或SIRC之前,需要禁用CMU。如果FIRC从开始就不存在,则无法启用CMU,因为启用CMU需要系统时钟。
•对于安全应用,CMU应在与WDOG看门狗在不同的时钟上运行。
•CMU在最坏情况下将FIRC时钟超出范围事件标记为+/-7.5%的准确度,条件是FIRC和SIRC符合器件数据手册规格范围,即在最坏的情况下分别为+/-1.1%和+/-3.3%。如果FIRC和SIRC违反规范,CMU精度将进一步下降到+/-7.5%以下。
•当REF_CNT编程值为1000时,CMU在标记FIRC时钟超出范围事件时所用的硬件安全反应时间为128 us,精确度为+/-7.5%。如果FIRC和SIRC违反规范,硬件安全反应时长将超过128 us。
•建议通过软件将时钟范围超出事件,设置为复位系统,而不是使用中断。
在S32K116 系统添加时钟监控单元。初始化时钟FIRC(48MHz),参考配置代码如下。
CMU0只能在时钟丢失时触发复位,为此,配置参考计数、启用CMU0中断并启用频率检查。CMU1只能在频率高于或低于编程限制时触发中断。为此,配置参考计数、HTCR和LTCR、中断和启用频率检查。重新启动后,系统必须验证复位是否由时钟丢失引起。功能Sanity_check执行此操作,如果它是由FIRC丢失引起的,则系统进入安全状态。如果导致CMU1中断,系统将验证中断原因,然后进入安全状态。
#include "S32K116.h"
#include "clocks.h"
#define GREEN_LED 15 /* 定义绿色 LED */
#define RED_LED 16 /* 定义红色 LED */
/* Sanity_check函数可能返回状态的枚举 */
typedef enum
{
FIRC_NOT_OK = 0,
FIRC_OK= 1
}Sanity_check_status_t;
/*该功能表示如果时钟监视器检测到FIRC有问题,系统应跳至安全状态。*/
void Safe_state(void)
{
for(;;)
{
/* 应用程序特定安全状态 */
}
}
// 通过读取RCM_SRS寄存器,验证上次系统复位是否由时钟丢失触发。
SOSC_AND_SPLL_OK:系统复位不是由LOC或LOL触发的
*SOSC_NOT_OK: LOC触发了系统复位
*SPLL_NOT_OK: 系统复位由LOL触发
Sanity_check_status_t Sanity_check(void)
{
///应用软件需要检测RCM的错误标志状态
/* 判断RCM_SRS寄存器的 CMU_LOC 位是否被置位 */
if(True == ((RCM->SRS & RCM_SRS_CMU_LOC_MASK) >> RCM_SRS_CMU_LOC_SHIFT))
{
/* 返回 SOSC_NOT_OK 状态*/
return FIRC_NOT_OK;
}
/* 默认返回 SOSC_AND_SPLL_OK 状态*/
return FIRC_OK;
}
/* SCG_CMU_LVD_LVWSCG_IRQHandler 表示 CMU中断。当频率不在编程范围内时,通过 CMU1触发,通过关闭绿色LED,点亮红色 LED指示。*/
void SCG_CMU_LVD_LVWSCG_IRQHandler(void)
{
/* 关闭绿色的 LED */
PTD->PCOR |= 1 << GREEN_LED;
/* 开启红色的 LED */
PTD->PSOR |= 1 << RED_LED;
///对于丢失的中断请求,应用程序可能需要在系统级别上包括检测或保护措施
/* 中断由CMU1引起,频率高于阈值 */
if(True == (CMU_FC_1->IER & CMU_FC_SR_FHH_MASK) >> CMU_FC_SR_FHH_SHIFT)
{
/* 清除中断的标志 */
CMU_FC_1->SR |= CMU_FC_SR_FHH_MASK;
/* 进入安装状态 */
Safe_state();
}
/* 中断由CMU1引起,频率低于阈值 */
else if(True == (CMU_FC_1->IER & CMU_FC_SR_FLL_MASK) >> CMU_FC_SR_FLL_SHIFT)
{
/* 清除中断的标志 */
CMU_FC_1->SR |= CMU_FC_SR_FLL_MASK;
/* 进入安装状态 */
Safe_state();
}
}
int main(void)
{
/* 验证之前的复位是否是由时钟丢失引起的 */
if(FIRC_NOT_OK == Sanity_check())
{
/* 进入安全状态 */
Safe_state();
}
/* 使能端口 D的时钟,开启绿色和红色的LED 指示 */
PCC-> PCCn[PCC_PORTD_INDEX] = PCC_PCCn_CGC_MASK;
/* 选择 GPIO */
PORTD->PCR[GREEN_LED] = PORT_PCR_MUX(1);
/* 设置端口 D 引脚 15 为输出 */
PTD->PDDR |= 1 << GREEN_LED;
/* 选择 GPIO */
PORTD->PCR[RED_LED] = PORT_PCR_MUX(1);
/* 设置端口 D 引脚 16 为输出 */
PTD->PDDR |= 1 << RED_LED;
/* 开启绿色 LED输出 */
PTD->PSOR |= 1 << GREEN_LED;
/* 配置寄存器将FIRC映射到引脚 */
FIRC_init();
/*启用NVIC模块中的CMC_FC_0和CMC_FC_1中断 */
NVIC_enable_CMU_interrupt();
/* 配置 SIRC */
SIRC_init();
/* 设置 SIRC 为系统时钟 */
SCG->RCCR = SCG_RCCR_SCS(2);
///对于安全相关应用,必须使用时钟监视器。如果应用安全功能使用SCG监视器的模块,用户应验证时钟监视器未被禁用,且其故障由软件管理。需要以下监督功能:内部快速参考时钟丢失、系统FIRC频率高于(可编程)上限频率参考、系统FIRC频率低于(可编程的)下限频率参考、应用软件必须在激活安全相关功能之前验证S32K1xx的初始化是否正确。时钟监视器已启用。
/* 配置 CMU0 来检测内部的 FIRC是否丢失 */
CMU0_init();
/* 配置 CMU1来检测 FIRC的频率是否变化 */
CMU1_init();
/* 关闭 FIRC,因此 CMU_FC_0 会触发复位 */
SCG->FIRCCSR &= ~SCG_FIRCCSR_FIRCEN_MASK;
for(;;){}
return 0;
}
#include "clocks.h"
/*NVIC_enable_CMU_interrupt 在NVIC中使能CMU模块的中断*/
void NVIC_enable_CMU_interrupt(void)
{
/* 清除 IRQs, SCG NVIC 中断 ID = 21 */
S32_NVIC->ICPR[0] = 1 << (SCG_CMU_LVD_LVWSCG_IRQn % 32);
/* 使能 IRQ */
S32_NVIC->ISER[0] = 1 << (SCG_CMU_LVD_LVWSCG_IRQn % 32);
}
/*SIRC_init 如果可以写入SIRCCSR,则禁用SIRC,以便可以选择8MHz频率,然后将DIV 1和2设置为除以1。启用SIRC并等待SIRC有效。*/
void SIRC_init(void)
{
/* 验证是否可以写入控制状态寄存器 */
if(False == ((SCG->SIRCCSR & SCG_SIRCCSR_LK_MASK) >> SCG_SIRCCSR_LK_SHIFT))
{
/* 禁用SIRC,以便进行更改 */
SCG->SIRCCSR &= ~(SCG_SIRCCSR_SIRCLPEN_MASK |SCG_SIRCCSR_SIRCEN_MASK);
SCG->SIRCDIV |= SCG_SIRCDIV_SIRCDIV1(1) | SCG_SIRCDIV_SIRCDIV2(1);
/*选择高频范围 (8MHz) */
SCG->SIRCCFG |= SCG_SIRCCFG_RANGE_MASK;
/* 使能 SIRC */
SCG->SIRCCSR |= SCG_SIRCCSR_SIRCLPEN_MASK |SCG_SIRCCSR_SIRCEN_MASK;
/* 等待直到 SIRC 是有效的 */
while(False == ((SCG->SIRCCSR & SCG_SIRCCSR_SIRCVLD_MASK) >> SCG_SIRCCSR_SIRCVLD_SHIFT));
}
}
/*FIRC_init 配置寄存器需要将FIRC输出到引脚上*/
void FIRC_init(void)
{
/* 配置内部的 FIRC到端口 B的第5脚 */
/* 在SCG_CLKOUTCNFG寄存器的CLKOUTSEL 中选择 FIRC */
SCG->CLKOUTCNFG |= SCG_CLKOUTCNFG_CLKOUTSEL(3);
/* 在SIM_CHIPCTL,CLKOUTSEL中 选择 FIRC*/
SIM->CHIPCTL |= SIM_CHIPCTL_CLKOUTSEL(0);
/* 配置 CLKOUT 分频 */
SIM->CHIPCTL |= SIM_CHIPCTL_CLKOUTDIV(0);
/* 使能 CLKOUT */
SIM->CHIPCTL |= SIM_CHIPCTL_CLKOUTEN_MASK;
/* 将 CLKOUT 配置到引脚 PTB5 */
/* 使能端口 B 时钟 */
PCC->PCCn[PCC_PORTB_INDEX] = PCC_PCCn_CGC_MASK;
/* 设置端口 B的引脚 5设为输出 */
PTB->PDDR |= 1 << PTB5;
/* 选择 CLKOUT */
PORTB->PCR[PTB5] = PORT_PCR_MUX(5);
}
/* CMU0_init:该函数配置 CMU0来检测是否有 FIRC时钟丢失。 为此,需要对参考计数进行编程,需要启用中断和频率检查。当FIRC丢失时,将触发复位。*/
void CMU0_init(void)
{
/* 使能 CMU0 时钟门控*/
PCC->PCCn[PCC_CMU0_INDEX] = PCC_PCCn_CGC_MASK;
/*
总线频率 = 8 MHz
参考时钟频率 = 8 MHz
监控时钟频率 = 48 MHz
* 公式1:
F1 = 3 * 参考时钟频率 / 总线频率
F1 = 3 * (8 MHz / 8MHz) = 3
* 公式 2:
F2 = 6 + 3 * (参考时钟频率 / 监控时钟频率)
F2 = 6 + 3 * (8 MHz / 48 MHz) = 6.5
* F1和F2结果之间的最大值Max(F1, F2),Max(3, 6.5) = 6.5
* 最大结果的上限 Ceiling(6.5) = 7
* RCCR[REF_CNT] = 7
*/
CMU_FC_0->RCCR = 7;
/* 使能 CMU0 中断*/
CMU_FC_0->IER |= CMU_FC_IER_FHHAEE_MASK | CMU_FC_IER_FLLAEE_MASK;
/* 使能频率检测 */
CMU_FC_0->GCR = CMU_FC_GCR_FCE_MASK;
/* 等待频率检查开始运行 */
while(False == (CMU_FC_0->SR & CMU_FC_SR_RS_MASK) >> CMU_FC_SR_RS_SHIFT);
}
/*CMU1_init:其功能配置CMU1以检测FIRC的频率是否不在编程限制之间。为此,需要对参考计数、HTRC和LTRC进行编程,需要启用中断和频率检查。如果频率不在限值之间,将触发SCG_CMU_LVD_LVWSCG_IRQ。*/
void CMU1_init(void)
{
/* 使能 CMU1时钟门控 */
PCC->PCCn[PCC_CMU1_INDEX] = PCC_PCCn_CGC_MASK;
/*
总线频率 = 8 MHz
参考时钟频率 = 8 MHz
监控时钟频率 = 48 MHz
* 公式1:
F1 = 3 * 参考时钟频率 / 总线频率
F1 = 3 * (8 MHz / 8MHz) = 3
* 公式 2:
F2 = 6 + 3 * (参考时钟频率 / 监控时钟频率)
F2 = 6 + 3 * (8 MHz / 48 MHz) = 6.5
* F1和F2结果之间的最大值Max(F1, F2),Max(3, 6.5) = 6.5
* 最大结果的上限 Ceiling(6.5) = 7
* RCCR[REF_CNT] = 7
*/
CMU_FC_1->RCCR = 7;
/*
* HTCR 和 LTCR 计算
* 参考时钟频率 = 8 MHz
* 监控时钟频率 = 48 MHz
* RCCR[REF_CNT] = 7
* Ideal_monitored_clock_count = (监控时钟频率 / 参考时钟频率 ) * REF_CNT
* Ideal_monitored_clock_count = (48 MHz / 8 MHz) * 7 = 42
*参考时钟频率和监控时钟频率变化+/- 5%
* CMU_FC_inaccuracy = 3
* HTCR[HFREF] = (Ideal_monitored_clock_count * (100% + 5%)) + CMU_FC_inaccuracy
* HTCR[HFREF] = (42 * (105%)) + 3 = 47.1 --> 47
* LTCR[LFREF] = (Ideal_monitored_clock_count * (100% - 5%)) - CMU_FC_inaccuracy
* LTCR[LFREF] = ( 42 * (95%)) - 3 = 36.9 --> 37
*/
CMU_FC_1->HTCR = 47;
CMU_FC_1->LTCR = 37;
/* 使能 CMU1 中断 */
CMU_FC_1->IER = CMU_FC_IER_FHHIE_MASK | CMU_FC_IER_FLLIE_MASK;
/* 使能频率检测 */
CMU_FC_1->GCR = CMU_FC_GCR_FCE_MASK;
/* 等待频率检查开始运行 */
while(False == (CMU_FC_1->SR & CMU_FC_SR_RS_MASK) >> CMU_FC_SR_RS_SHIFT);
}
全部0条评论
快来发表一下你的评论吧 !