STM32与FPGA在电赛发挥部分的应用:DDS信号发生器实战解析
2026/8/2 7:52:35 网站建设 项目流程

这次我们来看一个针对2026年电子设计竞赛(电赛)的实战技术专栏,聚焦于吉林省赛区发挥部分的第一问。对于电赛选手而言,发挥部分往往是拉开差距的关键,它不仅考验对基础知识的掌握,更挑战系统设计、算法实现和工程调试的综合能力。本文将直接切入核心,围绕STM32和FPGA这两个在电赛中应用最广泛的平台,为你拆解一个典型发挥部分题目的解题思路、硬件选型、软件框架与调试技巧。

无论你是初次参赛的新手,还是希望优化方案的老手,这篇文章都将提供一套从分析到验证的完整流程。我们会重点关注方案的可实现性、代码的模块化设计以及常见坑点的规避。你将看到如何将抽象的题目要求转化为具体的电路连接、驱动代码和算法逻辑,并最终通过实测数据验证方案的可行性。

1. 核心能力速览

在深入细节之前,我们先通过下表快速了解应对此类电赛发挥部分题目所需的核心技术栈与资源准备。这能帮助你快速判断自己是否具备相应的基础,以及需要提前准备哪些内容。

能力项说明与要求
核心控制器STM32系列单片机(如F103、F407、H743等) 或FPGA(如Xilinx Artix-7, Intel Cyclone IV)。STM32适合复杂逻辑与控制,FPGA擅长高速并行处理。
开发环境STM32: Keil MDK-ARM / STM32CubeIDE / VSCode + PlatformIO。 FPGA: Xilinx Vivado / Intel Quartus Prime。
关键外设掌握必会: GPIO、定时器(TIM)、ADC/DAC、USART/UART。提高: SPI/I2C通信、DMA、高级定时器、中断嵌套。
典型题目方向信号发生器(DDS)、数据采集、电机控制、通信协议、算法实现(如滤波、编码)、人机交互。
硬件门槛一套基础的STM32或FPGA开发板、示波器、信号源、万用表是调试必备。根据题目可能还需传感器、电机、屏幕等模块。
代码管理强烈推荐使用模块化、分层设计(如HAL/LL库驱动层、业务逻辑层),便于调试与复用。
调试能力熟练使用串口调试、逻辑分析仪、仿真器(ST-Link/J-Link)进行在线调试与问题定位。

2. 适用场景与使用边界

本专栏讨论的方案主要适用于全国大学生电子设计竞赛及其各省赛区的“发挥部分”题目。这类题目通常具有以下特点:

  • 适合谁:正在备赛的电赛学生团队、希望深入学习STM32/FPGA系统设计的嵌入式爱好者、需要解决特定测量或控制问题的工程师。
  • 能解决什么问题
    1. 将题目需求技术化:把“测量某参数”、“生成某波形”、“实现某通信”等要求,分解为具体的传感器选型、电路设计、外设驱动和算法。
    2. 提供可复用的框架:建立清晰的软件架构,如状态机、任务调度,使代码易于维护和扩展。
    3. 规避常见陷阱:分享在ADC采样精度、定时器同步、中断冲突、通信稳定性等方面的实战经验。
  • 不适合什么场景
    1. 对嵌入式开发毫无基础,需要从点亮LED学起的纯新手(建议先完成基础教程)。
    2. 追求单一固定答案的题目。发挥部分通常没有标准答案,本文提供的是方法论和典型实现路径。
    3. 完全脱离STM32/FPGA平台的其他技术栈题目。
  • 安全与合规边界
    1. 硬件安全:注意开发板的供电电压和IO口电平,避免短路或过压烧毁芯片。
    2. 代码原创:在理解的基础上借鉴和重构,避免直接抄袭,注重培养独立解决问题的能力。
    3. 仪器使用:规范使用示波器、电源等仪器,遵守实验室安全规定。

3. 环境准备与前置条件

在开始具体设计前,请确保你的软硬件环境已就绪。一个稳定的环境是高效调试的基础。

3.1 硬件准备清单

  • 主控板:根据题目复杂度和性能要求选择。
    • STM32F103C8T6(最小系统板):适合逻辑控制、简单通信。
    • STM32F407ZGT6:资源丰富,带FPU,适合需要浮点运算或复杂算法的场景。
    • FPGA开发板(如Basys3、DE10-Lite):适合需要高速、并行或定制硬件逻辑的场景。
  • 调试工具
    • ST-Link V2 或 J-Link 仿真器(用于STM32程序下载与调试)。
    • USB-TTL 串口模块(用于打印调试信息)。
    • 逻辑分析仪(可选,但分析数字信号时序非常有用)。
  • 测试仪器
    • 数字示波器(必备,观察波形、测量频率幅值)。
    • 函数信号发生器(用于产生测试信号)。
    • 直流稳压电源(为系统提供稳定供电)。
  • 其他模块:根据往届题目推测,可能需要准备OLED/LCD屏幕、按键/编码器、各类传感器(温湿度、光敏、超声波等)、电机驱动模块等。

3.2 软件开发环境

  • STM32开发
    • IDE选择:Keil MDK-ARM(经典,需安装芯片包)、STM32CubeIDE(免费,集成CubeMX,推荐)、VSCode + PlatformIO(轻量,可定制性强)。
    • 关键软件:STM32CubeMX(图形化配置工具,生成初始化代码)。
    • 驱动安装:确保ST-Link/USB-TTL的驱动已正确安装。
  • FPGA开发
    • IDE选择:Xilinx Vivado(用于Xilinx芯片)、Intel Quartus Prime(用于Intel/Altera芯片)。注意版本与开发板芯片型号的兼容性。
    • 学习基础:需要掌握Verilog或VHDL硬件描述语言。

3.3 知识储备

  • C语言基础:指针、结构体、位操作必须熟练。
  • 单片机/FPGA核心概念:时钟树、中断、定时器、通信协议(UART, SPI, I2C)。
  • 基本电路知识:能看懂原理图,理解上拉/下拉电阻、滤波电容的作用。

4. 解题思路分析与框架设计

面对一个发挥部分的题目,切忌直接动手写代码。合理的分析设计能事半功倍。我们以一个假设的题目为例:“设计一个正弦波信号发生器,要求频率可调范围为1Hz-10kHz,步进1Hz;幅度可调范围为0.1Vpp-3.3Vpp;可通过键盘设置参数并在LCD上显示。”

4.1 需求分解与技术选型

  1. 波形生成:核心是DDS(直接数字频率合成)技术。既可以用STM32的DAC配合定时器+DMA实现,也可以用FPGA实现以获得更高的频率精度和速度。这里假设选用STM32F407(带DAC和高级定时器)。
  2. 频率/幅度控制:需要精确的时钟控制和幅度缩放算法。
  3. 人机交互:矩阵键盘或独立按键输入,OLED/LCD显示。
  4. 系统调度:需要处理按键扫描、显示刷新、波形计算输出等多个任务,考虑使用状态机或简单的RTOS(如FreeRTOS)。

4.2 系统框架设计采用分层设计的思想:

  • 硬件驱动层:利用HAL库或LL库,完成DAC、TIM、GPIO(按键、LCD)、SPI/I2C(用于屏幕)的初始化。
  • 算法服务层:实现DDS查表算法、频率/幅度换算函数。
  • 业务逻辑层:实现主状态机,处理按键事件,更新显示内容,设置DDS参数。
  • 应用层main函数中的主循环,调度各层任务。

5. 关键模块实现与代码解析

接下来,我们针对核心的DDS波形生成模块,给出具体的实现步骤和代码片段。

5.1 DDS原理与查表法DDS的核心是一个相位累加器。我们采用查表法,提前计算好一个正弦周期波形的数字量并存入数组(正弦表)。通过改变相位累加器的步进值(频率控制字)来改变输出频率。

// dds_generator.h #ifndef __DDS_GENERATOR_H #define __DDS_GENERATOR_H #include "stm32f4xx_hal.h" #define SINE_TABLE_SIZE 1024 // 正弦表长度,影响频率分辨率 #define REF_CLOCK_FREQ 84000000 // 假设定时器时钟为84MHz #define DAC_MAX_VALUE 4095 // 12位DAC最大值 typedef struct { uint32_t phase_accumulator; // 相位累加器 uint32_t tuning_word; // 频率控制字,决定步进 uint16_t amplitude; // 幅度系数 (0 ~ DAC_MAX_VALUE) uint16_t *sine_table; // 正弦表指针 DAC_HandleTypeDef *hdac; // DAC句柄 TIM_HandleTypeDef *htim; // 触发DAC转换的定时器句柄 } DDS_HandleTypeDef; void DDS_Init(DDS_HandleTypeDef *hdds, DAC_HandleTypeDef *hdac, TIM_HandleTypeDef *htim); void DDS_SetFrequency(DDS_HandleTypeDef *hdds, float freq); void DDS_SetAmplitude(DDS_HandleTypeDef *hdds, float amplitude_volt); void DDS_Update(DDS_HandleTypeDef *hdds); // 在定时器中断中调用,更新DAC值 #endif
// dds_generator.c #include "dds_generator.h" #include <math.h> static uint16_t sine_table[SINE_TABLE_SIZE]; // 初始化,生成正弦表 void DDS_Init(DDS_HandleTypeDef *hdds, DAC_HandleTypeDef *hdac, TIM_HandleTypeDef *htim) { hdds->hdac = hdac; hdds->htim = htim; hdds->phase_accumulator = 0; hdds->tuning_word = 0; hdds->amplitude = DAC_MAX_VALUE; hdds->sine_table = sine_table; // 生成正弦表,值范围 0 ~ DAC_MAX_VALUE for (int i = 0; i < SINE_TABLE_SIZE; i++) { // 正弦值范围[-1, 1],映射到[0, DAC_MAX_VALUE] float sine_value = sin(2 * M_PI * i / SINE_TABLE_SIZE); sine_table[i] = (uint16_t)((sine_value + 1.0) / 2.0 * DAC_MAX_VALUE); } // 启动DAC和定时器 HAL_DAC_Start(hdds->hdac, DAC_CHANNEL_1); HAL_TIM_Base_Start_IT(hdds->htim); // 使用定时器中断触发更新 } // 设置输出频率 (Hz) void DDS_SetFrequency(DDS_HandleTypeDef *hdds, float freq) { // 计算频率控制字: Tuning_Word = (freq * TABLE_SIZE) / F_timer // F_timer 是定时器更新频率,由定时器分频和重载值决定 uint32_t timer_freq = REF_CLOCK_FREQ / (hdds->htim->Instance->PSC + 1) / (hdds->htim->Instance->ARR + 1); hdds->tuning_word = (uint32_t)((freq * SINE_TABLE_SIZE) / timer_freq); } // 设置输出幅度 (Vpp, 需根据硬件放大电路调整换算关系) void DDS_SetAmplitude(DDS_HandleTypeDef *hdds, float amplitude_volt) { // 假设 3.3V 对应 DAC_MAX_VALUE // 此处为简化模型,实际需考虑后端运放增益 float ratio = amplitude_volt / 3.3; if (ratio > 1.0) ratio = 1.0; if (ratio < 0.0) ratio = 0.0; hdds->amplitude = (uint16_t)(ratio * DAC_MAX_VALUE); } // 在定时器中断服务函数中调用此函数 void DDS_Update(DDS_HandleTypeDef *hdds) { hdds->phase_accumulator += hdds->tuning_word; uint32_t index = (hdds->phase_accumulator >> 22) & (SINE_TABLE_SIZE - 1); // 取高10位作为索引(假设SINE_TABLE_SIZE=1024) uint16_t dac_value = (hdds->sine_table[index] * hdds->amplitude) / DAC_MAX_VALUE; HAL_DAC_SetValue(hdds->hdac, DAC_CHANNEL_1, DAC_ALIGN_12B_R, dac_value); }

5.2 定时器与DMA配置为了高效、不间断地输出波形,我们使用一个定时器(如TIM6)产生固定频率的中断,并在中断中调用DDS_Update。更高效的方式是使用定时器触发DMA,将正弦表数据自动搬运到DAC数据寄存器。

// 使用STM32CubeMX配置或代码初始化 // TIM6 配置为更新频率 = 100kHz (ARR=839, PSC=0, 时钟84MHz) // 开启TIM6更新中断 // DAC 配置为定时器触发转换 // 在 stm32f4xx_it.c 的中断服务函数中 void TIM6_DAC_IRQHandler(void) { if (__HAL_TIM_GET_FLAG(&htim6, TIM_FLAG_UPDATE) != RESET) { __HAL_TIM_CLEAR_FLAG(&htim6, TIM_FLAG_UPDATE); DDS_Update(&hdds); // 更新DAC输出值 } }

5.3 人机交互实现按键扫描和显示刷新不宜在中断中长时间执行,建议在主循环中处理。

// main.c 主循环示例 int main(void) { // HAL初始化、外设初始化、DDS初始化... System_Init(); DDS_Init(&hdds, &hdac1, &htim6); LCD_Init(); Key_Init(); // 设置初始频率和幅度 DDS_SetFrequency(&hdds, 1000.0); // 1kHz DDS_SetAmplitude(&hdds, 1.65); // 1.65Vpp float current_freq = 1000.0; float current_amp = 1.65; while (1) { // 1. 扫描按键 Key_Scan(); if (Key_GetPress(KEY_UP)) { current_freq += 1.0; // 步进1Hz DDS_SetFrequency(&hdds, current_freq); } else if (Key_GetPress(KEY_DOWN)) { current_freq -= 1.0; DDS_SetFrequency(&hdds, current_freq); } // ... 其他按键处理 // 2. 更新显示 (避免过于频繁) static uint32_t display_tick = 0; if (HAL_GetTick() - display_tick > 200) { // 每200ms更新一次显示 display_tick = HAL_GetTick(); char buf[32]; sprintf(buf, "Freq:%5.1fHz", current_freq); LCD_DisplayString(0, 0, buf); sprintf(buf, "Amp:%4.2fV", current_amp); LCD_DisplayString(1, 0, buf); } // 3. 其他任务... HAL_Delay(10); // 主循环延时 } }

6. 系统调试与效果验证

代码编写完成后,必须通过系统化的调试来验证功能是否达标。

6.1 调试步骤

  1. 单元测试
    • 先不接DDS,测试按键和LCD显示是否正常。确保参数设置和显示逻辑正确。
    • 使用printf通过串口打印当前的频率控制字、幅度值等关键变量。
  2. 信号观测
    • 将STM32的DAC输出引脚连接到示波器。
    • 上电,观察是否有波形输出。如果没有,检查:
      • 定时器是否成功启动并进入中断。
      • DAC是否使能并配置为定时器触发模式。
      • 在DDS_Update函数中设置断点,查看相位累加器和查表索引是否正常变化。
  3. 参数验证
    • 频率精度:通过按键将频率设置为1Hz, 100Hz, 1kHz, 5kHz, 10kHz。用示波器的频率测量功能,对比实际测量值与设定值。计算误差。
    • 幅度精度:设置幅度为0.5Vpp, 1.0Vpp, 2.0Vpp, 3.0Vpp。用示波器测量峰峰值电压,对比设定值。注意DAC输出范围是0-3.3V,后端可能需要运放进行衰减或放大来匹配题目要求。
    • 波形失真度:观察正弦波是否光滑,有无明显的台阶(查表分辨率不足)或毛刺(干扰)。
  4. 边界与稳定性测试
    • 测试频率调到下限1Hz和上限10kHz时,波形是否稳定。
    • 快速连续按按键改变参数,观察系统响应是否及时,显示是否卡顿。
    • 长时间运行(如10分钟),观察波形是否有漂移或异常。

6.2 性能优化方向

  • 提高频率分辨率:增大正弦表长度(SINE_TABLE_SIZE)或提高定时器触发频率。
  • 降低CPU占用:使用DMA自动传输代替定时器中断,解放CPU。
  • 改善波形质量:如果波形有台阶感,可考虑使用插值算法;如果高频时波形失真,需检查定时器频率和代码执行效率是否够快。
  • 增加功能:扩展为多波形(方波、三角波)发生器,或加入频率扫描、幅度调制等功能。

7. 常见问题与排查方法

在电赛调试中,90%的时间都在解决问题。下表汇总了本方案可能遇到的典型问题及解决思路。

问题现象可能原因排查方式解决方案
无波形输出1. DAC或定时器未正确初始化。
2. 定时器中断未开启或未进入。
3. 输出引脚配置错误。
1. 检查CubeMX配置或初始化代码。
2. 在定时器中断函数内设置断点或翻转一个测试引脚。
3. 用万用表测量DAC输出引脚电压是否有变化。
1. 核对参考手册,确保外设时钟使能,配置参数正确。
2. 检查中断优先级和使能位。
3. 确认GPIO模式配置为模拟模式(DAC)或复用推挽输出。
波形频率不准1. 定时器时钟源或分频计算错误。
2. 频率控制字计算有误。
3. 系统主频未正确配置。
1. 用示波器测量定时器更新中断的实际频率。
2. 打印出计算的tuning_word值,与理论值对比。
3. 检查SystemClock_Config函数。
1. 重新计算定时器ARR和PSC值。
2. 核对DDS_SetFrequency函数中的计算公式,注意数据类型和浮点运算。
3. 使用STM32CubeMX重新生成时钟树配置。
波形幅度不对1. DAC参考电压不是预期的3.3V。
2. 幅度换算公式错误。
3. 后端运放电路增益计算错误。
1. 测量开发板上的VREF+电压。
2. 设置固定幅度值(如DAC_MAX_VALUE/2),看输出是否为1.65V左右。
3. 单独测试运放电路。
1. 确保供电稳定。有些板子VREF+需单独连接。
2. 修正DDS_SetAmplitude中的换算系数。
3. 根据运放电路重新计算软件中的幅度映射关系。
按键或显示不响应1. 按键去抖处理不当。
2. 显示驱动时序错误。
3. 主循环被阻塞。
1. 用逻辑分析仪抓取按键波形。
2. 检查LCD初始化序列和发送的数据。
3. 在按键扫描和显示函数中插入调试灯或串口打印。
1. 增加软件去抖或硬件电容滤波。
2. 对照LCD数据手册,严格遵循时序要求。
3. 避免在主循环中使用长延时HAL_Delay,改用状态机或定时器管理任务。
高频时波形失真1. 定时器更新频率跟不上。
2. 查表法输出速率达到极限。
3. 代码执行时间过长。
1. 提高定时器时钟或减小ARR值以提高中断频率。
2. 考虑使用DMA或FPGA方案。
3. 优化DDS_Update函数,使用查表代替实时计算。
1. 评估系统性能瓶颈,必要时更换更高主频的MCU或使用FPGA。
2. 使用DMA从内存向DAC自动传输数据,不占用CPU。
系统运行一段时间后死机1. 中断嵌套或优先级冲突导致死锁。
2. 堆栈溢出。
3. 看门狗未处理。
1. 检查所有中断的优先级配置。
2. 增大启动文件中的堆栈大小。
3. 检查是否开启了看门狗但未及时喂狗。
1. 合理规划中断优先级,避免在中断中调用可能阻塞的函数。
2. 优化局部变量,减少递归调用。
3. 若未使用看门狗,则禁用它。

8. 最佳实践与备赛建议

基于以上分析和实现,为电赛发挥部分准备提出以下建议:

  1. 模块化代码仓库:提前构建自己的代码库,将常用的驱动(OLED、按键、ADC、DAC、PWM、编码器)封装成独立的、接口清晰的模块。比赛时可以直接移植,节省大量时间。
  2. 掌握核心算法:熟练掌握DDS、PID控制、数字滤波(如均值、中值、卡尔曼)、FFT等常用算法。理解其原理并能用C语言实现。
  3. 善用调试工具
    • 串口调试:是最重要的信息输出渠道,用于打印变量、程序状态。
    • 逻辑分析仪:分析SPI、I2C、UART等数字通信时序是否准确,排查通信故障的神器。
    • 示波器:观察模拟信号、测量频率、幅值、观察噪声。
  4. 重视硬件可靠性
    • 电源:为数字部分和模拟部分分别供电或做好隔离,使用滤波电容。
    • 信号调理:ADC前端加运放跟随、滤波电路;DAC后端加运放驱动。
    • 布线:竞赛中虽不要求PCB,但杜邦线连接应整齐、牢固,避免虚焊和短路。
  5. 时间管理:四天三夜的比赛,合理规划时间。第一天确定方案、绘制框图、分配任务;第二、三天实现主要功能并调试;最后一天完善、优化、撰写报告。
  6. 报告撰写:报告是评分的重要依据。图文并茂,框图、流程图、电路图、程序流程图、测试数据图表一个都不能少。突出方案创新点、理论计算和实测数据的对比。

9. 总结与下一步

应对电赛发挥部分,核心在于系统性的分析和快速实现的能力。本文以“可调信号发生器”为例,详细展示了从题目分析、技术选型、模块编码到系统调试的全过程。最关键的不是记住这段代码,而是掌握这种“分解-实现-集成-验证”的方法论。

对于下一步,建议你:

  1. 动手复现:找一块STM32开发板,尝试将本文的DDS代码跑起来,用示波器观察波形。
  2. 尝试变种:将正弦波改为方波或三角波;尝试用PWM+滤波电路实现DAC功能;尝试用FPGA实现更高性能的DDS。
  3. 拓展功能:加入频率测量功能(使用输入捕获),做成一个简单的示波器或频谱分析仪。
  4. 学习RTOS:当系统任务复杂时,引入FreeRTOS等实时操作系统来管理任务,会让程序结构更清晰。

电赛的魅力在于将理论知识转化为实实在在的、能工作的系统。希望这篇长文能为你提供一条清晰的路径,助你在2026年电赛乃至未来的工程实践中,更加从容地应对挑战。建议收藏本文,在备赛过程中随时参考。

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

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

立即咨询