用C语言实现LED点阵屏的动态效果:16×16点阵上实现流畅动画滚动
16×16点阵屏跑滚动动画,卡顿和鬼影基本不是IO不够,而是“主循环delay刷屏+直接改显示数据”这套写法不对。正确结构是:字模只作数据源、显示缓冲隔离硬件、定时器中断做行扫描、主循环只改滚动偏移。
一、字模格式先统一
16×16单色点阵每字32字节,每行16点拆成2字节,行优先存。
取模规则必须固定成一条:
横向取模
高位在前(bit7对应最左列)
字节顺序和扫描代码一致
const uint8_t font_hi[] = { // “示”示例占位
0x00,0x00,0x00,0x00,0x00,0x3E,0x00,0x02,
/* ...共32字节... */
};
取模工具和驱动解析不匹配,字就左右镜像或断笔,先拿“实心行0xFF,0xFF”测一遍再上汉字。
二、显示缓冲:中断和主循环分开
直接把字模往屏上推,滚动时半截旧帧半截新帧=撕裂。开一块当前屏缓冲:
#define COL_BYTES 2
uint8_t disp_buf[16][COL_BYTES]; // 当前16行×16列
volatile uint8_t cur_row = 0;
行扫描用定时器中断,例如1ms一次,扫一行;16行扫完约16ms,整屏刷新约60Hz,肉眼不闪。
void TIM_IRQHandler(void){
row_off_all(); // 消隐,防鬼影
send_col_data(disp_buf[cur_row]); // 列数据进595
select_row(cur_row); // 开当前行
latch_595();
cur_row = (cur_row+1) & 0x0F;
}
中断里只做“取缓冲→发数据→切行”,别做位移、别做memcpy、别delay。
三、滚动本质:偏移截取
整条标语先做字模拼接缓冲,比如“HELLO”5字×16列=80列,每列16bit:
uint8_t msg_buf[80][COL_BYTES]; // 预展开成列
int scroll_off = 0; // 像素列偏移
刷新屏缓冲时,从msg_buf里按偏移取16列填进disp_buf:
void build_frame(void){
for(int r=0;r<16;r++){
for(int c=0;c<16;c++){
int src = scroll_off + c;
if(src < MSG_COLS)
disp_buf[r][c>>3] = msg_buf[src][r];
else
disp_buf[r][c>>3] = 0; // 空白填充
}
}
}
纯左移每帧scroll_off++即可;想平滑到“像素级”就用位偏移把相邻两列做移位或运算,别整字节跳。
四、速度别在中断里调
滚动节奏放主循环定时器或软计数:
void app_1ms_tick(void){
static uint16_t cnt=0;
if(++cnt >= 30){ // 约30ms移一列
cnt=0;
scroll_off++;
if(scroll_off >= MSG_COLS) scroll_off=0;
build_frame();
}
}
中断只管刷屏,主循环只管“下一帧内容”,两者解耦后CPU还能同时扫按键、解串口。
五、流畅的几个硬点
消隐必须做:切行前关所有行,不然旧列数据在新行亮一下出鬼影
双缓冲可选:build_frame写到back buf,中断切换front指针,滚动更稳
取模方向错:字镜像,先测单行
中断里算位移:扫描周期被拉长→闪
共阳/共阴取反:列数据按硬件反相,别在字模里硬改逻辑
74HC595时序:DS稳定→SHCP上升沿→STCP锁存,别把列数据和行选同时打
亮度不均:每行点亮时间要等长约1ms,别前面行多刷后面行少刷
六、最小落地顺序
静态显示一个汉字:字模→disp_buf→中断扫描
确认无鬼影、无镜像
多字拼msg_buf
主循环改scroll_off
build_frame截取
调速、加留白、循环复位
16×16点阵流畅滚动的核心就一句:字模是源,disp_buf是当前帧,中断只搬运,滚动只是偏移量往前推。主循环动内容、中断动时序,两边不抢同一块数据,动画就顺;反过来在中断里直接改字模、在主循环里delay刷屏,多少优化都救不回来。





