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所有,切勿用于商业! 若引用资料、代码、图片、文字等等请注明出处,谢谢!

   

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

posted @ 2022-02-20 17:18  logic3  阅读(1061)  评论(0编辑  收藏  举报