OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 5933|回复: 0
打印 上一主题 下一主题

[XILINX] 【正点原子FPGA连载】第十五章 RS485串口通信实验--摘自【正点原子】领航者ZYNQ之FPGA开发指南_V1.2

[复制链接]

1212

主题

1226

帖子

2

精华

超级版主

Rank: 8Rank: 8

积分
5221
金钱
5221
注册时间
2019-5-8
在线时间
1316 小时
跳转到指定楼层
楼主
发表于 2020-6-22 11:57:02 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 正点原子01 于 2020-6-30 11:18 编辑

1)实验平台:正点原子领航者ZYNQ开发板
2)平台购买地址:https://item.taobao.com/item.htm?&id=606160108761
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-301505-1-1.html
4)本章实例源码下载: RS485串口通信实验.zip (3.64 MB, 下载次数: 32)
5)对正点原子FPGA感兴趣的同学可以加群讨论:712557122
6)关注正点原子公众号,获取最新资料更新



第十五章RS485串口通信实验

    RS-485是针对UART串口的一种接口标准,它定义了串行通信系统中发送器和接收器的一系列电气特性。相比于RS-232,RS-485标准的通信系统抗干扰能力较强,可实现长距离数据传输,同时支持多个收发器连接到同一个通信网络中。因此,RS-485在工业控制领域以及有类似需求的系统中得到了广泛的应用。
本章包括以下几个部分:
1.1    RS-485简介
1.2    实验任务
1.3    硬件设计
1.4    程序设计
1.5    下载验证
1.1 RS-485简介
    在“串口通信实验”章节我们详细地介绍了UART串口通信以及RS-232接口标准。实际上,除了RS-232之外,RS-422和RS-485也都是常用的串行通信接口标准,它们定义了接口不同的电气特性,如RS-232是单端输入输出,而RS-422/485为差分输入输出等。
在介绍RS-485之前,我们先来了解一下串口通信过程中单端传输与差分传输的差别。单端传输是指在发送或接收过程中,用信号线对地线的电压值来表示逻辑“0”和“1”。而差分传输使用两根信号线来传输一路信号,这两根信号线上传输的信号幅值相等,相位相差180度(极性相反),用它们的差值来表示逻辑“0”和“1”,如下图所示。
15.1.1差分传输方式
    在传输过程中,当信号线上叠加了频率、幅值和相位都相同的干扰信号时(共模干扰),对于单端传输而言,由于地线电位为0,则传输的信号就包含了干扰信号;而在差分传输方式下,干扰可以通过两个信号线上电压的差值抵消,相当于抑制了共模干扰,如下图所示。因此相对于单端传输方式,差分传输大大提高了信号在传输过程中的抗干扰能力,但是需要多余的信号线来实现信号传输。
15.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增加了多点、双向通信能力,即允许多个发送器连接到同一条总线上,各设备通过使能信号控制发送和接收过程。
1.2 实验任务
    本节实验任务是使用两块领航者开发板通过RS-485端口互联,由各自开发板上的两个按键分别控制对方开发板上两个LED灯的亮灭。当按键按下时,对方开发板上对应的LED灯点亮;按键释放时,对应的LED灯熄灭。
1.3 硬件设计
    RS485串口部分的原理图如下图所示。由于ZYNQ PL侧串口输入输出引脚为TTL电平,用3.3V代表逻辑“1”,0V代表逻辑“0”;而RS-485电平标准采用差分信号的差值电压来代表逻辑“0”和“1”。因此当FPGA与RS485接口标准的设备通信时,需要加电平转换芯片SP3485,实现RS485电平与TTL电平的转换。
15.3.1 RS485串口原理图
    由于RS-485为半双工通信方式,需要通过使能信号来控制发送和接收过程。在下图中,电平转换芯片SP3485的2号引脚为低电平接收使能,3号引脚为高电平发送使能。在这里我们将两个引脚连接在一起,只需要通过一个信号RS485_DE即可控制收发过程:当RS485_DE为高电平时,SP3485处于发送过程;当RS485_DE为低电平时,SP3485处于接收过程。
15.3.2 RS232/RS485选择接口
    下图为RS232/RS485的选择接口,由上图可知,SP3485芯片端口的RS485_RX和RS485_TX并没有直接和ZYNQ的引脚相连接,而是连接到开发板的P1口,RS232串口和RS485串口共用P1口的UART2_TX和UART2_RX,UART2_TX和UART2_RX是直接和FPGA的引脚相连接的,这样的设计方式实现了有限IO的多种复用功能。因此,在做RS485串口通信实验时,需要使用杜邦线或者跳帽将RS485_RX和UART2_TX连接在一起,RS485_TX和UART2_RX连接在一起。
    除此之外,领航者开发板上还包括了RS485收发方向自动控制电路,如下图所示:
15.3.3RS485收发方向自动控制电路
    其中,“RS485_RX”网络由ZYNQ输出的UART2_TX驱动。当UART2_TX为高,即不发送时,三极管导通,“RD485_DE”被拉低,此时SP3485芯片工作在接收状态,RS485差分总线的电平被外部电阻强制拉高,达到了输出高电平的的状态。当UART2_TX为低,即开始发送时,三极管截止,“RD485_DE”被拉高,此时SP3485芯片工作在发送状态,RS485差分总线的电平由UART2_TX来驱动。这样就实现了RS485收发状态的自动控制。
    本实验中,各端口信号的管脚分配如下表所示:
表15.3.1 RS485串口通信实验管脚分配
   对应的约束语句如下所示:
  1. create_clock -period 20.000 -name sys_clk  [get_ports sys_clk]
  2. set_property -dict {PACKAGE_PIN U18 IOSTANDARDLVCMOS33} [get_ports sys_clk]
  3. set_property -dict {PACKAGE_PIN J15 IOSTANDARDLVCMOS33} [get_ports sys_rst_n]
  4. set_property -dict {PACKAGE_PIN L20 IOSTANDARDLVCMOS33} [get_ports {key[0]}]
  5. set_property -dict {PACKAGE_PIN J20 IOSTANDARDLVCMOS33} [get_ports {key[1]}]
  6. set_property -dict {PACKAGE_PIN J18 IOSTANDARDLVCMOS33} [get_ports {led[0]}]
  7. set_property -dict {PACKAGE_PIN H18 IOSTANDARDLVCMOS33} [get_ports {led[1]}]
  8. set_property -dict {PACKAGE_PIN J14 IOSTANDARDLVCMOS33} [get_ports rs485_uart_rxd]
  9. set_property -dict {PACKAGE_PIN K18 IOSTANDARDLVCMOS33} [get_ports rs485_uart_txd
复制代码
1.4 程序设计
    根据实验任务,我们可以大致规划出系统的控制流程:当检测到有按键按下或释放时,将按键数据通过RS485串口发送出去;而当RS485串口接收到对方发送的按键数据时,根据接收到的数据改变LED灯的显示状态。由此画出系统的功能框图如下所示:
15.4.1RS485串口实验系统框图
    由系统总体框图可知,FPGA部分包括五个模块,顶层模块(rs485_uart_top)、接收模块(uart_recv)、发送模块(uart_send)、按键消抖模块(key_debounce)和LED灯控制模块(led_ctrl)。其中在顶层模块中完成对另外四个模块的例化。
    由于RS-485只是对接口标准的定义,数据的传输仍然是按照UART串口通信协议进行。因此我们可以直接调用“串口通信实验”中的串口发送和接收模块。在这里我们仍然设置数据位为8位,停止位为1位,无校验位,波特率为115200bps。
各模块端口及信号连接如下图所示:
15.4.2顶层模块原理图
    key_debounce为按键消抖模块,在检测到有按键按下或释放时对按键数据进行消抖处理,在按键数据稳定后给出通知信号key_flag,并将数据由串口发送模块uart_send发送出去。uart_recv为串口接收模块,它负责接收对方发送的按键数据,并在一帧数据(8位)接收结束后给出通知信号uart_done。当LED灯控制模块led_ctrl检测到该通知信号时,根据接收到的按键数据改变板卡上LED灯的显示状态。
    顶层模块的代码如下:
  1. module rs485_uart_top(
  2. input           sys_clk,//外部50M时钟
  3. input           sys_rst_n,//外部复位信号,低有效

  4. input[1:0    key,//按键
  5. output[1:0    led,//led灯

  6. //uart接口
  7. input           rs485_uart_rxd,//rs485串口接收端口
  8. output          rs485_uart_txd     //rs485串口发送端口
  9. );

  10. //parameter define
  11. parameter  CLK_FREQ =50000000;//定义系统时钟频率
  12. parameter  UART_BPS =115200;//定义串口波特率

  13. //wire define   
  14. wire       tx_en_w;//UART发送使能
  15. wire       rx_done_w;//UART接收完毕信号
  16. wire[7:0 tx_data_w;//UART发送数据
  17. wire[7:0 rx_data_w;//UART接收数据
  18. wire[1:0 key_value_w;//消抖后的按键数据

  19. //*****************************************************
  20. //**                   main code
  21. //*****************************************************   
  22. assign tx_data_w ={6'd0,key_value_w};//将按键消抖后的值送到发送模块

  23.   uart_recv #(//串口接收模块
  24. .CLK_FREQ       (CLK_FREQ),//设置系统时钟频率
  25. .UART_BPS       (UART_BPS))//设置串口接收波特率
  26.   u_uart_recv(
  27. .sys_clk        (sys_clk),
  28. .sys_rst_n      (sys_rst_n),

  29. .uart_rxd       (rs485_uart_rxd),
  30. .uart_done      (rx_done_w),
  31. .uart_data      (rx_data_w)
  32. );

  33.   uart_send #(//串口发送模块
  34. .CLK_FREQ       (CLK_FREQ),//设置系统时钟频率
  35. .UART_BPS       (UART_BPS))//设置串口发送波特率
  36.   u_uart_send(
  37. .sys_clk        (sys_clk),
  38. .sys_rst_n      (sys_rst_n),

  39. .uart_en        (tx_en_w),
  40. .uart_din       (tx_data_w),
  41. .uart_txd       (rs485_uart_txd)
  42. );

  43.   key_debounce u_key_debounce(
  44. .sys_clk        (sys_clk),
  45. .sys_rst_n      (sys_rst_n),

  46. .key            (key),
  47. .key_flag       (tx_en_w),//按键有效通知信号
  48. .key_value      (key_value_w)//按键消抖后的数据
  49. );

  50.   led_ctrl u_led_ctrl(
  51. .sys_clk        (sys_clk),
  52. .sys_rst_n      (sys_rst_n),

  53. .led_en         (rx_done_w),//led控制使能
  54. .led_data       (rx_data_w[1:0]),//led控制数据
  55. .led            (led)
  56. );

  57. endmodule
复制代码
    顶层模块中主要完成对其余模块的例化,需要注意的是程序第27行:由于板卡上只有2个按键,而串口通信过程中数据位为8位,因此需要将消抖后得到的2按键位数据高位补6个零,然后再给到串口发送模块。同样,在将接收的按键数据用于LED灯控制时,仅将低2位有效位赋值给LED灯控制模块,如第67行所示。
    串口接收程序和串口发送程序与“串口通信实验”章节中的代码完全相同。
有关串口收发过程更详细的介绍请大家参考“串口通信实验”,下面我们来介绍一下另外两个模块:按键消抖模块和LED灯控制模块。
在机械按键按下和释放的过程中,由于机械触点的弹性作用,按键开关在闭合的瞬间不会立即稳定地导通,在释放时也不是立刻就能完全断开。因此,在闭合及断开的瞬间均伴随有一连串的抖动,如下图所示。按键的抖动过程体现在数字电路中就是不断变化的高低电平,为避免在抖动过程中采集到错误的按键状态,我们需要对按键数据进行消除抖动处理。
15.4.3机械按键抖动过程
    按键抖动的时间长短由按键的机械特性决定,一般为5ms~10ms,在抖动时间内按键状态可能会不断的发生变化。由于按键的抖动过程持续时间较短,很快就趋于稳定状态。因此在按键按下及释放之后,若按键能稳定在同一状态且持续时间达20ms,我们就认为抖动过程已经结束,此时的采集的按键数据有效。
    按键消抖模块的代码如下所示:
  1. module key_debounce(
  2. input            sys_clk,//外部50M时钟
  3. input            sys_rst_n,//外部复位信号,低有效

  4. input[1:0 key,//外部按键输入

  5. outputreg       key_flag,//按键数据有效信号
  6. outputreg[1:0 key_value         //按键消抖后的数据
  7. );

  8. //reg define   
  9. reg[31:0 delay_cnt;
  10. reg[1:0 key_reg;

  11. //*****************************************************
  12. //**                    main code
  13. //*****************************************************
  14. always@(posedge sys_clk ornegedge sys_rst_n)begin
  15. if(!sys_rst_n)begin
  16.           key_reg   <=2'b11;
  17.           delay_cnt <=32'd0;
  18. end
  19. elsebegin
  20.           key_reg <=key;
  21. if(key_reg != key)//一旦检测到按键状态发生变化(有按键被按下或释放)
  22.               delay_cnt <=32'd1000000;//给延时计数器重新装载初始值(计数时间为20ms)
  23. elseif(key_reg == key)begin//在按键状态稳定时,计数器递减,开始20ms倒计时
  24. if(delay_cnt >32'd0)
  25.                        delay_cnt <= delay_cnt -1'b1;
  26. else
  27.                        delay_cnt <= delay_cnt;
  28. end
  29. end
  30. end

  31. always@(posedge sys_clk ornegedge sys_rst_n)begin
  32. if(!sys_rst_n)begin
  33.           key_flag  <=1'b0;
  34.           key_value <=2'b11;
  35. end
  36. elsebegin
  37. if(delay_cnt ==32'd1)begin//当计数器递减到1时,说明按键稳定状态维持了20ms
  38.               key_flag  <=1'b1;//此时消抖过程结束,给出一个时钟周期的标志信号
  39.               key_value <= key;//并寄存此时按键的值
  40. end
  41. elsebegin
  42.               key_flag  <=1'b0;
  43.               key_value <= key_value;
  44. end
  45. end
  46. end

  47. 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. module led_ctrl(
  2. input            sys_clk,//外部50M时钟
  3. input            sys_rst_n,//外部复位信号,低有效

  4. input            led_en,//led控制使能
  5. input[1:0 led_data,//led控制数据

  6. outputreg[1:0 led               //led灯
  7. );

  8. //reg define
  9. reg led_en_d0;
  10. reg led_en_d1;

  11. //wire define
  12. wire led_en_flag;

  13. //*****************************************************
  14. //**                   main code
  15. //*****************************************************
  16. //捕获led_en上升沿,得到一个时钟周期的脉冲信号
  17. assign led_en_flag =(~led_en_d1)&led_en_d0;

  18. always@(posedge sys_clk ornegedge sys_rst_n)begin
  19. if(!sys_rst_n)begin
  20.           led_en_d0 <=1'b0;
  21.           led_en_d1 <=1'b0;
  22. end
  23. elsebegin
  24.           led_en_d0 <=led_en;
  25.           led_en_d1 <=led_en_d0;
  26. end
  27. end

  28. always@(posedge sys_clk ornegedge sys_rst_n)begin
  29. if(!sys_rst_n)
  30.           led <=2'b00;
  31. elseif(led_en_flag)//在led_en上升沿到来时,改变led灯的状态
  32.               led <=~led_data;//按键按下时为低电平,而led高电平时点亮
  33. else
  34.               led <=led;
  35. end

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

15.5.1领航者开发板实物图
    接下来我们下载程序,验证两个领航者开发板上的按键通过RS-485通信端口控制对方LED灯亮灭的功能。我们首先将生成的比特流文件固化到一个领航者开发板上,具体固化过程参见“程序固化实验”实验。固化完成之后断电,将JTAG下载器电缆拔出,插到另一个领航者板子上,然后将比特流文件下载到另一个领航者板子上。
    下载完成后我们给第一个固化程序的领航者开发板上电,依次按下任意一个开发板上的两个按键,可以观察到另外一个开发板上对应的LED灯在按下时点亮,释放时熄灭,说明程序下载验证成功。







回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则


关闭

原子哥极力推荐上一条 /1 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-12-14 17:17

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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