赞
踩
从本文开始,将连载fpga开发基础知识,将这几年浅显的fpga开发经验整理出来,一是梳理一下这几年给别人做fpga的经历,同时也是分享给大家,也希望大牛批评指正。
串口通信是非常基本且应用十分广泛的低速通信接口,无论是在dsp、单片机、arm还是在fpga中,编写uart串口通信程序是必备的基础。
首先要先了解UART串口通信的基本概念,UART串口通信是全双工的,支持发送和接收通信同时进行。硬件上UART串口只需要两条线tx和rx,分别进行发送和接收。UART串口通信没有同步时钟线,这就需要引入一个概念波特率来区分两位数据实现串行通信,波特率是指每秒传输的位数。常见的有9600bps、115200bps、460800bps等等。在fpga实现中,只需要理解每一位的时间长度是1/波特率,计数长度是时钟频率/波特率就可以了。
UART串口通信基本协议,一次UART串口发送和接收一般包含10bit或11bit,包含校验位是11bit,不包含则是10bit。有效数据位一般是8bit。依次为起始位、数据位、校验位(可选)、停止位。
起始位:首先发送的第一位,必须为0,提示接收方开始接收有效数据。
数据位:一般为8位,有效数据位。
校验位:可选位,一般为奇校验、偶校验、固定校验。校验位由数据位中1的个数来决定
停止位:最后发送的一位,必须为1,提示接收方结束接收。
包含以上所有位为一帧数据,经过一个间隔后发送或接收第二帧。由以上的内容我们可以知道如何发送和接收一帧数据了。接下来就是用fpga实现。
首先编写发送模块,先将发送数据缓存,状态机由空闲状态转为发送状态,空闲状态指示信号拉低,通知上层模块发送正在进行不要更新待发送字节。根据波特率进行计数,依次发送一帧数据的所有位,发送完成后状态机转入空闲拉高空闲状态,一次发送就完成了。
// // // // // // This source file is part of uart module. // // module features: 1.config bits number of one byte // // 2.config bitrate // // module function: transmit uart bits // // copyright of this source file belongs to mayidianzi in CSDN blog // // contact the author by e-mail 1061165280@qq.com // // // // //================================================================================ // Revision History: // Date By Revision Change Description //-------------------------------------------------------------------------------- // 2023/2/26 mayidianzi 1.0 Original //*******************************************************************************/ module uart_tx #( parameter CLK_FRE = 50, //module clock frequency(MHz) parameter BAUD_RATE = 115200, //serial baud rate parameter TRANSMIT_BITS = 9 //bits number per byte ) ( input clk, //clock input input rst_n, //asynchronous reset input, low active input[TRANSMIT_BITS-1:0] tx_data, //data to send input tx_data_valid, //data to be sent is valid output reg tx_data_ready, //send ready output tx_pin //serial data output ); / parameter config \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ //calculates the clock cycle for baud rate localparam CYCLE = CLK_FRE * 1000000 / BAUD_RATE; //state machine code localparam C_IDLE = 1;//wait for transmit localparam C_START = 2;//start bit localparam C_SEND_BYTE = 3;//data bits localparam C_STOP = 4;//stop bit localparam C_WAIT = 5;//blank between two bytes transmit // register define \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ reg[2:0] state; reg[15:0] cycle_cnt; //baud counter reg[3:0] bit_cnt;//bit counter reg[TRANSMIT_BITS-1:0] tx_data_latch; //latch data to send reg tx_reg; //serial data output assign tx_pin = tx_reg; // main code \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ //state machine always@(posedge clk or negedge rst_n) begin if(!rst_n) begin state <= C_IDLE; cycle_cnt <= 0; bit_cnt <= 0; end else case(state) C_IDLE: if(tx_data_valid == 1'b1) state <= C_START; else state <= C_IDLE; C_START: if(cycle_cnt == CYCLE - 1) begin cycle_cnt <= 0; state <= C_SEND_BYTE; end else cycle_cnt <= cycle_cnt + 1; C_SEND_BYTE: if(cycle_cnt == CYCLE - 1 && bit_cnt == TRANSMIT_BITS-1) begin state <= C_STOP; cycle_cnt <= 0; bit_cnt <= 0; end else if(cycle_cnt == CYCLE - 1) begin cycle_cnt <= 0; bit_cnt <= bit_cnt + 1; end else cycle_cnt <= cycle_cnt + 1; C_STOP: if(cycle_cnt == CYCLE - 1) begin state <= C_WAIT; cycle_cnt <= 0; end else cycle_cnt <= cycle_cnt + 1; C_WAIT: if(cycle_cnt == 10*CYCLE - 1) begin state <= C_IDLE; cycle_cnt <= 0; end else cycle_cnt <= cycle_cnt + 1; default: state <= C_IDLE; endcase end //data output always@(posedge clk or negedge rst_n) begin if(rst_n == 1'b0) tx_data_ready <= 1'b0; else if(state == C_IDLE) if(tx_data_valid == 1'b1) tx_data_ready <= 1'b0; else tx_data_ready <= 1'b1; else if(state == C_WAIT && cycle_cnt == 10*CYCLE - 1) tx_data_ready <= 1'b1; end always@(posedge clk or negedge rst_n) begin if(rst_n == 1'b0) begin tx_data_latch <= 0; end else if(state == C_IDLE && tx_data_valid == 1'b1) tx_data_latch <= tx_data; end always@(posedge clk or negedge rst_n) begin if(rst_n == 1'b0) tx_reg <= 1'b1; else case(state) C_IDLE,C_STOP: tx_reg <= 1'b1; C_START: tx_reg <= 1'b0; C_SEND_BYTE: tx_reg <= tx_data_latch[bit_cnt]; default: tx_reg <= 1'b1; endcase end endmodule
然后再来编写接收模块,接收模块状态机要检测起始位,发现信号拉低后进入接收状态,按照波特率进行计数依次接收所有位,之后转入空闲状态等待下一个起始位,同时拉高数据接收完毕信号提示上层锁存接收数据。
// // // // // // This source file is part of uart module. // // module features: 1.config bits number of one byte // // 2.config bitrate // // module function: receive uart bits // // copyright of this source file belongs to mayidianzi in CSDN blog // // contact the author by e-mail 1061165280@qq.com // // // // //================================================================================ // Revision History: // Date By Revision Change Description //-------------------------------------------------------------------------------- // 2023/2/26 mayidianzi 1.0 Original //*******************************************************************************/ module uart_rx #( parameter CLK_FRE = 50, //module clock frequency(MHz) parameter BAUD_RATE = 115200, //serial baud rate parameter RECEIVE_BITS = 9 //bits number per byte ) ( input clk, //clock input input rst_n, //asynchronous reset input, low active output reg[RECEIVE_BITS-1:0] rx_data, //received serial data output reg rx_data_valid, //received serial data is valid input rx_pin //serial data input ); / parameter config \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ //calculates the clock cycle for baud rate localparam CYCLE = CLK_FRE * 1000000 / BAUD_RATE; //state machine localparam C_WAIT = 0; //blank between two bytes localparam C_IDLE = 1; //wait for start bit localparam C_START = 2; //start bit localparam C_REC_BYTE = 3; //data bits localparam C_STOP = 4; //stop bit localparam C_DATA = 5; //data pack // register define \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ reg[2:0] state; reg[RECEIVE_BITS-1:0] rx_bits; //temporary storage of received data reg[15:0] cycle_cnt; //baud counter reg[3:0] bit_cnt; //bit counter // main code \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ //state machine always@(posedge clk or negedge rst_n) begin if(!rst_n) begin state <= C_WAIT; cycle_cnt <= 0; bit_cnt <= 0; end else case(state) C_WAIT: //wait for blank between two bytes if(cycle_cnt > CYCLE & rx_pin == 1) begin cycle_cnt <= 0; state <= C_IDLE; bit_cnt <= 0; end else if(rx_pin == 1) cycle_cnt <= cycle_cnt + 1; else cycle_cnt <= 0; C_IDLE: //wait for first start bit if(rx_pin == 0) state <= C_START; else state <= C_IDLE; C_START: //start bit if(cycle_cnt == CYCLE - 1) //one data cycle begin state <= C_REC_BYTE; cycle_cnt <= 0; end else cycle_cnt <= cycle_cnt + 1; C_REC_BYTE: if(cycle_cnt == CYCLE - 1 && bit_cnt == RECEIVE_BITS-1) //receive bits data begin state <= C_STOP; bit_cnt <= 0; cycle_cnt <= 0; end else if(cycle_cnt == CYCLE - 1) begin cycle_cnt <= 0; bit_cnt <= bit_cnt + 1; end else cycle_cnt <= cycle_cnt + 1; C_STOP: if(cycle_cnt == CYCLE - 1) begin state <= C_DATA; cycle_cnt <= 0; end else cycle_cnt <= cycle_cnt + 1; C_DATA: //data receive complete state <= C_IDLE; default: state <= C_WAIT; endcase end //data ready signal always@(posedge clk or negedge rst_n) begin if(rst_n == 1'b0) rx_data_valid <= 1'b0; else if(state == C_STOP) rx_data_valid <= 1'b1; else if(state == C_DATA) rx_data_valid <= 1'b0; end //data output always@(posedge clk or negedge rst_n) begin if(rst_n == 1'b0) rx_data <= 8'd0; else if(state == C_STOP) rx_data <= rx_bits;//latch received data end //receive serial data bit data always@(posedge clk or negedge rst_n) begin if(rst_n == 1'b0) rx_bits <= 8'd0; else if(state == C_REC_BYTE && cycle_cnt == CYCLE/2 - 1) rx_bits[bit_cnt] <= rx_pin; else rx_bits <= rx_bits; end endmodule
以上所有内容均记载在我的串口通用模块内,可以直接下载测试工程了解,或根据自己的工程需要进行改编。如果不想在串口通信方面耽误时间,集中力量开发高级功能,可直接调用我编写的通用串口模块,可直接进行例化实现串口通信功能。通用串口模块可分别自定义收发波特率、收发校验种类,具备串口通信常用的全部功能,可根据实际需求设置,减少您的开发周期,本通用串口模块经过仿真模拟和硬件测试,附有测试报告和开发说明,方便使用,传送门:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。