前言:

最近想实际做两个项目,认真学习怎么做一个系统,所以在看FPGA小梅哥2019的培训课程,发现他是从各个模块讲起,就是没有直接讲一个整体的系统,而是从一些模块开始,如串口发送。刚开始我想直接创造自己代码,但我觉得既然我是跟着别人学项目,那首先应该按照别人的要求,一步步来,学习别人的思路。模仿。

小梅哥先讲的是,串口发送模块,这个和他初级阶段的发送模块是相同的(经验就是把一些写好的模块,可以用在以后的实际工程,反复利用。启示就是,对于每一个基本模块,不要求能创造自己设计思路,但一定得熟练掌握别人的,可以灵活应用修改,比如黑金的串口通信模块,就特别好用。)

一 设计定义

第一系统功能:串口发送,即实现能够让串口发送八位数据位,并仿真通过。

简单概括为两个部分:一是串口发送的波特率设置。二是八位数据位的发送。

第二遇到问题及解决;

解决问题:抓因果关系。如下面的两个问题:

A:  bps_cnt_q一直在第零位。(由于设计逻辑是波特率时钟一为零,则bps_cnt_q赋值为零。修改为保持就解决勒)

B: 小梅哥仿真设计中,只让Send_en保持一个时钟周期,发送时间不够,怎么等到了发送完成信号的高电平。(原因是只要让Send_en为高电平,然后uart_state就会一直保持这个高电平,直到发送完成)即控制发送实际上是uart_state。(类似下面的代码,flag 相当于uart_state)

if(send_en) flag <= 1else if(send_over)flag <= 0;

启示是:对信号的控制方式有两种:一是通过边沿触发(如按键按下,为下降沿触发)。二是电平的状态(或者状态机)。如串口发送是靠uart_state的高电平状态,控制发送。

C: 为啥小梅哥,设计的代码是从分频计数值为1时,就让波特率时钟为高电平。(为了避免延迟一个数据位的时间(即多等待一个数据位的发送时间),从计数值一开始,可以提升电路效率。)

第三 串口发送的原理

第一部分:串口发送模块框图

对第一个问题记录,先是通过仿真波形发现输出显示不正确,再加入发送模块的波形,看到bps_cnt_q一直为0.

修改为红色部分代码:

always@(posedge clk or negedge rst_n)

if(!rst_n)

bps_cnt_q<=4'd0;

else if(bps_cnt_q==4'd12)

bps_cnt_q=4'd0;

else if(bps_clk)

bps_cnt_q<=bps_cnt_q+1'b1;

else

bps_cnt_q<=bps_cnt_q;

解决办法的问题:抓因果关系。即刚开始看Rs232_tx波形一直为高电平,那就在模块中看到Rs232_tx与bps_cnt_q 有关,那就看看bps_cnt_q是否一直处于初始状态,即0(因为这时Rs232_tx为高).观察bps_cnt_q 的波形果然一直处于零,那就再看看关于bps_cnt_q的代码,发现只要波特率时钟bps_clk为低电平,则让bps_cnt_q为零。找到这个问题,解决办法:那直接在bps_clk为低电平时,则让bps_cnt_q保持为前一个状态,就会每来一个波特率时钟,bps_cnt_q一直加到11.

第二部分:串口发送模块原理图(电路图)

波特率查找表

Baud_Set[2:0]

波特率

50M时钟的计数值

0

9600

5208-1

1

19200

2604-1

2

38400

1302-1

3

57600

868-1

4             115200                     434-1

二 设计输入

简单总结:照图施工。根据上面设计定义的模块框图和电路结构图,从上到下,从输入到输出,按因果或输入输出的先后逻辑编写好每一个模块就行了。

由串口发送模块结构图可知,有五种波特率选择器的查找表,还有波特率分频器,多路选择器,寄存器,二选一选择器等模块。由于篇幅有限,我就重点写发送控制信号,控制状态信号uart_state的设计。

电路结构图:

设计过程:

从第二个模块往右看(好好分析),发现为多路选择器,但发现它的输入不仅有r_data_byte,还有bps_cnt_q,那我继续看bps_cnt_q的输入bps_cn,而bps_cnt_q的输入为Div_cnt(分频计数器),分频计数器的输入除了bps_dr还有en_cnt, 而en_cnt就是uart_state(因为它们用线连起来的)而uart_state的输入是两级的二选一选择器,

结论:初级的二选一输入为bps_cnt_q为11时,则让初级的二选一输出uart_state为0.次级的二选一的输入为send_en, 当send_en,为高电平时则让次级的二选一输出uart_state为1.

核心模块:数据发送模块,则是根据时序图,即每隔一个波特率时钟,数据被移入一位,而数据的起始位引脚Rs232_tx为低电平,其他时刻(如停止位)为高,来判断起始位的到来,之后再一位位的送人数据位(从低到高串行)。

设计思路:线性序列就或多路选择器

代码如下

localparam START_BIT = 1'b0;

localparam STOP_BIT = 1'b1;

//data transend

reg tx_done;

reg r_Rs_232;

always@(posedge clk or negedge rst_n)

if(!rst_n)beginr_Rs_232<=1'b1;        tx_done<=1'b0; end

else begin

case (bps_cnt_q)

0:begin tx_done<=1'b0;r_Rs_232<=1'b1;end

1:begin r_Rs_232<=START_BIT;end

2:begin r_Rs_232<=r_data_byte[0];end

3:begin r_Rs_232<=r_data_byte[1];end

4:begin r_Rs_232<=r_data_byte[2];end

5:begin r_Rs_232<=r_data_byte[3];end

6:begin    r_Rs_232<=r_data_byte[4];end

7:begin    r_Rs_232<=r_data_byte[5];end

8:begin r_Rs_232<=r_data_byte[6];end

9:begin r_Rs_232<=r_data_byte[7];end

10:begin tx_done<=1'b0;r_Rs_232<=STOP_BIT;end

11:begin tx_done<=1'b1;end

default: r_Rs_232<=1'b1;

endcase

end

注意点:我把tx_done直接写在了数据发送模块,在11时直接赋值为高,少写了tx_done的寄存器模块。

代码综合后的错误

报错如下:

解决办法:就直接点击报错的48行,其实没有缺少endcase。而是在endcase的上面的多了一个分号,则就是去掉这个分号即可。

报错的,不一定就是这个错误(往往是这个位置的其他语法错误,如A多了符号或者写错了符合。

B信号位宽多或少。信号或寄存器变量不一致等。变量早已经声明(重复)或未定义。

C语法错误如下面常见的三个关键词未配对(多或少)

(begin 与end。  module 与endmodule 。case与 endcase及其中的default。))

附上完整设计代码:

module uart_tx

(

send_en,

data_byte,

baud_set,

clk,

rst_n,

Rs232_tx,

tx_done,

uart_state

);

input send_en;

input [7:0]data_byte;

input [2:0]baud_set;

input clk;

input rst_n;

output reg Rs232_tx;

output tx_done;

output uart_state;

reg [3:0]bps_cnt_q;

reg uart_state;

always@(posedge clk or negedge rst_n)

if(!rst_n)

uart_state <= 1'b0;

else if(send_en)

uart_state <= 1'b1;

else if(bps_cnt_q == 4'd12)

uart_state <= 1'b0;

else

uart_state <= uart_state;

//multiplexer(多路选择器)

reg [15:0]bps_dr;

always@(posedge clk or negedge rst_n)

if(!rst_n)

bps_dr<=16'd0;

else begin

case(baud_set)

0:bps_dr<=16'd5207;

1:bps_dr<=16'd2603;

2:bps_dr<=16'd1301;

3:bps_dr<=16'd867;

4:bps_dr<=16'd433;

default: bps_dr<=16'd5207;

endcase

end

//data reg to store input data

reg [7:0]r_data_byte;

always@(posedge clk or negedge rst_n)

if(!rst_n)

r_data_byte<=8'd0;

else if(send_en)

r_data_byte<=data_byte;

else

r_data_byte<=r_data_byte;

//div_cnt counter("分频")

reg [15:0]div_cnt;

always@(posedge clk or negedge rst_n)

if(!rst_n)

div_cnt<=16'd0;

else if(uart_state)

begin

if(div_cnt==bps_dr)

div_cnt<=16'd0;

else

div_cnt<=div_cnt+1'b1;

end

else

div_cnt<=16'd0;

//bps_clk gene

reg bps_clk;

always@(posedge clk or negedge rst_n)

if(!rst_n)

bps_clk<=1'b0;

else if(div_cnt==16'd1)

bps_clk<=1'b1;

else

bps_clk<=1'b0;

always@(posedge clk or negedge rst_n)

if(!rst_n)

bps_cnt_q<=4'd0;

else if(bps_cnt_q==4'd12)

bps_cnt_q=4'd0;

else if(bps_clk)

bps_cnt_q<=bps_cnt_q+1'b1;

else

bps_cnt_q<=bps_cnt_q;

localparam START_BIT = 1'b0;

localparam STOP_BIT = 1'b1;

//data transend

reg tx_done;

always@(posedge clk or negedge rst_n)

if(!rst_n)begin Rs232_tx<=1'b1; tx_done<=1'b0; end

else begin

case (bps_cnt_q)

4'd0:begin tx_done<=1'b0;Rs232_tx<=1'b1;end

4'd1:begin Rs232_tx<=START_BIT;end

4'd2:begin Rs232_tx<=r_data_byte[0];end

4'd3:begin Rs232_tx<=r_data_byte[1];end

4'd4:begin Rs232_tx<=r_data_byte[2];end

4'd5:begin Rs232_tx<=r_data_byte[3];end

4'd6:begin Rs232_tx<=r_data_byte[4];end

4'd7:begin Rs232_tx<=r_data_byte[5];end

4'd8:begin Rs232_tx<=r_data_byte[6];end

4'd9:begin Rs232_tx<=r_data_byte[7];end

4'd10:begin tx_done<=1'b0;Rs232_tx<=STOP_BIT;end

4'd11:begin tx_done<=1'b1;end

default: Rs232_tx<=1'b1;

endcase

end

endmodule

第三  仿真设计

`timescale 1ns/1ns

`define clock_period 20

module uart_tx_tb;

reg send_en;

reg [7:0]data;

reg [2:0]baud_set;

reg Clk;

reg Rst_n;

wire  Rs232_tx;

wire tx_done;

wire uart_state;

uart_tx uart_tx_m0

(

.send_en(send_en),

.data_byte(data),

.baud_set(baud_set),

.clk(Clk),

.rst_n(Rst_n),

.Rs232_tx(Rs232_tx),

.tx_done(tx_done),

.uart_state(uart_state)

);

initial Clk = 1;

always#(`clock_period/2) Clk =~Clk;

initial begin

Rst_n = 0; send_en = 0;

data = 0;baud_set = 0;

#(`clock_period*440+1);

Rst_n = 1;

#(`clock_period*440+1);

baud_set = 4;

#(`clock_period*440+1);

data = {3'b110,3'b010,2'b11};

#(`clock_period*440+1);

send_en =1;

#(`clock_period);

send_en =0;

@(posedge tx_done)

#(`clock_period*999+1);

Rst_n = 0;

#(`clock_period*666+1);

$stop;

end

endmodule

仿真波形

通过这个串口发送设计,我明白了一是怎么照图施工(根据电路结构图设计代码)。二是怎么通过观察波形找到问题,抓因果关系,再修改对应的逻辑。三是设计好后,记得检查常见的三类错误。

03 串口发送端口Rs232之简单驱动1的更多相关文章

  1. COM口,串行通讯端口,RS-232接口 基础知识

    COM口即串行通讯端口. COM口的接口标准规范和总线标准规范是RS-232,有时候也叫做RS-232口.电脑上的com口多为9针,最大速率115200bps.通常用于连接鼠标(串口)及通讯设备(如连 ...

  2. 用Java通过串口发送手机短信

    用Java通过串口发短信其实很简单,因为有现成的类库供我们使用.有底层的类库,也有封装好一点的类库,下面我介绍一下在 Win32 平台下发送短信的方法. 如果你想用更底层的类库开发功能更强大的应用程序 ...

  3. 【小梅哥FPGA进阶教程】串口发送图片数据到SRAM在TFT屏上显示

    十五.串口发送图片数据到SRAM在TFT屏上显示 之前分享过rom存储图片数据在TFT屏上显示,该方法只能显示小点的图片,如果想显示TFT屏幕大小的图片上述方法rom内存大小不够.小梅哥给了个方案,利 ...

  4. DELPHI中完成端口(IOCP)的简单分析(4)

    DELPHI中完成端口(IOCP)的简单分析(4)   在我以前写的文章中,一直说的是如何接收数据.但是对于如何发送数据却一点也没有提到.因为从代码量上来说接收的代码要比发送多很多.今天我就来写一下如 ...

  5. DELPHI中完成端口(IOCP)的简单分析(3)

    DELPHI中完成端口(IOCP)的简单分析(3)   fxh7622关注4人评论7366人阅读2007-01-17 11:18:24   最近太忙,所以没有机会来写IOCP的后续文章.今天好不容易有 ...

  6. DELPHI中完成端口(IOCP)的简单分析(2)

    DELPHI中完成端口(IOCP)的简单分析(2)   今天我写一下关于DELPHI编写完成端口(IOCP)的工作者线程中的东西.希望各位能提出批评意见.上次我写了关于常见IOCP的代码,对于IOCP ...

  7. DELPHI中完成端口(IOCP)的简单分析(1)

    DELPHI中完成端口(IOCP)的简单分析(1)   用DELPHI开发网络代码已经有一段时间了! 我发现在网上用VC来实现完成端口(IOCP)的代码很多,但是使用DELPHI来实现的就比较少了.对 ...

  8. 关于Matlab串口发送HEX格式字符

    终于想起来更新一下关于使用Matlab串口发送HEX格式字符.这个用法主要来自于我使用Matlab对机器人进行实时轨迹跟踪的绘制,由于底层限制,自己又不想在中间增加转换模块,就需要直接发送HEX格式指 ...

  9. WPF内实现与串口发送数据和接收数据

    原文:WPF内实现与串口发送数据和接收数据 与串口发送数据和接收数据,在此作一个简单的Demo.此Demo可以实现按下硬件按钮,灯亮,发送灯状态数据过来.并且可以实现几个灯同时亮,发送灯的状态数据过来 ...

随机推荐

  1. 作为前端,你需要懂得javascript实现继承的方法

    在ES6之前,javascript不跟其他语言一样,有直接继承的方法,它需要借助于构造函数+原型对象模拟实现继承.现在我们可以利用ES6的extends方法实现继承,如果想了解更多有关ES6实现的继承 ...

  2. RTL8812AU双频无线网卡在ubuntu19和20上的驱动安装

    旧爱已去 疫情在家,突然邻居敲门说,我这网上不了,帮下忙呗兄弟:兄弟都叫了,哥就冒回险,口罩扎起,一顿xxxx,原来是路由器没插到wlan口,看他拉网线可怜,就把我台式机上无线网卡送给他了,这就是又送 ...

  3. Dart的JIT 与 AOT

    JIT:Just In Time AOT:Ahead of Time 含义: 目前,程序主要有两种运行方式:静态编译与动态解释. 静态编译的程序在执行前全部被翻译为机器码,通常将这种类型称为AOT ( ...

  4. SQL语句中in 与 exists的区别

    SQL语句中in 与 exists的区别 SQL中EXISTS检查是否有结果,判断是否有记录,返回的是一个布尔型(true/false); IN是对结果值进行比较,判断一个字段是否存在于几个值的范围中 ...

  5. LaTex公式符号

    下面这个网站是我认为比较齐全的网站 http://www.mohu.org/info/symbols/symbols.htm

  6. 网络|Trojan 网络代理服务搭建

    Trojan 网络代理服务搭建 前言 本文目的在于帮助相同困惑的网友,让使用更加简单. Trojan为Trojan-GFW开源的一款新思路网络代理软件, 前期准备 [x] 服务器:系统CentOS 7 ...

  7. 利用iTunes给MP3添加专辑插图

    利用iTunes给MP3添加专辑插图 打开iTunes 准备好没有专辑插图的mp3文件和插图 将准备好的mp3文件拖入iTunes 右键菜单选择专辑信息选项 在专辑信息里面选择插图 点击左下角的添加插 ...

  8. 一次 Druid 连接池泄露引发的血案!

    最近某个应用程序老是卡,需要重启才能解决问题,导致被各种投诉,排查问题是 Druid 连接池泄露引发的血案.. 异常日志如下: ERROR - com.alibaba.druid.pool.GetCo ...

  9. HDFS文件系统基操--Java实现

    Java实现对HDFS文件系统的基本操作 1.准备好jar包 2.创建一个类 1. 测试连接 @Test //测试是否连接成功 public void test() { //添加配置 ==> c ...

  10. 04 namenode和datanode

    namenode元数据管理 1.什么是元数据? hdfs的目录结构及每一个文件的块信息(块的id,块的副本数量,块的存放位置<datanode>) 2.元数据由谁负责管理? namenod ...