RS485实验
介绍
RS485采用差分信号进行传输,半双工通信。RS485是一个总线,在同一总线上最多可以挂接32个节点。通信流程简单理解为默认为接收状态,发送数据时切换为发送状态,数据发送完毕后切换为接收状态。发送和接收分别由一个引脚控制,且使能信号相反,可以由一个gpio去控制。下图是一个RS485的芯片,和芯片连接的一端有4个管腿儿,对外只有两条是差分线的。这里注意一下同时发送和接收,发送和接收可以同时使能接收的,发送的数据不仅会对外发,同时也接到了内部的接收器上,因此收发同时使能的话,在没有其他设备发送数据时,发送数据的时候会收到自己发送的数据。
从硬件设计上,可以看到还是有可能会有两个设备同时使用总线发送数据的情况,完成半双工通信的话还需要软件上去实现同步。百度了一些资料,发现没有统一的说法,但是看到了一个CSMA/CD(波侦听多路访问/冲突检测),以太网用的是这种方式。大致意思是发送的时候去监听,如果接收到的数据和发送到的数据不一致,说明和其他设备争用了总线,就随机等待一段时间后再发送。
FPGA代码分析
接收和发送数据还是基于之前的串口模块,RS485多了方向的控制。例子设计了一个握手协议,1起始字节(0x55)+1长度字节+有效数据,fpga收到后将数据返回回来,如果可以收到发送的数据表示握手成功。数据需要缓存,所以需要使用一个fifo来实现,接收到的有效数据放入到FIFO里面,之后将FIFO中的数据发送出去。
状态机设计如下:
case(state)IDLE: /* 空闲状态,进行一些初始化操作 */beginstate <= RCV_HEAD;rs485_de <= 1'b0 ;rx_data_ready <= 1'b1 ;endRCV_HEAD: /* 起始字节接收状态,等待起始字节的到来 */beginif (rx_data_valid == 1'b1 && rx_data == 8'h55) //when received data is valid and data is 8'h55,it means the start of datastate <= RCV_COUNT ; endRCV_COUNT: /* 数据长度接收状态,接收有效的数据长度,保存到一个计数器中 */beginif (rx_data_valid == 1'b1) //record data counterbeginif (rx_data > 0) //if data counter bigger than 0, then goto receive data state or goto IDLE statestate <= RCV_DATA ; elsestate <= IDLE ;data_count <= rx_data ;endendRCV_DATA: /* 数据接收状态,把数据放到fifo里面,这一要看下fifo的时序 */beginfifo_wren <= rx_data_valid ;fifo_wdata <= rx_data ;if (rx_data_valid == 1'b1)beginif (rx_cnt == data_count - 1) //the last received databeginrx_cnt <= 8'd0 ;rs485_de <= 1'b1 ;state <= WAIT ;endelserx_cnt <= rx_cnt + 1'b1 ;endendWAIT: /* 等待状态,延时1ms */begin fifo_wren <= 1'b0 ;if (wait_cnt >= CLK_FRE * 1000) // wait for 1 ms for direction changebeginwait_cnt <= 32'd0 ;state <= SEND_WAIT;endelsebeginwait_cnt <= wait_cnt + 32'd1;endendSEND_WAIT: /* 数据发送等待状态,读取fifo数据 */begin if (tx_data_ready == 1'b1)beginif (tx_cnt == data_count) //the last data has transferredbegintx_cnt <= 8'd0 ; fifo_rden <= 1'b0 ;state <= IDLE ;endelsebeginfifo_rden <= 1'b1 ; //read data from fifostate <= SEND ;endendtx_data_valid <= 1'b0 ;endSEND: /* 数据发送状态,发送数据 */beginfifo_rden <= 1'b0 ;tx_data_valid <= 1'b1 ;if(tx_data_valid == 1'b1 && tx_data_ready == 1'b1 && tx_cnt < data_count)begintx_cnt <= tx_cnt + 8'd1; //Send data counterstate <= SEND_WAIT ;end enddefault:state <= IDLE;endcase
上板测试
没有485串口,不测试。
参考
- 原来RS-485这么简单?
- 图文详解RS-485,真的很详细了
- RS485能否从机向主机发出请求?冲突如何解决?