[米联客-安路飞龙DR1-FPSOC] FPGA基础篇连载-26 RS485串口程序收发环路设计
软件版本:Anlogic -TD5.9.1-DR1_ES1.1
操作系统:WIN10 64bit
硬件平台:适用安路(Anlogic)FPGA
实验平台:米联客-MLK-L1-CZ06-DR1M90G开发板
板卡获取平台:https://milianke.tmall.com/
登录"米联客"FPGA社区 http://www.uisrc.com 视频课程、答疑解惑!
1概述
在前面的课程中,我们已经学习了UART串口程序的设计,在工业场合为了提高串口的抗干扰能力,以及传输距离,RS485/RS422会更加广泛应用。在本文的实验中我们通过调用前面已经编写好的UART收发IP模块,以及FIFO,实现RS485的单工通信测试。其中重点是关于RS485芯片在单工通信方式下的信号控制逻辑设计。
2 RS485硬件电路设计
2.1 RS485驱动芯片介绍
SP3485 器件是一款3.3V 低功耗半双工收发器,符合RS-485 和RS-422 串行协议的规范。SP3485 可以满足RS-485 和RS-422 串行协议的电气规范,负载下最高10Mbps。
PIN |
功能 |
功能描述 |
1 |
RO |
数据接收输出 |
2 |
RE |
接收输出使能,低电平有效 |
3 |
DE |
输出使能,高电平有效 |
4 |
DI |
数据发送输入 |
5 |
GND |
地 |
6 |
A |
同相驱动器输出/接收器输入 |
7 |
B |
反相驱动器输出/接收器输入 |
8 |
VCC |
供电 |
2.2 RS485的应用电路设计
发送真值表:
INPUTS |
OUTPUTS |
|||
RE |
DE |
DI |
B |
A |
X |
1 |
1 |
0 |
1 |
X |
1 |
0 |
1 |
0 |
0 |
0 |
X |
High-Z |
|
1 |
0 |
X |
关闭 |
可以看到,结合原理图设计:
当DE是高电平,RE是任何值,DI决定了A B的输出电平;
当DE和RE都是0,AB 输出为高阻,这个时候,RO可以作为输入
接收真值表:
INPUTS |
OUTPUTS |
||
RE |
DE |
Va~Vb |
RO |
0 |
X |
-50mv |
1 |
0 |
X |
-200mv |
0 |
X |
X |
Open/Shorted |
1 |
1 |
1 |
X |
High-Z |
1 |
0 |
X |
关闭 |
当DE是0,DE为任意值,RO输入有效:
当Va~Vb的差模电压>-50mv RO输出1;
当Va~Vb的差模电压<-200mv RO输出0
2.3 瞬态抑制器的使用
对于工业场合的工作环境,SMBJ6.0瞬态抑制器,可以抑制RS485总线的浪涌,过压,对RS485芯片起到保护作用。
3 RS485的收发环路
3.1系统框图
RS485采用的通信协议依然是UART串口协议,因此收发模块还是使用前面已经写好的UART收发IP模块。为了实现单工模式的环路测试功能,这里增加了1个FIFO以及1个收发模块控制器。默认情况下,RS485芯片工作于接收数据状态,当FIFO有数据,并且一段时间内没有新数据到达,控制器切换RS485芯片到发送状态,从FIFO中取出缓存的数据发出。
3.2顶层模块源码
以下代码中核心部分为如何实现RS485数据方向的切换,以及数据到FIFO的缓存。
1 /*************RS485 半双工测试************* 2 --使用到了UART 发送和接收驱动 3 --通过FIFO缓存数据,并且检测到接收总线一段时间没有时间达到,进入发送模式,发送之前接收到的数据。 4 *********************************************************************/ 5 6 `timescale 1ns / 1ns //仿真时间刻度/精度 7 8 module rs485_top# 9 ( 10 parameter integer SYSCLKHZ = 25_000_000 //系统输入时钟频率参数 11 ) 12 ( 13 input I_sysclk,//系统时钟输入 14 output O_rs485_de, // 485 de控制,控制输入 输出方向,0 输入 1输出 15 input I_rs485_rx, // 485 RX输入总线 16 output O_rs485_tx // 485 TX输出总线 17 ); 18 19 wire reset_n; //内部上电延迟复位 20 wire rs485_rx; //内部RX 接收 21 wire rs485_fifo_empty; //FIFO空 22 wire rs485_fifo_rd_en; //FIFO 读使能 23 wire rs485_rvalid; //读数据有效 24 wire [7:0] rs485_rdata; //串口读数据(接收) 25 wire [7:0] rs485_wdata; //串口写数据(发送) 26 wire rs485_wbusy; //写忙 27 reg rs485_de; //485芯片的DE控制0 输入 1输出 28 reg rs485_wreq = 1'b0; //串口写请求 29 wire rs485_tx_start; //发送启动 30 reg rs485_tx_start_r = 1'b0; //发送启动寄存 31 reg [17:0] T_dcnt; //延迟计数器 32 reg [11:0] rstn_cnt = 12'd0; //复位计数器 33 34 //当rs485_de=0,设置rs485内部逻辑准备接收总线数据 35 assign rs485_rx = (rs485_de == 1'b0) ? I_rs485_rx : 1'b1; 36 //当FIFO中有数据,并且串口发送不忙,启动发送 37 assign rs485_tx_start = rs485_de&&(rs485_wbusy == 1'b0)&&(!rs485_fifo_empty); 38 //读FIFO使能,用rs485_tx_start的上升沿触发读FIFO 39 assign rs485_fifo_rd_en = (rs485_tx_start_r == 1'b0 & rs485_tx_start == 1'b1);//read fifo enable 40 //当rs485_de=0,设置rs485内部逻辑准备接收总线数据,否则,可以发送数据 41 assign O_rs485_de = rs485_de; 42 43 assign reset_n = rstn_cnt[11];//上电延迟复位 44 45 //复位计数器 46 always @(posedge I_sysclk)begin 47 if(rstn_cnt[11] == 1'b0) 48 rstn_cnt <= rstn_cnt + 1'b1; 49 else 50 rstn_cnt <= rstn_cnt; 51 end 52 53 //T_dcnt用于一段时间内,判断接收总线是否空闲 54 always @(posedge I_sysclk or negedge reset_n)begin 55 if(reset_n == 1'b0) 56 T_dcnt <= 17'd0; 57 else if(rs485_rx && (!rs485_fifo_empty))//如果RX总线是高电平,并且FIFO非空(有数据) 58 T_dcnt <= (T_dcnt[17] == 1'b1) ? T_dcnt : T_dcnt + 1'b1; //计数器累加 59 else 60 T_dcnt <= 0; //否则重置归零 61 62 end 63 64 //当rs485_de=0,设置rs485内部逻辑准备接收总线数据,否则,可以发送数据 65 always @(posedge I_sysclk )begin 66 if(reset_n ==1'b0)//if(rs485_de | reset_n ==1'b0) 67 rs485_de <= 1'b0;//重置rs485_de,切换到接收 68 else if((T_dcnt[17]==1'b1)|rs485_wreq|rs485_wbusy)//当T_dcnt延迟计数器到达计数值,或者串口发送控制器正在发送(rs485_wreq,rs485_wbusy有效) 69 rs485_de <= 1'b1;//设置rs485_de 70 else 71 rs485_de <= 1'b0;//设置rs485_de 72 end 73 74 //打拍寄存一次 75 always @(posedge I_sysclk) rs485_tx_start_r <= rs485_tx_start; 76 77 //rs485_wreq,延迟于rs485_fifo_rd_en,1个时钟,用于数据同步 78 always @(posedge I_sysclk) rs485_wreq <= rs485_fifo_rd_en; 79 80 //例化FIFO IP,FIFO设置标准模式,用户缓存接收到的帧 81 fifo_generator_0 inst_fifo ( 82 .clkw(I_sysclk), //写时钟输入 83 .wrst(reset_n == 1'b0), //写复位 84 .clkr(I_sysclk_p), //读时钟输入 85 .rrst(reset_n == 1'b0), //读复位 86 .di(rs485_rdata), //RX接收到的数据 87 .we(rs485_rvalid&(rs485_de==1'b0)), // 写FIFO 使能,当rs485_rvalid有效,写入数据 88 .re(rs485_fifo_rd_en), //FIFO读使能 89 .dout(rs485_wdata), //写数据 90 .empty_flag(rs485_fifo_empty) //FIFO 空 91 92 ); 93 94 //例化串口发送模块 95 uiuart_tx# 96 ( 97 .BAUD_DIV(SYSCLKHZ/115200 -1) //设置波特率 98 ) 99 uiuart_tx_u 100 ( 101 .I_clk(I_sysclk), //系统时钟 102 .I_uart_rstn(reset_n), //系统复位 103 .I_uart_wreq(rs485_wreq), //串口发送驱动器发送请求 104 .I_uart_wdata(rs485_wdata), //串口发送,数据 105 .O_uart_wbusy(rs485_wbusy), //串口发送总线忙 106 .O_uart_tx(O_rs485_tx) //串口发送总线 107 ); 108 109 //例化串口接收模块 110 uiuart_rx# 111 ( 112 .BAUD_DIV(SYSCLKHZ/115200 -1) //设置波特率 113 ) 114 uiuart_rx_u 115 ( 116 .I_clk(I_sysclk), //系统时钟 117 .I_uart_rx_rstn(reset_n), //系统复位 118 .I_uart_rx(rs485_rx), //串口接收总线 119 .O_uart_rdata(rs485_rdata), //串口接收数据 120 .O_uart_rvalid(rs485_rvalid)//串口接收数据有效 121 ); 122 123 endmodule
4 FPGA工程
关于软件安装、FPGA工程创建、编译、下载等请参考"米联客2023版FPGA课程-安路FPGA TD软件快速入门课程.pdf",该章节路径如下:
打开配套代码的FPGA工程如下:
米联客的代码管理规范,在对应的FPGA工程路径下创建uisrc路径,并且创建以下文件夹
01_rtl:放用户编写的rtl代码
02_sim:仿真文件或者工程
03_ip:放使用到的ip文件
04_pin:放fpga的pin脚约束文件或者时序约束文件
05_boot:放编译好的bit或者bin文件(一般为空)
06_doc:放本一些相关文档(一般为空)
5 RTL仿真
5.1仿真测试文件
1 `timescale 1ns/1ns //定义仿真时间刻度/精度 2 3 module sim_top_tb(); 4 5 localparam BPS = 'd115200 ; //波特率 6 localparam CLK_FRE = 'd25_000_000 ; //系统频率 7 localparam CLK_TIME = 'd250_000_000 /CLK_FRE; //计算系统时钟周期,以ns为单位 8 localparam BIT_TIME = 'd250_000_000 / BPS ; //计算出传输每个bit所需要的时间以ns为单位 9 localparam NUM_BYTES = 3; //需要发送的BYTES 10 11 reg sysclk_p; //系统时钟 12 reg bsp_clk ; //波特率时钟 13 reg uart_tx; //uart 数据发送,该信号接入到,FPGA的uart 接收 14 wire uart_rx; //uart 数据接收,该信号接入到,FPGA的uart 发送 15 wire rs485_de; 16 reg [8*NUM_BYTES-1:0] uart_send_data; //需要发送的数据 17 reg [7:0] uart_send_data_r; //寄存每次需要发送的BYTE 18 19 integer i,j; 20 21 //例化顶层模块 22 rs485_top# 23 ( 24 .SYSCLKHZ(CLK_FRE) 25 ) 26 rs485_top_inst 27 ( 28 .I_sysclk(sysclk_p), 29 .O_rs485_de(rs485_de), 30 .I_rs485_rx(uart_tx), 31 .O_rs485_tx(uart_rx) 32 ); 33 34 //仿真初始化 35 initial begin 36 37 //初始化REG寄存器 38 sysclk_p =0; 39 bsp_clk = 0; 40 uart_tx = 1; 41 i=0; 42 j=0; 43 44 uart_send_data =0; 45 uart_send_data_r =0; 46 47 #20000;//延迟20000ns,等待uart测试代码中的复位延迟 48 49 uart_send_data[(0*8) +: 8] = 8'b1001_0101;//初始化需要发送的第1个BYTE 50 uart_send_data[(1*8) +: 8] = 8'b0000_0101;//初始化需要发送的第2个BYTE 51 uart_send_data[(2*8) +: 8] = 8'b1000_0100;//初始化需要发送的第3个BYTE 52 53 //uart tx 发送数据 54 for(i=0; i<NUM_BYTES;i=i+1) 55 begin 56 57 uart_send_data_r = uart_send_data[(i*8) +: 8];//寄存需要发送的数据到寄存器 58 $display("uart_send_data : 0x%h",uart_send_data_r);//打印准备发送的数据 59 60 @(posedge bsp_clk); //发送起始位1bit 61 uart_tx = 1'b0; 62 63 for(j=0;j<8;j=j+1)begin//发送数据8bits 64 @(posedge bsp_clk); //发送 65 uart_tx = uart_send_data_r[j]; 66 end 67 68 @(posedge bsp_clk);//发送停止位1bit 69 uart_tx = 1'b1; 70 71 end 72 @(posedge bsp_clk); 73 #200 $finish; 74 end 75 76 always #(CLK_TIME/2) sysclk_p = ~sysclk_p; //产生主时钟 77 always #(BIT_TIME/2) bsp_clk = ~bsp_clk; //产生波特率时钟 78 79 80 endmodule
5.2仿真结果
6 实验演示
下载程序前,先确保FPGA工程已经编译。
6.1 硬件连接
请确保下载器和开发板已经正确连接,并且开发板已经上电(注意JTAG端子不支持热插拔,而USB接口支持,所以在不通电的情况下接通好JTAG后,再插入USB到电脑,之后再上电,以免造成JTAG IO损坏)
我们使用的是板卡自带的RS485接口的RS485_0接口,为了方便我们测试,我们使用了RS485转USB的转接线与我们的电脑端相连接,方便我们的通信
6.2 实验结果
本文来米联客(milianke),作者:米联客(milianke),转载请注明原文链接:https://www.cnblogs.com/milianke/p/18335293