1. FPGA 实现 RS422/485 串口通信概述
在工业控制和通信系统中,RS422/485 因其差分信号传输特性,相比传统 RS232 具有更强的抗干扰能力和更远的传输距离。FPGA 作为可编程逻辑器件,能够灵活实现各种通信协议,特别适合需要定制化时序和接口的场景。
RS422 和 RS485 的主要区别在于:
- RS422 是全双工通信,需要两对差分线
- RS485 是半双工通信,只需一对差分线
两者都采用差分信号传输,最大传输距离可达 1200 米(取决于波特率)
2. 通信协议设计要点
2.1 帧格式设计
典型的串口通信帧格式包括:
- 起始位(1位,低电平)
- 数据位(5-8位)
- 校验位(可选,1位)
- 停止位(1-2位,高电平)
在 VHDL 实现中,我们需要严格遵循这个时序规范。以常见的 8N1 格式(8位数据,无校验,1位停止位)为例,完整的帧包含:
- 1位起始位(0)
- 8位数据(LSB first)
- 1位停止位(1)
2.2 波特率生成
波特率时钟是串口通信的核心。假设系统时钟为 50MHz,要实现 115200bps 的波特率:
计算分频系数:
分频系数 = 系统时钟频率 / (波特率 × 过采样率)
= 50,000,000 / (115200 × 16) ≈ 27
因此需要设计一个分频计数器,每计数27个系统时钟周期产生一个波特率时钟周期。
3. VHDL 发送模块实现
3.1 发送状态机设计
发送模块应采用状态机实现,典型状态包括:
- IDLE:等待发送使能
- START:发送起始位
- DATA:发送数据位
- STOP:发送停止位
vhdl复制type tx_state_type is (IDLE, START_BIT, DATA_BITS, STOP_BIT);
signal tx_state : tx_state_type := IDLE;
3.2 发送移位寄存器
使用移位寄存器实现数据的串行化:
vhdl复制process(clk)
begin
if rising_edge(clk) then
case tx_state is
when IDLE =>
if tx_enable = '1' then
tx_state <= START_BIT;
shift_reg <= '1' & tx_data & '0'; -- 停止位+数据+起始位
bit_count <= 0;
end if;
when START_BIT =>
tx_serial <= shift_reg(0);
tx_state <= DATA_BITS;
when DATA_BITS =>
if bit_count < 8 then
tx_serial <= shift_reg(bit_count+1);
bit_count <= bit_count + 1;
else
tx_state <= STOP_BIT;
end if;
when STOP_BIT =>
tx_serial <= '1';
tx_done <= '1';
tx_state <= IDLE;
end case;
end if;
end process;
3.3 发送模块优化
为提高稳定性,可添加以下特性:
- 发送超时保护
- 发送缓冲区空标志
- 多字节FIFO接口
4. VHDL 接收模块实现
4.1 接收状态机设计
接收模块同样采用状态机实现:
- IDLE:等待起始位
- START:确认起始位
- DATA:接收数据位
- STOP:验证停止位
vhdl复制type rx_state_type is (IDLE, START_BIT, DATA_BITS, STOP_BIT);
signal rx_state : rx_state_type := IDLE;
4.2 过采样技术
采用16倍过采样提高抗干扰能力:
vhdl复制process(clk)
begin
if rising_edge(clk) then
case rx_state is
when IDLE =>
if rx_serial = '0' then -- 检测起始位
sample_count <= 7; -- 在中间点采样
bit_count <= 0;
rx_state <= START_BIT;
end if;
when START_BIT =>
if sample_count = 0 then
if rx_serial = '0' then -- 确认起始位
rx_state <= DATA_BITS;
sample_count <= 15; -- 重置采样计数器
else
rx_state <= IDLE; -- 假起始位
end if;
else
sample_count <= sample_count - 1;
end if;
when DATA_BITS =>
if sample_count = 0 then
shift_reg(bit_count) <= rx_serial;
bit_count <= bit_count + 1;
sample_count <= 15;
if bit_count = 7 then
rx_state <= STOP_BIT;
end if;
else
sample_count <= sample_count - 1;
end if;
when STOP_BIT =>
if sample_count = 0 then
if rx_serial = '1' then -- 验证停止位
rx_data <= shift_reg;
rx_done <= '1';
end if;
rx_state <= IDLE;
else
sample_count <= sample_count - 1;
end if;
end case;
end if;
end process;
4.3 接收模块优化
- 添加帧错误检测
- 实现波特率自动检测
- 增加接收缓冲区
5. RS422/485 接口设计
5.1 电平转换电路
FPGA 需要外接电平转换芯片实现 TTL 到 RS422/485 的转换:
常用芯片选择:
- RS422:MAX488/MAX490
- RS485:MAX485/MAX487
连接方式:
code复制FPGA TX ----| |---- RS422/485 A+
FPGA RX ----| 电平转换 |---- RS422/485 B-
| |---- RS422/485 Y+ (仅RS422)
| |---- RS422/485 Z- (仅RS422)
5.2 终端电阻配置
长距离传输时需要配置终端电阻:
- RS422:在接收端接100Ω电阻
- RS485:在总线两端各接120Ω电阻
6. 仿真验证
6.1 测试平台搭建
vhdl复制entity uart_tb is
end uart_tb;
architecture behavioral of uart_tb is
component uart_top
port(
clk : in std_logic;
reset : in std_logic;
tx_data : in std_logic_vector(7 downto 0);
tx_en : in std_logic;
rx : in std_logic;
tx : out std_logic;
rx_data : out std_logic_vector(7 downto 0);
rx_done : out std_logic;
tx_done : out std_logic
);
end component;
-- 测试信号声明
signal clk : std_logic := '0';
signal reset : std_logic := '1';
-- 其他信号...
constant CLK_PERIOD : time := 20 ns; -- 50MHz
constant BIT_PERIOD : time := 8.68 us; -- 115200bps
begin
-- 实例化UUT
uut: uart_top port map(
clk => clk,
reset => reset,
-- 其他连接...
);
-- 时钟生成
clk_process: process
begin
clk <= '0';
wait for CLK_PERIOD/2;
clk <= '1';
wait for CLK_PERIOD/2;
end process;
-- 测试激励
stim_proc: process
begin
-- 复位
reset <= '1';
wait for 100 ns;
reset <= '0';
wait for 100 ns;
-- 测试发送
tx_data <= x"55"; -- 01010101
tx_en <= '1';
wait for CLK_PERIOD;
tx_en <= '0';
-- 等待发送完成
wait until tx_done = '1';
wait for 1 us;
-- 测试回环接收
rx <= '0'; -- 起始位
wait for BIT_PERIOD;
-- 发送数据位
for i in 0 to 7 loop
rx <= tx_data(i);
wait for BIT_PERIOD;
end loop;
-- 停止位
rx <= '1';
wait for BIT_PERIOD;
-- 验证接收数据
assert rx_data = x"55" report "接收数据错误" severity error;
wait;
end process;
end behavioral;
6.2 仿真结果分析
在ModelSim中应检查:
- 发送时序是否符合预期
- 接收数据是否正确
- 波特率误差是否在允许范围内
- 起始位和停止位检测是否可靠
7. 上板调试技巧
7.1 常见问题排查
-
无通信:
- 检查电平转换芯片供电
- 验证FPGA引脚分配
- 测量差分信号波形
-
数据错误:
- 确认波特率设置
- 检查终端电阻
- 调整采样点位置
-
通信不稳定:
- 缩短传输距离测试
- 添加磁珠滤波
- 检查地线连接
7.2 调试工具推荐
- 逻辑分析仪:抓取FPGA内部信号
- 示波器:观察差分信号质量
- 串口调试助手:验证数据传输
8. 性能优化建议
- 添加FIFO缓冲提高吞吐量
- 实现硬件流控(RTS/CTS)
- 支持可变波特率
- 添加错误检测和重传机制
实际项目中,我曾遇到一个案例:在工业环境中,RS485通信受到变频器干扰。通过以下措施解决了问题:
- 将波特率从115200降至57600
- 在总线上增加TVS二极管
- 在FPGA代码中添加错误计数和自动重试机制
- 使用双绞屏蔽电缆并单端接地
这些经验表明,可靠的串口通信不仅取决于代码实现,还需要综合考虑硬件设计和环境因素。