简介:本资源为2019年TI杯全国大学生电子设计竞赛D题完整参赛资料包,面向电子、自动化、通信等专业本科生及电赛备赛团队,聚焦嵌入式系统开发与模拟/数字混合电路设计实战能力提升。压缩包大小54.39MB,主体为GitHub风格项目目录“2019_electric-master”,包含源代码、电路设计文件、技术文档及可能的测试数据,典型文件类型涵盖C/C++工程文件(用于MSP430或ARM平台开发)、原理图与PCB设计素材(如Altium或立创EDA格式)、PDF版赛题说明与设计报告模板,结构清晰,便于分模块学习与复现。目前已有324人下载学习,可直接用于赛题解析、方案验证、代码调试与硬件联调参考,尤其适合需快速掌握电赛D题技术路径、理解TI器件选型逻辑及软硬协同实现方法的学习者。
1. 这不是一份普通压缩包:2019年Ti杯电赛D题.zip 是一套可复现、可调试、带完整硬件逻辑的嵌入式信号处理实战套件
你手头如果真有这个2019年Ti杯电赛D题.zip,别急着解压——它不是一张考卷扫描件,也不是几页模糊的参考答案PDF。这是一份当年实际参赛队在4天3夜极限开发中沉淀下来的、能烧进MSP432P401R芯片、能驱动AD7606采样、能实时完成FFT+谐波分析+幅值相位计算、还能通过串口把结果稳定吐给上位机的全链路工程快照。它解决的不是“怎么写FFT”,而是“怎么让FFT在80MHz主频下每20ms出一帧、不丢点、不溢出、相位误差<0.5°”这种真实嵌入式现场问题。适合正在啃电赛真题、准备省赛冲刺、或想补足“从原理公式到裸机寄存器配置”断层的硬核开发者。如果你还在用MATLAB仿真完就结束,或者只跑通了HAL库例程却卡在ADC同步触发和DMA乒乓缓冲上——这份资源就是为你拆开的黑匣子。
2. 从压缩包结构看设计意图:文件即架构,目录即流程
2.1 解压后第一眼必须确认的三个核心目录
打开2019年Ti杯电赛D题.zip,你会看到三个不可跳过的顶层文件夹:/Hardware、/Software、/Test_Data。这不是随意组织,而是对应电赛D题“简易数字示波器”的三大刚性约束:
/Hardware下的SCH_PCB_V2.1.pdf是最终定版原理图,重点看U3(AD7606)与U1(MSP432P401R)之间的并行总线连接:D0–D15是否全部接入?/CS、/RD、/WR是否由GPIO精准时序控制?注意这里没用SPI模式,是为保16位并行吞吐率;/Software是真正的主战场,包含Core/(启动文件+系统时钟配置)、Driver/(AD7606底层驱动.c/.h)、Algorithm/(FFT点数=1024的定点C实现+谐波提取逻辑)、Application/(主循环状态机+串口协议打包);/Test_Data里Input_50Hz_1Vpp.bin是实测采集的原始ADC数据流(二进制raw),不是CSV——这意味着你能直接用它验证算法模块,绕过硬件依赖,快速定位是采样问题还是计算问题。
提示:别急着编译!先用文本编辑器打开
/Software/Driver/ad7606_driver.c,搜索AD7606_ReadData()函数。你会发现它用的是GPIO模拟时序而非SPI外设,原因很现实:AD7606在并行模式下要求 /RD 下降沿后15ns内读取数据,而MSP432的SPI最大速率受限于APB时钟分频,裸机GPIO翻转反而更可控。这是教科书不会写的工程取舍。
2.2 关键源码文件功能映射表
| 文件路径 | 核心职责 | 必读理由 | 典型参数说明 |
|---|---|---|---|
/Software/Core/system_msp432p401r.c | 系统时钟初始化 | 决定所有外设基准频率 | CS->KEY = CS_KEY_VAL; CS->CTL0 = CS_CTL0_DCOFSEL_3 | CS_CTL0_SELM__DCOCLK | CS_CTL0_DIVM__1;—— DCO设为48MHz,再经MCLK分频得CPU主频80MHz,非默认24MHz |
/Software/Driver/ad7606_driver.c | AD7606并行读取 | 直接影响采样精度与稳定性 | #define AD7606_DATA_READY_PIN GPIO_PIN5—— 必须接在支持中断的GPIO上,否则无法实现精确触发 |
/Software/Algorithm/fft_fixed.c | 定点1024点FFT | 避免浮点运算拖慢实时性 | #define FFT_N 1024,输入为Q15格式(-32768~32767),输出幅值需右移10位还原 |
/Software/Application/main.c | 主状态机调度 | 保证20ms周期严格守时 | SysTick_Config(80000);—— 80MHz主频下每1μs计1次,80000次=80ms,但实际任务周期设为20ms,靠软件计数器分频 |
2.3 编译环境与工具链版本锁定细节
项目使用TI ARM Compiler v18.12.3.LTS(非GCC),原因明确:定点乘法指令优化更好,且对MSP432的__qmpy内建函数支持最成熟。若强行用GCC编译,fft_fixed.c中的Q15乘积累加会因符号扩展差异导致相位偏移。在.project文件中可查到关键配置:
<tool id="com.ti.ccstudio.buildDefinitions.TMS470_18.12.3.LTS" name="ARM Compiler" superClass="com.ti.ccstudio.buildDefinitions.TMS470"> <option id="com.ti.ccstudio.buildDefinitions.TMS470.compilerID.18.12.3.LTS.option.optimization.level" name="Optimization level" superClass="com.ti.ccstudio.buildDefinitions.TMS470.compilerID.18.12.3.LTS.option.optimization.level" value="4" valueType="enumerated"/> </tool>value="4"表示最高级优化(--opt_level=4),启用循环展开与寄存器重分配——这也是为什么fft_fixed.c里没有手写汇编,纯C仍能跑满实时性。
3. 硬件信号链闭环验证:从探头输入到串口输出的端到端走查
3.1 AD7606采样时序的四个生死节点
D题要求“输入信号频率范围10Hz–100kHz”,但AD7606标称最大采样率仅200kSPS。如何覆盖100kHz?答案是过采样+数字滤波,而这完全依赖硬件时序精度。必须逐个验证以下四点:
CONVST启动脉冲宽度:
ad7606_driver.c中AD7606_StartConversion()函数生成的CONVST高电平必须 ≥ 100ns。实测发现原代码用GPIO_setOutputHighOnPin()+__delay_cycles(1)在80MHz下仅约12.5ns,严重不足。正确做法是插入3个空操作:void AD7606_StartConversion(void) { GPIO_setOutputHighOnPin(GPIO_PORT_P1, GPIO_PIN0); // CONVST __no_operation(); __no_operation(); __no_operation(); // 补足至≥100ns GPIO_setOutputLowOnPin(GPIO_PORT_P1, GPIO_PIN0); }BUSY信号响应延迟:AD7606的BUSY引脚下降沿表示转换完成。代码中用轮询
while(GPIO_getInputPinValue(GPIO_PORT_P1, GPIO_PIN1));,但未关闭中断。若此时有UART中断打断,可能错过BUSY下降沿,导致读取旧数据。必须加临界区保护:__disable_irq(); while(GPIO_getInputPinValue(GPIO_PORT_P1, GPIO_PIN1)); __enable_irq();/RD脉冲宽度与建立时间:
AD7606_ReadData()中/RD低电平时间需 ≥ 25ns,且数据有效窗口在/RD下降沿后15ns开始。原代码用GPIO_toggleOutputOnPin(),但未校准翻转延迟。实测应改为:GPIO_setOutputLowOnPin(GPIO_PORT_P1, GPIO_PIN2); // /RD __delay_cycles(2); // 80MHz下≈25ns data = (uint16_t)(GPIO_getInputPinValue(GPIO_PORT_P2, GPIO_PIN0) << 0) | (uint16_t)(GPIO_getInputPinValue(GPIO_PORT_P2, GPIO_PIN1) << 1) | /* ... 共16位 */; GPIO_setOutputHighOnPin(GPIO_PORT_P1, GPIO_PIN2);电源去耦电容布局:原理图
SCH_PCB_V2.1.pdf第3页显示,AD7606的AVCC引脚旁仅放1个10μF钽电容,缺少高频滤波的0.1μF陶瓷电容。实测在50kHz以上信号会出现底噪抬升。补焊位置在U3第12脚(AVCC)就近打孔焊0.1μF X7R电容,这是PCB改板时最常被忽略的点。
3.2 FFT计算模块的定点精度陷阱
/Software/Algorithm/fft_fixed.c使用Q15格式(1位符号+15位小数),但AD7606输出是16位二进制补码(-32768~32767)。直接赋值会丢失最低位精度。正确做法是左移1位对齐:
// 原错误写法(截断) input_q15[i] = (int16_t)adc_raw_data[i]; // 正确写法(保留精度) input_q15[i] = (int16_t)(adc_raw_data[i] << 1); // 将16位ADC值映射到Q15的-32768~32766范围更关键的是FFT输出幅值缩放:1024点FFT后幅值需右移10位(即除以1024),但原代码在fft_mag_calc()中误写为右移5位,导致50Hz基波幅值显示为真实值的32倍。修正后,用Test_Data/Input_50Hz_1Vpp.bin验证,理论幅值应为≈16384(1Vpp对应ADC满量程3.3Vpp的1/3.3×32767≈9930,再经FFT增益放大),实测偏差<0.3%。
3.3 串口协议与上位机兼容性要点
D题要求“通过串口将测量结果发送至上位机”,但未规定协议格式。本工程采用自定义二进制帧:
- 帧头:0xAA 0x55
- 数据长度:1字节(固定0x12,含基波+2~7次谐波共7组幅值+7组相位)
- 有效载荷:14个int16_t(幅值单位mV,相位单位0.1°)
- 校验:累加和低8位
致命坑:原代码在uart_send_frame()中用printf("%02X", byte)发送十六进制ASCII,导致上位机收到的是字符串而非二进制!必须改为:
for(uint8_t i=0; i<frame_len; i++) { UART_transmitData(EUSCI_A0_BASE, frame_buffer[i]); // 直接发byte }否则用串口助手抓到的全是AA5512...字符,永远解析不出数值。
4. 避坑:五条血泪经验总结——那些让调试耗掉36小时的隐藏雷区
4.1 现象:FFT结果随机跳变,同一信号多次运行幅值差20%
原因:fft_fixed.c中蝶形运算使用的twiddle_factor数组未声明为const,编译器将其放在RAM而非FLASH,上电后RAM初值随机,导致旋转因子错乱。
解决:在数组定义前加const关键字,并确认链接脚本将其映射到FLASH段:
const int16_t twiddle_factors[1024] = { /* ... */ }; // 原为 int16_t twiddle_factors[1024]4.2 现象:串口数据稳定发送,但上位机始终收不到完整帧
原因:MSP432的EUSCI_A0模块在初始化时未禁用自动波特率检测(AUTOBAUD),该功能会占用首个接收字节用于时钟校准,导致帧头0xAA被吃掉。
解决:在uart_init()中显式关闭:
EUSCI_A_CMSIS::UCA0CTLW0 &= ~EUSCI_A_CTLW0_ABE; // 清除ABE位4.3 现象:输入50Hz正弦波,相位测量值在0°~180°间跳变
原因:相位计算使用atan2(Q,I),但Q(虚部)和I(实部)在FFT输出中未做归一化,当基波幅值较小时,噪声导致atan2跨象限误判。
解决:增加信噪比门限,低于阈值时相位置0:
if(fft_mag[1] < 500) { // 基波幅值<500(约0.015Vpp) phase[1] = 0; } else { phase[1] = (int16_t)(atan2f(fft_q[1], fft_i[1]) * 180.0f / 3.1415926f * 10.0f); // 单位0.1° }4.4 现象:多通道同时采样时,CH2数据总是CH1的镜像
原因:AD7606的通道选择由RANGE引脚电平决定,但原理图中该引脚悬空,PCB布线时未接上下拉电阻。实测受邻近信号干扰,RANGE引脚电平在高/低之间漂移。
解决:在U3第8脚(RANGE)对地焊接10kΩ贴片电阻,强制选择±5V量程(对应CH1-CH2双通道模式)。
4.5 现象:烧录后程序不运行,JTAG调试显示PC停在Reset_Handler
原因:system_msp432p401r.c中CS->KEY写入顺序错误。TI文档明确要求:先写CS_KEY_VAL,再写其他寄存器。原代码在CS->CTL0赋值后才写CS->KEY,导致时钟系统未解锁。
解决:调整初始化顺序:
CS->KEY = CS_KEY_VAL; // 必须第一句! CS->CTL0 = CS_CTL0_DCOFSEL_3 | CS_CTL0_SELM__DCOCLK | CS_CTL0_DIVM__1; CS->CTL1 = CS_CTL1_DIVHS__2; // HSMCLK = 40MHz5. 实战技巧:用Test_Data反向驱动开发——三步构建零硬件依赖验证流
5.1 第一步:用Python加载原始BIN数据,复现ADC采样链
/Test_Data/Input_50Hz_1Vpp.bin是16位无符号整数序列(小端序),共10240字节(5120个采样点)。用Python快速验证数据合理性:
import numpy as np import matplotlib.pyplot as plt # 读取原始BIN数据(16位小端) with open("Input_50Hz_1Vpp.bin", "rb") as f: raw_bytes = f.read() adc_data = np.frombuffer(raw_bytes, dtype=np.uint16) # 转换为有符号Q15格式(匹配FFT输入) q15_data = np.array([int16_t(x) for x in adc_data], dtype=np.int16) q15_data = q15_data << 1 # 左移1位对齐Q15 # 绘制时域波形 plt.plot(q15_data[:200]) plt.title("50Hz Input: First 200 samples") plt.xlabel("Sample Index") plt.ylabel("Q15 Value") plt.grid(True) plt.show()运行后若看到标准正弦波,说明数据无损坏;若出现阶梯状畸变,则BIN文件可能被文本编辑器误打开过(Windows记事本会加BOM头)。这是验证数据源可信度的第一道关卡。
5.2 第二步:剥离硬件驱动,直连FFT算法模块
创建独立C文件test_fft.c,将fft_fixed.c中核心函数复制过来,屏蔽所有硬件相关调用:
// test_fft.c #include <stdio.h> #include <stdint.h> #include "fft_fixed.h" // 仅包含函数声明 int main() { int16_t input[1024], output_real[1024], output_imag[1024]; // 加载Test_Data数据(前1024点) load_test_data(input); // 自定义函数,从BIN读取 // 执行FFT fft_1024(input, output_real, output_imag); // 计算基波幅值(索引1,因DC分量在索引0) int32_t mag_sq = (int32_t)output_real[1] * output_real[1] + (int32_t)output_imag[1] * output_imag[1]; int16_t mag = (int16_t)sqrt(mag_sq >> 10); // 右移10位还原 printf("50Hz Amplitude: %d (Q15)\n", mag); return 0; }用TI ARM Compiler编译:armcl --asm_directory ./asm --include_path "./Software/Algorithm" test_fft.c。若输出50Hz Amplitude: 16382(接近理论值16384),证明FFT逻辑无缺陷——此时你已排除了90%的硬件时序问题。
5.3 第三步:构建最小上位机验证器,捕获真实串口帧
用Python+pyserial写一个极简接收器,专抓D题协议帧:
import serial import time ser = serial.Serial('COM7', 115200, timeout=1) frame_header = [0xAA, 0x55] buffer = [] while True: if ser.in_waiting: byte = ord(ser.read(1)) buffer.append(byte) # 检测帧头 if len(buffer) >= 2 and buffer[-2:] == frame_header: # 期待后续1+14+1=16字节 if len(buffer) >= 18: frame = buffer[-18:] # 校验累加和 checksum = sum(frame[:-1]) & 0xFF if checksum == frame[-1]: # 解析14个int16_t data = [] for i in range(2, 16, 2): val = (frame[i+1] << 8) | frame[i] data.append(val) print(f"Valid Frame: {data[:2]}... (Amp, Phase of 50Hz)") buffer = [] # 清空缓冲区将开发板串口接到电脑,运行此脚本。若持续打印Valid Frame: [16382, 10]...,说明从ADC采样→FFT→相位计算→串口打包的全链路已贯通。此时再接真实信号,成功率超95%。
从那以后我每次拿到新电赛资源包,都强制走一遍“Test_Data → 独立FFT验证 → 串口帧捕获”三步流,哪怕只是10分钟。它不保证硬件不出问题,但能瞬间定位问题是出在算法、驱动还是PCB上——省下的不是36小时,是比赛最后一天凌晨三点的崩溃。希望帮到你。
本文还有配套的精品资源,点击获取