上一节中成功实现了发送多个字节的数据。把需要发送的数据分成多段遵循uart协议的数据依次发送。上一节是使用状态机实现的,每发一次设定为一个状态,所以需要发送的数据越多,状态的个数越多,代码越长,因而冗长且适应范围不广 。

在这里,我通过优化代码,实现了把发送状态固定为3个,并且能适用任意长度的输入数据的功能。只需要修改一个参数即可实现。

学习:

1.error:cannot index into non-array type wire for 'dataN'

出现这个错误是因为dataN没有定义长度,添加定义【】即可。

2.想要修改/取出一个大的寄存器中的某几位,如要取dataN[10:18],我想用变量limits,limitx来作为上下限,这样子能实现取出不同段的值。按照这种想法我写成:data <= dataN [limitx:limits],会报错 : error:range must be bounded by constant expressions要求用常数来限制边界。

解决方法:写成 data <= dataN[(limitx)+:8] 意思是从limitx开始,向上取8位,limit可以是变量。

优化:我把下限写成寄存器变量limitx,在data赋值语句的begin-end内,即:

begin

limitx <= x *8 ;

data <= dataN[(limitx)+:8] ;

x <= x +1 ;

end

综合出来会有个limitx寄存器,data寄存器,一个clk上升沿到来,他们同时变化,limit会取上一个x的输出x0,data会取上一个limit的输出limit0,这样子data就延后了一个limit的输出;而我的目的是limitx变化后,再让data取变化后的limit1计算,这个代码无法实现这个目的。

解决:直接去掉limitx,写成

begin

data <= dataN[(x *8)+:8] ;

x <= x +1 ;

end

这样子就少了一个寄存器,节省资源,而且可以实现data取到前一个x的限定范围,不会出错。

观察波形可以发现这个问题,所以后面写代码时,能用计算式代替的尽量不要多设置一个寄存器。特别是运算都在同一个begin-end里面的情况。

3.底层的输出在顶层不可以被赋值,要被定义成wire。输入可以定义成reg,赋值。

4.always中的复位信号reset中应该复位的变量应包含所有本次always用到的变量,不复位的话仿真时会出现未知状态X。

5.再次确认,非阻塞赋值无论在begin-end中的多个语句顺序如何,综合出来的RTL级电路图是不变的。

 6.用parameter定义一个常数,那么在后面他是不能被重写的。不过例化时可以重新定义一次。

7.从串口应用的学习中,要懂得,开始位,结束位的意义。这两个位可以作为指示与标志,为发送系统增添功能,改变适应范围。也要注意使能端的意义,可以让系统分为工作和空闲两种状态。也要懂得状态机的使用,由多状态固定到指定状态的优化。最最重要的是,通过观察仿真波形来修改某个信号的判断条件,从而达到优化/修正的目的。

`timescale 1ns / 1ns
module uart_6_tb(
);
reg clk ;
reg reset ;
reg [79:0]dataN ;
reg send_pulse ;
wire uart_tx ;
wire sign ; uart_6_optimization uart_6_sim(//隐式例化
clk,
reset,
send_pulse,
dataN,
sign,
uart_tx
); initial clk = 1;
always #10 clk = ! clk ;
initial begin
reset = 0 ;
dataN = 40'd0 ;
send_pulse = 1'b0 ;
#201 ;
reset = 1 ;
dataN = 80'h123456789 ;
#200 ;
send_pulse = 1'b1 ;
#20;
send_pulse = 1'b0 ;
@(negedge sign) ;
dataN = 80'ha98765432 ;
#200 ;
send_pulse = 1'b1 ;
#20;//设置为20ns时识别不出,会出错。设置21可以解决//多给200ns延迟后错误消失,用20ns也可以
send_pulse = 1'b0 ;
@(negedge sign) ;
#200 ;
$stop ;
end endmodule
module uart_6_optimization(//实现优化的目标,输入N位的数据,利用固定的三状态实现。
clk ,//想要改变输入的位数,只需要修改dataN的位数定义和parameter N 即可
reset ,
send_pulse ,
dataN ,
sign ,
uart_tx
);
input clk ;
input reset ;
input send_pulse ;
input [79:0]dataN ;
output sign ;
output uart_tx ; reg [7:0]data ;
reg send_en ;
wire baud_rate ;
wire tx_done ;
reg sign ;
reg tx_done_ctrl ; uart_1_2 uart_6_opti( //设计输入
clk,//时钟
reset,//复位
data,//数据
send_en,//使能
tx_done_ctrl,
baud_rate,//波特率
uart_tx,//串口输出
tx_done//结束信号
);
assign baud_rate = 3'd5 ; reg [1:0]state ;
reg [3:0]x ;
parameter N = 37 ;//输入的位数 always@(posedge clk or negedge reset)
if (!reset )
begin
data <= 8'd0 ;
send_en <= 1'b0 ;
state <= 2'd0 ;
sign <= 1'b0 ;
tx_done_ctrl <= 1'b0 ;
x <= 4'd0 ;
end
else if( send_pulse == 1'd1 )
begin
state <= 2'd1 ;
x <= 4'd0 ;
tx_done_ctrl <= 1'b1 ;
sign <= 1'b1 ;
end
else if(tx_done_ctrl == 1'b1 )
tx_done_ctrl <= 1'b0 ;
else if (( state == 2'd1 ) && ( (x) * 8 < N ) && (tx_done == 1'b1 ))
begin
send_en <= 1'b1 ;
x <= x + 1'b1 ;//说明在begin end里面,非阻塞赋值综合出来的还是对变量的综合设计寄存器是有先后顺序的,综合后对一个时钟沿到来,data先变,x再变。emm,也不是说谁先变,是时钟对寄存器同时作用,然后寄存器同时变化,只不过data根据x上一次的输出而变化。
data <= dataN[( x * 8 )+: 8 ] ;//确实是先取了data,然后x再自加1.只不过一开始我多设置了一个寄存器limit,使得data要延后一个 周期才赋值。逻辑出错。波形不对 end
else if (( (x) * 8 >= N )&& (tx_done == 1'b1 ))
begin
sign <= 1'b0 ;
state <= 1'b0 ;
data <= 8'd0 ;
end endmodule
module uart_1_2(  //设计输入
clk,//时钟
reset,//复位
data,//数据
send_en,//使能
tx_done_ctrl,
baud_rate,//波特率
uart_tx,//串口输出
tx_done//结束信号
);
input clk;
input reset;
input [7:0]data;
input send_en;
input [2:0]baud_rate;
input tx_done_ctrl ;
output reg uart_tx;
output reg tx_done; reg [17:0]bit_tim; //设计逻辑
//把波特率转化为一位的持续时间 //单位时间内通过信道传输的码元数称为码元传输速率,即波特率,码元/s,一个码元可能由多个位组成。而比特率即 ‘位/s’
always@(baud_rate) //在这里一个 码元由一位组成,所以波特率=比特率
begin
case(baud_rate) //常见的串口传输波特率
3'd0 : bit_tim = 1000000000/300/20 ; //波特率为300
3'd1 : bit_tim = 1000000000/1200/20 ; //波特率为1200
3'd2 : bit_tim = 1000000000/2400/20 ; //波特率为2400
3'd3 : bit_tim = 1000000000/9600/20 ; //波特率为9600
3'd4 : bit_tim = 1000000000/19200/20 ; //波特率为19200
3'd5 : bit_tim = 1000000000/115200/20 ; //波特率为115200
default bit_tim = 1000000000/9600/20 ; //多余的寄存器位置放什么:默认速率
endcase
end reg [17:0]counter1 ;//用来计数每一位的持续时间
always@(posedge clk or negedge reset)
begin
if(!reset)//复位清零
counter1 <=17'b0 ;
else if (send_en )//使能端有效,计数
begin
if( counter1 == bit_tim - 1'b1 )//位持续时间到达时归零
counter1 <= 17'b0 ;
else
counter1 <= counter1 + 1'b1 ;//位持续时间没达到时继续进行
end
else counter1 <= 17'b0 ; //使能端无效时,清零
end reg [3:0]counter2 ; //输出第几位。如果忘了考虑归零,那么计数器会出现溢出归零,在这里是加到15然后归零
always@(posedge clk or negedge reset)
begin
if(!reset)//复位
counter2 <= 4'b0 ;
else if ( send_en )//使能端有效
begin
if(counter2 == 0)//消耗20ns,进入起始位。这个挺重要的,没有这个的话得消耗一位的时间进入起始位
counter2 <= counter2 +1'b1 ;
else if( counter1 == bit_tim - 1'b1 )//开始进行位移
counter2 <= counter2 + 4'b1 ;
else if(counter2 == 4'd11)
counter2 <= 4'd0 ;
else
counter2 <= counter2 ;
end
else//使能端无效,归零,进入空闲位
counter2 <= 4'b0 ;
end always@(posedge clk or negedge reset)
begin
if(!reset)//复位
begin
uart_tx <= 4'b1 ;
end
else if ( send_en )//使能端有效,输出每一位
case(counter2)
0:begin uart_tx <= 1'b1 ; end//设定第一位为空闲位。没有空闲位的话,使能端无效时,counter停留在0,不能保持输出高电平(取决于要输出的数据),不符合要求。
1:uart_tx <= 1'b0 ;//起始位
2:uart_tx <= data[0] ;
3:uart_tx <= data[1] ;
4:uart_tx <= data[2] ;
5:uart_tx <= data[3] ;
6:uart_tx <= data[4] ;
7:uart_tx <= data[5] ;
8:uart_tx <= data[6] ;
9:uart_tx <= data[7] ;
10:uart_tx <= 1'b1 ;//结束位
11:begin uart_tx <= 1'b1 ; end//为了让结束位跑满,设置11,作为第11个点,定第十位长度。
default uart_tx <= 1'b1 ;
endcase
else
uart_tx <= 1'b1 ;
end always@(posedge clk or negedge reset)
begin
if(!reset)//复位清零
tx_done <= 1'b0 ;
else if (send_en )//使能端有效
begin
if( counter2 == 0 )//
tx_done <= 1'b0 ;
else if (( counter2 == 10 ) && ( counter1 == bit_tim - 1'b1 ))
tx_done <= 1'b1 ;
else if (tx_done == 1'b1)
tx_done <= 1'b0 ;
else if (tx_done_ctrl == 1'b1)
tx_done <= 1'b1 ;
end
else if (tx_done_ctrl == 1'b1)
tx_done <= 1'b1 ;
else if (tx_done == 1'b1)
tx_done <= 1'b0 ;
end
endmodule

串口应用:遵循uart协议发送N位数据(状态优化为3个,适用任意长度的输入数据,取寄存器中的一段(用变量作为边界))的更多相关文章

  1. 串口应用:遵循uart协议,发送多个字节的数据(状态机)

    上一节中,我们遵循uart协议,它发送一次只能发送6/7/8位数据,我们不能随意更改位数(虽然在代码上可行),不然就不遵循uart协议了,会造成接收端无法接收. 在现实生活中,我们有时候要发的数据不止 ...

  2. java-TCP协议发送和接收数据

    TCP协议接收数据的步骤: A:创建接收数据的Socket对象 创建对象的时候要指定端口 B:监听客户端连接 等待客户端连接 C:获取Socket对象的输入流(字节流) D:读数据,并显示在控制台 E ...

  3. java实现http协议发送和接收数据

    public void sendMessage() throws Exception { System.out.println("调用servlet开始=================&q ...

  4. IIC、SPI、UART协议总结

    IIC 特点 1.Inter-Integrated Circuit,内部集成总线,半双工 2.短距离传输,有应答,速度较慢 3.SDA双向数据线,SCL时钟线 4.可以挂载多个设备,IIC设备有固化地 ...

  5. Java基础知识强化之网络编程笔记06:TCP之TCP协议发送数据 和 接收数据

    1. TCP协议发送数据 和 接收数据 TCP协议接收数据:• 创建接收端的Socket对象• 监听客户端连接.返回一个对应的Socket对象• 获取输入流,读取数据显示在控制台• 释放资源 TCP协 ...

  6. python调用Moxa PCOMM Lite通过串口Ymodem协议发送文件

    本文采用python 2.7编写. 经过长期搜寻,终于找到了Moxa PCOMM Lite.调用PCOMM.DLL可以非常方便的通过串口的Xmodem.Ymodem.Zmodem等协议传输文件,而无需 ...

  7. 基于STM32之UART串口通信协议(二)发送

    一.前言 1.简介 在上一篇UART详解中,已经有了关于UART的详细介绍了,也有关于如何使用STM32CubeMX来配置UART的操作了,而在该篇博客,主要会讲解一下如何实现UART串口的发送功能. ...

  8. 基于FPGA的UART协议实现(通过线性序列机)

    //////////////////2018/10/15 更新源代码: 实现uart这东西其实早就写了,不过不太完善,对于一个完美主义者来说,必须解决掉它. 1.什么是UART?        通用异 ...

  9. UART协议详解

    UART(Universal Asynchronous Receiver/Transmitter)是一种异步全双工串行通信协议,由Tx和Rx两根数据线组成,因为没有参考时钟信号,所以通信的双方必须约定 ...

随机推荐

  1. PHP 运行 mkdir() Permission Denied 的原因

    使用lamp,在上传文件时,PHP执行 mkdir($path) ,  出现没有权限的错误. 解决: 本次使用的时yii框架,所以首先确保 是apache的用户对web目录有权限,然后再给此用户加 r ...

  2. [题解][YZOJ50113] 枇杷树

    简要题意 \(m\) 个操作,每次操作都会产生一个树的版本 \((\)从 \(0\) 开始\()\). 一次操作把 \(x_i\) 版本的树的点 \(u\) 和 \(y_i\) 版本的树的点 \(v\ ...

  3. MyCat安装和基本配置

    安装包下载 下载地址:http://dl.mycat.org.cn/ 我只这里下的是1.6Linux安装包:http://dl.mycat.org.cn/1.6.7.6/20220419132943/ ...

  4. drools规则属性(rule attributes)的使用

    一.介绍 规则属性是您可以添加到业务规则以修改规则行为的附加规范. 在 DRL 文件中,您通常在规则条件和操作的上方定义规则属性,多个属性位于单独的行中,格式如下: rule "rule_n ...

  5. 106_Power Pivot之HR入离调转、在职、离职率相关指标

    博客:www.jiaopengzi.com 焦棚子的文章目录 请点击下载附件 一.背景 之前有帮公司HR做了些员工入离调转.在职.人工成本分析等(体量:4000人左右).在和其他朋友交流的时候得知,貌 ...

  6. 以人类 Person 为基类设计学生类 Student 和教师类 Teacher

    学习内容:实验二以人类 Person 为基类设计学生类 Student 和教师类 Teacher 示例代码: package 实验二; import java.util.Scanner; class ...

  7. nginx 源码安装配置详解(./configure)

    在"./configure"配置中,"--with"表示启用模块,也就是说这些模块在编译时不会自动构建,"--without"表示禁用模块, ...

  8. C++primer第二章

    第二章 :变量和基本类型 2.1 基本内置类型 C++定义了一套包含算术类型(arithmetic type)和空类型(void)在内的基本数据类型 2.1.1 算术类型 算术类型的分类: 整型(in ...

  9. CXP 协议中upconnection 与downconnection的说明及其区别

    概述 CXP定义了一个DEVICE和HOST之间点对点的连接协议.CXP的一个连接包含了一个MASTER物理连接和若干可选的SLAVE连接,每一个连接都定义了一组逻辑通道用于传输图像数据.实时触发.设 ...

  10. django框架6

    内容概要 神奇的双下划线查询 外键字段的创建 外键字段操作 多表查询 基于对象的跨表查询 基于双下划线的跨表查询 双下线查询扩展 如何查看SQL语句 内容详情 神奇的双下划线查询 1.查询年龄大于20 ...