使用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
相关产品推荐
相关产品推荐

