资讯动态

FPGA串口通信实战:USB转TTL与CH340G电路详解

发布时间:2026/10/2 1:54:07 来源:尧图企业网站定制
做FPGA开发头一个绕不开的外设通信接口用一句玩笑话总结就是点灯容易串口难调。很多新手从流水灯跳到串口通信时会在USB转TTL这个环节卡上一整天——明明下载线是好的驱动也装了可串口助手就是收不到数据或者收到一堆乱码。这篇文章就是把这条链路彻底讲透USB转TTL到底在转什么、CH340G外围电路怎么搭、FPGA端UART收发模块怎么实现以及上板调试时常见的一堆坑。适合刚接触FPGA、还在用USB Blaster或JTAG调试的同学也适合想自己画一块串口调试板的硬件爱好者直接参考。1. 方案选型为什么USB转TTL是新手的“保命外设”1.1 先搞清楚USB转TTL到底在转什么很多人把USB转TTL当成一根“线”实际上它是一个完整的协议转换设备。电脑USB口里跑的是USB协议主从架构、差分信号、以帧为单位传输而FPGA、MCU上的UART串口跑的是异步串行协议点对点、单端电平、按位传输。两边协议完全不对等所以必须有一颗桥接芯片在中间做翻译——这就是USB转TTL小板上那颗黑色芯片的职责。TTL电平这个说法要特别注意传统TTL是5V逻辑但现代FPGA的IO Bank供电普遍是3.3V甚至1.8V所以串口引脚上正常的TTL电平是0~3.3V以FPGA所在Bank电压为准。USB转TTL板子上的TXD、RXD引脚输出的就是这种0~3.3V逻辑电平可以直接连到FPGA的普通IO上。而早期的RS-232串口用的±12V电平和TTL完全不是一回事不能直接插——这也是为什么很多人从老式串口设备切换到USB转TTL时会烧芯片本质是电平整定问题。1.2 主流USB转UART桥接芯片对比市面上常见的USB转TTL方案不多我这儿直接拉个表对比大家照着选就行了芯片型号典型封装最高波特率驱动易得性价格区间模块新手友好度CH340GSOP-162Mbps内置驱动Win10/11免驱5~10元高CP2102QFN-241Mbps需装Silicon Labs驱动10~20元中高FT232RLSSOP-283Mbps官方驱动全平台完善25~50元中PL2303SOP-166Mbps早期版本驱动容易冲突5~10元低从项目标题就能看出我推荐CH340G。原因有三第一价格便宜SOP-16封装对新手焊接极其友好直插转DIP都行第二Windows 10/11系统基本免驱插上就能在设备管理器里看到COM口省去折腾驱动的环节第三资料全Datasheet是中文的网上能搜到的参考电路和PCB工程不计其数。CP2102性能更稳但封装太小FT232正品贵且假货多PL2303早期版本在新系统上驱动冲突严重。综合而言CH340G是新手期性价比最高的选择。1.3 CH340G的版本差异别买错后缀CH340系列有很多后缀CH340G、CH340C、CH340E、CH340N、CH340B。新手最容易踩的坑是买错封装或者选错引脚数量。CH340G是SOP-16封装外置12MHz晶振功能最全适合学习和DIYCH340C是CH340G的无晶振版本内置时钟SOP-16少了两颗电容和一颗晶振对新手反而更省事CH340E是SOP-8小封装引脚少但依然需要外置晶振CH340N也是SOP-8无晶振版常用于小体积产品。我个人的建议是学习用优先选CH340C或者CH340G。CH340C省去晶振外围器件最少即使焊接经验不足也能一次成功。但考虑到题目里专门提到“CH340G电路详解”下面我会重点讲CH340G的最小系统因为它的电路更经典理解了带晶振的版本无晶振版本的核心原理也就懂了。2. UART协议底层拆解FPGA串口通信的原理根基2.1 一帧串口数据长什么样在写FPGA代码之前必须把UART协议本身搞明白。UART是异步协议收发双方没有独立的时钟线全靠约定波特率和对线电平的采样来同步。平时不传输数据时TX线保持高电平空闲态要发送一帧数据时先拉低一个比特时间作为起始位告诉接收端“准备接收”然后逐位发送8个数据位通常LSB在前即低位先发最后拉高至少一个比特时间作为停止位。如果开启校验位那么数据位后还有一个校验位常见的有奇校验和偶校验。对FPGA和上位机调试来说默认配置就是“8-N-1”8个数据位无校验1个停止位。这个约定看起来简单但每个细节都直接影响收发能否成功。比如有些设备默认是“8-E-1”偶校验如果上位机和FPGA不匹配收到的就是乱码或直接无响应。新手一定要养成习惯先确认双方波特率一致、校验位一致、数据位一致再去查硬件。2.2 波特率与分频计算这是FPGA端的核心波特率就是每秒传输的比特数常见值有9600、115200、921600等。FPGA靠系统时钟产生一个特定频率的使能信号来“按节奏”发送或采样数据。以最常见的50MHz系统时钟为例要产生115200bps的波特率分频计数器的计数上限是50000000 / 115200 434.027...实际取整数434那么一个比特持续434个时钟周期。由于取整造成的误差约为0.006%远比UART协议允许的2%~3%误差小完全可以忽略。如果系统时钟是27MHz想产生115200就是27000000 / 115200 234.375取234后误差稍大但在短帧传输下也够用因为UART是在每个数据位的中间采样即使每位有点小偏差只要一帧范围内误差累积不超过半个位宽就不会出错。波特率分频设计时有一个小技巧用“计数器值 系统时钟 / 波特率”来定义参数而不是硬编码一个数字。这样以后换时钟频率或换波特率只需改动参数定义不需要改逻辑代码。我见过太多新手把分频值写死在状态机里调试时想换个9600试试还得满代码找数字非常痛苦。2.3 FPGA端UART发送模块的设计思路发送模块其实就是一个状态机空闲态IDLE保持TX线高电平检测到发送请求后拉低起始位然后按bit计数依次发送数据位和停止位。这里有两个容易出问题的细节一是起始位持续时间和数据位持续时间必须完全相等都等于“波特率分频值”个时钟周期二是发完停止位后要回到IDLE态并且置高TX线否则接收端可能把新的起始位误判成上一字节的延续。下面是一个可直接使用的UART发送模块Verilog代码采用50MHz时钟、115200波特率你可以把它封装成IP核反复使用module uart_tx #( parameter CLK_FREQ 50_000_000, parameter BAUD_RATE 115200 )( input wire clk, input wire rst_n, input wire tx_start, input wire [7:0] tx_data, output reg tx, output reg tx_busy ); localparam BIT_CNT_MAX CLK_FREQ / BAUD_RATE - 1; reg [15:0] cnt; reg [3:0] bit_cnt; reg tx_start_d0; wire tx_start_pulse tx_start ~tx_start_d0; localparam IDLE 2b00; localparam START 2b01; localparam DATA 2b10; localparam STOP 2b11; reg [1:0] state; always (posedge clk or negedge rst_n) begin if (!rst_n) begin state IDLE; tx 1b1; cnt 16d0; bit_cnt 4d0; tx_busy 1b0; end else begin tx_start_d0 tx_start; case (state) IDLE: begin tx 1b1; tx_busy 1b0; cnt 16d0; bit_cnt 4d0; if (tx_start_pulse) begin state START; tx_busy 1b1; tx 1b0; end end START: begin if (cnt BIT_CNT_MAX) begin cnt 16d0; state DATA; tx tx_data[0]; end else begin cnt cnt 1b1; end end DATA: begin if (cnt BIT_CNT_MAX) begin cnt 16d0; if (bit_cnt 4d7) begin bit_cnt 4d0; state STOP; tx 1b1; end else begin bit_cnt bit_cnt 1b1; tx tx_data[bit_cnt 1b1]; end end else begin cnt cnt 1b1; end end STOP: begin if (cnt BIT_CNT_MAX) begin cnt 16d0; state IDLE; tx_busy 1b0; end else begin cnt cnt 1b1; end end endcase end end endmodule这段代码的要点是起始位时直接赋低电平数据位时按bit_cnt索引逐位发送停止位重新拉高并等待一个位宽。tx_busy信号对外提示发送忙状态上层逻辑在tx_busy拉高期间不要发起新的发送请求否则会丢掉数据。实际上FPGA内部有很多UART核可以用但新手一定要亲手写一遍发送状态机这是理解异步协议最好的方式。2.4 FPGA端UART接收模块的设计思路接收比发送稍微复杂一点核心是“边沿检测中间采样”。空闲时RX线为高电平当检测到下降沿时说明收到起始位进入接收状态。此时不能立刻采样而是要在起始位的中点即半个位宽之后确认还在低电平防止毛刺干扰误判。之后每隔一个位宽采样一次数据位连续采样8次最后确认停止位为高。module uart_rx #( parameter CLK_FREQ 50_000_000, parameter BAUD_RATE 115200 )( input wire clk, input wire rst_n, input wire rx, output reg [7:0] rx_data, output reg rx_done ); localparam HALF_BIT CLK_FREQ / BAUD_RATE / 2 - 1; localparam BIT_CNT_MAX CLK_FREQ / BAUD_RATE - 1; reg [15:0] cnt; reg [3:0] bit_cnt; reg rx_d0, rx_d1; wire rx_negedge rx_d1 ~rx_d0; localparam IDLE 2b00; localparam START 2b01; localparam DATA 2b10; localparam STOP 2b11; reg [1:0] state; always (posedge clk or negedge rst_n) begin if (!rst_n) begin state IDLE; cnt 16d0; bit_cnt 4d0; rx_data 8d0; rx_done 1b0; rx_d0 1b1; rx_d1 1b1; end else begin rx_d0 rx; rx_d1 rx_d0; rx_done 1b0; case (state) IDLE: begin bit_cnt 4d0; cnt 16d0; if (rx_negedge) begin state START; cnt 16d0; end end START: begin if (cnt HALF_BIT) begin if (rx_d1 1b0) begin cnt 16d0; state DATA; end else begin state IDLE; end end else begin cnt cnt 1b1; end end DATA: begin if (cnt BIT_CNT_MAX) begin cnt 16d0; rx_data[bit_cnt] rx_d1; if (bit_cnt 4d7) begin bit_cnt 4d0; state STOP; end else begin bit_cnt bit_cnt 1b1; end end else begin cnt cnt 1b1; end end STOP: begin if (cnt BIT_CNT_MAX) begin cnt 16d0; state IDLE; if (rx_d1 1b1) begin rx_done 1b1; end end else begin cnt cnt 1b1; end end endcase end end endmodule接收模块里必须做两级同步rx_d0、rx_d1因为外部信号进入FPGA时可能处于亚稳态两级触发器可以把亚稳态的概率降到足够低。另外起始位中点采样时如果发现是高电平说明刚才的下降沿是毛刺干扰应该回到IDLE重新等待。这个“半步确认”机制是UART接收稳定的关键很多人写接收代码不确认起始位结果噪声一大就频繁误触发后面调试时很难排查。3. CH340G电路详解照着搭就能稳定运行3.1 CH340G引脚功能一次说清先把CH340G的SOP-16封装引脚认全。核心引脚包括1脚GND4脚VCC5V或3.3V供电5脚XI、6脚XO外接12MHz晶振7脚RTS#8脚DTR#9脚TXD10脚RXD11脚RI#12脚DSR#13脚DCD#14脚CTS#15脚V3。其中TXD和RXD是UART侧信号RTS#、CTS#、DTR#、DSR#这些是流控和状态引脚对纯数据收发来说可以不接。USB侧是D和D-直接连到USB座子的DP和DM。特别要注意的是7脚~14脚这一排的多功能引脚很多买回来的CH340G模块只引出了TXD、RXD、VCC、GND四个引脚但如果你想用CH340G实现自动下载电路像ESP32的自动烧录那样就会用到DTR#和RTS#。V3脚是内部3.3V稳压输出5V供电时V3脚接一个0.1uF退耦电容到地同时V3脚也会输出3.3V电压供外部使用要评估电流能力不建议带大负载如果系统本身是3.3V供电那么V3脚直接接VCC这个细节写Datasheet时容易看漏。3.2 CH340G最小系统电路搭建一个完整可用的CH340G电路除了芯片本身还需要以下外围器件VCC到GND之间的0.1uF去耦电容必须紧贴芯片12MHz晶振配22pF负载电容两端各一颗晶振电容和V3脚电容不可混用USB数据线上的D、D-走差分线两个DP/DM引脚处各加一个串联电阻常见取值22Ω或33Ω用于抑制振铃如果供电不稳定VCC入口加一颗10uF电解电容做储能画图时最容易犯的错是把晶振电容画在V3上或者不接负载电容直接让晶振起振。12MHz晶振必须配电容否则可能起振异常导致PC端无法枚举设备。还有一种情况是USB线太长、供电不足导致芯片反复复位所以电路设计上一定要保证USB口的5V经过磁珠或保险丝之后再给CH340G供电不要直接怼在芯片脚上。3.3 电平匹配3.3V FPGA连CH340G的电压问题CH340G的UART引脚TXD、RXD电平取决于VCC供电。如果你给CH340G供5V它的TXD输出高电平也是5V直接连到3.3V的FPGA IO上长期使用会超过IO绝对最大额定值轻则烧IO重则烧芯片。很多新手没意识到这个问题以为USB转TTL板上芯片供电多少V无所谓结果FPGA莫名其妙个别引脚失效查半天才发现是电平过压。解决方案有三种第一CH340G用3.3V供电这样TXD、RXD输出电平就是3.3V和FPGA完美匹配这是最简单可靠的做法第二CH340G用5V供电但TXD串联一个1kΩ电阻再连FPGA利用FPGA内部IO保护二极管钳位这种做法有风险不推荐新手用第三之间加电平转换芯片如TXS0108E适合多路信号混合的情况串口单路没必要。我自己的板子一律用3.3V给CH340G供电省心。3.4 从原理图到实物CH340G模块画板经验如果你打算自己画一块CH340G转串口模块布局上要把USB座放在板边D和D-从USB座出来后尽量等长、短距离走到芯片中间不要打过孔。晶振靠近XI和XO引脚晶振下方铺地铜不要走信号线。CH340G芯片下方可以用一个实心地焊盘帮助散热和减少噪声。TXD和RXD引脚如果需要引出到排针可以在排针附近预留几个GND引脚方便调试时接地线。如果只是新手学习不一定要从零画板——直接买一块现成的USB转TTL模块再按照我讲的方式检查供电电压和引脚定义也可以完成FPGA串口通信。画板是进阶需求核心是先跑通协议。4. 完整实操记录从FPGA工程到串口收发数据4.1 硬件连接与驱动确认先准备一套完整的硬件FPGA开发板、USB转TTL模块CH340G、USB线、杜邦线。连接原则是交叉连接——USB转TTL模块的TXD接FPGA的RXD引脚模块的RXD接FPGA的TXD引脚GND必须共地。这一步是新手最容易错的地方。很多人把TXD对TXD、RXD对RXD直连结果一收数据就是乱码或完全没反应。串口通信是交叉互联发送端连接收端接收端连发送端。共地也一样重要如果两个设备GND不连电平参考点不一致数据线即使接对也不会有稳定的高低电平。插上USB转TTL模块后到设备管理器看端口COM和LPT正常会出现一个“USB-SERIAL CH340”之类的COM口。如果是第一次使用系统会自动安装驱动。如果显示黄色感叹号需要手动装驱动。驱动安装包里包含的“CH340SER.EXE”可以直接运行注意别在检测到设备之前乱装驱动否则容易出现两个未知设备来回跳的灵异问题。4.2 FPGA工程UART收发测试代码在VivadoXilinx FPGA或QuartusAltera FPGA中新建工程把上面的uart_tx和uart_rx两个模块添加进去。顶层模块里做这样的逻辑收到PC发来的一个字节原样回发同时板载LED按收到的数据显示。这个“回环测试”是最实用的验证手段收发是否正常一眼就能看出来。module uart_loopback_top ( input wire clk, input wire rst_n, input wire rx, output wire tx, output wire [3:0] led ); wire rx_done; wire [7:0] rx_data; wire tx_busy; reg tx_start 1b0; reg [7:0] tx_data 8d0; uart_rx u_rx ( .clk(clk), .rst_n(rst_n), .rx(rx), .rx_data(rx_data), .rx_done(rx_done) ); uart_tx u_tx ( .clk(clk), .rst_n(rst_n), .tx_start(tx_start), .tx_data(tx_data), .tx(tx), .tx_busy(tx_busy) ); always (posedge clk or negedge rst_n) begin if (!rst_n) begin tx_start 1b0; tx_data 8d0; end else if (rx_done) begin tx_data rx_data; tx_start 1b1; end else if (tx_busy) begin tx_start 1b0; end end assign led rx_data[3:0]; endmodule这个顶层模块里的一个细节是tx_start触发一次后要靠tx_busy信号来拉低不然rx_done持续拉高时会重复发送多次。实际项目中如果需要连续收发建议用FIFO做缓冲但新手验证阶段最简化逻辑就够了。4.3 引脚约束把RX、TX映射到物理引脚这一步看似简单但坑最多。以为把rx分配到开发板的某个按键把tx分配到LED实际上按键和LED对应的引脚在开发板原理图里可能和某组外部接口复用。建议打开开发板原理图找到扩展排针接口上的名字比如标明“UART_RX”“UART_TX”的引脚。以Xilinx Artix-7开发板为例XDC约束文件里写set_property PACKAGE_PIN [get_ports {rx}] [你的RX引脚] set_property IOSTANDARD LVCMOS33 [get_ports {rx}] set_property PACKAGE_PIN [get_ports {tx}] [你的TX引脚] set_property IOSTANDARD LVCMOS33 [get_ports {tx}]引脚分配错了或者IO电平标准IOSTANDARD写成了LVCMOS18就可能导致信号拉不起来或者电平不识别。还有一个容易忽略的点如果是直接把USB转TTL模块用杜邦线捅在扩展排针上注意杜邦线接头可能松动接触不良时串口时好时坏。建议优先用带锁定功能的接线端子或者焊线否则排查起来很折磨人。4.4 串口助手配置与环路测试烧录完成后打开串口助手按下图配置波特率115200数据位8停止位1无校验位无流控。关键点关闭“发送新行”或者不要勾选“加回车换行”否则FPGA收到的数据里会多出0x0D和0x0A两个字节回显时你会发现只发了一个字符却回了三个个别时候甚至表现为“乱码”。先做最简单的手动测试在发送框输入A十六进制为0x41点击发送观察接收区是否出现A。如果出现了说明FPGA到CH340G再到PC的整条链路是通的。再输入一组十六进制数据如01 02 03 04观察是否原样回显。回显正常后还可以做一个压力测试设置串口助手每100ms自动发送一帧持续半小时看数据是否出现丢帧或错位。这个测试可以暴露出FPGA代码里接收FIFO缺失、逻辑时序不稳等问题。4.5 数据格式进阶ASCII与十六进制的坑很多新手在串口助手看到十六进制和字符混在一起就慌了。比如发送41如果用“十六进制发送”就是0x41显示为字符A如果用“文本发送”实际发的是0x34、0x31两个字节字符4和1的ASCII码FPGA回显时回了4和1两个字符。这不是乱码是格式理解问题。写FPGA代码时建议按照十六进制收发的思路来设计上位机调试时固定使用“HEX发送/HEX显示”模式能避免很多不必要的误会。5. 常见问题排查与避坑实录5.1 问题速查表这里整理了一张我在调试FPGA串口通信时踩过或帮别人排查过的高频问题清单按出现频率排列现象大概率原因排查方法设备管理器看不到COM口驱动未安装或芯片虚焊重装驱动检查USB线与模块供电COM口有感叹号驱动版本不匹配卸载后安装最新版CH340驱动串口打开后收不到任何数据TX/RX接反或GND未共地交换TX/RX两根杜邦线确保共地收到数据全是乱码波特率不一致、校验位不匹配确认双方波特率统一为8-N-1格式时好时坏动一下线就断杜邦线接触不良换线或改为焊接连接发一个字符回三个字符串口助手勾了回车换行关闭“发送新行”选项FPGA上电后串口无反应外部复位未释放或时钟没起振检查复位按键、时钟信号LED状态回环正常但回显偶尔丢字节接收FIFO未做缓冲忙时丢数据在顶层模块中增加FIFO或降低发送频率5.2 串口打不开或者识别异常的深度排查如果CH340G插入电脑后没有出现任何反应先看模块上的电源指示灯是否亮。如果不亮大概率是USB线本身是充电线而不是数据线——这是新手最容易忽视的坑充电线只有电源两根芯不包含D/D-所以模块等同没插数据。解决方法是换一根确认能传数据的USB线。如果电源灯亮但设备管理器没有任何反应用手摸芯片表面温度。微热是正常发烫说明芯片可能接反或VCC和GND短路立即断电检查。用万用表测量CH340G的VCC和GND之间阻值正常应该在几百欧姆以上如果接近0说明焊接短路或芯片已损坏。另外排查V3脚如果没有接0.1uF电容芯片也可能无法正常工作。还有一种情况是系统里装过旧版CH340驱动导致新版驱动无法识别。处理办法是到设备管理器里手动删除设备并勾选“删除驱动程序软件”然后重新插拔USB。Windows 11下尤其容易遇到驱动签名问题如果安装时提示“数字签名缺失”可以去安装包目录右键以管理员身份运行。5.3 有信号但数据乱码、错位的三板斧乱码问题本质是收发双方对每一位数据的“节奏”没有对齐。第一板斧核对波特率。用示波器抓TXD引脚的波形测量一帧起始位低电平的宽度如果是115200就应该是8.68us左右实际测出来如果是8.2us或9us说明模块或FPGA时钟有偏差。第二板斧检查数据位顺序。UART是LSB first如果哪位自作聪明在FPGA里做了位序反转上位机收到的每个字节都会变成倒序。第三板斧检查校验位和停止位。把串口助手配置为“8-E-1”而FPGA按“8-N-1”解析就会把所有数据都错位。实测中还有一个高频原因FPGA开发板和USB转TTL模块没有共地。尤其是用独立供电的FPGA板子和独立供电的USB转TTL小板时两边各有一个参考地如果不连GND波形虽然看起来有高有低但电平参考点不同接收端读到的根本不是稳定的0或1。判断方法是先把两边的GND用杜邦线连上看乱码是否立刻消失。5.4 关于CH340G的“自动下载电路”进阶经验调试完串口通信后大家往往会想试着把CH340G用于“一键下载”电路。CH340G的DTR#和RTS#在芯片内部是互相配合的可以在PC端控制串口时产生特定的电平跳变序列利用这个序列去控制FPGA配置芯片的复位和下载模式引脚实现自动进入下载模式。但这需要具体分析FPGA配置引脚的时序要求尤其是DTR#和RTS#的时序关系不同FPGA厂商要求不一样。这里只提醒一点自动下载电路里一定要加三极管或者MOS管做电平转换和缓冲不要直接把DTR#接到FPGA配置引脚。CH340G的DTR#和RTS#是推挽输出高电平时能输出VCC低电平时是0V但驱动能力有限且和FPGA配置逻辑的电平逻辑可能是反的。我调试自动下载时序时就遇到过因为时序毛刺导致FPGA反复进入配置模式的情况后来加上下拉电阻、调整RC延迟才解决。新手期建议先纯手动按复位下载自动下载是后续进阶的玩法。最后分享两个小技巧一个是串口调试时给FPGA程序里加一个“心跳回包”。每隔1秒FPGA主动向上位机发一个固定字节如果串口链路正常串口助手会稳定收到心跳如果中间断连或者死机心跳立刻消失。这个习惯能帮你快速区分是硬件链路问题还是FPGA逻辑问题。另一个是把串口助手换成支持脚本的调试工具比如Python的pyserial库或者自带脚本功能的串口调试软件可以自动做压力测试和数据校验以后调其他外设GPS模块、蓝牙模块、传感器模组时也都能复用。从一台电脑、一块FPGA、一个USB转TTL模块开始把串口通信彻底吃透后面任何带UART接口的芯片在你眼里都会透明不少。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑