阀泵定时靠软件延时抖动大,换输出比较又常被 OCxR、OCxRS 与 PRy 的关系卡住。按 dsPIC30F 参考手册第 14 章拆解八种 OCM 编码、单双比较时序与 PWM 周期精度,给出七步配置与排查表。
一台包装机用 dsPIC30F 做阀泵时序,原先靠定时器中断里翻转 IO,脉宽在 200 µs 到 5 ms 之间变化,操作工总说同一个配方动作有快有慢。改用输出比较模块后,边沿抖动从几个微秒压到一个指令周期。结论先给:输出比较不是另一种定时器中断,它把比较值直接送到引脚逻辑,匹配后的下一个指令时钟引脚就动作;真正决定脉冲形状的,是 OCxR 与 OCxRS 谁管上升沿、谁管下降沿,以及周期寄存器 PRy 与这两个值的大小关系。
本文按 Microchip《dsPIC30F 系列参考手册》DS70060C_CN 第 14 章整理,寄存器位定义、时序图与算例均取自该章。
每个输出比较通道有两个可选时基:OCTSEL(OCxCON<3>)为 0 选 Timer2,为 1 选 Timer3;器件最多 8 个通道,从 OC1 到 OC8,具体数量看型号数据手册。比较匹配发生在时基计数值与 OCxR 相等时,引脚在下一个指令时钟动作,再过 2 个 TCY 才置 OCxIF。这 2 TCY 的滞后只影响中断响应,不改变引脚边沿位置,所以脉宽精度取决于时基分辨率,而不是中断延迟。
两条硬约束常被忽略:一是输出比较不能用 32 位时基,必须把 T32(TxCON<3>)清零;二是切换模式前先把 OCM 清成 000 关掉通道,否则残留状态会让第一次比较就出错。手册在模式说明里专门写了这条建议。
与 MCPWM 的分工也很清楚:MCPWM 面向三相电机,带互补输出、硬件死区和专用故障引脚;输出比较只做到点翻转、到点出脉冲,适合电磁阀、计量泵、蜂鸣器、外部触发这类单路时序。这些活用 MCPWM 来做,通道和死区资源都浪费。
模式选择全部压在 OCxCON 的低三位上,先把这张表记住,现场看代码就不用翻手册。
| OCM<2:0> | 模式 | 引脚行为 | 典型用途 |
|---|---|---|---|
| 000 | 禁止 | 引脚交还 LAT/TRIS 控制 | 切换模式前必须先回到这里 |
| 001 | 单比较,置高 | 初始低,匹配后拉高并保持 | 上电使能、单次门控 |
| 010 | 单比较,置低 | 初始高,匹配后拉低并保持 | 定时关断 |
| 011 | 单比较,翻转 | 每个周期匹配点翻转一次 | 方波、时钟分频输出 |
| 100 | 双比较,单脉冲 | OCxR 定上升沿,OCxRS 定下降沿,只出一次 | 阀动作、超声波发射 |
| 101 | 双比较,连续脉冲 | 每个周期出一次脉冲 | 泵 PWM、脉冲串 |
| 110 | PWM,无故障引脚 | 占空比经缓冲寄存器更新 | 加热、调速 |
| 111 | PWM,带故障引脚 | OCFA/OCFB 为低时输出高阻 | 需要急停的功率级 |
OCM=011 时,引脚初始为低,之后每来一次匹配就翻转一次。麻烦在于复位后内部输出逻辑固定为 0,而翻转模式又允许软件指定初始状态。手册例 14-1 给的写法是:先写 OCM=001 把引脚钉在低电平,再把 bit1 置起来变成 011;想要初始高电平,就先写 010、再置 bit0。多这一步的意义是让引脚在进入翻转模式前有一个确定状态,否则上电第一拍是高是低要看运气。
中断标志也要按手册的时序理解:引脚动作后 2 个 TCY 才置 OCxIF,ISR 里必须软件清零;如果中断响应慢到跨过下一个匹配点,标志会再次置位,看起来像进了两次中断。用翻转模式测频率时,最好在 ISR 里只做计数,把业务处理放到主循环。
OCM=100 或 101 时,OCxR 与 OCxRS 分别对应脉冲的前沿和后沿,两者比的是同一个递增计数器 TMRy。想让脉冲按预期出现,必须满足 PRy ≥ OCxRS 且 OCxRS > OCxR;一旦越界,手册表 14-1 和表 14-2 列了几种“看似配置成功、实际没波形”的情况。
| 寄存器关系 | 实际现象 | 处理 |
|---|---|---|
| OCxR = 0 | 第一个周期不出脉冲,从第二次周期复位后才开始 | 脉冲整体延后一个周期,需要立即出脉冲就把 OCxR 设到 1 以上 |
| OCxR ≥ OCxRS | 上升沿在本周期,下降沿落到下一个周期,脉宽跨周期 | 保证 OCxRS > OCxR,否则脉宽等于周期减差值 |
| OCxRS > PRy | 只出现上升沿,没有下降沿;单脉冲模式不置 OCxIF | 把 PRy 调到不小于 OCxRS |
| OCxR > PRy | 不支持,计数器在匹配前已复位,引脚一直是低 | 先核算 TCY 与计数范围,别让比较值超出周期 |
按 10 MIPS、TCY=100 ns 算一组实际值:PR2 写 0x4000 得 16384 个计数,周期 1.6384 ms;OC1R 写 0x0800 是 204.8 µs 处起脉冲,再加 0x0400 个计数得到 102.4 µs 脉宽,OC1RS 就是 0x0C00。三个数都在 16 位范围内,且满足 0x0C00 小于 0x4000,这时示波器上才能看到干净的单个脉冲。
OCM=110 或 111 进入 PWM 后,OCxR 变成只读的占空比寄存器,用户只能写 OCxRS。周期匹配时硬件才把 OCxRS 装进 OCxR,这就是双缓冲:中途改占空比不会切出半个周期的怪波形。周期公式是(PRy+1)× TCY × 预分频比,占空比按 OCxR 的计数值算,0x0000 是 0%,大于 PRy 是 100%,等于 PRy 时只有一个计数周期为低。
手册例 14-6 用 PR2=0x00BF、OC1RS=0x0060,在 TCY=100 ns 下得到(191+1)×100 ns=19.2 µs,即 52.08 kHz,占空比 96/192 正好一半。这个例子值得抄下来当自检基准:频率对不上先查 TCY 和预分频,占空比不对先查是不是写错了寄存器。
| PWM 频率(10 MIPS) | 周期寄存器 | 可用精度 | PWM 频率(30 MIPS) | 可用精度 |
|---|---|---|---|---|
| 19 Hz | 0xFFFF(8 分频) | 16 位 | 57 Hz | 16 位 |
| 305 Hz | 0x7FFF | 15 位 | 916 Hz | 15 位 |
| 9.77 kHz | 0x03FF | 10 位 | 29.3 kHz | 10 位 |
| 78.1 kHz | 0x007F | 7 位 | 234 kHz | 7 位 |
这张表说明频率和分辨率是互相挤占的。10 MIPS 下要 20 kHz,按公式算可用计数约 2000 个,理论分辨率接近 11 位;如果直接抄 78.1 kHz 那档参数,只剩 7 位,占空比调一格就是 0.8%,加热丝上能看到明显台阶。选型阶段就该把目标频率代进公式,而不是写完代码再看波形。
OCM=111 才启用故障保护:OCFA 管通道 1 到 4,OCFB 管通道 5 到 8,引脚检测到逻辑 0 后,对应 PWM 输出立即进入高阻,且与器件时钟无关。这里有两个细节:故障引脚必须自己把 TRIS 位置 1 配成输入,使能 PWM 模式并不会替你改方向;OCFLT 是只读位,只有外部故障消失并且重新写 OCM 使能 PWM 时才清零,靠软件写 0 是清不掉的。
低功耗下,休眠时输出比较整体停摆,引脚保持进休眠前的电平;空闲模式则要看 OCSIDL,为 1 时模块停止,为 0 时模块继续工作,但还必须把对应时基的 TxSIDL 也清 0。手册设计技巧里那条“OCSIDL 没置 1,引脚却停了”,答案就在 TxSIDL 上,实际停的是定时器。
/* dsPIC30F,10 MIPS(TCY = 100 ns),OC1 用 Timer2 出单脉冲 */
void oc1_single_pulse(uint16_t rise, uint16_t width)
{
OC1CON = 0x0000; /* OCM=000:切换模式前先关通道 */
T2CON = 0x0000; /* TCKPS=1:1,T32=0,不能用 32 位时基 */
PR2 = 0x4000; /* 周期 = 16384 × 100 ns = 1.6384 ms */
OC1R = rise; /* 上升沿计数,如 0x0800 = 204.8 µs */
OC1RS = rise + width; /* 下降沿计数,必须不大于 PR2 */
IFS0bits.OC1IF = 0;
IEC0bits.OC1IE = 1; /* 下降沿后 2 TCY 置标志 */
OC1CON = 0x0004; /* OCM=100 单脉冲,OCTSEL=0 选 Timer2 */
T2CONbits.TON = 1; /* 启动时基 */
}
如果改成连续脉冲,把 OC1CON 写 0x0005;改成 PWM,写 0x0006 并把 OC1R/OC1RS 都设成占空比计数值。模式切换仍然要先回 0x0000,这一点在中断里动态改模式时尤其重要。
| 现象 | 根因 | 改法 |
|---|---|---|
| 完全没有波形 | OCM 还是 000,或 TON 没置 1,或 T32=1 | 按七步顺序核对,T32 必须为 0 |
| 只有上升沿没有下降沿 | OCxRS 大于 PRy | 把 PRy 调到不小于 OCxRS |
| 脉宽等于一个周期 | OCxR 大于等于 OCxRS,下降沿跨到下一周期 | 保证 OCxRS 大于 OCxR |
| 第一个脉冲晚一个周期 | OCxR 写成 0 | 需要立即动作就把 OCxR 设到 1 以上 |
| PWM 占空比改了没反应 | 写的是只读的 OCxR | 只写 OCxRS,等周期匹配后自动装载 |
| 空闲模式下输出停了 | OCSIDL 或 TxSIDL 为 1 | 两个位都清 0,模块才会在空闲模式继续跑 |
一句话总结:输出比较的价值在于把边沿交给硬件,软件只算三个数——上升沿、下降沿、周期;OCxR 与 OCxRS 的关系决定脉冲形状,PRy 决定它们能不能落在同一个周期里,先把这三个数的账算平,再谈中断和故障保护。
还想接着看:dsPIC30F2010 电机控制 PWM 死区与故障输入、dsPIC30F QEI 与输入捕捉接口、dsPIC33F 电流环 Q15 定点与饱和。
本文基于 Microchip《dsPIC30F 系列参考手册》DS70060C_CN 技术资料整理优化。
沧州艾诺威电子 — 国家高新技术企业,20+项国家专利
嵌入式系统开发 · 物联网方案 · AI智能硬件 · 一站式交付
电话:13930711029 | 邮箱:tech@czinv.com | 24小时内响应