• 12_基于FPGA的串口通信


    12_基于FPGA的串口通信

    实验原理

    FPGA和上位机电脑之间的通信采用UART串口通信方式,首先介绍下九针串口硬件的结构:

    它一共有9个引脚,但是最重要的3个引脚是:

    引脚2: RxD (接收数据).

    引脚3: TxD (发送数据).

    引脚5: GND ().

    仅使用3跟电缆,你就可以发送和接收数据.

    串口的通信协议

    首先要说波特率的概念:含义是每秒传送的二进制位数。通信双方的波特率一定要相等,比如常用的波特率有4800bps,9600bps,19200bps,115200bps等。

    数据格式:1位起始位+8位数据位+1位校验位(可选)+1位停止位;RS-232是使用异步通讯协议。也就是说数据的传输没有时钟信号。接收端必须有某种方式,使之与接收数据同步。
    对于RS-232来说,是这样处理的:

    串行线缆的两端事先约定好串行传输的参数(传输速度(既是波特率)、传输格式等)

    当没有数据传输的时候,发送端向数据线上发送"1" ;每传输一个字节之前,发送端先发送一个"0"来表示传输已经开始。这样接收端便可以知道有数据到来了。开始传输后,数据以约定的速度和格式传输,所以接收端可以与之同步每次传输完成一个字节之后,都在其后发送一个停止位("1") 让我们来看看0x55是如何传输的:

    0x55
    的二进制表示为:01010101
    但是由于先发送的是最低有效位,所以发送序列是这样的: 1-0-1-0-1-0-1-0

     

    实验设计原理(发送模块,接收模块,波特率发生模块的分别设计原理)

    波特率产生模块:原理就是对时钟分频得到波特率时钟,本程序是对50MHZ时钟进行650分频可近似得到9600bps*8倍的时钟来控制发送和接收模块。

    发送模块:在波特率时钟的驱动下用状态机把要发送的数据传送给上位机;

    接收模块:在波特率时钟驱动下一旦收到起始位启动接收状态机接收上位机的数据并通过数码管显示。

     

    硬件原理图

    图中MAX232是电平转换芯片,因为RS232电平:采用-12V-3V,等价于逻辑"0"+3V+12V的逻辑电平,等价于逻辑"1",但是FPGA的电平属于TTL电平,因此通信双方不能直接通信,需经过电平转换。

    实验代码

    /********************************版权声明**************************************

    ** 大西瓜团队

    **

    **----------------------------文件信息--------------------------

    ** 文件名称: uart.v

    ** 创建日期:

    ** 功能描述:串口通信__FPGA和上位机通信(波特率:9600bps,10bit1位起始位,8个数据位,1个结束)

    ** 操作过程:按动key2FPGAPC发送"da xi gua"一次,KEY1是复位按键。

    ** 字符串(串口调试工具设成字符格式接受和发送方式),FPGA接受(09)后显示在7段数码管上。

    ** 硬件平台:大西瓜第三代开发板,http://daxiguafpga.taobao.com

    ** 版权声明:本代码属个人知识产权,本代码仅供交流学习.

    **---------------------------修改文件的相关信息----------------

    ** 修改人:

    ** 修改日期:    

    ** 修改内容:

    *******************************************************************************/

    module uart(clk,rst,rxd,txd,en,seg_data,key_input);

     

    input clk,rst;

    input rxd; //串行数据接收端

    input key_input; //按键输入

     

    output[7:0] en;

    output[7:0] seg_data;

    reg[7:0] seg_data;

    output txd; //串行数据发送端

    ////////////////////inner reg////////////////////

    reg[15:0] div_reg; //分频计数器,分频值由波特率决定。分频后得到频率8倍波特率的时钟

    reg[2:0] div8_tras_reg; //该寄存器的计数值对应发送时当前位于的时隙数

    reg[2:0] div8_rec_reg; //该寄存器的计数值对应接收时当前位于的时隙数

    reg[3:0] state_tras; //发送状态寄存器

    reg[3:0] state_rec; //接受状态寄存器

    reg clkbaud_tras; //以波特率为频率的发送使能信号

    reg clkbaud_rec; //以波特率为频率的接受使能信号

    reg clkbaud8x; //8倍波特率为频率的时钟,它的作用是将发送或接受一个bit的时钟周期分为8个时隙

     

    reg recstart; //开始发送标志

    reg recstart_tmp;

     

    reg trasstart; //开始接受标志

     

    reg rxd_reg1; //接收寄存器1

    reg rxd_reg2; //接收寄存器2,因为接收数据为异步信号,故用两级缓存

    reg txd_reg; //发送寄存器

    reg[7:0] rxd_buf; //接受数据缓存

    reg[7:0] txd_buf; //发送数据缓存

     

    reg[2:0] send_state; //这是发送状态寄存器

    reg[19:0] cnt_delay; //延时去抖计数器

    reg start_delaycnt; //开始延时计数标志

    reg key_entry1,key_entry2; //确定有键按下标志

     

    ////////////////////////////////////////////////

    parameter div_par=16'h145;

    //分频参数,其值由对应的波特率计算而得,按此参数分频的时钟频率是波倍特率的8    

    //倍,此处值对应9600的波特率,即分频出的时钟频率是9600*8 (CLK 50M)

     

    ////////////////////////////////////////////////

    assign txd=txd_reg;

    //assign lowbit=0;

     

    assign en=0; //7段数码管使能信号赋值

     

    always@(posedge clk )

    begin

        if(!rst) begin

            cnt_delay<=0;

            start_delaycnt<=0;

         end

        else if(start_delaycnt) begin

            if(cnt_delay!=20'd800000) begin

                cnt_delay<=cnt_delay+1'b1;

             end

            else begin

                cnt_delay<=0;

                start_delaycnt<=0;

             end

         end

        else begin

            if(!key_input&&cnt_delay==0)

                    start_delaycnt<=1;

         end

    end

     

    always@(posedge clk)

    begin

        if(!rst)

            key_entry1<=1'b0;

        else begin

            if(key_entry2)

                key_entry1<=1'b0;

            else if(cnt_delay==20'd800000) begin

                if(!key_input)

                    key_entry1<=1'b1;

             end

         end

    end

     

    always@(posedge clk )

    begin

        if(!rst)

            div_reg<=0;

        else begin

            if(div_reg==div_par-1'b1)

                div_reg<=0;

            else

                div_reg<=div_reg+1'b1;

         end

    end

     

    always@(posedge clk)//分频得到8倍波特率的时钟

    begin

        if(!rst)

            clkbaud8x<=0;

        else if(div_reg==div_par-1)

            clkbaud8x<=~clkbaud8x;

    end

     

     

    always@(posedge clkbaud8x or negedge rst)

    begin

        if(!rst)

            div8_rec_reg<=0;

        else if(recstart)//接收开始标志

            div8_rec_reg<=div8_rec_reg+1'b1;//接收开始后,时隙数在8倍波特率的时钟下加1循环

    end

     

    always@(posedge clkbaud8x or negedge rst)

    begin

        if(!rst)

            div8_tras_reg<=0;

        else if(trasstart)

            div8_tras_reg<=div8_tras_reg+1'b1;//发送开始后,时隙数在8倍波特率的时钟下加1循环

    end

     

    always@(div8_rec_reg)

    begin

        if(div8_rec_reg==7)

            clkbaud_rec=1;//在第7个时隙,接收使能信号有效,将数据打入

        else

            clkbaud_rec=0;

    end

     

    always@(div8_tras_reg)

    begin

        if(div8_tras_reg==7)

            clkbaud_tras=1;//在第7个时隙,发送使能信号有效,将数据发出

        else

            clkbaud_tras=0;

    end

     

    always@(posedge clkbaud8x or negedge rst)

    begin

        if(!rst) begin

            txd_reg<=1;

            trasstart<=0;

            txd_buf<=0;

            state_tras<=0;

            send_state<=0;

            key_entry2<=0;

         end

        else begin

            if(!key_entry2) begin

                if(key_entry1) begin

                    key_entry2<=1;

                    txd_buf<=8'd68; //"D"

                 end

             end

            else begin

                case(state_tras)

                    4'b0000: begin //发送起始位

                        if(!trasstart&&send_state<7)

                            trasstart<=1;

                        else if(send_state<7) begin

                            if(clkbaud_tras) begin

                                txd_reg<=0;

                                state_tras<=state_tras+1'b1;

                             end

                         end

                        else begin

                            key_entry2<=0;

                            state_tras<=0;

                         end                    

                    end        

                    4'b0001: begin //发送第1

                        if(clkbaud_tras) begin

                            txd_reg<=txd_buf[0];

                            txd_buf[6:0]<=txd_buf[7:1];

                            state_tras<=state_tras+1'b1;

                         end

                     end

                    4'b0010: begin //发送第2

                        if(clkbaud_tras) begin

                            txd_reg<=txd_buf[0];

                            txd_buf[6:0]<=txd_buf[7:1];

                            state_tras<=state_tras+1'b1;

                         end

                     end

                     4'b0011: begin //发送第3

                         if(clkbaud_tras) begin

                            txd_reg<=txd_buf[0];

                            txd_buf[6:0]<=txd_buf[7:1];

                            state_tras<=state_tras+1'b1;

                         end

                     end

                    4'b0100: begin //发送第4

                        if(clkbaud_tras) begin

                            txd_reg<=txd_buf[0];

                            txd_buf[6:0]<=txd_buf[7:1];

                            state_tras<=state_tras+1'b1;

                         end

                     end

                    4'b0101: begin //发送第5

                        if(clkbaud_tras) begin

                            txd_reg<=txd_buf[0];

                            txd_buf[6:0]<=txd_buf[7:1];

                            state_tras<=state_tras+1'b1;

                         end

                     end

                    4'b0110: begin //发送第6

                        if(clkbaud_tras) begin

                            txd_reg<=txd_buf[0];

                            txd_buf[6:0]<=txd_buf[7:1];

                            state_tras<=state_tras+1'b1;

                         end

                     end

                    4'b0111: begin //发送第7

                        if(clkbaud_tras) begin

                            txd_reg<=txd_buf[0];

                            txd_buf[6:0]<=txd_buf[7:1];

                            state_tras<=state_tras+1'b1;

                         end

                     end

                    4'b1000: begin //发送第8

                        if(clkbaud_tras) begin

                            txd_reg<=txd_buf[0];

                            txd_buf[6:0]<=txd_buf[7:1];

                            state_tras<=state_tras+1'b1;

                         end

                     end

                    4'b1001: begin //发送停止位

                        if(clkbaud_tras) begin

                            txd_reg<=1;

                            txd_buf<=8'h55;

                            state_tras<=state_tras+1'b1;

                         end

                     end

                    4'b1111:begin

                        if(clkbaud_tras) begin

                            state_tras<=state_tras+1'b1;

                            send_state<=send_state+1'b1;

                            trasstart<=0;

                            case(send_state)

                                3'b000:

                                    txd_buf<=8'd97;//"a"

                                3'b001:

                                    txd_buf<=8'd120;//"x"

                                3'b010:

                                    txd_buf<=8'd105;//"i"

                                3'b011:

                                    txd_buf<=8'd103;//"g"

                                3'b100:

                                    txd_buf<=8'd117;//"u"

                                3'b101:

                                    txd_buf<=8'd97;//"a"

                                default:

                                    txd_buf<=0;

                             endcase

                         end

                     end

                    default: begin

                        if(clkbaud_tras) begin

                            state_tras<=state_tras+1'b1;

                            trasstart<=1;

                         end

                     end

                 endcase

             end

         end

    end

     

    always@(posedge clkbaud8x or negedge rst)//接受PC机的数据

    begin

        if(!rst) begin

            rxd_reg1<=0;

            rxd_reg2<=0;

            rxd_buf<=0;

            state_rec<=0;

            recstart<=0;

            recstart_tmp<=0;

         end

        else begin

             rxd_reg1<=rxd;

             rxd_reg2<=rxd_reg1;

             if(state_rec==0) begin

                 if(recstart_tmp==1) begin

                     recstart<=1;

                     recstart_tmp<=0;

                    state_rec<=state_rec+1'b1;

                  end

                  else if(!rxd_reg1&&rxd_reg2) //检测到起始位的下降沿,进入接受状态

                    recstart_tmp<=1;

             end

             else if(state_rec>=1&&state_rec<=8) begin

                  if(clkbaud_rec) begin

                     rxd_buf[7]<=rxd_reg2;

                    rxd_buf[6:0]<=rxd_buf[7:1];

                    state_rec<=state_rec+1'b1;

                 end

             end

             else if(state_rec==9) begin

                 if(clkbaud_rec) begin

                     state_rec<=0;

                    recstart<=0;

                 end

             end

         end

    end

     

    always@(rxd_buf) //将接受的数据用数码管显示出来

    begin

    case (rxd_buf)

            8'h30:

                seg_data=8'b11000000;

            8'h31:

                seg_data=8'b11111001;

            8'h32:

                seg_data=8'b10100100;

            8'h33:

                seg_data=8'b10110000;

            8'h34:

                seg_data=8'b10011001;

            8'h35:

                seg_data=8'b10010010;

            8'h36:

                seg_data=8'b10000010;

            8'h37:

                seg_data=8'b11111000;

            8'h38:

                seg_data=8'b10000000;

            8'h39:

                seg_data=8'b10010000;

            8'h41:

                seg_data=8'b10001000;

            8'h42:

                seg_data=8'b10000011;

            8'h43:

                seg_data=8'b11000110;

            8'h44:

                seg_data=8'b10100001;

            8'h45:

                seg_data=8'b10000110;

            8'h46:

                seg_data=8'b10001110;

            default:

                seg_data=8'b11111111;

         endcase

    end    

     

    endmodule

     

    实验操作

    实验效果

     

     

     

    大西瓜FPGA-->https://daxiguafpga.taobao.com

    配套开发板:https://item.taobao.com/item.htm?spm=a1z10.1-c.w4004-24211932856.3.489d7241aCjspB&id=633897209972

    博客资料、代码、图片、文字等属大西瓜FPGA所有,切勿用于商业! 若引用资料、代码、图片、文字等等请注明出处,谢谢!

       

    每日推送不同科技解读,原创深耕解读当下科技,敬请关注微信公众号"科乎"。

  • 相关阅读:
    Centos或Windows中部署Zookeeper集群及其简单用法
    Linux中使用sendmail发送邮件,指定任意邮件发送人
    使用log4net将C#日志发送到Elasticsearch
    在Centos6或者7上安装Kafka最新版
    最简单的配置Centos中JAVA的环境变量的方法
    JAVA通过oshi获取系统和硬件信息
    JAVA代码中获取JVM信息
    使用JavaCV播放视频、摄像头、人脸识别
    JAVA中通过JavaCV实现跨平台视频/图像处理-调用摄像头
    Linux中使用Vim快速更换文档中Windows换行符为Linux平台
  • 原文地址:https://www.cnblogs.com/logic3/p/15915907.html
Copyright © 2020-2023  润新知