串口UART学习笔记(一)
买了一个开发板学习FPGA,找到的各种东西就记录在这个博客里了,同时也方便把自己不会的问题找到的结果记录一下,都是自己手打,所以可能说的话不那么严谨,不那么精准,看到的人要带着自己的思考去看,记住尽信书不如无书,哈哈哈。。。。。。
一、UART是什么?
UART是一种通用串行数据总线,也就是用于数据传输。是用于主机与辅助设备进行通信。这里的主机理解为计算机,计算机内部采用并行数据,辅助设备采用串行数据。中间需要设备进行数据转换,这也决定了UART工作原理是将传输数据的每个字符一位接一位地传输。UART采用异步传输模式,异步传输将比特分成小组进行传送,小组可以是8位的1个字符或更长。发送方可以在任何时刻发送这些比特组,而接收方从不知道它们会在什么时候到达。例如计算机键盘与主机的通信。UART用于远距离传输较为适合。可以数据同时发送和接收。
起始位:先发出一个逻辑”0”信号,表示传输字符的开始。
数据位:可以是5~8位逻辑”0”或”1”。如ASCII码(7位),扩展BCD码(8位)。
校验位:数据位加上这一位后,使得“1”的位数应为偶数(偶校验)或奇数(奇校验)。
停止位:它是一个字符数据的结束标志。可以是1位、1.5位、2位的高电平。
空闲位:处于逻辑“1”状态,表示当前线路上没有资料传送。
二、UART串口通信一般包括部分
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Module Name: clkdiv
// 产生一个波特率9600的16倍频的时钟,9600*16= 153600
// 相当于50MHz的326分频,50000000/153600=326
//////////////////////////////////////////////////////////////////////////////////
module clkdiv(clk50, clkout);
input clk50; //系统时钟
output clkout; //采样时钟输出
reg clkout;
reg [:] cnt; //分频进程,对50Mhz的时钟326分频
always @(posedge clk50)
begin
if(cnt == 'd162)
begin
clkout <= 'b1;
cnt <= cnt + 'd1;
end
else if(cnt == 'd325)
begin
clkout <= 'b0;
cnt <= 'd0;
end
else
begin
cnt <= cnt + 'd1;
end
end
endmodule
分频比较简单,我写的另一个,
`timescale 1ns / 1ps module clkdiv(clk50, clkout);
input clk50; //系统时钟
output clkout; //采样时钟输出
reg clkout;
reg [:] cnt; always @(posedge clk50)
begin
if (cnt < 'd162)
cnt <= cnt + 'b1;
else if (cnt == 'd162)
begin
clkout <= ~clkout;
cnt <= 'b0;
end
end
三、串口发送程序
UART采用异步传输,就涉及起始位与停止位,下面是代码例子,我的总结用红笔标出。
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Module Name: uarttx
// 说明:16个clock发送一个bit,
//////////////////////////////////////////////////////////////////////////////////
module uarttx(clk, datain, wrsig, idle, tx);
input clk; //UART时钟
input [:] datain; //需要发送的数据
input wrsig; //发送命令,上升沿有效
output idle; //线路状态指示,高为线路忙,低为线路空闲
output tx; //发送数据信号
reg idle, tx;
reg send;
reg wrsigbuf, wrsigrise;
reg presult;
reg[:] cnt; //计数器
parameter paritymode = 'b0; //检测发送命令是否有效,判断wrsig的上升沿 //先检测发送命令是否有效,然后判断线路状态
always @(posedge clk)
begin
wrsigbuf <= wrsig;
wrsigrise <= (~wrsigbuf) & wrsig; //边沿检测,检测发送命令
end always @(posedge clk)
begin
if (wrsigrise && (~idle)) //当发送命令有效且线路为空闲时,启动新的数据发送进程
begin
send <= 'b1;
end
else if(cnt == 'd168) //一帧资料发送结束
begin
send <= 'b0;
end
end /////////////////////////////////////////////////////////////////////////
//使用168个时钟发送一个数据(起始位、8位数据、奇偶校验位、停止位),每位占用16个时钟// //停止位为8个时钟周期
////////////////////////////////////////////////////////////////////////
always @(posedge clk)
begin
if(send == 'b1) begin
case(cnt) //tx变低电平产生起始位,0~15个时钟为发送起始位
'd0: begin
tx <= 'b0;
idle <= 'b1;
cnt <= cnt + 'd1;
end
'd16: begin
tx <= datain[]; //发送数据位的低位bit0,占用第16~31个时钟
presult <= datain[]^paritymode; //奇偶校验位的获取
idle <= 'b1;
cnt <= cnt + 'd1;
end
'd32: begin
tx <= datain[]; //发送数据位的第2位bit1,占用第47~32个时钟
presult <= datain[]^presult;
idle <= 'b1;
cnt <= cnt + 'd1;
end
'd48: begin
tx <= datain[]; //发送数据位的第3位bit2,占用第63~48个时钟
presult <= datain[]^presult;
idle <= 'b1;
cnt <= cnt + 'd1;
end
'd64: begin
tx <= datain[]; //发送数据位的第4位bit3,占用第79~64个时钟
presult <= datain[]^presult;
idle <= 'b1;
cnt <= cnt + 'd1;
end
'd80: begin
tx <= datain[]; //发送数据位的第5位bit4,占用第95~80个时钟
presult <= datain[]^presult;
idle <= 'b1;
cnt <= cnt + 'd1;
end
'd96: begin
tx <= datain[]; //发送数据位的第6位bit5,占用第111~96个时钟
presult <= datain[]^presult;
idle <= 'b1;
cnt <= cnt + 'd1;
end
'd112: begin
tx <= datain[]; //发送数据位的第7位bit6,占用第127~112个时钟
presult <= datain[]^presult;
idle <= 'b1;
cnt <= cnt + 'd1;
end
'd128: begin
tx <= datain[]; //发送数据位的第8位bit7,占用第143~128个时钟
presult <= datain[]^presult;
idle <= 'b1;
cnt <= cnt + 'd1;
end
'd144: begin
tx <= presult; //发送奇偶校验位,占用第159~144个时钟 //将计算结果作为奇偶校验码输出
presult <= datain[]^paritymode; //无意思,此行计算结果无用,多余
idle <= 'b1;
cnt <= cnt + 'd1;
end
'd160: begin
tx <= 'b1; //发送停止位,占用第160~167个时钟
idle <= 'b1;
cnt <= cnt + 'd1;
end
'd168: begin
tx <= 'b1;
idle <= 'b0; //一帧资料发送结束
cnt <= cnt + 'd1;
end
default: begin
cnt <= cnt + 'd1;
end
endcase
end
else begin
tx <= 'b1;
cnt <= 'd0;
idle <= 'b0;
end
end
endmodule
四、串口接收程序
成为UART接收信号,将一位一位的串口数据转化为并行数据。
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Module name uartrx.v
// 说明: 16个clock接收一个bit,16个时钟采样,取中间的采样值
//////////////////////////////////////////////////////////////////////////////////
module uartrx(clk, rx, dataout, rdsig, dataerror, frameerror);
input clk; //采样时钟
input rx; //UART数据输入
output dataout; //接收数据输出
output rdsig; //接收数据有效,高说明接收到一个字节 ,来区分数据处于接收状态或者接收控制信号
output dataerror; //数据出错指示
output frameerror; //帧出错指示 reg[:] dataout;
reg rdsig, dataerror;
reg frameerror;
reg [:] cnt;
reg rxbuf, rxfall, receive;
parameter paritymode = 'b0;
reg presult, idle; always @(posedge clk) //检测线路rx的下降沿, 线路空闲的时候rx为高电平
begin
rxbuf <= rx;
rxfall <= rxbuf & (~rx); //下降沿检测,检测是否接收到接收信号
end always @(posedge clk)
begin
if (rxfall && (~idle)) //检测到线路的下降沿并且原先线路为空闲,启动接收数据进程
begin
receive <= 'b1; //开始接收数据
end
else if(cnt == 'd168) //接收数据完成
begin
receive <= 'b0;
end
end /////////////////////////////////////////////////////////////////////////
//使用176个时钟接收一个数据(起始位、8位数据、奇偶校验位、停止位),每位占用16个时钟//
////////////////////////////////////////////////////////////////////////
always @(posedge clk)
begin
if(receive == 'b1)
begin
case (cnt)
'd0: //0~15个时钟为接收第一个比特,起始位
begin
idle <= 'b1;
cnt <= cnt + 'd1;
rdsig <= 'b0;
end
'd24: //16~31个时钟为第1个bit数据,取中间第24个时钟的采样值 //在发送程序中,第0位数据在16个时钟周期后开始传输,接收过程中,
begin //从第24个时钟周期开始接收第0位数据,保证信号被采集。
idle <= 'b1;
dataout[] <= rx;
presult <= paritymode^rx;
cnt <= cnt + 'd1;
rdsig <= 'b0;
end
'd40: //47~32个时钟为第2个bit数据,取中间第40个时钟的采样值
begin
idle <= 'b1;
dataout[] <= rx;
presult <= presult^rx;
cnt <= cnt + 'd1;
rdsig <= 'b0;
end
'd56: //63~48个时钟为第3个bit数据,取中间第56个时钟的采样值
begin
idle <= 'b1;
dataout[] <= rx;
presult <= presult^rx;
cnt <= cnt + 'd1;
rdsig <= 'b0;
end
'd72: //79~64个时钟为第4个bit数据,取中间第72个时钟的采样值
begin
idle <= 'b1;
dataout[] <= rx;
presult <= presult^rx;
cnt <= cnt + 'd1;
rdsig <= 'b0;
end
'd88: //95~80个时钟为第5个bit数据,取中间第88个时钟的采样值
begin
idle <= 'b1;
dataout[] <= rx;
presult <= presult^rx;
cnt <= cnt + 'd1;
rdsig <= 'b0;
end
'd104: //111~96个时钟为第6个bit数据,取中间第104个时钟的采样值
begin
idle <= 'b1;
dataout[] <= rx;
presult <= presult^rx;
cnt <= cnt + 'd1;
rdsig <= 'b0;
end
'd120: //127~112个时钟为第7个bit数据,取中间第120个时钟的采样值
begin
idle <= 'b1;
dataout[] <= rx;
presult <= presult^rx;
cnt <= cnt + 'd1;
rdsig <= 'b0;
end
'd136: //143~128个时钟为第8个bit数据,取中间第136个时钟的采样值
begin
idle <= 'b1;
dataout[] <= rx;
presult <= presult^rx;
cnt <= cnt + 'd1;
rdsig <= 'b1; //接收数据有效
end
'd152: //159~144个时钟为接收奇偶校验位,取中间第152个时钟的采样值
begin
idle <= 'b1;
if(presult == rx)
dataerror <= 'b0;
else
dataerror <= 'b1; //如果奇偶校验位不对,表示数据出错
cnt <= cnt + 'd1;
rdsig <= 'b1;
end
'd168: //160~175个时钟为接收停止位,取中间第168个时钟的采样值
begin
idle <= 'b1;
if('b1 == rx)
frameerror <= 'b0;
else
frameerror <= 'b1; //如果没有接收到停止位,表示帧出错
cnt <= cnt + 'd1;
rdsig <= 'b1;
end
default:
begin
cnt <= cnt + 'd1;
end
endcase
end
else
begin
cnt <= 'd0;
idle <= 'b0;
rdsig <= 'b0;
end
end
endmodule
五、控制程序
这里主要学习程序的调用,如何用总的控制程序完成UART数据传输。
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Module Name: uart_test
//
//////////////////////////////////////////////////////////////////////////////////
module uart_test(clk50, rx, tx, reset);
input clk50;
input reset;
input rx;
output tx; wire clk; //clock for 9600 uart port
wire [:] txdata,rxdata; //串口发送数据和串口接收数据 //产生时钟的频率为16*9600
clkdiv u0 (
.clk50 (clk50), //50Mhz的晶振输入
.clkout (clk) //16倍波特率的时钟
); //串口接收程序
uartrx u1 (
.clk (clk), //16倍波特率的时钟
.rx (rx), //串口接收
.dataout (rxdata), //uart 接收到的数据,一个字节
.rdsig (rdsig), //uart 接收到数据有效
.dataerror (),
.frameerror ()
); //串口发送程序
uarttx u2 (
.clk (clk), //16倍波特率的时钟
.tx (tx), //串口发送
.datain (txdata), //uart 发送的数据
.wrsig (wrsig), //uart 发送的数据有效
.idle () ); endmodule
就像c语言里调用子函数一样,每个 模块是并行运行的, 各个模块连接完成整个系统需要一个顶层文件(top-module) 。 顶层文件 通过调用、连接低层模块的实例来实现复杂的功能。
学习UART通信,最主要还是理解异步和串口这两个东西,方便远距离传输,串口一位一位传输,牺牲了时间降低时序要求。
串口UART学习笔记(一)的更多相关文章
- 基于fpga uart学习笔记
2018年7月24日 uart 接收 部分测试成功,多谢开源骚客 邓堪文老师 ,想学的同学可以微信公众号搜索开源骚客 好啦!言归正传. 1.先附上老师的时序图,自己有点懒不想画,rx_t.rx_tt. ...
- JZ2440开发板:UART(串口)使用(学习笔记)
查看UART在硬件上的信息,阅读JZ2440原理图可以看到: JZ2440开发板的UART0是可以跟USB相接的,用于打印调试,UART1,UART2两个串口用来外接模块.所以本文仅对UART0进行操 ...
- Uart学习笔记
分享一个蛮好的链接:https://blog.csdn.net/wordwarwordwar/article/details/73662379 今天在看的资料是S家的DW_apb_uart的官方文档. ...
- 嵌入式学习笔记(综合提高篇 第一章) -- 利用串口点亮/关闭LED灯
1 前言 从踏入嵌入式行业到现在已经过去了4年多,参与开发过的产品不少,有交换机.光端机以及光纤收发器,停车场出入缴费系统,二维码扫码枪,智能指纹锁以及数字IC芯片开发等; 涉及产品中中既有 ...
- stm32学习笔记----双串口同时打开时的printf()问题
stm32学习笔记----双串口同时打开时的printf()问题 最近因为要使用串口2外接PN532芯片实现通信,另一方面,要使用串口1来将一些提示信息输出到上位机,于是重定义了printf(),使其 ...
- STM32学习笔记(二)——串口控制LED
开发板芯片:STM32F407ZGT6 PA9-USART1_TX,PA10-USART1_RX; PF9-LED0,PF10-LED1; 一.串口1配置过程(不使用串口中断): 1.使能时钟,包括G ...
- STM32学习笔记(四)——串口控制LED(中断方式)
目录: 一.时钟使能,包括GPIO的时钟和串口的时钟使能 二.设置引脚复用映射 三.GPIO的初始化配置,注意要设置为复用模式 四.串口参数初始化配置 五.中断分组和中断优先级配置 六.设置串口中断类 ...
- LM3S之boot loader学习笔记-1
LM3S之boot loader学习笔记-1 彭会锋 (首先声明,此系列文章编写参考了很多资料,其中一些内容是原版内容的引用和整理,并加入了一些自己的见解,我已经尽量标明引用部分,如有未全部标注部分, ...
- FPFA学习笔记的系列
1.Zynq 学习裸跑系列 学会Zynq(1)搭建Zynq-7000 AP SoC处理器 作者:FPGADesigner 学会Zynq(2)Zynq-7000处理器的配置详解 作者:FPGADesig ...
随机推荐
- Spring学习之-各注解的含义总结
注解配置 @ComponentScan("spittr.web"):/在加载Spring上下文时,会扫描spittr.web包查找组件 @ComponentScan注解扫描的组件有 ...
- tree 向下查找 (删除整条tree)
需求:通过点击获取需要删除的id(即获取到整条信息),如果该条数据没有子集,通过id删除即可,如果有子集,则该数据下所有的子集都需要删 删除后页面的数据更新在 下一篇 讲解 1 const id =' ...
- 如何使git忽略某些文件或文件夹
为什么要忽略某些文件或文件夹的变化? git作为一款项目文件变更版本管理软件,其主要功能之一就是追踪项目文件夹内各种文件及文件夹的变更情况.但是,在日常使用中,并非项目文件夹下的所有文件及文件夹变更都 ...
- Zabbix3.4服务器的搭建--CentOS7
本教程是目前最简单的Zabbix搭建教程.因为不是编译方式,直接用官方的分发包(rpm)安装. 1.前期准备 安装CentOS 7.4系统后.开启网络功能,其他东西均可不装.切记一定不要配置环境.还有 ...
- 使用Hibernate注解Annotations进行对象映射的异常处理
通过Hibernate注解Annotations进行对象映射,想在Oracle数据库中自动创建表,代码如下: 实体类: import javax.persistence.Basic;import ja ...
- BZOJ3196:二逼平衡树(线段树套Splay)
Description 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作: 1.查询k在区间内的排名 2.查询区间内排名为k的值 3.修改某一位值上的数值 4.查询k在 ...
- TCP建立连接和释放连接过程
TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的.可靠的.基于字节流的传输层通信协议.TCP建立连接需要三次握手,释放连接需要四次握手. 1.TCP整 ...
- Yii2.0随笔 路由
1.去掉index.php,按照pathinfo模式访问 例:http://***.com/控制器/方法 (1)把web服务器的网站目录指向所在模块的web目录 (2)在main.php的 'comp ...
- android 下使用Direct Texture
要使用Direct Texture,需要有一份android系统的源码 部分C++代码如下: #include <stdio.h> #include <stdlib.h> #i ...
- Unity经验之谈-DoTween动画结束匿名委托之巨坑
产生问题: 成百上千个物体放在List列表里面循环,每个物体都要使用移动和移动结束事件. BUG: 动画结束之后我想隐藏该物体,结果却没有正常的隐藏,代码如下 foreach (var item in ...