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