简介:这份资源是面向FPGA初学者与嵌入式开发者的Actel SmartFusion串口通信例程,围绕在混合信号SoC上实现UART功能展开,帮助读者打通从硬件描述语言编程、时钟管理到固件开发的完整链路。压缩包共842个文件,约4.72MB,以vhd、v等HDL源码和c、h等固件代码为核心,辅以prw、psm等工程配置、dbs与dat数据文件、xml与txt说明文档及log、rpt运行报告,完整保留了Libero IDE工程结构。内容覆盖UART收发逻辑、波特率发生器、状态机设计、CMIP时钟配置以及Cortex-M3中断处理等关键环节,并包含仿真验证与JTAG下载调试所需文件。目前已有228人学习下载,适合希望借助真实工程快速理解SmartFusion开发流程、掌握串口通信实现方法的读者参考借鉴。1. 从一块吃灰的 SmartFusion 开发板说起:这个串口例程到底能跑出什么手头有一块 Actel(现 Microsemi)的 SmartFusion 开发板,Cortex-M3 硬核加 FPGA 逻辑混搭,性能不差,但很多人拿到手就卡在第一步——串口不通。板子上的 UART 要么没输出,要么乱码,要么干脆连不上终端。这个例程就是解决这个问题的:它把 SmartFusion 的 MSS(微控制器子系统)串口外设驱动、FPGA 侧的引脚分配、以及中断收发逻辑全部打通,烧进去就能在串口助手里看到回显。适合谁?适合手里有 SmartFusion 板子但还没跑通串口的嵌入式开发者,也适合想学 Actel FPGA 软硬协同设计的人。它不炫技,但能让你在半小时内看到第一个字符从板子发到电脑上。2. 工程结构拆解:从 .c 到 .pdc 的完整链路2.1 目录里到底放了什么拿到这个例程包,第一件事是别急着打开 IDE,先看目录结构。典型的 Actel SmartFusion 串口例程会包含这几个核心部分:uart_demo/ ├── src/ │ ├── main.c # 主循环,串口初始化与回显逻辑 │ ├── uart_driver.c # MSS UART 驱动,寄存器级操作 │ ├── uart_driver.h # 驱动接口声明 │ └── sys_config.c # 系统时钟与引脚复用配置 ├── fpga/ │ ├── top.v # FPGA 顶层,负责引脚映射 │ └── uart_fpga.v # 可选的 FPGA 侧 UART 逻辑 ├── constraints/ │ └── uart_demo.pdc # 物理约束文件,绑定引脚 ├── project/ │ └── uart_demo.prj # Libero 工程文件 └── readme.txt # 版本说明与硬件连接提示这个结构里,src/是 Cortex-M3 跑的固件,fpga/是 FPGA 逻辑,constraints/决定信号从哪个引脚进出。很多人只改main.c就编译,结果串口没反应,问题往往出在.pdc文件里引脚绑错了。2.2 启动流程:从复位向量到串口初始化SmartFusion 上电后,Cortex-M3 先从 Flash 取复位向量,执行启动文件,然后跳到main()。串口例程的main()通常长这样:#include "uart_driver.h" #include "sys_config.h" int main(void) { // 系统时钟配置:MSS 主频通常设为 100MHz MSS_CLOCK_CONFIG(); // 初始化 UART0,波特率 115200,8N1 UART_init(UART0, 115200, DATA_8BIT, PARITY_NONE, STOP_1BIT); // 使能接收中断,收到字符后触发 UART0_IRQHandler UART_enable_irq(UART0, UART_RX_IRQ); // 发送一个启动提示 UART_send_string(UART0, "SmartFusion UART Demo Ready\r\n"); while (1) { // 主循环空转,实际收发放中断里处理 } }这段代码的关键在UART_init的第二个参数——波特率。SmartFusion 的 MSS UART 时钟源来自 PLL,如果MSS_CLOCK_CONFIG()里分频设错,实际波特率会偏,终端上就是乱码。常见做法是先用示波器量 TX 引脚,确认位宽是否对应 115200(约 8.68μs 一位),再回头调分频系数。2.3 中断收发:为什么你的回显会丢字符串口例程的核心是中断服务函数。轮询方式虽然简单,但一旦主循环里有延时,接收缓冲区就会溢出。这个例程用中断收发:void UART0_IRQHandler(void) { uint8_t rx_data; // 判断是接收中断还是发送中断 if (UART_get_irq_status(UART0) & UART_RX_IRQ_FLAG) { // 从 FIFO 读一个字节 rx_data = UART_receive_byte(UART0); // 回显:把收到的字符原样发回去 UART_send_byte(UART0, rx_data); // 如果收到回车,额外发一个换行,方便终端显示 if (rx_data == '\r') { UART_send_byte(UART0, '\n'); } // 清中断标志,否则会反复进中断 UART_clear_irq(UART0, UART_RX_IRQ_FLAG); } }这里有个血泪经验:UART_clear_irq必须放在最后,而且清标志的时机要在读走数据之后。如果先清标志再读 FIFO,某些批次芯片会丢字节。另外,回显逻辑里加\r转\n是为了适配 Windows 终端,Linux 下反而会多一个空行,按需删掉即可。2.4 FPGA 侧引脚映射:.pdc 文件怎么写SmartFusion 的串口引脚不是固定死的,得在.pdc里手动绑。典型配置:# uart_demo.pdc set_io UART0_TXD -pin G12 set_io UART0_RXD -pin G11 set_io UART0_RTS -pin F12 set_io UART0_CTS -pin F11G12、G11这些是封装引脚号,具体值取决于你用的是哪款 SmartFusion(A2F200、A2F500 等)。绑错引脚的典型现象是:TX 引脚用示波器量不到波形,或者波形幅度只有 1V 左右——那说明绑到了 FPGA 的普通 IO 而非 MSS 专用引脚。MSS UART 只能走特定引脚,查数据手册的“MSS I/O Assignment”表最稳妥。3. 跑通第一步:Libero 工程配置与烧录3.1 新建工程还是直接打开 .prj如果你拿到的例程包里有.prj文件,直接用 Libero 打开最省事。但要注意版本兼容性:老例程可能是 Libero 9.x 建的,你用 11.x 打开会提示升级,升级后约束文件可能失效。我一般会先备份一份原始.pdc,再让工具自动升级,升级完对比引脚绑定有没有变。如果没有.prj,就手动新建工程,器件选对型号(比如 A2F200M3F-FGG484),然后:把src/下的.c和.h文件加入软件工程把fpga/下的.v文件加入硬件工程把.pdc文件加入约束在 Libero 的“Configure Flash”里勾选“MSS UART”作为调试输出3.2 时钟配置:最容易翻车的地方SmartFusion 的 MSS 时钟树有点绕:外部晶振 → PLL → 分频器 → 外设时钟。串口例程里通常有个sys_config.c专门管这个:void MSS_CLOCK_CONFIG(void) { // 假设外部晶振 20MHz,PLL 倍频到 100MHz MSS_PLL_CONFIG(20, 100); // APB0 总线分频,UART 挂在 APB0 上 MSS_APB0_CLOCK_DIV(2); // 100MHz / 2 = 50MHz // 使能 UART0 时钟 MSS_UART0_CLOCK_ENABLE(); }如果MSS_APB0_CLOCK_DIV设成 1,UART 时钟变成 100MHz,而波特率寄存器是按 50MHz 算的,结果就是实际波特率翻倍,终端收到乱码。排查方法:用示波器量 TX 引脚,如果位宽是预期的一半,就是这里的问题。3.3 烧录与终端连接编译通过后,用 FlashPro 烧录.stp文件到 SmartFusion。烧录时注意:模式选择“MSS 编程”,不是“FPGA 编程”如果板子上有 JTAG 和 SWD 两种接口,确认例程用的是哪种烧录完成后给板子断电再上电,有些批次需要冷启动才生效终端这边,Windows 下用串口助手,Linux 下用minicom或screen:# Linux 下连接串口 sudo screen /dev/ttyUSB0 115200如果终端里只看到乱码,先检查波特率、数据位、停止位、校验位是否和代码一致。如果完全没输出,用示波器量 TX 引脚,没波形就是引脚绑错或时钟没使能。4. 避坑与排查:串口不通时先看这五条4.1 现象:终端无任何输出,TX 引脚也无波形原因:MSS UART 时钟未使能,或者引脚绑到了 FPGA 普通 IO 而非 MSS 专用引脚。解决:检查sys_config.c里有没有MSS_UART0_CLOCK_ENABLE();检查.pdc里绑定的引脚号是否在数据手册的 MSS UART 可选引脚列表内。常见错误是把 TX 绑到了 FPGA 的 bank 0 引脚,那些引脚只能做普通 IO。4.2 现象:有波形但终端全是乱码原因:波特率不匹配。要么时钟分频算错,要么终端设置和代码不一致。解决:用示波器量一个字符的位宽。115200 波特率下,一位约 8.68μs,一个字节(8N1)约 86.8μs。如果量出来是 43.4μs,说明实际波特率是 230400,检查MSS_APB0_CLOCK_DIV是否设成了 1 而非 2。4.3 现象:能收到字符但丢数据,长字符串尤其明显原因:接收 FIFO 溢出。中断里处理太慢,或者没及时清中断标志。解决:把UART_clear_irq放在读数据之后;如果主循环里有长延时,考虑加大 FIFO 触发阈值,或者用 DMA 搬运。SmartFusion 的 MSS UART 有 16 字节 FIFO,触发阈值可设 1/4、1/2、3/4。4.4 现象:烧录后第一次上电正常,复位后就不行了原因:复位后时钟配置丢失,或者.pdc约束没被正确加载。解决:检查启动文件里有没有在main()之前重新配置时钟;有些例程把时钟配置放在SystemInit()里,如果你换了启动文件,这段可能被覆盖。另外确认 FlashPro 烧录时勾选了“MSS 配置”而非仅“FPGA 配置”。4.5 现象:Libero 编译报错“Cannot find constraint file”原因:.pdc文件路径变了,或者工程升级后约束文件后缀被改。解决:在 Libero 的“Design Flow”里手动重新添加约束文件,指向constraints/uart_demo.pdc。如果文件被升级工具改成了.pdcx,改回.pdc或重新绑定引脚。5. 二次开发:从回显到自定义协议5.1 改波特率与数据格式例程默认 115200-8N1,要改成 9600 或加校验位,改UART_init的参数即可:// 改成 9600 波特率,7 数据位,偶校验,2 停止位 UART_init(UART0, 9600, DATA_7BIT, PARITY_EVEN, STOP_2BIT);但注意:改完波特率后,MSS_APB0_CLOCK_DIV可能也要跟着调。SmartFusion 的 UART 波特率计算公式是:波特率 = APB0时钟 / (16 × (波特率分频值 + 1))如果 APB0 是 50MHz,要 9600 波特率,分频值约 324。驱动里通常有自动计算函数,但如果你手改寄存器,得自己算。5.2 加一个简单的命令解析回显只是起点,实际项目里往往要解析命令。比如收到LED ON就点亮板子上的 LED:#define CMD_BUF_SIZE 32 static char cmd_buf[CMD_BUF_SIZE]; static uint8_t cmd_idx = 0; void UART0_IRQHandler(void) { uint8_t rx_data; if (UART_get_irq_status(UART0) & UART_RX_IRQ_FLAG) { rx_data = UART_receive_byte(UART0); // 收到回车表示一条命令结束 if (rx_data == '\r' || rx_data == '\n') { cmd_buf[cmd_idx] = '\0'; // 解析命令 if (strcmp(cmd_buf, "LED ON") == 0) { GPIO_set_pin(LED_GPIO, 1); UART_send_string(UART0, "LED is ON\r\n"); } else if (strcmp(cmd_buf, "LED OFF") == 0) { GPIO_set_pin(LED_GPIO, 0); UART_send_string(UART0, "LED is OFF\r\n"); } else { UART_send_string(UART0, "Unknown command\r\n"); } cmd_idx = 0; // 清空缓冲区,准备下一条 } else { // 普通字符存入缓冲区,防止溢出 if (cmd_idx < CMD_BUF_SIZE - 1) { cmd_buf[cmd_idx++] = rx_data; } } UART_clear_irq(UART0, UART_RX_IRQ_FLAG); } }这段代码里,cmd_idx的边界检查很关键。如果不检查,收到超长字符串就会写穿cmd_buf,导致 HardFault。我一般会把CMD_BUF_SIZE设成 2 的幂次,方便后续用环形缓冲区替换。5.3 用 FPGA 侧逻辑做硬件流控SmartFusion 的 MSS UART 支持 RTS/CTS 硬件流控,但前提是 FPGA 侧把对应引脚配好。在top.v里:module top( input wire UART0_RXD, output wire UART0_TXD, output wire UART0_RTS, input wire UART0_CTS ); // MSS UART 信号直连到顶层引脚 // 注意:MSS 外设信号在 Libero 里需要显式例化 MSS_UART uart_inst( .RXD(UART0_RXD), .TXD(UART0_TXD), .RTS(UART0_RTS), .CTS(UART0_CTS) ); endmodule然后在.pdc里把UART0_RTS和UART0_CTS绑到物理引脚。硬件流控开启后,接收方 FIFO 快满时拉高 RTS,发送方检测到 CTS 变高就暂停发送。这个机制在高速率(如 921600)下尤其有用,能避免丢包。5.4 验证方法:用 Python 脚本做自动化测试改完代码后,手动敲命令测试效率低。我一般写个 Python 脚本自动发命令、收响应:import serial import time # 打开串口,超时设 1 秒 ser = serial.Serial('/dev/ttyUSB0', 115200, timeout=1) time.sleep(0.5) # 等板子启动 # 发命令并读响应 def send_cmd(cmd): ser.write((cmd + '\r').encode()) time.sleep(0.1) response = ser.read_all().decode(errors='ignore') return response.strip() # 测试 LED 命令 print(send_cmd('LED ON')) # 期望输出:LED is ON print(send_cmd('LED OFF')) # 期望输出:LED is OFF print(send_cmd('HELLO')) # 期望输出:Unknown command ser.close()这个脚本能快速回归测试,改完中断逻辑或命令解析后跑一遍,确认没破坏原有功能。注意time.sleep(0.1)是给板子留处理时间,如果命令复杂可以加大。5.5 一个容易忽略的细节:中断优先级SmartFusion 的 Cortex-M3 支持嵌套中断。如果串口中断优先级设得太低,被其他中断(比如定时器)打断,接收 FIFO 可能溢出。在UART_enable_irq之后加一句:NVIC_SetPriority(UART0_IRQn, 1); // 设成较高优先级具体优先级数值取决于你的系统里有多少中断源。我一般把串口设成 1 或 2,定时器设成 3,这样串口能及时响应。从那以后我每次调 SmartFusion 串口,都强制走一遍“示波器量 TX → 确认波特率 → 检查 .pdc 引脚 → 跑 Python 回归脚本”的流程,少一步都可能翻车。希望帮到你。本文还有配套的精品资源,点击获取
简介:这份资源是面向FPGA初学者与嵌入式开发者的Actel SmartFusion串口通信例程,围绕在混合信号SoC上实现UART功能展开,帮助读者打通从硬件描述语言编程、时钟管理到固件开发的完整链路。压缩包共842个文件,约4.72MB,以vhd、v等HDL源码和c、h等固件代码为核心,辅以prw、psm等工程配置、dbs与dat数据文件、xml与txt说明文档及log、rpt运行报告,完整保留了Libero IDE工程结构。内容覆盖UART收发逻辑、波特率发生器、状态机设计、CMIP时钟配置以及Cortex-M3中断处理等关键环节,并包含仿真验证与JTAG下载调试所需文件。目前已有228人学习下载,适合希望借助真实工程快速理解SmartFusion开发流程、掌握串口通信实现方法的读者参考借鉴。1. 从一块吃灰的 SmartFusion 开发板说起:这个串口例程到底能跑出什么手头有一块 Actel(现 Microsemi)的 SmartFusion 开发板,Cortex-M3 硬核加 FPGA 逻辑混搭,性能不差,但很多人拿到手就卡在第一步——串口不通。板子上的 UART 要么没输出,要么乱码,要么干脆连不上终端。这个例程就是解决这个问题的:它把 SmartFusion 的 MSS(微控制器子系统)串口外设驱动、FPGA 侧的引脚分配、以及中断收发逻辑全部打通,烧进去就能在串口助手里看到回显。适合谁?适合手里有 SmartFusion 板子但还没跑通串口的嵌入式开发者,也适合想学 Actel FPGA 软硬协同设计的人。它不炫技,但能让你在半小时内看到第一个字符从板子发到电脑上。2. 工程结构拆解:从 .c 到 .pdc 的完整链路2.1 目录里到底放了什么拿到这个例程包,第一件事是别急着打开 IDE,先看目录结构。典型的 Actel SmartFusion 串口例程会包含这几个核心部分:uart_demo/ ├── src/ │ ├── main.c # 主循环,串口初始化与回显逻辑 │ ├── uart_driver.c # MSS UART 驱动,寄存器级操作 │ ├── uart_driver.h # 驱动接口声明 │ └── sys_config.c # 系统时钟与引脚复用配置 ├── fpga/ │ ├── top.v # FPGA 顶层,负责引脚映射 │ └── uart_fpga.v # 可选的 FPGA 侧 UART 逻辑 ├── constraints/ │ └── uart_demo.pdc # 物理约束文件,绑定引脚 ├── project/ │ └── uart_demo.prj # Libero 工程文件 └── readme.txt # 版本说明与硬件连接提示这个结构里,src/是 Cortex-M3 跑的固件,fpga/是 FPGA 逻辑,constraints/决定信号从哪个引脚进出。很多人只改main.c就编译,结果串口没反应,问题往往出在.pdc文件里引脚绑错了。2.2 启动流程:从复位向量到串口初始化SmartFusion 上电后,Cortex-M3 先从 Flash 取复位向量,执行启动文件,然后跳到main()。串口例程的main()通常长这样:#include "uart_driver.h" #include "sys_config.h" int main(void) { // 系统时钟配置:MSS 主频通常设为 100MHz MSS_CLOCK_CONFIG(); // 初始化 UART0,波特率 115200,8N1 UART_init(UART0, 115200, DATA_8BIT, PARITY_NONE, STOP_1BIT); // 使能接收中断,收到字符后触发 UART0_IRQHandler UART_enable_irq(UART0, UART_RX_IRQ); // 发送一个启动提示 UART_send_string(UART0, "SmartFusion UART Demo Ready\r\n"); while (1) { // 主循环空转,实际收发放中断里处理 } }这段代码的关键在UART_init的第二个参数——波特率。SmartFusion 的 MSS UART 时钟源来自 PLL,如果MSS_CLOCK_CONFIG()里分频设错,实际波特率会偏,终端上就是乱码。常见做法是先用示波器量 TX 引脚,确认位宽是否对应 115200(约 8.68μs 一位),再回头调分频系数。2.3 中断收发:为什么你的回显会丢字符串口例程的核心是中断服务函数。轮询方式虽然简单,但一旦主循环里有延时,接收缓冲区就会溢出。这个例程用中断收发:void UART0_IRQHandler(void) { uint8_t rx_data; // 判断是接收中断还是发送中断 if (UART_get_irq_status(UART0) & UART_RX_IRQ_FLAG) { // 从 FIFO 读一个字节 rx_data = UART_receive_byte(UART0); // 回显:把收到的字符原样发回去 UART_send_byte(UART0, rx_data); // 如果收到回车,额外发一个换行,方便终端显示 if (rx_data == '\r') { UART_send_byte(UART0, '\n'); } // 清中断标志,否则会反复进中断 UART_clear_irq(UART0, UART_RX_IRQ_FLAG); } }这里有个血泪经验:UART_clear_irq必须放在最后,而且清标志的时机要在读走数据之后。如果先清标志再读 FIFO,某些批次芯片会丢字节。另外,回显逻辑里加\r转\n是为了适配 Windows 终端,Linux 下反而会多一个空行,按需删掉即可。2.4 FPGA 侧引脚映射:.pdc 文件怎么写SmartFusion 的串口引脚不是固定死的,得在.pdc里手动绑。典型配置:# uart_demo.pdc set_io UART0_TXD -pin G12 set_io UART0_RXD -pin G11 set_io UART0_RTS -pin F12 set_io UART0_CTS -pin F11G12、G11这些是封装引脚号,具体值取决于你用的是哪款 SmartFusion(A2F200、A2F500 等)。绑错引脚的典型现象是:TX 引脚用示波器量不到波形,或者波形幅度只有 1V 左右——那说明绑到了 FPGA 的普通 IO 而非 MSS 专用引脚。MSS UART 只能走特定引脚,查数据手册的“MSS I/O Assignment”表最稳妥。3. 跑通第一步:Libero 工程配置与烧录3.1 新建工程还是直接打开 .prj如果你拿到的例程包里有.prj文件,直接用 Libero 打开最省事。但要注意版本兼容性:老例程可能是 Libero 9.x 建的,你用 11.x 打开会提示升级,升级后约束文件可能失效。我一般会先备份一份原始.pdc,再让工具自动升级,升级完对比引脚绑定有没有变。如果没有.prj,就手动新建工程,器件选对型号(比如 A2F200M3F-FGG484),然后:把src/下的.c和.h文件加入软件工程把fpga/下的.v文件加入硬件工程把.pdc文件加入约束在 Libero 的“Configure Flash”里勾选“MSS UART”作为调试输出3.2 时钟配置:最容易翻车的地方SmartFusion 的 MSS 时钟树有点绕:外部晶振 → PLL → 分频器 → 外设时钟。串口例程里通常有个sys_config.c专门管这个:void MSS_CLOCK_CONFIG(void) { // 假设外部晶振 20MHz,PLL 倍频到 100MHz MSS_PLL_CONFIG(20, 100); // APB0 总线分频,UART 挂在 APB0 上 MSS_APB0_CLOCK_DIV(2); // 100MHz / 2 = 50MHz // 使能 UART0 时钟 MSS_UART0_CLOCK_ENABLE(); }如果MSS_APB0_CLOCK_DIV设成 1,UART 时钟变成 100MHz,而波特率寄存器是按 50MHz 算的,结果就是实际波特率翻倍,终端收到乱码。排查方法:用示波器量 TX 引脚,如果位宽是预期的一半,就是这里的问题。3.3 烧录与终端连接编译通过后,用 FlashPro 烧录.stp文件到 SmartFusion。烧录时注意:模式选择“MSS 编程”,不是“FPGA 编程”如果板子上有 JTAG 和 SWD 两种接口,确认例程用的是哪种烧录完成后给板子断电再上电,有些批次需要冷启动才生效终端这边,Windows 下用串口助手,Linux 下用minicom或screen:# Linux 下连接串口 sudo screen /dev/ttyUSB0 115200如果终端里只看到乱码,先检查波特率、数据位、停止位、校验位是否和代码一致。如果完全没输出,用示波器量 TX 引脚,没波形就是引脚绑错或时钟没使能。4. 避坑与排查:串口不通时先看这五条4.1 现象:终端无任何输出,TX 引脚也无波形原因:MSS UART 时钟未使能,或者引脚绑到了 FPGA 普通 IO 而非 MSS 专用引脚。解决:检查sys_config.c里有没有MSS_UART0_CLOCK_ENABLE();检查.pdc里绑定的引脚号是否在数据手册的 MSS UART 可选引脚列表内。常见错误是把 TX 绑到了 FPGA 的 bank 0 引脚,那些引脚只能做普通 IO。4.2 现象:有波形但终端全是乱码原因:波特率不匹配。要么时钟分频算错,要么终端设置和代码不一致。解决:用示波器量一个字符的位宽。115200 波特率下,一位约 8.68μs,一个字节(8N1)约 86.8μs。如果量出来是 43.4μs,说明实际波特率是 230400,检查MSS_APB0_CLOCK_DIV是否设成了 1 而非 2。4.3 现象:能收到字符但丢数据,长字符串尤其明显原因:接收 FIFO 溢出。中断里处理太慢,或者没及时清中断标志。解决:把UART_clear_irq放在读数据之后;如果主循环里有长延时,考虑加大 FIFO 触发阈值,或者用 DMA 搬运。SmartFusion 的 MSS UART 有 16 字节 FIFO,触发阈值可设 1/4、1/2、3/4。4.4 现象:烧录后第一次上电正常,复位后就不行了原因:复位后时钟配置丢失,或者.pdc约束没被正确加载。解决:检查启动文件里有没有在main()之前重新配置时钟;有些例程把时钟配置放在SystemInit()里,如果你换了启动文件,这段可能被覆盖。另外确认 FlashPro 烧录时勾选了“MSS 配置”而非仅“FPGA 配置”。4.5 现象:Libero 编译报错“Cannot find constraint file”原因:.pdc文件路径变了,或者工程升级后约束文件后缀被改。解决:在 Libero 的“Design Flow”里手动重新添加约束文件,指向constraints/uart_demo.pdc。如果文件被升级工具改成了.pdcx,改回.pdc或重新绑定引脚。5. 二次开发:从回显到自定义协议5.1 改波特率与数据格式例程默认 115200-8N1,要改成 9600 或加校验位,改UART_init的参数即可:// 改成 9600 波特率,7 数据位,偶校验,2 停止位 UART_init(UART0, 9600, DATA_7BIT, PARITY_EVEN, STOP_2BIT);但注意:改完波特率后,MSS_APB0_CLOCK_DIV可能也要跟着调。SmartFusion 的 UART 波特率计算公式是:波特率 = APB0时钟 / (16 × (波特率分频值 + 1))如果 APB0 是 50MHz,要 9600 波特率,分频值约 324。驱动里通常有自动计算函数,但如果你手改寄存器,得自己算。5.2 加一个简单的命令解析回显只是起点,实际项目里往往要解析命令。比如收到LED ON就点亮板子上的 LED:#define CMD_BUF_SIZE 32 static char cmd_buf[CMD_BUF_SIZE]; static uint8_t cmd_idx = 0; void UART0_IRQHandler(void) { uint8_t rx_data; if (UART_get_irq_status(UART0) & UART_RX_IRQ_FLAG) { rx_data = UART_receive_byte(UART0); // 收到回车表示一条命令结束 if (rx_data == '\r' || rx_data == '\n') { cmd_buf[cmd_idx] = '\0'; // 解析命令 if (strcmp(cmd_buf, "LED ON") == 0) { GPIO_set_pin(LED_GPIO, 1); UART_send_string(UART0, "LED is ON\r\n"); } else if (strcmp(cmd_buf, "LED OFF") == 0) { GPIO_set_pin(LED_GPIO, 0); UART_send_string(UART0, "LED is OFF\r\n"); } else { UART_send_string(UART0, "Unknown command\r\n"); } cmd_idx = 0; // 清空缓冲区,准备下一条 } else { // 普通字符存入缓冲区,防止溢出 if (cmd_idx < CMD_BUF_SIZE - 1) { cmd_buf[cmd_idx++] = rx_data; } } UART_clear_irq(UART0, UART_RX_IRQ_FLAG); } }这段代码里,cmd_idx的边界检查很关键。如果不检查,收到超长字符串就会写穿cmd_buf,导致 HardFault。我一般会把CMD_BUF_SIZE设成 2 的幂次,方便后续用环形缓冲区替换。5.3 用 FPGA 侧逻辑做硬件流控SmartFusion 的 MSS UART 支持 RTS/CTS 硬件流控,但前提是 FPGA 侧把对应引脚配好。在top.v里:module top( input wire UART0_RXD, output wire UART0_TXD, output wire UART0_RTS, input wire UART0_CTS ); // MSS UART 信号直连到顶层引脚 // 注意:MSS 外设信号在 Libero 里需要显式例化 MSS_UART uart_inst( .RXD(UART0_RXD), .TXD(UART0_TXD), .RTS(UART0_RTS), .CTS(UART0_CTS) ); endmodule然后在.pdc里把UART0_RTS和UART0_CTS绑到物理引脚。硬件流控开启后,接收方 FIFO 快满时拉高 RTS,发送方检测到 CTS 变高就暂停发送。这个机制在高速率(如 921600)下尤其有用,能避免丢包。5.4 验证方法:用 Python 脚本做自动化测试改完代码后,手动敲命令测试效率低。我一般写个 Python 脚本自动发命令、收响应:import serial import time # 打开串口,超时设 1 秒 ser = serial.Serial('/dev/ttyUSB0', 115200, timeout=1) time.sleep(0.5) # 等板子启动 # 发命令并读响应 def send_cmd(cmd): ser.write((cmd + '\r').encode()) time.sleep(0.1) response = ser.read_all().decode(errors='ignore') return response.strip() # 测试 LED 命令 print(send_cmd('LED ON')) # 期望输出:LED is ON print(send_cmd('LED OFF')) # 期望输出:LED is OFF print(send_cmd('HELLO')) # 期望输出:Unknown command ser.close()这个脚本能快速回归测试,改完中断逻辑或命令解析后跑一遍,确认没破坏原有功能。注意time.sleep(0.1)是给板子留处理时间,如果命令复杂可以加大。5.5 一个容易忽略的细节:中断优先级SmartFusion 的 Cortex-M3 支持嵌套中断。如果串口中断优先级设得太低,被其他中断(比如定时器)打断,接收 FIFO 可能溢出。在UART_enable_irq之后加一句:NVIC_SetPriority(UART0_IRQn, 1); // 设成较高优先级具体优先级数值取决于你的系统里有多少中断源。我一般把串口设成 1 或 2,定时器设成 3,这样串口能及时响应。从那以后我每次调 SmartFusion 串口,都强制走一遍“示波器量 TX → 确认波特率 → 检查 .pdc 引脚 → 跑 Python 回归脚本”的流程,少一步都可能翻车。希望帮到你。本文还有配套的精品资源,点击获取