搜索
bottom↓
回复: 0

【正点原子FPGA连载】第二十章RS485串口通信实验

[复制链接]

出0入234汤圆

发表于 2021-1-28 11:18:16 | 显示全部楼层 |阅读模式
1)实验平台:正点原子超越者FPGA开发板
2)  章节摘自【正点原子】超越者之FPGA开发指南
3)购买链接:https://item.taobao.com/item.htm?&id=631660290421
4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/fpga/zdyz-chaoyuezhe.html
5)正点原子官方B站:https://space.bilibili.com/394620890
6)正点原子FPGA技术交流群:905624739
QQ群头像.png

100846rel79a9p4uelap24.jpg

100846f1ce1fg14zbg0va4.png

第二十章RS485串口通信实验


RS-485是针对UART串口的一种接口标准,它定义了串行通信系统中发送器和接收器的一系列电气特性。相比于RS-232,RS-485标准的通信系统抗干扰能力较强,可实现长距离数据传输,同时支持多个收发器连接到同一个通信网络中。因此,RS-485在工业控制领域以及有类似需求的系统中得到了广泛的应用。
本章包括以下几个部分:
2020.1  简介
20.2  实验任务
20.3  硬件设计
20.4  程序设计
20.5  下载验证


20.1简介
在“串口通信实验”章节我们详细地介绍了UART串口通信以及RS-232接口标准。实际上,除了RS-232之外,RS-422和RS-485也都是常用的串行通信接口标准,它们的接口定义了不同的电气特性,如RS-232是单端输入输出,而RS-422/485为差分输入输出等。
在介绍RS-485之前,我们先来了解一下串口通信过程中单端传输与差分传输的差别。单端传输是指在发送或接收过程中,用信号线对地线的电压值来表示逻辑“0”和“1”。而差分传输使用两根信号线来传输一路信号,这两根信号线上传输的信号幅值相等,相位相差180度(极性相反),用它们的差值来表示逻辑“0”和“1”,如图 20.1.1所示。
20553.png

图 20.1.1 差分传输方式

在传输过程中,当信号线上叠加了频率、幅值和相位都相同的干扰信号时(共模干扰),对于单端传输而言,由于地线电位为0,则传输的信号就包含了干扰信号;而在差分传输方式下,干扰可以通过两个信号线上电压的差值抵消,相当于抑制了共模干扰,如图 20.1.2所示。因此相对于单端传输方式,差分传输大大提高了信号在传输过程中的抗干扰能力,但是需要多余的信号线来实现信号传输。
20835.png

图 20.1.2 差分传输抑制共模干扰

RS-232接口标准出现较早,信号采用负逻辑电平、单端传输方式工作。通过一根信号线发送,一根信号线接收,加上一根地线,RS-232可实现全双工通信。由于单端传输方式抗干扰能力差,导致RS-232标准通信距离短(小于15米),数据传输速率低等问题。另外RS-232仅支持一对一通信,存在无法实现多个设备互联的缺点。
RS-422由RS-232发展而来,它是为弥补RS-232之不足而提出的。RS-422采用差分传输(又称平衡传输)方式,将最大传输速率提高到10Mbps;当传输速率在100kbps以下时,传输距离可达1200米。由于采用差分传输方式,RS-422需要4根信号线来实现全双工通信,两根用于发送、两根用于接收,一般会再加上一根地线。RS-422允许在一条传输总线上连接最多10个接收器,从而实现单个设备发送,多个设备接收的功能。
为扩展应用范围,在RS-422基础上又制定了RS-485标准。RS-485同样采用差分传输方式,但是RS-485只有2根信号线,由发送和接收共用,因此发送和接收不能同时进行,只能实现半双工通信。RS-485增加了多点、双向通信能力,即允许多个发送器连接到同一条总线上,各设备通过使能信号控制发送和接收过程。
20.2实验任务
本节实验任务是使用两块超越者开发板通过RS-485端口互联,由各自开发板上的四个按键分别控制对方开发板上四个LED灯的亮灭。当按键按下时,对方开发板上对应的LED灯点亮;按键释放时,对应的LED灯熄灭。
20.3硬件设计
RS485串口部分的原理图如图 20.3.1所示。由于FPGA串口输入输出引脚为TTL电平,用3.3V代表逻辑“1”,0V代表逻辑“0”;而RS-485电平标准采用差分信号的差值电压来代表逻辑“0”和“1”。因此当FPGA与RS485接口标准的设备通信时,需要加电平转换芯片SP3485,实现RS485电平与TTL电平的转换。
201725.png

图 20.3.1 RS485串口原理图

由于RS-485为半双工通信方式,需要通过使能信号来控制发送和接收过程。在图 20.3.1中,电平转换芯片SP3485的2号引脚为低电平接收使能,3号引脚为高电平发送使能。在这里我们将两个引脚连接在一起,只需要通过一个信号RS485_DE即可控制收发过程:当RS485_DE为高电平时,SP3485处于发送过程;当RS485_DE为低电平时,SP3485处于接收过程。
202000.png

图 20.3.2 RS232/RS485选择接口

图 20.3.2为RS232/RS485的选择接口,由上图可知,SP3485芯片端口的RS485_RX和RS485_TX并没有直接和FPGA的引脚相连接,而是连接到开发板的P1口,RS232串口和RS485串口共用P1口的UART2_TX和UART2_RX,UART2_TX和UART2_RX是直接和FPGA的引脚相连接的,这样的设计方式实现了有限IO的多种复用功能。因此,在做RS485串口通信实验时,需要使用杜邦线或者跳帽将RS485_RX和UART2_TX连接在一起,RS485_TX和UART2_RX连接在一起。
本实验中,各端口信号的管脚分配如下表所示:
表 20.3.1 RS485串口通信实验管脚分配
2031.png

对应的UCF约束语句如下所示:
  1. NET "sys_clk" LOC = N8 | IOSTANDARD = "LVCMOS33";
  2. NET "sys_rst_n" LOC = G16 | IOSTANDARD = "LVCMOS33";
  3. NET "rs485_uart_rxd" LOC = M11 | IOSTANDARD = "LVCMOS33";
  4. NET "rs485_uart_txd" LOC = P12 | IOSTANDARD = "LVCMOS33";
  5. NET "key[3]" LOC = R3 | IOSTANDARD = "LVCMOS33";
  6. NET "key[2]" LOC = N6 | IOSTANDARD = "LVCMOS33";
  7. NET "key[1]" LOC = N5 | IOSTANDARD = "LVCMOS33";
  8. NET "key[0]" LOC = P5 | IOSTANDARD = "LVCMOS33";
  9. NET "led[3]" LOC = T3 | IOSTANDARD = "LVCMOS33";
  10. NET "led[2]" LOC = M7 | IOSTANDARD = "LVCMOS33";
  11. NET "led[1]" LOC = M6 | IOSTANDARD = "LVCMOS33";
  12. NET "led[0]" LOC = T5 | IOSTANDARD = "LVCMOS33";
复制代码

20.4程序设计
根据实验任务,我们可以大致规划出系统的控制流程:当检测到有按键按下或释放时,将按键数据通过RS485串口发送出去;而当RS485串口接收到对方发送的按键数据时,根据接收到的数据改变LED灯的显示状态。由此画出系统的功能框图如下所示:
203521.png

图 20.4.1 RS485串口实验系统框图

由系统总体框图可知,FPGA部分包括五个模块,顶层模块(rs485_uart_top)、接收模块(uart_recv)、发送模块(uart_send)、按键消抖模块(key_debounce)和LED灯控制模块(led_ctrl)。其中在顶层模块中完成对另外四个模块的例化。
由于RS-485只是对接口标准的定义,数据的传输仍然是按照UART串口通信协议进行。因此我们可以直接调用“串口通信实验”中的串口发送和接收模块。在这里我们仍然设置数据位为8位,停止位为1位,无校验位,波特率为115200bps。
key_debounce为按键消抖模块,在检测到有按键按下或释放时对按键数据进行消抖处理,在按键数据稳定后给出通知信号key_flag,并将数据由串口发送模块uart_send发送出去。uart_recv为串口接收模块,它负责接收对方发送的按键数据,并在一帧数据(8位)接收结束后给出通知信号uart_done。当LED灯控制模块led_ctrl检测到该通知信号时,根据接收到的按键数据改变板卡上LED灯的显示状态。
顶层模块的代码如下:
  1. 1  module rs485_uart_top(
  2. 2      input           sys_clk,           //外部50M时钟
  3. 3      input           sys_rst_n,         //外部复位信号,低有效
  4. 4      
  5. 5      input  [3:0]    key,               //按键
  6. 6      output [3:0]    led,               //led灯
  7. 7      
  8. 8      //uart接口
  9. 9      input           rs485_uart_rxd,    //rs485串口接收端口
  10. 10     output          rs485_uart_txd     //rs485串口发送端口
  11. 11     );
  12. 12   
  13. 13   
  14. 14 always @(posedge sys_clk or negedge sys_rst_n) begin
  15. 15     if (!sys_rst_n) begin
  16. 16         rs485_uart_txd_d0 <= 1'b0;
  17. 17         rs485_uart_rxd_d0 <= 1'b0;         
  18. 18     end
  19. 19     else begin
  20. 20         rs485_uart_rxd_d0  <= rs485_uart_rxd;                  
  21. 21         rs485_uart_txd_d0  <= rs485_uart_rxd;
  22. 22     end   
  23. 23 end   
  24. 24
  25. 25 reg rs485_uart_txd_d0;
  26. 26 reg rs485_uart_rxd_d0;
  27. 27
  28. 28 //parameter define
  29. 29 parameter  CLK_FREQ = 50000000;        //定义系统时钟频率
  30. 30 parameter  UART_BPS = 115200;          //定义串口波特率
  31. 31     
  32. 32 //wire define   
  33. 33 wire       tx_en_w;                    //UART发送使能
  34. 34 wire       rx_done_w;                  //UART接收完毕信号
  35. 35 wire [7:0] tx_data_w;                  //UART发送数据
  36. 36 wire [7:0] rx_data_w;                  //UART接收数据
  37. 37 wire [3:0] key_value_w;                //消抖后的按键数据
  38. 38
  39. 39 wire [35 : 0] CONTROL0;
  40. 40 wire [35 : 0] CONTROL;
  41. 41 wire CLK;
  42. 42 wire [63 : 0] TRIG0;
  43. 43
  44. 44 //*****************************************************
  45. 45 //**                    main code
  46. 46 //*****************************************************   
  47. 47 assign tx_data_w = {4'd0,key_value_w}; //将按键消抖后的值送到发送模块
  48. 48
  49. 49 assign TRIG0[0] = rs485_uart_rxd ;
  50. 50 assign TRIG0[1] = rs485_uart_txd ;
  51. 51
  52. 52 uart_recv #(                           //串口接收模块
  53. 53     .CLK_FREQ       (CLK_FREQ),        //设置系统时钟频率
  54. 54     .UART_BPS       (UART_BPS))        //设置串口接收波特率
  55. 55 u_uart_recv(                 
  56. 56     .sys_clk        (sys_clk),
  57. 57     .sys_rst_n      (sys_rst_n),
  58. 58     
  59. 59     .uart_rxd       (rs485_uart_rxd),
  60. 60     .uart_done      (rx_done_w),
  61. 61     .uart_data      (rx_data_w)
  62. 62     );
  63. 63     
  64. 64 uart_send #(                           //串口发送模块
  65. 65     .CLK_FREQ       (CLK_FREQ),        //设置系统时钟频率
  66. 66     .UART_BPS       (UART_BPS))        //设置串口发送波特率
  67. 67 u_uart_send(                 
  68. 68     .sys_clk        (sys_clk),
  69. 69     .sys_rst_n      (sys_rst_n),
  70. 70     .uart_tx_busy   (),
  71. 71     .uart_en        (tx_en_w),
  72. 72     .uart_din       (tx_data_w),
  73. 73     .uart_txd       (rs485_uart_txd)
  74. 74     );
  75. 75     
  76. 76 key_debounce u_key_debounce(
  77. 77     .sys_clk        (sys_clk),
  78. 78     .sys_rst_n      (sys_rst_n),
  79. 79     
  80. 80     .key            (key),
  81. 81     .key_flag       (tx_en_w),         //按键有效通知信号
  82. 82     .key_value      (key_value_w)      //按键消抖后的数据
  83. 83     );
  84. 84     
  85. 85 led_ctrl u_led_ctrl(
  86. 86     .sys_clk        (sys_clk),
  87. 87     .sys_rst_n      (sys_rst_n),
  88. 88     
  89. 89     .led_en         (rx_done_w),       //led控制使能
  90. 90     .led_data       (rx_data_w[3:0]),  //led控制数据
  91. 91     .led            (led)
  92. 92 );
  93. 93
  94. 94 endmodule
复制代码

顶层模块中主要完成对其余模块的例化,需要注意的是程序第47行:由于板卡上只有4个按键,而串口通信过程中数据位为8位,因此需要将消抖后得到的4按键位数据高位补四个零,然后再给到串口发送模块。同样,在将接收的按键数据用于LED灯控制时,仅将低四位有效位赋值给LED灯控制模块,如第90行所示。
有关串口收发过程更详细的介绍请大家参考“串口通信实验”,下面我们来介绍一下另外两个模块:按键消抖模块和LED灯控制模块。
在机械按键按下和释放的过程中,由于机械触点的弹性作用,按键开关在闭合的瞬间不会立即稳定地导通,在释放时也不是立刻就能完全断开。因此,在闭合及断开的瞬间均伴随有一连串的抖动,如图 20.4.2所示。按键的抖动过程体现在数字电路中就是不断变化的高低电平,为避免在抖动过程中采集到错误的按键状态,我们需要对按键数据进行消除抖动处理。
207433.png

图 20.4.2 机械按键抖动过程

按键抖动的时间长短由按键的机械特性决定,一般为5ms~10ms,在抖动时间内按键状态可能会不断的发生变化。由于按键的抖动过程持续时间较短,很快就趋于稳定状态。因此在按键按下及释放之后,若按键能稳定在同一状态且持续时间达20ms,我们就认为抖动过程已经结束,此时的采集的按键数据有效。
按键消抖模块的代码如下所示:
  1. 1  module key_debounce(
  2. 2      input            sys_clk,          //外部50M时钟
  3. 3      input            sys_rst_n,        //外部复位信号,低有效
  4. 4      
  5. 5      input      [3:0] key,              //外部按键输入
  6. 6      
  7. 7      output reg       key_flag,         //按键数据有效信号
  8. 8      output reg [3:0] key_value         //按键消抖后的数据
  9. 9      );
  10. 10
  11. 11 //reg define   
  12. 12 reg [31:0] delay_cnt;
  13. 13 reg [ 3:0] key_reg;
  14. 14
  15. 15 //*****************************************************
  16. 16 //**                    main code
  17. 17 //*****************************************************
  18. 18 always @(posedge sys_clk or negedge sys_rst_n) begin
  19. 19     if (!sys_rst_n) begin
  20. 20         key_reg   <= 4'b1111;
  21. 21         delay_cnt <= 32'd0;
  22. 22     end
  23. 23     else begin
  24. 24         key_reg <= key;
  25. 25         if(key_reg != key)             //一旦检测到按键状态发生变化(有按键被按下或释放)
  26. 26             delay_cnt <= 32'd1000000;  //给延时计数器重新装载初始值(计数时间为20ms)
  27. 27         else if(key_reg == key) begin  //在按键状态稳定时,计数器递减,开始20ms倒计时
  28. 28                  if(delay_cnt > 32'd0)
  29. 29                      delay_cnt <= delay_cnt - 1'b1;
  30. 30                  else
  31. 31                      delay_cnt <= delay_cnt;
  32. 32              end           
  33. 33     end   
  34. 34 end
  35. 35
  36. 36 always @(posedge sys_clk or negedge sys_rst_n) begin
  37. 37     if (!sys_rst_n) begin
  38. 38         key_flag  <= 1'b0;
  39. 39         key_value <= 4'b1111;         
  40. 40     end
  41. 41     else begin
  42. 42         if(delay_cnt == 32'd1) begin   //当计数器递减到1时,说明按键稳定状态维持了20ms
  43. 43             key_flag  <= 1'b1;         //此时消抖过程结束,给出一个时钟周期的标志信号
  44. 44             key_value <= key;          //并寄存此时按键的值
  45. 45         end
  46. 46         else begin
  47. 47             key_flag  <= 1'b0;
  48. 48             key_value <= key_value;
  49. 49         end  
  50. 50     end   
  51. 51 end
  52. 52     
  53. 53 endmodule
复制代码

程序中第25行不断检测按键状态,一旦发现按键状态发生改变时,就给计数器delay_cnt赋初值1000000。在按键状态不发生改变时delay_cnt递减从而实现倒计时的功能,在倒计时过程中,一旦检测到按键状态发生改变,则说明有抖动产生,此时重新给delay_cnt赋初值,并开始新一轮倒计时。在50Mhz时钟驱动下,delay_cnt若能由1000000递减至1,则说明按键状态保持稳定时间达20ms,此时输出一个时钟周期的通知信号key_flag,并将此时的按键数据寄存输出。
串口接收模块在接收对方发送的按键数据后,将数据低4位(高4位为零)给到LED控制模块,并输出通知信号uart_done。LED灯控制模块在检测到uart_done的上升沿时,利用接收到的按键数据改变LED灯的显示状态。
LED灯控制模块的代码如下:
  1. 1  module led_ctrl(
  2. 2      input            sys_clk,          //外部50M时钟
  3. 3      input            sys_rst_n,        //外部复位信号,低有效
  4. 4      
  5. 5      input            led_en,           //led控制使能
  6. 6      input      [3:0] led_data,         //led控制数据
  7. 7      
  8. 8      output reg [3:0] led               //led灯
  9. 9      );
  10. 10
  11. 11 //reg define
  12. 12 reg led_en_d0;
  13. 13 reg led_en_d1;
  14. 14
  15. 15 //wire define
  16. 16 wire led_en_flag;
  17. 17
  18. 18 //*****************************************************
  19. 19 //**                    main code
  20. 20 //*****************************************************
  21. 21 //捕获led_en上升沿,得到一个时钟周期的脉冲信号
  22. 22 assign led_en_flag = (~led_en_d1) & led_en_d0;
  23. 23
  24. 24 always @(posedge sys_clk or negedge sys_rst_n) begin
  25. 25     if (!sys_rst_n) begin
  26. 26         led_en_d0 <= 1'b0;
  27. 27         led_en_d1 <= 1'b0;
  28. 28     end
  29. 29     else begin
  30. 30         led_en_d0 <= led_en;
  31. 31         led_en_d1 <= led_en_d0;
  32. 32     end
  33. 33 end
  34. 34
  35. 35 always @(posedge sys_clk or negedge sys_rst_n) begin
  36. 36     if (!sys_rst_n)
  37. 37         led <= 4'b0000;
  38. 38     else if(led_en_flag)               //在led_en上升沿到来时,改变led灯的状态
  39. 39             led <= ~led_data;          //按键按下时为低电平,而led高电平时点亮
  40. 40         else
  41. 41             led <= led;
  42. 42 end
  43. 43     
  44. 44 endmodule
复制代码

由于超越者开发板上的按键在按下时为低电平,而LED为高电平时点亮,因此为了实现按键按下时点亮对应LED灯的功能,需要将按键数据取反后赋值给LED输出端口寄存器,如代码中第39行所示。
20.5下载验证
将两个超越者开发板上的RS485接口用两根杜邦线连接起来,如图 20.5.1所示。连接时注意接口位置一一对应,不要接反了。另外超越者开发板上的CAN接口与RS485接口十分相像,使用时请注意区分。还有一点需要注意的是,两块开发板的P1口都需要使用杜邦线或者跳帽进行连接选择RS485口,否则无法进行RS485串口通信,然后分别将两个开发板上的下载器一端连电脑,另一端与开发板上的JTAG下载端口连接,最后连接电源线。
2011326.png

图 20.5.1 超越者RS485接口、跳帽位置

打开电源开关,接下来我们下载程序,验证两个超越者开发板上的按键通过RS-485通信端口控制对方LED灯亮灭的功能。工程打开后双击“Configure Target Device”一栏中的“Manage Configuration Project(iMAPCT)” (上图红框位置),在弹出的界面中双击“Boundary Scan”,下载界面如图 20.5.2所示。
2011611.png

图 20.5.2 程序下载界面

开发板电源打开后,点击工具栏中的“Initialize chain”图标(图 20.5.3红框位置),添加工程目录下的“rs485_uart_top.bit”文件。然后双击“Program” 将工程编译完成后得到的bit文件下载到第一块开发板中,如图 20.5.4所示。然后将下载器连到第二块开发板,将“rs485_uart_top. bit文件下载到第二块开发板中。
2011905.png

图 20.5.3 硬件连接

2011964.png

图 20.5.4 程序下载完成界面

下载完成后我们依次按下任意一个开发板上的四个按键,可以观察到另外一个开发板上对应的LED灯在按下时点亮,释放时熄灭,说明程序下载验证成功。
就能在开发板上看到RS485串口通信实验的效果如下图所示。
2012127.png

图 20.5.5 RS485串口通信实验效果图


阿莫论坛20周年了!感谢大家的支持与爱护!!

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-27 11:02

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表