FPGA串口(rs422)调试笔记

本文记录了作者在项目中从理解串口概念、通过UART实现数据收发,到拼接数据并替换USB程序,最终成功通过串口进行数据传输的全过程。过程中遇到的困惑和解决方案对新手很有帮助。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

前言

一、串口到底指的什么?

二、调试记录

1.先通过串口助手实现uart的发送与接收

2.编写top文件,把连续接收的8bit的数据拼接成16bit数据(之后需要的位数)

3.删去原USB程序,把串口程序拼上去

总结


前言

团队项目需求,由于USB通信存在数据被测试环境干扰的问题,且考虑到所需通信速率不高,所以转为串口通信,下面是自己的调试记录,方便自己理解与日后回顾。

一、串口到底指的什么?

了解之前,我也很疑惑,听到大家都在说串口啦,uart啦,rs232啦,rs422啦,rs485啦,说的我一脸懵逼。后来自己了解后,才发现其实类别都不同,不能放在一起说。

串口包含了uart,还包含usb等等,可以说是uart是串口,但串口不一定是uart,我是这样理解的,不知道对不对。串口是物理层次上说的,一般有两种形式的物理接口,DB9和4针的杜邦线插口。

uart包含了rs232,rs422和rs485这些接口标准。可以说这三种接口的物理层不同,输出的电平不同,但通信协议是一样的,也就是说,编程实现是一样的,只是硬件电路电路上不同,当然其传输速率,抗干扰能力等等也自不相同。

二、调试记录

1.先通过串口助手实现uart的发送与接收

从Xilinx嫖的uart接收与发送的例程,改改引脚定义:团队做的pcb用芯片把rs422电平转化为FPGA芯片识别的ttl电平,所以要做的就是得输进去一个rs422电平。

懵逼的我拿了个一头是usb另一头是DB9的线插上去,咦~怎么不管用啊。后来才发现这根线是usb转rs232的线。。。。。我拿了个rs232转rs422的转换器,接上后,咦~~~管用了。

后来加购了个usb直接转rs422电平的线,就可以了。(嘘,我原本以为,usb连接电脑,那边出来的只能是rs232的电平,之后回来再看的时候,不准笑话自己,哈哈哈)

代码如下(by Xilinx):

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
//Module name uart_rx
//Description: 16 个 clock 接收一个 bit,16 个时钟采样,取中间的采样值
//(by_Xilinx)
//////////////////////////////////////////////////////////////////////////////////
module uart_rx(
    input                    clk, //采样时钟
    input                    rst_n, //复位信号
    input                    rx, //UART 数据输入
    output   reg[7:0]    dataout, //接收数据输出
    output    reg        rdsig, 
    output    reg        dataerror, //数据出错指示
    output    reg        frameerror//帧出错指示
);


reg [7:0] cnt;
reg          rxbuf, rxfall, receive;
parameter paritymode = 1'b0;
reg         presult;
reg          idle;//线路状态指示,高为线路忙,低为线路空闲
always @(posedge clk) //检测线路的下降沿
    begin
        rxbuf <= rx;
        rxfall <= rxbuf & (~rx);
    end
////////////////////////////////////////////////////////////////
//启动串口接收程序
////////////////////////////////////////////////////////////////
always @(posedge clk)
    begin
        if (rxfall && (~idle)) begin//检测到线路的下降沿并且原先线路为空闲,启动接收数据进程
            receive <= 1'b1;
        end
        else if(cnt == 8'd168) begin //接收数据完成
            receive <= 1'b0;
        end
    end
////////////////////////////////////////////////////////////////
//串口接收程序, 16 个时钟接收一个 bit
////////////////////////////////////////////////////////////////
always @(posedge clk or negedge rst_n)
    begin
        if (!rst_n) begin
            idle<=1'b0;
            cnt<=8'd0;
            rdsig <= 1'b0;
            frameerror <= 1'b0; 
            dataerror <= 1'b0; 
            presult<=1'b0;
        end 
        else if(receive == 1'b1) begin
        case (cnt)
            8'd0:begin
                idle <= 1'b1;
                cnt <= cnt + 8'd1;
                rdsig <= 1'b0;
            end
            8'd24:begin //接收第 0 位数据
                idle <= 1'b1;
                dataout[0] <= rx;
                presult <= paritymode^rx;
                cnt <= cnt + 8'd1;
                rdsig <= 1'b0;
            end
            8'd40:begin //接收第 1 位数据 
                idle <= 1'b1;
                dataout[1] <= rx;
                presult <= presult^rx;
                cnt <= cnt + 8'd1;
                rdsig <= 1'b0;
            end
            8'd56:begin //接收第 2 位数据 
                idle <= 1'b1;
                dataout[2] <= rx;
                presult <= presult^rx;
                cnt <= cnt + 8'd1;
                rdsig <= 1'b0;
            end
            8'd72:begin //接收第 3 位数据 
                idle <= 1'b1;
                dataout[3] <= rx;
                presult <= presult^rx;
                cnt <= cnt + 8'd1;
                rdsig <= 1'b0;
            end
            8'd88:begin //接收第 4 位数据 
                idle <= 1'b1;
                dataout[4] <= rx;
                presult <= presult^rx;
                cnt <= cnt + 8'd1;
                rdsig <= 1'b0;
            end
            8'd104:begin //接收第 5 位数据 
                idle <= 1'b1;
                dataout[5] <= rx;
                presult <= presult^rx;
                cnt <= cnt + 8'd1;
                rdsig <= 1'b0;
            end
            8'd120:begin //接收第 6 位数据 
                idle <= 1'b1;
                dataout[6] <= rx;
                presult <= presult^rx;
                cnt <= cnt + 8'd1;
                rdsig <= 1'b0;
            end
            8'd136:begin //接收第 7 位数据 
                idle <= 1'b1;
                dataout[7] <= rx;
                presult <= presult^rx;
                cnt <= cnt + 8'd1;
                rdsig <= 1'b1;
            end
            8'd152:begin //接收奇偶校验位 
                idle <= 1'b1;
            if(presult == rx)
                dataerror <= 1'b0;
            else
                dataerror <= 1'b1; //如果奇偶校验位不对,表示数据出错
                cnt <= cnt + 8'd1;
                rdsig <= 1'b1;
            end
            8'd168:begin
                idle <= 1'b1;
            if(1'b1 == rx)
                frameerror <= 1'b0;
            else
                frameerror <= 1'b1; //如果没有接收到停止位,表示帧出错
                cnt <= cnt + 8'd1;
                rdsig <= 1'b1;
            end
            default: begin
                cnt <= cnt + 8'd1;
            end
        endcase
    end
            else begin
                cnt <= 8'd0;
                idle <= 1'b0;
                rdsig <= 1'b0;
            end
        end
endmodule
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Module Name: uart_tx 
// 说明:16 个 clock 发送一个 bit, 一个起始位,8 个数据位,一个校验位,一个停止位
//////////////////////////////////////////////////////////////////////////////////
module uart_tx(
		input		clk, 		//UART 时钟
		input		rst_n, 	//系统复位
		input	   [7:0]  datain, 	//需要发送的数据
		input		wrsig,	//发送命令,上升沿有效
		output reg	idle, 	//线路状态指示,高为线路忙,低为线路空闲
		output reg 	tx			//发送数据信号
	);
		

		reg 	send;
		reg 	wrsigbuf, wrsigrise;
		reg 	presult;
		reg[7:0] cnt; //计数器
		
parameter paritymode = 1'b0;
////////////////////////////////////////////////////////////////
//检测发送命令 wrsig 的上升沿
////////////////////////////////////////////////////////////////
always @(posedge clk)
	begin
		wrsigbuf <= wrsig;
		wrsigrise <= (~wrsigbuf) & wrsig; 
	end
////////////////////////////////////////////////////////////////
//启动串口发送程序
////////////////////////////////////////////////////////////////
always @(posedge clk)
	begin
	if (wrsigrise && (~idle)) //当发送命令有效且线路为空闲时,启动新的数据发送进程
		begin
			send <= 1'b1;
		end
	else if(cnt == 8'd168) //一帧数据发送结束
		begin
			send <= 1'b0;
		end
	end
////////////////////////////////////////////////////////////////
//串口发送程序, 16 个时钟发送一个 bit
////////////////////////////////////////////////////////////////
always @(posedge clk or negedge rst_n)
	begin
		if (!rst_n) begin
			tx <= 1'b0;
			idle <= 1'b0;
			cnt<=8'd0;
			presult<=1'b0;
		end
		else if(send == 1'b1) begin
	case(cnt) //产生起始位
		8'd0: begin
			tx <= 1'b0;
			idle <= 1'b1;
			cnt <= cnt + 8'd1;
		end
		8'd16: begin
			tx <= datain[0]; //发送数据 0 位
			presult <= datain[0]^paritymode;
			idle <= 1'b1;
			cnt <= cnt + 8'd1;
		end
		8'd32: begin
			tx <= datain[1]; //发送数据 1 位
			presult <= datain[1]^presult;
			idle <= 1'b1;
			cnt <= cnt + 8'd1;
		end
		8'd48: begin
			tx <= datain[2]; //发送数据 2 位
			presult <= datain[2]^presult;
			idle <= 1'b1;
			cnt <= cnt + 8'd1;
		end
		8'd64: begin
			tx <= datain[3]; //发送数据 3 位
			presult <= datain[3]^presult;
			idle <= 1'b1;
			cnt <= cnt + 8'd1;
		end
		8'd80: begin 
			tx <= datain[4]; //发送数据 4 位
			presult <= datain[4]^presult;
			idle <= 1'b1;
			cnt <= cnt + 8'd1;
		end
		8'd96: begin
			tx <= datain[5]; //发送数据 5 位
			presult <= datain[5]^presult;
			idle <= 1'b1;
			cnt <= cnt + 8'd1;
		end
		8'd112: begin
			tx <= datain[6]; //发送数据 6 位
			presult <= datain[6]^presult;
			idle <= 1'b1;
			cnt <= cnt + 8'd1;
		end
		8'd128: begin 
			tx <= datain[7]; //发送数据 7 位
			presult <= datain[7]^presult;
			idle <= 1'b1;
			cnt <= cnt + 8'd1;
		end
		8'd144: begin
			tx <= presult; //发送奇偶校验位
			presult <= datain[0]^paritymode;
			idle <= 1'b1;
			cnt <= cnt + 8'd1;
		end
		8'd160: begin
			tx <= 1'b1; //发送停止位 
			idle <= 1'b1;
			cnt <= cnt + 8'd1;
		end
		8'd168: begin
			tx <= 1'b1; 
			idle <= 1'b0; //一帧数据发送结束
			cnt <= cnt + 8'd1;
		end
		default: begin
			cnt <= cnt + 8'd1;
		end
	endcase
end
	else begin
		tx <= 1'b1;
		cnt <= 8'd0;
		idle <= 1'b0;
	end
end
endmodule

2.编写top文件,把连续接收的8bit的数据拼接成16bit数据(之后需要的位数)

光这个功能,搞了我两天,我也是崩了,有时候数据不变化,有时候错位(我就是个菜鸡)。

但好在自己看着Modelsim的仿真图,一点点改好了,接收到的十六位数据也正常了。这就是乐趣所在吧,在自己操作下,一点点实现它的价值,感觉就很棒!

3.删去原USB程序,把串口程序拼上去

先用Modelsim仿真,再用signaltap抓实时数据,最后用串口助手发送所需要的数据,成功被后续芯片接收,并测的有效果,表明已通过串口发送到目标芯片,并产生所需数据。

后续和团队软件人员沟通,作出一版通过串口发送的界面,再进行调试。


总结

第一次单独完成一个小功能,还是很开心的,记录下来,数年后再看看这个小菜鸡。

遇到问题,就想着去解决,不知道的东西,就去查资料,现在这个阶段所学的东西,肯定已经有人做过,参考一下,感谢CSDN能提供交流的平台。学无止境,继续前行。

<think>好的,我现在需要帮用户寻找关于RS422/RS485串口通讯板卡的技术资料、驱动下载和相关产品信息。首先,我需要回顾用户提供的参考引用内容,特别是引用[1]中提到的领航者开发板的接口设计和电路控制部分,以及引用[2]中的STM32相关项目和代码库链接。 用户的问题涉及技术文档、驱动程序和下载资源,所以我要先确定从哪里获取这些资料。通常,这类资源可以从板卡制造商的官方网站、开源社区或者硬件相关的论坛找到。例如,参考引用里提到的领航者开发板和STM32项目可能提供一些线索。此外,用户可能需要具体的产品型号或厂商信息,但当前提供的引用中没有明确提到,所以可能需要更通用的建议。 接下来,我需要考虑用户可能的实际需求。他们可能在开发或维护使用RS422/RS485接口的设备,需要配置硬件连接,如正确的跳线设置,或者需要驱动程序来使操作系统识别板卡。此外,技术文档可能包括接口规范、电路图、通信协议说明等。 根据引用[1],领航者开发板使用SP3485芯片,并且需要物理连接UART2_TX和RX到RS485的收发端,这可能意味着用户需要类似的配置说明。因此,在回答中应该提到查阅对应板卡的用户手册,确认硬件连接方式,比如使用杜邦线连接特定引脚。 关于驱动程序,如果板卡使用的是常见的芯片如SP3485或MAX485,可能不需要单独的驱动,因为这些通常通过操作系统自带的串口驱动工作。但如果板卡有额外的控制器或功能,可能需要特定的驱动,这时建议访问制造商网站下载。 技术文档方面,用户手册、参考设计以及Modbus协议的应用笔记都是重要的资源。引用[2]中的STM32项目可能包含示例代码和Modbus实现,这对用户可能有帮助,特别是如果他们使用的是STM32微控制器的话。 最后,生成的相关问题需要围绕用户可能进一步的需求,比如硬件设计、调试工具、协议选择等。例如,如何设计RS485电路,或者如何选择Modbus和自定义协议的区别。 需要确保回答结构清晰,分点列出资源获取途径、硬件连接注意事项、驱动安装步骤以及示例项目参考,同时加入引用标记。可能还需要提醒用户检查板卡的具体型号,以获取更准确的资料。</think>### RS422/RS485串口通讯板卡技术资料获取指南 #### 一、技术文档获取途径 1. **板卡厂商官网** 直接访问板卡制造商官网(如研华、MOXA、研扬等),在“技术支持”或“下载中心”栏目搜索对应型号的技术手册。例如领航者开发板的RS485接口设计文档,需参考其硬件手册中的接口复用说明[^1]。 2. **开源硬件平台** - GitHub/GitCode等平台提供开源项目设计文档,如引用[2]中的STM32-RS485-Modbus项目,包含电路图与代码说明[^2]。 - 推荐搜索关键词:`RS485 breakout board schematic` 或 `RS422 interface design`。 3. **芯片手册** - 若板卡基于特定芯片(如SP3485、MAX485等),需查阅芯片数据手册,明确电气特性与接口定义。例如SP3485的收发时序和端接电阻配置要求。 #### 二、驱动程序下载 1. **操作系统内置驱动** RS422/RS485通常通过UART实现,在Linux/Windows中可使用标准串口驱动(如`ttyS`或`COMx`)。需确认板卡的UART控制器型号(如FTDI FT232、CP2102等)。 2. **专用驱动场景** 若板卡集成协议转换功能(如Modbus-RTU转TCP),需从厂商获取专用驱动。例如MOXA的NPort系列提供Windows/Linux驱动及SDK。 #### 三、硬件连接关键点 1. **接口复用配置** 如领航者开发板的RS485与RS232共用UART引脚,需通过跳线连接`RS485_RX`↔`UART2_TX`及`RS485_TX`↔`UART2_RX`。 $$ \text{物理层连接逻辑: } \text{FPGA\_UART} \leftrightarrow \text{SP3485} \leftrightarrow \text{RS485\_Bus} $$ 2. **方向控制电路** 自动收发控制电路可避免信号冲突,设计时需注意使能信号(DE/RE)的时序与MCU GPIO的配合。 #### 四、示例项目参考 1. **STM32 Modbus从站实现** 引用[2]中的开源项目提供RS485半双工通信的代码框架,支持功能码读取与写入操作。 ```c // Modbus RTU数据解析示例 void MODBUS_Process(void) { if (RxBuffer[0] == SLAVE_ADDR) { switch (RxBuffer[1]) { // 功能码判断 case 0x03: Handle_ReadHoldingRegisters(); break; } } } ``` 2. **Linux串口配置** 使用`stty`命令设置RS485参数: ```bash stty -F /dev/ttyUSB0 9600 cs8 -parenb crtscts rs485 ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值