接着上一篇,今天我们来建立一个能用于实际工程中的DEMO。
首先,为了使我们的发送机不像上一个DEMO一样无节制的循环发送,我们需要修改代码,增加使发送机停止发送的控制部分,修改后的代码如下:
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer: lwy
//
// Create Date: 16:00:47 11/11/2013
// Design Name: usb-uart tx device for NEXYS3
// Module Name: UART_CTRL
// Project Name:
// Target Devices:
// Tool versions:
// Description:This component may be used to transfer data over a UART device. It will
// serialize a byte of data and transmit it over a TXD line. The serialized
// data has the following characteristics:
// *9600 Baud Rate
// *8 data bits, LSB first
// *1 stop bit
// *no parity
//
// Dependencies:
// Port Descriptions:
// clk -- 外部100M时钟输入;
// data -- 需要传输的数据,8位;
// send -- 发送使能端,低电平开始一个字符传输;
// tx -- 向uart device发送的串行数据;
// busy -- 线路状态标志,高时表示线路忙,低时表示线路空闲
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module UART_CTRL(
clk,
data,
send,
tx,
busy
); input clk,send;
input [:] data;
output reg tx,busy; //状态机状态定义
parameter Idel = 'b00,//空闲状态
Rdy = 'b01,//数据准备完成
LoadByte = 'b10,//数据传入
SendBit = 'b11;//数据发送 reg [:] BspClkReg;//波特率分频计数
reg BspClk;//波特率时钟 reg [:] tx_data;//发送的数据,加上起始位和停止位
reg [:] tx_byte_count;//发送位数计数 reg [:] state;//状态寄存器 //波特率分频模块,波特率:9600
always@(posedge clk)
begin
BspClkReg <= BspClkReg + ;
if(BspClkReg == )
begin
BspClkReg <= ;
BspClk <= ~BspClk;
end
end
//串口发送模块
always@(posedge BspClk)
begin
case(state)
Idel : begin
tx <= ;
busy <= ;
tx_byte_count <= ;
if(send) state <= Rdy;
end
Rdy : begin
if(~send) state <= Idel;
else begin
tx_byte_count <= ;
tx <= ;
busy <= ;
state <= LoadByte;
end
end
LoadByte : begin
if(~send) state <= Idel;
else begin
tx_data <= {'b1,data,1'b0};
tx <= ;
busy <= ;
state <= SendBit;
end
end
SendBit : begin
if(~send) state <= Idel;
else begin
tx <= tx_data[];
busy <= ;
tx_data <= tx_data >> ;
tx_byte_count <= tx_byte_count + ;
if(tx_byte_count == )
state <= Idel;
else
state <= SendBit;
end
end
endcase
end endmodule
对比上一个DEMO中的状态机部分,我们只是在空闲状态外的每个状态中增加了当send信号无效时使状态机返回空闲状态的控制代码,这样我们就可以通过这个send信号来控制是否使发送机发送信息。
接下来我们要做的工作就是怎么有效的向发送机输送我们需要发送的信息,并控制发送机正确的运转。要做到这一点,就需要发送机有一个反馈信号,告诉发送控制模块是否已经完成了一次发送工作。其实,我们仔细再看上面的代码,会发现有一个信号我们一直没有管它,那就是busy信号,这个信号就是考虑到这一点而做的准备工作。当发送机处在空闲状态,即不发送信息时,它是低电平,其他时候是高电平,也就是说当我们完成了一次发送工作(一个字符发送完成)的时候,会有一个下降沿。我们的发送控制部分即可捕捉这个下降沿来改变发送机输入端的信息,从而控制发送机发送下一个需要发送的不同字符。这一部分的控制代码如下:
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer: lwy
//
// Create Date: 16:00:47 11/11/2013
// Design Name: usb-uart tx device for NEXYS3
// Module Name: usbuart
// Project Name:
// Target Devices:
// Tool versions:
// Description: 这个模块用来控制向UartCtrl模块的写入字符串数据
//
// Dependencies:
// Port Descriptions: CLK -- 系统时钟;
// reset -- 系统复位信号
// btn -- 字符串发送命令,高电平有效,这里接入一个button
// TxBit -- 串行字符输出,接UartCtrl模块的tx端,即最终串行数据通过这个端口相device传送
// Revision:
// Revision 0.01 - File Created
// Additional Comments: 请注意strLen、string这两个常数,如果要改变发送的字符串内容,只需将string赋值为需要发送
// 的字符串,将strLen赋值为发送字符串长度即可。
//
//////////////////////////////////////////////////////////////////////////////////
module usbuart(
CLK,
reset,
btn,
TxBit
); input CLK,reset,btn;
output TxBit; //////////////////////////////////////////////////////////////////////////////////
parameter strLen = ; //字符串长度
parameter string = "Hello USB_UART!";//字符串数据
////////////////////////////////////////////////////////////////////////////////// reg [:] TxData;//当前发送的数据
reg send_n;//发送使能,当启动一次数据发送且没有收到UART_CTRL数据发送完成的信号是,因该将它一直保持为高电平
wire lock;//控制数据的改变,当UART_CTRL数据线“忙”(busy为高电平)时,应禁止改变数据,而且send_n应该一直为高
reg [:] charCount;//当前发送字符数,一次最多发送255个字符
reg [:] Index;//当前字符位索引 reg [*strLen:] str_UART;//存储字符代码 //状态定义
parameter s0 = 'b00,s1 = 2'b01,s2 = 'b10,s3 = 2'b11; reg [:] state;//状态变量
wire trigger;//触发信号,由固定脉冲触发模块输出
wire trigger_n;//将高电平触发信号改为低电平
assign trigger_n = ~trigger; //发送字符控制
always@(negedge trigger_n or negedge lock)
begin
if(~trigger_n) //低电平触发发送
begin
str_UART = string;//初始化字符串
state = s1;
TxData = ;//\n
send_n = ;
charCount = strLen;
end
else begin
case(state)
s0 : begin
TxData = ;
send_n = ;
end
s1 : begin
state = s2;
TxData = ;//\r
send_n =;
end
s2 : begin
Index = charCount * - ;
TxData = str_UART[Index-:];//发送字符串
send_n = ;
charCount = charCount - ;
if(charCount == )
state = s0;
else
state = s2;
end
default : state = s0;
endcase
end
end //调用UART_CTRL模块
UART_CTRL UartCtrl(
.clk(CLK),
.data(TxData[:]),
.send(send_n),
.tx(TxBit),
.busy(lock)
);
//调用PulTri模块,产生稳定的触发信号
PulTri pulse(
.clk(CLK),
.reset_n(~reset),
.start(btn),
.pulse(trigger)
); endmodule
发送字符控制部分由两个信号触发,分别是trigger_n和lock,trigger_n连接按键信号取反后的信号(开发板上的按键按下为高电平),lock连接反馈busy信号。按键按下后触发一次字符串的发送,首先发送换行符(\n\r),然后根据字符串字符个数的设置,循环传送相关字符的ASCII码,这时候状态机的运转靠反馈busy信号触发。当一次字符串发送完成后,将send置为无效,这样发送机停止运转,反馈busy信号也就无效了,开始等待下一个trigger_n信号的触发。通过这三个信号相互配合就能完成一个字符串的发送。
问题到这仍然还有,因为我们这个trigger_n检测的是按键电平信号,我们知道一次按键按下,速度再快也有几百毫秒的时间,也就是说这个电平信号会持续上百毫秒甚至几秒的时间,而且这个时间通常是不受控制的。这样的触发信号在我们这个模块中是不能接受的。我们需要的这个触发信号每次持续的有效时间不能超过1/9600秒,否则整个时序控制就会错乱,发送状态机接下来的工作会是一种无法预知的状态。因此,我们需要改造这个触发信号,即无论按键按下的时间有多长,最后得到的触发信号的宽度是一定的,或则说我们利用的是按键的边沿信号。为此,我设计了一个能产生稳定触发信号的模块,也上面的代码中例化的PulTri这个模块。这个模块的设计代码如下:
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer: lwy
//
// Create Date: 23:02:53 11/12/2013
// Design Name:
// Module Name: PulTri
// Project Name:
// Target Devices:
// Tool versions:
// Description: 这个模块用来产生pulsewide个时钟宽度的脉冲(高电平)而不关心触发信号脉冲宽度是多少,该模块的输出脉冲宽度固定为pulsewide个时钟周期
//
// Port Descriptions: clk -- 系统时钟,他关系到最后的输出脉冲宽度
// reset_n -- 系统复位信号,低电平复位
// start -- 触发信号端口,高电平触发
// pulse -- 输出脉冲,该脉冲宽度固定为
//
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// pulsewide这里定义为一常数5,可以根据需要调整
//
//////////////////////////////////////////////////////////////////////////////////
module PulTri(
clk,
reset_n,
start,
pulse
); input clk,reset_n,start;
output reg pulse; parameter pulsewide = ;//调整触发脉冲的宽度,pulsewide*clk reg counten;
reg [:] count; initial
begin
counten <= ;
count <= ;
pulse <= ;
end //计数器启动标记,表示一次延时计数开始
always @ ( posedge clk )
begin
if ( reset_n == 'b0 )
counten <= 'b0;
else
begin
if ( start == 'b1 )
counten <= 'b1;
else if ( start == 'b0 && count > pulsewide )
counten <= 'b0;
end
end //延时计数器,保证延时 pulsewide 个时钟周期
always @ ( posedge clk )
begin
if ( reset_n == 'b0 )
count <= ;
else
begin
if ( counten == 'b0 )
count <= ;
else if ( counten == 'b1 && count <= pulsewide )
count <= count + ;
else if ( counten == 'b0 && start == 1'b0 )
count <= ;
end
end //输出定宽脉冲
always @ ( negedge clk )
begin
if ( reset_n == 'b0 || count >= pulsewide )
pulse <= 'b0;
else if ( counten == 'b1 )
pulse <= 'b1;
end endmodule
为了验证它是否达到了我们预先的目的,我在modelsim中进行了仿真,得到下面的波形图:
start信号是我们的随机触发信号(在这个DEMO中即是按键信号),我们发现无论这个start信号的脉宽是多少,最后得到的pulse信号的宽度都是一定的,在上面的代码中我们知道它的宽度为pulsewide*clk,是我们可以设定的(仿真中pulsewide为5),说明这个模块达到了我们预先的目的。其实,这个模块在实际的应用中是很有价值的,我们可以理解为它将一种非理想的状况转换成了一种接近理想的状况。
最后,我们的将编译生成的bit数据流文件下载进板子中,能在PC的超级终端(或则串口调试助手)中得到下面的结果:
可见,上面的DEMO与上一篇文章中的相比已经有了很大的改观,这是一个真正有实用价值的DEMO!如果我们像Quartus中一样将它做成一个SOPC嵌入式IP核,将顶层模块中的string和strLen这两个常数改成能用软件设置的寄存器,这样我们就能在软件中编程完成各种字符的发送工作,这是不是很有意义呢!
NEXYS 3开发板练手--USB UART(三)的更多相关文章
-
NEXYS 3开发板练手--USB UART(一)
接上一篇文章,今天来讲讲这个USB UART串口发送机. 我们知道,当我们的微处理器(单片机.FPGA.DSP等)要和电脑进行通信的时候一般会采用串行通信方式,而最常用的串行通信协议的物理层接口是RS ...
-
NEXYS 3开发板练手--USB UART(二)
上一篇文章中提到实际上我们操作的只是一个“伪”USB协议,我们真正需要完成的收发机遵循的协议应该是异步串行通信协议.这个协议对于大家来说应该是再熟悉不过了,在这里我就不多废话了.需要说明的是,我在这个 ...
-
NEXYS 3开发板练手--LED与数码管时钟
做科研的时候从学校拿到一块基于Xilinx公司Spartan-6主芯片的FPGA开发板,因为之前一直在用Altera公司的FPGA,一开始接触它还真有点不太习惯.但毕竟核心的东西还是不会变的,于是按照 ...
-
基于uFUN开发板的心率计(三)Qt上位机的实现
前言 上两周利用周末的时间,分别写了基于uFUN开发板的心率计(一)DMA方式获取传感器数据和基于uFUN开发板的心率计(二)动态阈值算法获取心率值,介绍了AD采集传感器数据和数据的滤波处理获取心率值 ...
-
canvas练手项目(三)——Canvas中的Text文本
Canvas中的Text文本也是一个知识点~,我们需要掌握一下几个基本的Text操作方法 首先是重要参数textAlign和textBaseline: textAlign left center ri ...
-
练手WPF(三)——扫雷小游戏的简易实现(下)
十四.响应鼠标点击事件 (1)设置对应坐标位置为相应的前景状态 /// <summary> /// 设置单元格图样 /// </summary> /// <para ...
-
练手WPF(三)——扫雷小游戏的简易实现(上)
一.创建项目1.创建WPF项目,设置初始化窗口大小(初级难度):高x宽为430x350.2.添加文件夹Images,并添加相关图片. 3.xaml中引入图片资源. <Window.Resourc ...
-
练手WPF(三)——扫雷小游戏的简易实现(中)
八.随机布雷 /// <summary> /// 随机布地雷 /// </summary> /// <param name="mineNum"> ...
-
基于STM32L476开发板的USB音频设备
现代音频设备中有很多知识产权. 我想研究创建一个与手机交互的算法设备(运行non-trivial算法的嵌入式设备). 我发现创建一个Lightning设备比创建一个连接到Android手机的的USB设 ...
随机推荐
-
Cenos7 编译安装 Mariadb Nginx PHP Memcache ZendOpcache (实测 笔记 Centos 7.0 + Mariadb 10.0.15 + Nginx 1.6.2 + PHP 5.5.19)
环境: 系统硬件:vmware vsphere (CPU:2*4核,内存2G,双网卡) 系统版本:CentOS-7.0-1406-x86_64-DVD.iso 安装步骤: 1.准备 1.1 显示系统版 ...
-
再次认识 vertical-align
css中的基础知识,上次在刷 segmentfault 遇见了一个相关的问题有再次看过 vertical-align 的描述.今天自己也遇见一个类似的问题,再次深入学习一下. vertical-ali ...
-
equals(==),toString
equals/==: 只有指向同一个对象是,才返回true. 特殊: String s1 = new String("hello"); String s2 = new String ...
-
转载:Cellebrite携两大移动数据服务强势来华
[IT168专稿]随着移动互联网的发展,智能终端也越来越普及,围绕整个移动互联网的产业链产生了巨大的商机.有这么一家做移动数据传输服务的厂商,他们一直专注在移动领域,为运营商和零售商以及司法部门提供服 ...
-
linux 开启wifi热点
1,在网络连接管理中创建一个wifi连接,点击 Add,然后选Wi-Fi 2,设置wifi热点名字.wifi接连名字 3,设置 Mode 选 Ad-hoc,其它默认. 4,在 Wi-Fi Securi ...
-
Qt之镜像旋转
简述 Qt中可以对图片进行任何处理,改变亮度.灰度.透明度.大小.形状等,当然也可以进行镜像旋转! 简单的几行代码,有时就可以事半功倍...甚至图片不用经过美工处理就可以直接拿来使用! 简述 实现 原 ...
-
又来折腾Linux
硬盘坏了之后就没装过Linux了,因为弄了一个很老的台式机的80G并口硬盘,根本不够用的,一直懒得理. 前段时间实验室的老机子得报销了,但是里面的东西还可以拆下来,所以又拆下了两个硬盘,这样就有三个8 ...
-
web.xml里<;filter-mapping>;中的<;dispatcher>;作用
2.4版本的servlet规范在部属描述符中新增加了一个<dispatcher>元素,这个元素有四个可能的值:即REQUEST,FORWARD,INCLUDE和ERROR,可以在一个< ...
-
Win7 NFS 设置详解 | X-Space
Win7 NFS 设置详解 | X-Space Win7 NFS 设置详解
-
支付宝sdk集成,报系统繁忙 请稍后再试(ALI64)
移动快捷支付,往往需要集成支付宝的sdk,集成的过程相对简单,只要按照支付宝的文档,进行操作一般不会出问题. 下面主要说明一下,集成sdk后报"系统繁忙 请稍后再试(A ...