打印
[应用相关]

STM32 HAL库时钟系统详解

[复制链接]
122|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
tpgf|  楼主 | 2025-4-14 08:50 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
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

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

2208

主题

16578

帖子

18

粉丝