使用ATMEGA128产生任意PWM信号

时间:2016-01-20 09:43:19

标签: microcontroller avr atmega pwm

我熟悉Atmega128及其家用微控制器中的PWM生成。我一直在使用预标量和其他寄存器来生成频率。但我必须生成20KHz的pwm信号。我试过,但我无法得到所需的输出。谁能建议我或帮我怎么做?

据我所知,在atmega128中,1条指令需要1个周期。使用16MHz晶振,1指令在1 / 16M秒内完成。 我尝试用25us占空比产生20Khz信号(50 us)。但是我在示波器中获得的频率(277.78 Hz)远低于20KHz 我的计算是 16MH = 20000Hz * 800。 对于0-399计数,我使端口高和 399-799算,我把港口降低了。

void frequency(void){   // 20kHz Frequency  
    if (cnt1 <= 399){
        PORTB |= (1<<7);
    } else {
        PORTB &= ~(1<<7);
    }
    cnt1++;
    if (cnt1 >= 800)    cnt1 = 0;
}

2 个答案:

答案 0 :(得分:1)

我无法访问128但已验证其16位定时器1与328和32U4类似,因此下面的内容应该稍作修改(主要的关键点可能是查找什么引脚溢出寄存器绑定到):

#include <avr/io.h>
#include <util/delay.h>

struct CTC1
{
    static void setup()
    {
        // CTC mode with TOP-OCR1A

        TCCR1A = 0;
        TCCR1B = _BV(WGM12);

        // toggle channel A on compare match

        TCCR1A = (TCCR1A & ~(_BV(COM1A1) | _BV(COM1A0))) | _BV(COM1A0);

        // set channel A bound pin PB1 to output mode

#if defined(__AVR_ATmega32U4__)
        DDRB |= _BV(5);
#else
        DDRB |= _BV(1);
#endif
    }

    static void set_freq(float f)
    {
        static const float f1 = min_freq(1), f8 = min_freq(8), f64 = min_freq(64), f256 = min_freq(256);

        uint16_t n;

        if (f >= f1)        n = 1;
        else if (f >= f8)   n = 8;
        else if (f >= f64)  n = 64;
        else if (f >= f256) n = 256;
        else                n = 1024;

        prescale(n);

        OCR1A = static_cast<uint16_t>(round(F_CPU / (2 * n * f) - 1));
    }

    static void prescale(uint16_t n)
    {
        uint8_t bits = 0;

        switch (n)
        {
            case    1:  bits = _BV(CS10);               break;
            case    8:  bits = _BV(CS11);               break;
            case   64:  bits = _BV(CS11) | _BV(CS10);   break;
            case  256:  bits = _BV(CS12);               break;
            case 1024:  bits = _BV(CS12) | _BV(CS10);   break;
            default:    bits = 0;
        }

        TCCR1B = (TCCR1B & ~(_BV(CS12) | _BV(CS11) | _BV(CS10))) | bits;
    }

    static inline float min_freq(uint16_t n)
    {
        return ceil(F_CPU / (2 * n * 65536));
    }
};

void setup()
{
    CTC1::setup();
    CTC1::set_freq(20e3);
}

void loop()
{
    // do whatever
    _delay_ms(1);
}

int main()
{
    setup();
    for (;;)
        loop();
}

我在我的示波器上进行了测试,并测量了在16MHz运行的328p时的20kHz。如果20kHz是您需要的唯一频率,那么您可以大大简化这一过程。翻译代码以使用其中一个8位定时器也很简单,虽然我还没有确认它可以用这些定时器达到20kHz。

答案 1 :(得分:0)

在C中使用计数器实现PWM或任何时间关键确实不是一个好主意。虽然C将您的代码转换为特定的机器代码,但您并不知道需要多长时间。 您的代码转换为:

services.AddMvc(config =>
{
    var policy = new AuthorizationPolicyBuilder()
                     .RequireAuthenticatedUser()
                     .Build();

    config.Filters.Add(new AuthorizeFilter(policy));
});

,而是像这样(简化,人类可读):

[HttpGet]
[AllowAnonymous]
public string Get(int id)
{
}

如果你从“C”的角度来看,你需要做至少

make port B high 400 times (PORTB |= (1<<7);)
make port B low 400 times (PORTB &= ~(1<<7);)

然后依赖于编译器优化所有加载和写入的好处(通常非常好)。 如果您真正了解编译器并且不使用太多优化(通常是复杂的),或者通过在汇编程序中编写代码,您可以控制延迟。但是你必须使用类似于我对机器代码的解释的逻辑(汇编程序接近人类可读的机器代码)。

我认为您的解决方案是计时器中断。 atmega128有一个很好的教程here

你的意思是:

  

我尝试以25us占空比生成20Khz信号(50 us)。

你的意思是20kHz信号,50%占空比?所以25us低,25 us高?

如果是这种情况,您可以使用一个定时器中断和一个(二进制)计数器来执行此操作。 确切地说,您可以在提供的链接中阅读“8位计时器示例”。