买了一个开发板学习FPGA,找到的各种东西就记录在这个博客里了,同时也方便把自己不会的问题找到的结果记录一下,都是自己手打,所以可能说的话不那么严谨,不那么精准,看到的人要带着自己的思考去看,记住尽信书不如无书,哈哈哈。。。。。。

 一、UART是什么?

UART是一种通用串行数据总线,也就是用于数据传输。是用于主机与辅助设备进行通信。这里的主机理解为计算机,计算机内部采用并行数据,辅助设备采用串行数据。中间需要设备进行数据转换,这也决定了UART工作原理是将传输数据的每个字符一位接一位地传输。UART采用异步传输模式,异步传输将比特分成小组进行传送,小组可以是8位的1个字符或更长。发送方可以在任何时刻发送这些比特组,而接收方从不知道它们会在什么时候到达。例如计算机键盘与主机的通信。UART用于远距离传输较为适合。可以数据同时发送和接收。

1,异步传输是面向字符的传输,而同步传输是面向比特的传输。

                                                2,异步传输的单位是字符同步传输的单位是帧。
                                                3,异步传输通过字符起始和停止码抓住再同步的机会,而同步传输则是在数据中抽取同步信息。
                                                4,异步传输对时序的要求较低,同步传输往往通过特定的时钟线路协调时序。
                                                5,异步传输相对于同步传输效率较低。
我的理解是对时序要求是采用UART的原因,数据到达需要给出到达信号也解释了UART采用8位1字符的串行数据输出模式,也解释了协议中起始位与停止位的重要性。
uart指通用异步收发传输器,本质上是硬件,用来异步传输数据。RS232是一种物理层协议,规定了特定的接口标准。https://www.zhihu.com/question/22632011 
 
进行数据传输时,需要考虑时钟同步问题,传输速率有一个很重要的概念。波特率是衡量资料传送速率的指标。表示每秒钟传送的符号数(symbol)。一个符号代表的信息量(比特数)与符号的阶数有关。例如资料传送速率为120字符/秒,传输使用256阶符号,每个符号代表8bit,则波特率就是120baud,比特率是120*8=960bit/s。位 bit (比特)(Binary Digits):存放一位二进制数,即 0 或 1,最小的存储单位。字节Byte:8个二进制位为一个字节(B),最常用的单位。
常用波特率9600,19200,38400,115200等。
其中各位的意义如下: 
起始位:先发出一个逻辑”0”信号,表示传输字符的开始。 
数据位:可以是5~8位逻辑”0”或”1”。如ASCII码(7位),扩展BCD码(8位)。 
校验位:数据位加上这一位后,使得“1”的位数应为偶数(偶校验)或奇数(奇校验)。 
停止位:它是一个字符数据的结束标志。可以是1位、1.5位、2位的高电平。 
空闲位:处于逻辑“1”状态,表示当前线路上没有资料传送。

     二、UART串口通信一般包括部分
    任何程序都包括一个主控制程序,因为是双向通信,还需要串口发送程序,串口接收程序,时钟控制程序。举例来分别解释四个程序的代码。
      这里采用50MHz的系统时钟,产生UART时钟信号产生和发送的波特率为9600bps。为了保证采样的时候不会发生误码或者滑码,我们对于一位数据不只采用一个时钟周期进行数据采集,而采用16个时钟周期。那么就需要对时钟进行分频处理。50,000,000/(16*9600),分频系数取整为326 。偶数分频比较简单,此处采用了326,至于奇数分频以及如何做出占空比为50%以后整理。
 
 
 `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学习笔记(一)的更多相关文章

  1. 基于fpga uart学习笔记

    2018年7月24日 uart 接收 部分测试成功,多谢开源骚客 邓堪文老师 ,想学的同学可以微信公众号搜索开源骚客 好啦!言归正传. 1.先附上老师的时序图,自己有点懒不想画,rx_t.rx_tt. ...

  2. JZ2440开发板:UART(串口)使用(学习笔记)

    查看UART在硬件上的信息,阅读JZ2440原理图可以看到: JZ2440开发板的UART0是可以跟USB相接的,用于打印调试,UART1,UART2两个串口用来外接模块.所以本文仅对UART0进行操 ...

  3. Uart学习笔记

    分享一个蛮好的链接:https://blog.csdn.net/wordwarwordwar/article/details/73662379 今天在看的资料是S家的DW_apb_uart的官方文档. ...

  4. 嵌入式学习笔记(综合提高篇 第一章) -- 利用串口点亮/关闭LED灯

    1      前言 从踏入嵌入式行业到现在已经过去了4年多,参与开发过的产品不少,有交换机.光端机以及光纤收发器,停车场出入缴费系统,二维码扫码枪,智能指纹锁以及数字IC芯片开发等; 涉及产品中中既有 ...

  5. stm32学习笔记----双串口同时打开时的printf()问题

    stm32学习笔记----双串口同时打开时的printf()问题 最近因为要使用串口2外接PN532芯片实现通信,另一方面,要使用串口1来将一些提示信息输出到上位机,于是重定义了printf(),使其 ...

  6. STM32学习笔记(二)——串口控制LED

    开发板芯片:STM32F407ZGT6 PA9-USART1_TX,PA10-USART1_RX; PF9-LED0,PF10-LED1; 一.串口1配置过程(不使用串口中断): 1.使能时钟,包括G ...

  7. STM32学习笔记(四)——串口控制LED(中断方式)

    目录: 一.时钟使能,包括GPIO的时钟和串口的时钟使能 二.设置引脚复用映射 三.GPIO的初始化配置,注意要设置为复用模式 四.串口参数初始化配置 五.中断分组和中断优先级配置 六.设置串口中断类 ...

  8. LM3S之boot loader学习笔记-1

    LM3S之boot loader学习笔记-1 彭会锋 (首先声明,此系列文章编写参考了很多资料,其中一些内容是原版内容的引用和整理,并加入了一些自己的见解,我已经尽量标明引用部分,如有未全部标注部分, ...

  9. FPFA学习笔记的系列

    1.Zynq 学习裸跑系列 学会Zynq(1)搭建Zynq-7000 AP SoC处理器 作者:FPGADesigner 学会Zynq(2)Zynq-7000处理器的配置详解 作者:FPGADesig ...

随机推荐

  1. Windows 编译 MQTT C++ Client

    MQTT MQTT(Message Queuing Telemetry Transport,消息队列遥测传输)是IBM开发的一个即时通讯协议,有可能成为物联网的重要组成部分.该协议支持所有平台,几乎可 ...

  2. eigenMatrix

    #include <iostream> using namespace std; #include <ctime> // Eigen 部分 #include <Eigen ...

  3. 105 + 106. Construct Binary Tree from Preorder and Inorder Traversal (building trees)

    Given preorder and inorder traversal of a tree, construct the binary tree. Note: You may assume that ...

  4. 如何理解“Unix 里一切都是文件”这句话-在 UNIX 中,一切都是字节流

    UNIX 操作系统的设计.用户界面.文化和演变都是建立在它的一套统一的想法和概念上.其中最重要的一点可能是“一切皆文件”,而这个概念被认为是 UNIX 的灵魂之一. 这一关键设计原则提供了一个统一的范 ...

  5. iOS 人机交互指导方针(iOS Human Interface Guidelines)

    iOS 人机交互指导方针(iOS Human Interface Guidelines) 太阳火神的漂亮人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名 ...

  6. HDU 6370 Werewolf 【并查集】

    任意门:http://acm.hdu.edu.cn/showproblem.php?pid=6370 Werewolf Time Limit: 2000/1000 MS (Java/Others)   ...

  7. Kali-linux识别活跃的主机

    尝试渗透测试之前,必须先识别在这个目标网络内活跃的主机.在一个目标网络内,最简单的方法将是执行ping命令.当然,它可能被一个主机拒绝,也可能被接收.本节将介绍使用Nmap工具识别活跃的主机. 网络映 ...

  8. CQRS轻量级框架【CQRSlite】学习使用小记

    前言 这几天在研究DDD和CQRS.快把我绕晕了.发现国外的好文质量还是挺高的.之所以先体验CQRSlite这个小框架,是因为看了一位大神写的文章:https://www.codeproject.co ...

  9. h5做的app和原生app的区别

    之所以说h5做的app和原生app的区别,是因为一位博友的问题: 随着 h5 的普及,是不是不再需要开发 app ? 我的回答是要分业务需求,分场合而定. 比如现在的微信小程序这么流行,甚至也取代了不 ...

  10. HDU 1735 字数统计(模拟+一点点贪心的思想)

    题目戳我 字数统计 Time Limit: 1000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total S ...