5—基于FPGA(ZYNQ-Z2)的多功能小车—软件设计—蓝牙串口

目录

1.蓝牙模块介绍

2.UART介绍

3.Verilog代码:

3.1Uart_RX模块:

3.2 分频模块:

3.3 Uart驱动模块

3.4 Uart控制模块

4. 总览


1.蓝牙模块介绍

  我使用的是JDY-31蓝牙模块,在连线中,要注意RX-TX,TX-RX。

  即FPGA约束的TX对应蓝牙模块的RX,约束的RX对应蓝牙模块的TX。

5—基于FPGA(ZYNQ-Z2)的多功能小车—软件设计—蓝牙串口_第1张图片


2.UART介绍

蓝牙模块使用UART串口通信协议,具体介绍如下:

UART(通用异步收发器)是一种常见的串口通信协议。

UART协议的主要特点如下:

  1. 异步通信:UART使用异步通信方式,不需要时钟同步信号。数据的传输以字符为单位,每个字符由起始位、数据位、校验位(可选)和停止位组成。

  2. 数据格式:UART可以支持不同的数据格式。常见的数据位数包括5位、6位、7位和8位,常见的停止位数为1位或2位,常见的校验位有奇校验、偶校验和无校验。

  3. 波特率:波特率是指每秒钟传输的比特数。UART支持不同的波特率,常见的波特率有9600、115200等。发送和接收的设备必须以相同的波特率进行通信。

  4. 帧同步:UART通过起始位和停止位来确定数据帧的边界。起始位用于标识数据传输的开始,停止位用于标识数据传输的结束。

  5. 单工或半双工通信:UART通信可以是单向的(只有发送或只有接收)或半双工的(同时具有发送和接收功能)。

5—基于FPGA(ZYNQ-Z2)的多功能小车—软件设计—蓝牙串口_第2张图片

232通信时序图如下:

5—基于FPGA(ZYNQ-Z2)的多功能小车—软件设计—蓝牙串口_第3张图片


3.Verilog代码:

具体Verilog的实现代码如下:

3.1顶层模块:

module rs232(
    input wire clk,
    input wire rst_n,
    input wire rx,
    output wire tx
    );

wire rx_flag;
wire [7:0] rx_data;
wire [7:0] tx_data;

Uart_TX uart_tx(
    .clk(clk),
    .rst_n(rst_n),
    .data(rx_data),
    .start_flag(rx_flag),
    .tx(tx)
    );

Uart_RX uart_rx(
    .clk(clk),
    .rst_n(rst_n),
    .rx(rx),
    .rx_data(rx_data),
    .rx_flag(rx_flag)
    );
    
endmodule

这段代码是RS232顶层模块。

其中包含一个发送器(Uart_TX)和一个接收器(Uart_RX)。实现了上位机发送信息后,FPGA将上位机的信息的返回。


3.2 Uart_TX模块:

`timescale 1ns / 1ps

module Uart_TX(
    input wire clk,
    input wire rst_n,
    input wire [7:0] data,
    input wire start_flag,
    output wire tx
    );
    
/******************内部变量**********************/
parameter N=13021; //N=系统时钟频率/波特率;f=125Mhz,baud=9600。表示N个系统时钟一个数据位
reg work_en;//工作使能
reg [31:0] baud_cnt;//波特计数器
reg bit_flag;//比特标志
reg [3:0] bit_cnt;//数据位计数,0-8
reg tx_reg;
assign tx=tx_reg;
/******************工作使能**********************/
always@(posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            work_en<=0;
        else if(start_flag==1)//开始迟滞一个clk,工作使能
            work_en<=1;
        else if((bit_cnt==4'd9)&&(bit_flag==1))
            work_en<=0;
        else
            work_en<=work_en;
    end
/*******************波特计数器**********************/
always@(posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            baud_cnt<=0;
        else if((baud_cnt==N-1)||(work_en==0))
            baud_cnt<=0;
        else if(work_en==1)
            baud_cnt<=baud_cnt+1;
    end

/*******************比特标志**********************/
always@(posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            bit_flag<=0;
        else if(baud_cnt==1)
            bit_flag<=1;
        else
            bit_flag<=0;
    end

/*******************数据位计数**********************/
always@(posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            bit_cnt<=0;
        else if((bit_cnt==4'd9)&&(bit_flag==1))
            bit_cnt<=0;
        else if(bit_flag==1)
            bit_cnt<=bit_cnt+1;
    end

/*******************数据输出**********************/
always@(posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            tx_reg<=1;
        else if(bit_flag==1) 
            case(bit_cnt)
                0:tx_reg<=0;
                1:tx_reg<=data[0];
                2:tx_reg<=data[1];
                3:tx_reg<=data[2];
                4:tx_reg<=data[3];
                5:tx_reg<=data[4];
                6:tx_reg<=data[5];
                7:tx_reg<=data[6];
                8:tx_reg<=data[7];
                9:tx_reg<=1;
                default:tx_reg<=1;
            endcase
    end
endmodule

首先根据系统时钟和波特率计算出N(我的FPGA是125Mhz,需要波特率9600)。

之后根据RS232的时序图写出各个模块,即可实现串口发送。


3.3 Uart_RX模块
 

`timescale 1ns / 1ps

module Uart_RX(
    input wire clk,
    input wire rst_n,
    input wire rx,
    output reg [7:0] rx_data,
    output reg rx_flag
    );

/******************内部变量**********************/
parameter N=13021; //N=系统时钟频率/波特率;f=125Mhz,baud=9600。表示N个系统时钟一个数据位
reg rx_reg1;//打排数据
reg rx_reg2;//打排数据
reg rx_reg3;//打排数据
reg start_flag;//起始标志
reg work_en;//工作使能
reg [31:0] baud_cnt;//波特计数器
reg bit_flag;//比特标志
reg [3:0] bit_cnt;//数据位计数,0-8
reg [7:0] rx_data_reg;//数据寄存器
reg rx_flag_reg;//数据标识符寄存器

/*******************数据打拍**********************/
//数据打拍,避免亚稳态
always@(posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            rx_reg1<=1;
        else
            rx_reg1<=rx;
    end

always@(posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            rx_reg2<=1;
        else
            rx_reg2<=rx_reg1;
    end

always@(posedge clk or negedge rst_n)
    begin
        if(rst_n==0)
            rx_reg3<=1;
        else
            rx_reg3<=rx_reg2;
    end

/*******************起始标志**********************/
always@(posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            start_flag<=0;
        else if((rx_reg3==1)&&(rx_reg2==0)&&(work_en==0))//迟滞reg3一个clk
            start_flag<=1;
        else 
            start_flag<=0;
    end

/*******************工作使能**********************/
always@(posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            work_en<=0;
        else if(start_flag==1)//开始迟滞一个clk,工作使能
            work_en<=1;
        else if((bit_cnt==8)&&(bit_flag==1))
            work_en<=0;
        else
            work_en<=work_en;
    end

/*******************波特计数器**********************/
always@(posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            baud_cnt<=0;
        else if((baud_cnt==N-1)||(work_en==0))
            baud_cnt<=0;
        else
            baud_cnt<=baud_cnt+1;
    end

/*******************比特标志**********************/
always@(posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            bit_flag<=0;
        else if(baud_cnt==(N/2-1))
            bit_flag<=1;
        else
            bit_flag<=0;
    end

/*******************数据位计数**********************/
always@(posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            bit_cnt<=0;
        else if((bit_cnt==8)&&(bit_flag==1))
            bit_cnt<=0;
        else if(bit_flag==1)
            bit_cnt<=bit_cnt+1;
    end
        
/*******************数据寄存器**********************/
always@(posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            rx_data_reg<=0;
        else if((bit_cnt>=1)&&(bit_cnt<=8)&&(bit_flag==1))
            rx_data_reg<={rx_reg3,rx_data_reg[7:1]};
    end
    
/*******************数据标识符寄存器**********************/
always@(posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            rx_flag_reg<=0;
        else if((bit_cnt==8)&&(bit_flag==1))
            rx_flag_reg<=1;
        else 
            rx_flag_reg<=0;
    end

/*******************数据输出**********************/
always@(posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            rx_data<=0;
        else if(rx_flag_reg==1)
            rx_data<=rx_data_reg;
    end

always@(posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            rx_flag<=0;
        else
            rx_flag<=rx_flag_reg;
    end
    
endmodule

依然是首先计算N,之后根据时序图实现。


4. 总览

效果如下:

5—基于FPGA(ZYNQ-Z2)的多功能小车—软件设计—蓝牙串口_第4张图片

具体结构如下:

你可能感兴趣的:(fpga开发)