1. STM32时钟系统概述
STM32微控制器的时钟系统是其核心功能之一,它为处理器内核、外设和存储器提供精确的时钟信号。与传统的51单片机不同,STM32采用了更为复杂的时钟树结构,这种设计带来了更高的灵活性和性能优化空间。
1.1 STM32时钟系统的主要特点
多时钟源:STM32支持多种时钟源,包括内部高速/低速RC振荡器、外部高速/低速晶体振荡器以及PLL倍频器等。
时钟树结构:采用分层次的时钟树设计,允许不同外设使用不同频率的时钟。
灵活的时钟分配:可以独立控制各个外设的时钟使能,降低功耗。
时钟安全系统(CSS):可监测外部时钟故障并自动切换到内部时钟源。
1.2 主要时钟源
STM32通常包含以下时钟源:
HSI (High Speed Internal):内部高速RC振荡器,典型频率为8MHz或16MHz
HSE (High Speed External):外部高速晶体/陶瓷谐振器或外部时钟源,4-26MHz
LSI (Low Speed Internal):内部低速RC振荡器,约32kHz,主要用于独立看门狗和RTC
LSE (Low Speed External):外部低速晶体,32.768kHz,主要用于RTC
PLL (Phase Locked Loop):锁相环,用于倍频HSI或HSE时钟
2. HAL库中的时钟配置
HAL库提供了一套完整的API来配置和管理STM32的时钟系统,简化了复杂的寄存器操作。
2.1 时钟配置结构体
HAL库使用RCC_OscInitTypeDef和RCC_ClkInitTypeDef两个主要结构体来配置时钟:
typedef struct {
uint32_t OscillatorType; // 指定要配置的振荡器类型
uint32_t HSEState; // HSE状态
uint32_t LSEState; // LSE状态
uint32_t HSIState; // HSI状态
uint32_t HSICalibrationValue; // HSI校准值
uint32_t LSIState; // LSI状态
RCC_PLLInitTypeDef PLL; // PLL配置
} RCC_OscInitTypeDef;
typedef struct {
uint32_t ClockType; // 要配置的时钟类型
uint32_t SYSCLKSource; // 系统时钟源
uint32_t AHBCLKDivider; // AHB预分频器
uint32_t APB1CLKDivider; // APB1预分频器
uint32_t APB2CLKDivider; // APB2预分频器
} RCC_ClkInitTypeDef;
2.2 典型时钟配置流程
初始化振荡器配置:
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 8;
RCC_OscInitStruct.PLL.PLLN = 336;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 7;
HAL_RCC_OscConfig(&RCC_OscInitStruct);
初始化时钟配置:
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5);
3. 时钟树详解
理解STM32的时钟树对于正确配置系统时钟至关重要。下面以STM32F4系列为例说明主要时钟路径。
3.1 系统时钟(SYSCLK)路径
系统时钟可以有多个来源:
HSI:内部16MHz RC振荡器
HSE:外部4-26MHz晶体或时钟输入
PLL:锁相环输出
系统时钟通过AHB预分频器后生成HCLK(CPU时钟),然后进一步分频生成PCLK1(APB1外设时钟)和PCLK2(APB2外设时钟)。
3.2 PLL配置
PLL是生成高频系统时钟的关键部件,其配置需要考虑以下参数:
PLL源(PLLM):选择HSI或HSE作为PLL输入
分频系数(PLLM):对输入时钟进行分频
倍频系数(PLLN):对分频后的时钟进行倍频
系统时钟分频(PLLP):生成系统时钟
USB OTG FS/SDIO/RNG时钟分频(PLLQ):生成48MHz时钟
计算公式:
VCO输入频率 = PLL输入时钟频率 / PLLM
VCO输出频率 = VCO输入频率 × PLLN
PLL输出时钟 = VCO输出频率 / PLLP
USB/SDIO/RNG时钟 = VCO输出频率 / PLLQ
3.3 外设时钟
STM32的外设时钟主要分为:
AHB总线时钟(HCLK):用于CPU、内存和DMA
APB1总线时钟(PCLK1):低速外设,最大频率通常为系统时钟的1/4
APB2总线时钟(PCLK2):高速外设,最大频率通常为系统时钟的1/2
每个外设都有独立的时钟使能位,可以在RCC寄存器中控制。
4. 实际应用示例
1. SysTick定时器概述
SysTick是ARM Cortex-M内核提供的一个24位递减计数器,常用于操作系统的时间基准或简单的延时功能。与普通定时器相比,SysTick具有以下特点:
内核集成:作为Cortex-M内核的一部分,所有基于该内核的MCU都具备此功能
简单易用:配置简单,不需要复杂的初始化过程
高优先级:通常具有最高的异常优先级之一
操作系统友好:专为操作系统设计,适合作为系统时基
2. HAL库中的SysTick配置
HAL库已经内置了对SysTick的基本配置,通常在HAL_Init()函数中初始化。主要配置包括
// 在HAL_Init()中调用
HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority) {
// 配置SysTick每1ms中断一次
if (HAL_SYSTICK_Config(SystemCoreClock / (1000U / uwTickFreq)) > 0U) {
return HAL_ERROR;
}
// 设置SysTick中断优先级
HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority, 0U);
return HAL_OK;
}
3. 基于SysTick的延时实现
3.1 毫秒级延时实现
HAL库已经提供了毫秒级延时函数HAL_Delay(),其实现原理如下:
__weak void HAL_Delay(uint32_t Delay) {
uint32_t tickstart = HAL_GetTick();
uint32_t wait = Delay;
// 防止因计数器溢出导致的问题
if (wait < HAL_MAX_DELAY) {
wait += (uint32_t)(uwTickFreq);
}
while ((HAL_GetTick() - tickstart) < wait) {
// 可以在此处添加低功耗模式代码
}
}
3.2 微秒级延时实现
由于SysTick通常配置为1ms中断,要实现更精确的微秒级延时,需要直接操作SysTick寄存器:
void HAL_Delay_us(uint32_t us) {
uint32_t start = SysTick->VAL;
uint32_t clock = HAL_RCC_GetHCLKFreq() / 1000000; // 每个微秒的时钟周期数
uint32_t load = SysTick->LOAD;
uint32_t ticks = us * clock;
uint32_t elapsed;
while(1) {
uint32_t current = SysTick->VAL;
if (current < start) {
elapsed = start - current;
} else {
elapsed = (load - current) + start;
}
if (elapsed >= ticks) {
break;
}
}
}
6. 总结
基于SysTick的延时实现是STM32开发中最常用的时间控制方法之一。HAL库已经提供了基本的毫秒级延时功能,通过深入了解SysTick的工作原理,我们可以实现更精确的微秒级延时和各种复杂的时间控制逻辑。在实际应用中,需要根据具体需求选择适当的延时方法,并考虑系统性能和功耗的平衡。
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/m0_75187370/article/details/147096080
|