赞
踩
本系列文章基于开发板黑金A309,FPGA芯片为Xilinx公司的spartan6,本系列文章记录FPGA学习历程。
通用异步收发传输器,通常称为UART。本文采用的是RS232接口标准。串口通信原理在网上很容易搜到,串口通信是双机通信中最先学到的通信方式,是异步串行通信。
串口通信的连接图如下所示:
串行通信中消息桢组成为:
本实验传输不用校验位。
串行通信中,波特率非常重要,是数据能否正确传输的重要保障,波特率即一秒钟传输多少字。本文选用波特率为115200。
波特率相当于异步串行通信中的时基单元,所以非常重要。
硬件上,AX309采用了USB转串口芯片CP2102。
采用状态转移图方式编写代码,发送模块的状态转换图如下所示:
发送模块的方框图如下:
module uart_tx #( parameter CLK_FRE = 50, parameter BAUD_RATE = 115200 ) ( input clk, input rst, input[7:0] tx_data,//发送数据 input tx_data_valid,//发送数据有效标志 output tx_pin, output reg tx_data_ready ); localparam CYCLE = CLK_FRE * 1000000/BAUD_RATE; localparam S_IDLE = 1; localparam S_START = 2; localparam S_SEND_BYTE = 3; localparam S_STOP = 4; reg[2:0] state; reg[2:0] next_state; reg[15:0] cycle_cnt; reg[2:0] bit_cnt; reg[7:0] tx_data_latch; reg tx_reg; assign tx_pin = tx_reg; always@(posedge clk or negedge rst) begin if(rst == 1'b0) state <= S_IDLE; else state <= next_state; end //状态转移 always@(*) begin case(state) S_IDLE: if(tx_data_valid == 1'b1) next_state <= S_START; else next_state <= S_IDLE; S_START: if(cycle_cnt == CYCLE-1) next_state <= S_SEND_BYTE; else next_state <= S_START; S_SEND_BYTE: if(cycle_cnt == CYCLE-1 && bit_cnt == 3'd7) next_state <= S_STOP; else next_state <= S_SEND_BYTE; S_STOP: if(cycle_cnt == CYCLE-1) next_state <= S_IDLE; else next_state <= S_STOP; default:next_state <= S_IDLE; endcase end //发送标志位 always@(posedge clk or negedge rst) begin if(rst == 1'b0) tx_data_ready <= 1'b0; else if(state == S_IDLE) if(tx_data_valid == 1'b1) tx_data_ready <= 1'b0; else tx_data_ready <= 1'b1; else if(state == S_STOP && cycle_cnt == CYCLE-1) tx_data_ready <= 1'b1; end always@(posedge clk or negedge rst) begin if(rst == 1'b0) tx_data_latch <= 8'd0; else if(state == S_IDLE && tx_data_valid == 1'b1) tx_data_latch <= tx_data; end //数据输出 always@(posedge clk or negedge rst) begin if(rst == 1'b0) tx_reg <= 1'b0; else case(state) S_IDLE,S_STOP: tx_reg <= 1'b1; S_START: tx_reg <= 1'b0; S_SEND_BYTE: tx_reg <= tx_data_latch[bit_cnt]; default: tx_reg <= 1'b1; endcase end //比特位计数 always@(posedge clk or negedge rst) begin if(rst == 1'b0) bit_cnt <= 3'd0; else if(state == S_SEND_BYTE) if(cycle_cnt == CYCLE-1) bit_cnt <= bit_cnt +1'b1; else bit_cnt <= bit_cnt; else bit_cnt <= 3'd0; end //波特率计数 always@(posedge clk or negedge rst) begin if(rst == 1'b0) cycle_cnt <= 16'd0; else if((state == S_SEND_BYTE && cycle_cnt == CYCLE-1)|| next_state != state) cycle_cnt <= 16'd0; else cycle_cnt <= cycle_cnt + 16'd1; end endmodule
发送模块仿真波形图,如下所示:
接收模块的状态转换图:
接收模块的方框图:
module uart_rx #( parameter CLK_FRE = 50, parameter BAUD_RATE = 115200 ) ( input clk, input rst, input rx_pin, input rx_ready, output reg rx_valid, output reg[7:0] rx_data ); localparam CYCLE = (CLK_FRE * 1000000)/BAUD_RATE; localparam S_IDLE = 0; localparam S_START = 1; localparam S_RX_CYCLE = 2; localparam S_DATA = 3; localparam S_STOP = 4; reg[2:0] state; reg[2:0] next_state; reg rx_d0; reg rx_d1; wire rx_negedge; reg[7:0] rx_bits; reg[2:0] bit_cnt; reg[15:0] cycle_cnt; assign rx_negedge = ~rx_d0 && rx_d1; //消息桢起始位,下降沿 always@(posedge clk or negedge rst) begin if(rst == 1'b0) begin rx_d0 <= 1'b0; rx_d1 <= 1'b0; end else begin rx_d0 <= rx_pin; rx_d1 <= rx_d0; end end always@(posedge clk or negedge rst) begin if(rst == 1'b0) state <= S_IDLE; else state <= next_state; end //状态转移 always@(*) begin case(state) S_IDLE: if(rx_negedge) next_state <= S_START; else next_state <= S_IDLE; S_START: if(cycle_cnt == CYCLE-1) next_state <= S_RX_CYCLE; else next_state <= S_START; S_RX_CYCLE: if(cycle_cnt == CYCLE-1 && bit_cnt == 3'd7) next_state <= S_STOP; else next_state <= S_RX_CYCLE; S_STOP: if(cycle_cnt == CYCLE/2-1) next_state <= S_DATA; else next_state <= S_STOP; S_DATA: if(rx_ready) next_state <= S_IDLE; else next_state <= S_DATA; default: next_state <= S_IDLE; endcase end //波特率计数器 always@(posedge clk or negedge rst) begin if(rst == 1'b0) cycle_cnt <= 16'd0; else if((state == S_RX_CYCLE && cycle_cnt == CYCLE-1)||next_state != state) cycle_cnt <= 16'd0; else cycle_cnt <= cycle_cnt + 16'd1; end //比特位计数器 always@(posedge clk or negedge rst) begin if(rst == 1'b0) bit_cnt <= 3'd0; else if(state == S_RX_CYCLE ) if(cycle_cnt == CYCLE-1) bit_cnt <= bit_cnt + 3'd1; else bit_cnt <= bit_cnt; else bit_cnt <= 3'd0; end //串行数据转为并行数据 always@(posedge clk or negedge rst) begin if(rst == 1'b0) rx_bits <= 8'd0; else if(state == S_RX_CYCLE && cycle_cnt == CYCLE/2-1) rx_bits[bit_cnt] <= rx_pin; else rx_bits <= rx_bits; end //数据接收有效标志位 always@(posedge clk or negedge rst) begin if(rst == 1'b0) rx_valid <= 1'b0; else if(state == S_STOP && next_state != state) rx_valid <= 1'b1; else if(state == S_DATA && rx_ready) rx_valid <= 1'b0; end //数据接收 always@(posedge clk or negedge rst) begin if(rst == 1'b0) rx_data <= 8'd0; else if(state == S_STOP && next_state != state) rx_data <= rx_bits; else rx_data <= rx_data; end endmodule
模块整体波形图如下所示
采用两个寄存器,利用非阻塞赋值,对输入数据进行打拍,进而获得消息桢的下降沿。如下图所示:
内部状态转移和寄存器的波形图如下所示:
设计一个顶层程序,让FPGA每隔1秒发送一段字符串,并在等待时间里可接收计算机发送的数据,并将接收数据发送至PC端。
结构方框图如下所示:
FPGA每接收到一次数据就对LED进行电平翻转,所以增加了LED输出口,控制LED0。
代码如下:
module uart_test #( parameter CLK_FRE = 50, parameter BAUD_RATE = 115200 ) ( input clk, input rst, input rx, output tx, output reg led ); localparam IDLE = 0; localparam SEND = 1; localparam WAIT = 2; reg[1:0] state; reg[1:0] next_state; reg[7:0] tx_data; reg[7:0] tx_str; reg tx_data_valid; wire tx_data_ready; wire rx_ready; wire[7:0] rx_data; wire rx_valid; reg[3:0] tx_cnt; reg[31:0] wait_cnt; assign rx_ready = 1'b1; always@(posedge clk or negedge rst) begin if(rst == 1'b0) begin wait_cnt <= 32'd0; tx_cnt <= 8'd0; state <= IDLE; tx_data <= 8'd0; tx_data_valid <= 1'b0; led <= 1'b0; end else case(state) IDLE: state <= SEND; SEND: begin wait_cnt <= 32'd0; tx_data <= tx_str; if(tx_data_valid == 1'b1 && tx_data_ready == 1'b1 && tx_cnt < 4'd12)//发送字符串"I LOVE YOU" begin tx_cnt <= tx_cnt + 4'd1; end else if(tx_data_valid && tx_data_ready)//等待最后一个字节发送完 begin tx_cnt <= 8'd0; tx_data_valid <= 1'b0; state <= WAIT; end else if(~tx_data_valid) begin tx_data_valid <= 1'b1; end end WAIT://等待1s的间隔,等待时间里可接收字符串,并送至发送模块 begin wait_cnt <= wait_cnt +1'b1; if(rx_valid == 1'b1) begin tx_data_valid <= 1'b1; tx_data <= rx_data; led <= ~led; end else if(tx_data_valid && tx_data_ready) begin tx_data_valid <= 1'b0; end else if(wait_cnt >= CLK_FRE * 1000000) begin state <= SEND; end end default: state <= IDLE; endcase end //要发送的字符串 always@(*) begin case(tx_cnt) 4'd0:tx_str <= "I"; 4'd1:tx_str <= " "; 4'd2:tx_str <= "L"; 4'd3:tx_str <= "O"; 4'd4:tx_str <= "V"; 4'd5:tx_str <= "E"; 4'd6:tx_str <= " "; 4'd7:tx_str <= "Y"; 4'd8:tx_str <= "O"; 4'd9:tx_str <= "U"; 4'd10:tx_str <= "\r"; 4'd11:tx_str <= "\n"; default:tx_str <= 8'd0; endcase end uart_tx #( .CLK_FRE (CLK_FRE), .BAUD_RATE (BAUD_RATE) )uart_tx ( .clk(clk), .rst(rst), .tx_data(tx_data), .tx_data_valid(tx_data_valid), .tx_pin(tx), .tx_data_ready(tx_data_ready) ); uart_rx #( .CLK_FRE (CLK_FRE), .BAUD_RATE (BAUD_RATE) )uart_rx ( .clk(clk), .rst(rst), .rx_pin(rx), .rx_ready(rx_ready), .rx_valid(rx_valid), .rx_data(rx_data) ); endmodule
使用UCF文件对FPGA端口进行定义:
NET "clk" LOC = T8 | TNM_NET = sys_clk_pin;
TIMESPEC TS_sys_clk_pin = PERIOD sys_clk_pin 50000 kHz;
##
NET "rst" LOC = L3 | IOSTANDARD = "LVCMOS33"; ## reset pushbutton
##################################################################################
#USB Serial RS232 Pin define
##################################################################################
NET "rx" LOC = C11 | IOSTANDARD = "LVCMOS33"; ## Uart RXD:U4_TXD
NET "tx" LOC = D12 | IOSTANDARD = "LVCMOS33"; ## Uart TXD:U4_RXD
##################################################################################
#LED Pin define
##################################################################################
NET "led" LOC = P4 | IOSTANDARD = "LVCMOS33"; ## LED1
1、ERROR:HDLCompiler:1511 - “C:\Users\HP\Desktop\FPGA_demo\04_uart\rtl\uart_rx.v” Line 29: Mix of blocking and non-blocking assignments to variable <rx_bits> is not a recommended coding practice.
错误原因:阻塞赋值与非阻塞赋值同时使用
2、锁存器警告,case语句需要写完整,一定要写”default“,负责就会产生不需要的锁存器,对后续的时序设计带来影响。定义的寄存器一定要给定初值,负责也会产生锁存器。
串口结果如图所示:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。