【问题标题】:change PWM duty cycle from within ISR从 ISR 中更改 PWM 占空比
【发布时间】:2023-03-04 12:59:01
【问题描述】:

我在实现抖动功能以升级我的 ATmega88 的 PWM 分辨率和它控制的 LED 时遇到一些问题。 我的想法是,使用“一种”浮点数,由两个 uint8_t 作为“小数位”组成,例如“255.31”将由“0xff.0x1f”(BYTE_HIGH.BYTE_LOW)表示。

现在我希望 OCR 中的值在 BYTE_HIGH 和 BYTE_HIGH+1 之间抖动,为此我需要第二个计数器(除了定时器本身)来生成覆盖的占空比(定时器的占空比必须在之后递增(int) 255-BYTE_LOW 周期)。但是在 main() 中管理这些东西会导致闪烁,因此我想在称为计时器溢出的 ISR 中执行此操作。

我的 avr 以 20 MHz 运行,为了获得足够的抖动频率,我不能使用高于 8 的预分频器。您认为我可能过于频繁地调用 ISR 吗?但是 ISR 中没有太多代码。

我能想到的另一个问题是,ISR 必须写入可能被计时器同时读取的 OCR,但这不是这种“线程保存”,因为计时器从不写入 OCR ?

我没有适当地找到任何与我的问题有关的东西,所以我希望得到一些提示。请参阅下面的代码

#include <avr/interrupt.h>
#include <avr/io.h>

#define compare_register OCR2B  //duty cycle

volatile uint8_t dither_count=0;
volatile uint8_t BYTE_HIGH=0;
volatile uint8_t BYTE_LOW=0;

void init_pwm(void){

//some standard pwm initializations
TCCR2A |= (1 << COM2A1);
TCCR2A |= (1 << COM2B1);
// non inverting, fast PWM mode and prescaler to
TCCR2A |= (0 << WGM21) | (1 << WGM20);
TCCR2B |= (1 << CS21);
// set desired pin as an output (not sure, if it's the correct pin, because I left out other pins in use)
DDRD = (1 << DDD3)|(1<<DDD4);

// enable interrupt on timer overflow
TIMSK2 |= (1<<TOIE2);

// enable global interrupts
  sei();
}

ISR(TIMER2_OVF_vect){
  dither_count++;
  if(dither_count<=BYTE_LOW){
    compare_register=BYTE_HIGH+1;
  }else{
    compare_register=BYTE_HIGH;
  }
}

int main(){
init_pwm();
//this or any function dimming the leds
  BYTE_HIGH=10;
  BYTE_LOW=1;
}

这理论上应该可行,但不幸的是它在实际中不起作用。 ISR 被调用,但未按预期运行。我是否在 ISR 中正确调用了变量? 如果有人能看到我犯的一个根本性错误,我会很高兴,尤其是因为我没有任何串行接口可用于调试......

编辑:同时我发现,ISR 显然卡住了,会阻塞在我的 main 函数中执行的任何代码......

【问题讨论】:

  • 你的意思是if (dither_count == 255)?请注意,测试是无用的:dither_count++ 无论如何都以 256 为模。
  • @EdgarBonet 你是对的,但我不知道,谢谢!您看到一般错误吗?
  • @EdgarBonet 这个顺便说一句不成功,我刚刚试过......

标签: avr pwm isr dithering


【解决方案1】:

我在 Arduino Uno 上尝试了您的程序。该板是基于 ATmega328P,除了一些额外的内存,基本相同 你的 Mega88(相同的数据表)。我首先在 main()结束:

init_pwm();

// Blink the LED on PB5.
DDRB |= _BV(PB5);
for (;;) {
    PINB |= _BV(PB5);
    _delay_ms(400);
}

PWM 工作显然需要第一行。 LED 闪烁 只是测试程序是否卡在 ISR 中。 结果是:没有。 LED 按预期闪烁。脉宽调制 输出在示波器上看起来也不错。请注意,与 代码中的注释说,您正在 阶段配置计时器 正确 PWM 模式,而不是快速 PWM。那么定时器周期就是2×255定时器 时钟周期而不是 256。

我反汇编了二进制文件并尝试计算 CPU 周期数 需要服务中断。程序编译在-Os 优化级别,我得到了大约 50。鉴于中断每次触发 2×255×8 = 4080 个周期,大约是 CPU 功率的 1.2% 花费在服务中断上。

【讨论】:

  • 感谢您的回复!对我来说,这听起来并不像是程序不应该工作的原因。我意识到在这种情况下我使用的是相位校正 PWM,对于不正确的评论感到抱歉。如果我需要由所有 3 个计时器触发此中断,这将需要大约 5% 的 CPU,并且将有足够的容量来处理 TWI 通信。你同意吗?您知道在此 ISR 中更改占空比时可能出现的任何问题吗?
  • @SimSon:在三个计时器上执行此操作应该没有问题。从 ISR 中更改占空比也没有问题:这是正常的事情,并且计时器旨在处理它。我对你的问题的问题是你在问“怎么了?”关于一个运行良好的程序(除了被遗忘的init_pwm();)。
  • 嗯,只是我的 LED 似乎没有以小于 1/255 的步长变暗。当我设置 BYTE_HIGH=0, BYTE_LOW=128 时,我希望它们看起来是常规 PWM 设置为 1 时亮度的一半。实际上并非如此。我将审查我的其余代码并尽快报告!同时感谢您的宝贵时间!
  • 设置 BYTE_HIGH=1BYTE_LOW=128 我可以在示波器上看到脉冲在 1 µs 和 2 µs 之间交替。
  • 好的,这正是我所期望的行为。嗯,也许我的问题应该是“我在哪里可以找到合适的眼镜”......我会尽快尝试。看来我绝对可以排除 ISR 是我的问题。非常感谢,埃德加博内特!!!
猜你喜欢
  • 2017-01-04
  • 1970-01-01
  • 2018-03-26
  • 1970-01-01
  • 1970-01-01
  • 2017-09-14
  • 2014-07-14
  • 2013-05-14
  • 2011-09-07
相关资源
最近更新 更多