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

16*16 LED矩阵搭配ATtiny85使用rgb结构体时显示异常求助

16×16 LED矩阵搭配ATtiny85显示异常问题排查

问题背景

使用16×16 LED矩阵与ATtiny85开发板,需求为每行点亮对应行号位置的LED(暂不考虑蛇形排列)。最初使用byte matrix[16][16]手动设置单元格数值时显示正常,但替换为rgb matrix[16][16](结构体定义struct rgb {byte r, g, b})后,显示出现异常。

LED驱动核心代码

#define LED PB0
#define byte unsigned char

struct rgb {
    byte r;
    byte g;
    byte b;
};

// WS2812时序参考:HIGH 0.8µs ±150ns,LOW 0.45µs ±150ns;LOW 0.4µs ±150ns,HIGH 0.85µs ±150ns
// 8MHz时钟下,1周期=125ns

void setBitHigh(byte pin) {
    PORTB |= _BV(pin); // 1周期=125ns
    asm("nop");
    asm("nop");
    asm("nop");
    asm("nop");
    asm("nop"); // 累计0.75µs

    PORTB &= ~_BV(pin); // 1周期
    asm("nop");
    asm("nop");
    asm("nop"); // 累计0.5µs
}

void setBitLow(byte pin) {
    PORTB |= _BV(pin); // 1周期
    asm("nop");
    asm("nop"); // 累计0.375µs

    PORTB &= ~_BV(pin); // 1周期
    asm("nop");
    asm("nop");
    asm("nop");
    asm("nop");
    asm("nop");
    asm("nop"); // 累计0.875µs
}

void trueByte(byte pin, byte intensity) {
    for (int i = 7; i >= 0; i--) {
        (intensity & _BV(i)) ? setBitHigh(pin) : setBitLow(pin);
    }
}

void falseByte(byte pin) {
    for (int i = 0; i < 8; i++) {
        setBitLow(pin);
    }
}

void setPixel(byte pin, rgb color) {
    DDRB |= _BV(pin);

    (color.g > 0) ? trueByte(pin, color.g) : falseByte(pin);
    (color.r > 0) ? trueByte(pin, color.r) : falseByte(pin);
    (color.b > 0) ? trueByte(pin, color.b) : falseByte(pin);
}

显示正常的代码及效果

代码

void display(byte matrix[WIDTH][HEIGHT]) {
    for (byte rowIdx = 0; rowIdx < WIDTH; rowIdx++) {
        for (byte cellIdx = 0; cellIdx < HEIGHT; cellIdx++) {
            setPixel(LED, {matrix[rowIdx][cellIdx], 0, 0});
        }
    }
}

byte m[WIDTH][HEIGHT] = {
    { 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    { 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    { 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    // ... 剩余行省略
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0},
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15}
};

int main() {
    display(m);
}

效果

显示正常效果

显示异常的代码及效果

代码

void display(rgb matrix[WIDTH][HEIGHT]) {
    for (byte rowIdx = 0; rowIdx < WIDTH; rowIdx++) {
        for (byte cellIdx = 0; cellIdx < HEIGHT; cellIdx++) {
            setPixel(LED, matrix[rowIdx][cellIdx]);
        }
    }
}

rgb m[WIDTH][HEIGHT] = {
    {
            {15, 0, 0},
            {0, 0, 0},
            {0, 0, 0},
            {0, 0, 0},
            {0, 0, 0},
            {0, 0, 0},
            {0, 0, 0},
            {0, 0, 0},
            {0, 0, 0},
            {0, 0, 0},
            {0, 0, 0},
            {0, 0, 0},
            {0, 0, 0},
            {0, 0, 0},
            {0, 0, 0},
            {0, 0, 0}
    },
    // ... 剩余行省略
};

int main() {
    display(m);
}

效果

显示异常效果

解决建议

1. 核心问题:SRAM内存溢出

ATtiny85的SRAM仅512字节,16×16的rgb数组需要16×16×3=768字节,远超可用内存上限,导致内存溢出,破坏程序数据与执行流程,这是显示异常的主要原因。

解决方法:

  • 使用PROGMEM存储数组:将数组放到Flash内存(ATtiny85有8KB Flash,足够容纳),读取时通过AVR的PROGMEM函数获取数据:
    #include <avr/pgmspace.h>
    
    // 定义数组到Flash
    const rgb m[WIDTH][HEIGHT] PROGMEM = {
        // 初始化内容不变
    };
    
    void display() {
        rgb color;
        for (byte rowIdx = 0; rowIdx < WIDTH; rowIdx++) {
            for (byte cellIdx = 0; cellIdx < HEIGHT; cellIdx++) {
                // 从Flash读取颜色数据
                color.r = pgm_read_byte(&m[rowIdx][cellIdx].r);
                color.g = pgm_read_byte(&m[rowIdx][cellIdx].g);
                color.b = pgm_read_byte(&m[rowIdx][cellIdx].b);
                setPixel(LED, color);
            }
        }
    }
    
  • 动态生成像素数据:无需预先存储数组,在循环中实时判断并生成颜色,完全节省数组内存:
    void display() {
        for (byte rowIdx = 0; rowIdx < WIDTH; rowIdx++) {
            for (byte cellIdx = 0; cellIdx < HEIGHT; cellIdx++) {
                rgb color = {0,0,0};
                if (cellIdx == rowIdx) {
                    color.r = 15;
                }
                setPixel(LED, color);
            }
        }
    }
    

2. 优化结构体参数传递

setPixel当前采用值传递rgb结构体,会额外复制3个字节的数据,可能影响WS2812的时序精度。建议改为指针传递,减少内存操作:

void setPixel(byte pin, const rgb* color) {
    DDRB |= _BV(pin);

    (color->g > 0) ? trueByte(pin, color->g) : falseByte(pin);
    (color->r > 0) ? trueByte(pin, color->r) : falseByte(pin);
    (color->b > 0) ? trueByte(pin, color->b) : falseByte(pin);
}

// 调用时传地址
setPixel(LED, &color);

3. 稳定WS2812时序

手动添加nop的时序控制容易受编译器优化、额外代码指令影响,建议用纯汇编实现字节发送,或使用定时器精确控制电平时长,避免时序偏差导致的LED解析错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 16:17:34