## 一、为什么需要进阶? 普通 Arduino 编程中,`delay()` 和 `analogWrite()` 用起来简单,但无法满足高精度实时控制的需求。定时器中断让你获得微秒级的执行节奏,硬件 PWM 提供无 jitter 的波形,PID 则是闭环控制的灵魂。三者结合,才能构建真正的运动控制、温控或平衡机器人。 ## 二、定时器中断原理与配置 AVR 芯片(如 ATmega328P)内置 Timer0、Timer1、Timer2。其中 Timer0 已被 Arduino 的 `millis()` 占用,所以进阶应用推荐使用 **Timer1(16位)** 或 **Timer2(8位)**。 ### 1. CTC 模式(Clear Timer on Compare Match) 计数器从 0 递增到 OCRnx 时,自动清零并触发比较匹配中断。中断频率公式: `f_interrupt = F_CPU / (2 * N * (OCRnA + 1))` N 为预分频系数(1、8、64、256、1024)。 ### 2. 配置 Timer1 产生 1kHz 中断 ```c void setupTimer1() { cli(); // 关全局中断 TCCR1A = 0; // 正常端口操作 TCCR1B = 0; TCNT1 = 0; // 计数器清零 OCR1A = 1999; // 16MHz / (2*8*2000) = 1kHz TCCR1B |= (1 << WGM12); // CTC 模式 TCCR1B |= (1 << CS11); // 预分频 8 TIMSK1 |= (1 << OCIE1A); // 使能比较匹配中断 sei(); // 开全局中断 } ISR(TIMER1_COMPA_vect) { // 放置实时任务,如 PID 计算、传感器采样 pidCompute(); } ``` ## 三、硬件 PWM:超越 analogWrite `analogWrite()` 实际是软件模拟 PWM,频率固定约 490Hz,且占用定时器中断资源。进阶场景应使用硬件 PWM,通过寄存器直接配置,可调整频率、极性和分辨率。 ### 1. Timer1 的 PWM 配置(相位修正模式) ```c void setupPWM() { // 设置 PB1 (Pin 9) 为输出 DDRB |= (1 << PB1); // 相位修正 PWM,顶部值 ICR1 ICR1 = 159; // 20kHz 基准频率 TCCR1A |= (1 << WGM11); TCCR1B |= (1 << WGM13); // 非反向 PWM,输出 PB1 TCCR1A |= (1 << COM1A1); // 预分频 1,PWM 频率 = 16MHz / (2*160) ≈ 50kHz TCCR1B |= (1 << CS10); OCR1A = 80; // 占空比 50% } ``` 注意:使用 Timer1 PWM 时,其比较中断会与 PWM 共享同一寄存器,需合理规划。推荐 **Timer1 只用于 PWM,Timer2 用于中断** 以避免冲突。 ### 2. 标准库 API 与自定义兼容 Arduino 的 `analogWriteFrequency()`(仅限 Due/Zero)在 AVR 上不可用。若想变更频率,必须直接操作寄存器。这也意味着你的代码不再跨平台,但换来的是性能和可控性。 ## 四、PID 控制核心实现 PID 在嵌入式端通常采用位置式或增量式。增量式输出控制量增量,适合电机调速;位置式输出绝对值,适合舵机或加热器。以下为增量式 PID 结构: ```c struct PID { float Kp, Ki, Kd; float target; float integral; float prevError; }; float pidUpdate(struct PID *pid, float measurement, float dt) { float error = pid->target - measurement; float P = pid->Kp * error; pid->integral += error * dt; float I = pid->Ki * pid->integral; float derivative = (error - pid->prevError) / dt; float D = pid->Kd * derivative; pid->prevError = error; return P + I + D; } ``` ### 在中断中调用 PID 将 PID 计算放入定时器中断,保证固定采样周期,避免 `delay()` 导致的时间漂移。注意中断中的代码要短且快,避免浮点运算过重(可改用浮点但保持简单)。 ## 五、完整示例:直流电机速度闭环 电路:直流电机 + 编码器(或霍尔传感器)接入 Pin 2(中断0),PWM 输出 Pin 9(Timer1)。目标转速设定后,通过 PID 调整 PWM 占空比。 ```c // 引脚定义 #define ENC_PIN 2 #define PWM_PIN 9 volatile long encoderCount = 0; float targetRPM = 3000; float pwmValue = 0; // 编码器中断,每转 20 脉冲,测速周期 10ms ISR(INT0_vect) { encoderCount++; } // 精确 2ms 定时器中断 (Timer2 CTC) void setupTimer2() { TCCR2A = 0; TCCR2B = 0; TCNT2 = 0; OCR2A = 124; // 16MHz / (128 * (125)) = 1kHz → 1ms TCCR2A |= (1 << WGM21); // CTC TCCR2B |= (1 << CS22); // 预分频 64 TIMSK2 |= (1 << OCIE1A); // 注意:Timer2 比较 A } volatile int pidTickCounter = 0; ISR(TIMER2_COMPA_vect) { pidTickCounter++; if (pidTickCounter >= 5) { // 5ms 执行一次 PID pidTickCounter = 0; // 测速:每 5ms 的脉冲数换算 RPM static long prevCount = 0; long diff = encoderCount - prevCount; prevCount = encoderCount; float rpm = (diff / 20.0) / (0.005) * 60.0; // 20 脉冲/转 // PID 计算 float error = targetRPM - rpm; static float integral = 0; static float prevErr = 0; integral += error * 0.005; float output = 0.05 * error + 0.01 * integral + 0.001 * (error - prevErr)/0.005; prevErr = error; // 输出限制 0-255 output = constrain(output, 0, 255); OCR1A = output; // 直接写 PWM 寄存器 } } void setup() { pinMode(ENC_PIN, INPUT_PULLUP); pinMode(PWM_PIN, OUTPUT); // 外部中断 0 触发方式:上升沿 EICRA |= (1 << ISC01); EIMSK |= (1 << INT0); setupTimer2(); // 启动定时器中断 setupTimer1PWM(); // 配置 Timer1 PWM,见上文 Serial.begin(115200); } void loop() { // 主循环只负责显示和参数调整 static unsigned long lastPrint = 0; if (millis() - lastPrint > 100) { lastPrint = millis(); Serial.println(targetRPM); } } ``` ## 六、注意事项与调参建议 - **中断优先级与嵌套**:AVR 中断不支持嵌套。若有多个中断,必须保证 ISR 执行时间小于最短中断周期,否则会丢事件。 - **共享寄存器冲突**:Timer1 的 OCR1A 同时控制 PWM 比较值,若还需使用比较中断,应使用 OCR1B。本例仅用 PWM,故安全。 - **浮点性能**:ATmega328P 无硬件浮点单元。PID 计算中的浮点运算在 8MHz 下耗时较长,可将采样周期放宽至 5ms 以上,或改用整数定标 PID。 - **抗积分饱和**:输出被钳位时,应暂停积分累加,防止系统恢复时超调。 - **编码器噪声**:电机电刷会产生电磁干扰,务必在编码器电源端加去耦电容,信号线使用屏蔽双绞线。 - **调试方法**:先用固定 PWM 开环验证电机响应,再引入 PID。调节时先调 P,再调 I,最后用 D 抑制振荡。 ## 七、总结 通过寄存器级编程,Arduino 不再局限于学习玩具,而能胜任实时控制任务。定时器中断提供了精确的时间基准,硬件 PWM 保证了输出质量,PID 算法则让系统拥有自适应调节能力。掌握这三者,你便能向自平衡机器人、温控仪、云台稳定器等复杂项目迈出一大步。