前言:

最近想实际做两个项目,认真学习怎么做一个系统,所以在看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. 10分钟进阶SpringBoot - 05. 数据访问之JDBC(附加源码分析+代码下载)

    10分钟进阶SpringBoot - 05. 数据访问之JDBC 代码下载:https://github.com/Jackson0714/study-spring-boot.git 一.JDBC是什么 ...

  2. bootstrap简介与入门

    bootstrap前端框架 1.概念:一个前端开发的框架,Bootstrap,来自 Twitter,是目前很受欢迎的前端框架.Bootstrap 是基于 HTML.CSS.JavaScript 的,它 ...

  3. 一行python代码搞定文件分享

    给同事分享文件,如你所知通过聊天工具,网盘或linux命令各种方法,还有一个也可以尝试下:使用一行python代码快速搭建一个http服务器在局域网内进行下载. python3使用: python3 ...

  4. 添加Windows 10开机启动项:No Hyper-V

    在Windows 10 1903版本加入了一项沙盒功能,1903版本以上的系统可以在控制面板-程序和功能-启用或关闭Windows功能中勾选Windows 沙盒选项,根据操作重启后即可打开沙盒功能. ...

  5. JZOJ 1301. treecut

    1301. treecut (Standard IO) Time Limits: 1000 ms Memory Limits: 131072 KB Description 有一个N个节点的无根树,各节 ...

  6. spring boot actuator服务监控与管理

    1.引入actuator所需要的jar包 <dependency> <groupId>org.springframework.boot</groupId> < ...

  7. redis 出现(error) MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. Commands that may modify the data set are disabled. Please check Redis logs for details

    如果在ubuntu安装的redis含端口使用,但是某些时候常常出现 (error) MISCONF Redis is configured to save RDB snapshots, but is ...

  8. Flask 使用pycharm 创建项目,一个简单的web 搭建

    1:新建项目后 2:Flask web 项目重要的就是app 所有每个都需要app app=Flask(__name__)   3:Flask 的路径是有app.route('path')装饰决定, ...

  9. Python卸载

    前言 自己瞎折腾下载Python3.8.2,把之前下载好的python3.7.3覆盖掉.在运行之前Python环境的程序多次未果后.找到原因,Python3.7.3的包不支持Python3.8.2.于 ...

  10. Spring Boot入门系列(六)如何整合Mybatis实现增删改查

    前面介绍了Spring Boot 中的整合Thymeleaf前端html框架,同时也介绍了Thymeleaf 的用法.不清楚的朋友可以看看之前的文章:https://www.cnblogs.com/z ...