1. 模拟比较器:从基础原理到Stellaris实现
在嵌入式硬件设计的工具箱里,模拟比较器(Analog Comparator)是一个看似简单却至关重要的“守门员”。它的核心任务纯粹而直接:实时比较两个模拟电压的大小,并立刻给出一个非此即彼的数字判决——高电平或低电平。这个判决过程,本质上是一个高速、开环的“裁决”行为。想象一下一个没有反馈回路的运算放大器,它的输出不再试图去逼近某个中间值,而是被驱动到电源轨的极限,只为清晰地告诉你:“V+ 大于 V-” 还是 “V- 大于 V+”。这种将连续的模拟世界转化为绝对的数字判断的能力,使其成为系统安全、电源监控、信号调理和事件检测中不可或缺的一环。
在德州仪器(TI)的Stellaris LM3S2950这类微控制器中,模拟比较器被集成为了一个功能完整的外设模块。它不再是分立元件,而是成为了片上系统(SoC)的一部分,这意味着我们可以通过软件寄存器对其进行精细的配置和控制,极大地提升了设计的灵活性和集成度。模块通常包含多个独立的比较器单元(LM3S2950有三个),每个单元都可以灵活选择输入源(外部引脚或内部可编程参考电压),并自由配置输出目的地(触发中断或直接驱动GPIO引脚)。这种集成化设计,让我们能够用几行代码就实现过去需要额外芯片和电路才能完成的功能。
1.1 核心功能与典型应用场景解析
模拟比较器的应用场景广泛,其价值在于它提供了一种简单、快速、可靠的模拟信号阈值检测机制。
过压与欠压保护(OVP/UVP):这是最经典的应用之一。将电源电压通过电阻分压后接入比较器的负输入端(VIN-),将一个稳定的基准电压(可以是内部参考电压或外部精密基准源)接入正输入端(VIN+)。当电源电压正常时,VIN- < VIN+,比较器输出高电平;一旦电源电压异常升高,导致VIN- > VIN+,比较器输出立即翻转为低电平。这个跳变可以触发微控制器的中断,系统能在微秒级内执行关断或保护动作,防止后级电路损坏。同理,交换输入极性即可实现欠压保护。
窗口比较器(Window Comparator):使用两个比较器可以构建一个窗口检测电路。一个比较器检测信号是否超过上限,另一个检测是否低于下限。两个输出通过逻辑门组合,可以判断信号是否处于一个安全的“窗口”之内。这在电池电量监测、传感器信号范围校验中非常有用。在Stellaris中,我们可以利用两个独立的比较器单元和软件逻辑来实现这一功能。
模拟信号到数字事件的转换:例如,在光电编码器或霍尔传感器应用中,传感器输出的模拟正弦波信号需要被转换为方波脉冲以进行计数。将传感器信号接入一个比较器,与一个设定的阈值(如信号的平均值)进行比较,即可得到清晰的数字边沿,用于精确测量转速或位置。
唤醒低功耗系统:在电池供电的设备中,MCU可能处于深度睡眠状态以节省功耗。我们可以配置一个比较器,持续监控某个关键信号(如按键电压、传感器信号)。当信号超过阈值时,比较器输出的跳变可以直接作为唤醒源,将MCU从睡眠中拉回,实现极低功耗的待机与事件触发。
在LM3S2950中,比较器的输出不仅可以被软件读取,还能直接映射到特定的GPIO引脚(如C0o, C1o, C2o),这意味着它可以直接驱动外部电路(如LED指示灯)或作为其他数字电路的输入,无需CPU干预,实现了纯硬件的快速响应路径。
1.2 Stellaris LM3S2950模拟比较器模块架构
LM3S2950的模拟比较器模块是一个相对独立且结构清晰的子系统。理解其架构是进行正确配置的前提。
模块包含三个完全独立的比较器(Comparator 0, 1, 2)。每个比较器都是一个完整的比较单元,拥有自己的正负输入引脚、控制逻辑和状态寄存器。它们共享一个内部可编程电压参考源,这个参考源是一个精密的电阻阶梯网络(Resistor Ladder),可以通过ACREFCTL寄存器进行配置,产生不同的参考电压供各个比较器使用。
每个比较器的核心配置围绕两个主要寄存器展开:
控制寄存器(ACCTLn):这是“指挥官”。它决定比较器的行为模式,包括:
- 输入源选择(ASRCP):正输入端(VIN+)是用外部引脚(Cx+)的电压,还是使用模块共享的内部参考电压?这个选择决定了比较的基准。
- 输出极性控制(CINV):是否将比较结果反转?这对于匹配不同的逻辑电平或简化电路设计很有用。例如,默认VIN+ > VIN-时输出1,反转后则输出0。
- 中断触发模式(ISEN, ISLVAL):配置在何种条件下产生中断。是输出为特定电平时(电平触发),还是在输出上升沿、下降沿或任意边沿时(边沿触发)?这为软件提供了灵活的事件响应机制。
状态寄存器(ACSTATn):这是“观察员”。它只有一个关键位——输出值位(OVAL)。软件通过读取这个位,可以随时获知当前比较器的数字输出状态,是1还是0。
模块的中断系统由三个寄存器协同管理,构成了一个标准的中断状态机:
- 原始中断状态寄存器(ACRIS):实时反映每个比较器的输出是否满足了中断触发条件(无论中断是否被允许)。这是一个“事实”记录器。
- 中断使能寄存器(ACINTEN):软件通过设置此寄存器,决定允许哪个比较器的中断请求能继续向上传递。这是一个“开关”。
- 已屏蔽中断状态寄存器(ACMIS):这是最终提交给CPU的中断状态。它等于
ACRIS & ACINTEN。只有当中断被使能且触发条件发生时,对应的位才会置1。向该位写1可以清除中断标志。
重要提示:在配置使用模拟比较器功能前,有一个极易忽略但至关重要的硬件配置步骤。对于用作模拟输入(Cx+, Cx-)的GPIO引脚,必须禁用其数字输入功能。具体操作是清除该引脚在GPIO模块中对应的
GPIODEN寄存器的位。如果不这样做,数字输入缓冲器会试图对模拟电压进行采样,可能导致引脚电流异常增大,甚至影响模拟信号的准确性。这是混合信号设计中的一个常见陷阱。
2. 内部可编程参考电压的配置与计算
Stellaris模拟比较器模块的一个亮点是其集成的内部参考电压源。它省去了外部分压电路,提高了集成度和可靠性。这个参考源本质上是一个连接在模拟电源(AVDD)和地之间的电阻分压网络,并通过一个多路选择器(MUX)将不同的分压点引出。
2.1 参考电压源结构详解
如图15-3所示,内部参考结构是一个电阻阶梯。关键的控制在于ACREFCTL寄存器的三个字段:
- EN(使能位,第9位):这是总开关。置1,电阻阶梯上电,参考电压源工作;置0,则完全断电以节省功耗。在初始化时,必须先使能此位,参考电压才会稳定。
- RNG(范围位,第8位):此位选择电阻阶梯的总电阻值,从而决定参考电压的范围。
RNG=0:总电阻为31R。此时参考电压范围较高,计算公式为VREF = (VREF字段值 / 32) * AVDD。例如,当AVDD为3.3V,VREF=16时,VREF = (16/32)*3.3V = 1.65V。其可用范围大约是0.85V到2.448V(对应VREF值约8到23)。RNG=1:总电阻为23R。此时参考电压范围较低且包含0V,计算公式为VREF = (VREF字段值 / 24) * AVDD。同样AVDD=3.3V,VREF=12时,VREF = (12/24)*3.3V = 1.65V。其可用范围是0V到2.152V(对应VREF值0到15)。
- VREF(电压选择字段,第3-0位):这是一个4位字段,值从0到15。它控制着多路选择器连接到电阻阶梯的哪个抽头。不同的
RNG模式下,相同的VREF值对应的实际电压不同。
2.2 配置实例与计算过程
假设我们的系统AVDD为3.3V,我们需要为比较器0配置一个1.65V的内部参考电压,且希望参考电压范围能包含0V(即选择RNG=1模式)。
确定VREF值:根据公式
VREF = (VREF字段值 / 24) * 3.3V = 1.65V。 解方程:VREF字段值 = (1.65V * 24) / 3.3V = 12。 因此,VREF字段应设置为12(二进制1100)。组合寄存器值:
EN = 1(使能参考源)RNG = 1(选择23R模式,范围0-2.152V)VREF = 12(二进制1100)- 寄存器
ACREFCTL的其他位均为保留位,必须写0。 所以,ACREFCTL寄存器的值应为:EN在位9,RNG在位8,VREF在位3-0。 计算:(1<<9) | (1<<8) | 12 = 0x200 | 0x100 | 0xC = 0x30C。
因此,我们需要向ACREFCTL寄存器写入0x0000030C(这里的高位是保留位,通常为0)。写入后,内部参考电压源就会稳定输出1.65V,可供所有比较器选用。
实操心得:内部参考的精度与稳定性:片内电阻阶梯的绝对精度通常一般(可能误差在±5%到±10%),且受温度影响。对于需要高精度阈值比较的应用(如精密电源监控),建议使用外部精密基准电压源(如TL431、REF30xx系列)连接到比较器的外部输入引脚。内部参考更适合对精度要求不高的场景,如电池粗略电量指示、非关键信号的过载检测等。
3. 模拟比较器的完整配置流程与代码实现
理解了原理和寄存器后,我们来完成一个完整的配置示例:将比较器0配置为使用内部1.65V参考电压作为正端输入,负端接外部引脚C0-,输出不反转,并使其能产生中断。
3.1 配置步骤拆解
以下是基于Stellaris外设驱动库(DriverLib)风格的手动寄存器操作流程:
使能外设时钟:任何外设在使用前,必须让系统控制器为其提供时钟。模拟比较器模块的时钟由
RCGC1寄存器控制。// 假设使用系统控制寄存器基址 #define SYSCTL_RCGC1_R (*((volatile uint32_t *)0x400FE104)) // 使能模拟比较器模块时钟(ACMP0在RCGC1的位20) SYSCTL_RCGC1_R |= 0x00100000; // 插入少量延时,等待时钟稳定 __asm__ volatile("nop"); __asm__ volatile("nop");配置GPIO引脚:将用作模拟输入的引脚(此处是C0-对应的GPIO引脚)配置为模拟功能。
- 找到C0-对应的GPIO端口和引脚(根据数据手册,LM3S2950的C0-对应某个GPIO引脚,例如PF2)。
- 将该引脚的方向设置为输入(
GPIODIR寄存器相应位清0)。 - 关键一步:禁用该引脚的数字输入功能,即清除
GPIODEN寄存器中对应的位。这是为了避免数字输入缓冲器干扰模拟信号。 - 通常不需要设置上下拉(
GPIOPUR/GPIOPDR),因为模拟输入阻抗很高。
// 假设C0-对应PF2 #define GPIO_PORTF_DEN_R (*((volatile uint32_t *)0x4002551C)) // 禁用PF2的数字输入 GPIO_PORTF_DEN_R &= ~(1<<2);配置内部参考电压:如前所述,配置
ACREFCTL寄存器产生1.65V参考。#define ACMP_ACREFCTL_R (*((volatile uint32_t *)0x4003C010)) ACMP_ACREFCTL_R = 0x0000030C; // EN=1, RNG=1, VREF=12 // 等待参考电压稳定,通常需要几个微秒 for(volatile int i=0; i<100; i++);配置比较器0控制寄存器:这是核心配置。
- 设置
ASRCP字段为1,选择内部参考电压作为正端输入(VIN+)。 - 设置
CINV为0,输出不反转。 - 设置
ISEN和ISLVAL以配置中断。例如,设置ISEN=0x0(电平触发),ISLVAL=1(当输出为高电平时产生中断)。 - 必须设置
ASRCP位:这是手册中强调的步骤,在启用比较器功能前必须完成。
#define ACMP_ACCTL0_R (*((volatile uint32_t *)0x4003C024)) // ASRCP=1 (内部参考), ISEN=0 (电平), ISLVAL=1 (高电平触发), CINV=0 // 位10-9是ASRCP,值为1。位3-2是ISEN,值为0。位4是ISLVAL,值为1。 ACMP_ACCTL0_R = (1<<9) | (1<<4); // 0x0400 | 0x0010 = 0x0410- 设置
使能比较器中断:在模块级中断使能寄存器中,开启比较器0的中断。
#define ACMP_ACINTEN_R (*((volatile uint32_t *)0x4003C008)) ACMP_ACINTEN_R |= (1<<0); // 使能比较器0中断同时,别忘了在NVIC(嵌套向量中断控制器)中使能模拟比较器模块的中断通道,并编写对应的中断服务函数(ISR)。
读取比较器状态:在中断服务函数或主循环中,可以通过读取
ACSTAT0寄存器的OVAL位来获取当前输出值。#define ACMP_ACSTAT0_R (*((volatile uint32_t *)0x4003C020)) uint32_t comparator_output = (ACMP_ACSTAT0_R >> 1) & 0x01; // 读取OVAL位(位1)
3.2 一个完整的初始化函数示例
void AnalogComparator0_Init(void) { // 1. 使能时钟 SYSCTL_RCGC1_R |= 0x00100000; // 使能ACMP时钟 volatile uint32_t delay = SYSCTL_RCGC1_R; // 空读,确保时钟稳定 // 2. 配置C0-引脚(假设为PF2)为模拟输入 // 使能GPIOF时钟 SYSCTL_RCGC2_R |= 0x00000020; delay = SYSCTL_RCGC2_R; // 清除PF2的DIGITAL ENABLE GPIO_PORTF_DEN_R &= ~(1<<2); // 确保PF2方向为输入(复位后默认即为输入) // 3. 配置内部参考电压为1.65V ACMP_ACREFCTL_R = 0x0000030C; // EN=1, RNG=1, VREF=12 for(delay=0; delay<1000; delay++); // 简单延时等待稳定 // 4. 配置比较器0:内部参考为正端,电平触发,高电平触发中断 ACMP_ACCTL0_R = (1<<9) | (1<<4); // ASRCP=01, ISLVAL=1 // 5. 使能模块内比较器0中断 ACMP_ACINTEN_R |= (1<<0); // 6. 配置NVIC(假设ACMP中断号为14) NVIC_EN0_R |= (1<<14); // 在NVIC中使能中断 // IntRegister(INT_ACOMP0, AnalogComparator0_ISR); // 注册中断函数(需具体实现) } // 中断服务函数示例 void AnalogComparator0_ISR(void) { // 读取并清除中断标志 uint32_t int_status = ACMP_ACMIS_R; ACMP_ACMIS_R = int_status; // 写1清除对应位 if(int_status & 0x01) { // 检查是否是比较器0中断 uint32_t output_state = (ACMP_ACSTAT0_R >> 1) & 0x01; // 根据output_state执行相应操作,例如触发ADC采样、改变GPIO状态等 if(output_state) { // 比较器输出为高(VIN+ > VIN-) // 执行相关任务... } else { // 比较器输出为低(VIN+ < VIN-) // 执行相关任务... } } }4. PWM模块:数字世界模拟化的核心引擎
如果说模拟比较器是将模拟量转化为数字判决,那么脉冲宽度调制(PWM)则是其逆向过程——用数字方法精确地控制模拟量。PWM的核心思想是生成一个固定频率的方波,并通过改变方波中高电平所占的时间比例(即占空比)来等效一个模拟电压的平均值。占空比越大,平均电压越高;占空比越小,平均电压越低。这种技术完美地桥接了微控制器的数字输出与外部模拟执行器(如电机、LED、加热丝)之间的鸿沟。
Stellaris LM3S2950的PWM模块是一个功能强大且灵活的定时器系统,专为复杂的电机控制和功率转换应用而设计。它远不止是简单的定时器输出比较,而是一个包含同步、死区、故障保护等高级功能的完整信号生成引擎。
4.1 PWM模块架构与核心组件
模块由三个相同的**PWM发生器块(Generator Block)和一个输出控制块(Output Control Block)**构成。每个发生器块能独立产生两路PWM信号(PWMxA和PWMxB),因此总共可提供6路独立的PWM输出。
每个PWM发生器块是其心脏,包含以下关键子模块:
16位计数器(Timer):这是PWM信号的节拍器。它有两种基本工作模式:
- 递减计数模式(Down Mode):计数器从装载值(LOAD)递减到0,然后立即重载,周而复始。此模式产生边沿对齐的PWM波形,信号边沿发生在计数器为0或LOAD的时刻。
- 递增/递减计数模式(Up/Down Mode):计数器从0递增到LOAD,然后递减回0,如此循环。此模式产生中心对齐的PWM波形,信号变化对称地分布在计数周期两侧。这种模式能显著减少电机驱动中的谐波噪声,是三相逆变器驱动的首选。
两个PWM比较器(Comparator A/B):每个比较器持续将计数器的当前值与一个用户设定的比较值(CMPA, CMPB)进行比较。当匹配发生时,会产生一个短暂的高脉冲。在递增/递减模式下,匹配事件会区分“向上匹配”和“向下匹配”。
PWM信号发生器(Signal Generator):这是逻辑控制中心。它接收来自计数器(零点和装载点)和比较器(匹配点)的事件脉冲,并根据用户预先在
PWMnGENA和PWMnGENB寄存器中设定的“动作表”,来驱动两路PWM输出(PWMA和PWMB)的电平。例如,可以设定“在计数器向上计数匹配CMPA时,将PWMA置高;在计数器向下计数匹配CMPA时,将PWMA置低”,从而生成一个中心对齐、占空比由CMPA决定的PWM波。死区发生器(Dead-Band Generator):这是驱动半桥或全桥功率电路的安全卫士。它接收信号发生器送来的一路原始PWM信号,生成两路互补的、带有可编程延迟的PWM信号。一路在原始信号上升沿后延迟开启,另一路在原始信号下降沿后延迟开启。这个“延迟”就是死区时间,确保同一桥臂的上管和下管不会因为开关速度不同而出现同时导通的“直通”现象,从而烧毁功率器件。
中断选择器:允许用户选择计数器事件(零、装载、匹配A/B上/下)中的任何一个或组合作为中断源,从而在PWM波形的特定位置精确触发CPU中断,用于更新比较值(实现闭环控制)或执行其他任务。
输出控制块则负责最后的信号路由与修饰。它可以:
- 独立启用或禁用每一路PWM输出到引脚。
- 对任何一路输出进行极性反转(将主动高变为主动低)。
- 配置故障输入(Fault)信号如何快速禁用指定的PWM输出,实现硬件级保护。
- 同步多个PWM发生器的计数器,确保多路信号之间的严格相位关系。
4.2 PWM信号生成机制深度解析
理解动作表是灵活运用PWM发生器的关键。以生成一对中心对齐、互补的PWM信号为例(常用于半桥驱动):
假设计数器工作在递增/递减模式。我们需要定义PWMA和PWMB在以下六个事件发生时的动作:计数器=0(Zero),计数器=LOAD,向上计数匹配CMPA(CMPA Up),向下计数匹配CMPA(CMPA Down),向上计数匹配CMPB(CMPB Up),向下计数匹配CMPB(CMPB Down)。
对于PWMA(控制上管):
- 在
CMPA Up事件时,动作设置为“驱动为高”(Drive High)。 - 在
CMPA Down事件时,动作设置为“驱动为低”(Drive Low)。 - 其他四个事件动作设置为“无操作”(Do Nothing)。
对于PWMB(控制下管),我们希望它与PWMA互补:
- 在
CMPA Down事件时(即PWMA变低时),动作设置为“驱动为高”。 - 在
CMPA Up事件时(即PWMA变高时),动作设置为“驱动为低”。 - 其他事件“无操作”。
这样,我们就用CMPA一个参数同时控制了两路信号的占空比,且它们天然互补。然后,将PWMA送入死区发生器,生成最终带死区延迟的两路桥臂驱动信号。
同步与更新机制:在复杂的多相控制(如三相电机)中,保持各相PWM的同步至关重要。PWM模块提供了两种更新比较值(CMPA/B)和装载值(LOAD)的模式:
- 立即更新:新写入的值在当前计数周期结束后(计数器归零时)立即生效。这可能导致不同发生器块的值在不同周期更新,引起相位抖动。
- 同步更新:新写入的值先被缓存。当软件触发一个全局同步信号(通过
PWMSYNC寄存器)时,所有配置为同步更新的发生器块,会在各自的下一个计数器归零时刻,统一采用新值。这保证了所有PWM通道在同一时刻平滑切换到新的调制模式,对于矢量控制等算法至关重要。
5. PWM模块配置实战:生成带死区的互补PWM
让我们以控制一个直流有刷电机的H桥为例,配置PWM发生器0,产生一对频率为20kHz,占空比50%,且带有1us死区时间的互补PWM信号(PWM0和PWM1)。假设系统时钟为50MHz。
5.1 参数计算与寄存器配置
计算装载值(LOAD)确定频率: PWM频率由计数器时钟和装载值决定。计数器时钟一般为系统时钟分频后的PWM时钟。假设我们使用不分频,即PWM时钟=系统时钟=50MHz。 在递增/递减模式下,一个完整的PWM周期是计数器从0到LOAD再到0的过程,共计
2 * LOAD个时钟周期。 因此,PWM频率Fpwm = Fpwm_clk / (2 * LOAD)。 我们需要Fpwm = 20kHz,所以LOAD = Fpwm_clk / (2 * Fpwm) = 50,000,000 / (2 * 20,000) = 1250。 装载值PWM0LOAD应设置为1250。计算比较值(CMPA)确定占空比: 占空比定义为高电平时间与整个周期时间的比值。在中心对齐模式下,高电平时间对应于计数器从
LOAD - CMPA计数到LOAD,再向下计数到LOAD - CMPA的这一段区间。 更直观的公式是:占空比 = CMPA / LOAD。 我们需要50%占空比,所以CMPA = LOAD * 0.5 = 1250 * 0.5 = 625。 比较值PWM0CMPA应设置为625。计算死区时间参数: 死区时间由两个寄存器
PWM0DBRISE(上升沿延迟)和PWM0DBFALL(下降沿延迟)控制,其值代表延迟的时钟周期数。 我们需要1us的死区时间。系统时钟周期Tclk = 1 / 50MHz = 0.02us。 因此,死区时钟周期数DB_COUNT = 1us / 0.02us = 50。 我们将PWM0DBRISE和PWM0DBFALL都设置为50。
5.2 配置流程与代码
void PWM_Generator0_Init(void) { // 1. 使能PWM模块和对应GPIO端口的时钟 SYSCTL_RCGC0_R |= 0x00100000; // 使能PWM模块时钟 SYSCTL_RCGC2_R |= 0x00000020; // 使能GPIOF时钟(假设PWM0,1在PF0,PF1) volatile uint32_t delay = SYSCTL_RCGC2_R; // 2. 配置GPIO引脚为PWM功能 // 假设PWM0对应PF0,PWM1对应PF1 GPIO_PORTF_AFSEL_R |= 0x03; // 使能PF0和PF1的备用功能 GPIO_PORTF_PCTL_R = (GPIO_PORTF_PCTL_R & 0xFFFFFF00) | 0x00000055; // 配置PF0,1为PWM功能 GPIO_PORTF_DEN_R |= 0x03; // 使能数字功能 GPIO_PORTF_DIR_R |= 0x03; // 设置为输出方向 // 3. 配置PWM发生器0 // 先禁用发生器以便配置 PWM0_CTL_R &= ~0x00000001; // 清除PWM0ENABLE位(假设PWM0_CTL_R是PWM0控制寄存器地址) // 配置计数器模式为递增/递减,并设置同步更新模式(可选) PWM0_CTL_R = (0x0 << 0); // 模式:递增/递减模式。如果需要立即更新,此位为0。 // 配置装载值和比较值 PWM0_LOAD_R = 1250 - 1; // 装载值,注意有些硬件要求写入N-1 PWM0_CMPA_R = 625 - 1; // 比较器A值 // 比较器B本例未使用,可设为0或LOAD // 配置死区控制 PWM0_DBCTL_R |= 0x00000001; // 使能死区发生器 PWM0_DBRISE_R = 50 - 1; // 上升沿延迟 PWM0_DBFALL_R = 50 - 1; // 下降沿延迟 // 配置动作发生器(PWM0GENA和PWM0GENB) // 对于PWMA(上管):在CMPA Up时置高,CMPA Down时置低 PWM0_GENA_R = (0x3 << 0) | (0x2 << 4); // 位[3:0]对应CMPA Up动作=0x3(驱动高),位[7:4]对应CMPA Down动作=0x2(驱动低) // 对于PWMB(下管):我们希望与PWMA互补,但通过死区发生器实现,这里PWMB可以配置为与PWMA相同或常低,由死区发生器生成互补信号。 // 更常见的做法是:将发生器配置为生成互补对模式。许多PWM模块有直接生成互补对的模式。 // 以Stellaris为例,可以通过配置PWM0_GENB_R来让PWMB直接作为PWMA的互补输出(在死区前)。 // 假设我们配置PWMB在CMPA Down时置高,CMPA Up时置低(与PWMA反相)。 PWM0_GENB_R = (0x2 << 0) | (0x3 << 4); // CMPA Up动作=0x2(驱动低),CMPA Down动作=0x3(驱动高) // 4. 配置输出控制块 // 使能PWM0和PWM1输出 PWM_ENABLE_R |= 0x0003; // 使能PWM0和PWM1输出位 // 如果需要反相输出(低电平有效),在此配置PWMINVERT寄存器 // 5. 重新使能PWM发生器0 PWM0_CTL_R |= 0x00000001; // 置位PWM0ENABLE }注意事项:死区生成的两种方式:上述代码展示了一种方式:配置两路独立的PWM信号(PWMA和PWMB)为互补关系,然后让其中一路通过死区发生器。另一种更简洁的方式是,PWM模块通常提供“互补对”模式。在此模式下,只需配置一路PWMA的动作,并启用死区发生器,模块会自动生成PWMA和其互补信号PWMB(带死区)。需要查阅具体数据手册中
PWMnCTL寄存器的“MODE”位或类似功能。
6. 模拟比较器与PWM的协同应用案例
将模拟比较器和PWM结合,可以构建高效的闭环控制系统。一个典型的应用是基于电流斩波的步进电机微步驱动。
场景:我们使用PWM驱动一个步进电机驱动芯片(如DRV8825)来微步控制电机。为了限制电机绕组电流,我们通常在绕组上串联一个小的采样电阻,将电流转化为电压。
实现方案:
- PWM角色:PWM发生器产生频率固定的PWM信号,控制电机驱动芯片的电流调节端。占空比决定了目标电流值。
- 模拟比较器角色:将采样电阻两端的电压(代表实际电流)放大后,接入模拟比较器的一个输入端(例如C0-)。另一个输入端(C0+)连接一个由DAC或PWM滤波产生的可编程阈值电压,这个阈值对应我们设定的目标电流峰值。
- 闭环控制流程:
- 初始时,PWM输出一个占空比,电机绕组电流开始上升。
- 当电流采样电压超过比较器设定的阈值电压时,比较器输出翻转(例如从低变高)。
- 这个翻转信号可以连接到PWM模块的故障输入(Fault)引脚,或者触发一个高优先级中断。
- 如果使用故障引脚:可以配置PWM输出控制块,使得当Fault信号有效时,立即强制相关的PWM输出进入安全状态(低电平),从而关闭功率管,电流开始下降。当电流下降到阈值以下,比较器输出恢复,Fault解除,PWM自动恢复输出。这就形成了一个硬件实现的、响应速度极快的峰值电流限制环,也称为“斩波”控制。
- 如果使用中断:在比较器中断服务程序中,软件可以迅速修改PWM的占空比或直接禁用PWM输出,实现电流调节。虽然速度比硬件Fault慢,但更灵活,可以实现更复杂的控制算法(如PID)。
这种硬件比较器+PWM故障保护的组合,实现了对电流的实时、无延迟监控,是保护功率电路和电机免受过流损坏的关键技术。软件则可以在更高的层次上调整电流阈值(通过改变DAC输出或内部参考电压)和PWM占空比,实现速度、位置等闭环控制。
7. 常见问题排查与调试技巧
在实际开发中,模拟比较器和PWM模块可能会遇到一些典型问题。以下是一些排查思路:
模拟比较器无输出或输出不稳定:
- 检查时钟与电源:确认已通过
RCGC1寄存器使能了模拟比较器模块时钟,并等待了足够的时间(通常几个时钟周期)让时钟稳定。检查模拟电源AVDD是否稳定、干净。 - 验证GPIO配置:这是最常见的问题。务必确认用作模拟输入(Cx+, Cx-)的引脚已禁用数字输入(清除
GPIODEN对应位)。同时,确保没有意外使能了内部上拉/下拉电阻。 - 确认内部参考已使能:如果使用内部参考,检查
ACREFCTL寄存器的EN位是否已置1,并等待了足够的稳定时间(数据手册会给出典型值,通常几微秒到几十微秒)。 - 检查输入电压范围:确保待比较的电压在芯片允许的模拟输入电压范围内(通常为0V到AVDD)。超出范围可能导致比较器工作异常或损坏。
- 注意噪声与滞后:比较器对输入噪声很敏感,在阈值附近微小的噪声可能导致输出频繁抖动。如果应用允许,可以考虑在软件中增加去抖逻辑(如连续多次读取确认),或者如果芯片支持,启用比较器的内置迟滞功能(某些型号有)。
PWM无输出或频率/占空比不对:
- 时钟与分频:首先确认PWM模块的时钟已使能(
RCGC0寄存器)。然后检查PWM时钟分频器配置(PWMnCTL中的分频位)。错误的时钟源或分频系数是导致频率不对的首要原因。 - 输出使能:确认
PWMENABLE寄存器中对应PWM输出位的使能位已经置1。同时检查对应的GPIO引脚是否已正确配置为PWM功能(AFSEL置1,PCTL选择正确)。 - 发生器使能:确认
PWMnCTL寄存器中的ENABLE位(或类似功能位)已置1。在配置过程中,建议先清除此位,配置完所有参数后再置位。 - 装载值与比较值关系:在递减模式下,确保比较值(CMPA/B)小于或等于装载值(LOAD)。如果比较值大于装载值,则永远不会发生匹配事件,PWM输出可能常高或常低。在递增/递减模式下,比较值也应介于0和LOAD之间。
- 动作生成器配置:仔细检查
PWMnGENA/B寄存器的配置。每个事件(Zero, Load, CMPA Up/Down, CMPB Up/Down)对应的动作(无操作、驱动高、驱动低、翻转)是否正确。一个错误的动作设置会导致波形完全不符合预期。 - 死区时间异常:如果死区时间看起来无效或不对,检查
PWMnDBCTL是否已使能,并确认PWMnDBRISE和PWMnDBFALL的值是否基于正确的PWM时钟周期计算。过长的死区时间会导致有效占空比严重缩小。
中断无法触发:
- 中断使能链:检查三个层级的使能:1) 外设模块级中断使能(如
ACINTEN或PWMnINTEN);2) 中断控制器级使能(如NVIC中的对应中断号);3) 全局中断使能(CPSR中的I位,通常由__enable_irq()函数打开)。 - 中断标志清除:在中断服务函数中,必须清除对应的中断标志位(通常是向
ACMIS或PWMnISC寄存器的相应位写1)。如果忘记清除,中断会持续触发,导致系统卡死。 - 触发条件:确认中断触发条件(边沿、电平)设置是否符合实际信号变化。例如,如果配置为上升沿触发,但信号始终为高,则不会进入中断。
调试时,最有效的工具是示波器和逻辑分析仪。用示波器观察比较器的输入引脚电压和输出引脚(如果使能了输出到引脚)波形,可以直观判断比较器是否在工作。用逻辑分析仪同时抓取PWM输出、故障引脚、以及关键的GPIO控制信号,可以清晰地分析时序问题,特别是死区时间是否准确插入。在软件中,通过读取ACSTATn或PWMnCOUNT等状态寄存器的值,也能帮助判断模块的内部状态。