1 基于51单片机的LED点阵汉字显示系统设计
点击链接下载protues仿真设计资料:https://download.csdn.net/download/m0_51061483/91926461
1.1 系统概述与设计目标
LED点阵屏因其亮度高、显示直观、成本低、耐用性强等特点,被广泛应用于公交站牌、商场字幕屏、车载信息屏、门头招牌以及各类仪器设备的人机界面。对于单片机课程设计而言,点阵显示系统能综合训练学生在硬件接口、时序控制、显示扫描、数据组织与动态效果实现等方面的能力,具有很强的代表性和实践价值。
本设计以51单片机为核心控制器,采用4块16×16 LED点阵模块构成横向拼接显示区域,实现汉字“毕业快乐”的静态显示与滚动显示两种模式。系统要求在静态显示模式下,4块点阵分别显示“毕”“业”“快”“乐”四个汉字,字形饱满、亮度均匀;在滚动显示模式下,采用逐列移位的方式实现从右向左的平滑滚动效果,呈现类似公交站LED字幕屏的动态展示体验。
为了达到“字形饱满、亮度均匀、滚动平滑”的要求,系统必须在电路与程序上重点解决以下关键问题:
- 点阵扫描方式选择:16×16点阵一般采用行列扫描,若拼接4块,需要更合理的扫描策略与数据组织方式。
- 驱动能力与接口扩展:直接用51单片机IO驱动大规模LED会出现IO不足、驱动电流不足的问题,需引入锁存器/移位寄存器/行列驱动器等模块。
- 显示刷新与亮度控制:扫描刷新频率要足够高,以避免闪烁;同时要保证各列/各行占空比一致,使亮度均匀。
- 字库与显示缓存设计:汉字点阵数据需要以合适的数据结构存储,静态显示要快速刷新,滚动显示要高效移位并拼接四字序列。
- 滚动算法实现:逐列滚动要求将整条字幕视为一个连续的“列流”,在每次滚动时更新显示缓存,实现平滑移动。
2 系统功能设计
2.1 功能需求分析
系统主要功能由静态显示与滚动显示两部分组成,同时为了呈现稳定字幕屏效果,还必须具备基础的显示驱动与刷新机制。
2.1.1 静态显示功能
- 在4块16×16点阵模块上同时显示4个汉字:
- 第1块显示“毕”
- 第2块显示“业”
- 第3块显示“快”
- 第4块显示“乐”
- 字形要求:
- 笔画完整,不缺笔断线
- 比例协调,尽量居中
- 显示效果要求:
- 亮度均匀,不出现某些行/列明显更亮或更暗
- 无肉眼可见闪烁
静态显示的本质是:显示内容不变化,但系统仍需持续高速扫描刷新点阵,否则LED会熄灭或闪烁。因此静态显示依赖于稳定的扫描程序与恒定的数据源。
2.1.2 滚动显示功能
滚动显示需要将“毕业快乐”作为一个连续字符串,在4块点阵构成的横向屏幕上从右向左平滑滚动。滚动方式采用“逐列移位”,意味着:
- 每次滚动步进为1列像素(而不是整字跳动),平滑度更高。
- 在滚动过程中,“毕”“业”“快”“乐”会从右侧逐列进入显示区域,左侧逐列移出。
- 要实现类似公交站LED屏的感觉,滚动速度必须可控,且刷新与滚动应分离:
- 刷新频率高:保证无闪烁
- 滚动更新频率低:保证运动速度合适
滚动显示的实现关键在于“显示缓存”与“源数据流”的组织:必须将整个字幕的点阵数据按列拼接,滚动时从源数据取出下一列补到右端,并将缓存整体左移。
2.2 设计指标与性能要求
为了保证显示效果,本系统可设定如下建议指标(作为设计说明内容的丰富补充):
- 显示刷新频率:≥100Hz(整屏刷新),肉眼基本无闪烁。
- 行扫描方式:16行扫描,每行点亮时间一致。
- 滚动速度:每列移动间隔约20ms80ms可调,常见字幕屏约30ms50ms较舒适。
- 字库分辨率:16×16汉字点阵,满足笔画表现力。
- 显示宽度:4×16=64列,显示高度16行。
3 系统电路设计
3.1 硬件总体结构
系统硬件采用模块化设计,主要由以下模块组成:
- 51单片机最小系统模块
- LED点阵显示模块(4块16×16点阵)
- 列数据输出与锁存模块(移位寄存器/锁存器)
- 行选通驱动模块(译码器/行驱动晶体管阵列)
- 电流驱动与限流模块(确保亮度与保护IO)
- 电源模块(5V供电、去耦与滤波)
- 可选:模式切换按键模块(若需要静态/滚动切换,可扩展)
本题目未要求按键切换,但在课程设计中常见“默认滚动 + 静态展示”或用按键切换模式。即便不加入按键,系统仍可在程序中按时间自动切换显示模式,使演示效果更丰富。
3.2 51单片机最小系统模块
3.2.1 模块功能
51单片机负责:
- 输出点阵列数据(通过移位寄存器或并行锁存)
- 控制行扫描时序
- 完成显示刷新任务
- 管理显示缓存与字模数据
- 实现滚动算法(逐列移位与新列补入)
3.2.2 关键电路要点
- 时钟电路:通常使用12MHz或11.0592MHz晶振,保证定时器计数稳定。
- 复位电路:上电复位+按键复位,便于调试。
- 去耦电容:单片机VCC与GND附近放置0.1uF,抑制数字开关噪声。
- IO规划:点阵驱动需要较多控制信号,必须利用扩展芯片减少占用:
- 串行输出:数据(DATA)、时钟(CLK)、锁存(LATCH)即可驱动大量列数据
- 行选择:可用译码器(如74HC138)减少行控制IO占用
3.3 LED点阵显示模块(4块16×16)
3.3.1 模块功能与组成
16×16 LED点阵模块由16行×16列LED组成,每个像素点由行与列交叉决定点亮。点阵通常有两种极性结构:
- 共阳行/共阴列
- 共阴行/共阳列
无论哪种结构,本质都是通过扫描点亮:
- 在某一时刻选通一行(或一列)
- 同时在列端输出该行需要点亮的像素数据
- 快速切换行/列,利用视觉暂留形成完整图像
4块16×16拼接后形成64×16显示区域:
- 宽度:64列(适合显示长字幕与滚动)
- 高度:16行(满足16×16汉字字模)
3.3.2 亮度均匀性与扫描占空比
采用行扫描时,每次只点亮1行,单个LED的占空比约为1/16,因此为了亮度足够:
- 驱动电流需适当提高(但不能超过LED与驱动器承受能力)
- 扫描频率需足够高,避免闪烁
- 每行点亮时间应尽量一致,避免亮度不均
- 数据输出与行切换应避免“鬼影”(行切换瞬间列数据未稳定导致误亮)
3.4 列数据输出与锁存模块
3.4.1 模块功能
由于显示宽度达到64列,若直接用单片机并口输出64位数据,IO资源远远不够,必须采用扩展驱动方案。常见方案为:
- 74HC595串行输入并行输出移位寄存器:
- 每片提供8位并行输出
- 64列需要8片级联
- 单片机只需3根线:DATA、CLK、LATCH
- 74HC573/74HC574锁存器 + 并口扩展:
- 并口输出需要更多IO,不如595省引脚
在字幕屏设计中,74HC595是最常用方案之一:成本低、连线清晰、级联方便,尤其适合多列输出。
3.4.2 数据锁存与显示稳定性
移位寄存器存在“移位过程输出变化”的问题,因此需要锁存:
- 先把64位新列数据通过串行方式移入8片595
- 在移位完成后统一翻转LATCH,将数据同时更新到输出端
- 再选通行,使该行显示稳定
这个过程能显著减少鬼影与闪烁,保证显示清晰。
3.5 行选通驱动模块
3.5.1 模块功能
行选通决定当前显示哪一行(共16行)。若直接用16个IO控制行,浪费资源且不利于扩展,因此常用:
- 74HC154(4-16译码器):4根地址线选择16行之一,输出为低有效或高有效视器件而定。
- 74HC138(3-8译码器)+扩展:两片组合实现16行选择。
- 行驱动晶体管阵列(如ULN2803):用于行电流放大,保护译码器与单片机。
3.5.2 驱动能力与电流放大
一行点亮时可能同时点亮多个LED,行端电流较大:
- 若16列中点亮8个,行电流约为8×单LED电流
- 再乘以4块拼接的行并行关系(取决于行是否共用)
因此行驱动常需三极管/MOSFET或ULN2803进行电流放大,保证可靠驱动且不损坏逻辑芯片。
3.6 电源与滤波模块
3.6.1 模块功能
系统通常工作在5V供电,LED点阵电流波动大,容易造成电源纹波与干扰,电源模块需要:
3.6.2 亮度与功耗考虑
字幕屏点阵同时点亮的LED越多,电流越大,供电能力必须满足最大亮度时的电流需求。若供电不足,会出现:
- 亮度明显变暗
- 单片机复位或显示乱码(电源跌落)
因此电源设计是保证稳定显示的重要基础。
4 程序设计
4.1 软件总体架构
程序设计的核心目标是实现“高速稳定刷新 + 低速平滑滚动”。建议将软件划分为以下模块:
- 系统初始化模块(时钟、IO、定时器)
- 点阵驱动模块(74HC595移位与锁存、行选择)
- 显示刷新模块(行扫描中断/轮询刷新)
- 字模数据模块(16×16汉字字库:毕、业、快、乐)
- 静态显示模块(将4字映射到显示缓存)
- 滚动显示模块(将整句按列拼接,逐列左移更新缓存)
- 定时管理模块(刷新定时与滚动定时分离)
为了获得最佳显示效果,推荐使用定时器中断进行刷新扫描:
- 定时器每隔固定时间(如1ms)刷新下一行
- 主循环负责滚动更新(如每30ms更新一次缓存)
这种方式能避免主循环阻塞导致刷新不均匀,从而保证亮度一致。
4.2 显示数据组织方式
4.2.1 字模存储格式
16×16汉字通常按“行”存储,每行16列需要16位,可用2个字节表示,因此一个16×16汉字占用:
- 16行 × 2字节/行 = 32字节
可定义字模数组:
code unsigned char font_bi[32];code unsigned char font_ye[32];code unsigned char font_kuai[32];code unsigned char font_le[32];
每两个字节对应一行的16列点亮信息:
- 高字节表示第0~7列
- 低字节表示第8~15列(或相反,视电路列映射)
4.2.2 显示缓存设计
由于屏幕宽度为64列,高度16行,可以将显示缓存按“行缓冲”设计:
- 每行64列 = 64位 = 8字节
- 16行总缓存 = 16×8 = 128字节
定义:
unsigned char disp_buf[16][8];
其中disp_buf[row][byte]表示第row行的第byte个字节数据。刷新扫描时,只需取出当前行的8字节,依次送入8片74HC595即可。
这种结构对静态显示与滚动显示都非常友好:
- 静态:直接把四个字按行拷贝到缓存对应位置
- 滚动:对每一行的64位进行左移,并在右端补入新列位
4.3 系统初始化模块
4.3.1 初始化内容
- IO口初始化:DATA、CLK、LATCH、行选择地址线、蜂鸣器/指示灯(如有)
- LCD无(本系统无LCD)
- 定时器初始化:
- 定时器0用于行扫描刷新
- 定时器1或软件计时用于滚动节拍
4.3.2 初始化示例代码
#include <reg52.h>
sbit SR_DATA = P2^0;
sbit SR_CLK = P2^1;
sbit SR_LATCH = P2^2;
// 行选择地址线示例(若使用4-16译码器)
sbit ROW_A = P1^0;
sbit ROW_B = P1^1;
sbit ROW_C = P1^2;
sbit ROW_D = P1^3;
void Timer0_Init(void)
{
TMOD &= 0xF0;
TMOD |= 0x01; // 定时器0方式1
TH0 = (65536 - 1000) >> 8; // 1ms@12MHz(示例)
TL0 = (65536 - 1000) & 0xFF;
ET0 = 1;
EA = 1;
TR0 = 1;
}
void System_Init(void)
{
SR_DATA = 0;
SR_CLK = 0;
SR_LATCH = 0;
ROW_A = 0; ROW_B = 0; ROW_C = 0; ROW_D = 0;
Timer0_Init();
}
4.4 点阵驱动模块(74HC595输出)
4.4.1 模块功能
将当前行的64列数据通过串行方式送入8片级联的74HC595,并锁存输出。
4.4.2 输出函数示例
void ShiftOut_Byte(unsigned char dat)
{
unsigned char i;
for(i = 0; i < 8; i++)
{
SR_CLK = 0;
SR_DATA = (dat & 0x80) ? 1 : 0;
dat <<= 1;
SR_CLK = 1;
}
}
void Latch_Data(void)
{
SR_LATCH = 0;
SR_LATCH = 1;
}
4.4.3 发送一行64列数据
注意发送顺序要与硬件级联方向一致:若最先移入的数据对应最末端595输出,则需要反序发送。以下为一种常见方式:从高字节到低字节依次发送。
extern unsigned char disp_buf[16][8];
void Output_RowData(unsigned char row)
{
unsigned char i;
for(i = 0; i < 8; i++)
{
ShiftOut_Byte(disp_buf[row][i]);
}
Latch_Data();
}
4.5 行扫描刷新模块
4.5.1 刷新原理
行扫描的关键步骤:
- 关闭上一行(避免鬼影)
- 输出当前行列数据(64列)
- 选通当前行
- 保持短暂时间
- 切换到下一行
为了显示稳定,行扫描通常放在定时器中断里执行,保证每行点亮时间一致。
4.5.2 行选择函数示例
void Select_Row(unsigned char row)
{
ROW_A = (row & 0x01) ? 1 : 0;
ROW_B = (row & 0x02) ? 1 : 0;
ROW_C = (row & 0x04) ? 1 : 0;
ROW_D = (row & 0x08) ? 1 : 0;
}
4.5.3 定时器中断刷新示例
unsigned char cur_row = 0;
void Timer0_ISR(void) interrupt 1
{
TH0 = (65536 - 1000) >> 8;
TL0 = (65536 - 1000) & 0xFF;
// 输出当前行数据并选通
Output_RowData(cur_row);
Select_Row(cur_row);
cur_row++;
if(cur_row >= 16) cur_row = 0;
}
若需要避免鬼影,可在切换行前先关闭行选通(例如将译码器使能关闭),再更新列数据后再开启行,这取决于实际硬件是否提供行使能控制脚。
4.6 字模数据模块(“毕”“业”“快”“乐”)
4.6.1 字模来源与处理方式
16×16汉字字模通常来自:
- 字模软件(如PC端点阵取模工具)
- 现成16×16国标字库(部分课程会提供)
本系统只需4个汉字,因此采用“定制字模数组”最简单:
- 存储量小(4×32=128字节)
- 查找快、实现简单
4.6.2 字模数组示例结构(示例数据非真实字模)
code unsigned char font_bi[32] = {
0x00,0x00,0x7F,0xFE, /* ... 共32字节 ... */ 0x00,0x00
};
code unsigned char font_ye[32] = {
0x00,0x00,0x3F,0xFC, /* ... */ 0x00,0x00
};
code unsigned char font_kuai[32] = {
0x00,0x00,0x1F,0xF8, /* ... */ 0x00,0x00
};
code unsigned char font_le[32] = {
0x00,0x00,0x0F,0xF0, /* ... */ 0x00,0x00
};
实际使用时应替换为正确的取模数据。为了保证字形饱满,建议在取模时选择合适字体(如黑体)、合适的粗细,并居中对齐。
4.7 静态显示模块
4.7.1 模块功能
将“毕”“业”“快”“乐”四个字分别映射到显示缓存disp_buf中,使其在4块16×16点阵上同时显示。由于每块宽16列,总宽64列,因此四字在缓存中的列位置分别为:
- “毕”:第0~15列
- “业”:第16~31列
- “快”:第32~47列
- “乐”:第48~63列
按行拷贝:每行16列(2字节),四字拼接就是每行8字节,正好对应disp_buf[row][0..7]。
4.7.2 静态写缓存示例
extern unsigned char disp_buf[16][8];
void Load_StaticText(void)
{
unsigned char row;
for(row = 0; row < 16; row++)
{
// 每行在字模中占2字节
disp_buf[row][0] = font_bi[row*2];
disp_buf[row][1] = font_bi[row*2 + 1];
disp_buf[row][2] = font_ye[row*2];
disp_buf[row][3] = font_ye[row*2 + 1];
disp_buf[row][4] = font_kuai[row*2];
disp_buf[row][5] = font_kuai[row*2 + 1];
disp_buf[row][6] = font_le[row*2];
disp_buf[row][7] = font_le[row*2 + 1];
}
}
这样加载后,刷新中断会自动按行扫描显示,形成静态“四字同屏”。
4.8 滚动显示模块(逐列左移)
4.8.1 滚动显示的核心思路
滚动显示最关键的是把整句“毕业快乐”看作一个连续的“列流”。由于每个字宽16列,四个字总宽为64列。若只滚动一次显示窗口,理论上可以直接将64列数据当作源,然后移动窗口显示;但为了实现“从右向左进入、滚动循环播放”的字幕屏效果,通常要在两端加入空白列作为间隔:
- 左右加入若干空白列(例如16列空白),使字与字之间不粘连、滚动更像字幕。
因此可构建一个更长的滚动源:
源宽度 = 空白间隔 + 64列字数据 + 空白间隔
也可以把4字循环拼接为无限流,采用取模方式不断取新列。
在实现上最简单可靠的方式是:
- 准备一个“列源数组”,每一列包含16行的点亮信息(即16位)。
- 每次滚动:
- 显示缓存整体左移1列
- 从列源取出下一列补到显示缓存最右侧
- 滚动索引到末尾后回到开头,实现循环滚动。
4.8.2 行缓冲左移实现方法
因为disp_buf按行存储,每行是8字节(64列),要整体左移一列,可以对每行执行“位级左移”:
- 逐字节移位并带进位(carry)
- 最右侧空出的最低位由“新列位”补入(或最高位补入,取决于你的列方向定义)
4.8.3 构建列源(按列组织)
静态字模是按行组织的,滚动更适合按列组织。可以在初始化时将四字字模转换成列源。列源中每列用16位表示该列16行点亮情况:
- bit0表示第0行
- bit15表示第15行
或反过来,需与行扫描极性一致。
为避免占用大量RAM,可将列源存放在代码区(code),但列源生成更复杂。课程设计中常用方法是直接手工准备按列的数据或在PC端生成。若要在单片机端生成,也可在初始化时将行字模转换成列源缓存在RAM(列数不多时可行)。
4.8.4 滚动左移与补列示例代码(核心算法)
以下示例假设:
- 每次滚动从列源取出“新列”的16位数据
new_col new_col的第row位表示该行最右侧新像素点的点亮状态disp_buf[row][0]为最左侧字节,disp_buf[row][7]为最右侧字节- 左移表示文字向左移动(从右往左滚动)
unsigned int Get_NextColumn(void); // 返回16位列数据(示例框架)
unsigned int scroll_index = 0;
void Scroll_Left_Once(void)
{
unsigned char row, b;
unsigned int new_col = Get_NextColumn(); // 16行的列数据
for(row = 0; row < 16; row++)
{
unsigned char carry = 0, next_carry = 0;
// 对一行的8字节进行位级左移
for(b = 0; b < 8; b++)
{
next_carry = (disp_buf[row][b] & 0x80) ? 1 : 0; // 最高位将移出
disp_buf[row][b] <<= 1;
disp_buf[row][b] |= carry;
carry = next_carry;
}
// 最右侧补入新列像素:放入最后一个字节的最低位
// 若需要补入最高位,则应调整移位方向与补位位置
if(new_col & (1 << row))
disp_buf[row][7] |= 0x01;
else
disp_buf[row][7] &= 0xFE;
}
}
该算法实现了整屏逐列左移,并在最右侧补入新列数据,实现平滑滚动的核心效果。
4.9 滚动列源读取模块
4.9.1 模块功能
提供函数 Get_NextColumn(),每次返回滚动序列中的下一列16位数据,并更新索引,实现循环滚动。
4.9.2 列源组织建议
可以定义一个列源数组:
code unsigned int col_stream[STREAM_LEN];
其中每个元素是16位列数据,STREAM_LEN为列数总长度。列源内容可包含:- 前导空白列(全0)
- “毕”“业”“快”“乐”逐列数据
- 尾随空白列(全0)
这样滚动时更像字幕屏,字与字之间有间隔。
4.9.3 读取函数示例
#define STREAM_LEN 96
code unsigned int col_stream[STREAM_LEN] = {
// 这里应填入真实列数据:前若干0 + 文字列 + 若干0
// 示例省略
};
unsigned int Get_NextColumn(void)
{
unsigned int col = col_stream[scroll_index];
scroll_index++;
if(scroll_index >= STREAM_LEN) scroll_index = 0;
return col;
}
在课程设计说明中,可以强调:列源数据可以通过上位机工具生成,避免在单片机端做复杂转换,从而提升可靠性与开发效率。
4.10 静态与滚动模式管理模块
4.10.1 模式切换策略
即使题目未要求按键切换,也可以在程序中设计时间切换:
- 上电先静态显示3秒
- 然后进入滚动显示循环
或加入按键:按一下切换静态/滚动。
4.10.2 简单模式控制示例
bit mode_scroll = 0;
unsigned int ms_cnt = 0;
void Main_Task(void)
{
// 假设每次调用间隔为10ms
ms_cnt += 10;
if(ms_cnt < 3000)
{
if(mode_scroll != 0)
{
mode_scroll = 0;
Load_StaticText();
}
}
else
{
mode_scroll = 1;
}
if(mode_scroll)
{
static unsigned int scroll_tick = 0;
scroll_tick += 10;
if(scroll_tick >= 40) // 40ms滚动一步
{
scroll_tick = 0;
Scroll_Left_Once();
}
}
}
4.11 主程序框架与任务调度
4.11.1 程序运行机制说明
- 定时器中断:负责行扫描刷新(高频,保证稳定)
- 主循环:负责模式管理、滚动更新(低频,控制速度)
这种“刷新与滚动分离”的结构非常适合LED点阵字幕屏:刷新越稳定,亮度越均匀;滚动越规律,运动越平滑。
4.11.2 主程序示例
void Delay_ms(unsigned int ms);
unsigned char disp_buf[16][8];
int main(void)
{
System_Init();
Load_StaticText();
while(1)
{
Main_Task();
Delay_ms(10);
}
}
5 显示效果优化与工程实现要点
5.1 鬼影与闪烁问题的解决思路
若出现鬼影(不该亮的点短暂亮起),通常原因是:
- 行切换时列数据未稳定
- 锁存时序不合理
- 行驱动未关闭导致切换瞬间误亮
解决策略:
- 切换行前先关闭行使能
- 更新列数据并锁存后再开启行
- 保证移位与锁存的时序稳定
- 适当增加几个微秒延时让信号稳定
5.2 亮度均匀性优化
亮度不均常见原因:
- 刷新周期不稳定(主循环刷新导致某些行显示时间更长)
- 行驱动电流不足或某些行压降更大
- 电源纹波导致亮度随电流变化
优化策略:
- 用定时器中断固定周期刷新
- 行驱动使用晶体管/ULN2803增强驱动
- 增加电源滤波电容与合理走线
5.3 滚动平滑性与速度控制
滚动过快会看不清,过慢则缺乏字幕屏动感。建议:
- 滚动步进固定为1列
- 时间间隔推荐30ms~60ms
- 可通过按键或电位器(扩展)调整速度
5.4 字模质量与视觉效果
“字形饱满”高度依赖字模质量:
- 取模时选择黑体或等线体更适合点阵显示
- 字形应居中,四边留空合理
- 笔画不能过细,否则16×16分辨率下容易断笔
- 可以针对点阵特性做手工微调,使“毕”“业”“快”“乐”更美观
6 总结
基于51单片机的LED点阵汉字显示系统通过4块16×16点阵模块构建64×16显示区域,实现了“毕业快乐”的静态同屏显示与从右向左的逐列平滑滚动显示,具备字幕屏典型的视觉效果。系统在硬件上采用移位寄存器输出与行扫描驱动相结合的方案,有效解决了IO资源不足与驱动能力不足的问题,并通过锁存更新与定时器扫描保证显示稳定、亮度均匀。
在软件设计上,系统采用“高频刷新 + 低频滚动”的结构,将点阵扫描刷新交由定时器中断处理,从而保证每行点亮时间一致,避免闪烁与亮度不均;同时通过显示缓存与逐列移位算法实现平滑滚动,使文字在64列宽屏幕上连续移动,呈现类似公交站LED字幕屏的动态展示体验。通过合理的字模组织、缓存映射与滚动列源管理,系统不仅能够完成既定显示任务,还具备良好的扩展能力,可进一步实现更多汉字、更多特效(闪烁、反显、速度调节、多行滚动)与更大尺寸字幕屏的升级设计。
103