☰
STM32+FPGA双核系统架构与实战:从测频到上云
2026/10/8 22:36:22 网站建设 项目流程

你可能也被“STM32+FPGA双核技术系统”这个标题吸引过。刚开始接触这个概念时,我也在想,这到底是硬核炫技还是真有需求?直到我拿到一个四路频率测量、一路MIPI图像采集、步进电机加减速控制、最后还要把数据上报云端的项目,才彻底明白:有些活单靠MCU能凑合,单靠FPGA也能硬扛,但一起做就会特别痛苦。STM32+FPGA双核系统的本质不是两块芯片叠在一起,而是把“会做事”和“能扛事”的两个角色凑成一个高效团队。

这套方案适合谁参考?适合那些已经在用STM32但发现定时器、GPIO翻转速度、外部总线带宽不够用的朋友,也适合刚入门FPGA、想找一个真实项目把状态机、FIFO、UART、LVDS这些模块串起来的新手。今天这篇文章不打算写成论文,只从我自己实际做过的项目出发,把架构、接口、测频、显示、电机、上云这些环节的取舍和坑一次讲清楚。

1. 为什么“STM32+FPGA双核”不是叠buff

1.1 两颗芯片的性格差异

STM32是典型的顺序执行处理器,主频一般几十到几百MHz,跑指令、跑RTOS、跑TCP/IP协议栈是它的强项。它的外设非常丰富,串口、CAN、ADC、DMA、以太网、USB,基本上一颗芯片能搞定一个控制板的全部需求。但它的致命问题在于“时间确定性”:同一个操作,可能因为中断、总线仲裁、Cache命中与否,消耗的时间完全不同。你很难用它去实现ns级别稳定的脉冲输出或高速并行数据采集。

FPGA则是完全另一种性格。它没有“指令流”,所有的逻辑都是并行化的硬件电路。你可以同时跑十个计数器、八个FIFO、两组LVDS差分线,而且每个逻辑单元的处理时延是确定的。但它不适合写复杂协议代码。你要是在FPGA里硬写一个完整MQTT客户端,或者跑文件系统,那个工作量足以让人怀疑人生。

所以STM32+FPGA双核的第一层逻辑,是让两个人干各自擅长的事。STM32负责“干什么”,FPGA负责“怎么干得又快又稳”。这就像项目经理和车间流水线的关系:你给项目经理说“今天出100件货”,他能拆任务、协调资源、处理异常;但真正一个时钟周期出一个结果的重复劳动,必须交给流水线。

1.2 任务怎么切:控制面和数据面分离

我一般把系统分成两个平面:控制面和数据面。控制面包括命令解析、状态管理、通信协议、电机轨迹计算、用户交互,这些全部放在STM32;数据面包括数据采样、等精度测频、边沿捕捉、DDS波形生成、图像行场同步、串行差分接收,这些放在FPGA。

拿我做过的一套测频系统举例。STM32负责接收上位机命令,比如“测1秒、通道A、显示到LCD”,然后把参数打包通过FSMC接口写进FPGA寄存器;FPGA收到配置后立刻启动硬件计数器,等计数结束把结果放在一个双端口RAM里,拉一个中断通知STM32来读。整个过程STM32只做了两次配置和一次读数据,剩下的高频计数全部在FPGA内部跑,测频精度直接取决于参考时钟,而不是CPU中断抖动。

这种切分还有个好处:调试边界清晰。FPGA出问题时,多半是时序或状态机问题;STM32出问题时,多半是协议或外设配置问题。把调试范围缩小,排错效率会显著提升。

1.3 哪些场景值得上双核

不是所有项目都需要STM32+FPGA。普通传感器采集、低速控制,一颗STM32绰绰有余;简单的逻辑替换,一颗FPGA也能应付。真正值得用双核的场景有几个明显特征:需要并行执行多个实时任务,比如同时测频、同时采集图像、同时输出多路PWM;需要高带宽数据传输,比如高速ADC采样后的数据必须在几个时钟内搬走;需要对外设接口做自定义扩展,比如用普通IO模拟MIPI、LVDS或专用传感器时序;需要系统在恶劣电磁环境下稳定运行,FPGA的硬件逻辑比中断驱动的MCU更适合扛干扰。

反之,如果你只是点个灯、读个温湿度,强行上双核就是给自己制造不必要的麻烦。双核系统的成本、功耗、PCB面积、开发难度都是成倍增长的,项目立项前一定要做“加减法”:加法留给确定性计算,减法砍掉不必要的并行扩展。

2. 双核系统架构与关键接口设计

2.1 选型先算账:引脚、资源、带宽

很多朋友一开始就在纠结选哪颗FPGA、哪个STM32型号。我的建议是先算账,后选型,至少算三笔账:引脚数、逻辑资源、通信带宽。

先看引脚数。FPGA要接多少外部信号?摄像头并行数据可能有8~16位,ADC可能有12~16位,多路频率测量要分配3~5个通道,LCD如果是RGB接口还要二十多根线。把这些引脚全部列出来,加上时钟、复位、调试脚,就基本确定了FPGA的最小封装。千万别只看核心逻辑,最后发现引脚不够就尴尬了。系统里那几个IO口,我习惯留出20%的余量,方便调试和后期扩展。

再看逻辑资源。想做DDS波形发生器、等精度测频、UART、FIFO、状态机,这类模块用不了太多资源,入门级FPGA完全够;如果要做图像缓存、MIPI接收、LVDS转并行数据,资源占用就会迅速上升,尤其是Block RAM。图像的一行数据就要几KB,一帧缓存更是几百KB起步,选型前务必估算存储资源。

最后算通信带宽。假设STM32和FPGA之间要在1秒内传一帧320x240的灰度图像,数据量约76KB。用SPI的话,如果SPI跑20MHz,理论带宽只有2.5MB/s,扣除协议开销勉强够用;用8位并口加读写控制,跑30MHz,带宽就有30MB/s,余地充分。高速图像场景我基本不会用SPI,直接上FSMC或自定义并行总线。

接口方式典型带宽优点适用场景
UART0.1~1MB/s简单、抗干扰好低速控制、调试信息
SPI/QSPI2~50MB/s引脚少、速率可调小包数据、LCD控制
8/16位并行10~100MB/s带宽大、时序直观图像、高速采样
FSMC/FMC与STM32总线同步可映射成内存地址大数据量批量存取

2.2 STM32与FPGA之间怎么连才不憋屈

选好接口协议后,硬件连接方式通常有三种:直接GPIO直连、并行总线、差分信号。低速控制命令用GPIO直连就够,一个写使能、加8位数据、加一个中断信号,简单粗暴,也好排查问题。STM32先拉低片选和写信号,把数据放到数据线,然后拉高写使能,FPGA在上升沿采样。这个过程用示波器抓一下就知道有没有问题。

如果数据量一大,GPIO直连会非常痛苦,这时候就要用FSMC/FMC。FSMC能把FPGA映射到STM32的地址空间,STM32直接往变量地址写数据就像操作内部RAM一样。硬件连接上,把FSMC的地址线、数据线、NBL、NOE、NWE连到FPGA,FPGA内部写一个简单的总线从设备逻辑,解析地址和读写信号。这个方案我用了很多年,最直观的好处是STM32代码里不需要显式封装复杂的读写字函数,一句*(volatile uint16_t*)0x60000000 = value;就完成了传输。

对于LVDS这种高速差分信号,就不能用普通GPIO直连了。STM32几乎没有原生LVDS接口,通常由FPGA接收LVDS,转成并行数据后再通过FSMC桥给STM32。这里有个关键点,FPGA的LVDS输入引脚必须做差分端接,常见做法是在PCB上靠近FPGA放置100欧姆差分电阻;如果IO Bank支持内部端接,可以直接配置Internal Termination,省掉外围电阻,但不同系列的FPGA能力不同,用前一定要查Datasheet。

2.3 通信协议和缓存设计

STM32和FPGA之间的通信协议,建议用一个简单可靠的帧格式。我常用的是:帧头2字节、命令字1字节、长度1字节、数据区N字节、CRC校验2字节。MCU发命令给FPGA时,FPGA解析帧头、校验CRC,然后执行;FPGA回传数据时,同样按这个格式组帧。这里有一点要提醒:两条芯片之间的接口容易受干扰,特别是电机驱动、继电器动作时,偶发数据错误概率会高很多。如果没做CRC校验,后期排查会非常痛苦。CRC简单点可以用CRC-16/CCITT,在FPGA里是组合逻辑或者LFSR状态机,资源开销很小。

数据量大的时候,协议之上必须加缓存机制。FPGA侧在收到数据后先放进一个FIFO或双端口RAM,STM32再通过DMA过来搬运,不要每次一个字都靠中断。双缓冲是另一个实用技巧:FPGA将“当前帧”写入Buffer A,同时“下一帧”在Buffer B积累,帧同步信号到来时再切换,避免STM32读到一半数据被更新。这个机制在图像采集和高速ADC项目中尤其重要。

有人会问,STM32里跑FreeRTOS,怎么和这些缓存结构对接?我的做法是定义一个共享数据模型,比如:MeasurementData g_fpgaData; SemaphoreHandle_t g_dataReadySem;。FPGA通过中断通知STM32后,中断服务函数里只GiveSemaphore,真正读取数据的任务在等待到信号量后,从FIFO或双口RAM里拷贝数据。这样中断处理时间极短,既不阻塞系统,又保证数据不会丢。

2.4 时钟、复位与电源设计

双核系统的时钟设计比单芯片系统更容易出问题。STM32和FPGA通常各自用晶振,但两者之间传递数据时,必须考虑跨时钟域。FPGA内部模块经常用到多时钟域,比如系统主时钟50MHz、采样时钟120MHz、串口时钟波特率分频;跨时钟域的信号处理不好,就会出现偶发错码、状态机跳飞。

几个常用的处理办法:单个bit信号用两级或三级同步寄存器打拍;多位数据用异步FIFO或握手协议;对严格要求对齐的场景,可以把STM32侧接口时钟直接引给FPGA做输入,让FPGA采样逻辑使用统一的接口时钟。我自己就吃过跨时钟域的亏,一个标志信号没同步就直接用,结果大约每十分钟出现一次状态机误触发,查了一整天才定位到。从那以后,凡是跨时钟域的握手信号,一律先打三拍。

复位设计也容易被忽略。FPGA上电后不建议直接进入主逻辑,最好用一个“上电复位+按键复位”的组合,复位信号要经过施密特触发器或RC滤波消除毛刺。STM32侧如果是用外部复位,复位时间要足够长,至少满足FPGA配置完成再加几个毫秒,否则FPGA还没加载完毕,STM32就开始初始化外设,两者通信会失败。

电源方面可以考虑先给FPGA供电,保证其完成配置后再让STM32运行。简单做法是两个芯片各用独立LDO或DC-DC,通过使能脚控制时序。STM32和FPGA的IO电平也要匹配:STM32常用3.3V,FPGA也支持3.3V,但LVDS bank通常需要单独的差分参考电压,不容搞混。

3. 核心模块实战:测频、显示、电机、上云

3.1 频率测量:为什么FPGA做得更“稳”

我最常被问到的FPGA项目就是频率测量。STM32本身有定时器的输入捕获模式,测低频信号很简单,比如测量1kHz PWM,用输入捕获配合DWT延时,误差几个Hz完全没问题。但一旦信号频率升到几十MHz,或者要求同时测多路频率,STM32定时器就显得力不从心。这时FPGA用等精度测量法是更好的选择。

等精度测频的思路很简单:设一个基准闸门时间T,比如1秒,在闸门开启期间同时统计被测信号的上升沿个数Nx和基准时钟的上升沿个数Ns。如果基准时钟频率为Fs,那么被测频率Fx = Nx / Ns * Fs。因为闸门开启与待测信号同步,所以被测信号计数没有量化误差,误差只取决于基准时钟。用FPGA实现时,状态机生成一个“闸门开/关”信号,两个计数器分别累加,闸门关闭后锁存计数值,等待STM32读取。

// 简单的等精度测频计数器内核,伪代码风格 reg gate_q, gate_sync; reg [31:0] counter_ref, counter_sig; wire gate_rise = gate_q && !gate_prev; reg [31:0] ref_latched, sig_latched; always @(posedge clk_ref) begin if (counter_ref == TARGET_COUNT) begin counter_ref <= 0; gate_internal <= 1'b0; end else if (start_cmd) begin counter_ref <= 0; gate_internal <= 1'b1; end else if (gate_internal) begin counter_ref <= counter_ref + 1; end // 同步待测信号边沿,再用它做闸门同步 // 实际要注意跨时钟域处理 end

这个模块做完后,STM32侧的工作就很简单了:写寄存器启动测量,轮询中断标志,读完两个计数值再套公式计算。我实测下来,以50MHz参考时钟、1秒闸门,频率分辨率能做到几Hz以内,而且STM32任务切换和中断嵌套完全不影响测量结果。这就是双核系统最真实的收益。

3.2 LCD显示:一次把ILI9341和中文编码问题说透

很多热词里都提到STM32+LCD,尤其是ILI9341读ID问题是A1A1。这块芯片在国产开发板上太常见了,驱动方式有SPI、8位并口、16位并口。我通常用SPI刷显示,因为接线简单,但SPI刷全屏比较慢,适合显示文字和简单图形;如果要做流畅的UI动态刷新,建议还是用并口或FSMC。

ILI9341读ID踩坑很多。正常情况下,用命令0xD3可以读到三个字节,正确结果类似0x00 0x93 0x41;如果你从软件SPI调试串口读出来是0xA1A1,多半不是型号不对,而是MISO配置有问题。软件SPI读数据时,主机必须在发完命令字节后立刻将MISO引脚切换为输入模式,并留出足够的时间让从机驱动数据线;如果MISO一直保持推挽输出模式,或者读数据时选的采样沿不对,ID读回来就是乱码。硬件上MISO线最好加上拉电阻,对读ID这类慢速操作更可靠。

GBK转UTF8也是中文显示的经典问题。STM32内部如果只存UTF8字符串,ILI9341驱动里的中文字库通常以GB2312/GBK编码索引,两者不一致就会显示乱码。有两个解决办法:一是上位机在生成字库时,先把编码统一成GBK,代码里不转码;二是在STM32内做一张转换表,把UTF8字符串转成GBK再查字库。第二种方法灵活但占Flash,我一般只保留常用汉字,转一次就存缓存数组。

FPGA还能配合LCD做更复杂的事,比如用FPGA产生RGB LCD的时序、把STM32写入的显存数据扫描到屏幕上,这能让STM32彻底摆脱刷屏消耗。不过这个方案要占用FPGA逻辑和RAM,属于双核系统里比较进阶的做法。如果你只想把LCD当一个监控面板,STM32+SPI完全够用。

3.3 步进和伺服:STM32算轨迹,FPGA发脉冲

电机控制是“双核分工”最典型的例子。五线四相步进电机可以直接由STM32定时器产生PWM脉冲,但在多轴联动、高频脉冲输出、加减速要求严格的场合,STM32定时器处理会显得很吃力。我用过的方案是:STM32负责插补计算,把每一步的脉冲间隔、方向、步数写进FPGA的寄存器;FPGA内部维护一个脉冲发生器,定时发送脉冲给驱动器,再实时产生方向信号。

FPGA里做脉冲发生器其实很简单:一个计数器,一个比较寄存器。计数器累加到比较值就翻转一次脉冲输出,同时更新下一个步长的比较值。这个比较值就是STM32算出来的当前速度对应的定时周期,速度越快,周期越短。加减速曲线可以预先算好放在STM32的RAM里,运行时逐条写入FPGA。这样即使STM32被中断拖住十几微秒,脉冲输出依然不会有抖动,驱动器就不会因为丢脉冲而失步。

伺服电机通过485控制时,也类似。STM32负责Modbus或自定义485协议栈,FPGA负责监控编码器反馈信号。因为FPGA能在几个时钟周期内捕捉编码器AB相脉冲,位置计数非常准。你可以在FPGA里做一个硬件位置计时器,STM32通过FSMC接口周期性读取当前位置,这样系统响应速度就比单纯靠STM32外部中断计数快得多。有人会质疑FPGA做485会浪费,其实如果只需要发送ASCII字符串,比如状态查询命令,FPGA串口模块也能轻松实现,但完整的PID闭环、伺服参数读写、模式切换这些逻辑,还是STM32更适合。

3.4 物联网网关:FreeRTOS+LwIP+巴法云

在双核系统里,STM32还有另一个大任务:联网。FreeRTOS配LwIP是目前很成熟的方案,再接一个MQTT库就能接入巴法云这类物联网平台。FPGA采集数据不等于系统上云,真正要把数据上报,还需要协议栈、时间戳、异常重传、设备管理等软件层面的东西。

我习惯把整个上报流程拆成几个任务:采集任务从FPGA读取数据,转换成人读得懂的单位;协议任务负责MQTT主题整理、QoS等级设置、心跳包维护;云平台上报任务定时把最新数据发布到主题。FreeRTOS的任务优先级要排好,一般把数据采集任务设为最高优先级,因为它直接影响实时性;MQTT网络任务可以放低一些,LwIP调用不能阻塞其他任务,必要时用消息队列传递数据。

巴法云这类平台接入时,一个常见坑是MQTT连接不稳定。测试阶段直接用IP连没问题,换成域名后就会因为DNS解析失败而掉线。解决办法是不要把域名解析放在任务启动流程的关键路径上,可以先解析一次缓存,业务线程与网络线程分离。在FPGA侧,采集的数据最好先用环形缓冲保留最近几十个采样点,等MQTT恢复后集中补报,这样数据不会完全丢失。

STM32里保存配置数据也需要处理端序和编码。很多时候ASCII和UTF8混着来,比如设备序列号用ASCII,设备名称用UTF8,云平台上显示才会正常。如果全部硬编码,后面改配置非常麻烦。我在这个时候才体会到,双核系统并不是只管FPGA的并行逻辑,STM32侧的任务架构如果没设计好,照样会拖后腿。

3.5 顺便讲个低成本的FPGA信号发生器

信号发生器也是FPGA入门常见的项目,几乎是DDS的教科书级应用。DDS原理不复杂:用一个相位累加器,每个时钟周期加一次频率控制字,高位数作为ROM查找表的地址,ROM里存正弦波一个周期的幅值数据,输出就是正弦波。给定时钟频率Fclk、相位累加器位数N,输出频率fout = fctrl * Fclk / 2^N。

我在系统里把DDS频率控制字交给STM32计算,FPGA只需要实现累加器和ROM查找。STM32收到用户按键或上位机命令,算出fctrl后通过接口写入FPGA,FPGA就能稳定输出对应频率。实测下来,50MHz系统时钟、32位累加器,几Hz以上频率都能稳定输出到外部运放。如果想让输出波形更平滑,可以在FPGA内再加一级Sinc插值滤波器,但波形质量要求不高时,这步可以省掉。

用FPGA做信号发生器有个好处:多路输出、扫频、方波、三角波都能同时生成,而且互不干扰。STM32做参数输入和显示,FPGA做波形生成,正好把双核系统中的“机器语言”和“人机接口”分得明明白白。你要是想在FPGA里用UART发送ASCII字符串给电脑端显示频率值,也不是难事,一个简单的波特率发生器加状态机就能完成,但调试时还是建议优先用串口从STM32打印,省得两头查。

4. 联调、工具链与固件协作

4.1 STM32工程搭建:从CubeMX到VSCode+J-Link

最近几年STM32开发环境变化不小,除了Keil,越来越多团队开始用VSCode+CMake+J-Link。我第一次把工程从Keil迁移到VSCode时,最大的感受是:代码检索和Git对比确实舒服,但环境配置比Keil麻烦很多。核心步骤如下:先用STM32CubeMX生成外设初始化代码,图形化配置FSMC、UART、ADC、GPIO;再用STM32CubeCLT或GCC工具链编译;最后通过VSCode的Cortex-Debug插件连接SEGGER J-Link,配置好device、interface、swd这些参数。

芯片包安装也是一道关卡。CubeMX下载芯片支持包时经常很慢或失败,我习惯直接从ST官网手动下载对应封装的包,然后导入CubeMX信息库。VSCode下调试时,J-Link固件版本和驱动要匹配,有些老J-Link在新驱动下会报“The connected probe appears to be a clone”之类的错误,那大概率是固件太旧,需要用J-Link Commander升级。

有人喜欢直接在工程里写裸机代码,但我建议跑FreeRTOS。双核系统里STM32往往要处理通信、显示、云平台等多个任务,裸机主循环加中断的处理模式很容易变得混乱。FreeRTOS的队列、信号量、任务通知,都特别适合承接FPGA中断触发的事件流。

4.2 FPGA开发流程和几个提升效率的习惯

FPGA开发流程比STM32更依赖工具链。无论是Quartus还是Vivado,从头到尾都要经过设计输入、仿真、综合、布局布线、时序分析、比特流生成、下载调试。新手最容易犯的错是不做仿真直接上板。一个简单的状态机加上跨时钟域信号,仿真不跑,上板查错可能要花十倍时间。ModelSim或者Vivado自带的Simulator都能用,别嫌仿真麻烦。

我在FPGA内部写模块时会严格划分时钟域,每个模块顶层只接收来自同一个时钟域的输入。跨时钟域的FIFO用现成的IP核生成,不要自己手写。组合逻辑块的时序也挺关键,时序不满足时先看关键路径是“寄存器到寄存器”长,还是“引脚到寄存器”长。后者多半是引脚约束或IO标准配置问题,比如LVDS输入没有设置差分端接。

还值得提一下状态机编码。FPGA里状态机可以用独热码或二进制码。独热码每个状态只有一个bit为1,译码逻辑简单,组合逻辑延时小,速度能跑得更高,但触发器用得多;二进制码最省寄存器,适合状态多、逻辑不复杂的场景。我一般优先用独热码,因为FPGA逻辑单元里触发器资源比较充裕,而时序收敛更重要。case语句里必须给默认分支,防止状态机进入非法状态。

4.3 双核联调的Debug三板斧

STM32和FPGA联调,最怕两边都觉得自己没错。我把这几年积累的调试技巧归纳成三板斧:第一板斧是分模块回环测试,第二板斧是抓波形,第三板斧是设计观测点。

分模块回环测试,就是把通信先断开,各自验证自身功能。FPGA单独用SignalTap或ILA抓内部信号,STM32单独用串口打印寄存器数据。两边都确认没问题后,再把FSMC或SPI物理总线接上。联调时优先检验的是CS信号、WR/RD信号、Busy/ACK反馈,而不是上来就看最终数据对不对。时序上有一点偏差,数据可能整体错位。

抓波形要用逻辑分析仪,别只依赖示波器。STM32的FSMC写周期通常只有几十纳秒,示波器单触发抓一次绝对没问题,但想要看波形序列和时序关系,逻辑分析仪更好用。可以把CS、WR、地址线、数据线上的触发条件设为“CS下降沿且WR上升沿出现时”,一次就能抓到完整写周期。

观测点是指代码里的调试追踪点。STM32侧每个关键函数入口出口打串口日志,带上时间戳;FPGA侧把状态机当前状态、FIFO水位、握手信号引到空闲引脚,用逻辑分析仪实时观察。两边的时间戳对应不上时,优先怀疑通信协议和位宽配置,而不是功能逻辑。这套方法几乎能解决我遇到过的所有“偶发”问题。

5. 我踩过的坑,随便挑几个说

5.1 板上信号完整性相关坑

双核系统有高速信号、低速信号、电机电源混合,信号完整性问题特别多。最典型的坑是FPGA的LVDS接收线和普通GPIO挨得太近,差分线没有做阻抗控制,导致高频率时接收误码。解决方法是PCB布线时,LVDS差分对走线要等长、靠近、包地,远离时钟线和开关电源。

另一个坑是FPGA IO口配置成Hysteresis Input Mode。这个模式其实很有用,施密特触发器输入可以增大最小输入电压和最大输入电压之间的门限宽度,对边沿缓慢的信号非常友好。按键、编码器、外部低频方波信号,如果不用这个模式,按键抖动、编码器毛刺都可能被当成有效边沿。但开滞回之后,输入切换点的迟滞会让信号边沿在时间上有一点偏移,测频率或测相位时需要考虑,必要时还是转化为差分或被同步后的信号。

电源噪声也很常见。FPGA核心电压和IO电压分开供电,很多板子为了省钱共用LDO,结果FPGA内部翻转电流一大,STM32和FPGA之间的通信就开始出错。后来我把FPGA内核电源单独用DC-DC或低噪声LDO供电,再用磁珠隔离数字地和模拟地,通信才恢复稳定。双核系统的地平面设计要从一开始就考虑,不能靠飞线解决。

5.2 STM32外设的经典问题

STM32外设坑数起来一大串,这里挑几个高频的。ADC切换通道后第一两次采样值经常异常,这个我在驱动多通道ADC时经常遇到。解决方法是每次切换通道后写入足够长的采样时间,或者连续采样两次丢弃第一次结果。ADC的参考电压也尽量用外部基准,内部VREF受温度影响比较大,会影响测量精度。

CAN通信突然连不上,也是网上问得最多的问题。表面上看总线没问题,代码没变,但就是进不了中断。排查时先看总线电平,CANH和CANL间有2.5V左右的差分电压,再看是否每侧都加了120欧终端电阻。如果总线没问题,查看波特率是否因为系统主频变化产生了误差,CAN波特率容差要求很高,主频即使差一点也可能导致同步失败。项目里我都是先从DBG接口重新烧写程序,在连接稳定时修改配置,不要等出问题再想怎么恢复。

STM32把JTAG引脚复用成普通IO,也是一个隐蔽的坑。PA15、PB3、PB4默认是JTAG调试口,如果你把它们当普通GPIO用,必须在初始化里执行禁用JTAG的配置。一旦禁用,ST-Link/J-Link通过JTAG就再也连不上,只能靠重新上电并保持复位引脚拉低,再尝试连接,或者改用SWD。所以项目里我都保留SWD模式,把JTAG完全关闭,因为这个模式至少还能接两根线调试程序。

5.3 FPGA逻辑设计的几个深坑

FPGA逻辑上的坑,很多跟开发者的经验积累有关。MIPI接收是出了名的难,D-PHY的数据率动辄几百Mbps甚至Gbps级别,普通的FPGA GPIO根本不能直接接收MIPI信号,必须用专用差分IO Bank,配合硬核PHY或外置物理层芯片。用普通IO直怼MIPI,即使是做验证也大概率失败。如果你只是想读取摄像头图像,更稳妥的方案是选自带MIPI接口的FPGA芯片或使用摄像头转并行接口的桥接芯片。

FPGA与STM32之间的跨时钟域,我前面已经说了要打拍和用FIFO,这里再补充一个“异步FIFO深度不够”的坑。FSMC写入数据量大时,FIFO深度不够,很容易发生写满、读空,导致数据错帧。解决办法是:根据最大突发数据量、读写时钟频率比,计算FIFO深度至少要等于“满载传输过程中写入的数据量减去读出的数据量”的最大值,并留出两倍余量。我的经验值是,系统时钟50MHz、接口时钟30MHz、一次突发传输16KB数据,FIFO至少留32KB,否则传输过程中水位告警会频繁触发。

还有独热码状态机时忘记写default分支。这会导致复位时状态未知,进入非法状态后永远跳不回来。虽然综合工具有时会自动处理,但我从不让工具猜,状态枚举里一定添加一种“非法状态”并默认跳回复位态。类似的,case语句用独热码时,信号同时多bit置位的可能性虽然理论上不该存在,但外部干扰或亚稳态会让它发生,所以必须做状态检测和自恢复。

最后聊一个让我印象深刻的“玄学现象”:FPGA里偶尔会出现一个信号看起来物理上该是低电平,却总被读成高电平。原因可能是该信号跨时钟域后没同步,也可能是IO口管脚根本没有物理连接。用逻辑分析仪直接抓FPGA芯片引脚波形是最快的确认方式。逻辑分析仪比仿真器更接近物理真实性,尤其适合排查“理论上不可能但实际发生了”的问题。

我个人在实际操作中的体会是,STM32+FPGA双核系统能不能做出价值,不在于你用多贵的芯片,而在于你愿不愿意在架构阶段多花几天把任务边界、接口时序、协议帧和缓存空间算清楚。每踩一个坑,后面的项目就少踩一次。这套组合非常适合那些想从“单片机能搞定”跨到“高速并行处理也能搞定”的应用场景。如果你正被定时器捕获精度不够、图像刷屏卡顿、多路并行采集不稳定的问题折磨,不妨从这个架构开始试。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询