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

  在现实生活中,我们有时候要发的数据不止8位,这时候就得多次发送了。分多段发送,就是说发送一次数据的时间里发送系统有多个状态,这便是状态机。即有限状态自动机,通常体现为一张流程图。一般包含state(状态),event(事件),action(动作),transition(转换)四个要素。

如在此情景下,有以下几个状态:

像这种有多个状态的情景,我们可以设置状态变量state,使能端enable,结束位tx_done(一般在底层)来控制状态的转换。

所以关键的点在于,把问题逻辑抽象化。最好是画出一个流程图来,大部分问题便迎刃而解了。

注意:

1.仿真时给的脉冲20ns可能会使判断条件无效,设置为21ns即可。或者给个200ns的延迟后再给20ns的脉冲。

2.通过观察其他变量的波形,来设置tx_done变化的判定条件,从而优化了tx——done的持续时间。

3.可以加入一个signal信号来控制仿真合适结束(通过@(negedge signal))

4.隐式例化:例化时不改变端口的数量,顺序,便是隐式例化,只需要定义端口的reg类型即可。

不足:

1.状态太多,代码很长。

2.适应范围不广。

思考:

1.如何优化状态机,做到只用3个状态就能实现上述功能。

2.优化代码使得可以十分简单的修改代码从而达到发送任意字节的数据的功能(字节有上限)。

module uart_4_nbyte(//用ifelse的方法传送五个字节的数据
clk,
reset,
data40,
send_pulse,
uart_tx,
sign
);
input clk ;
input reset ;
input send_pulse ;
input [39:0]data40 ;
output uart_tx ;
output sign ; reg send_en ;
wire tx_done ;
reg [7:0]data ; uart_1_1 uart_4_nbyte1( //设计输入
.clk(clk),//时钟
.reset(reset),//复位
.data(data),//数据
.send_en(send_en),//使能
.baud_rate(3'd5),//波特率
.uart_tx(uart_tx),//串口输出
.tx_done(tx_done)//结束信号
); reg [2:0]state ;
reg sign ; always@(posedge clk or negedge reset)
if (!reset)
begin
state <= 1'b0 ;
sign <= 1'b0 ;
data <= 0 ;
end
else if(send_pulse == 1'b1)
begin
state <= 1'b1 ;
send_en <= 1'b1 ;
sign <= 1 ;
end
else if (state == 1 )
begin
if(!tx_done)
begin
data <= data40[7:0] ;
send_en <= 1 ;
end
else if(tx_done)
begin
state <= 2 ;
send_en <= 0 ;
end
end
else if (state == 2 )
begin
if(!tx_done)
begin
data <= data40[15:8] ;
send_en <= 1 ;
end
else if(tx_done)
begin
state <= 3 ;
send_en <= 0 ;
end
end
else if (state == 3 )
begin
if(!tx_done)
begin
data <= data40[23:16] ;
send_en <= 1 ;
end
else if(tx_done)
begin
state <= 4 ;
send_en <= 0 ;
end
end
else if (state == 4 )
begin
if(!tx_done)
begin
data <= data40[31:24] ;
send_en <= 1 ;
end
else if(tx_done)
begin
state <= 5 ;
send_en <= 0 ;
end
end
else if (state == 5 )
begin
if(!tx_done)
begin
data <= data40[39:32] ;
send_en <= 1 ;
end
else if(tx_done)
begin
state <= 6 ;
send_en <= 0 ;
end
end
else if (state == 6 )
begin
state <= 1'b0 ;
sign <= 1'b0 ;
end endmodule
module uart_1_1(  //设计输入
clk,//时钟
reset,//复位
data,//数据
send_en,//使能
baud_rate,//波特率
uart_tx,//串口输出
tx_done//结束信号
);
input clk;
input reset;
input [7:0]data;
input send_en;
input [2:0]baud_rate;
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
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 ;
end
else if (tx_done == 1'b1)
tx_done <= 1'b0 ;
end
endmodule
`timescale 1ns / 1ns
module uart_4_tb(
);
reg clk ;
reg reset ;
reg [39:0]data40 ;
reg send_pulse ;
wire uart_tx ;
wire sign ; uart_4_nbyte uart_4_sim(//隐式例化
clk,
reset,
data40,
send_pulse,
uart_tx,
sign
); initial clk = 1;
always #10 clk = ! clk ;
initial begin
reset = 0 ;
data40 = 40'd0 ;
send_pulse = 1'b0 ;
#201 ;
reset = 1 ;
data40 = 40'h123456789a ;
#200 ;
send_pulse = 1'b1 ;
#20;
send_pulse = 1'b0 ;
@(negedge sign) ;
data40 = 40'ha987654321 ;
#200 ;
send_pulse = 1'b1 ;
#20;//设置为20ns时识别不出,会出错。设置21可以解决//多给200ns延迟后错误消失,用20ns也可以
send_pulse = 1'b0 ;
@(negedge sign) ;
#200 ;
$stop ;
end endmodule
module uart_5_nbyte(//用case的方法传送五个字节的数据
clk,
reset,
data40,
send_pulse,
uart_tx,
sign
);
input clk ;
input reset ;
input send_pulse ;
input [39:0]data40 ;
output uart_tx ;
output sign ; reg send_en ;
wire tx_done ;
reg [7:0]data ; uart_1_1 uart_5_nbyte1( //设计输入
.clk(clk),//时钟
.reset(reset),//复位
.data(data),//数据
.send_en(send_en),//使能
.baud_rate(3'd5),//波特率
.uart_tx(uart_tx),//串口输出
.tx_done(tx_done)//结束信号
); reg [2:0]state ;
reg sign ; always@(posedge clk or negedge reset)
if (!reset)
begin
state <= 1'b0 ;
sign <= 1'b0 ;
data <= 0 ;
end
else if(send_pulse == 1'b1)
begin
state <= 1'b1 ;
send_en <= 1'b1 ;
sign <= 1 ;
end
else case(state)
1:
begin
if(!tx_done)
begin
data <= data40[7:0] ;
send_en <= 1 ;
end
else if(tx_done)
begin
state <= 2 ;
send_en <= 0 ;
end
end
2:
begin
if(!tx_done)
begin
data <= data40[15:8] ;
send_en <= 1 ;
end
else if(tx_done)
begin
state <= 3 ;
send_en <= 0 ;
end
end
3:
begin
if(!tx_done)
begin
data <= data40[23:16] ;
send_en <= 1 ;
end
else if(tx_done)
begin
state <= 4 ;
send_en <= 0 ;
end
end
4:
begin
if(!tx_done)
begin
data <= data40[31:24] ;
send_en <= 1 ;
end
else if(tx_done)
begin
state <= 5 ;
send_en <= 0 ;
end
end
5:
begin
if(!tx_done)
begin
data <= data40[39:32] ;
send_en <= 1 ;
end
else if(tx_done)
begin
state <= 6 ;
send_en <= 0 ;
end
end
6:
begin
state <= 1'b0 ;
sign <= 1'b0 ;
end
endcase endmodule

串口应用:遵循uart协议,发送多个字节的数据(状态机)的更多相关文章

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

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

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

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

  3. C# 串口操作系列(3) -- 协议篇,二进制协议数据解析

    原文地址:http://blog.csdn.net/wuyazhe/article/details/5627253 我们的串口程序,除了通用的,进行串口监听收发的简单工具,大多都和下位机有关,这就需要 ...

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

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

  5. UART协议详解

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

  6. stm32实现DMX512协议发送与接收(非标)

    最近把玩了一下485,期间也接触了dmx512通信协议,该协议主要用于各种舞台灯光的控制当中,进而实现各种光效以及色彩变化.根据标准的512协议,其物理连接与传统上的RS485是完全一致的,并没有什么 ...

  7. C#使用SMTP协议发送验证码到QQ邮箱

    C#使用SMTP协议发送验证码到QQ邮箱 在程序设计中,发送验证码是常见的一个功能,用户在注册账号时或忘记密码后,通常需要发送验证码到手机短信或邮箱来验证身份,此篇博客介绍在C#中如何使用SMTP协议 ...

  8. stm32f103c8串口USART1发送多一字节

    用UART写了一段Bootloader代码,遇到了一个很奇怪的现象. 代码如下:简单介绍一下就是先统一配置MCU的IO端口,然后配置串口参数,然后循环发送‘0’和'\r’.16进制是0x30 0x0d ...

  9. Android(java)学习笔记80:UDP协议发送数据

    UDP协议发送数据:我们总是先运行接收端,再运行发送端发送端: 1 package cn.itcast_02; import java.io.IOException; import java.net. ...

随机推荐

  1. 流量录制回放工具jvm-sandbox-repeater入门篇——录制和回放

    在上一篇文章中,把repeater服务部署介绍清楚了,详细可见:流量录制回放工具jvm-sandbox-repeater入门篇--服务部署 今天在基于上篇内容基础上,再来分享下流量录制和回放的相关内容 ...

  2. 项目完成小结 - Django3.x版本 - 开发部署小结 (2)

    前言 好久没更新博客了,最近依然是在做之前博客说的这个项目:项目完成 - 基于Django3.x版本 - 开发部署小结 这项目因为前期工作出了问题,需求没确定好,导致了现在要做很多麻烦的工作,搞得大家 ...

  3. 从 rails 窥探 web 全栈开发(零)

    从 rails 窥探 web 全栈开发(零) 本文将讲述在学习之前几个必须要知道的概念,这些词汇在 rails 中都会出现. 本文前置条件:安装好 Ruby. 从 rails 窥探 web 全栈开发( ...

  4. arts-week14

    Algorithm 923. 3Sum With Multiplicity - LeetCode Review Building a network attached storage device w ...

  5. 105_Power Pivot财务科目(层级深度&筛选深度)

    博客:www.jiaopengzi.com 焦棚子的文章目录 请点击下载附件 1.背景 在财务科目中,需要按照科目层级来显示:在excel中都是用公式来实现,而且对于数据的管理及更新是一件头痛的事情, ...

  6. awd平台搭建及遇到的问题

    1.安装docker环境 a.使用的是ubuntu系统,通过sudo apt install docker.io进行docker得安装,此方式会自动启动docker服务. b.通过curl -s ht ...

  7. 个人冲刺(七)——体温上报app(二阶段)

    冲刺任务:完成特殊情况功能 MoreActivity.java package com.example.helloworld; import android.content.Intent; impor ...

  8. netty系列之:protobuf在UDP协议中的使用

    目录 简介 UDP在netty中的表示 DatagramPacketEncoder DatagramPacketDecoder 总结 简介 netty中提供的protobuf编码解码器可以让我们直接在 ...

  9. 20212115朱时鸿 《python程序设计》实验四报告

    课程:<Python程序设计>班级: 2121姓名: 朱时鸿学号:20212115实验教师:王志强实验日期:2022年5月28日必修/选修: 公选课 1.实验内容 Python综合应用:爬 ...

  10. Docker容器:将带UI的程序直接转为Web应用,so easy

    摘要:使用Docker容器,将带UI的程序,直接转换为Web应用.很方便,跟大家分享一下. 本文分享自华为云社区<使用Docker容器,将带UI的程序,直接转为Web应用>,作者:tsjs ...