深入解析MC68HC908GZ TIM1定时器:从原理到PWM与输入捕获实战
1. 项目概述与TIM1模块定位
在嵌入式开发领域,尤其是面对像MC68HC908GZ这类经典的8位微控制器时,定时器模块往往是项目成败的关键。它不像CPU那样引人注目,却像心脏的节拍器,默默地为整个系统提供精确的时间基准,驱动着从简单的延时闪烁到复杂的电机控制等一系列核心功能。我接触过不少项目,从消费电子到工业控制,很多看似玄妙的时序问题、波形失真,追根溯源,往往是对定时器底层机制理解不透彻导致的。今天,我们就来彻底拆解MC68HC908GZ系列中的TIM1模块,这不仅仅是一次寄存器功能的罗列,更是一次从芯片设计者视角出发,理解如何将“时间”这个抽象概念,通过硬件逻辑精准掌控的旅程。
TIM1是一个16位的定时器接口模块,它远不止一个简单的计数器。它集成了输入捕获、输出比较和脉宽调制三大功能于一身。输入捕获能像高速相机快门一样,精准定格外部事件发生的瞬间时刻;输出比较则像一个守时的闹钟,在预设的时间点触发你设定的动作;而PWM功能,则是实现模拟量控制(比如调节电机转速、LED亮度)的数字化利器。理解TIM1,就等于掌握了让单片机与物理世界进行精确时序交互的钥匙。无论是测量传感器脉冲的宽度,还是生成驱动步进电机的精准脉冲序列,亦或是产生可调占空比的方波,都离不开对它的娴熟运用。接下来,我将结合数据手册和实际调试经验,带你从内部结构到寄存器配置,从基础功能到高级应用,完整地走一遍。
2. TIM1模块核心架构与工作原理拆解
要驾驭TIM1,首先得看清它的“五脏六腑”。数据手册中的框图是它的解剖图,但我们得把它翻译成工程师能直观理解的运作模型。
2.1 时钟血脉:预分频器与计数器
TIM1的心脏是一个16位向上计数器,它的每一次“心跳”都来源于一个可编程的时钟源。这个时钟源并非直接来自系统主频,而是经过一个7选1的预分频器。预分频器的作用至关重要,它通过对内部总线时钟进行1、2、4、8、16、32、64分频,为计数器提供合适频率的计数脉冲。选择哪个分频比,取决于你对定时精度和定时范围的需求。比如,你的系统总线时钟是8MHz,如果你需要每1微秒计数一次,那就选择1分频;如果你需要测量一个长达几十毫秒的脉冲,则可以选择64分频来扩展计数范围,避免计数器过快溢出。
这个16位计数器有两种工作模式:自由运行模式和模数计数模式。在自由运行模式下,计数器从0x0000开始,一直累加到0xFFFF,然后溢出归零,重新开始,周而复始,像一个不知疲倦的跑圈者。而在模数计数模式下,计数器从0x0000开始,累加到你通过T1MODH:T1MODL寄存器设定的模数值后,就产生溢出并归零。这就像设定了一个固定长度的跑道,跑完一圈就重新开始。模数模式对于生成固定周期的信号(如PWM)特别有用,因为溢出周期是严格确定的。
2.2 功能器官:输入捕获与输出比较通道
TIM1拥有两个独立的通道(Channel 0和Channel 1),每个通道都可以被灵活配置为输入捕获或输出比较模式,这是其功能强大的基础。
输入捕获的机制可以想象成一个带时间戳的触发器。当通道被配置为输入捕获模式,并且其对应的引脚(PTD4/T1CH0或PTD5/T1CH1)上出现你所设定的有效边沿(上升沿、下降沿或任意边沿)时,硬件会立即将当前16位计数器的值“抓拍”下来,锁存到对应的通道寄存器(T1CH0H:L或T1CH1H:L)中,并置位通道标志位(CHxF),同时可以产生中断。这个被锁存的值,就是事件发生的精确时刻。通过连续捕获同一个脉冲的上升沿和下降沿的时刻值,两者相减就能得到脉冲宽度;通过捕获两个相邻上升沿的时刻,就能计算出信号频率。这里的关键在于“立即”和“硬件”,它不依赖CPU的轮询,因此精度可以做到很高,仅受限于计数器的时钟分辨率。
输出比较的机制则像一个闹钟。你事先在通道寄存器里设定好一个目标时间值。计数器在自由奔跑,硬件会不断地将计数器的当前值与通道寄存器里的目标值进行比较。当两者相等时,就表示“闹钟响了”。此时,硬件会根据你的配置,自动对对应的引脚执行三种操作之一:置为高电平、置为低电平、或者翻转电平。同时,也会置位通道标志位并可能产生中断。这个功能可以用来在精确的延时后触发一个动作,或者生成一个周期性的脉冲信号。
2.3 高级合成:脉宽调制模式
PWM是输出比较功能的一个经典且强大的应用。TIM1通过结合模数计数模式和输出比较功能来生成PWM。其原理是:模数寄存器(T1MODH:T1MODL)决定了PWM波的周期(即溢出周期),而通道寄存器(T1CHxH:L)则决定了输出比较事件发生的时间点,这个时间点与周期起点的差值,就是高电平或低电平的持续时间,从而决定了占空比。
具体来说,你需要开启计数器的溢出翻转功能(设置TOVx位)。这样,每当计数器溢出时,引脚电平会自动翻转一次,这定义了PWM波的一个边沿。同时,你将通道配置为输出比较模式,并设置为在比较匹配时,将引脚电平驱动到与溢出翻转相反的状态(例如,溢出时翻转为高,则比较匹配时设置为低)。于是,一个完整的PWM周期就产生了:从溢出点开始,电平翻转;运行到比较匹配点,电平再次变化;直到下一个溢出点,周期重复。通过修改模数寄存器的值可以改变频率,修改通道寄存器的值则可以改变占空比。
这里有一个非常重要的细节:为了避免在修改PWM参数时产生毛刺或错误的脉冲,TIM1提供了缓冲模式。在普通(无缓冲)模式下,你直接改写正在控制当前输出的通道寄存器,如果写入时机不当,可能会破坏当前周期的波形。而在缓冲模式下,你可以将两个通道(通常是Channel 0和Channel 1)链接起来,形成一个“双缓冲”结构。你可以在后台(非活动通道)写入新的比较值,当前台(活动通道)的周期结束后,硬件会自动在下一个周期开始时切换为使用后台的新值,从而实现了PWM参数的无缝、无毛刺更新,这对于电机控制等要求波形连续平滑的应用至关重要。
3. 寄存器详解与配置实战
理解了架构,我们就要动手配置了。所有对TIM1的控制,都通过一组内存映射的寄存器完成。数据手册给出了地址和位定义,但如何组合使用它们,才是实践中的关键。
3.1 核心控制寄存器:T1SC
地址$0020的TIM1状态与控制寄存器是TIM1的总开关和调速器。
- TOF与TOIE:溢出标志与中断使能。当计数器达到模数值(模数模式)或0xFFFF(自由运行)时,TOF被硬件置1。如果TOIE也为1,则会产生定时器溢出中断。清除TOF需要先读T1SC(此时TOF=1),再向TOF位写0。这是一个标准的“读-修改-写”清除序列,防止中断丢失。
- TSTOP与TRST:停止与复位位。TSTOP=1时,计数器暂停,这在调试和精确控制计数起点时非常有用。特别注意:如果你希望TIM1的中断能将MCU从WAIT低功耗模式唤醒,则进入WAIT前不能设置TSTOP。TRST是只写位,写1会立即将计数器和预分频器清零,然后该位自动清零。它可以用来同步计数器起点。
- PS[2:0]:预分频选择位。这三位决定了计数器的时钟频率,是定时精度的第一道闸门。其选择关系必须熟记(见下表),尤其是在计算定时时间和PWM频率时。
| PS2 | PS1 | PS0 | TIM1时钟源 (假设总线时钟=CLK) |
|---|---|---|---|
| 0 | 0 | 0 | CLK / 1 |
| 0 | 0 | 1 | CLK / 2 |
| 0 | 1 | 0 | CLK / 4 |
| 0 | 1 | 1 | CLK / 8 |
| 1 | 0 | 0 | CLK / 16 |
| 1 | 0 | 1 | CLK / 32 |
| 1 | 1 | 0 | CLK / 64 |
| 1 | 1 | 1 | 保留 |
实操心得:在系统初始化阶段,我习惯先
TSTOP=1, TRST=1将定时器彻底停止并清零,然后配置所有其他参数(模数值、比较值、通道模式等),最后再TSTOP=0启动定时器。这能确保定时器从一个完全确定的状态开始工作,避免因寄存器写入顺序导致的不可预测行为。
3.2 通道控制寄存器:T1SC0与T1SC1
地址$0025和$0028的通道状态控制寄存器,决定了每个通道的具体行为模式。两个寄存器结构类似,但Channel 0的功能更强大(多一个MS0B位用于缓冲模式)。
- CHxF与CHxIE:通道标志与中断使能。功能与TOF/TOIE类似,但针对具体通道的输入捕获或输出比较事件。
- MSxB与MSxA:模式选择位。这是配置功能的核心。它们与ELSxB:A位共同决定通道模式,其组合关系必须严格参照数据手册的表格(见下表摘要)。例如,要设置Channel 0为上升沿输入捕获,则需要配置
MS0A=0, ELS0B:A=01。要设置其为无缓冲、比较匹配时清零输出的模式,则需要MS0A=1, ELS0B:A=10。 - ELSxB与ELSxA:边沿/电平选择位。在输入捕获模式下,选择触发边沿;在输出比较/PWM模式下,选择匹配时的输出动作(翻转、置高、置低)。
- TOVx:溢出翻转位。这是实现PWM的关键位。当
TOVx=1时,每次定时器溢出,对应通道的引脚电平会自动翻转。特别注意:在PWM模式下,输出比较动作应设置为“置位”或“清零”,绝不能设置为“翻转”。否则将无法生成稳定的PWM,也无法实现0%或100%的占空比。 - CHxMAX:最大占空比位。当
CHxMAX=1且TOVx=1时,该通道输出恒为高电平(100%占空比)。当CHxMAX=0且TOVx=0时,输出恒为低电平(0%占空比)。这提供了一种快速关闭PWM输出的方法。
3.3 数据寄存器:计数器、模数与通道寄存器
- T1CNTH:L:计数器值寄存器。它们是只读的,反映了计数器的实时值。有一个重要的硬件特性:读取高字节
T1CNTH时,低字节T1CNTL的当前值会被锁存到一个缓冲器中。后续读取T1CNTH得到的都是之前锁存的低字节值,直到你真正去读取一次T1CNTL,这个锁存器才会更新。因此,为了读取完整的、同步的16位计数器值,必须先读T1CNTH,再读T1CNTL。如果在断点中断中读了T1CNTH,退出前务必读一次T1CNTL来解锁存器,否则后续读取的计数器值将一直是中断发生时的旧值。 - T1MODH:L:计数器模数寄存器。它们决定了模数计数模式下的溢出值。写入时有顺序要求:必须先写高字节
T1MODH,再写低字节T1MODL。在写入低字节之前,溢出中断会被禁止。这保证了模数值更新的原子性。 - T1CHxH:L:通道寄存器。在输入捕获模式下,它们是只读的,存放捕获到的时刻值。在输出比较或PWM模式下,它们是可读写的,存放要比较的目标值。
4. 关键功能实现与代码示例
理论说得再多,不如一行代码。下面我将以几个典型场景为例,展示如何配置TIM1。假设系统总线时钟为8MHz。
4.1 实现1ms定时中断
这是最基础的需求,常用于系统心跳时钟。
// 目标:使用TIM1溢出中断,每1ms产生一次中断。 // 假设总线时钟 = 8MHz, 选择预分频为 64, 则TIM1时钟 = 8MHz / 64 = 125kHz // 计数器周期 = 1 / 125kHz = 8us // 要达到1ms溢出,需要计数值 N = 1ms / 8us = 125 // 模数值 = N - 1 = 124 (因为从0开始计数) // 使用模数计数模式。 void TIM1_Init_1ms(void) { T1SC = 0x00; // 先停止定时器,清除所有标志,预分频选择位为000(/1),但我们会马上修改 T1SC_TRST = 1; // 复位计数器和预分频器 // 配置预分频为64,即PS[2:0]=110。同时保持TSTOP=1,TOIE=0(稍后开启) T1SC = 0x46; // 二进制 0100 0110,即TOF=0, TOIE=0, TSTOP=1, PS=110 // 设置模数值为124 (0x007C) T1MODH = 0x00; T1MODL = 0x7C; // 清除任何可能挂起的中断标志 T1SC_TOF = 0; // 读T1SC(上面已读)后写0清除TOF // 启用溢出中断,并启动定时器 T1SC_TOIE = 1; // 开启溢出中断 T1SC_TSTOP = 0; // 启动计数器! } // 在中断服务例程中 interrupt void TIM1_OVF_ISR(void) { T1SC_TOF = 0; // 清除溢出标志(读后写0) // 你的1ms任务在这里执行... // 例如:更新系统时钟,扫描按键等。 }4.2 测量脉冲宽度(输入捕获)
测量PTD4引脚上正脉冲的宽度。
// 思路:配置Channel 0为输入捕获模式,上升沿触发。 // 在第一个上升沿中断中,记录捕获值并改为下降沿触发。 // 在下降沿中断中,再次记录捕获值,两次值之差乘以计数周期即为脉冲宽度。 // 注意处理计数器溢出。 unsigned int capture_rise_time = 0; unsigned int pulse_width_ticks = 0; void TIM1_Init_InputCapture(void) { // 首先,停止并复位定时器 T1SC = 0x44; // TSTOP=1, TRST=1, 预分频根据需求设置,例如/1 (PS=000) // 使用自由运行模式,模数寄存器保持默认值0xFFFF即可 // 配置Channel 0为输入捕获,初始为上升沿触发 // MS0A=0, ELS0B:A=01 (上升沿) T1SC0 = 0x10; // CH0F=0, CH0IE=1(使能中断), MS0B=0, MS0A=0, ELS0B=0, ELS0A=1, TOV0=0, CH0MAX=0 // 启动定时器,自由运行 T1SC_TSTOP = 0; T1SC_TRST = 0; } interrupt void TIM1_CH0_ISR(void) { static char edge_state = 0; // 0:等待上升沿,1:等待下降沿 unsigned int capture_val; // 读取捕获值,注意顺序:先高后低 capture_val = T1CH0H; capture_val = (capture_val << 8) | T1CH0L; if (edge_state == 0) { // 捕获到上升沿 capture_rise_time = capture_val; // 切换为下降沿触发 T1SC0_ELS0A = 0; // ELS0B:A 从 01 变为 10 (下降沿) T1SC0_ELS0B = 1; edge_state = 1; } else { // 捕获到下降沿 // 计算脉冲宽度计数值,考虑溢出情况 if (capture_val >= capture_rise_time) { pulse_width_ticks = capture_val - capture_rise_time; } else { // 发生了溢出, 0xFFFF - rise + capture + 1 pulse_width_ticks = (0xFFFF - capture_rise_time) + capture_val + 1; } // 切换回上升沿触发,准备下一次测量 T1SC0_ELS0A = 1; // ELS0B:A 从 10 变为 01 (上升沿) T1SC0_ELS0B = 0; edge_state = 0; // 这里可以处理pulse_width_ticks,转换为时间单位 } // 清除通道中断标志 (读后写0) T1SC0_CH0F = 0; }4.3 生成1kHz,占空比50%的PWM(无缓冲)
在PTD4引脚上生成PWM。
// 目标:PWM频率1kHz,占空比50%。 // 总线时钟8MHz,预分频设为/1,则计数器时钟为8MHz,周期0.125us。 // PWM周期 = 1 / 1kHz = 1000us。 // 需要的计数器溢出值 = 1000us / 0.125us = 8000。 // 占空比50%,则比较匹配值 = 8000 * 50% = 4000。 // 使用模数计数模式,溢出时翻转引脚(TOV0=1),比较匹配时清零引脚(ELS0B:A=10)。 void TIM1_Init_PWM_1kHz_50(void) { // 停止并复位定时器 T1SC = 0x44; // TSTOP=1, TRST=1, PS=000 (/1) // 设置PWM周期 (模数值 = N-1 = 7999) T1MODH = 0x1F; // 7999 = 0x1F3F T1MODL = 0x3F; // 设置PWM占空比 (比较值 = 4000) T1CH0H = 0x0F; // 4000 = 0x0FA0 T1CH0L = 0xA0; // 配置Channel 0为无缓冲PWM模式 // MS0A=1 (输出比较/PWM), ELS0B:A=10 (比较匹配时清零输出), TOV0=1 (溢出时翻转) // 假设初始输出极性为高,则溢出变低,比较匹配变高,形成高电平在前的脉冲。 // 如果需要低电平在前,则设置ELS0B:A=11 (比较匹配时置位),并在初始化时确保引脚初始为低。 T1SC0 = 0x68; // CH0IE=0(禁用中断,使用查询或无需中断), MS0B=0, MS0A=1, ELS0B=1, ELS0A=0, TOV0=1, CH0MAX=0 // 启动定时器 T1SC_TSTOP = 0; }4.4 实现缓冲PWM输出(双缓冲更新)
这是高级应用,用于需要平滑改变PWM占空比而无毛刺的场合,如电机调速。
// 使用Channel 0和Channel 1链接实现缓冲PWM,输出在PTD4(T1CH0)引脚。 // Channel 1引脚(PTD5)可用作普通IO。 // 核心是设置MS0B=1,启用缓冲模式。 void TIM1_Init_Buffered_PWM(void) { // 1. 停止并复位定时器 T1SC = 0x44; // TSTOP=1, TRST=1 // 2. 设置PWM周期 (例如,0x03FF = 1023个计数周期) T1MODH = 0x03; T1MODL = 0xFF; // 3. 初始化两个通道的比较值(占空比) // 假设初始占空比50%,比较值=512 (0x0200) T1CH0H = 0x02; // 通道0寄存器,初始控制输出 T1CH0L = 0x00; T1CH1H = 0x02; // 通道1寄存器,缓冲值,先设为相同 T1CH1L = 0x00; // 4. 配置Channel 0为缓冲PWM模式 // MS0B=1 (启用缓冲), MS0A=X(无关), ELS0B:A=10 (比较匹配时清零), TOV0=1 (溢出翻转) // 此配置下,T1SC1寄存器被忽略,Channel 1硬件被用于缓冲。 T1SC0 = 0x58; // 二进制 0101 1000: CH0IE=0, MS0B=1, MS0A=0, ELS0B=1, ELS0A=0, TOV0=1 // 5. 启动定时器 T1SC_TSTOP = 0; } // 函数:更新PWM占空比(双缓冲安全更新) void Update_PWM_DutyCycle(unsigned int new_duty_ticks) { // 关键:必须写入当前非活动的通道寄存器。 // 需要软件跟踪当前哪个通道是活动的。一个简单的方法是检查CH0F标志的触发顺序, // 但更稳健的方法是利用缓冲切换发生在溢出时刻的特性。 // 这里采用一种常见策略:在定时器溢出中断中更新下一个周期的值。 // 例如,在溢出中断服务例程中: // static unsigned char active_channel = 0; // 0: CH0活跃, 1: CH1活跃 // if (active_channel == 0) { // // 当前CH0控制输出,将新值写入CH1寄存器 // T1CH1H = (unsigned char)(new_duty_ticks >> 8); // T1CH1L = (unsigned char)(new_duty_ticks); // active_channel = 1; // } else { // // 当前CH1控制输出,将新值写入CH0寄存器 // T1CH0H = (unsigned char)(new_duty_ticks >> 8); // T1CH0L = (unsigned char)(new_duty_ticks); // active_channel = 0; // } // 注意:新值必须在下一个溢出发生前写入,否则会使用旧值再运行一个周期。 }5. 低功耗模式下的操作与避坑指南
MC68HC908GZ支持WAIT和STOP两种低功耗模式。TIM1在这两种模式下的行为,直接影响着系统的功耗和唤醒能力。
WAIT模式:执行WAIT指令后,CPU时钟停止,但外设时钟(包括TIM1的时钟源)通常继续运行(取决于具体芯片的配置)。这意味着TIM1的计数器仍在工作。重要提示:如果TIM1被启用(TBON=1 for TBM,或TIM1计数器运行),并且中断使能,那么TIM1产生的中断可以将MCU从WAIT模式唤醒。但是,在WAIT模式下,CPU不能访问寄存器。因此,如果你不需要TIM1在WAIT模式下工作,为了节省功耗,务必在进入WAIT前停止TIM1(设置TSTOP位)。如果需要TIM1做周期性唤醒,则确保其配置正确且中断使能。
STOP模式:执行STOP指令后,主振荡器可能停止,系统进入最低功耗状态。TIM1是否工作取决于振荡器在STOP模式下是否被允许运行(由配置寄存器中的OSCENINSTOP位控制)。如果振荡器停止,TIM1自然停止。如果振荡器在STOP模式下保持运行,则TIM1可以继续工作,并用于产生周期性唤醒中断。同样,在STOP模式下无法访问寄存器。关键避坑点:数据手册特别强调,如果不需要TIM1在STOP模式下运行,务必在进入STOP前禁用TIM1模块(对于TIM1,停止计数器可能不够,可能需要关闭模块时钟,具体需查电源管理相关寄存器),以最大化省电效果。
关于Break中断:当使用片上调试模块触发Break中断时,TIM1计数器会停止,输入捕获功能也被禁止。这有利于在断点处检查系统状态。但需要注意,在Break状态下,对状态标志位(如TOF, CHxF)的清除操作可能受系统集成模块(SIM)中BCFE位的控制。默认情况下(BCFE=0),在Break状态写寄存器不会真正清除状态位,这是一种保护机制。如果你希望在Break调试期间也能正常清除标志位,需要先设置BCFE=1。
6. 常见问题排查与实战经验
在实际项目中,调试TIM1相关功能时,以下几个问题是高频雷区:
1. 定时不准或中断不触发
- 检查时钟源:确认TIM1的预分频器(PS[2:0])设置是否正确。算一下计数周期是否和你预期的一致。一个常见的错误是忽略了预分频器。
- 检查模数值:在模数计数模式下,溢出发生在计数器值等于模数值时,而不是从0计数到模数值。所以,如果你想要每N个时钟中断一次,模数寄存器应设置为N-1。
- 检查中断使能和全局中断:确认TOIE或CHxIE已置1,并且CPU的全局中断允许位(I位)已清除(允许中断)。
- 检查标志位清除方式:TOF和CHxF标志必须用“读寄存器(标志位为1时)-然后写0”的序列清除。直接写0是无效的。在中断服务程序开头,必须严格按此操作。
2. PWM输出没有波形或占空比不对
- 确认引脚功能:PTD4/T1CH0和PTD5/T1CH1是复用引脚。必须将对应的端口数据方向寄存器(DDRD)的相应位设置为输出,引脚才能输出波形。
- 检查TOVx和ELSxB:A配置:这是最容易出错的地方。要生成PWM,必须设置
TOVx=1(溢出翻转)。同时,输出比较动作必须设置为“强制输出为固定电平”(ELSxB:A=10清零 或=11置位),绝不能设置为“翻转”(ELSxB:A=01)。比较动作的电平应与溢出翻转的电平相反,才能形成一个脉冲。 - 检查模数值和比较值:用示波器测量周期和脉宽,反推计算一下,看是否与寄存器设置值相符。确保写入的是16位值,注意高低字节顺序。
- 无缓冲模式下的更新毛刺:在PWM运行中直接修改通道寄存器(比较值)会导致当前周期波形错乱。务必遵循数据手册的建议:若要更新为更小的值(缩短脉宽),在输出比较中断中更新;若要更新为更大的值(加长脉宽),在定时器溢出中断中更新。或者,直接使用缓冲模式。
3. 输入捕获值跳动或不准
- 消抖处理:如果捕获的是机械开关等信号,必须在硬件或软件上做消抖处理,否则会捕获到多次边沿。
- 中断响应延迟:输入捕获是硬件行为,精度高。但如果在捕获中断中执行太耗时的任务,可能影响下一次捕获的及时处理,甚至丢失中断。确保中断服务程序尽量简短。
- 计数器溢出处理:在测量长脉冲时,计数器可能溢出。你的捕获差值计算代码必须考虑溢出情况,如前面示例所示,进行进位判断。
- 边沿选择错误:确认ELSxB:A设置的是你想要的边沿(上升、下降、任意)。
4. 缓冲模式工作异常
- 通道链接:缓冲模式仅适用于Channel 0和Channel 1链接,且输出固定在T1CH0引脚。确保设置了
MS0B=1。 - 活动通道跟踪:在缓冲模式下,软件必须跟踪当前是哪个通道的寄存器在控制输出,并将新的比较值写入非活动的通道寄存器。如果写错了,就变成了无缓冲更新,可能产生毛刺。一个可靠的策略是在定时器溢出中断中,交替向两个通道寄存器写入新值。
- T1SC1寄存器失效:一旦设置
MS0B=1,T1SC1寄存器的配置就无效了,Channel 1的状态和控制完全由T1SC0决定。PTD5/T1CH1引脚可以作为普通IO使用。
5. 低功耗模式下定时器失效
- 检查WAIT/STOP配置:确认在进入低功耗模式前,没有错误地停止了定时器(TSTOP=1)。如果希望定时器唤醒MCU,则必须保持其运行且中断使能。
- 检查时钟源:在STOP模式下,如果主振荡器停了,且没有其他时钟源(如内部低功耗振荡器)供给TIM1,那么TIM1当然不工作。查阅芯片手册的电源管理章节,确认在目标低功耗模式下,TIM1的时钟是否依然存在。
终极调试建议:当你怀疑TIM1相关问题时,第一件事应该是用示波器或逻辑分析仪查看对应的引脚波形。观察是否有信号、频率对不对、占空比如何、边沿是否干净。这能最直观地告诉你硬件是否在按预期工作。同时,结合仿真器或调试器,单步执行代码,观察关键寄存器(T1CNT, T1SC, T1SC0等)的值是否在按预期变化。寄存器配置阶段,养成“先停止(TSTOP),再配置,最后启动”的好习惯,能避免很多时序竞态问题。
