YTM32B1LD0系列MCU时钟模块介绍
YTM32B1LD0系列MCU是苏州云途半导体针对车身控制领域推出的入门级32位产品,最高主频达48MHz,内嵌64KB Flash和8KB SRAM,MCU内嵌CAN-FD,LIN等丰富接口,满足车规可靠性要求AEC-Q100。可以应用于车身传感器控制、电机控制、胎压监测、电动座椅、电动尾门、天窗、灯光控制及内饰灯控制等方面。
CPM的基本特性
YTM32B1LD0系列的时钟和电源模块统一通过Clock and Power Control Module(CPM)控制。CPM的主要特性包括:
三个内嵌的时钟源
- 内部48MHz振荡器
- 内部低速128KHz低频振荡器
- 内部低速2KHz的低频振荡器
- 支持8-24MHz晶体振荡器
- 系统时钟和总线时钟分频独立可配置
- 支持最低5uA的低功耗模式
- 支持外设时钟管理,可以单独控制每一个外设的时钟
从上面CPM的框图中,可以看到系统时钟可以从内部高速FIRC,内部SIRC或者外部晶振(EOSC)三者任选其一,框图中PwrCtrlClk是一个芯片的启动时钟,默认是48MHz的FIRC,系统时钟直接通过CPM的SCCR寄存器CLKMD域选择。注意在进行时钟切换的时候,务必确保新的时钟源已经打开,如果系统直接切换到了一个没有开启的时钟源,那么系统将会因为失去时钟而无法运行,此时如果没有配置时钟监测模块(CMU),那么系统会进入锁死状态,只能手动复位或者重新上电。
配置好系统时钟之后,可以通过CPM的SCCR寄存器SYSDIV域配置系统时钟的分频比,因为MCU默认支持48MHz,所以默认可以保持此处不分频,如果需要通过降低主频来减小功耗,可以将分频比例适当加大。系统总线时钟频率建议保持为系统主频的一半,所以CPM的SCCR寄存器IPSDIV域保持默认值即可。
系统外设时钟管理
系统外设的时钟源统一由IPC模块统一管理,YTM32B1LD0系列MCU的外设时钟源主要有总线时钟、内部高速FIRC48MHz、外部晶振EOSC和内部低速SIRC 128KHz。IPC模块可以控制外设模块时钟是否启用,如果模块支持功能时钟,IPC还可以控制外设选择哪种功能时钟,并且可以实现对外设时钟进行预分频。对于外设时钟的具体配置可以参考下面的表格:
No. | ModuleName | BusClock | Default ON | FunctionClock | AdditionalClock |
---|---|---|---|---|---|
0 | CPM | BUS_CLK | ON | ||
1 | RCM | BUS_CLK | ON | ||
2 | CCM | BUS_CLK | ON | ||
3 | EFM | BUS_CLK | ON | ||
4 | DMA | SYS_CLK | OFF | ||
5 | GPIO | SYS_CLK | OFF | ||
6 | DIVSQRT | SYS_CLK | OFF | ||
7 | CRC | SYS_CLK | OFF | ||
8 | FlexCAN0 | SYS_CLK | OFF | EOSC_CLK | |
9 | eTMR0 | SYS_CLK | OFF | TCLK0/1/2 | |
10 | eTMR1 | SYS_CLK | OFF | TCLK0/1/2 | |
11 | eTMR2 | SYS_CLK | OFF | TCLK0/1/2 | |
12 | IpTMR0 | BUS_CLK | OFF | YES | |
13 | pTMR0 | BUS_CLK | OFF | ||
14 | RTC | BUS_CLK | OFF | 128K_DIV4/2KHz_DIV2 | |
15 | WDG | BUS_CLK | ON | 128K_DIV4/2KHz | |
16 | I2C0 | BUS_CLK | OFF | YES | |
17 | I2C1 | BUS_CLK | OFF | YES | |
18 | SPI0 | BUS_CLK | OFF | YES | |
19 | SPI1 | BUS_CLK | OFF | YES | |
20 | SPI2 | BUS_CLK | OFF | YES | |
21 | UART0 | BUS_CLK | OFF | YES | |
22 | UART1 | BUS_CLK | OFF | YES | |
23 | UART2 | BUS_CLK | OFF | YES | |
24 | Reserved | BUS_CLK | |||
25 | ACMP0 | BUS_CLK | OFF | YES | |
26 | ADC0 | BUS_CLK | OFF | YES |
上述表格中,第一列代表模块在IPC控制寄存器的序号,BUS_CLK代表模块的默认时钟,这里主要注意有的模块是和系统时钟SYS_CLK同频,使用这些模块的时候,计算时钟频率需要按照系统时钟频率计算(默认48MHz)。根据系统运行需要,系统默认会开启一部分模块的时钟,对于其他外设,建议只有在使用的时候才打开时钟,这样可以有效的降低系统功耗。表格中FunctionClock一列为YES的代表相应的模块支持功能时钟,当系统进入低功耗模式的时候,这些模块都可以在FunctionClock下继续工作,并可以正常唤醒系统。
上述表格的最后一列为模块的其他可选时钟:
- FlexCAN模块支持使用系统时钟和外部晶振,二者的选择是通过FlexCAN内部寄存器选择的,当使用EOSC作为时钟时,务必确保EOSC已经正确初始化,否则FlexCAN模块无法正常工作。
- eTMR模块同样支持使用系统时钟和外部时钟输入,因为该模块并不支持通过IPC选择功能时钟,因而无法在低功耗模式下运行。当eTMR选择外部时钟时,需要将相应引脚的功能选择到eTMR外部时钟输入到功能。
- RTC模块的时钟可以选择内部128KHz时钟4分频(32K)或者内部2KHz时钟2分频(1K),当系统进入低功耗模式的时候,如果SIRC 128K或者2KHz时钟保持开启,那么RTC模块在低功耗模式下可以正常运行,也可以正常唤醒系统。后续YTM系列MCU都支持外部32.768KHz的晶振作为RTC的时钟。
- WDG模块和RTC模块类似,同样支持128KHz时钟4分频(32K)和内部2KHz时钟作为模块功能时钟。
时钟和电源初始化代码
在进行芯片初始化的时候,完成必要的变量初始化之后就要对系统时钟和电源配置进行一个初始化的配置,在YTM32B1LD0系列MCU中,这部分的初始化主要涉及以下代码:
/* ******************************************************************************
* File: clock_config.c
* ******************************************************************************/
#include "clock_config.h"
/* *************************************************************************
* Configuration structure for peripheral clock configuration 0
* ************************************************************************* */
/*! @brief peripheral clock configuration 0 */
peripheral_clock_config_t peripheralClockConfig0[NUM_OF_PERIPHERAL_CLOCKS_0] = {
{
.clkName = UART1_CLK, /* 初始化UART1模块时钟 */
.clkGate = true, /* 启用UART1模块时钟 */
.clkSrc = CLK_SRC_BUS, /* UART1使用BUS时钟,其他可选FIRC,EOSC,部分外设不支持 */
.divider = DIV_BY_1, /* 默认UART1时钟不分频 */
},
};
/* 采用const可以保证变量存储于Flash,不占用RAM空间,也可以防止变量被意外改动 */
const cpm_config_t cpmConfig =
{
.sysClkSrc = CPM_SYSTEM_CLOCK_SRC_FIRC, /* 系统时钟使用 FIRC(48MHz) */
.sircConfig =
{
.initialize = true, /* 打开SIRC时钟 */
.enableInDeepsleep = false, /* SIRC在DeepSleep模式下不开启 */
.isLock = false, /* 不锁定SIRC配置 */
},
.eoscConfig =
{
.initialize = true, /* 初始化并启用EOSC时钟 */
.freq = FEATURE_CPM_EOSC_FREQ, /* 外部晶振时钟频率配置,此处为demo板24M */
.enableInDeepsleep = false, /* EOSC在DeepSleep模式下不开启 */
.enableBypassMode = false, /* 非有源晶振,开启内部放大电路 */
},
.fircConfig =
{
.initialize = true, /* 初始化并启用FIRC时钟 */
.enableInDeepsleep = false, /* FIRC在DeepSleep模式下不开启 */
},
.sysDiv = CPM_SYS_CLK_DIV_BY_1, /* 系统时钟1分频 */
.ipsDiv = CPM_IPC_CLK_DIV_BY_2, /* 系统总线时钟2分频 */
.clockOutConfig =
{
.initialize = false, /* 不配置时钟输出功能 */
.source = CPM_CLKOUT_SEL_MASTER_CLK, /* 时钟输出默认选择系统时钟 */
},
.wdtClkConfig =
{
.initialize = false, /* 不初始化WDG模块时钟 */
.wdtClkSel = CPM_WDGCLK_SEL_SIRC2K, /* WDG模块使用SIRC 2KHz 时钟 */
.isLock = false, /* 不锁定模块配置 */
}
};
const cpm_lvd_config_t lvdConfig =
{
.initialize = true, /* 初始化低压监测模块 */
.enable = true, /* 启用低压监测模块 */
.BOREnable = true, /* 使能BOR复位电路 */
.lowPowerEnable = true, /* 低功耗模式启用低压监测模块 */
.riseIntEnable = true, /* LVD置位中断使能(上升沿,产生LVD)*/
.fallIntEnable = true, /* LVD清零中断使能(下降沿,LVD恢复)*/
.clearIntFlag = true, /* 初始化时自动清除LVD标志位 */
.threshold = 12, /* LVD阈值选择,对应电压参考数据手册 */
.hysteresisLevel = 3 /* LVD迟滞监测等级 */
};
/* *************************************************************************
* Configuration structure for Clock Configuration 0
* ************************************************************************* */
/*! @brief User Configuration structure clock_managerCfg_0 */
clock_manager_user_config_t clockMan1_InitConfig0 = {
.cpmConfigPtr = &cpmConfig, /* cpm模块初始化变量指针 */
.lvdConfigPtr = &lvdConfig, /* LVD模块初始化变量指针 */
.ipcConfig =
{
.peripheralClocks = peripheralClockConfig0,/* 外设时钟配置数组地址 */
.count = NUM_OF_PERIPHERAL_CLOCKS_0, /* 外设时钟配置数组长度 */
}
};
/*! @brief Array of pointers to User configuration structures */
clock_manager_user_config_t const * g_clockManConfigsArr[] = {
&clockMan1_InitConfig0 /* 系统时钟配置数组,此处仅配置1组 */
};
/*! @brief Array of pointers to User defined Callbacks configuration structures */
/* 时钟切换回调函数,支持切换前回调和切换后回调. */
clock_manager_callback_user_config_t * g_clockManCallbacksArr[] = {(void*)0};
系统初始化函数调用:
/* 初始化系统时钟配置数组,可以根据需要定义多种场景的时钟配置 */
CLOCK_SYS_Init(g_clockManConfigsArr, CLOCK_MANAGER_CONFIG_CNT,
g_clockManCallbacksArr, CLOCK_MANAGER_CALLBACK_CNT);
/* 进行时钟模式切换, 此处切换到第一组配置 */
CLOCK_SYS_UpdateConfiguration(0U, CLOCK_MANAGER_POLICY_AGREEMENT);
常见问题解答
- 芯片内部是否有FLL/PLL等倍频模块,芯片主时钟选哪种比较好?
YTM32B1LD0芯片内部没有时钟倍频模块,芯片主时钟一般建议选用内部FIRC 48MHz的时钟,这样可以充分发挥芯片的性能,内部FIRC 48MHz不依赖外部时钟,只需要在时钟初始化的时候保持开启状态就可以正常使用。在低功耗模式(包含DeepSleep和Standby)不建议开启FIRC。当系统主时钟选择FIRC的时候,一些会时钟要求比较高的模块比如CAN和LIN依然可以选择外部晶振作为参考时钟,这个时候外部晶振建议选择8MHz就可以了,如果需要使用CAN-FD,外部晶振可以选择频率稍为高一些的晶振。
芯片在低功耗模式下哪些时钟可以开启?
- 内部高速FIRC 48MHz时钟一般不建议开启,当
cpmConfig.fircConfig.enableInDeepsleep == true
的时候,系统无法进入Standby模式,请求默认会上升为DeepSleep模式。 - 内部低速128KHz时钟可以选择性开启,比如要用到lpTMR/RTC/WDG/GPIO数字滤波等功能的时候,可以将
cpmConfig.sircConfig.enableInDeepsleep
设置为true
,这样这些模块可以在DeepSleep和Standby模式下工作,也可以正常唤醒系统。 - 外部EOSC一般也不建议开启
- 内部高速FIRC 48MHz时钟一般不建议开启,当
最后更新于 2021-12-17 03:25:44 并被添加「嵌入式 时钟 初始化 应用笔记」标签,已有 3420 位童鞋阅读过。
本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。