一文吃透树莓派 Pico 定时器:硬件原理和工作模式
定时器的基本结构和概念
时钟是树莓派 Pico 所有硬件(包括定时器)运行的 “基础节拍”:不同外设需要不同频率的时钟来工作,Pico 的时钟系统分为两步工作:
- 获取时钟源:从多个稳定的 “基础时钟源”(如晶振、振荡器)获取原始时钟信号;
- 时钟分配:将原始时钟信号经过 “分频(降低频率)、使能(开关控制)” 后,分配给不同的外设(如定时器、UART、ADC)。
接下来,我们对通用定时器、PWM 定时器和 Systick 系统滴答定时器的基本结构和概念进行基本介绍。在树莓派 Pico 中除了常见的通用定时器、PWM 定时器、Systick 系统滴答定时器、RTC 实时时钟、WDT 看门狗定时器外,还有以下外设具有各自独立的定时器:
- PIO状态机: 有一个可生成中断的 32 位定时器,频率等于系统时钟频率(clk_sys,是给处理器、总线的核心时钟,通常为 125MHz);
- DMA: 有四个内部起搏计时器(pacing timers),独立计时,时钟与 DMA 外设的时钟(通常是 clk_peri)相互关联。
1.通用定时器
树莓派 Pico 的通用定时器具有以下特性:
- 64 位单独递增计数器:频率固定 1MHz,每 1us 增加一个计数;
- 4 个 alarms 闹钟:当 64 位计数器的低 32 位与 alarm 闹钟设定值相匹配时,会产生中断;
- 计数器可以从一对锁存寄存器中读取,所以多个处理器同时访问也不用考虑竞争问题。
通用定时器的时钟输入由看门狗定时器提供,频率为 1MHz:

图左侧的红色模块是 Pico 的 “原始时钟来源”,提供不同特性的基础时钟:
- Crystal Oscillator(XOSC) :外接晶振(通常是 12MHz),是最稳定的时钟源;
- Ring Oscillator(ROSC) :芯片内部的环形振荡器,启动快但精度较低;
- USB PLL/System PLL:锁相环电路,可将基础时钟倍频到更高频率(如 System PLL 可输出 125MHz,作为系统核心时钟);
- GPCLK0-1:从 GPIO 引脚复用的外部时钟输入;
- External clocks/Relaxation oscillators:其他外部时钟或弛张振荡器。
图右侧的模块是 “时钟分配单元”:它从左侧的 “Clock sources” 选择一个时钟源,经过 “分频器(图中的 ÷)” 调整频率、“使能开关(en)” 控制通断后,将时钟发送给对应的外设。其中与定时器相关的关键关联是:
- clk_ref 时钟:分配给 “Watchdog & Timers(看门狗与定时器)”;
- 通用定时器的时钟:由 clk_ref 经过看门狗处理后得到的,频率为 1MHz。
1.1 计数器单元
计时器有一个 64 位计数器,但 RP2040 只有一个 32 位数据总线。这意味着读写 TIME 计数值各自需要通过一对寄存器进行(计时器寄存器的基址为 TIMER_BASE 0x40054000):
- TIMEHW 寄存器(写高 32 位数据)和 TIMELW 寄存器(写低 32 位数据)来写时间:

- TIMEHR 寄存器(读高 32 位数据)和 TIMELR 寄存器(读低 32 位数据)来读取时间:

需要注意的是,虽然在理论上可以通过写入 TIMEHW 和 TIMELW 寄存器来强制执行一个新的时间值,但不鼓励这样做。这是因为计时器值预计将由 Pico 的 SDK 单调增加。
1.2 alarm 闹钟单元
定时器有 4 个 alarm 闹钟单元,每个 alarm 闹钟单元都可以输出一个单独的中断。alarm 闹钟单元与 64 位计数器的低 32 位相匹配,则 alarm 闹钟单元计时的上限为 2^32 us = 4295 s = 72 min。
若需要启用 alarm 闹钟单元,则需要进行对以下寄存器进行配置:

- ALARMx(x = 0~3)寄存器:用于设定闹钟时间,到达则产生开火事件,产生警报

- ARMED 寄存器:指示每个 alarm 闹钟是否产生警报

- INTE 寄存器:使能或失能 alarm 闹钟中断

- INTR 寄存器、INTF 寄存器、INTS 寄存器:查看 alarm 中断状态或强制产生中断



2. PWM 定时器
2.1 PWM 定时器的基本结构和特性
PWM 定时器包含 8 个”近乎独立“的 16 位可编程计数器,它们共享相同的 125MHz 输入时钟(系统时钟),但时钟分频器是彼此独立的。

树莓派 Pico 的 PWM 定时器具有以下特性:
- 每个计数器通道都有两个输出(总共 16 个 PWM 通道),可以连接到所有 GPIO 引脚中的任何一个
- 分频器具有 8 整数 4 小数分频,允许 16 位 PWM 进行调节的信号输出频率为 125MHz 至 7Hz
- 每个 PWM 输出通道允许调节的占空比范围是 0~100
- 可以产生中断请求或触发 DMA
- 每个计数器的两个通道中有一个通道可以用作输入,作为 PWM 输出的启动信号,该输入接受上升沿触发、下降沿触发或高电平触发,该功能可以用作测量外部信号频率和占空比
- 定时器递增时相位可调节
- 各个通道之间可以同步启动并允许,从而进行功率控制
每个 GPIO 与 PWM 通道的对应关系如下:

可以在两个 GPIO 引脚上选择相同的 PWM 输出;相同的信号将出现在每个 GPIO 上。
2.2 PWM 通道的启动信号
这里,PWM 通道中的 B 引脚可以用作 PWM 的启动信号,需要注意的是,当同时使用多个 PWM 通道的 B 引脚时,多个 B 引脚上的信号经过 OR 运算后,才作为 PWM 的启动信号。
我们可以通过配置 CSR 寄存器中的 DIVMODE 位进行选择是上升沿触发、下降沿触发或高电平触发:

2.3 PWM 定时器的工作原理和计数模式
简单来说,PWM 定时器的工作原理为 PWM 中有个定时器,随着时钟周期进行递增,同时每递增一次,就会对比当前计数器的计数值和预设的计数值:

- 当前计数器的计数值 < 预设的计数值:PWM 通道对应的 GPIO 引脚输出高电平
- 当前计数器的计数值 > 预设的计数值:PWM 通道对应的 GPIO 引脚输出低电平
上图所示,为 PWM 定时器的向上计数模式(到达设定值时置低),PWM 定时器也支持中心对齐计数模式:

两个计数模式可通过 CSR 寄存器的 PH_CORRECT 位进行切换:

2.4 PWM 参数的修改
这里,PWM 定时器的计数周期(计数的最大值)由 TOP 寄存器控制,最大周期为 65536 个时钟周期,计数器比较值由 CC 寄存器进行控制,时钟分频由 DIV 寄存器进行控制



PWM 输出周期和频率可由以下公式进行计算:


PWM 定时器的输出占空比可在 0~100% 之间变化,CC 值为 0 将产生 0% 的输出,TOP + 1 的 CC 值(将产生 100% 的输出:

2.5 PWM 定时器的双寄存器机制
这里,需要注意的是 PWM 定时器的 TOP 寄存器和 CC 寄存器具有双寄存器机制。 简单来说,就是 TOP 寄存器和 CC 寄存器实际上包括以下几个部分:预装载 TOP 寄存器、影子 TOP 寄存器、预装载 CC 寄存器、影子 CC 寄存器,影子寄存器才是真正起作用的寄存器,预装载寄存器为影子寄存器提供缓冲,提前做数据或指令准备; 因为定时器工作往往具有一定周期性,如果每次我们的参数修改都直接作用于实际寄存器,往往不可避免会影响到当前周期的正常计数以及相关的输出动作。
考虑一下这种情况:当我们需要切换 PWM 的占空比时,我们需要修改定时器 CC 寄存器中的数值,我们理想中的情况应该是下图这样,当我们修改定时器 CC 寄存器中的数值时很有可能当前 PWM 定时器正处于一个计数的周期内,我们希望本周期内输出的 PWM 波占空比按照上一次预设的数值进行输出,而当计数器开始新的计数周期时,输出的 PWM 波才会按照本次新设定的计数阈值输出对应占空比的 PWM 波:

若是不采用双寄存器机制,用户通过软件预设的计数值马上就会更新到定时器的 CC 寄存器中,会导致 PWM 波形输出产生异常:

这里,需要注意的是,用户使用软件操作的永远只是预装载寄存器,当我们修改预装载寄存器中的数值后,影子寄存器并不会马上拷贝预装载寄存器中的数值,而是等到下一个更新事件发生时才将预装载寄存器中的数值传输到影子寄存器中:

- 定时器使用向上计数模式时:计数器到达原先预设的计数阈值,计数值清零产生更新事件
- 定时器使用中央对齐模式时:计数器停止向下计数并再次开始向上计数时产生更新事件
3. Systick 系统滴答定时器
3.1 Systick 定时器简介
SysTick 定时器(又名系统滴答定时器)是存在于 Cortex-M0 内核中的一个定时器,只要是 ARM Cotex-M 系列内核的 MCU 都包含这个定时器。使用内核的 SysTick 定时器来实现延时或 RTOS 的心跳时钟,可以不占用系统定时器,节约资源。 由于 SysTick 是在 CPU 核内部实现的,跟 MCU 外设无关,因此它的代码可以在不同厂家之间移植。
树莓派 Pico 中的 Systick 定时器具有如下特性:
- 为 24 位向下计数的定时器
- 附加的可配置优先级 SysTick 中断
- 频率为 1MHz,1us 计数一次
3.2 控制 Systick 定时器
Systick 定时器主要由以下四个寄存器进行控制(Cortex-M0 内核基地址为 PPB_BASE 0xe0000000):

Systick 定时器的工作原理简单来说就是不断向下计数,当前计数值为 0 时,从自动重装载寄存器中自动程序装载定时初值,然后再进行下一次计数。

对于 SYST_CSR 寄存器而言,具有以下四个位:
- COUNTFLAG 位:主要的作用就是防止误读以及多读,如果在上次读取该位后,SysTick 已经数到了 0,本次读取的值为 1;反之,若上次未读取该位,那么读取该位后将自动清零
- CLKSOURCE 位:主要用来选择时钟源,可以选择外部时钟或者系统时钟
- TICKINT 位:主要功能就是选择是否在倒计时结束后产生中断
- ENABLE 位:失能或使能 Systick 定时器
使用 SysTick SYST_RVR 寄存器来指定要在计数器达到 0 时加载到当前值寄存器中的起始值。它可以是介于 0 到 0x00FFFFFF 之间的任何值,当从 1 计算到 0 时,SysTick 异常请求和计数标志会被激活。:

SYST_CVR 可以用于获取当前计数值,用任何值写入它都会清除寄存器为 0。清除此寄存器还会清除 SysTick 控制和状态寄存器的 COUNTFLAG 位:

使用 SysTick 校准值寄存器进行校准或获取校准状态:


时钟是树莓派 Pico 所有硬件(包括定时器)运行的 “基础节拍”:不同外设需要不同频率的时钟来工作,Pico 的时钟系统分为两步工作:
1. **获取时钟源**:从多个稳定的 “基础时钟源”(如晶振、振荡器)获取原始时钟信号;
2. **时钟分配**:将原始时钟信号经过 “分频(降低频率)、使能(开关控制)” 后,分配给不同的外设(如定时器、UART、ADC)。
浙公网安备 33010602011771号