串口通信及RAM读写
一、工程简介
串口通信简单来说就是一种以连续的串行数据流形式发送或接收数据的方式。以数据帧为单位,一次传输一帧数据。一般来说,一帧数据有八个数据位,根据传输方式不同会加上不同的起始位以及停止位,所以一般一帧数据有9~11位。传输一帧数据时,每次传输一个二进制位。
SRAM叫做静态随机存取存储器。 静态随机存取存储器是随机存取存储器的一种。所谓的“静态”,是指这种存储器只要保持通电,里面储存的数据就可以恒常保持。相对之下,动态随机存取存储器(DRAM)里面所储存的数据就需要周期性地更新。
该工程就是利用串口通信的方式将上位机发送过来的数据接收后写入到FPGA的RAM中,等待接收到发送命令,就将RAM中的信息读取出来发送到上位机。
本文主要是为了记录项目过程,将模块中的各个步骤都拆开来详细说明,所以会显得有点冗长且乱。
二、功能介绍
该设计具体功能为:
利用串口通信,从PC端接收到数据存储到FPGA的RAM中;等到按下按键,将RAM中的数据通过串口通信一次性全部发送到上位机去。
根据设计功能,可将本项目大体可划分为五个模块:
- 串口发送
- 串口接收
- RAM读写控制
- 按键消抖
- RAM核调用
注:隐约记得好像有一个错误是上位机接收到的数据是发送数据的双份,也就是上位机发送出去并经过RAM的读写后再回到上位机是两份重复的数据。因为自那以后没用过这个程序,也就没有找过原因
三、具体实现
1.顶层模块
顶层模块中实例化各个功能模块并调用,根据上述几个部分编写功能模块。(顶层程序的编写一般放到最后,根据最终的各个功能模块的接口进行编写)
module ram_uart(
input clk,
input rst_n,
input rs232_rx,
input key_in,
output rs232_tx
);
wire [7:0] rx_data;
wire [7:0] tx_data;
wire tx_done;
wire rx_done;
wire key_flag;
wire key_state;
wire [11:0] rdaddress;
wire [11:0] wraddress;
wire wren;
wire send_en;
fsm_key_filter uut_key_filter(
.clk(clk),
.rst_n(rst_n),
.key(key_in),
.key_flag(key_flag),
.key_state(key_state)
);
uart_rx2 uut_uart_rx(
.clk(clk),
.rst_n(rst_n),
.rs232_rx(rs232_rx),
.baud_set(3'd0),
.rx_done(rx_done),
.data_byte(rx_data)
);
uart_tx2 uut_uart_tx(
.clk(clk),
.rst_n(rst_n),
.send_en(send_en),
.baud_set(3'd0),
.data_byte(tx_data),
.rs232_tx(rs232_tx),
.tx_done(tx_done),
.uart_state()
);
ram_ip ram_dual(
.clock(clk),
.data(rx_data),
.rdaddress(rdaddress),
.wraddress(wraddress),
.wren(wren),
.q(tx_data)
);
uart_ctrl uut_controller(
.clk(clk),
.rst_n(rst_n),
.key_flag(key_flag),
.key_state(key_state),
.rx_done(rx_done),
.tx_done(tx_done),
.rdaddress(rdaddress),
.wraddress(wraddress),
.wren(wren),
.send_en(send_en)
);
endmodule
2.UART接收模块
先定义端口以及变量:
module uart_rx2
(
input clk,
input rst_n,
input rs232_rx,
input [2:0] baud_set,
output reg rx_done,
output reg [7:0] data_byte
);
reg rs232_rx_reg1;
reg rs232_rx_reg2;
reg rs232_rx_syn1;
reg rs232_rx_syn2;
reg uart_state;
reg [8:0] div_cnt;
reg [8:0] cnt_max;//div_cnt max value
reg baud_clk;
reg [7:0] baud_cnt;
reg [3:0] reg_data_byte [7:0];
reg [2:0] START;
reg [2:0] STOP;
// wire pos_edge;
wire neg_edge;
对信号延时两个时钟周期:
always@(posedge clk or negedge rst_n)
if(!rst_n)begin
rs232_rx_syn1 <= 0;
rs232_rx_syn2 <= 0;
end else begin
rs232_rx_syn1 <= rs232_rx;
rs232_rx_syn2 <= rs232_rx_syn1;
end
波特率计数器设置(可调):
//------baud_set------------------------
always@(*)
case(baud_set)
3'd0: cnt_max <= 9'd325;
3'd1: cnt_max <= 9'd163;
3'd2: cnt_max <= 9'd81;
3'd3: cnt_max <= 9'd54;
3'd4: cnt_max <= 9'd27;
default: cnt_max <= 9'd325;
endcase
//----frequence div-->baud_clk-----------
always@(posedge clk or negedge rst_n)
if(!rst_n)
div_cnt <= 0;
else if(uart_state)begin
if(div_cnt == cnt_max)
div_cnt <= 0;
else
div_cnt <