FPGA接收串口数据并通过LCD1602显示

这篇具有很好参考价值的文章主要介绍了FPGA接收串口数据并通过LCD1602显示。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、前言

在学习《FPGA设计与Verilog HDL实现》第九章内容Verilog驱动常用I/O外设时,书中有一个驱动LCD1602的例程,但其是通过状态机显示固定的几个字符。本着动手实践的原则,决定利用手头的硬件实现FPGA接收串口数据并在LCD1602上显示,下面记录项目开始的过程。因为刚接触FPGA不久,如有错误欢迎大家批评指正。

  • 硬件:EP4CE6E22C8NG
  • 开发工具:Quartus II 13.0 + Modelsim + Visio
  • 全局时钟:50MHz

二、设计框图

  • 了解LCD1602的接口和数据读写时序,这部分内容也比较多,参看panhongfeng111的博客:基于FPGA的LCD1602显示屏驱动。
  • 根据需要实现的功能划分模块,我们把模块划分为串口接收LCD显示控制两部分,如图所示:
    FPGA接收串口数据并通过LCD1602显示

按照上述划分的框图,分模块把波形图绘制出来(方便后面程序的编写),不一定需要全部绘制,方便自己编写代码时捋清时序就行。

  • 串口接收部分框图

FPGA接收串口数据并通过LCD1602显示

串口接收部分时序图

FPGA接收串口数据并通过LCD1602显示

  • LCD控制部分框图

FPGA接收串口数据并通过LCD1602显示

LCD控制部分波形图(没画完,也不规范,自己画的时候能理解就行)
FPGA接收串口数据并通过LCD1602显示
三、模块代码的编写
(这部分不大好描述,稍微注释一下方便大家理解)

  • 串口接收模块代码
module uart_rx													//模块名
#(
	parameter 	UART_BPS =  14'd9600			,				//设置波特率
	parameter 	CLK_FREQ =  26'd50_000_000						//全局时钟
)
(
	input		wire				sys_clk	,							//时钟
	input		wire				sys_rst_n,							//复位
	input		wire				rx			,						//串口接收端
	
	output	reg	[7:0]		po_data	,							//输出数据
	output	reg				po_flag								//输出标志位
);

reg				rx_reg1;
reg				rx_reg2;
reg				rx_reg3;
reg				start_flag;
reg				work_en;
reg	[15:0]	baud_cnt;
reg				bit_flag;
reg	[3:0]		bit_cnt;
reg	[7:0]		rx_data;
reg				rx_flag;

parameter BAUD_CNT_MAX = CLK_FREQ / UART_BPS;			//由波特率和全局时钟计算出计数最大值

always@(posedge sys_clk or negedge sys_rst_n)			//打拍,与系统时钟同步
	if(sys_rst_n == 1'b0)
		rx_reg1 <= 1'b1;
	else 
		rx_reg1 <= rx;
		
always@(posedge sys_clk or negedge sys_rst_n)			//打两拍,同步
	if(sys_rst_n == 1'b0)
		rx_reg2 <= 1'b1;
	else 
		rx_reg2 <= rx_reg1;

always@(posedge sys_clk or negedge sys_rst_n)			//打三拍,方便产生标志信号
	if(sys_rst_n == 1'b0)
		rx_reg3 <= 1'b1;
	else 
		rx_reg3 <= rx_reg2;
		
always@(posedge sys_clk or negedge sys_rst_n)			//串口通信开始标志信号
	if(sys_rst_n == 1'b0)		
		start_flag <= 1'b0;
	else if((rx_reg2 == 1'b0) && (rx_reg3 == 1'b1) && (work_en == 1'b0))
		start_flag <= 1'b1;
	else
		start_flag <= 1'b0;
		
always@(posedge sys_clk or negedge sys_rst_n)			//串口通信标志信号
	if(sys_rst_n == 1'b0)	
		work_en <= 1'b0;
	else if(start_flag == 1'b1)
		work_en <= 1'b1;
	else if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
		work_en <= 1'b0;
	else
		work_en <= work_en;
		
always@(posedge sys_clk or negedge sys_rst_n)			//根据波特率计数
	if(sys_rst_n == 1'b0)	
		baud_cnt <= 16'd0;
	else if((baud_cnt == BAUD_CNT_MAX - 1'b1) || (work_en == 1'b0))
		baud_cnt <= 16'd0;
	else 
		baud_cnt <= baud_cnt + 1'b1;
		
always@(posedge sys_clk or negedge sys_rst_n)			//产生读取数据标志位
	if(sys_rst_n == 1'b0)	
		bit_flag <= 1'b0;
	else if(baud_cnt == BAUD_CNT_MAX / 2 - 1)
		bit_flag <= 1'b1;
	else
		bit_flag <= 1'b0;
		
always@(posedge sys_clk or negedge sys_rst_n)			//对接收位数计数
	if(sys_rst_n == 1'b0)	
		bit_cnt <= 4'd0;
	else if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
		bit_cnt <= 4'd0;
	else if(bit_flag ==1'b1)
		bit_cnt <= bit_cnt + 1'b1;
	else 
		bit_cnt <= bit_cnt;
		
always@(posedge sys_clk or negedge sys_rst_n)			//移位接收字节数据
	if(sys_rst_n == 1'b0)
		rx_data <= 8'd0;
	else if((bit_cnt >= 4'd1) && (bit_cnt <= 4'd8) && (bit_flag == 1'b1))
		rx_data <= {rx_reg3,rx_data[7:1]};
		
always@(posedge sys_clk or negedge sys_rst_n)			//完成接收一字节标志位
	if(sys_rst_n == 1'b0)
		rx_flag <= 1'b0;
	else if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
		rx_flag <= 1'b1;
	else 
		rx_flag <= 1'b0;
	
always@(posedge sys_clk or negedge sys_rst_n)			//接收数据
	if(sys_rst_n == 1'b0)
		po_data <= 8'd0;
	else if(rx_flag == 1'b1)
		po_data <= rx_data;
		
always@(posedge sys_clk or negedge sys_rst_n)			//同步接收数据标志信号
	if(sys_rst_n == 1'b0)
		po_flag <= 1'b0;
	else
		po_flag <= rx_flag;
		
endmodule
  • LCD控制模块代码
module lcd_ctrl
(
	input		wire						sys_clk		,
	input		wire						sys_rst_n	,
	input		wire		[7:0]			po_data		,
	input		wire						po_flag		,
	                  
	output	reg						lcd_rs		,
	output	reg						lcd_rw		,
	output	reg						lcd_en		,
	output	reg		[7:0]			lcd_data		
);
			
//---------------------接收字节计数--------------------------

reg	[7:0]		byte_reg [0:31];
reg	[7:0]		byte_cnt		;
integer			i;

always@(posedge sys_clk , negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		byte_cnt <= 8'd0;
	else if((byte_cnt == 8'd255) && (po_flag == 1'b1))
		byte_cnt <= 8'd0;
	else if(po_flag == 1'b1)
		byte_cnt <= byte_cnt + 1'b1;
	else		
		byte_cnt <= byte_cnt;
		
always@(posedge sys_clk , negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		begin
			for(i=0;i<32;i=i+1)
				byte_reg[i] = 8'd0;
		end
	else if(po_flag == 1'b1)
		byte_reg[byte_cnt % 32] <= po_data;

//----------------产生lcd1602使能驱动clk_en--------------------

reg	[31:0]	clk_cnt	;
reg				clk_en	;

always@(posedge sys_clk , negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		clk_cnt <= 32'd0;
	else if(clk_cnt == 32'd49999)
		clk_cnt <= 32'd0;
	else
		clk_cnt <= clk_cnt + 1'b1;
		
always@(posedge sys_clk , negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		clk_en <= 1'b0;
	else if(clk_cnt == 32'd49999)
		clk_en <= 1'b1;
	else
		clk_en <= 1'b0;
		
//----------------LCD1602显示状态机----------------------------

localparam	IDLE			=	4'd0	,
				MODE_SET 	= 	4'd1	,
				MODE_SET1 	= 	4'd2	,
				CURSOR_SET	=	4'd3	,
				CURSOR_SET1	=	4'd4	,
				CADDR_SET	= 	4'd5	,
				CADDR_SET1	=	4'd6	,
				CLEAR_SET	=	4'd7	,
				CLEAR_SET1	=	4'd8	,
				ADDR_SET		=	4'd9	,
				ADDR_SET1	=	4'd10	,
				DATA_SET		=	4'd11	,
				DATA_SET1	=	4'd12	;
				
parameter	MODE_COM		= 	8'h38	,
				CURSOR_COM	=	8'h0c	,
				CADDR_COM	=	8'h06	,
				CLEAR_COM	= 	8'h01	,
				ADDRESS1		= 	8'h80	,
				ADDRESS2		=	8'hc0	;
				
reg		[3:0]		state			;
reg		[7:0]		send_cnt		;

always@(posedge sys_clk , negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		begin
			state <= IDLE;
			lcd_rs <= 1'b0;
			lcd_en <= 1'b0;
			lcd_rw <= 1'b0;
			lcd_data <= 8'd0;
			send_cnt <= 8'd0;
		end
	else if(clk_en == 1'b1)
		begin
			case(state)
			IDLE	:
						begin
							if(byte_cnt >= 1'b1)
								state <=	MODE_SET;
							else
								state <= IDLE;
							lcd_rs <= 1'b0;
							lcd_en <= 1'b0;
							lcd_rw <= 1'b0;
							lcd_data <= 8'd0;
						end
			MODE_SET	:
						begin
							state <=	MODE_SET1;
							lcd_rs <= 1'b0;
							lcd_en <= 1'b1;
							lcd_rw <= 1'b0;
							lcd_data <= MODE_COM;
						end
			MODE_SET1	:
						begin
							state <=	CURSOR_SET;
							lcd_en <= 1'b0;
						end
			CURSOR_SET	:
						begin
							state <=	CURSOR_SET1;
							lcd_rs <= 1'b0;
							lcd_en <= 1'b1;
							lcd_rw <= 1'b0;
							lcd_data <= CURSOR_COM;
						end
			CURSOR_SET1	:
						begin
							state <=	CADDR_SET;
							lcd_en <= 1'b0;
						end		
			CADDR_SET	:
						begin
							state <=	CADDR_SET1;
							lcd_rs <= 1'b0;
							lcd_en <= 1'b1;
							lcd_rw <= 1'b0;
							lcd_data <= CADDR_COM;
						end
			CADDR_SET1	:
						begin
							state <=	CLEAR_SET;
							lcd_en <= 1'b0;
						end	
			CLEAR_SET	:
						begin
							state <=	CLEAR_SET1;
							lcd_rs <= 1'b0;
							lcd_en <= 1'b1;
							lcd_rw <= 1'b0;
							lcd_data <= CLEAR_COM;
						end	
			CLEAR_SET1	:
						begin
							state <=	ADDR_SET;
							lcd_en <= 1'b0;
						end
			ADDR_SET	:
						begin
							state <=	ADDR_SET1;
							lcd_rs <= 1'b0;
							lcd_en <= 1'b1;
							lcd_rw <= 1'b0;
							if(send_cnt % 32 == 5'd15)
								lcd_data <= ADDRESS2;
							else if(send_cnt % 32 == 5'd0)
								lcd_data <= ADDRESS1;
							else
								lcd_data <= lcd_data;
						end		
			ADDR_SET1	:
						begin
							state <=	DATA_SET;
							lcd_en <= 1'b0;
						end	
			DATA_SET	:
						begin
							state <=	DATA_SET1;
							lcd_rs <= 1'b1;
							lcd_en <= 1'b1;
							lcd_rw <= 1'b0;
							lcd_data <= byte_reg[send_cnt];
							if(send_cnt < 8'd255)
								send_cnt <= send_cnt + 1'b1;
							else if(send_cnt == 8'd255)
								send_cnt <= 8'd0;
						end
			DATA_SET1	:
						begin
							if(byte_cnt > send_cnt)
								begin
									if(send_cnt == 5'd15)
										state <=	ADDR_SET;
									else
										state <= DATA_SET;
								end
							else
								state <= DATA_SET1;
							lcd_en <= 1'b0;
						end
			default	:
						begin
							state <= IDLE;
							lcd_rs <= 1'b0;
							lcd_en <= 1'b0;
							lcd_rw <= 1'b0;
							lcd_data <= 8'd0;
							send_cnt <= 5'd0;
						end
			endcase
		end
	else
		begin
			lcd_rs <= lcd_rs;
			lcd_en <= lcd_en;
			lcd_rw <= lcd_rw;
			lcd_data <= lcd_data;
			send_cnt <= send_cnt;
		end
		
endmodule

四、顶层模块和Verilog Test Bench代码

  • 顶层模块代码
module lcd1602_test
(
	input		wire				sys_clk		,
	input		wire				sys_rst_n	,
	input		wire				rx				,

	output	wire				lcd_rs		,
	output	wire				lcd_rw		,
	output	wire				lcd_en		,
	output	wire	[7:0]		lcd_data		
);

wire		[7:0]		po_data	;
wire					po_flag	;

uart_rx
#(
	.UART_BPS (14'd9600			),
	.CLK_FREQ (26'd50_000_000	)
)
uart_rx_inst
(
	.sys_clk		(sys_clk		),
	.sys_rst_n	(sys_rst_n	),
	.rx			(rx			),
	
	.po_data		(po_data		),
	.po_flag		(po_flag		)
);

lcd_ctrl		lcd_ctrl_inst
(
	.sys_clk		(sys_clk		),
	.sys_rst_n	(sys_rst_n	),
	.po_data		(po_data		),
	.po_flag		(po_flag		),
	
	.lcd_rs		(lcd_rs		),
	.lcd_rw		(lcd_rw		),
	.lcd_en		(lcd_en		),
	.lcd_data	(lcd_data	)		
);

endmodule

  • Test Bench仿真代码
`timescale 1ns/1ns

module tb_lcd1602_test();

reg					sys_clk;
reg					sys_rst_n;
reg					rx;

wire					lcd_rs	;
wire					lcd_rw	;
wire					lcd_en	;
wire		[7:0]		lcd_data	;

initial
	begin
		sys_clk <= 1'b1;
		sys_rst_n <= 1'b0;
		rx <= 1'b1;
		#20
		sys_rst_n <= 1'b1;
		
	end

always #10 sys_clk <= ~sys_clk;

task	rx_byte();
	
	integer	j;
	for(j = 0; j < 8; j = j + 1)
		rx_bit(j);
		
endtask	

initial
	begin
		#200
		rx_byte();
		rx_byte();
		rx_byte();
		rx_byte();
		rx_byte();
		rx_byte();
	end


task	rx_bit
(
	input		reg	[7:0]		data
);

integer i;

for(i = 0;i < 10;i = i + 1)
	begin
		case(i)
			0: rx <= 1'b0;
			1: rx <= data[0];
			2: rx <= data[1];
			3: rx <= data[2];
			4: rx <= data[3];
			5: rx <= data[4];
			6: rx <= data[5];
			7: rx <= data[6];
			8: rx <= data[7];		
			9: rx <= 1'b1;
		endcase
		#(5208*20);
	end
	
endtask

lcd1602_test	lcd1602_test_inst
(
	.sys_clk		(sys_clk		),
	.sys_rst_n	(sys_rst_n	),
	.rx			(rx			),

	.lcd_rs		(lcd_rs		),
	.lcd_rw		(lcd_rw		),
	.lcd_en		(lcd_en		),
	.lcd_data	(lcd_data	)	
);

endmodule

五、仿真结果和上板验证

  • 仿真结果

FPGA接收串口数据并通过LCD1602显示文章来源地址https://www.toymoban.com/news/detail-462173.html

  • 上板验证
    串口调试助手发送 123456789ABCDEFGHIJKLM
    FPGA接收串口数据并通过LCD1602显示
    LCD1602显示
    FPGA接收串口数据并通过LCD1602显示

到了这里,关于FPGA接收串口数据并通过LCD1602显示的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • FPGA-串口接收图像写入RAM并读出在TFT显示屏上显示

    需要用到的模块有: 1,UART_RX(串口接收模块); 2,串口接受的数据存放到RAM模块; 3,RAM IP核; 4,时钟IP核 (TFT显示屏驱动时钟的产生); 5,TFT显示驱动模块; 具体构建方式及详见(其中的串口接收部分) FPGA-UART串口 https://blog.csdn.net/weixin_46897065/article/details/135586405?spm=

    2024年03月19日
    浏览(37)
  • LCD1602液晶显示屏

    主函数 LCD1602.c LCD1602.h 接线图:   1、1602屏幕=16x2=32个字符,总共有32个字符  2、每个字符由35个像素组成 每个像素由一小块液晶控制 --------------------------------------------------------------------------------------------------------------------------------- 液晶的控制原理: 不施加电压——液晶完

    2024年02月07日
    浏览(37)
  • LCD1602的使用及显示

    第 1 脚: VSS 为电源地 第 2 脚: VDD 接 5V 正电源 第 3 脚: VL 为液晶显示器对比度调整端,接正电源时对比度最弱,接地时对比度最高,对比度过高时会产生“鬼影”,使用时可以通过一个 10K 的电位器调整对比度。 第 4 脚: RS 为寄存器选择 , 高电平时选择数据寄存器、低电平时

    2024年02月07日
    浏览(73)
  • 实验(四):LCD1602显示实验

            实验目的:                 1. 掌握LCD1602显示控制方法;                 2. 掌握利用Proteus进行单片机控制系统的仿真及调试方法。                 3. 掌握单片机开发板的使用。         任务:                 1.根据要求编写程

    2024年02月02日
    浏览(31)
  • LCD1602液晶显示模块

    1、概述: LCD1602(Liquid Crystal Display)是一种工业字符型液晶,能够同时显示 16×02,32个 字符(16列两行)。是我们接触引脚最多的模块。 LCD1602我们的非标准协议(标准协议有IIC、IIS、SPI)中比较容易懂的玩法。 2、引脚说明:翻阅LCD1602说明书 共有16根引脚,如下表: 编号 符号

    2024年02月16日
    浏览(36)
  • 单片机lcd1602的显示

    首先,你需要了解LCD1602的引脚功能。LCD1602有16个引脚,其中包括: Gnd:电源地脚 Vcc:5V电源正极 VL:液晶显示器对比度调整端,接正电源时对比度最弱,接地电源时对比度最高 RS:寄存器选择,高电平1时选择数据寄存器、低电平0时选择指令寄存器 RW:读写信号线,高电平(

    2024年01月17日
    浏览(30)
  • 矩阵键盘控制LCD1602显示屏显示数字

     主函数部分,其中的LCD1602.h的头文件是在哔哩哔哩江科大自化协的博主的视频资料 总结:     首先是我学习时遇到的问题: 在我一开始运行的时候出现的问题就是,一开始在给主函数的keynumber赋值的时候,等号的左值是叫做Matrixkey的函数,当我按下1按键时显示屏显示01,

    2024年02月11日
    浏览(30)
  • 用lcd1602制作简单的时钟显示

    本文最终的效果如图,lcd上能显示一个电子时钟,并能正确的显示时间。  如图打开proteus,导入元器件并连线,如果是实物的话,别忘了购买lcd屏上的对比度调节变阻器,不然显示不出来,对比度不是太浅就是太深,根本就看不到,需要注意。我就上了这个坑了,没有哪个文

    2024年02月11日
    浏览(36)
  • 初识LCD1602及编程实现字符显示

    LCD1602是一种工业字符型液晶,能够同时显示16x02即32字符(16列两行) 引脚说明 第 1 脚 : VSS 为电源地 第 2 脚 : VDD 接 5V 正电源 第 3 脚 : VL 为液晶显示器对比度调整端,接正电源时对比度最弱,接地时对比度最高,对比度过高时会产生“鬼影”,使用时可以通过一个 10K 的电位

    2023年04月18日
    浏览(31)
  • STM32显示外设集--液晶显示模块(LCD1602)

    一、介绍 产品图 二、 资源获取 欢迎关注微信公众号--星之援工作室 发送(LCD1602) 三、线路连接图 四、代码编写 LCD1602.h LCD1602.c main.h 五、参考 LCD1602是一种字母数字液晶显示模块,可以显示16个字符和2行文本,因此被命名为LCD1602。它通常用于各种电子项目,特别是

    2024年02月11日
    浏览(32)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包