P89LPC9321 CCU模块实战:输入捕获与PWM驱动电机控制

发布时间:2026/6/26 12:40:50
P89LPC9321 CCU模块实战:输入捕获与PWM驱动电机控制 1. 项目概述与核心价值在嵌入式系统开发尤其是涉及电机控制、电源管理或精密传感器接口的项目里定时器/计数器单元Timer/Counter的深度掌握是区分“能用”和“用好”的关键。很多工程师在项目初期面对数据手册里大段的寄存器描述和时序图常常感到无从下手最终只能照搬例程知其然而不知其所以然。当项目需求稍微变化比如需要更精确的频率测量、更稳定的PWM波形或者处理带噪声的输入信号时就会遇到瓶颈。P89LPC9321这款经典的8位微控制器其内置的捕获/比较单元CCU功能相当强大且典型。它集成了输入捕获Input Capture和脉宽调制PWM两大核心功能。输入捕获不是简单地“读引脚状态”而是一个由硬件自动完成的、高精度的“时间戳”记录器这对于测量未知信号的脉宽或周期至关重要。PWM也不仅仅是“输出方波”其非对称与对称模式的选择直接关系到电机驱动的效率、噪音以及开关电源的纹波特性。本文将彻底拆解P89LPC9321 CCU模块的工作原理从最底层的寄存器位操作到中断服务程序的编写策略并结合我在多个电机驱动和电源项目中的实际踩坑经验为你呈现一份可以直接“抄作业”又明白“为什么这么写”的实战指南。无论你是正在评估这款芯片还是已经用它遇到了难题相信这篇深入解析都能帮你打通任督二脉。2. 输入捕获机制深度解析输入捕获功能的核心思想是利用定时器这个不断运行的“精密时钟”在外部引脚发生特定事件如上升沿或下降沿的瞬间“咔嚓”一声拍下快照将这个时刻的定时器计数值锁存到专门的寄存器中。这个过程完全由硬件自动完成不受软件延迟影响因此精度极高。2.1 基础工作原理与寄存器配置在P89LPC9321中CCU模块提供了两个独立的输入捕获通道A和B。其使能是默认的但要让其工作必须完成几个关键配置。首先必须将对应的I/O引脚例如P1.2对应捕获AP1.3对应捕获B配置为输入模式。这是很多新手容易忽略的第一步如果引脚被配置为输出或准双向口外部信号根本无法触发捕获逻辑。配置通常通过相应的端口模式寄存器如P1M1, P1M2完成。其次需要配置捕获边沿。这是通过CCU控制寄存器CCCRx x为A或B中的ICESx位来设定的。ICESx 0表示在下降沿触发捕获ICESx 1则表示在上升沿触发。例如要测量一个正脉冲的宽度你可以先配置为上升沿捕获记录时间T1在中断服务程序中改为下降沿捕获记录时间T2那么脉宽就是 (T2 - T1) * 定时器计数周期。这里的关键是改变边沿选择的操作必须在捕获中断服务程序ISR内完成以确保能捕捉到紧接着的相反边沿。当捕获事件发生时硬件会自动将16位定时器TH2:TL2的当前值搬运到对应的16位输入捕获寄存器ICRAH:ICRAL或ICRBH:ICRBL中。同时相应的中断标志位TICF2x位于TIFR2寄存器中会被置1。注意读取捕获寄存器的顺序有严格规定必须先读低字节ICRxL再读高字节ICRxH。这是因为当你读取ICRxL时高字节的内容会被自动锁存到一个影子寄存器中随后读取ICRxH时实际读到的是这个影子寄存器的值而非实时的高字节。这个机制保证了在读取16位值的过程中即使定时器仍在运行也能获得一个完整且一致的“快照”。如果错误地先读高字节或者读了两次低字节中间没读高字节得到的数据将是错乱的。2.2 高级特性噪声滤波与事件延迟在实际的工业环境中输入信号常常伴有毛刺噪声。一个轻微的抖动可能会被误认为是一个有效的边沿导致测量结果严重错误。P89LPC9321的CCU提供了一个简单但有效的数字噪声滤波器。通过设置CCCRx寄存器中的ICNFx位为1可以启用该滤波器。启用后捕获逻辑需要对输入信号进行连续4次采样采样频率为CCLK/2且4次采样值都一致时才认为检测到了一个有效的边沿。这相当于一个简单的数字滤波器能有效滤除宽度小于8个CCLK周期的窄脉冲噪声。实操心得滤波器的代价是延迟。使能噪声滤波后从真实边沿出现到被确认为捕获事件会有最多4个采样周期的延迟即8个CCLK周期。在计算高精度时间间隔时这个固定延迟需要被考虑进去尤其是在高频信号测量时。如果你的信号非常干净可以关闭滤波器以获得最快的响应。另一个强大的功能是事件延迟计数器。它允许你在连续发生多个边沿后才触发一次捕获。这通过CCCRx寄存器中的ICECx[2:0]三位来控制。ICECx2ICECx1ICECx0延迟边沿数0000 (每个边沿都捕获)00110102011310041015110711115这个功能的应用场景非常巧妙。例如在测量电机编码器的转速时编码器每转一圈会输出N个脉冲。如果你只关心每圈的转速而不需要每个脉冲的瞬时速度就可以将延迟设置为N-1这样每N个脉冲才产生一次捕获中断大大降低了CPU的中断负载。又比如可以对一个高频信号进行分频测量每16个周期测量一次既能降低软件开销又能通过计算平均值提高测量精度和稳定性。2.3 中断处理与软件设计要点捕获事件可以触发中断。要使能中断需要设置三个位全局中断使能EAIEN0.7、CCU模块中断使能ECCUIEN1.4以及具体的捕获通道中断使能位TICIE2xTICR2.0或TICR2.1。当中断发生时程序会跳转到CCU的中断向量。中断标志TICF2x必须由软件手动清除通常是通过向该位写0来实现注意很多架构是写1清标志但P89LPC9321这里是写0清除务必查阅数据手册确认。一个稳健的捕获中断服务程序ISR流程如下读取TISE2寄存器中断状态编码寄存器判断中断源虽然对于单一捕获任务可能不需要但在多中断源共存时很重要。立即读取捕获寄存器值先ICRxL后ICRxH并将该值存入一个软件缓冲区。这是最重要的一步防止后续操作覆盖寄存器。根据测量需求可能需要进行边沿切换修改ICESx位为下一次捕获做准备。清除对应的中断标志位TICF2x。退出中断。踩坑记录中断服务程序中的耗时操作。中断服务程序应该尽可能短小精悍。避免在捕获ISR中进行复杂的数学运算如浮点数计算或函数调用。通常的做法是在ISR中仅完成“读取数据”和“设置标志”的动作将耗时的计算如计算脉宽、频率放到主循环或低优先级任务中。否则可能因为ISR执行时间过长错过后续的捕获事件导致数据丢失。3. PWM工作原理与模式详解脉宽调制PWM是一种通过调节数字脉冲信号的占空比来模拟模拟量输出的技术。占空比Duty Cycle是指一个周期内高电平时间占总周期的比例。P89LPC9321的CCU提供了两种主要的PWM模式非对称PWM和对称PWM这两种模式在波形和适用场景上有本质区别。3.1 非对称PWM模式非对称PWM也称为边沿对齐PWM。在此模式下定时器只进行递减计数。无论TDIR2位如何设置硬件都会强制其为递减模式。其工作流程如下定时器从你设定的周期值TOR2开始递减计数。在计数过程中硬件不断将当前计数值TH2:TL2与比较寄存器OCRxH:OCRxL中的值进行比较。当计数值与比较值匹配时根据OCMx[1:0]位的设置对输出引脚执行操作。当计数值递减到0时产生下溢输出引脚状态可能再次改变同样取决于OCMx设置同时定时器自动重载TOR2的值开始下一个周期的递减。输出引脚行为OCMx[1:0] 01 非反相PWM置位点在比较匹配时输出引脚被置为高电平或有效电平。清除点在定时器下溢计数值回到TOR2时输出引脚被清除为低电平。这意味着高电平的宽度由比较寄存器的值直接决定。假设TOR21000OCR300则每个周期开始后计数值从1000递减减到300时输出变高减到0时输出变低然后开始新周期。输出波形的高电平时间是固定的对应计数值300段时间低电平时间是变化的。输出引脚行为OCMx[1:0] 11 反相PWM清除点在比较匹配时输出引脚被清除为低电平。置位点在定时器下溢时输出引脚被置为高电平。此时比较值决定了低电平的宽度。模式选择与计算通过向TMOD2[1:0]写入10H来选择非对称PWM模式。PWM频率的计算公式为Fpwm Fccu / (TOR2 1)。其中Fccu是CCU的时钟频率。占空比的计算公式为占空比 (OCR 1) / (TOR2 1)对于非反相PWM。这里“1”是因为计数器从TOR2递减到0总共经历了TOR21个计数周期。3.2 对称PWM模式对称PWM也称为中心对齐PWM。在此模式下定时器先递增达到TOR2后改变方向递减减到0后再改变方向递增如此往复。其工作流程如下定时器从0开始递增计数。在递增阶段当计数值与比较值匹配时根据OCMx设置和计数方向对输出引脚执行一次操作。计数达到TOR2后转为递减。在递减阶段当计数值再次与比较值匹配时会根据OCMx设置和计数方向对输出引脚执行另一次操作。计数减回0后再次转为递增开始下一个周期。输出引脚行为OCMx[1:0] 01 非反相PWM清除点在递增计数过程中发生比较匹配时清除输出引脚变低。置位点在递减计数过程中发生比较匹配时置位输出引脚变高。输出引脚行为OCMx[1:0] 11 反相PWM置位点在递增计数过程中发生比较匹配时置位输出引脚变高。清除点在递减计数过程中发生比较匹配时清除输出引脚变低。核心差异与优势对称PWM的波形是关于周期中心对称的。与非对称PWM相比其开关动作电平跳变在周期内分布得更均匀。这带来了一个巨大优势在相同的PWM频率下对称PWM产生的谐波分量更小尤其是偶次谐波被有效抑制。在电机驱动和开关电源应用中这意味着更小的电磁干扰EMI、更低的电机噪音和更平滑的转矩输出。因此在音频应用如Class D放大器或对噪声敏感的高性能电机驱动中对称PWM是首选。模式选择与计算通过向TMOD2[1:0]写入11H来选择对称PWM模式。PWM频率的计算公式为Fpwm Fccu / (2 * TOR2)。注意这里分母是2*TOR2因为一个完整的PWM周期包含了从0到TOR2再回到0的过程。占空比的计算公式为占空比 OCR / TOR2对于非反相PWM。例如TOR21000OCR300则输出波形在中心点附近对称高电平时间对应的计数值为600300上300下占空比为30%。3.3 交替输出模式与桥式驱动这是非对称PWM模式下的一个增强功能专为驱动H桥电路设计。通过设置TCR20寄存器中的ALTAB或ALTCD位可以将PWM通道A/B或C/D配置成交替对。在这种模式下一对PWM输出如A和B在每个定时器周期交替有效。具体来说在一个周期内通道A输出有效的PWM脉冲在下一个周期通道B输出有效的PWM脉冲如此循环。这个功能对于驱动直流电机或步进电机的H桥至关重要。它可以用来实现被动续流或主动刹车。例如在电机驱动中我们通常需要避免H桥同侧的两个开关管如上管和下管同时导通否则会导致短路。使用交替输出模式配合合适的死区时间通常需要外部硬件或软件插入可以确保同一桥臂的两个信号永远不会同时有效从硬件逻辑上防止了直通短路大大提高了系统的可靠性。3.4 同步更新与HALT功能同步PWM寄存器更新在PWM运行过程中如果直接修改比较寄存器OCRx或周期寄存器TOR2可能会在脉冲中间产生一个“毛刺”或畸变的脉冲。为了避免这种情况P89LPC9321采用了影子寄存器机制。当你写入OCRxH或OCRxL时值并不会立即生效而是先存入一组影子寄存器。只有当软件向TCOU2位在TCR21寄存器中写入1时影子寄存器中的值才会在下一个定时器溢出或下溢事件发生时同步更新到真正的工作寄存器中。这个机制确保了PWM波形的连续性和平滑性对于电机平稳调速和电源稳定输出至关重要。在更新PWM参数时标准的操作顺序是1) 写入新的OCRx值 2) 置位TCOU2。HALT功能这是一个安全特性。当HLTEN位使能后如果输入捕获A引脚上发生了一个使能的捕获事件所有PWM输出引脚会立即被强制设置为FCOx位在CCCRx寄存器中所定义的状态高或低同时HLTRN位被置1表明发生了急停。此时定时器本身继续运行但PWM输出被锁死。这个功能常用于实现紧急刹车、过流保护或故障安全状态。要恢复PWM输出必须由软件清除HLTRN位。软件也可以通过直接写HLTRN1来主动触发HALT状态。4. 时钟源与PLL提升PWM分辨率的关键PWM的分辨率直接决定了你对输出量控制的精细程度。分辨率通常用位数表示例如10位分辨率意味着占空比可以有2^101024个不同的等级。分辨率由定时器的位数和计数范围决定。P89LPC9321的CCU定时器是16位的理论上最大计数范围是0~65535但这受限于时钟频率和所需的PWM频率。基本关系PWM频率Fpwm、时钟频率Fccu和分辨率Resolution之间存在制约关系。对于非对称PWM近似有Fpwm ≈ Fccu / (2^Resolution)。这意味着在固定的时钟频率下提高PWM频率必然会降低分辨率反之要获得高分辨率就必须接受较低的PWM频率。为了突破主系统时钟PCLK频率的限制P89LPC9321的CCU模块内置了一个锁相环。PLL可以将输入时钟倍频产生一个更高的内部时钟CCUCLK供PWM定时器使用。PLL的输入频率范围是0.5 MHz到1 MHz。它会将这个频率乘以32产生输出。因此要使用PLL你需要通过TCR21寄存器中的PLLDV[3:0]位对PCLK进行分频以得到落在0.5-1 MHz范围内的输入频率。计算公式为PLL_input PCLK / (N 1)其中N是PLLDV[3:0]的值0-15。然后CCUCLK PLL_input * 32。例如假设系统主频PCLK 12 MHz。我们取N11则PLL_input 12 MHz / (111) 1 MHz正好在范围内。那么CCUCLK 1 MHz * 32 32 MHz。使用这个32 MHz的时钟即使我们需要一个20 kHz的PWM频率常用于电机驱动也能获得很高的分辨率TOR2_max 32 MHz / 20 kHz 1600。这相当于大约10.6位log2(1600)的分辨率比直接使用12 MHz主频此时TOR2_max600分辨率仅9.2位要精细得多。重要警告异步操作风险。当定时器使用PLL产生的CCUCLK运行时它与单片机内核及其他外设使用PCLK是异步的。数据手册明确警告“不鼓励在异步模式下读写定时器结果可能不可预测”。这意味着在PLL使能后应尽量避免在程序运行时频繁读取TH2/TL2的值或者将其用于与系统其他部分严格同步的计时。中断标志的置位和清除也会有若干CCLK周期的延迟。通常PLL模式用于“设置好就放手”的纯PWM输出场景或者配合输入捕获进行频率测量捕获值本身是准确的。PLL启动序列配置PWM模块模式、周期、占空比但先不要启动定时器即不要设置TMOD2启动位。根据PCLK频率计算合适的N值使PCLK/(N1)落在0.5-1 MHz之间并将N写入PLLDV。置位PLLEN位。然后需要循环等待直到读取PLLEN位为1这表明PLL已经锁定并稳定。最后写入TMOD2启动定时器。5. CCU中断结构与高效服务程序设计CCU模块有7个独立的中断源1个定时器溢出中断TOIF22个输入捕获中断TICF2A, TICF2B4个输出比较中断TOCF2A-D。它们共享同一个中断向量。这意味着当进入CCU中断服务程序时你首先需要判断是哪个事件触发了中断。5.1 中断优先级与状态查询硬件上固定了这7个中断源的优先级从高到低依次为定时器溢出 - 输入捕获A - 输入捕获B - 输出比较A - B - C - D。当多个中断同时发生时TISE2寄存器CCU中断状态编码寄存器的ENCINT[2:0]位会给出当前待处理的、优先级最高的中断源编码。ENCINT[2:0]中断源优先级000无中断挂起-001输出比较 D最低010输出比较 C011输出比较 B100输出比较 A101输入捕获 B110输入捕获 A111定时器溢出最高5.2 通用中断服务程序框架一个健壮且高效的CCU中断服务程序应该遵循以下流程以处理可能同时发生的多个中断// 假设使用SDCC或Keil C编译器中断号根据具体编译器定义 void CCU_ISR(void) interrupt INTERRUPT_CCU { unsigned char int_source; do { // 1. 读取当前最高优先级的中断源 int_source TISE2 0x07; // 取低3位 switch(int_source) { case 7: // 定时器溢出中断 (TOIF2) // 处理溢出例如更新软件计数器、同步PWM参数等 TIFR2 ~(1 7); // 清除TOIF2标志 (写0清除) break; case 6: // 输入捕获A中断 (TICF2A) // 读取捕获值注意顺序先读ICRAL再读ICRAH capture_value_low ICRAL; capture_value_high ICRAH; // 可以在这里切换捕获边沿或设置软件标志供主循环处理 TIFR2 ~(1 0); // 清除TICF2A标志 break; case 5: // 输入捕获B中断 (TICF2B) capture_value_low ICRBL; capture_value_high ICRBH; TIFR2 ~(1 1); // 清除TICF2B标志 break; case 4: // 输出比较A中断 (TOCF2A) // 在PWM模式下输出比较中断可用于在特定时刻更新其他参数 // 例如实现复杂的PWM序列 TIFR2 ~(1 3); // 清除TOCF2A标志 break; // ... 处理其他输出比较中断 case 0: // 无中断挂起 default: // 正常情况下不应进入这里除非读取有误 break; } // 2. 再次读取TISE2检查是否还有其他挂起的中断 // 由于清除了当前最高优先级的中断标志编码器会输出下一个最高优先级的中断源 int_source TISE2 0x07; } while (int_source ! 0); // 循环处理直到所有挂起的中断都被处理完毕 // 3. 中断返回 }这个do...while循环结构确保了在一次中断入口中能够处理完所有同时挂起的CCU中断避免了因中断嵌套或丢失中断可能带来的问题。这是编写多中断源共享一个向量时的标准做法。实操心得中断标志的清除时机。一定要在处理完该中断对应的任务后再清除中断标志。例如在输入捕获中断中必须先安全地读取ICRxL/H寄存器然后再清除TICF2x标志。如果先清除标志但在读取寄存器前又发生了新的捕获事件寄存器值会被覆盖导致数据丢失。此外清除标志后该中断源才能响应新的中断请求。6. 完整配置流程与实战代码示例下面我将以一个具体的实战场景为例展示如何配置P89LPC9321的CCU模块实现一路对称PWM输出用于驱动电机和一路输入捕获用于测量编码器频率。假设系统主频PCLK 12 MHz我们希望使用PLL将CCU时钟提升到32 MHz。6.1 初始化步骤详解第一步系统与端口初始化// 1. 配置端口模式 // 假设P1.2作为输入捕获A引脚P1.6作为PWM输出B引脚根据数据手册映射 // 将P1.2配置为输入高阻态P1.6配置为推挽输出 P1M1 ~(1 2); // P1.2: 清除M1 P1M2 | (1 2); // P1.2: 设置M2 配置为输入具体模式需查表此处为示例 P1M1 ~(1 6); // P1.6: 清除M1 P1M2 ~(1 6); // P1.6: 清除M2 配置为准双向口或根据手册设为推挽输出 // 2. 初始化CCU相关SFR指针如果使用绝对地址访问 // 也可以直接通过sfr关键字定义这里以地址操作为例 #define TCR20 (*(unsigned char volatile xdata *)0xF8) #define TCR21 (*(unsigned char volatile xdata *)0xF9) #define TOR2H (*(unsigned char volatile xdata *)0xFA) #define TOR2L (*(unsigned char volatile xdata *)0xFB) #define OCRBH (*(unsigned char volatile xdata *)0xFC) // 以OCRB为例 #define OCRBL (*(unsigned char volatile xdata *)0xFD) #define CCCRA (*(unsigned char volatile xdata *)0xFE) // 捕获控制A // ... 其他寄存器定义第二步配置并启动PLL// 1. 停止定时器如果正在运行 // TMOD2[1:0] 00 停止定时器根据手册可能需写其他值此处假设00为停止 // 2. 配置PLL分频器 // PCLK12MHz目标PLL输入1MHz N (12/1) - 1 11 TCR21 0x00; // 先清零确保BRGEN0虽然我们不用BRG并设置PLLDV TCR21 | (11 0x0F); // 设置PLLDV[3:0] 1011 (11) // 3. 启动PLL并等待锁定 TCR20 | (1 5); // 设置PLLEN位假设第5位是PLLEN需查手册确认位定义 while(!(TCR20 (1 5))); // 等待PLLEN位读回为1表明PLL已锁定 // 注意实际位定义需严格对照数据手册表43/44第三步配置PWM通道B对称模式// 目标生成一个20kHz占空比50%的对称PWM // CCUCLK 32 MHz, Fpwm 20 kHz // 对于对称PWM: TOR2 Fccu / (2 * Fpwm) 32,000,000 / (2 * 20,000) 800 // 占空比50%则 OCR 占空比 * TOR2 0.5 * 800 400 // 1. 设置周期值 TOR2 800 TOR2H (800 8) 0xFF; // 高字节 TOR2L 800 0xFF; // 低字节 // 2. 设置比较值 OCRB 400 OCRBH (400 8) 0xFF; OCRBL 400 0xFF; // 3. 配置比较控制寄存器CCCRB假设地址 // 设置输出模式为非反相对称PWM (OCMB[1:0] 01) // 同时可以设置FCOBHALT时的输出值例如设为0低电平 // 假设CCCRB寄存器位定义bit1:0OCMB, bit2FCOB #define CCCRB (*(unsigned char volatile xdata *)0xFF) // 示例地址 CCCRB 0x01; // OCMB01, FCOB0 // 4. 配置定时器控制寄存器TCR20 // 使能PWM输出选择对称模式可能还需要其他控制位 // 假设bit1:0 TMOD2[1:0], 11表示对称PWM bit2 PWM使能等 TCR20 ~0x03; // 清除模式位 TCR20 | 0x03; // 设置为对称PWM模式 (11) // 可能还需要设置其他位如定时器方向等在对称模式下无效第四步配置输入捕获通道A// 目标测量输入到P1.2引脚信号的频率使用上升沿捕获使能噪声滤波 // 1. 配置捕获控制寄存器CCCRA // ICES01 (上升沿), ICNF01 (使能噪声滤波), ICEC0[2:0]000 (每个边沿都捕获) // 假设CCCRA寄存器位定义bit0ICES0, bit1ICNF0, bit4:2ICEC0[2:0] CCCRA 0x03; // 二进制 0000 0011, 即ICES01, ICNF01, ICEC0000 // 2. 使能输入捕获A中断 TICR2 | (1 0); // 设置TICIE2A位假设第0位第五步使能中断并启动定时器// 1. 使能CCU模块中断 IEN1 | (1 4); // 设置ECCU位 (IEN1.4) // 2. 使能全局中断 EA 1; // 设置EA位 (IEN0.7) // 3. 最后启动CCU定时器 // 通过设置TCR20的模式位我们已经设置了对称模式(11)这本身可能就启动了定时器。 // 或者可能需要一个单独的启动位。根据数据手册向TMOD2[1:0]写非00值即启动。 // 我们之前TCR20已设为0x03假设这包含了启动命令。 // 更安全的做法是明确操作模式位寄存器可能叫TMOD2或集成在TCR20中。 // 假设向TCR20的bit1:0写入11即启动对称PWM定时器。 // 代码已在第三步第4点执行。6.2 输入捕获中断服务程序示例unsigned int capture_start_time 0; unsigned int capture_end_time 0; unsigned char capture_state 0; // 0:等待第一个上升沿 1:已捕获上升沿等待下降沿 volatile unsigned long period_ticks 0; // 测得的周期定时器计数 volatile bit new_measurement_ready 0; // 测量完成标志 void CCU_ISR(void) interrupt 5 { // 中断号5是CCU中断需查手册确认 unsigned char int_src TISE2 0x07; if(int_src 6) { // 输入捕获A中断 if(capture_state 0) { // 第一次上升沿捕获 capture_start_time_low ICRAL; // 必须先读低字节 capture_start_time_high ICRAH; capture_start_time (capture_start_time_high 8) | capture_start_time_low; // 切换为下降沿捕获准备捕获脉冲结束 CCCRA ~(1 0); // 清除ICES0位设置为下降沿捕获 capture_state 1; } else if(capture_state 1) { // 下降沿捕获脉冲结束 capture_end_time_low ICRAL; capture_end_time_high ICRAH; capture_end_time (capture_end_time_high 8) | capture_end_time_low; // 计算脉宽需要考虑定时器溢出 // 简单情况假设两次捕获间隔远小于定时器溢出周期(65536 ticks) period_ticks capture_end_time - capture_start_time; // 切换回上升沿捕获准备下一次测量 CCCRA | (1 0); // 设置ICES0位上升沿捕获 capture_state 0; new_measurement_ready 1; // 通知主循环 } TIFR2 ~(1 0); // 清除TICF2A中断标志 } // 可以添加其他中断源的处理... }关键点处理定时器溢出。上面的示例代码假设两次捕获发生在同一个定时器计数周期内没有发生溢出。在实际应用中如果测量的脉宽或周期很长定时器可能发生溢出。一个健壮的方案是在定时器溢出中断TOIF2中对一个软件扩展计数器如timer_overflow_count进行加一。在计算时间间隔时公式应为period (capture_end_time overflow_count_end * 65536) - (capture_start_time overflow_count_start * 65536)。你需要同时记录捕获发生时软件扩展计数器的值。6.3 主循环处理与频率计算void main(void) { float frequency_hz; unsigned long period_total_ticks; // ... 初始化代码调用前面的初始化函数 while(1) { if(new_measurement_ready) { new_measurement_ready 0; // 计算信号频率 // period_ticks 是定时器计数差值 // 定时器时钟频率 Fccu 32 MHz (使用PLL后) // 定时器计数周期 T_tick 1 / Fccu // 信号周期 T_signal period_ticks * T_tick // 信号频率 F_signal 1 / T_signal Fccu / period_ticks if(period_ticks ! 0) { // 避免除零错误 frequency_hz 32000000.0 / (float)period_ticks; // Fccu32MHz // 现在 frequency_hz 就是测量到的信号频率 // 可以用于显示、控制算法等... } // 示例根据测量的频率调整PWM占空比一个简单的闭环 // 假设我们想将电机转速稳定在某个频率对应值 // 这只是一个非常简化的示例真实PID控制更复杂 // unsigned int target_ticks ...; // 目标周期对应的计数值 // int error target_ticks - period_ticks; // 根据error调整OCRB值... // OCRBH (new_ocr 8) 0xFF; // OCRBL new_ocr 0xFF; // TCOU2 1; // 触发影子寄存器更新 } // ... 其他主循环任务 } }通过以上完整的配置和代码框架你应该能够基于P89LPC9321的CCU模块构建出具备高精度输入测量和高质量PWM输出能力的嵌入式系统。记住实际开发中务必反复核对数据手册中的寄存器地址和位定义并根据具体的外设引脚映射进行配置。调试时可以先用示波器观察PWM输出波形用信号发生器产生已知频率的方波测试输入捕获功能逐步验证每个环节的正确性。