ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

STM32+FPGA双核系统开发:架构分工、通信选型与调试技巧

STM32+FPGA双核系统开发:架构分工、通信选型与调试技巧 1. 为什么STM32FPGA成了工控与仪器项目里的“双核”标配1.1 单芯片搞不定的两类活协议栈与硬实时并行先说个结论STM32FPGA这套“双核”组合本质不是双CPU而是异构双处理器。一个负责“管”一个负责“干”各干各擅长的事合起来才叫真正的双核技术系统。STM32这类MCU优势在“生态”和“协议栈”。RCU、TCP/IP、USB、SDIO、文件系统、GUI一堆现成库直接调跑个FreeRTOS做任务调度非常顺。你要让它去实时处理几十路高速信号、精确输出纳秒级脉冲它就力不从心了。原因很简单MCU是串行取指执行中断响应再快也架不住高频事件源源不断涌进来更别说还要同时维护通信协议栈。FPGA的优势在“并行”和“时序”。几百个计数器可以同时跑逻辑门延迟决定了处理速度和软件执行完全两个维度。但FPGA做复杂状态机、跑通信协议栈就很痛苦写一个TCP/IP协议栈试试光状态机就能绕晕你。就算塞个软核进去效率也远不如ARM核。所以这两个芯片凑在一起不是叠性能而是补短板。我在实际项目里的直观感受是STM32FPGA技术系统基本覆盖了从传感器采集、实时控制到网络协议上云的完整链路。谁在做这类东西搞仪器仪表、运动控制、机器视觉预处理、物联网边缘网关的工程师几乎都会碰到这个架构。1.2 从搜索热度看大家的真实痛点我整理了一下去年年底到今年大家搜得多的关键词很有参考价值FPGA侧的频率测量、串口发送ASCII字符串、图像处理、LVDS接收、MIPI接口、进位链TDC测量时间、信号发生器、串口升级QSPI。STM32侧的定时器捕获测频率、ADC切换通道、GBK转UTF8、巴法云、FreeRTOS物联网网关、LWIP协议栈、CAN通信连不上、ILI9341读ID是a1a1、DWT、LD文件、J-Link下载环境。看出来了吗这些需求单独看都很零散但拼起来其实就是一套典型的“STM32FPGA”系统零件FPGA负责高速采集和接口转换STM32负责协议处理和上云。搜FPGA频率测量的人很可能正在做一台需要STM32读取测量结果的频率计搜LVDS接收和MIPI的人多半在给图像传感器做数据搬运搬完还是要交给ARM核跑算法或通讯。这篇文章就是按这套思路拆解的。不管你拿FPGA做数据采集、接口转换还是实时控制也不管你用STM32做网关、显示还是通讯下面这些设计取舍和调试链路应该都能对得上。2. 双核系统的架构分工先分活再选通信2.1 任务划分的几条硬规则不少初学者拿到STM32FPGA的开发板第一反应是“FPGA是不是抢STM32的活我全用STM32行不行”这个问题问得挺好答案也简单先看你有没有硬实时需求。我给自己定过几条任务划分规则用了几年还没出过错**规则一超过几十kHz的周期性信号处理交给FPGA。**比如PWM输出20kHz以上的多路方波、高速ADC采样、编码器正交解码STM32做起来要么占用大量CPU时间要么精度上不去。**规则二复杂但低频的状态逻辑留在STM32。**比如人机交互界面、报警状态机、参数配置菜单、云端指令解析这种活让FPGA做纯属折磨。**规则三对外通信协议统一由STM32接管。**串口、CAN、Ethernet、MQTT、HTTP都放STM32。理由很简单MCU生态里有现成协议栈FPGA写协议栈费时费力还容易出bug。**规则四硬实时闭环控制放FPGA。**比如电流环、位置环、需要固定延时的信号链一般放在FPGA里跑保证时钟级确定性。STM32顶多算“闭环下位机”负责参数给定和状态上报。搜“STM32控制伺服电机485”这类需求本质就是STM32做主控下发参数FPGA做高速插补或脉冲输出。总结成一句话FPGA管“每个时钟周期都在发生的细节”STM32管“需要长期权衡的事情和对外交流”。这套分工定下来系统架构就自动清晰了。2.2 双核之间的通信选型FSMC、SPI、UART怎么取舍分完活接下来最关键的是STM32和FPGA之间怎么通信。通信带宽直接决定你能把多少数据从FPGA搬到STM32也决定延迟。常用三种方案直接上对比表格通信方式典型速率特点适合场景FSMC/可变静态存储控制器并行总线几十MB/s以上受IO口和时序制约STM32把FPGA映射成外部SRAM读写像操作内存一样简单占用IO多高速ADC采集、图像数据搬运、大块数据块交换SPI从机模式1-10Mbps左右取决于分频和从机能力引脚少实现简单适合中低速数据块温度采样、状态寄存器轮询、配置命令下发UART115200bps-1Mbps最简单但速率低传输有波特率容错问题调试链路、低频率状态上报我从项目实操角度说点文档里不太会写的体会FSMC这个名字听起来高大上其实就是STM32片内外设的一个接口控制器可以把外部器件当作内存来访问。FPGA端只需要实现一个“异步SRAM读写的状态机”STM32这边配好时序参数接下来用*(uint16_t *)0x64000000 data;这么一行代码就能写数据到FPGA。反过来读也一样。这是速度最快、代码最干净的方案。缺点也明显要占用GPIO一大片对PCB布线要求高。SPI是我个人推荐的“起步方案”。STM32做主机FPGA做从机两根数据线加一根时钟线一版就能通。很多项目一辈子用SPI就够了没必要一上来就搞FSMC。需要注意SPI从机模式下FPGA的响应时机必须足够快否则主机拉低片选后等不到数据就直接读超时。早期建议在FPGA里做一个简单的8位寄存器阵列把配置参数、状态标志、测量结果都映射成寄存器STM32通过SPI读写寄存器逻辑清晰也不容易错。UART只建议用在调试链路上。有个隐蔽的坑两边都是“近似波特率”哪怕误差在±1%以内长时间连续收发大数据包也可能偶发出错。所以UART帧格式里一定要带校验一旦收到错误帧就重发别裸奔。2.3 握手协议与数据帧格式设计通信物理链路选好了还得定一套双方都认的数据帧格式。我踩过的最痛的一个坑就是前期图省事直接定义了一个固定长度的结构体数组结果需求一改双方同步改代码改到后面版本错乱简直无法维护。后来我学乖了所有通信都走“统一帧格式”用最朴素的方案帧头(0xA5) | 命令字 | 数据长度 | 数据区 | 校验和帧头1字节命令字1字节长度2字节数据区最多4096字节校验和用简单的累加即可。FPGA收到帧后先解析帧头和长度再接收数据区并计算校验校验通过就执行命令不通过直接丢弃并回报错误标志。为什么这么做因为STM32和FPGA两边是不同的开发工具链调试时经常一边在改Verilog一边在改C代码。有了统一帧格式两边各自维护状态机只要遵守帧格式这条“契约”各自内部怎么改都不影响对面。这里再分享一个高级技巧可以把FPGA内部所有寄存器设计成“地址映射表”STM32读写一个寄存器地址FPGA返回对应的状态/数值。就像你访问一条内存地址一样调试和维护都极其方便。尤其是搜“STM32 ADC切换通道”或者“FPGA信号发生器”这类项目有了寄存器映射STM32改频率、切换通道、读测量结果全部变成对某个地址的读/写操作代码可读性提升一个等级。3. FPGA端三个值得练手的模块从频率测量到高速接口3.1 频率测量等精度测量原理与实现要点先说一个热词“FPGA实现频率测量”这在项目里几乎必做也是最容易理解FPGA并行优势的练手模块。频率测量主要有三种思路测频法在固定闸门时间内数被测信号的脉冲个数频率脉冲数/时间。适合测高频低频测不准。测周法测量被测信号一个周期内的高频基准时钟个数频率基准频率/计数。适合测低频高频反而测不准。等精度测量法把测频法的闸门设计成“跟随被测信号边沿”的同步闸门。闸门开启和关闭都自动对齐被测信号的边沿同时计数被测脉冲数和基准时钟数无论高频率低频率相对误差都一样而且只取决于基准时钟的准确度。FPGA实现等精度测量非常自然因为里面对被测信号和基准时钟各有一个计数器两者并行跑闸门信号一拉高就开始计闸门结束同时锁存两个计数器的值。这个“同时锁存”是FPGA的天然能力换成MCU需要精确的中断配合很难做到。伪代码层面长这样// 等精度频率计核心逻辑简化 always (posedge clk_100m) begin gate_sync test_signal ? 1 : 0; // 闸门同步到被测信号 end // 基准计数器和被测计数器 always (posedge clk_100m) begin if (gate_sync !gate_sync_prev) cnt_ref 0; // 闸门打开开始计数 else if (gate_sync) cnt_ref cnt_ref 1; end // 频率结果 被测计数 / 基准计数 * 基准频率实际项目里还要加几个细节多次测量取平均消抖动、自动量程切换、防止计数溢出。代码本身不难难的是理解“同时计数”背后对时序的要求。我见过不少初学者把这个逻辑写成先数被测信号再数基准信号那就完全没有并行意义了。你要想清楚FPGA让你能同时干两件事这才是价值所在。3.2 串口发送ASCII字符串一个看似简单但容易踩坑的模块搜“FPGA实现串口发送ASCII字符串”特别多原因很实在FPGA需要把测量结果发出去最简单的就是通过串口用ASCII码发出数字和字符。但真写起来发现一堆细节。UART发送模块的核心是波特率发生器。用一个计数器分频出波特率时钟比如100MHz系统时钟要发115200bps计数器分频系数大约是868在每个波特率时钟沿按位发送。发送一帧的顺序是起始位08位数据低位在前停止位1。这就是基础。容易踩的坑有三个第一个坑是字符串状态机。你设计了一个“发送字符串”的模块但字符串不是一个字节你要用一个状态机控制数据选择、总线占用、字节间时间间隔。如果状态机写得不仔细很容易出现字节之间间隔太短或太长的问题。间隔太短接收方跟不上间隔太长又拉低整体效率。第二个坑是ASCII转换。FPGA里没有sprintf这个函数你要自己把二进制测量结果转换成十进制的一串ASCII码再发送。可以写一个“除10取余”的转换状态机先用组合逻辑做除法取余再查表变成字符。这个过程占资源不大但是初学者容易写错顺序。我自己习惯先在仿真里验证一遍转换结果再上板。第三个坑是不要为了追求简洁而把发送逻辑全塞进一个always块。拆分模块是FPGA好习惯波特率发生器一个模块ASCII转换一个模块发送状态机一个模块。每个模块单独测试出问题只改一小块。很多FPGA项目后期的难易程度完全取决于前期模块切分是否合理。3.3 进阶TDC时间测量、LVDS接收与MIPI接口为什么只能靠FPGA如果频率测量和串口让你打好了基础那该聊聊真正让FPGA不可替代的东西了。搜“FPGA进位链TDC测量时间”“FPGA的LVDS接收”“FPGA实现MIPI”这些才是双核系统里FPGA的核心价值所在。TDC即时间数字转换器简单说就是测量一个时间间隔有多长。用MCU实现分辨率基本在微秒级别靠定时器捕获。而FPGA里可以利用进位链的延迟时间做TDC分辨率能达到几十皮秒甚至更高。原理很简单信号通过FPGA内部的进位链CARRY4传播每一级延迟在皮秒量级用D触发器把传播结果锁存翻译成二进制时间值就能测出信号到达的精确时刻。搜“使用FPGA进位链TDC测量时间”的人通常是在做激光雷达、超声测距、粒子物理实验这类对时间精度要求极高的项目用MCU根本不可能实现。LVDS接收属于高速串行接口。LVDS信号是差分对速率经常上百Mbps甚至更高需要用FPGA的专用收发引脚和高速时钟管理模块去做数据采样恢复。STM32的IO基本不支持LVDS这种电平标准就算外接转接芯片数据率也跟不上。所以只要你的系统需要接高速ADC、LVDS摄像头或高速背板总线FPGA几乎是唯一选择。MIPI接口更典型。MIPI是移动设备摄像头和显示屏常用的高速串行接口D-PHY速率经常到Gbps级别。FPGA需要做lane对齐、时钟恢复、数据字节打包然后才把图像数据交给STM32做处理或显示。这块逻辑复杂时序要求高但只要你跟着官方例程做一次D-PHY接收对FPGA高速设计的理解会立马上一个台阶。我做这些模块的一个重要心得是高速接口设计优先看官方IP核和参考设计不要自己硬造轮子。Xilinx有MIPI CSI-2 IP核Altera有LVDS IP核先把官方demo跑通再逐步改成自己需要的接口参数能省掉至少两周的调试时间。等你有经验了再去手写协议逻辑也不迟。4. STM32端实现细节从HAL库到物联网网关4.1 时钟树、DWT和定时器捕获测频率/测脉宽的MCU方案把FPGA分了那么多活STM32这边也不是没事干。搜“STM32定时器捕获测频率”“STM32 DWT”这类关键词的人说明很多人其实想在MCU侧直接测频率或者用DWT做精确计时。STM32定时器捕获的原理是输入引脚检测到上升沿/下降沿时定时器计数器的当前值被自动锁存到捕获寄存器同时触发中断。连续两次捕获的计数值之差乘以定时器时钟周期就是信号周期。测量低频信号很准高频信号会由于中断响应时间抖动产生误差。实操时HAL库写法很简洁// 初始化定时器输入捕获以TIM2为例 TIM_IC_InitTypeDef sIC; sIC.ICPolarity TIM_ICPOLARITY_RISING; // 上升沿捕获 sIC.ICSelection TIM_ICSELECTION_DIRECTTI; sIC.ICPrescaler TIM_ICPSC_DIV1; sIC.ICFilter 0; // 滤波可设为2-15以抗毛刺 HAL_TIM_IC_ConfigChannel(htim2, sIC, TIM_CHANNEL_1); HAL_TIM_IC_Start_IT(htim2, TIM_CHANNEL_1);然后在中断回调里读取两次捕获值求差即可。注意一个陷阱如果信号频率很低定时器可能溢出需要加一个溢出计数器把溢出也计入周期。很多初学者漏掉这点低频信号测出来是错的。DWT则是一个容易被低估的工具。Cortex-M3/M4内核自带Data Watchpoint and Trace单元其中有一个32位周期计数器频率等于内核时钟。用一行代码就能启动CoreDebug-DEMCR | CoreDebug_DEMCR_TRCENA_Msk; DWT-CYCCNT 0; DWT-CTRL | DWT_CTRL_CYCCNTENA_Msk;之后读DWT-CYCCNT就可以获得精确到周期的计时。我常用它来进行代码片段耗时统计和分析比写一堆GPIO翻转测量准得多。做双核系统联调时用DWT测STM32读写FPGA某个寄存器整个流程耗时定位瓶颈非常有效。这里顺便回应一个热词“STM32 HAL库下载”。HAL库其实就在STM32CubeMX的安装包里不需要单独下载。用CubeMX生成工程初始化代码你会发现外设配置工作量直接减半。唯一的建议是生成代码后自己手动添加的逻辑块和生成区之间做主备版本管理避免CubeMX重新生成把代码冲掉。4.2 ADC多通道切换与DMA以及ILI9341读ID为a1a1这类“玄学”问题ADC多通道切换是STM32常见的需求。用HAL库写ADC时如果你的扫描模式没有正确配置切换通道后第一次采样值常常不准这是因为通道切换后需要稳定时间。我当时解决方法是把每轮采样的第一次结果丢弃从第二次开始使用或者干脆用DMA一直在循环采样DMA缓冲区里的前几个数据直接忽略。不要追求“一上电就采到完美值”工程上做“过滤掉暂态样本”反而是最省事的。再吐槽一个经典问题STM32使用ILI9341读ID返回a1a1。搜这个词的人基本都被这个坑绊过。a1a1这个值其实不是芯片坏了而是时序没配对或者ILI9341的ID寄存器读取方式不对。正常ILI9341读ID应该返回0x93如果你读回来全是0xA1A1多半是你在发“读ID命令”前没有先进入SPI正确的读写模式或者时钟相位极性配错了。换到双核系统里这种“读回来永远是同一串值”的现象同样常见——比如STM32通过SPI读FPGA寄存器如果两边时钟极性或相位不一致读回来的数据永远是0xFF或0x00看起来像“玄学”实际就是协议细节没对齐。解决这类问题的通用思路用逻辑分析仪抓SPI的CS、SCK、MOSI、MISO四根线对比数据手册时序图。比起漫无目的地改软件一次波形分析比什么都管用。4.3 FreeRTOSLWIP巴法云STM32端作为网关的实现思路双核系统里STM32还有个重要身份物联网网关。搜“FreeRTOS STM32物联网网关”“STM32网关lwip协议栈”“STM32 巴法云”的用户就是要把采集到的数据送上云同时接收云端的控制指令。我的标准框架是FreeRTOS做任务调度LWIP协议栈跑网络巴法云或者自建MQTT服务器做上云通道。整体任务划分可以做成三四个任务数据采集任务定期从FPGA那边读测量结果存到共享区。网络任务运行MQTT客户端周期发布数据到主题。控制任务订阅云端主题收到指令后解析成参数再通过SPI/FSMC下发FPGA执行。任务与任务之间用FreeRTOS队列或者信号量传递避免裸机循环带来的CPU浪费。这也是STM32相对于FPGA最大的优势所在你有完整的RTOS生态写网络应用和云端对接几乎都是搭积木。这里提醒一个和云端交互直接相关的细节如果你设备需要处理云端下发的中文消息就绕不开编码转换。搜“STM32 GBK转UTF8”的人多半是显示用户名称或中文指令时出现了乱码。云端消息基本都是UTF-8编码而很多串口屏或文件系统用的是GBK。你没做转换就显示必然乱码。实现思路很简单先查表记住GBK和UTF-8的对应关系按字节流判判断当前是ASCII还是双字节中文再做映射。这个表比较大可以放到外部Flash或SD卡里按需加载。5. 联调阶段最容易翻车的地方与排查链路5.1 电平与IO标准3.3V对3.3V也可能出问题很多人以为STM32和FPGA都是3.3V电平直接连就行了。实际上两个芯片的IO电气特性本质上都是3.3V但驱动能力和灌电流能力差别很大。如果FPGA的IO管脚配置成1.8V的Bank电压而你直接和3.3V的STM32引脚相连电流灌入可能导致芯片锁死甚至烧毁。所以联调第一件事确认FPGA各个Bank的VCCO电压和STM32的电平标准匹配。FPGA里像LVDS这类高速接口需要专用差分引脚切记不能接到普通IO上那些普通IO不支持LVDS电气标准。还有一点开发板上FPGA和STM32经常分布在两个区域走线经过排针或转接板。一旦信号质量不佳现象就是“时好时坏”“高温后频繁出错”。排查方式很简单用示波器看波形上升沿是否光滑有没有回沟。看到回沟优先怀疑信号完整性问题而不是代码问题。5.2 时序违例与毛刺用示波器和逻辑分析仪复现一次真实排查我印象最深的一次联调问题现象是STM32读FPGA的数据偶尔会读到FF程序里怎么查都查不出原因。后来抓了CS信号和数据信号的波形发现FPGA发出的数据在CS拉低的时候还没有完全稳定STM32在CS下降沿一采就采到了中间状态。这个问题的根因是FPGA端输出的建立时间不够。解决方式三种调整STM32侧的读写时序参数在CubeMX的时序配置里把地址建立时间和数据建立时间适当放宽。FPGA侧对输出数据打一拍也就是加一个寄存器延迟输出让数据在CS有效前就已经稳定。换用更慢的时钟或者下降沿采样。排查链路清晰的话这类问题不超过半天就能定位。最怕的是直接怀疑“是不是硬件坏了”换芯片重焊问题依旧然后又回到软件死循环。所以我强烈建议双核联调时示波器和逻辑分析仪必须配合使用尤其是看协议总线的时序一个逻辑分析仪能省几倍的时间。5.3 从“STM32 CAN通信突然连不上”延伸的通信故障排查思想搜“STM32 CAN通信突然连不上”这项也很热。CAN通信出问题的常见原因有波特率配置不匹配、总线缺少终端电阻、某个节点错误率过高导致总线被动关闭。排查顺序通常也是先量总线波形再查配置再查节点状态寄存器。我把这套排查思想推广到双核系统里特别适合任何莫名其妙的通信故障第一步隔离层。先把双核通信断开STM32自发自回环测试FPGA用仿真激励测试分别确认两端自身是否正常。第二步波形层。用示波器或逻辑分析仪抓通信引脚的电气波形确认电平、时序、握手信号是否和协议一致。第三步协议层。加打印或者把FPGA收到的字节流回传确认帧头、长度、校验每个字段是否变换。第四步环境层。排除干扰源检查供电稳定性确认没有地电位差。这条排查链路我用了很多年几乎通吃所有双核通信问题。而不是一上来就盯着代码反复看很多问题根本不在代码里。6. 项目落地后的改进空间与我的个人体会6.1 可扩展方向从双核到更复杂的异构系统STM32FPGA双核架构用熟了以后你可以自然往两个方向扩展。一是往“软核SoC”方向走。现在主流FPGA都支持在内部例化ARM软核或硬核处理器比如Zynq系列就是ARM核与FPGA在同一个芯片内通信带宽远高于外部并口。如果你的项目需要更高速率的数据交换比如图像处理流水线每秒传输几百MB可以考虑直接上Zynq这类SoC FPGA。STM32FPGA是入门和中等成本的王道Zynq则是高性能扩展。二是往“多核分工”方向走。在双核基础上再加一颗DSP做音频或振动信号处理或者加一颗NPU做AI推理FPGA仍然充当前端的接口汇聚和时序控制角色。搜“FPGA图像处理”和“FPGA创新设计大赛选题”的人很多都会走向这种综合异构架构。6.2 我做过的几个坑位总结与给新手的建议最后说说我个人实际操作中的体会。如果让我给刚接触STM32FPGA双核系统的人提几条建议第一条是先把链路跑通再追求性能。很多人一上来就设计高深的DMA和并行总线结果基础通信还没通反而怀疑人生。我建议第一个demo就用UART或者SPIFPGA返回一个固定寄存器值STM32读到就表示通信成功。在这个基础上再逐步增加功能效率会高很多。第二条是FPGA逻辑一定要做仿真。哪怕是一个状态机也值得在仿真里把波形看清楚了再上板。FPGA调试难度远高于MCU因为内部信号你看不到而仿真能让你看到一切。我见过太多人直接上板信号出问题又不知道内部状态走到哪瞎猜半天。第三条是想清楚“哪些数据必须实时哪些可以缓存”。双核系统的性能瓶颈经常不在芯片本身而在通信链路的带宽和延迟。一开始就把数据流分类高频实时数据在FPGA内部就地处理只把结果传给STM32低频状态数据才走完整帧协议。这个设计的良好与否直接决定系统最终性能。最后一条版本管理一定从一开始就做。STM32代码、FPGA工程、引脚约束文件、通信协议版本全部纳入版本管理。两个芯片的项目最怕的就是改了FPGA工程忘改STM32工程然后莫名其妙花了三天找“bug”结果发现协议字段错位了。别问我怎么知道的这个坑我摔过不止一次。
返回列表