03 串口发送端口Rs232之简单驱动1
前言:
最近想实际做两个项目,认真学习怎么做一个系统,所以在看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. 第二部分:串口发送模块原理图(电路图) 波特率查找表
|
二 设计输入
简单总结:照图施工。根据上面设计定义的模块框图和电路结构图,从上到下,从输入到输出,按因果或输入输出的先后逻辑编写好每一个模块就行了。
由串口发送模块结构图可知,有五种波特率选择器的查找表,还有波特率分频器,多路选择器,寄存器,二选一选择器等模块。由于篇幅有限,我就重点写发送控制信号,控制状态信号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的寄存器模块。
代码综合后的错误
|
附上完整设计代码:
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的更多相关文章
- COM口,串行通讯端口,RS-232接口 基础知识
COM口即串行通讯端口. COM口的接口标准规范和总线标准规范是RS-232,有时候也叫做RS-232口.电脑上的com口多为9针,最大速率115200bps.通常用于连接鼠标(串口)及通讯设备(如连 ...
- 用Java通过串口发送手机短信
用Java通过串口发短信其实很简单,因为有现成的类库供我们使用.有底层的类库,也有封装好一点的类库,下面我介绍一下在 Win32 平台下发送短信的方法. 如果你想用更底层的类库开发功能更强大的应用程序 ...
- 【小梅哥FPGA进阶教程】串口发送图片数据到SRAM在TFT屏上显示
十五.串口发送图片数据到SRAM在TFT屏上显示 之前分享过rom存储图片数据在TFT屏上显示,该方法只能显示小点的图片,如果想显示TFT屏幕大小的图片上述方法rom内存大小不够.小梅哥给了个方案,利 ...
- DELPHI中完成端口(IOCP)的简单分析(4)
DELPHI中完成端口(IOCP)的简单分析(4) 在我以前写的文章中,一直说的是如何接收数据.但是对于如何发送数据却一点也没有提到.因为从代码量上来说接收的代码要比发送多很多.今天我就来写一下如 ...
- DELPHI中完成端口(IOCP)的简单分析(3)
DELPHI中完成端口(IOCP)的简单分析(3) fxh7622关注4人评论7366人阅读2007-01-17 11:18:24 最近太忙,所以没有机会来写IOCP的后续文章.今天好不容易有 ...
- DELPHI中完成端口(IOCP)的简单分析(2)
DELPHI中完成端口(IOCP)的简单分析(2) 今天我写一下关于DELPHI编写完成端口(IOCP)的工作者线程中的东西.希望各位能提出批评意见.上次我写了关于常见IOCP的代码,对于IOCP ...
- DELPHI中完成端口(IOCP)的简单分析(1)
DELPHI中完成端口(IOCP)的简单分析(1) 用DELPHI开发网络代码已经有一段时间了! 我发现在网上用VC来实现完成端口(IOCP)的代码很多,但是使用DELPHI来实现的就比较少了.对 ...
- 关于Matlab串口发送HEX格式字符
终于想起来更新一下关于使用Matlab串口发送HEX格式字符.这个用法主要来自于我使用Matlab对机器人进行实时轨迹跟踪的绘制,由于底层限制,自己又不想在中间增加转换模块,就需要直接发送HEX格式指 ...
- WPF内实现与串口发送数据和接收数据
原文:WPF内实现与串口发送数据和接收数据 与串口发送数据和接收数据,在此作一个简单的Demo.此Demo可以实现按下硬件按钮,灯亮,发送灯状态数据过来.并且可以实现几个灯同时亮,发送灯的状态数据过来 ...
随机推荐
- 【HI AI:人机协同 赋能未来系列】计算机是最好的左脑
AI:人机协同 赋能未来系列]计算机是最好的左脑"> 编者按: 计算机领域的热点总是在不断更替,从大数据到云计算再到人工智能,这些热点的背后离不开专家学者们在这些领域一点一滴聚沙成塔的 ...
- Leetcode 239题 滑动窗口最大值(Sliding Window Maximum) Java语言求解
题目链接 https://leetcode-cn.com/problems/sliding-window-maximum/ 题目内容 给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧 ...
- C++走向远洋——55(项目一3、分数类的重载、>>
*/ * Copyright (c) 2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名:text.cpp * 作者:常轩 * 微信公众号:Worldhe ...
- python类变量与构造函数的使用
类变量:可在类的所有实例之间共享的变量 实例类对象:类的实例是调用类对象来创建的.如:par = Parent(),par就是类Parent的一个实例类对象. 实例变量(成员变量):同一个类对象可以创 ...
- 从0到1,本地到远程git程序过程
从0到1,本地到远程git程序过程 切记一定要在需要提交代码的文件夹下git init,既是你使用了什么 tortoisegit什么工具,或者你在idea环境下已经add了,但是仍然需要你在当前文件夹 ...
- JMeter-完成批量的接口测试
前言 当我们在工作中进行接口测试时,项目的接口肯定不止一个,而是很多很多,而且每个接口都需要进行正确参数,错误参数,参数为空,特殊字符等方式来测试接口是否能够正确返回所需的响应值. 今天,我们来一起学 ...
- centos安装图形界面通常有两种方式
centos安装图形界面通常有两种方式 1.通过系统安装,在配置选择软件界面,选择GNOME桌面模式.
- IAR软件使用的快捷键配置以及配置cc2530环境
以下是我对IAR软件使用的快捷键配置cc2530以及配置环境的总结,如下图所示 弹出保存窗口 工程生成完毕——生成.c文件 快捷键ctrl+s保存.c文件 选择 ...
- node--非阻塞式I/O,单线程,异步,事件驱动
1.单线程 不同于其他的后盾语言,node是单线程的,大大节约服务器开支 node不为每个客户创建一个新的线程,仅使用一个线程.通过非阻塞I/O以及 事件驱动机制,使其宏观上看是并发的,可以处理高并发 ...
- golang切片
切片与数组 go的数组是这样的 array := [3]int{1,2,3} array := [...]int{1,2,3} go的切片 array := []int{1,2,3} //1 arra ...