M0S12系列时钟管理
时钟管理是嵌入式系统开发中的一个关键部分,特别是在基于ARM Cortex-M0内核的单片机中。M0S12系列单片机提供了多种时钟源和时钟配置选项,以满足不同应用场景的需求。本节将详细介绍M0S12系列单片机的时钟管理原理和配置方法,并通过具体的代码示例来展示如何在实际项目中应用这些知识。
时钟源
M0S12系列单片机支持多种时钟源,包括内部时钟源和外部时钟源。每种时钟源都有其特点和适用场景,开发人员可以根据具体需求选择合适的时钟源。
内部时钟源
-
内部低速RC振荡器(IRC8M)
- 频率:8 MHz
- 特点:启动时间短,但精度较低。
- 应用场景:适用于对启动时间要求较高且对精度要求不高的场合。
-
内部高速RC振荡器(IRC32K)
- 频率:32 kHz
- 特点:低功耗,适用于低功耗模式。
- 应用场景:适用于RTC(实时时钟)等低功耗应用。
-
内部低功耗RC振荡器(LPO1K)
- 频率:1 kHz
- 特点:极低功耗。
- 应用场景:适用于需要长时间运行在低功耗模式的设备。
外部时钟源
-
外部高速晶振(HSE)
- 频率范围:4 MHz - 24 MHz
- 特点:精度高,但启动时间较长。
- 应用场景:适用于需要高精度时钟的应用,如USB通信。
-
外部低速晶振(LSE)
- 频率:32.768 kHz
- 特点:低功耗,精度高。
- 应用场景:适用于RTC等低功耗应用。
时钟树结构
M0S12系列单片机的时钟树结构如图所示:
+---------------------+
| HSE |
+---------------------+
|
v
+---------------------+
| PLL |
+---------------------+
|
v
+---------------------+
| AHB |
+---------------------+
|
v
+---------------------+
| APB1 |
+---------------------+
|
v
+---------------------+
| APB2 |
+---------------------+
- HSE:外部高速晶振,可以直接作为系统时钟或通过PLL倍频后作为系统时钟。
- PLL:锁相环,可以将HSE或IRC8M的频率倍频,以提供更高的系统时钟频率。
- AHB:高级高性能总线,连接CPU和高速外围设备。
- APB1:高级外设总线1,连接低速外围设备。
- APB2:高级外设总线2,连接中速外围设备。
时钟配置
选择系统时钟源
M0S12系列单片机可以通过配置寄存器来选择系统时钟源。系统时钟源的选择通常通过RCC_CFGR
寄存器中的SW
位字段来设置。
// 选择系统时钟源为HSE
void selectSystemClockSourceHSE(void) {
// 使能HSE
RCC_CR |= RCC_CR_HSEON;
// 等待HSE稳定
while (!(RCC_CR & RCC_CR_HSERDY)) {
// HSE未稳定,继续等待
}
// 选择HSE作为系统时钟源
RCC_CFGR &= ~(RCC_CFGR_SW);
RCC_CFGR |= RCC_CFGR_SW_HSE;
}
// 选择系统时钟源为IRC8M
void selectSystemClockSourceIRC8M(void) {
// 选择IRC8M作为系统时钟源
RCC_CFGR &= ~(RCC_CFGR_SW);
RCC_CFGR |= RCC_CFGR_SW_IRC8M;
}
配置PLL
PLL可以将HSE或IRC8M的频率倍频,以提供更高的系统时钟频率。通过配置RCC_PLLCFGR
寄存器可以实现PLL的倍频和分频。
// 配置PLL,将HSE频率倍频为72 MHz
void configurePLL(void) {
// 使能HSE
RCC_CR |= RCC_CR_HSEON;
// 等待HSE稳定
while (!(RCC_CR & RCC_CR_HSERDY)) {
// HSE未稳定,继续等待
}
// 配置PLL
RCC_PLLCFGR |= RCC_PLLCFGR_PLLSRC_HSE | (8 << RCC_PLLCFGR_PLLM_Pos) | (9 << RCC_PLLCFGR_PLLN_Pos);
// 使能PLL
RCC_CR |= RCC_CR_PLLON;
// 等待PLL稳定
while (!(RCC_CR & RCC_CR_PLLRDY)) {
// PLL未稳定,继续等待
}
// 选择PLL作为系统时钟源
RCC_CFGR &= ~(RCC_CFGR_SW);
RCC_CFGR |= RCC_CFGR_SW_PLL;
}
配置AHB、APB1和APB2总线时钟
通过配置RCC_CFGR
寄存器中的相关位字段,可以设置AHB、APB1和APB2总线的时钟分频系数。
// 配置AHB、APB1和APB2总线时钟
void configureBusClocks(void) {
// 配置AHB总线时钟分频系数为1
RCC_CFGR &= ~(RCC_CFGR_HPRE);
RCC_CFGR |= RCC_CFGR_HPRE_DIV1;
// 配置APB1总线时钟分频系数为2
RCC_CFGR &= ~(RCC_CFGR_PPRE1);
RCC_CFGR |= RCC_CFGR_PPRE1_DIV2;
// 配置APB2总线时钟分频系数为1
RCC_CFGR &= ~(RCC_CFGR_PPRE2);
RCC_CFGR |= RCC_CFGR_PPRE2_DIV1;
}
配置系统时钟
通过配置RCC_CFGR
寄存器中的SW
位字段和RCC_CR
寄存器中的HSION
、HSEON
、PLLON
等位字段,可以实现系统时钟的配置。
// 配置系统时钟为72 MHz
void configureSystemClock(void) {
// 使能HSE
RCC_CR |= RCC_CR_HSEON;
// 等待HSE稳定
while (!(RCC_CR & RCC_CR_HSERDY)) {
// HSE未稳定,继续等待
}
// 配置PLL
RCC_PLLCFGR |= RCC_PLLCFGR_PLLSRC_HSE | (8 << RCC_PLLCFGR_PLLM_Pos) | (9 << RCC_PLLCFGR_PLLN_Pos);
// 使能PLL
RCC_CR |= RCC_CR_PLLON;
// 等待PLL稳定
while (!(RCC_CR & RCC_CR_PLLRDY)) {
// PLL未稳定,继续等待
}
// 配置AHB总线时钟分频系数为1
RCC_CFGR &= ~(RCC_CFGR_HPRE);
RCC_CFGR |= RCC_CFGR_HPRE_DIV1;
// 配置APB1总线时钟分频系数为2
RCC_CFGR &= ~(RCC_CFGR_PPRE1);
RCC_CFGR |= RCC_CFGR_PPRE1_DIV2;
// 配置APB2总线时钟分频系数为1
RCC_CFGR &= ~(RCC_CFGR_PPRE2);
RCC_CFGR |= RCC_CFGR_PPRE2_DIV1;
// 选择PLL作为系统时钟源
RCC_CFGR &= ~(RCC_CFGR_SW);
RCC_CFGR |= RCC_CFGR_SW_PLL;
// 等待系统时钟源切换完成
while ((RCC_CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL) {
// 系统时钟源未切换到PLL,继续等待
}
}
配置外部低速晶振(LSE)
LSE可以用于RTC等低功耗应用。通过配置RCC_BDCR
寄存器可以实现LSE的使能和配置。
// 使能LSE
void enableLSE(void) {
// 使能LSE
RCC_BDCR |= RCC_BDCR_LSEON;
// 等待LSE稳定
while (!(RCC_BDCR & RCC_BDCR_LSERDY)) {
// LSE未稳定,继续等待
}
}
配置内部低速RC振荡器(IRC32K)
IRC32K可以用于低功耗模式下的RTC。通过配置RCC_CSR
寄存器可以实现IRC32K的使能和配置。
// 使能IRC32K
void enableIRC32K(void) {
// 使能IRC32K
RCC_CSR |= RCC_CSR_LSION;
// 等待IRC32K稳定
while (!(RCC_CSR & RCC_CSR_LSIRDY)) {
// IRC32K未稳定,继续等待
}
}
配置RTC时钟源
RTC时钟源可以是LSE或IRC32K。通过配置RCC_BDCR
寄存器可以实现RTC时钟源的选择。
// 选择LSE作为RTC时钟源
void selectRTCSourceLSE(void) {
// 使能LSE
RCC_BDCR |= RCC_BDCR_LSEON;
// 等待LSE稳定
while (!(RCC_BDCR & RCC_BDCR_LSERDY)) {
// LSE未稳定,继续等待
}
// 选择LSE作为RTC时钟源
RCC_BDCR |= RCC_BDCR_RTCSEL_LSE;
// 使能RTC时钟
RCC_BDCR |= RCC_BDCR_RTCEN;
}
// 选择IRC32K作为RTC时钟源
void selectRTCSourceIRC32K(void) {
// 使能IRC32K
RCC_CSR |= RCC_CSR_LSION;
// 等待IRC32K稳定
while (!(RCC_CSR & RCC_CSR_LSIRDY)) {
// IRC32K未稳定,继续等待
}
// 选择IRC32K作为RTC时钟源
RCC_BDCR |= RCC_BDCR_RTCSEL_LSI;
// 使能RTC时钟
RCC_BDCR |= RCC_BDCR_RTCEN;
}
时钟故障检测
为了确保系统的稳定性,M0S12系列单片机提供了时钟故障检测(Clock Security System, CSS)功能。通过配置RCC_CR
寄存器中的CSSON
位字段可以启用CSS。
// 启用HSE时钟故障检测
void enableClockSecuritySystem(void) {
// 启用HSE时钟故障检测
RCC_CR |= RCC_CR_CSSON;
}
时钟配置示例
以下是一个完整的时钟配置示例,包括选择系统时钟源为HSE、配置PLL、设置总线时钟分频系数、使能LSE、选择RTC时钟源为LSE等步骤。
#include "stm32f10x.h"
// 选择系统时钟源为HSE
void selectSystemClockSourceHSE(void) {
// 使能HSE
RCC_CR |= RCC_CR_HSEON;
// 等待HSE稳定
while (!(RCC_CR & RCC_CR_HSERDY)) {
// HSE未稳定,继续等待
}
// 选择HSE作为系统时钟源
RCC_CFGR &= ~(RCC_CFGR_SW);
RCC_CFGR |= RCC_CFGR_SW_HSE;
}
// 配置PLL,将HSE频率倍频为72 MHz
void configurePLL(void) {
// 使能HSE
RCC_CR |= RCC_CR_HSEON;
// 等待HSE稳定
while (!(RCC_CR & RCC_CR_HSERDY)) {
// HSE未稳定,继续等待
}
// 配置PLL
RCC_PLLCFGR |= RCC_PLLCFGR_PLLSRC_HSE | (8 << RCC_PLLCFGR_PLLM_Pos) | (9 << RCC_PLLCFGR_PLLN_Pos);
// 使能PLL
RCC_CR |= RCC_CR_PLLON;
// 等待PLL稳定
while (!(RCC_CR & RCC_CR_PLLRDY)) {
// PLL未稳定,继续等待
}
// 选择PLL作为系统时钟源
RCC_CFGR &= ~(RCC_CFGR_SW);
RCC_CFGR |= RCC_CFGR_SW_PLL;
// 等待系统时钟源切换完成
while ((RCC_CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL) {
// 系统时钟源未切换到PLL,继续等待
}
}
// 配置AHB、APB1和APB2总线时钟
void configureBusClocks(void) {
// 配置AHB总线时钟分频系数为1
RCC_CFGR &= ~(RCC_CFGR_HPRE);
RCC_CFGR |= RCC_CFGR_HPRE_DIV1;
// 配置APB1总线时钟分频系数为2
RCC_CFGR &= ~(RCC_CFGR_PPRE1);
RCC_CFGR |= RCC_CFGR_PPRE1_DIV2;
// 配置APB2总线时钟分频系数为1
RCC_CFGR &= ~(RCC_CFGR_PPRE2);
RCC_CFGR |= RCC_CFGR_PPRE2_DIV1;
}
// 使能LSE
void enableLSE(void) {
// 使能LSE
RCC_BDCR |= RCC_BDCR_LSEON;
// 等待LSE稳定
while (!(RCC_BDCR & RCC_BDCR_LSERDY)) {
// LSE未稳定,继续等待
}
}
// 选择LSE作为RTC时钟源
void selectRTCSourceLSE(void) {
// 使能LSE
RCC_BDCR |= RCC_BDCR_LSEON;
// 等待LSE稳定
while (!(RCC_BDCR & RCC_BDCR_LSERDY)) {
// LSE未稳定,继续等待
}
// 选择LSE作为RTC时钟源
RCC_BDCR |= RCC_BDCR_RTCSEL_LSE;
// 使能RTC时钟
RCC_BDCR |= RCC_BDCR_RTCEN;
}
// 启用HSE时钟故障检测
void enableClockSecuritySystem(void) {
// 启用HSE时钟故障检测
RCC_CR |= RCC_CR_CSSON;
}
// 主函数
int main(void) {
// 选择系统时钟源为HSE
selectSystemClockSourceHSE();
// 配置PLL,将HSE频率倍频为72 MHz
configurePLL();
// 配置AHB、APB1和APB2总线时钟
configureBusClocks();
// 使能LSE
enableLSE();
// 选择LSE作为RTC时钟源
selectRTCSourceLSE();
// 启用HSE时钟故障检测
enableClockSecuritySystem();
// 初始化其他外设
// ...
// 主循环
while (1) {
// 应用程序代码
}
}
时钟配置注意事项
- 时钟源稳定性:在配置时钟源时,务必确保目标时钟源已经稳定。可以通过读取相应的状态寄存器来判断时钟源是否稳定。
- 时钟树结构:配置时钟时,需要考虑时钟树结构,确保各个总线时钟的频率符合外设的要求。
- 时钟故障检测:启用时钟故障检测功能可以提高系统的可靠性,但需要注意处理时钟故障中断。
- 低功耗模式:在低功耗模式下,某些时钟源可能会被关闭,需要确保在进入和退出低功耗模式时正确配置时钟源。
时钟故障中断处理
时钟故障检测(CSS)功能启用后,如果HSE时钟源发生故障,会触发时钟故障中断。需要在中断处理函数中处理时钟故障。
// 时钟故障中断处理函数
void NMI_Handler(void) {
// 检查是否是HSE时钟故障
if (RCC_CSR & RCC_CSR_CSSF) {
// 清除时钟故障标志
RCC_CSR &= ~RCC_CSR_CSSF;
// 切换到内部RC振荡器
selectSystemClockSourceIRC8M();
// 重新配置PLL
configurePLL();
// 重新配置总线时钟
configureBusClocks();
// 重新初始化外设
// ...
}
// 其他中断处理
// ...
}
低功耗模式下的时钟管理
在低功耗模式下,M0S12系列单片机可以关闭某些时钟源以节省功耗。例如,进入STOP模式时,可以关闭HSE和PLL,仅保留IRC32K或LSE作为RTC时钟源。
// 进入STOP模式
void enterSTOPMode(void) {
// 选择IRC32K作为RTC时钟源
selectRTCSourceIRC32K();
// 使能RTC
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
PWR鞥PeriphCmd(PWR_PowerDownMode, ENABLE);
// 进入STOP模式
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
__WFI(); // 进入深度### 低功耗模式下的时钟管理
在低功耗模式下,M0S12系列单片机可以关闭某些时钟源以节省功耗。例如,进入STOP模式时,可以关闭HSE和PLL,仅保留IRC32K或LSE作为RTC时钟源。这种配置可以确保设备在长时间低功耗运行时仍然能够保持准确的时间。
#### 进入STOP模式
在进入STOP模式之前,需要确保RTC时钟源已经正确配置,并且相关外设已经准备好进入低功耗模式。以下是一个示例代码,展示了如何进入STOP模式并保留RTC时钟源。
```c
#include "stm32f10x.h"
// 选择RTC时钟源为IRC32K
void selectRTCSourceIRC32K(void) {
// 使能IRC32K
RCC_CSR |= RCC_CSR_LSION;
// 等待IRC32K稳定
while (!(RCC_CSR & RCC_CSR_LSIRDY)) {
// IRC32K未稳定,继续等待
}
// 选择IRC32K作为RTC时钟源
RCC_BDCR |= RCC_BDCR_RTCSEL_LSI;
// 使能RTC时钟
RCC_BDCR |= RCC_BDCR_RTCEN;
}
// 进入STOP模式
void enterSTOPMode(void) {
// 选择IRC32K作为RTC时钟源
selectRTCSourceIRC32K();
// 使能PWR时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
// 使能备用区域时钟
PWR_BackupAccessCmd(ENABLE);
// 配置PWR进入STOP模式
PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI);
// 进入STOP模式
__WFI(); // 进入深度睡眠模式
}
// 主函数
int main(void) {
// 选择系统时钟源为HSE
selectSystemClockSourceHSE();
// 配置PLL,将HSE频率倍频为72 MHz
configurePLL();
// 配置AHB、APB1和APB2总线时钟
configureBusClocks();
// 使能LSE
enableLSE();
// 选择LSE作为RTC时钟源
selectRTCSourceLSE();
// 启用HSE时钟故障检测
enableClockSecuritySystem();
// 初始化其他外设
// ...
// 进入STOP模式
enterSTOPMode();
// 主循环
while (1) {
// 应用程序代码
}
}
时钟配置注意事项
- 时钟源稳定性:在配置时钟源时,务必确保目标时钟源已经稳定。可以通过读取相应的状态寄存器来判断时钟源是否稳定。
- 时钟树结构:配置时钟时,需要考虑时钟树结构,确保各个总线时钟的频率符合外设的要求。
- 时钟故障检测:启用时钟故障检测功能可以提高系统的可靠性,但需要注意处理时钟故障中断。
- 低功耗模式:在低功耗模式下,某些时钟源可能会被关闭,需要确保在进入和退出低功耗模式时正确配置时钟源。
时钟故障中断处理
时钟故障检测(CSS)功能启用后,如果HSE时钟源发生故障,会触发时钟故障中断。需要在中断处理函数中处理时钟故障,确保系统能够恢复到正常的工作状态。
// 时钟故障中断处理函数
void NMI_Handler(void) {
// 检查是否是HSE时钟故障
if (RCC_CSR & RCC_CSR_CSSF) {
// 清除时钟故障标志
RCC_CSR &= ~RCC_CSR_CSSF;
// 切换到内部RC振荡器
selectSystemClockSourceIRC8M();
// 重新配置PLL
configurePLL();
// 重新配置总线时钟
configureBusClocks();
// 重新初始化外设
// ...
}
// 其他中断处理
// ...
}
总结
时钟管理是嵌入式系统开发中的一个重要环节,尤其是在基于ARM Cortex-M0内核的M0S12系列单片机中。通过合理选择和配置时钟源,可以满足不同应用场景的需求,提高系统的性能和可靠性。同时,低功耗模式下的时钟管理也是确保设备在长时间运行时节省功耗的关键。希望本节的内容能够帮助开发人员更好地理解和应用M0S12系列单片机的时钟管理功能。