资讯动态

Actel FPGA串口通信入门:从UART协议到Libero SoC上板调试全解析

发布时间:2026/9/7 5:23:23 来源:尧图企业网站定制
简介这是一份面向初学者的Actel SmartFusion FPGA串口通信例程配套完整的工程源码与固件代码帮助学习者快速掌握混合信号片上系统中UART模块的设计与调试方法。资源包共包含842个文件压缩后约4.72兆字节内有工程数据库文件dbs/dat、工程配置文档prw/psm、Verilog/VHDL源码vhd/v、C语言固件c/h以及仿真与脚本文件bfm/tcl并附带文本说明与日志覆盖从FPGA逻辑设计到嵌入式处理器固件开发的主要环节。目前已有227人学习下载适合需要从零搭建此类串口项目的工程师或相关专业学生。通过学习该例程可以理解UART的数据帧格式、波特率时钟配置、收发移位寄存器与状态机设计并参考完整的HDL模块、固件初始化流程和中断处理代码还可以学到仿真测试平台的搭建方法获得仿真验证与实际调试的清晰思路。 在嵌入式开发圈子里FPGA 的串口通信总给人一种“既简单又不简单”的感觉。说它简单是因为 UART 协议本身只有几根线、几个字节的时序说它不简单是因为一旦换到 Actel现在叫 Microchip / Microsemi这个体系里光是把工程跑起来、把管脚约束对、把时钟配明白就够新手折腾一整天。我最初从 Xilinx 转过来用 Actel 的板子时就踩过不少坑尤其是串口这种基础外设工具链一变很多以往想当然的操作全都要重新适应。这篇内容就是基于 Actel FPGA 的串口例程把从建工程、写 UART 收发逻辑、分配管脚到上板调试的整个过程完整拆开来看。文章既适合刚接触 FPGA 的初学者也适合那些之前只用过 Vivado 或 Quartus、想快速上手 Libero SoC 的老工程师。我自己实测下来的感受是一旦把串口例程从零跑通后面再碰 Actel 的其它外设和逻辑就顺多了它几乎是进入这个系列最好的敲门砖。1. 先从整体思路说起为什么选 Actel、为什么串口是入门第一课1.1 Actel 这个系列的特别之处Actel FPGA 最大的特点是基于 Flash 架构而不是 SRAM 架构。这个差异带来的好处非常实际芯片上电后配置立即生效不需要外挂 SPI Flash 来加载比特流天然具备更可靠的上电启动能力。在军工、航空航天、工业控制这类对安全性和稳定性要求极高的场景里这一点很关键。另外Actel 的 FPGA 在低功耗方面表现也相当不错。它的静态功耗通常比同等规模的 SRAM 型 FPGA 更低加上内部集成了一些安全特性比如 Secure Boot、防篡改、反克隆所以在很多关键设备上都能看到它的身影。不过这也在开发层面带来一个门槛你不能用 Xilinx 或者 Intel 的开发思路直接套过来设计流程、工程结构、约束方式都有差异。我当时入手的是 SmartFusion2 系列的板子严格来说它内部还有 ARM Cortex-M3 硬核但我的目标是先用纯 FPGA 逻辑把串口收发的通路跑通不依赖硬核。这样既能验证自己对 Actel 架构的理解也为以后写纯逻辑 IP 打下基础。1.2 串口在 FPGA 学习中的特殊地位UART 串口几乎是所有外设通信协议里最容易理解的协议之一。它没有时钟线也没有复杂的仲裁机制物理上就是 TX、RX 两根线加上共地就能完成点对点通信。正因为足够简单它非常适合作为 FPGA 入门的“第一课”需要你理解时钟分频需要你写状态机需要你处理异步信号的同步这几个能力恰好就是 FPGA 开发最核心的基本功。串口例程的典型场景是FPGA 通过 USB 转串口模块连接到电脑用户在电脑上用串口调试助手发送一个字节FPGA 接收后原样返回也就是回环测试再把接收到的数据在调试助手窗口显示出来。这个例程一旦跑通说明时钟、复位、状态机、管脚约束、烧录流程、上位机联调这条链路全都是通的。反过来如果这些环节中哪一步有问题串口就是最好的排查信号因为只要有一根线上不对数据就收不到。2. 串口要打通核心细节必须吃透2.1 UART 协议和帧格式的直观理解UART 每次传输以一帧为最小单元。最通用的帧格式是 8-N-1也就是 8 个数据位、无校验位、1 个停止位。一帧数据的顺序是空闲时 TX 保持高电平要发送数据时先拉低一个位时间表示起始位随后依次送出数据位低位在前最后拉高一个位时间作为停止位。理解了这帧结构写 Verilog 时思路就会非常清晰。发送逻辑就是按时序要求把电平拉高拉低接收逻辑则是从 RX 线上采集电平并按位拼接。我用一句话给新手解释串口通信的本质就是“脱同步的约定式通信”。双方并不共享时钟靠的是预先约定好的波特率也就是每秒传送多少位。为了降低误差接收端通常还要在一位数据的中间时刻进行采样防止采样点靠近跳变沿造成误判。2.2 波特率分频系数到底怎么算波特率分频是串口逻辑里最基础也最容易出错的一环。分频系数取决于两个东西系统时钟频率和目标波特率。如果采用 16 倍过采样也就是每个数据位内部采样 16 次那么分频数的计算方法是分频系数 系统时钟频率 / (波特率 × 16)举个实际例子系统时钟 50MHz目标波特率 115200。那么分频系数 50_000_000 / (115200 × 16)结果大约是 27.126。由于分频系数只能是整数取 27 的话实际波特率为 50_000_000 / (27 × 16) ≈ 115740误差大约是 0.47%这在串口通信标准中是完全可以接受的。再比如 25MHz 时钟、9600 波特率分频系数 25_000_000 / (9600 × 16) ≈ 162.76取 163实际波特率约 9585误差约 0.16%也没问题。实际操作中我给自己的建议是优先选择 115200 波特率因为它的整数分频在常见时钟频率下通常误差更小而且调试助手配置也方便。下面是几个常用组合的参考表系统时钟频率目标波特率16倍过采样分频系数实际波特率误差50MHz960032695880.13%50MHz115200271157400.47%25MHz960016395850.16%25MHz115200141116073.1%25MHz 配 115200 的误差就太大了这种情况建议换 57600 或者用 PLL 把时钟提上去。记住一个原则串口误差最好控制在 2% 以内否则长时间的帧传输累积极易出错。2.3 Libero SoC 工具链的独特点Actel 系列的开发工具是 Libero SoC它和 Vivado、Quartus 的使用逻辑有些类似但工程管理方式差别不小。首次打开 Libero 需要申请 License虽然免费版的功能对多数学习场景够用但同步工程、IP 管理等环节需要花时间适应。我用 Libero 遇到的第一个坑是新建工程时芯片型号必须选对否则综合、布局布线都会报错。第二个坑是Actel 的顶层设计和管脚约束通常通过 HDL 文件先确定端口列表然后再在 Pin Assignment 视图里分配具体引脚。如果直接照搬 Xilinx 的思维去写 XDC 约束反而会绕弯路。还有一点特别重要Actel 的时钟资源分配和全局缓冲处理跟 Xilinx 不一样。自己在代码中描述的 clk 信号在综合后通常需要手动确认它被分配到了全局时钟网络GLB。如果时钟跑在普通 I/O 上时序会很差串口这种低速逻辑虽然能工作但后续复杂设计肯定不行。3. 实操过程从新建工程到串口收发数据3.1 顶层模块设计与引脚分配我的思路是做一个可复用的串口回环模块顶层模块例化 uart_tx 和 uart_rx 两个子模块接收端收到一个字节后通过一个握手信号交给发送端送出。再加上一个计数器驱动的 LED 闪烁用来直观确认 FPGA 已正常工作。顶层代码结构大致如下module top( input wire clk, input wire rst_n, input wire uart_rxd, output wire uart_txd, output wire led ); wire rx_done; wire [7:0] rx_data; reg [7:0] tx_data; reg tx_start; wire tx_busy; always (posedge clk or negedge rst_n) begin if (!rst_n) begin tx_data 8d0; tx_start 1b0; end else if (rx_done) begin tx_data rx_data; tx_start 1b1; end else begin tx_start 1b0; end end uart_rx #( .CLK_FREQ(50_000_000), .BAUD_RATE(115200) ) u_uart_rx ( .clk(clk), .rst_n(rst_n), .rx_pin(uart_rxd), .rx_data(rx_data), .rx_done(rx_done) ); uart_tx #( .CLK_FREQ(50_000_000), .BAUD_RATE(115200) ) u_uart_tx ( .clk(clk), .rst_n(rst_n), .tx_data(tx_data), .tx_start(tx_start), .tx_pin(uart_txd), .tx_busy(tx_busy) ); reg [25:0] led_cnt; always (posedge clk or negedge rst_n) begin if (!rst_n) led_cnt 26d0; else led_cnt led_cnt 1b1; end assign led led_cnt[25]; endmodule这里把参数放在模块例化处直接传入是为了适应不同板卡的时钟频率。我的板载时钟是 50MHz如果你用的是 25MHz就把顶层里的 CLK_FREQ 改成 25000000不用动底层代码。这个设计思路建议大家保留以后换板子调试能省不少事。拿到板子之后管脚分配必须小心。一般开发板原理图上会标注 USB 转串口的 RXD、TXD 接到 FPGA 的哪个引脚。注意命名里面 FPGA 的 RXD 接到的是板上串口芯片的 TXD 输出所以顶层里 uart_rxd 就是 FPGA 的输入引脚正好对应串口芯片发出的数据。3.2 uart_tx 发送模块的实现发送模块的核心是状态机。我习惯用四段式写法IDLE、START、DATA、STOP。空闲时tx_pin 保持高电平只要收到 tx_start 信号状态机就进入 START 状态把 tx_pin 拉低一个位时间然后在 DATA 状态按位发送字节每发送一位都要等待一个 bit_time最后 STOP 状态拉高一个位时间。关键的一点是位时间的计算。我用了参数化的常量localparam CLK_DIV CLK_FREQ / BAUD_RATE;这个参数可以直接算出每个位需要多少个系统时钟周期。发送一个字节总共需要 1起始位 8数据位 1停止位个位时间。如果加到校验位就再增加一位。8-N-1 配置下一帧是 10 个位时间。实测中最常出的问题是发送时序中的毛刺。如果状态机里的状态跳变没有同步到统一的字节计数上可能在位中间发生端口电平抖动。解决办法是在每个状态内部都用一个位时钟计数器只有计数完成才允许跳转到下一个状态避免时序竞争。3.3 uart_rx 接收模块的实现接收端比发送端复杂一些因为 RX 信号来自外部和本地时钟没有任何相位关系。设计时最常见的做法是先对这个异步信号做两级触发器同步消除亚稳态然后检测下降沿来识别起始位的到来。我习惯用 16 倍过采样方式接收把一位时间平均分成 16 份在每一位的中间也就是第 8 个采样点读取 RX 电平。这样做的好处是即使波特率有点误差采样点也不会轻易跑到数据位的边界上稳定性明显更好。reg [3:0] sample_cnt; reg [3:0] bit_cnt; reg [7:0] rx_shift;当检测到起始位后sample_cnt 从 0 计数到 15每当计数到 8 时取一次 RX 线电平。由于起始位已经确认是低电平数据位会在后续第 1 到第 8 个采样窗口内依次落入 rx_shift 寄存器。全部完成后拉高 rx_done同时把 rx_data 输出给顶层。接收模块里我特别强调一个细节起始位检测不能只看一个下降沿。如果 RX 线上出现毛刺或者干扰会误判成起始位导致整帧接收错位。更稳妥的做法是下降沿出现后等待半个位时间再采样一次如果依然是低电平才认定为真正的起始位。这个小改动在实际调试中非常有用尤其当你身边有电机、继电器这类强干扰源时。3.4 上板验证与回环测试代码写完、管脚分配好之后就是综合、布局布线、生成比特流、烧录。这个流程在不同工具里表现不一样Libero SoC 的步骤相对繁琐但每一步都有日志提示。我实测下来比较顺利的流程是先跑 Synthesis看有没有语法或综合错误然后跑 Place and Route确认没有时序违规最后 Generate Bitstream通过 FlashPro 工具下载到板子。完成之后打开串口调试助手选择正确的 COM 口波特率设为 115200数据位 8、停止位 1、无校验然后发送一个“A”观察调试窗口是否收到“A”。如果收到说明整个链路已经通了。如果没收到别急着改代码先看看 USB 转串口芯片的驱动是否装好COM 口号是否被其他软件占用连线是否正常。很多时候问题并不在 FPGA而在上位机环境。4. 常见问题与排查技巧实录4.1 完全收不到数据问题可能出在哪这个问题我遇到不止一次每次排查顺序都几乎一样。最简单的概率原因就是串口调试助手发送的数据格式和 FPGA 设置的不一致。比如上位机发送的是 8-E-1而 FPGA 接收端只按 8-N-1 解析那数据肯定是错的。这时候先把双方参数统一成 8-N-1再试。其次要检查的是管脚约束。Libero SoC 里分配管脚后要确保顶层信号的命名和原理图上的网络标号一致。很多新手在这里出的问题很扎心代码写得没错但管脚约束文件里把 uart_rxd 和 uart_txd 对调了导致外部进来的数据接到了一个输出引脚上电气上就直接有问题。还有一点很隐蔽USB 转串口模块本身是交叉线还是直连线。一般开发板上已经把交叉关系处理好了但如果你手头是独立的 USB 转串口模块接到 FPGA 的扩展排针就要注意 TX 接 RX、RX 接 TX。如果都接成同向的自然是一点数据都收不到。4.2 收到的数据是乱码多半是时钟和波特率的问题乱码和完全收不到是两种现象。乱码说明链路通了但位采样点不对或频率误差偏大。先确认系统时钟频率是不是代码里写的那样。有的板子虽然 PCB 上写 50MHz但因为使用了内部 RC 振荡器实际频率可能是 51MHz误差会传导到波特率上。如果确认时钟源没问题再看采样方式。只用单次边沿采样而不是中间采样在数据位边界附近如果有抖动就会采到错误电平。换成 16 倍过采样并在位中间读取基本能解决绝大多数乱码问题。我在调试中还有过一个很蠢的教训上位机波特率设成了 115200代码里参数也写的 115200但板卡实际晶振是 25MHz。这时候理论误差已经 3.1%数据偶尔对偶尔错。后来我把波特率降到 57600问题立刻消失。所以如果你手头的板子时钟频率不高别硬凑高波特率合适才是最好的。4.3 数据丢帧和回环时序竞争如果上位机连续发送一串数据FPGA 只回显了其中一部分那就是丢帧了。原因通常是接收处理逻辑的时序设计不够完善。我的最初版本里顶层只是简单地把 rx_done 作为 tx_start 触发信号但没有考虑 tx_busy 状态。当上位机连续发送时接收端每收到一帧就拉高一次 tx_start但发送端还在忙上一帧的数据新的 tx_start 就会被忽略导致丢帧。解决办法是在顶层加一个“忙等待”逻辑只有当 tx_busy 为低时才允许新的 tx_start 生效。或者更稳妥一点在接收和发送之间插入一个 FIFO把接收到的数据缓存下来发送端空闲时再从 FIFO 中读取。对于简单的回环演示忙等待就够了如果你以后想做成一个协议解析模块加 FIFO 会是更好的选择。还有一种丢帧原因容易忽略接收模块的 rx_done 只维持一个时钟周期如果顶层采样逻辑有延迟就可能漏掉。建议在处理信号前先做了一级寄存器打拍保证采样窗口可靠。5. 串口例程跑通之后还能往哪些方向延展回环测试通过只是第一步。想继续深入的话我建议从三个方向扩展。一是把固定的波特率参数改成运行时可配置的寄存器这样就能配合上位机随时切换波特率不用重新综合。二是把单字节收发改成数据包协议比如加上帧头、帧尾和 CRC 校验这是串口通信走向实际工程必经的一步也是以后做指令交互、固件升级的基础。三是把接收数据缓存进 FIFO 或 Block RAM批量处理这个方向会接触到 Actel 内部存储资源的使用方式。当时我做回环例程时顺手又加了一个基于状态机的指令解析功能上位机发送“LED_ON”就点亮板载 LED发送“LED_OFF”就熄灭。这个功能花费的时间不多但让我对状态机的理解更深了一层而且调试过程中对各种边界情况的处理也有了直观认识。如果有兴趣你也可以照着这个思路去打磨自己的串口模块。回到工具链的话题Libero 这个 IDE 刚开始用会觉得繁琐但只要多建几个工程、多跑几次流程适应之后其实还挺顺手。串口例程作为第一个 Actel 实践项目再合适不过它不牵扯高速时序不依赖复杂 IP只要逻辑和约束正确基本上几个小时就能看见结果。而这几小时积累下来的工具操作经验在今后的项目里都会反复用到。本文还有配套的精品资源点击获取

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

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

免费获取报价