NEXYS 3开发板练手--USB UART(三)
接着上一篇,今天我们来建立一个能用于实际工程中的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设 ...
随机推荐
- DevExpress学习01——下载与安装
记得刚接触编程时,虽然实现了功能,但用户界面十分丑陋,老师叫我们美化一下界面,不要千篇一律,当时觉得能够写出来功能就洋洋得意了,不觉得界面丑陋.后来,在程序比赛中,我接触了一种第三方控件,它可以快速实 ...
- JDBC四(web基础学习笔记十)
一.增加 .修改.删除.查询 将功能整合在一个类中 package pb.base; import java.sql.Connection; import java.sql.DriverManager ...
- 算法笔记_001:斐波那契数的多种解法(Java)
本篇文章解决的问题来源于算法设计与分析课程的课堂作业,主要是运用多种方法来计算斐波那契数.具体问题及解法如下: 一.问题1: 问题描述:利用迭代算法寻找不超过编程环境能够支持的最大整数的斐波那契数是第 ...
- Jacoco的原理(各个覆盖率的解释)
覆盖率计数器 Jacoco使用一系列的不同的计数器来做覆盖率的度量计算.所有这些计数器都是从java的class文件中获取信息,这些class文件可以(可选)包含调试的信息在里面.即使在没有源码的情况 ...
- 一致性 hash 算法( consistent hashing )(转)
consistent hashing 算法早在 1997 年就在论文 Consistent hashing and random trees 中被提出,目前在 cache系统中应用越来越广泛: 1 基 ...
- eclipse tomcat路径更改后启动报错
eclipse tomcat路径更改后启动报错 CreateTime--2018年5月3日14:48:22 Author:Marydon 1.情景还原 2.原因 本地的tomcat路径修改后,ec ...
- 微信小程序条码、二维码生成模块
代码地址如下:http://www.demodashi.com/demo/13994.html 一.前期准备工作 软件环境:微信开发者工具 官方下载地址:https://mp.weixin.qq.co ...
- Android API之android.provider.ContactsContract.Contacts
android.provider.ContactsContract.Contacts 对应contacts数据表.RawContacts的一个聚合(aggregate)代表同一个人.每个人在数据表co ...
- 【laravel5.4 + TP5.0】hasOne和belongsTo的区别
1.从字面理解:假如A比B大,那么A hasOne B: B belongsTo A: 2.个人总结: 3.从代码角度: 主要是看你是在哪一个model(模型)中编写这个关联关系,父关联对象就是在父关 ...
- HDUOJ-------- Fibonacci again and again
Fibonacci again and again Time Limit : 1000/1000ms (Java/Other) Memory Limit : 32768/32768K (Java/ ...