
简介压缩包围绕FT232H USB转串口芯片提供完整驱动源码面向嵌入式开发者、工业控制及USB通信学习者可用于研究FTDI芯片驱动架构与MPSSE高速串行通信机制的实现。包体共11个文件由5个C源码、5个头文件和1个Makefile组成整体仅47KB代码量精简适合逐文件通读。源码覆盖设备枚举、打开、读写、参数配置等基础操作并针对FT232H实现了MPSSE多协议同步串行引擎接口支持SPI、I2C、JTAG等协议fast.c中的快速传输优化、support.c中的辅助函数以及多份头文件中的接口声明使驱动层次清晰、易于扩展。已有535人学习下载。读者可借此深入理解USB驱动分层与FTDI库内部实现也可在mpsse.c基础上扩展自定义串行协议或参考fast.c改进传输效率对入门和进阶开发均有实用价值。1. FT232H为什么这款USB转接芯片成了嵌入式调试的常备件很多人第一次认识FT232H是拿着一个没引线的小板子焊了两排排针插到电脑上发现设备管理器里多出一个USB Serial Converter。再装个驱动就能用Python把一串字节发出去点亮LED、读完一颗SPI Flash的ID。这个芯片是FTDI家的USB转FIFO/串口/GPIO/SPI/I2C桥接方案一颗芯片把电脑和各类低速总线设备串起来调试传感器、存储器、FPGA配置、固件烧录都靠它。它的核心价值在于不需要单片机参与电脑上直接写脚本就能操作硬件总线。适合的对象很明确——经常和嵌入式外设打交道、需要快速验证原理、又不想为了测一颗芯片去焊一块最小系统的工程师。它不一定是最快、最强、最便宜的选择却是最容易跑通的方案。2. 先看清这颗芯片再动手管脚、通道和驱动选型2.1 从名字看功能FIFO桥接、SPI/I2C主机模式和自带时钟发生器FT232H不是一颗简单的USB转串口芯片。名字里的232会让人误以为它只替代RS232实际上它内部有一个MPSSEMulti-Protocol Synchronous Serial Engine引擎这个引擎负责把USB包翻译成SPI/I2C/JTAG时序。芯片内部有一个内置的时钟发生器能产生6MHz到30MHz的时钟在MPSSE模式下SCLK频率可调用于驱动SPI或I2C的时钟线。它有两个协议通道Channel A/B但FT232H只引出一个通道和FT2232H的双通道定位不同。它的管脚分组大致是USB端是一对D/D-电源端VCC和GNDIO端一组是ADBUS0~7另一组是ACBUS0~7。其中ADBUS0~3在MPSSE模式下会映射为SCLK、MOSI、MISO、CS等信号ACBUS0~3则承载一些控制信号。实际做板时很多开发者会忽略一个关键管脚VI/O——这是IO电平参考电压的输入端。FT232H的IO电平不是固定3.3V而是跟随VI/O引脚的电压。如果VI/O悬空输出电平会落到一个不可预估的状态这个问题我后面还会专门提。选型上要注意FT232H的IO逻辑电平上限取决于VI/O数据手册标称IO可承受的绝对最大电压高于3.6V但不建议直接接5V逻辑。如果目标板是5V的传感器就需要外接电平转换不能想当然FT232H可以容忍5V。2.2 同类芯片怎么选CP2102、CH340、FT2232H与FT232H的边界做USB转接方案时很多人会拿CP2102、CH340、FT2232H做比较。它们各有各的主场。芯片串口SPI主机I2C主机GPIO备注CP2102有无无无纯串口驱动简单最常用CH340有无无无便宜兼容性好但无高级功能FT232R有无无有bitbang经典串口芯片GPIO有限FT232H有通过MPSSE有有有bitbang单通道适合轻量调试FT2232H有有双通道有双通道有双通道可同时跑两路总线我的习惯是只用来调试串口选CP2102因为它便宜、驱动成熟要烧录SPI Flash、模拟I2C传感器选FT232H要同时调试两路SPI设备比如同时挂一个Flash和一块FPGA就上FT2232H。FT232H的边界在于它是单通道在同一个时间点只能配置成一种MPSSE模式要么SPI要么I2C要么JTAG虽然可以运行时切换但同一时刻做不了SPI和I2C同时跑这件事。FT232H的另一个独特价值是它支持245同步FIFO模式。在这个模式下芯片可以把USB端的数据以高达40MB/s的速率搬到本地总线配合FPGA做数据采集是常见玩法。这块我放在最后一章细说很多人买这块芯片就是冲着这个模式去的。2.3 驱动层选型libftdi、D2XX还是pyftdi什么时候用哪个芯片本身是硬件真正决定开发效率的是驱动库。FTDI提供了两条快车道一是厂商自己的D2XX驱动Windows/macOS/Linux都有直接通过USB Vendor请求操作芯片性能好但和系统串口驱动不兼容二是操作系统内核自带的ftdi_sio驱动它把FT232H识别成一个串口设备/dev/ttyUSB0好处是随手能用坏处是MPSSE功能会被封装掉不能直接发SPI时序。libftdi是开源社区维护的库底层通过libusb通信和内核的ftdi_sio互斥。在Linux上要使用libftdi或pyftdi必须先把ftdi_sio模块从设备上解绑。pyftdi是libftdi的Python封装但它实际上直接调用pyusb走的是libusb通道不依赖libftdi的C库用起来更省心。我的选择逻辑是这样的只在终端看串口日志用系统自带驱动就够了在Linux上做Python脚本控制优先pyftdi在Windows上用Qt或C做上位机用D2XX或libftdi都行但要注意D2XX和系统串口驱动不能同时绑定同一设备。踩坑经验是Windows下有些用户装了FTDI的VCP驱动就是那种让设备显示为COM口的驱动之后再用pyftdi就找不到设备了原因是设备被VCP驱动占住pyusb无权访问。解决方法是手动更新驱动为WinUSB或libusb-win32后面我会给出具体步骤。3. 在Windows和Linux下把FT232H跑起来安装、识别和最小读写3.1 Windows下安装CDM驱动并用Zadig确认设备枚举FT232H在Windows上第一次插电系统通常会识别为未知设备需要安装FTDI的CDMCombined Driver Model驱动。FTDI官方发布的驱动安装包同时提供VCP虚拟串口和D2XX两种接口。安装完成后设备管理器里会出现USB Serial Converter和对应的COM端口两项。这一步本身没有什么难度真正的坑在后面当你用pyftdi或D2XX API去open设备时如果设备正被VCP驱动持有USB Vendor请求会被系统拦截。我一般会先安装CDM驱动让设备正常枚举然后打开Zadig这是一个开源的USB驱动安装工具常见的USB驱动切换都靠它把FT232H的驱动从FTDI VCP切换为WinUSB。Zadig操作很简单插上设备后在Options里勾选List All Devices下拉选择FT232H或USB Serial Converter目标驱动选WinUSBx86/x64按系统位数点击Replace Driver。# 注意Zadig是图形化工具没有命令行参数 # 但你可以用wdi-simple.exe做无人值守替换 wdi-simple.exe --vid 0403 --pid 6014 --type 0 --name FT232H WinUSB这段命令的含义是指定设备的VID/PIDFT232H的VID是0403PID是6014把驱动类型替换为WinUSB。type 0对应WinUSBtype 1对应libusb-win32。完成后在设备管理器里确认设备变成了WinUSB device。这样做的目的是把设备从系统串口驱动手里解放出来交给libusb栈管理。要注意的是这一步不可逆性不强——后悔药随时有。想恢复VCP驱动重新运行FTDI的CDM安装包或者在设备管理器里更新驱动程序手动选择FTDI VCP就有机会回去。但Windows 10以上版本有个恶心问题驱动缓存会导致你明明改了WinUSB重启后又被系统按最佳匹配给装回VCP。处理方法是去设备管理器里把该设备卸载勾选删除此设备的驱动程序软件再重新插拔。3.2 Linux下用ftdi_sio还是libftdi处理内核驱动抢占Linux下FT232H的命运比Windows好一些。插上后内核的ftdi_sio模块通常会自动加载设备出现在/dev/ttyUSB0。这对串口场景是好事但对pyftdi/D2XX场景就是障碍。如果直接用pyftdi扫描设备通常能扫到设备但open时报DeviceBusy或Access denied十有八九是因为ftdi_sio占着设备。常见的做法是先解绑内核驱动再让libusb接管# 查看设备当前的驱动绑定 lsusb -t # 找到FT232H对应的usb设备路径形如1-1.3:1.0 # 把ftdi_sio驱动的绑定取消注意替换成你实际的bus/port echo 1-1.3:1.0 /sys/bus/usb/drivers/ftdi_sio/unbind # 让usbfs的authorized接口确认设备可用 lsusb -d 0403:6014这段操作的核心是从内核驱动的绑定列表里把设备松绑之后设备虽然还在USB总线上但不再被ftdi_sio占用。lsusb -t可以看到设备挂在哪一层一般形如1-1.3:1.0其中1是总线号1.3是hub端口串。如果不确定路径可以用lsusb -t输出里的/sys/bus/usb/devices/下的目录名来对照。如果我需要长期把这块芯片留给libftdi用我会建议在系统层面把它固定下来# 创建一个udev规则在设备插入时自动解绑ftdi_sio cat /etc/udev/rules.d/99-ft232h-libftdi.rules EOF ACTIONadd, SUBSYSTEMusb, ATTR{idVendor}0403, ATTR{idProduct}6014, RUN/bin/sh -c echo $kernel /sys/bus/usb/drivers/ftdi_sio/unbind EOF # 重新加载udev规则 udevadm control --reload-rules3.3 用pyftdi跑通第一个GPIO闪烁和SPI读取驱动这层解决后真正能上手的第一件事是用pyftdi在ADBUS0上翻转一个GPIO。pyftdi的使用模式是先创建控制器对象再open指定设备然后配置方向并写值。from pyftdi.ftdi import Ftdi from pyftdi.gpio import GpioController # 打开FT232H设备url格式ftdi://ftdi:232h:系列号/接口号 gpio GpioController() gpio.configure(ftdi://ftdi:232h/1, direction0x01) # 只把ADBUS0设为输出 # 循环闪烁ADBUS0假设LED接在ADBUS0和GND之间 import time for _ in range(5): gpio.write(0x01) # ADBUS0拉高 time.sleep(0.2) gpio.write(0x00) # ADBUS0拉低 time.sleep(0.2) gpio.close()这段代码里configure的direction参数每一位对应一个ADBUS引脚1为输出0为输入。0x01意味着只把ADBUS0配成输出其余保持输入。gpio.write写入一个字节只对direction里标记为输出的位有效。跑通这一步后基本验证了USB链路、驱动和pyftdi都正常。接着可以挑战SPI读取。最简单的SPI设备是25系列Flash比如W25Q64它的0x90命令能读取厂商ID和设备ID。from pyftdi.spi import SpiController spi SpiController() spi.configure(ftdi://ftdi:232h/1) flash spi.get_port(cs0, freq1000000, mode0) # CS0, 1MHz, SPI Mode 0 # 发送0x90Read Manufacturer/Device ID后跟3个dummy字节 # W25Q系列的JEDEC ID读取命令是0x9F这里以0x9F为例 response flash.exchange([0x9F, 0x00, 0x00, 0x00, 0x00], 2) print(JEDEC ID bytes:, [hex(b) for b in response])exchange的机制是把列表里所有字节通过MOSI发出同时从MISO收同样数量的字节。这里发出5个字节最后2字节是Flash回填的Manufacturer ID和Device ID。我习惯把第一次SPI读写落在Flash上因为它不涉及复杂的初始化失败时排查范围小——接错线、时钟极性错、CS没拉低都有明确的症状。4. 把FT232H当SPI/I2C主机用从接线到时序参数4.1 MPSSE引擎为什么SCLK频率可以到30MHzFT232H的核心是MPSSE引擎。它从USB端收到配置命令比如输出SCLK频率为1MHz、发送0x90这个字节然后按配置把数据串行地搬到目标设备上。MPSSE的最大优势是它不占用电脑CPU——USB控制器把数据丢进芯片内部FIFOMPSSE按自己的节奏发出去。FT232H的MPSSE时钟上限是30MHz。注意这个30MHz只是SCLK频率不是数据吞吐量实际吞吐率受USB传输效率和协议开销影响。我实测过在1MHz SCLK下读写W25Q64读一页数据的时间大约多出20%的协议开销和理论值吻合。freq参数可以设成整数也可以是小数pyftdi会按芯片内部时钟分频来匹配。关于时钟配置有个常见误用把freq设为30MHz去驱动一颗只支持20MHz的SPI Flash大概率读到乱码。MPSSE只保证输出指定的SCLK不保证目标设备能吃得消。稳妥的做法是从低到高试先1MHz跑通再逐步提高。一旦高频率下校验失败不要怀疑MPSSE先检查接线和逻辑分析仪抓到的波形。4.2 SPI模式与时钟极性配置CPOL/CPHA对应关系pyftdi的SpisPort.get_port()里mode参数支持0~3对应SPI的四种模式。很多人在这里翻车原因是他们只看数据手册上的CPOL/CPHA却搞不清FT232H在MPSSE模式下怎么映射。SPI ModeCPOLCPHA空闲时钟电平数据采样沿适用场景000低上升沿大多数Flash/传感器101低下降沿部分ADC210高下降沿某些SD卡SPI模式311高上升沿常见SPI NOR Flash在MPSSE里CPOL由空闲SCLK电平配置决定CPHA由数据在SCLK的第几个沿采样决定。pyftdi的mode参数直接帮你组合好了不用手动去设寄存器。我的经验是初始化时先选mode 0如果读回的数据是0xFF或0x00这种整齐的全1全0再尝试mode 1/2/3多数情况下是CPOL/CPHA不匹配导致采样点落在数据跳变上。如果手头没有逻辑分析仪可以通过一个简单的试探判断发送0x00到一颗Flash如果Flash回0xFF但不回0x00说明MISO没信号如果MISO波形有但数据全错优先怀疑时钟极性和相位。还有一个土办法用GPIO模式拉高/拉低SCLK看Flash是否能识别命令但这太绕了不如直接切换mode测试。4.3 I2C总线实现细节地址模式、SCLK拉伸与电平转换FT232H的I2C主机模式同样走MPSSE但I2C和SPI有个本质差别I2C是半双工的数据线SDA是双向的需要处理ACK/NACK、时钟拉伸和总线仲裁。MPSSE为了处理这些内部加了状态机不再像SPI那样简单地把字节丢出去就行。pyftdi的I2C接口和SPI接口风格类似但多了地址参数和更细的时序控制from pyftdi.i2c import I2cController i2c I2cController() i2c.configure(ftdi://ftdi:232h/1) # 扫描总线上有哪些设备地址 found i2c.scan() print(Found I2C devices:, [hex(addr) for addr in found]) # 如果找到0x48比如常见的LM75温度传感器读取温度 sensor i2c.get_port(0x48) # LM75的0x00寄存器是温度值读2字节 data sensor.read(0x00, 2) print(Raw temp:, data)I2C扫描的机制是依次向每个7位地址发送一个读请求收到ACK就认为设备存在。这里有个边界某些设备比如EEPROM在刚上电时可能来不及响应扫描导致漏检。解决办法是在scan前先延时几百毫秒或者在configure后主动发送一个总线复位一般是通过GPIO把SCL拉高拉低9次。I2C的另一个坑是上拉电阻。FT232H没有内置I2C上拉SDA/SCL必须外部接上拉电阻常见2.2k~10k到3.3V。如果忘了接上拉会出现一个怪现象pyftdi能扫描到设备但有时地址会偏移——这其实是总线电平在高低阈值间浮动读到的ACK字节随机。最好的办法是确认VI/O供电正常且在SDA/SCL各接一个4.7k电阻到3.3V。4.4 实际读写Flash的完整代码示例把前面几块合起来一个完整可抄作业的SPI Flash读写流程如下。这里用W25Q64为例做读JEDEC ID、写使能、写页、读回校验。from pyftdi.spi import SpiController spi SpiController() spi.configure(ftdi://ftdi:232h/1) flash spi.get_port(cs0, freq500000, mode0) def read_jedec_id(): # 0x9F命令后面不需要地址但需要3个dummy字节来装回ID return flash.exchange([0x9F, 0x00, 0x00, 0x00, 0x00], 3) def write_enable(): flash.exchange([0x06], 0) # 写使能无回读 def read_status(): # 读状态寄存器判断BIT0WIP是否为零 return flash.exchange([0x05, 0x00], 1)[0] def page_program(addr, data): write_enable() cmd [0x02, (addr 16) 0xFF, (addr 8) 0xFF, addr 0xFF] data flash.exchange(cmd, 0) # 等待WIP清0 while read_status() 0x01: pass def read_data(addr, length): cmd [0x03, (addr 16) 0xFF, (addr 8) 0xFF, addr 0xFF] return flash.exchange(cmd, length) # 先读ID确认接线 print(JEDEC ID:, [hex(b) for b in read_jedec_id()]) # 在一个空区域写一页假设0x1000处安全 test_data list(range(256)) page_program(0x1000, test_data) read_data(0x1000, 256) test_data这段代码把SPI最常用的几个操作都覆盖了。flash.exchange里的read_len参数决定从MISO收几个字节。注意写使能0x06必须在每次页编程前重新发送因为一次写操作完成后WEL位会被自动清除。这只是个最小验证流程直接用page_program不影响Flash原有内容但做原型验证前我依然会先读JEDEC ID——它能一次性确认接线的MOSI、MISO、SCLK、CS是否都正常。5. 避坑与常见问题供电、电平、驱动冲突和时序翻车的五个现场5.1 现象IO输出电平不对高电平只有0.7V逻辑分析仪测不到信号原因:FT232H的IO电平由VI/O引脚决定。VI/O悬空或没接到目标板供电时输出驱动器的参考电压不稳定导致高电平电压异常。解决把VI/O接到目标板的IO电源通常是3.3V。如果你的目标板是5V系统先把VI/O接到3.3V再把所有数据线用电平转换器接到5V侧。千万不要把VI/O接到5V否则对FT232H不一定立刻损坏但长期超压会把芯片IO保护二极管击穿。5.2 现象Linux上pyftdi扫描得到设备但configure时报DeviceBusy或者usb.core.USBError原因ftdi_sio内核驱动占用了设备libusb无法获取设备访问权。解决按第3.2节的方式解绑ftdi_sio或者在外层写udev规则。另外当你在一个会话里跑pyftdi程序崩溃退出但没释放设备也会报Busy。用fuser /dev/ttyUSB0看看有没有进程占着杀掉僵尸python进程后再试。5.3 现象SCLK频率设高了之后读JEDEC ID返回全0xFF但模式0、低频率下正常原因目标设备尤其是比较老的Flash芯片不支持那么高的SPI时钟或者你用的杜邦线过长信号完整性问题在高频下显形。解决降频到500kHz试确认是目标设备极限问题还是线材问题。如果用短粗线降频后恢复正常就是线缆问题如果还是全0xFF就是Flash本身不支持或接线有断点。杜邦线超过20cm时我一般不把freq设超过10MHz这算是玄学但血泪经验如此。5.4 现象Windows下用pyftdi open设备报usb.core.USBError: [Errno 13] Access denied (insufficient permissions)原因设备被FTDI的VCP驱动USB Serial Converter占用或者系统还未把设备识别为WinUSB。解决打开Zadig确认驱动是否为WinUSB如果不是就替换。如果替换后仍然Access denied检查设备是否被其他进程如串口监视工具打开着。Windows下D2XX驱动和VCP驱动共存时直接调D2XX有时候反而比pyftdi简单因为D2XX有独立的访问通道。5.5 现象SPI读数据时前几个字节是正确的后面的字节全是0原因通常是CS时序问题。FT232H的CS比如ADBUS3在exchange期间是自动控制的但如果你手动控制CS用GPIO去拉低又在SPI读命令发送过程中改变了GPIO状态CS会被错误释放。解决整个exchange期间CS必须保持低。用pyftdi的get_port(cs0)时芯片自动管理CS不要额外去操作GPIO。如果你的代码里混合使用了SpiPort和GpioController注意它们共用同一个ADBUS口切换时CS的状态可能被GPIO操作篡改。要么全交给SpiPort要么彻底不用GPIO不要混着来。6. 进阶用FT232H做Flash烧录器与逻辑分析仪验证的心得6.1 用FT232H给SPI Flash烧录的最小流程很多人拿到FT232H做的第一件正事不是读传感器而是给一颗SPI Flash刷固件。比如路由器、主板上的BIOS芯片用烧录器之前先用FT232H急就章地刷一次能省下买专用烧录器的钱和等待时间。流程不复杂读JEDEC ID确认连接读原片备份擦除芯片0x60或0xC7命令整片写入再读回校验。FT232H在这个场景下的局限是频率不算高、单通道一次只能接一颗芯片但胜在可随时用python脚本在几分钟内完成。我自己的经验是烧录前务必先备份原片至少备份到文件里避免后续救砖没有后悔药。如果Flash容量是16MB用30MHz的SCLK理论擦除加写入加校验大约需要几分钟比几十块钱的专用烧录器慢不少但对偶尔折腾的人已经够用。6.2 当FT232H变成逻辑分析仪用IO采样代替专用分析仪除了当SPI/I2C主机FT232H的bitbang模式还能当简易逻辑分析仪。通过pyftdi的gpio接口以较高的频率采样输入引脚抓一段波形存成CSV再用Python分析时序。这个玩法不追求采样率但对付低频的I2C、UART信号绰绰有余。用它调试新画的板子时我常常先接上FT232H跑一段自己的驱动代码同时用另一个通道采样定位初始化序列到底挂在哪一步。相比上万字节的专用分析仪这个方案够自己折腾了。它的边界也要说清楚FT232H的GPIO采样频率远不如专用逻辑分析仪不能用来抓高速差分信号但它能同时兼顾发送和采样这一点在很多调试场景下比专用工具更方便。用这个方法验证SPI时序时我会把SCLK频率降到500kHz以下确保采样的点数足够多否则波形上只能看到一片糊。说到底FT232H最值得投入的不是它某一个单独的功能而是它把USB和低速总线之间的鸿沟填平了。从GPIO闪烁到SPI Flash烧录再到简易逻辑分析一根USB线加一枚芯片就能覆盖日常大半调试需求。用过一年后我的体会是别贪频率参数先把电平规矩、驱动装对、线接牢靠这块小芯片能陪你走很远。希望帮到你。本文还有配套的精品资源点击获取