买了一个开发板学习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. temp表空间被过多占用处理方法

    这个步骤比较简单,查询v$sort_usage就可以了: (select username,session_addr,sql_id,contents,segtype,blocks*8/1024/102 ...

  2. OC基础数据类型-NSArray

    1.数组的初始化 NSArray *array = [[NSArray alloc] initWithObjects:@"One", @"Two", @&quo ...

  3. 我不 大冰2017新书pdf免费下载

    善意能消戾,善意能得缘,善意能带业往生,善意能回头是岸.善意能够帮人捕捉并建立起独特的幸福感. “我不”是一种善意坦然,也是一种善意的随缘,更是一句善意的自省. <我不>——百万级畅销书作 ...

  4. [原]Linux 命令行 发送邮件

    1.mail -s hi xx@yy.com 给xx@yy.com发一封主题为hi的信(没有正文) 编辑完内容后Ctrl-D结束. 2.echo "This is a test mail!& ...

  5. SAP S/4HANA extensibility扩展原理介绍

    SAP产品总的extensibility扩展原理介绍: 看Jerry这篇文章. SAP Cloud for Customer Extensibility的设计与实现 我的同事Boris写的. 而本文是 ...

  6. ABAP SICF服务和Java Servlet的比较

    In my opinion ABAP ICF handler and Java Servlet play the same role in enhancement which enables your ...

  7. background-color和background-image相关细节

    1.background-color 是以border-box作为他的左上角来定位的 2.background-image 默认是以padding-box作为他的左上角来定位的 3.backgroun ...

  8. AngularJs学习笔记--Understanding the Controller Component

    原版地址:http://docs.angularjs.org/guide/dev_guide.mvc.understanding_model 在angular中,controller是一个javasc ...

  9. 快速搭建一个SSM框架demo

    我之所以写一个快速搭建的demo,主要想做一些容器的demo,所以为了方便大家,所以一切从简,简单的3层架构 先用mysql的ddl,后期不上oracle的ddl ; -- ------------- ...

  10. CentOS 安装 dokuwiki

    前期准备:yum 安装httpd和mysql 1.下载dokuwiki安装包 http://download.dokuwiki.org/: 2.centos系统中httpd的根目录为:/var/www ...