STM32之LL库使用(一)

cubmax配置

SYS配置

在STM32CubeMX的SYS配置中,Debug选项决定了芯片与调试器(如ST-Link、J-Link)之间的通信协议以及是否开启高级跟踪功能。

为了确保核心逻辑清晰,我将这些模式按功能深度分为"基础调试"与"高级跟踪"两大类为您解析:

一、 基础调试模式(最常用)

  1. Serial Wire (SWD)
    • 效果:使用2根线(SWDIO、SWCLK)进行调试。
    • 特点:占用引脚最少,是目前Cortex-M内核最主流的调试方式。它支持基本的程序下载、单步调试、变量查看。
  2. JTAG (4 Pins)
    • 效果:使用4根线(TMS、TCK、TDI、TDO)。
    • 特点:传统的调试接口,支持多芯片串联(Daisy Chain)。如果你的开发工具链较老,或者需要链式调试多个设备,通常选择此项。
  3. JTAG (5 Pins)
    • 效果 :在4引脚基础上增加了 nTRST(异步复位)引脚。
    • 特点:提供了额外的硬件复位控制,增强了调试连接的稳定性。

二、 异步跟踪模式(支持printf打印)

  1. Trace Asynchronous Sw
    • 效果 :在 SWD 的基础上增加了一根 SWO 引脚。
    • 特点 :支持 ITM (Instrumentation Trace Macrocell)。它可以实现不占用串口的高速数据输出(类似printf),并能实时监测系统的功耗、变量波形等,而无需停止CPU运行。

三、 同步跟踪模式(指令级追踪)

这类模式通常用于极其复杂的Bug排查,需要配合支持ETM(Embedded Trace Macrocell)的高端调试器(如J-Trace)。

  1. JTAG with Trace Synchro (1/2/4 bits)
    • 效果 :在JTAG基础上增加 Trace ClockTrace Data(1/2/4位可选)。
    • 特点 :支持实时指令追踪。它能记录CPU执行过的每一条指令流。位数(1/2/4 bits)越多,数据带宽越高,在高主频下捕获的数据越完整,不会因为缓冲区溢出丢失记录。
  2. Trace Synchro Sw (1 bit)
    • 效果 :在 SWD 基础上增加同步跟踪引脚。
    • 特点:与上述功能类似,但底层基于SWD协议。

总结建议:

  • 常规开发 :选 Serial Wire 即可。
  • 需要低成本查看实时变量/打印日志 :选 Trace Asynchronous Sw
  • 需要分析程序崩溃前的精准运行路径 :需硬件支持并选择 Trace Synchro 相关模式。

为了提高处理效率,如果您需要针对某一特定模式(如Trace同步跟踪)的硬件接线或在IDE中的具体配置方法,可以告知我,我为您做进一步拆解。

在STM32 CubeMX配置中,虽然列表里提供了很多选项,但并不是"随便选哪一个都行"。选择Timebase Source(时基源)主要取决于你的项目是否使用了操作系统(如FreeRTOS)以及对硬件资源的规划。

以下是选择建议和核心逻辑:

一、 默认选择:SysTick

  • 适用场景:不使用操作系统(裸机开发)时。
  • 理由SysTick是Cortex-M内核自带的24位定时器,不占用MCU的通用外设定时器资源。它是HAL库默认的时基来源,用于处理HAL_Delay()和各种超时机制。
    在使用 LL库(Low-Layer) 时,情况与 HAL 库有所不同,虽然 SysTick 依然是最常见的选择,但其背后的逻辑和实现方式有显著区别:

1.1. LL库并不强制依赖 SysTick

  • HAL库 :高度依赖一个全局变量 uwTick(由时基源每 1ms 触发一次中断进行累加)。HAL 的各种超时机制(Timeout)和 HAL_Delay() 都必须依靠这个时基。
  • LL库 :设计初衷是轻量化和接近底层。LL 库本身没有 内置的 LL_Delay() 函数,也没有强制要求一个全局的 Tick 计数器。如果你不需要延时函数或超时检查,LL 库甚至可以完全不配置时基。

1.2. 如果你在 LL 库中需要"延时"或"时基"

如果你想在 LL 库项目中实现类似 Delay 的功能,通常有以下两种做法:

  • 方案 A:依然使用 SysTick(最推荐)
    • 即便使用 LL 库,SysTick 依然是实现 1ms 延时最节省资源的方式,因为它不占用任何外设定时器。
    • 注意 :你需要自己编写 SysTick_Handler 中断服务函数,并手动维护一个计数变量(例如 ms_counter++)。
  • 方案 B:使用软件循环(NOP)
    • 在极度追求精简的 LL 项目中,有人会通过特定的循环次数来实现粗略延时,但这不属于"时基源"范畴。

1.3. LL 库配合 RTOS 时的原则

这一点与 HAL 库完全一致

  • 如果你在 LL 库项目中引入了 FreeRTOS 或其他操作系统,SysTick 必须留给操作系统内核。
  • 此时,如果你还需要一个独立的硬件时基(例如用于精确的传感器采样触发),你必须选择一个硬件定时器(如 TIM6/TIM7)。

1.4. 关键区别总结

特性 HAL 库 LL 库
时基必要性 强制需要,否则 HAL 库无法正常运行 可选,仅在需要延时/超时功能时配置
CubeMX 生成代码 自动生成 HAL_IncTick() 等完整逻辑 仅生成初始化代码,逻辑(如计数)需手动编写
RTOS 冲突 必须切换 Timebase Source 到 TIMx 必须切换,且需手动处理 TIMx 中断逻辑

核心建议:

如果你正在从 HAL 库迁移到 LL 库,且项目不涉及 RTOS,继续选择 SysTick 是最简单、最高效的。

为了提高处理效率,如果您需要了解如何在 LL 库中手动实现基于 SysTick 的延时函数,或者需要针对特定外设的底层配置逻辑,我们可以针对代码实现细节进行结构化拆解讨论。

二、必须更改的情况:使用 RTOS(如 FreeRTOS)

  • 适用场景:当你启用FreeRTOS等实时操作系统时。
  • 理由 :FreeRTOS 强制要求占用 SysTick 作为其系统节拍(System Tick)。如果HAL库也使用 SysTick,两者会产生优先级冲突。
  • 后果 :如果在RTOS环境下不更改HAL库的时基源,可能会导致系统死锁或在调用HAL_Delay()时产生逻辑错误。
  • 官方建议 :此时必须在CubeMX中将 Timebase Source 改为一个硬件定时器(如 TIM1, TIM6 等)。

三、如何在众多 TIMx 中做出选择?

如果你决定使用硬件定时器作为时基,建议遵循以下优先级:

  • 首选:基本定时器(TIM6 / TIM7)
    • 这些定时器功能最单一(仅能计时,没有PWM、输入捕获等功能)。
    • 优点:不占用宝贵的引脚资源和高级功能,是作为系统时基的最佳"牺牲品"。
  • 次选:通用定时器(TIM2 / TIM3 / TIM4 / TIM5 等)
    • 如果芯片没有TIM6/7,或者它们已被占用,则选择这些。
    • 注意:尽量避开你计划用于PWM输出或编码器接口的定时器。
  • 最后选择:高级定时器(TIM1 / TIM8)
    • 这些定时器功能最强(支持死区控制、紧急刹车等,常用于电机控制)。
    • 建议:除非万不得已,否则不要浪费高级定时器来做简单的1ms时基计时。

总结建议

  1. 裸机开发 :选 SysTick
  2. RTOS开发 :选 TIM6TIM7(如果芯片有的话);如果没有,选一个不常用的通用定时器。

为了确保核心逻辑清晰,如果您后续需要针对特定型号(如F1、F4、H7系列)的定时器分配进行深入讨论,或者项目逻辑较为复杂,建议我们可以针对具体的定时器功能需求进行结构化拆解讨论。

RCC配置

在 STM32 CubeMX 中配置 RCC(复位和时钟控制)时,针对 HSE (High Speed External)LSE (Low Speed External) 的时钟源选择,决定了硬件引脚的连接方式以及内部振荡电路的工作模式。

以下是这三个选项的功能与效果详解:

1. Disable (禁用)

  • 功能:关闭外部时钟源引脚。
  • 效果
    • 引脚占用 :对应的时钟引脚(如 OSC_IN/OSC_OUTOSC32_IN/OSC32_OUT)被释放,可以作为普通 GPIO 使用。
    • 时钟来源 :系统将无法使用外部晶振,必须依赖芯片内部的 RC 振荡器(HSI 用于高速,LSI 用于低速/看门狗/RTC)。
    • 适用场景:对时钟精度要求不高、为了节省成本不焊接外部晶振、或需要节省引脚资源的简单应用。

2. Crystal/Ceramic Resonator (晶体/陶瓷谐振器)

  • 功能 :使用无源晶振(Passive Crystal)。
  • 效果
    • 电路连接:需要连接一个无源晶体,并根据晶体手册在两个引脚(IN 和 OUT)上分别跨接负载电容到地。
    • 内部逻辑:STM32 内部的皮尔斯振荡器(Pierce Oscillator)电路会被激活,提供增益以维持晶体振荡。
    • 性能:精度高、稳定性好(通常在 10-50 ppm 左右)。这是绝大多数嵌入式开发板的标准配置。
    • 适用场景:需要精准时钟(如 USB 通信、CAN 总线、以太网或高精度定时任务)的场景。

3. Bypass Clock Source (旁路时钟源)

  • 功能 :使用有源时钟(Active Clock)。
  • 效果
    • 电路连接:外部提供一个已经成波形的方波或正弦波信号(通常来自有源晶振、其他单片机的时钟输出或信号发生器)。
    • 引脚占用 :信号仅输入到 OSC_IN (或 OSC32_IN) 引脚。由于不需要内部振荡电路,OSC_OUT (或 OSC32_OUT) 引脚通常被释放,可作 GPIO 使用。
    • 内部逻辑:STM32 内部的振荡器驱动电路被关闭(旁路掉),直接接收外部逻辑电平。
    • 适用场景:板卡上已经存在一个高质量的时钟源(如 TCXO 温补晶振),或者多个芯片需要同步使用同一个时钟源。

核心对比总结

选项 硬件需求 精度 引脚占用 功耗控制
Disable 低(内部RC) 0 最省电,无需外部元件
Crystal 无源晶振 + 电容 2个引脚 需开启内部振荡电路
Bypass 有源晶振/外部信号 极高(取决于源) 1个引脚 内部电路关闭,较省电

温馨提示:

由于时钟树配置(Clock Tree)涉及到倍频、分频以及不同外设的频率限制,内容较为复杂。为了提高处理效率确保核心逻辑清晰,如果您需要进一步了解如何配置具体的 PLL(锁相环)参数,或者如何根据特定外设(如 USB 或 CAN)的要求来计算时钟频率,建议我们可以针对具体的时钟路径进行结构化拆解讨论。

时钟配置

在 STM32F407 的开发中,HSE 和 LSE 分别扮演着"性能引擎"和"精准计时"的角色。以下是它们各自的具体作用和应用场景:

1. HSE (High Speed External) ------ 高速外部时钟

配置值:8 MHz

  • 核心作用:系统主频的基石
    STM32F407 的最高主频为 168 MHz。8 MHz 的 HSE 通常不直接驱动内核,而是作为 PLL(锁相环) 的输入源。通过 PLL 的倍频,将 8 MHz 提升至 168 MHz,从而让 CPU 运行在最高性能。
  • 外设驱动
    • USB 通信:USB 需要非常精确的 48 MHz 时钟。内部 HSI(高速内部时钟)由于温漂较大,通常无法满足 USB 的协议要求,必须使用 HSE 经过 PLL 分频得到。
    • 以太网 (Ethernet):在进行网络通信时,PHY 芯片往往需要与 MCU 时钟同步,HSE 提供的稳定频率是工业级通信的基础。
    • CAN 总线:高比特率的 CAN 通信对时钟抖动非常敏感,HSE 能确保波特率的稳定,减少错误帧。
  • 为什么要用它(相比 HSI)
    内部 HSI 虽然起振快且省成本,但受温度影响大(精度约 1%),而 HSE 使用外部晶振,精度通常在 10-50 ppm,是高性能和高可靠性通信的必备选择。

2. LSE (Low Speed External) ------ 低速外部时钟

配置值:32.768 kHz

  • 核心作用:实时时钟 (RTC)
    32.768 kHz 是一个特殊的频率( 2 15 2^{15} 215 Hz)。RTC 内部通过一个 15 位的分频器,可以非常精确地产生 1秒 的中断。
  • 低功耗与备份域
    • Vbat 供电:当主电源(VCC)断开时,如果有纽扣电池接在 Vbat 引脚,LSE 依然可以持续运行。这意味着即使设备关机,RTC 里的时间(年、月、日、时、分、秒)依然在走。
    • 唤醒功能:在待机(Standby)或停止(Stop)模式下,LSE 可以驱动定时器定期唤醒 MCU。
  • 为什么要用它(相比 LSI)
    内部 LSI(约 32 kHz)精度极差(误差可达 20% 以上),只能用于看门狗防止死机。如果你需要手表级的计时精度(每天误差在几秒内),必须配置 LSE。

核心差异对比

特性 HSE (8 MHz) LSE (32.768 kHz)
主要用途 驱动 CPU 内核、USB、CAN、以太网 驱动 RTC (万年历)、低功耗唤醒
优先级 性能与速度优先 功耗与计时精度优先
停机表现 深度睡眠模式下通常关闭 可在 Vbat 模式下独立运行
稳定性 极高,受环境影响小 极高,专门针对计时优化

配置建议:

在 CubeMX 的 Clock Configuration 选项卡中,通常建议将 PLLSourceMux 选择为 HSE,然后通过 Main PLL 倍频至 168 MHz;同时将 RTC Clock Mux 选择为 LSE

温馨提示:

由于时钟树的配置涉及到多个分频器(M, N, P, Q)以及 APB1/APB2 总线频率的限制,为了提高处理效率确保核心逻辑清晰,如果您在配置具体的时钟树数值(如满足 USB 的 48MHz 或设定特定的串口波特率)时遇到困难,建议我们可以针对"时钟树参数计算"进行结构化拆解讨论。

LL库的选择

导出工程后时钟配置和GPIO配置代码:

c 复制代码
void SystemClock_Config(void)
{
    LL_FLASH_SetLatency(LL_FLASH_LATENCY_5);//CPU时钟周期与Flash访问时间:5个等待周期
    LL_FLASH_GetLatency();//读取等待周期(写完之后读一下操作)
    LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1);//内部主调压器的输出电压

    LL_RCC_HSE_Enable();//HSE时钟使能
    while(LL_RCC_HSE_IsReady() != 1)  //等待 HSE 就绪
    {
    }
    //PLL配置:外部高速时钟(8MHz晶振),8MHz /4 *168 /2 =168MHz
    LL_RCC_PLL_ConfigDomain_SYS(LL_RCC_PLLSOURCE_HSE, LL_RCC_PLLM_DIV_4, 168, LL_RCC_PLLP_DIV_2);
    LL_RCC_PLL_Enable();//PLL锁相环使能
    while(LL_RCC_PLL_IsReady() != 1)  //等待 till PLL 就绪
    {
    }

    LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1);//AHB总线时钟分配:1分频,168MHz
    LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_4);//APB1总线时钟分配:4分频,42MHz
    LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_2);//APB2总线时钟分配:2分频,84MHz
    LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL);//系统时钟源选择:PLL锁相环
    while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL)//等待系统时钟就绪
    {
    }

    LL_Init1msTick(168000000);//系统滴答定时器设置1ms延时周期
    LL_SYSTICK_SetClkSource(LL_SYSTICK_CLKSOURCE_HCLK);//系统嘀嗒定时器时钟源:AHB时钟168MHz
    LL_SetSystemCoreClock(168000000);//系统内核时钟168MHz
}

void LED_GPIO_Init(void)
{
    LL_GPIO_InitTypeDef GPIO_InitStruct = {0};

    /* 使能LED引脚对应的GPIO端口时钟 */
    LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOH);
    LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOE);
    LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOE);

    /* 配置LED1引脚默认输出电压 */
    LL_GPIO_ResetOutputPin(LED1_GPIO_Port, LED1_Pin);
    LL_GPIO_ResetOutputPin(LED2_GPIO_Port, LED2_Pin);
    LL_GPIO_ResetOutputPin(LED3_GPIO_Port, LED3_Pin);

    /*配置GPIO引脚 */
    GPIO_InitStruct.Pin = LED1_Pin;             // I/O编号
    GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT; // 输出模式
    GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL; //推挽输出
    GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;         // 不使用上拉/下拉输出
    GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;// I/O操作速度 低
    /* 根据上面的配置初始化LED的引脚 */
    LL_GPIO_Init(LED1_GPIO_Port, &GPIO_InitStruct);

    /*配置GPIO引脚 */
    GPIO_InitStruct.Pin = LED2_Pin;             // I/O编号
    GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT; // 输出模式
    GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL; //推挽输出
    GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;         // 不使用上拉/下拉输出
    GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;// I/O操作速度 低
    /* 根据上面的配置初始化LED的引脚 */
    LL_GPIO_Init(LED2_GPIO_Port, &GPIO_InitStruct);

    /*配置GPIO引脚 */
    GPIO_InitStruct.Pin = LED3_Pin;             // I/O编号
    GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT; // 输出模式
    GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL; //推挽输出
    GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;         // 不使用上拉/下拉输出
    GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;// I/O操作速度 低
    /* 根据上面的配置初始化LED的引脚 */
    LL_GPIO_Init(LED3_GPIO_Port, &GPIO_InitStruct);
}
相关推荐
三佛科技-1341638421213 分钟前
FT32F072xx、FT32F072xB、FT32F072x6/x8基于ARM Cortex-M0内核32位单片机分析
arm开发·单片机·嵌入式硬件·智能家居·pcb工艺
清风66666614 分钟前
基于单片机的火焰与温度联动检测及声光灭火控制系统
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
chen_mangoo28 分钟前
Rockchip debian预置安装deb包
linux·驱动开发·嵌入式硬件
梁山1号31 分钟前
【关于CAN】
c语言·stm32·单片机
尼喃33 分钟前
汽车摩托车LED车灯驱动芯片IC,精准调光抗干扰,为车灯性能提升赋能
单片机·汽车·51单片机·芯片
金线银线还是铜线?1 小时前
BLE设备 MF9006 PMIC 能量协同设计 低功耗物联网供电方案
嵌入式硬件·物联网·射频工程·iot
yuanmenghao1 小时前
CAN系列 — (3) Radar Object List 在 MCU 内部是如何被拼装、校验并最终被消费的?
单片机·嵌入式硬件·自动驾驶·信息与通信
yuanmenghao1 小时前
CAN系列 — (4) Radar Header 报文:为什么它是 MCU 感知周期的“锚点”
网络·单片机·自动驾驶·信息与通信
飞睿科技1 小时前
乐鑫ESP32-S3-BOX-3,面向AIoT与边缘智能的新一代开发套件
人工智能·嵌入式硬件·esp32·智能家居·乐鑫科技
Y1rong1 小时前
STM32之SPI
stm32·单片机·嵌入式硬件