这一篇在干嘛?
点阵屏是地铁站、商店门口滚动字幕的「缩小版」:一块 16×16 的 LED 矩阵共 256 个发光点,FPGA 通过两根总线分别控制行和列,让特定的点亮起来,就拼出了一个汉字。本实验学习点阵显示字符的基本原理,并掌握用数控分频控制速度、实现点阵扫描的方法。上板后点阵会循环显示「杭州康芯」四个字。
实验目标与原理
实验目的
- 学习点阵显示字符的基本原理。
- 掌握用数控分频控制速度,实现点阵扫描的基本方式。
从一个 LED 到一块点阵
先看最小单元。一个发光二极管(LED)要亮,必须让电流从正极流向负极:正极(阳极)接高电平、负极(阴极)接低电平。把 64 个 LED 按 8 行 8 列整整齐齐焊在一起,每个 LED 放在一条行线和一条列线的交叉点上,就是 8×8 点阵——这是所有点阵屏的基础单元。
8×8 点阵的实际外观图如图 1:一共 64 个发光点构成,上下两排引脚,每排 8 个,一共 16 个。使用时要点阵侧面有文字的那面对准自己。

图 1 排列

图 2 行列

图 3 8×8 点阵实际引脚端
8×8 点阵的显示原理
如图 2,8×8 点阵共由 64 个发光二极管组成,R 为列,C 为行,且每个发光二极管是放置在行线和列线的交叉点上。当对应的某一行置 1 电平、某一列置 0 电平,则相应的二极管就亮:
- 点亮一个点:将第一个点点亮,则 R1 脚接高电平、C1 脚接低电平,第一个点就亮了;
- 点亮一行:R1 脚接高电平,而 C1~C8 这些引脚接低电平,那么第一行就会点亮;
- 点亮一列:R1 脚接低电平,而 C1~C8 接高电平,那么第一列就会点亮。
图 3 是实际点阵图,标注了每个引脚对应的引脚名。
从 8×8 到 16×16:扫描与余晖
一个 16×16 点阵由 4 块 8×8 点阵拼成,共 256 个 LED。如果给每个 LED 单独配一根 FPGA 引脚,需要 256 根线,显然不现实。点阵巧妙的地方在于共享走线:一行上的所有 LED 阳极接在同一根行线上,一列上的所有 LED 阴极接在同一根列线上。于是 16×16 点阵只需要 16 根行线 + 16 根列线 = 32 根信号线(本实验的 en[15:0] 与 dataout[15:0])。
代价是「串扰」:想让第 3 行第 5 列那个点亮,把行 3 拉高、列 5 拉低后,第 3 行和第 5 列交叉的其他点也满足导通条件。解决办法就是扫描——每一时刻只完整驱动一行:把当前行的行线拉高、把这一行 16 个点对应的列线按字模数据拉低/拉高,其余 15 根行线全部拉低;停留一小段时间后切换到下一行。只要整屏刷新的频率足够快(一般大于 50Hz),人眼余晖效应会把逐行点亮的画面「融合」成一幅稳定完整的图像,看起来就像 256 个点同时亮着。
「数控分频控制速度」指的就是用计数器对系统时钟分频,得到扫描切换的节拍:分频系数大,扫描换行慢,滚动/动画速度就慢;分频系数小,速度就快。把字模数据存成查找表,配合一个滚动偏移量,就能让「杭州康芯」四个字在点阵上循环滚动显示。
FPGA 里的扫描流程是什么样的
把上面的原理落到 FPGA 上,扫描控制可以拆成四个并行的职责:
- 分频节拍:对 50MHz 系统时钟做数控分频,得到一个几 kHz 量级的扫描时钟。分频系数由参数(数控)给定,改一个数字就能整体调快调慢显示与滚动速度。
- 行地址生成:扫描时钟驱动一个 4 位计数器(0~15),循环产生当前要驱动的行号。计数器每加一,整屏就切换到下一行;16 行扫完回到第 0 行,完成一帧。
- 行选通:把行号译码成 one-hot(16 根 en 线里只有一根有效),选中当前行。
- 列数据查表:根据当前行号和滚动偏移量,从字模表里取出这一行 16 位的数据送到 dataout 上。
滚动显示「杭州康芯」的原理:四个汉字各占一个 16×16 的字模,横向排成一条 64 列宽的「长卷」。屏幕每一时刻只显示长卷上 16 列宽的窗口,窗口在长卷上按分频节拍逐列右移,移到末尾再回到开头,看起来就是文字从右往左循环滚动。滚动速度和扫描速度由两个独立的分频系数控制,互不干扰。
字模是什么?
要在点阵上显示一个 16×16 的汉字,先在「取模软件」里把汉字画进 16×16 的格子,有笔画的格子记 1、没有的记 0,每一行凑成 16 位,一个字就是 16 个 16 位数,这就是字模。FPGA 里用一个 ROM 或 case 语句存下这些字模,扫描到第几行就取出对应的那一行数据送到列线上。
为什么要用 LVCMOS33、驱动强度 8
细看引脚约束会发现规律:所有数据/选通脚都是 IOSTANDARD = LVCMOS33 且 DRIVESTRENGTH = 8。LVCMOS33 表示 3.3V 单端电平,与点阵模块的逻辑电平匹配;DRIVESTRENGTH = 8 是把 FPGA 输出驱动能力调到 8mA 档——一个 LED 点的电流由行线(同时灌 16 个点的电流)承担,较强的驱动能保证点亮的点不发暗。时钟和复位脚(clk、rst)则配了 PULLTYPE = PULLUP,保证复位前信号有确定的电平。
实验仿真
根据描述语言进行仿真测试,分析波形。
引脚分配
管脚约束如下(原文分两段给出,均一字不差保留):
set_pin_assignment { clk } { LOCATION = R7; IOSTANDARD = LVCMOS33; PULLTYPE = PULLUP; }
set_pin_assignment { dataout[0] } { LOCATION = K12; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { dataout[10] } { LOCATION = G16; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { dataout[11] } { LOCATION = G14; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { dataout[12] } { LOCATION = K15; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { dataout[13] } { LOCATION = J16; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { dataout[14] } { LOCATION = H15; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { dataout[15] } { LOCATION = H16; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { dataout[1] } { LOCATION = L14; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { dataout[2] } { LOCATION = M14; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { dataout[3] } { LOCATION = P11; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { dataout[4] } { LOCATION = L10; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { dataout[5] } { LOCATION = M10; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { dataout[6] } { LOCATION = D16; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { dataout[7] } { LOCATION = E15; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { dataout[8] } { LOCATION = F14; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { dataout[9] } { LOCATION = F15; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { en[0] } { LOCATION = D14; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { en[10] } { LOCATION = M15; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { en[11] } { LOCATION = L16; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { en[12] } { LOCATION = N16; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }set_pin_assignment { en[13] } { LOCATION = N14; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { en[14] } { LOCATION = P16; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { en[15] } { LOCATION = P15; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { en[1] } { LOCATION = G11; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { en[2] } { LOCATION = G12; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { en[3] } { LOCATION = F13; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { en[4] } { LOCATION = H13; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { en[5] } { LOCATION = H14; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { en[6] } { LOCATION = J14; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { en[7] } { LOCATION = J13; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { en[8] } { LOCATION = K16; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { en[9] } { LOCATION = M16; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { rst } { LOCATION = T6; IOSTANDARD = LVCMOS33; PULLTYPE = PULLUP; }信号含义:clk(R7)是 50MHz 系统时钟,rst(T6)是复位;en[15:0] 是 16 根行(列)选通线,dataout[15:0] 是 16 根列(行)数据线,两者一一对应接到点阵模块的行、列驱动上。
行列极性别接反
按本实验的点阵接法,被选中点亮的条件是「行置 1、列置 0」。如果把 en 与 dataout 的极性或对应顺序弄反,现象会是「想亮的点不亮、不亮的点反而成片亮」或整屏乱亮。调试时先用「只亮一个点 / 只亮一行」的简单数据验证行列映射,再载入完整字模。
上板流程
- 将 16×16 点阵模块插在开发板 J1 口上;
- 编译工程并下载程序;
- 观察点阵显示。
现象与总结
下载程序之后,可以看到点阵循环显示「杭州康芯」。




总结一下本实验的核心思想:点亮一个 LED 靠「行高列低」,点亮整屏靠「逐行扫描 + 余晖效应」,控制显示速度靠「数控分频」。这套行列扫描思路和后面动态数码管扫描(8 位动态扫描设计)是一脉相承的,学会了点阵扫描,数码管扫描就是换了个「字模表」而已。
自测
自测
- 点阵中某个 LED 点亮的电平条件是什么?请分别说明点亮一个点、一行、一列时行线 R 和列线 C 应如何设置。
- 16×16 点阵共 256 个 LED,为什么只需要 32 根信号线?代价是什么问题,如何解决?
- 什么是人眼余晖效应?整屏刷新频率至少要多大才能看到稳定的画面?
- 什么是字模?一个 16×16 的汉字需要多少个 16 位数来描述?
- 「数控分频控制速度」在本实验里控制的是什么?增大分频系数对显示效果有什么影响?