本文将以 uart 发送为例,介绍 Quartus 的 RS232 UART IP 核的配置方法
一、UART 模块的配置及添加
1、调用 IP Catalog 界面
Tools —> IP Catalog
2、搜索找到并双击打开 RS232 UART
双击后会引导填写名称及存放路径,设置好后会自动打开 Platform Designer 页面
3、UART 参数配置
Parameters 这里的 Avalon Type 选择 Streaming,也就是数据流模式,我们将按字节传入和传出数据 其它配置项与常用 UART 无疑
4、时钟配置
因为配置波特率的相关参数是该 IP 核自动实现的,所以需要加入时钟模块,告知系统输入时钟的频率 (1)View —> System Contents 打开 System Contents 配置界面 (2)View —> IP Catelog 打开 IP Catalog 搜索 clock 并双击添加 Clock Source (3)配置将要为 UART 模块提供的数据频率,这里是 50 MHz (4)连接时钟模块与 UART 模块的 clk 和 reset
5、Generate —> Generate HDL 生成模块
6、在 Quartus 中添加生成的模块
(1)先将 Project Navigator 设置为 Files,然后在 Files 处右键选择 Add (2)添加刚刚生成的模块,然后点击 OK 保存
二、顶层文件的引用
1、实例化
从 Platform Designer 生成模块实例,将其拷贝到 Quartus 的顶层文件中 Generate —> Generate Instantiation Template 打开 Instantiation Template HDL Language 选择 Verilog,Copy 该实例,粘贴到顶层文件中
2、网络连接
uart u0 (
.clk_clk (clk), // clk.clk
.reset_reset_n (rst_n), // reset.reset_n
// .rs232_0_from_uart_ready (<connected-to-rs232_0_from_uart_ready>), // rs232_0_avalon_data_receive_source.ready
// .rs232_0_from_uart_data (<connected-to-rs232_0_from_uart_data>), // .data
// .rs232_0_from_uart_error (<connected-to-rs232_0_from_uart_error>), // .error
// .rs232_0_from_uart_valid (<connected-to-rs232_0_from_uart_valid>), // .valid
.rs232_0_to_uart_data (uart_send_data), // rs232_0_avalon_data_transmit_sink.data
// .rs232_0_to_uart_error (<connected-to-rs232_0_to_uart_error>), // .error
.rs232_0_to_uart_valid (uart_send_valid), // .valid
.rs232_0_to_uart_ready (uart_send_ready), // .ready
.rs232_0_UART_RXD (uart_rx), // rs232_0_external_interface.RXD
.rs232_0_UART_TXD (uart_tx) // .TXD
);
将带有 from 的连线(UART 接收数据使用的)及不用的连线注释掉,只连接发送数据需要用到的连线
三、发送逻辑的实现
1、UART TX 不忙时 rs232_0_to_uart_ready 为1,表示已经准备好发送数据了 2、这时将 rs232_0_to_uart_valid 置1,UART 将开始对 rs232_0_to_uart_data 的端口的数据进行发送 3、对于发送的控制:如果想在发完一个字节后停止或者发送一个字节改变数据(紧接着发送第二个字节),控制时序需要与 UART 模块的 clk_clk 同步。 下面的代码实现的是:发送一个字节停止,等待1s后发送下一个字节
module FPGA_UART (
input clk,
input rst_n,
input uart_rx,
output uart_tx
);
reg [7:0] uart_send_data = 8'd0;
reg uart_send_valid;
wire uart_send_ready;
reg [31:0] counter = 32'd0;
parameter state_wait = 4'd0;
parameter state_send = 4'd1;
reg [3:0] state = state_wait;
reg send_flag = 0;
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
uart_send_valid <= 0;
counter <= 32'd0;
uart_send_data <= 8'd0;
state <= state_wait;
send_flag = 0;
end
else begin
case (state)
state_wait: begin
counter <= counter + 32'd1;
if(counter >= 32'd49999999) begin
counter <= 32'd0;
state <= state_send;
end
end
state_send: begin
if(uart_send_ready && !send_flag) begin
send_flag <= 1;
uart_send_valid <= 1;
end
else begin
uart_send_data <= uart_send_data + 8'd1;
uart_send_valid <= 0;
state <= state_wait;
send_flag <= 0;
end
end
endcase
end
end
uart u0 (
.clk_clk (clk), // clk.clk
.reset_reset_n (rst_n), // reset.reset_n
// .rs232_0_from_uart_ready (<connected-to-rs232_0_from_uart_ready>), // rs232_0_avalon_data_receive_source.ready
// .rs232_0_from_uart_data (<connected-to-rs232_0_from_uart_data>), // .data
// .rs232_0_from_uart_error (<connected-to-rs232_0_from_uart_error>), // .error
// .rs232_0_from_uart_valid (<connected-to-rs232_0_from_uart_valid>), // .valid
.rs232_0_to_uart_data (uart_send_data), // rs232_0_avalon_data_transmit_sink.data
// .rs232_0_to_uart_error (<connected-to-rs232_0_to_uart_error>), // .error
.rs232_0_to_uart_valid (uart_send_valid), // .valid
.rs232_0_to_uart_ready (uart_send_ready), // .ready
.rs232_0_UART_RXD (uart_rx), // rs232_0_external_interface.RXD
.rs232_0_UART_TXD (uart_tx) // .TXD
);
endmodule
综合下载到开发板,用上位机观察数据,与预期结果一致,每秒发送一个字节: 如果想要实现,不停歇地将一组多个字节的数据依次发送,可以这样修改:
reg send_flag = 0; // 这个标志位的作用应该是表示移出数据标志位,这里懒得改了
reg end_flag = 0;
reg [15:0] data_buf = 16'h1236; // 测试数据
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
uart_send_valid <= 0;
counter <= 32'd0;
uart_send_data <= 8'd0;
state <= state_wait;
send_flag = 0;
end
else begin
case (state)
state_wait: begin
counter <= counter + 32'd1;
if(counter >= 32'd49999999) begin
counter <= 32'd0;
state <= state_send;
uart_send_data <= data_buf[7:0]; // 提前移出第一个字节
end
end
state_send: begin
if(end_flag) begin
end_flag <= 0;
uart_send_valid <= 0;
send_flag <= 0;
state <= state_wait;
end
else begin
uart_send_valid <= 1;
send_flag <= 1;
if(send_flag) begin // 这里是为了让uart_send_valid维持一个周期后等第一个字节发送完成后移出后面的字节
uart_send_data <= data_buf[15:8]; // 模拟字节移出
end_flag = 1; // 强行退出发送状态,实际运用中会判断数据长度
end
end
end
endcase
end
end
观察运行情况,每一秒发送两个字节,分别是 0x36,0x12:
|