You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用AVR定时器、Fast PWM及中断驱动WS2812B的问题排查

用AVR定时器中断驱动WS2812B的可行性排查

我清楚WS2812B灯带对时序精度要求极高,通常采用汇编代码或成熟开源库实现驱动,但仍想尝试验证:**是否真的无法通过AVR定时器+中断来驱动该灯带?**毕竟Atmega328p的官方手册明确指出,定时器可实现精准时序与波形生成。

我使用搭载16MHz Atmega328p的Arduino Nano,计划通过Timer0生成位bang信号驱动灯带。参考light_ws2812库的有效时序计算得出:

  • 短周期(T0H或T1L)为350ns,对应约5个定时器步长
  • 长周期(T1H或T0L)为900ns,对应约14个定时器步长

基于此,我将Timer0配置为Atmega328p手册中的模式7(Fast PWM模式),通过OCR0A定义TOP值为19(14+5),根据待发送的比特值将OCR0B设置为5或14,并通过计数器溢出中断实时修改OCR0B的值。

首次尝试点亮一个红色LED,编写了如下代码:

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


#define T0H_t 5
#define T1H_t 14
#define TOTAL_t (T0H_t+T1H_t)

#define GET_TIME(B) (B & 1? T1H_t : T0H_t)

#define MAX_LEDS 10
#define BUFFER_SIZE (3*MAX_LEDS)


/**
 * @brief Buffer for GRB values to be sent via bit-banging.
 * 
 * Bytes are sent in MSBF. When all bytes are sent, buffer should get reset.
 */
struct sending_buffer 
{
    uint8_t buffer [BUFFER_SIZE];   // stores RGB bytes to be sent

    uint8_t byte_index;             // index to byte being sent
    uint8_t bit_index;              // index to next bit to be sent
    uint8_t last_byte;              // index of last byte + 1
};

struct sending_buffer colors_to_send;


struct sending_buffer * init_buffer(struct sending_buffer * sb)
{
    sb->byte_index = 0;
    sb->bit_index = 7;
    sb->last_byte = 0;

    return sb;
}

// returns the bit value of next bit to be sent from sb
uint8_t get_next_bit(struct sending_buffer * sb)
{
    uint8_t bit = (sb->buffer[sb->byte_index] >> sb->bit_index--) & 1;

    sb->bit_index = sb->bit_index > 7 ? 7 : sb->bit_index ; // Bytes are sent in MSBF
    if(sb->bit_index == 7) 
    {
        sb->byte_index++;
        sb->byte_index %= BUFFER_SIZE;
    }

    return bit;
}

uint8_t put_byte(struct sending_buffer * sb, uint8_t byte)
{
    sb->buffer[sb->last_byte++] = byte;
}

uint8_t send_color(uint8_t r, uint8_t g, uint8_t b)
{
    put_byte(&colors_to_send, g);
    put_byte(&colors_to_send, r);
    put_byte(&colors_to_send, b);
}

void init_send()
{
    if(colors_to_send.byte_index < colors_to_send.last_byte)
    {
        uint8_t next_bit = get_next_bit(&colors_to_send);
        OCR0B = GET_TIME(next_bit);
    }
    sei();
}

void setup_interrupt()
{
    /**
     * @brief Setting up Fast PWM mode with TOP value defined by OCR0A ; clearing OC0B on c-m between OCR0B and TCNT0.
     * 
     */

    // Set Fast PWM mode with TOP value defined by OCR0A
    TCCR0A |= (1 << WGM00) | (1 << WGM01);
    TCCR0B |= (1 << WGM02);

    // Set COM0B1 to clear OC0B on compare match
    TCCR0A |= (1 << COM0B1);

    // Set timer without prescaler (CS00)
    TCCR0B |= (1 << CS00);

    // Set TOP value
    OCR0A = TOTAL_t; // Assuming TOTAL_t is correctly calculated elsewhere

    // Set initial value of OCR0B to 0 (clear OC0B)
    OCR0B = 0;

    // Set pin 5 (OC0B) as output
    DDRD |= (1 << PD5);

    // Enable interrups
    TIMSK0 |= (1 << TOIE0);
}


ISR(TIMER0_OVF_vect)
{
    if(colors_to_send.byte_index < colors_to_send.last_byte)
    {
        uint8_t next_bit = get_next_bit(&colors_to_send);
        OCR0B = GET_TIME(next_bit);
    }
}

int main()
{
    _delay_ms(10);
    init_buffer(&colors_to_send);

    send_color(255,0,0);

    setup_interrupt();
    init_send();
    while(colors_to_send.byte_index < colors_to_send.last_byte)
    ;
    cli();
    OCR0B = 0;
    _delay_ms(10);

    while(1);

    return 0;
}

目前这段代码无法正常工作,想请教:是代码存在错误(若有,错误位置在哪),还是这种驱动WS2812B的方法本身就不可行?


内容的提问来源于stack exchange,提问作者grybouilli

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 00:10:54