FPGA之VGA/LCD数字时钟显示

news2024/12/23 11:31:02

文章目录

  • 前言
  • 一、LCD显示控制
    • 1.LCD显示一个字符
    • 2.LCD显示多个字符
  • 二、数字时钟输出
    • 1.数字时钟
    • 2.十进制数据拆分BCD码
  • 三、按键检测及LCD驱动
    • 1.按键检测
    • 2.LCD驱动
  • 四、总结


前言

软件实现了在4.3寸LCD左上角显示一个数字时钟,效果如下图所示。本文针对VGA/LCD控制时许有一定基础的人群,博主的开发环境为Quartus13.1和一个随便哪家的开发板,使用4.3寸LCD(RGB565接口),兼容VGA,但是相关参数需要更改。软件中部分代码模块借用野火电子的软件,感谢。后文以LCD进行说明。
获取源代码、字模软件、rom初始化文件等点击此处
在这里插入图片描述


一、LCD显示控制

1.LCD显示一个字符

LCD在显示时是从左到右、从上到下进行刷新显示,4.3寸lcd显示区域大小为480x272,软件在坐标(24,32)处划定了一个长24宽32的显示区域,将此区域命名为square1,显示的内容为时的十位(22:15中左边第一个2),如下图所示。
在这里插入图片描述
在显示一个字符1时先进行取模,如下图所示,点阵大小也为24x32,与上图square1对应,因此lcd在square1中一行一行刷新时也判断字模的值是否为1,是则点亮lcd对应位置,反之则变暗。下面举例进行说明。square1在扫描前三行时,字模为0,第四行的第13个像素点为1,那么此时lcd对应的位置点亮。软件中lcd点亮即将该点像素变红。
软件实现时,将字模存储在宽度为24bit,深度为32的rom中,square1的32行读出rom地址0的值,在本行的第24列开始判断rom地址0读出值rom_dat的最高位bit23,然后判断bit23值是否为1,是则将lcd该点变红,反之变黑,之后继续判断bit30、bit29、bit28…bit0,一行判断完成后则判断下一行。square1的第33行读出rom的地址1的数据,然后进行判断。重复以上过程直到square1最后一行,此时对应rom地址31的数据。由此完成一个字符的显示。
在这里插入图片描述
部分代码如下:

parameter 	POINTX   ='d24, //squr1
			POINTY   ='d32;
			
parameter 	WIDTH	 ='d24,
			HEIGH	 ='d32;			

else if(squr1_rden)begin
		if(squrrom_dat[WIDTH+POINTX-pix_x])
			o_tft_dat<=RED ;
		else
			o_tft_dat<=0;//BLUE;
	end 

squrrom_rdaddr =(pix_y-HEIGH)

2.LCD显示多个字符

完成一个字符显示后多个字符显示就很简单,继续划定剩下3个字符和冒号区域,4个字符的点阵大小都是24x32,冒号点阵大小为16x32。软件使用两个rom存放字模,一个存储0-9字符,地址0-31为字符“0”,32-63为字符“1”,后续依次递增;另一个存储“:”。4个字符共用一个rom,字符寻址、显示部分代码如下图:

always@(*)begin
	if(!rst_n)
		squrrom_rdaddr='d0;
	else if(squr1_rden)
		squrrom_rdaddr=suqr1_addr;
	else if(squr2_rden)
		squrrom_rdaddr=suqr2_addr;
	else if(squr3_rden)
		squrrom_rdaddr=suqr3_addr;
	else if(squr4_rden)
		squrrom_rdaddr=suqr4_addr;	
end	

assign suqr1_addr=(pix_y-HEIGH)+ squr1*32;
assign suqr2_addr=(pix_y-HEIGH)+ squr2*32;
assign suqr3_addr=(pix_y-HEIGH)+ squr3*32;
assign suqr4_addr=(pix_y-HEIGH)+ squr4*32;

always@(posedge clk,negedge rst_n)
	if(!rst_n)
		o_tft_dat<='d0;
	else if(squr1_rden)begin
		if(squrrom_dat[WIDTH+POINTX-pix_x])
			o_tft_dat<=RED ;
		else
			o_tft_dat<=0;//BLUE;
	end else if(squr2_rden)begin
		if(squrrom_dat[WIDTH+POINTX2-pix_x])
			o_tft_dat<=RED ;
		else
			o_tft_dat<=0;//YELLOW;		
	end else if(dot_rden)begin
		if(dotrom_dat[WIDTHDOT+POINTXDOT-pix_x] && dot_vld)
			o_tft_dat<=RED ;
		else
			o_tft_dat<=0;//CYAN;
	end else if(squr3_rden)begin
		if(squrrom_dat[WIDTH+POINTX3-pix_x])
			o_tft_dat<=RED ;
		else
			o_tft_dat<=0;//PURPPLE;	
	end else if(squr4_rden)begin
		if(squrrom_dat[WIDTH+POINTX4-pix_x])
			o_tft_dat<=RED ;
		else
			o_tft_dat<=0;//GRAY;		
	end else 
		o_tft_dat<='d0;


二、数字时钟输出

1.数字时钟

软件输出小时、分钟两个数值,直接对时钟计数产生,部分代码如下:


always@(posedge clk,negedge rst_n)
	if(!rst_n)
		cnt<='d0;
	else if(cnt==MS-1)
		cnt<='d0;
	else
		cnt<=cnt+'d1;
		
always@(posedge clk,negedge rst_n)
	if(!rst_n)
		cntms<='d0;
	else if(cnt==MS-1)begin
		if(cntms=='d1000-1)
			cntms<='d0;
		else
			cntms<=cntms+'d1;
	end else 
		cntms<=cntms;

always@(posedge clk,negedge rst_n)
	if(!rst_n)
		cnts<='d0;
	else if((cnt==MS-1) && (cntms=='d1000-1))begin
		if(cnts=='d60-1)
			cnts<='d0;
		else 
			cnts<=cnts+'d1;
	end else 
		cnts<=cnts;
		
assign o_sec_fg =	(cnt==MS-1) && (cntms=='d1000-1);

always@(posedge clk,negedge rst_n)
	if(!rst_n)
		cntmin<='d0;
	else if(min_ctl) begin
		if(cntmin>='d60-1)
			cntmin<='d0;
		else
			cntmin<=cntmin+'d1;
	end else if(o_sec_fg && (cnts=='d60-1))	begin
		if(cntmin=='d60-1)
			cntmin<='d0;
		else
			cntmin<=cntmin+'d1;
	end else 
		cntmin<=cntmin;
		
always@(posedge clk,negedge rst_n)
	if(!rst_n)
		cnthour<='d0;
	else if(hour_ctl)begin
		if(cnthour>='d24-1)
			cnthour<='d0;
		else
			cnthour<=cnthour+'d1;
	end else if(o_sec_fg && (cnts=='d60-1) &&(cntmin=='d60-1))begin
		if(cnthour=='d24-1)
			cnthour<='d0;
		else 
			cnthour<=cnthour+'d1;
	end else 
		cnthour<=cnthour;		

2.十进制数据拆分BCD码

对于一个两位数的数字,常用的提取个位和十位的方法是除以10得到十位,除以10取余数得到个位,但是对于FPGA来说以上方法将使用更多资源,并且不符合FPGA编程习惯,因此一般使用加3移位法实现二进制转BCD码,部分代码如下:

always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_shift   <=  5'd0;
    else    if((cnt_shift == 5'd7) && (shift_flag == 1'b1))
        cnt_shift   <=  5'd0;
    else    if(shift_flag == 1'b1)
        cnt_shift   <=  cnt_shift + 1'b1;
    else
        cnt_shift   <=  cnt_shift;
       
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        data_shift  <=  14'b0;
    else    if(cnt_shift == 5'd0)
        data_shift  <=  {8'b0,data};
    else    if((cnt_shift <= 6) && (shift_flag == 1'b0))
        begin
            data_shift[09:06]   <=  (data_shift[09:06] > 4) ? (data_shift[09:06] + 2'd3) : (data_shift[09:06]);
            data_shift[13:10]   <=  (data_shift[13:10] > 4) ? (data_shift[13:10] + 2'd3) : (data_shift[13:10]);
        end
    else    if((cnt_shift <= 6) && (shift_flag == 1'b1))
        data_shift  <=  data_shift << 1;
    else
        data_shift  <=  data_shift;

always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        shift_flag  <=  1'b0;
    else
        shift_flag  <=  ~shift_flag;

always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        begin
            unit    <=  4'b0;
            ten     <=  4'b0;
        end
    else    if(cnt_shift == 5'd7)
        begin
            unit    <=  data_shift[09:06];
            ten     <=  data_shift[13:10];

        end

三、按键检测及LCD驱动

1.按键检测

软件使用两个按键对小时、分钟进行控制,功能比较简单,只能进行加一调整,即按下某个按键对应数值加一。因为轻触按键在按下时存在机械抖动,所以不能简单判断下降沿就认为按键按下,需要在下降沿后延时20ms在判断按键情况,部分代码如下:

always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_20ms <= 20'b0;
    else    if(key_in == 1'b1)
        cnt_20ms <= 20'b0;
    else    if(cnt_20ms == CNT_MAX && key_in == 1'b0)
        cnt_20ms <= cnt_20ms;
    else
        cnt_20ms <= cnt_20ms + 1'b1;
        
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        key_flag <= 1'b0;
    else    if(cnt_20ms == CNT_MAX - 1'b1)
        key_flag <= 1'b1;
    else
        key_flag <= 1'b0;

2.LCD驱动

LCD驱动时序与VGA类似,因此在使用VGA是只需要更改时钟、行列计数等参数,驱动代码如下:


//parameter define
parameter H_SYNC    =   10'd41  ,   //行同步
          H_BACK    =   10'd2   ,   //行时序后沿
          H_VALID   =   10'd480 ,   //行有效数据
          H_FRONT   =   10'd2   ,   //行时序前沿
          H_TOTAL   =   10'd525 ;   //行扫描周期
parameter V_SYNC    =   10'd10  ,   //场同步
          V_BACK    =   10'd2   ,   //场时序后沿
          V_VALID   =   10'd272 ,   //场有效数据
          V_FRONT   =   10'd2   ,   //场时序前沿
          V_TOTAL   =   10'd286 ;   //场扫描周期

//wire  define
wire            rgb_valid       ;   //VGA有效显示区域
wire            pix_data_req    ;   //像素点色彩信息请求信号

//reg   define
reg     [9:0]   cnt_h   ;   //行扫描计数器
reg     [9:0]   cnt_v   ;   //场扫描计数器

//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//

//o_tft_clk,o_tft_de,o_tft_bl:TFT像素时钟、数据使能、背光信号
assign  o_tft_clk = tft_clk_9m    ;
assign  o_tft_de  = rgb_valid     ;
assign  o_tft_bl  = sys_rst_n     ;

//cnt_h:行同步信号计数器
always@(posedge tft_clk_9m or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_h   <=  10'd0   ;
    else    if(cnt_h == H_TOTAL - 1'd1)
        cnt_h   <=  10'd0   ;
    else
        cnt_h   <=  cnt_h + 1'd1   ;

//o_hsync:行同步信号
assign  o_hsync = (cnt_h  <=  H_SYNC - 1'd1) ? 1'b1 : 1'b0  ;

//cnt_v:场同步信号计数器
always@(posedge tft_clk_9m or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_v   <=  10'd0 ;
    else    if((cnt_v == V_TOTAL - 1'd1) &&  (cnt_h == H_TOTAL-1'd1))
        cnt_v   <=  10'd0 ;
    else    if(cnt_h == H_TOTAL - 1'd1)
        cnt_v   <=  cnt_v + 1'd1 ;
    else
        cnt_v   <=  cnt_v ;

//o_vsync:场同步信号
assign  o_vsync = (cnt_v  <=  V_SYNC - 1'd1) ? 1'b1 : 1'b0  ;

//rgb_valid:VGA有效显示区域
assign  rgb_valid = (((cnt_h >= H_SYNC + H_BACK)
                    && (cnt_h < H_SYNC + H_BACK + H_VALID))
                    &&((cnt_v >= V_SYNC + V_BACK)
                    && (cnt_v < V_SYNC + V_BACK + V_VALID)))
                    ? 1'b1 : 1'b0;

//pix_data_req:像素点色彩信息请求信号,超前rgb_valid信号一个时钟周期
assign  pix_data_req = (((cnt_h >= H_SYNC + H_BACK - 1'b1)
                    && (cnt_h < H_SYNC + H_BACK + H_VALID - 1'b1))
                    &&((cnt_v >= V_SYNC + V_BACK)
                    && (cnt_v < V_SYNC + V_BACK + V_VALID)))
                    ? 1'b1 : 1'b0;

//o_pix_x,o_pix_y:VGA有效显示区域像素点坐标
assign  o_pix_x = (pix_data_req == 1'b1)
                ? (cnt_h - (H_SYNC + H_BACK - 1'b1)) : 10'h3ff;
assign  o_pix_y = (pix_data_req == 1'b1)
                ? (cnt_v - (V_SYNC + V_BACK )) : 10'h3ff;

//o_rgb_tft:输出像素点色彩信息
assign  o_rgb_tft = (rgb_valid == 1'b1) ? pix_data : 16'b0 ;

四、总结

整个软件不复杂,RTL视图如下,主要由timer产生小时、分钟数值,经过BCD转换后输入到pic_char模块,然后将rgb输出到tft_ctl模块,最终实现上图效果,软件工程如下:源代码、字模软件、rom初始化文件等。
软件RTL图

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/144669.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

leetcode:2103. 环和杆(python3解法)

难度&#xff1a;简单 总计有 n 个环&#xff0c;环的颜色可以是红、绿、蓝中的一种。这些环分布穿在 10 根编号为 0 到 9 的杆上。 给你一个长度为 2n 的字符串 rings &#xff0c;表示这 n 个环在杆上的分布。rings 中每两个字符形成一个 颜色位置对 &#xff0c;用于描述每个…

makefile 入门

make常用选项 # make 默认在当前目录中寻找GUNmakefile,makefile,Makefile的文件作为make的输入文件 # -f 可以指定默认的输入文件名,如: -f MyMakefile # -v 显示make版本号 # -n 只输出命令,但不执行,一般用于测试 # -s 只执行命令,但不显示具体命令,与在命令中使用作用一样…

第四十四讲:神州防火墙双机热备配置

两台防火墙硬件型号和软件版本都完全相同&#xff0c;为了避免防火墙不堪重负而宕机引起网络中断&#xff0c;可以考虑应用双机热备&#xff08;HA&#xff09;解决方案。双机热备能够把两台防火墙构成一个工作组&#xff0c;一主一备&#xff0c;保证数据通信畅通&#xff0c;…

【实际开发01】- 单元测试 ( 追求正确性 )

目录 0. 单元测试 概念 / 解析 1. 为什么要进行单元测试 1. JUnit ~ Test 2. IDEA 中使用 junit 单元测试 , 不能使用 Scanner 的解决方法 3. Junit 测试 Tutorial 1. daiding 4. Test 修饰的方法必须 public 1. validatePublicVoidNoArgMethods(Test.class, false, er…

功率二极管的损耗分析和选型原则

功率二极管的损耗分析和选型原则 tip&#xff1a;参考网上资料&#xff0c;学习为主 1.二极管的分类 2.二极管的损耗组成 3.二级管的损耗分析 4.应用实例1.Flyback电源电路二极管损耗计算 5.实例应用2.BOOST电路二极管损耗计算 6.实例应用3.大功率整流桥二极管参数计算 7.选型…

sqli-labs 5~6 多命通关攻略

sqli-labs 5~6 多命通关攻略描述判断注入类型正常输入不正常输入错误输入判断 SQL 查询结果的列数猜测 SQL 查询结果中的列数为两列猜测 SQL 查询结果中的列数为三列猜测 SQL 查询结果中的列数为四列爆破方式的可行性函数 UpdateXML()爆破&#xff08;报错注入&#xff09;爆破…

农业智能化进入“刚需时代 ” ,维视智造机器视觉实验室赋能新农科人才培养

1、传统农业数字化转型 新农科人才急需紧缺数千年来&#xff0c;农业是我国立国基础&#xff0c;农业兴衰关系到国家的命运。在大力推动乡村振兴的背景下&#xff0c;高校作为强农兴农的“国之重器”&#xff0c;在培育“农”的传人、新农科建设方面扮演着不可替代的角色。世界…

C++入门——内联函数、extern “C“

一. 内联函数 1.概念及分析 以inline修饰的函数叫做内联函数&#xff0c;编译时C编译器会在调用内联函数的地方展开&#xff0c;没有函数调 用建立栈帧的开销&#xff0c;内联函数提升程序运行的效率。 int Add(int a, int b) {int c a b;return c; }int main() {int re…

聚焦技术创新实力,时序数据库 TDengine 荣登中国技术先锋年度评选两大榜单

2023 年 1 月 5 日&#xff0c;中国技术先锋年度评选 | 2022 中国最受开发者欢迎的技术活动榜单正式发布。作为中国领先的新一代开发者社区&#xff0c;SegmentFault 思否依托社区活动板块及全站数百万开发者用户行为数据&#xff0c;及活动规模、内容好评度、行业综合影响力指…

Go语言设计与实现 --调度器(详细介绍)

GMP和GM模型 先来一张图&#xff1a; G(Goroutine)&#xff1a;代表Go 协程Goroutine&#xff0c;存储了 Goroutine 的执行栈信息、Goroutine 状态以及 Goroutine 的任务函数等。G的数量无限制&#xff0c;理论上只受内存的影响&#xff0c;创建一个 G 的初始栈大小为2-4K&…

vue项目安装使用element_UI

安装element_UI之前需要安装VUE脚手架框架! 第一步: 在Vscode 随意打开一个文件夹,在集成终端打开 npm i -g vue/cli (检测: vue -V) 第二步:新建一个文件夹,并且在集成终端打开安装VUE脚手架 需要输入命令: vue create yan6 //yan6 为自定义文件名 1: 选第三个自定义 2: 将…

SpringBoot缓存数据(官方案例)

在线文档项目结构 1.源码克隆&#xff1a;git clone https://github.com/spring-guides/gs-caching.git 2.包含两个项目initial和complete&#xff0c;initial可以根据文档练习完善&#xff0c;complete是完整项目 3.功能描述&#xff1a;构建应用程序&#xff0c;在图书存储库…

JAVA并发编程工具篇--1理解线程池任务的执行和线程的销毁

前言&#xff1a;在编程中我们为什么要使用线程池&#xff0c;线程池中的线程是怎么执行任务的&#xff0c;线程池中的线程是如何复用和销毁的&#xff1b; 1 什么是线程池&#xff1a; 提前创建一些线程放到一个地方&#xff0c;使用的时候直接获取&#xff0c;避免频繁的创建…

CalDAV网页客户端AgenDAV

什么是 AgenDAV &#xff1f; AgenDAV 是一个类似于 Google 日历的 CalDAV 网络客户端&#xff0c;具有 AJAX 界面&#xff0c;允许用户管理自己的日历和共享的日历。 注意事项 AgenDAV依赖于 CalDAV 服务器&#xff08;Bakal、DAViCal 等&#xff09;&#xff0c;所以需要先安…

软件测试员在面试中常遇问题

目前&#xff0c;疫情已经逐渐得到了控制&#xff0c;各行各业都掀起了复工大潮。与此同时&#xff0c;软件测试的招聘需求也随着复工的开始而变得紧急起来&#xff0c;而求职者应该怎样抓住机会进行应聘呢&#xff1f;首先最重要的就是多刷面试题&#xff0c;这样才能才面试过…

CSS权威指南(五)字体

文章目录1.字体族2.font-face3.字重&#xff08;font-weight&#xff09;4.字号&#xff08;font-size&#xff09;5.字形&#xff08;font-style&#xff09;6.字体拉伸&#xff08;font-stretch&#xff09;7.字距&#xff08;font-kerning&#xff09;8.字体变形&#xff08…

Python 办公自动化,全网最全整理来了!拒绝无效率加班!

大家好&#xff0c;今天给大家分享一篇 Python 自动化办公干货&#xff0c;整整42个实战项目案例。每一个项目案例都有详细的视频讲解&#xff0c;是一套非常全面的Python自动化办公项目&#xff0c;建议大家收藏后学习&#xff0c;梳理不易&#xff0c;记得点赞支持。详细目录…

【菜菜的CV进阶之路 - 深度学习环境搭建】常用软件安装

四、安装网易云 双系统装完了&#xff0c;下一步当然是&#xff0c;休息一下&#xff0c;听一首歌啦~ 1、连网&#xff1a;只能使用wifi连&#xff0c;网线直连的话&#xff0c;还需要配置 2、安装网易云&#xff1a; 下载最新的Linux安装包&#xff0c;然后 sudo apt inst…

数据的存储(C语言)

数据类型&#xff1a; 要了解数据是如何存储的&#xff0c;我们就得先知道C语言中的数据类型 基本数据类型 基本数据类型&#xff0c;也就是C语言内置类型&#xff1a; char -> 字符型 short -> 短整型 int -> 整…

html textarea 插入字符在光标处

textarea 插入字符在光标处前言深度解析1 效果图上代码前言 深度解析 1 效果图 上代码 <!DOCUMENT><html><head> <link rel"stylesheet" href"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css&qu…