当前位置: 首页 > news >正文

STM32 HAL库时钟系统详解

1. STM32时钟系统概述

STM32微控制器的时钟系统是其核心功能之一,它为处理器内核、外设和存储器提供精确的时钟信号。与传统的51单片机不同,STM32采用了更为复杂的时钟树结构,这种设计带来了更高的灵活性和性能优化空间。

1.1 STM32时钟系统的主要特点

  1. 多时钟源:STM32支持多种时钟源,包括内部高速/低速RC振荡器、外部高速/低速晶体振荡器以及PLL倍频器等。

  2. 时钟树结构:采用分层次的时钟树设计,允许不同外设使用不同频率的时钟。

  3. 灵活的时钟分配:可以独立控制各个外设的时钟使能,降低功耗。

  4. 时钟安全系统(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_OscInitTypeDefRCC_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 典型时钟配置流程

  1. 初始化振荡器配置

    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);
  2. 初始化时钟配置

    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)路径

系统时钟可以有多个来源:

  1. HSI:内部16MHz RC振荡器

  2. HSE:外部4-26MHz晶体或时钟输入

  3. PLL:锁相环输出

系统时钟通过AHB预分频器后生成HCLK(CPU时钟),然后进一步分频生成PCLK1(APB1外设时钟)和PCLK2(APB2外设时钟)。

3.2 PLL配置

PLL是生成高频系统时钟的关键部件,其配置需要考虑以下参数:

  1. PLL源(PLLM):选择HSI或HSE作为PLL输入

  2. 分频系数(PLLM):对输入时钟进行分频

  3. 倍频系数(PLLN):对分频后的时钟进行倍频

  4. 系统时钟分频(PLLP):生成系统时钟

  5. 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具有以下特点:

  1. 内核集成:作为Cortex-M内核的一部分,所有基于该内核的MCU都具备此功能

  2. 简单易用:配置简单,不需要复杂的初始化过程

  3. 高优先级:通常具有最高的异常优先级之一

  4. 操作系统友好:专为操作系统设计,适合作为系统时基

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的工作原理,我们可以实现更精确的微秒级延时和各种复杂的时间控制逻辑。在实际应用中,需要根据具体需求选择适当的延时方法,并考虑系统性能和功耗的平衡。

 有不懂大家可以留言评论私信!!!

 

相关文章:

  • AndroidTV 当贝播放器-v1.5.2-官方简洁无广告版
  • SAP-ABAP:BAPI_ACC_DOCUMENT_POST 详解(总账、应收账款、应付账款等业务场景的自动化集成)
  • Android 存储路径
  • 大模型学习八:‌Sealos 私有化部署之VMware 安装ubuntu22.04 虚拟机安装(实操)
  • 【产品】ToB产品需求分析
  • 【Java SE】泛型详解
  • GAT-GRAPH ATTENTION NETWORKS(论文笔记)
  • 计算机组成原理笔记(十四)——3.4指令类型
  • 某益网络面经总结
  • 单链表专题(C语言)
  • 基于SpringBoot的电影订票系统(源码+数据库+万字文档+ppt)
  • 架构师面试(三十):IM 分层架构
  • 架构生命周期(高软57)
  • CSS padding(填充)学习笔记
  • C# Winform 入门(16)之图片合成
  • Linux--线程概念与控制
  • 突破边界:从 C# 到 Python 的范式跃迁与实战指南
  • 图像分割基础学习
  • vLLM部署Qwen2.5-Omni 提供API的详细步骤
  • CSE lesson2 chrony服务器
  • 美乌矿产协议签署被曝“临门一脚”时生变,美方提附加条件
  • 浪尖计划再出发:万亿之城2030课题组赴九城调研万亿产业
  • 中国海警位中国黄岩岛领海及周边区域执法巡查
  • 陕西省通报6起违反八项规定典型问题,省卫健委原主任刘宝琴违规收受礼品礼金
  • 4月译著联合书单|心爱之物:热爱如何联结并塑造我们
  • 排除燃气爆炸、人为放火可能,辽宁辽阳火灾事故起火原因正在调查