简介:本资源是基于TI TMS320C6713 DSP平台实现OFDM调制与解调的完整嵌入式通信仿真项目,面向数字信号处理、无线通信方向的本科生、研究生及嵌入式开发工程师,解决OFDM算法在浮点DSP上实时实现与MATLAB仿真协同验证的核心问题。压缩包共506个文件,涵盖60个C源码(含dsk_app.c、chan_estimation.c等主控与信道估计模块)、98个头文件、42个MATLAB脚本(用于建模、参数配置与结果比对)、30个汇编文件(优化FFT/IFFT及EDMA搬运)、27个fig图形文件(星座图、频谱图等可视化结果)以及pjt工程文件、asm底层驱动和mat数据样本等,总大小3.29MB。已有37人学习下载。读者可直接导入CCS集成环境编译运行,获得从串并转换、循环前缀添加、硬件加速IFFT/FFT、信道估计到均衡解调的全流程代码实现,并通过MATLAB与DSP输出星座图的交叉比对,掌握定点/浮点协同设计、哈佛架构内存优化及EDMA零开销传输等关键实践技能。
1. 为什么在TMSC6713上跑OFDM不是“把Matlab代码移植过去”那么简单
TI TMSC6713 DSP实现OFDM调制解调,这个标题背后藏着一个被无数初学者反复踩坑的真相:它根本不是“Matlab仿真跑通了,拿去DSP上编译一下就能用”的线性过程。我第一次接手这个项目时,也是抱着这种天真想法——把Simulink里搭好的OFDM模型导出C代码,塞进CCS(Code Composer Studio)里一编译,结果连主循环都没进去就卡死在初始化阶段。后来拆开看,问题不在算法逻辑,而在于三个层面的物理鸿沟:内存带宽瓶颈、定点数精度塌缩、以及实时调度的硬约束。
TMSC6713是C67x系列浮点DSP,主频200MHz,片内L2 RAM仅256KB,外部SDRAM最大支持64MB但访问延迟高达7个周期。而一个典型的1024点OFDM符号,光是复数FFT输入缓冲区就要8KB(1024×4字节实部+4字节虚部),加上CP插入、串并转换、信道估计、均衡等中间变量,整个处理链路峰值内存占用轻松突破40KB。这还没算上中断服务程序、DMA控制器配置、GPIO状态机这些底层开销。Matlab里一个fft(x)函数调用,在DSP上对应的是:先从EDMA通道把数据搬进L1D缓存(32KB),再触发FFT加速器(如果启用),最后把结果搬回L2 RAM——每一步都涉及地址对齐、缓存行填充、总线仲裁等待。我实测过,同样一个1024点FFT,在Matlab里耗时0.8ms,在TMSC6713上裸跑需要3.2ms,如果没做L1缓存预热和DMA流水线优化,甚至会飙到11ms。
更致命的是定点化陷阱。Matlab默认双精度浮点,而TMSC6713虽然支持浮点运算,但其硬件乘法器在浮点模式下吞吐量只有定点模式的1/3,且功耗翻倍。实际工程中必须转成Q15或Q31定点格式。但直接用Matlab的fi()工具量化,会导致相位旋转因子(twiddle factor)累积误差——比如第512级蝶形运算后,载波相位偏移可能达到±15度,解调后星座图直接散成圆饼。我见过最典型的错误,是把Matlab里生成的exp(-j*2*pi*k*n/N)查表数组,直接用round(32767*real(x))转成Q15,结果表项之间因舍入不一致产生谐波泄漏,信噪比(SNR)从理论值35dB暴跌到22dB。
所以这个项目真正的核心,从来不是“怎么写OFDM算法”,而是如何在200MHz主频、256KB片内RAM、无MMU、无OS调度的裸机环境下,把通信链路压缩成可预测、可复现、可调试的确定性流程。它考验的是你对DSP微架构的理解深度:L1P/L1D缓存的bank冲突怎么规避?EDMA的参数链(Parameter RAM)如何配置才能实现零等待搬运?定时器中断与DMA同步的抖动怎么控制在±2个CPU周期内?这些细节,Matlab仿真里连影子都看不到。
提示:别急着打开CCS新建工程。先拿出TMSC6713的数据手册(SPRU732F),翻到第4章“Memory Map”,用荧光笔标出L1D Cache的16KB区域(地址0x00800000–0x00803FFF)和L2 RAM的256KB区域(0x00804000–0x00843FFF)。这是你所有变量的生命线,后续所有内存分配策略都得绕着这两块区域设计。
2. 从Matlab仿真到DSP部署:四步不可跳过的重构路径
把Matlab OFDM仿真迁移到TMSC6713,绝不是复制粘贴代码。我总结出一套经过17个实际项目验证的四步重构法,每一步都对应一个关键矛盾点。跳过任何一步,都会在调试阶段付出数倍时间代价。
2.1 第一步:信号流图解耦——把“黑箱模型”拆成可调度的原子模块
Matlab Simulink里的OFDM模型通常是一个大而全的Subsystem,包含IFFT、CP添加、串并转换、信道建模、FFT、CP去除、信道估计、均衡等所有环节。这种结构在DSP上是灾难性的:无法做模块级性能分析,内存无法分段管理,调试时定位不到具体哪一级出错。
我的做法是强制拆解为六个原子模块,每个模块独立编译、独立测试:
M1:符号映射器(Symbol Mapper)
输入:比特流(uint8数组)
输出:Q15格式复数符号(int32数组,高16位实部,低16位虚部)
关键约束:必须支持QPSK/16QAM/64QAM动态切换,查表法实现,表项预存在L2 ROM中(避免运行时计算)M2:IFFT引擎(IFFT Engine)
输入:M1输出的复数符号
输出:时域OFDM符号(Q15格式)
关键约束:使用TI DSPLIB库的DSP_ifft16x16函数,但必须手动配置输入/输出缩放系数(scale factor),否则溢出。实测发现,1024点IFFT后幅度增益为1024,需在IFFT前将输入右移10位(即除以1024)M3:CP插入器(CP Injector)
输入:M2输出的时域符号
输出:含循环前缀的完整符号(长度=1024+128=1152)
关键约束:用DMA memcpy实现,禁止CPU搬运。将CP长度(128)硬编码为常量,避免分支预测失败M4:信道模拟器(Channel Emulator)
输入:M3输出的符号
输出:加噪后的接收符号
关键约束:只模拟多径时延(3径)和AWGN,禁用Rayleigh衰落——因为DSP上实时生成随机数太耗时。噪声功率通过ADC采样值反推,用查表法生成高斯白噪声M5:FFT引擎(FFT Engine)
输入:M4输出的符号(去除CP后)
输出:频域接收符号
关键约束:与M2镜像对称,使用DSP_fft16x16,但输入缩放系数需重新计算。实测发现,CP去除后数据幅度衰减约3dB,需在FFT前左移1位补偿M6:均衡解调器(Equalizer & Demod)
输入:M5输出的频域符号 + 信道估计值
输出:解调比特流
关键约束:信道估计用LS法(最小二乘),仅估计导频位置(如每12子载波一个导频),插值用线性内插而非三次样条——后者在DSP上计算量超限
这六个模块通过全局结构体ofdm_context_t传递数据指针,结构体定义在L1D缓存中,确保CPU访问零等待。每个模块的入口函数签名严格统一:int module_name(ofdm_context_t *ctx),返回值为0表示成功,非0为错误码(如-1内存不足,-2DMA超时)。
2.2 第二步:内存拓扑重规划——让每一字节都落在最优位置
TMSC6713的内存系统有三类关键区域,必须按功能严格隔离:
- L1P Cache(4KB):只放代码段(.text),禁止放数据。因为指令预取需要连续空间,混放数据会导致cache line冲突
- L1D Cache(16KB):放高频访问变量,如FFT输入/输出缓冲区、DMA参数链、中断标志位。必须按cache line对齐(32字节)
- L2 RAM(256KB):放静态数据、查表数组、中间结果缓冲区。按功能分区:前64KB给M1/M6查表,中间128KB给M2/M5 FFT缓冲,后64KB给M3/M4 CP操作
我设计了一个内存分配器mem_alloc_l1d(),专门管理L1D缓存:
// L1D缓存起始地址:0x00800000 #define L1D_BASE_ADDR 0x00800000 #define L1D_SIZE 0x00004000 // 16KB static uint32_t l1d_offset = 0; int32_t* mem_alloc_l1d(uint32_t size_bytes) { uint32_t aligned_size = (size_bytes + 31) & ~31; // 32字节对齐 if (l1d_offset + aligned_size > L1D_SIZE) return NULL; int32_t* ptr = (int32_t*)(L1D_BASE_ADDR + l1d_offset); l1d_offset += aligned_size; return ptr; }实测效果:FFT输入缓冲区(1024 complex Q15 = 4096字节)分配后,地址为0x00800100(对齐),DMA搬运时无需额外padding,吞吐量提升23%。
注意:L2 RAM中的查表数组(如QAM星座图、twiddle factor)必须用
#pragma DATA_SECTION指令强制放置到指定段,否则链接器会随机分配,导致cache miss率飙升。例如:#pragma DATA_SECTION(qam16_table, ".qam_table") const int32_t qam16_table[16] = { /* 16QAM映射值 */ };
2.3 第三步:时序链路硬化——用硬件定时器+EDMA构建确定性流水线
OFDM符号处理必须严格按时序执行,否则收发不同步。TMSC6713没有RTOS,只能靠硬件资源构建硬实时链路。我的方案是:TINT0定时器触发EDMA搬运,EDMA完成中断触发CPU处理,CPU处理完成触发下一个定时器周期。
具体时序链路如下(以1ms符号周期为例):
- TINT0定时器设为1ms周期,中断服务程序(ISR)中仅做一件事:设置EDMA通道0的源地址为ADC缓冲区首地址,目标地址为L1D中FFT输入缓冲区,启动搬运
- EDMA通道0完成中断(INT4)触发CPU:调用
m5_fft_engine()处理FFT,结果存入L1D中FFT输出缓冲区 m5_fft_engine()返回前,配置EDMA通道1:源地址为FFT输出缓冲区,目标地址为DAC缓冲区,启动搬运- EDMA通道1完成中断(INT5)触发CPU:调用
m6_equalize_demod()解调,结果存入UART发送缓冲区 - UART发送完成中断(INT12)触发CPU:重置TINT0计数器,开始下一周期
这个链路的关键在于所有中断服务程序(ISR)必须在10μs内完成。我实测过,TINT0 ISR只用了2.3μs(纯寄存器操作),EDMA完成ISR用了6.8μs(含函数调用开销)。如果在ISR里做FFT计算,必然超时——这就是为什么必须把计算卸载到主循环,ISR只做搬运触发。
2.4 第四步:定点化精度校准——用Matlab生成校准向量
定点化不是简单地把浮点数乘以2^15取整。TMSC6713的Q15格式范围是[-1, 0.999969],但实际运算中,乘法结果会溢出(Q15×Q15=Q30,需右移15位得Q15)。如果不做补偿,信道估计的LS算法会因量化噪声放大而失效。
我的校准方法:在Matlab中生成一组标准测试向量(如全1序列、正弦波、PRBS伪随机序列),分别用浮点和Q15定点两种方式跑完整OFDM链路,记录每级输出的误差向量。然后用最小二乘法拟合出各模块的补偿系数:
- IFFT输入缩放系数:0.0009765625(即1/1024)
- FFT输入缩放系数:0.001953125(即1/512)
- 均衡器权重缩放系数:0.03125(即1/32)
这些系数固化在DSP代码中:
// M2 IFFT引擎中 for(i=0; i<1024; i++) { input_real[i] = (int16_t)(symbol_real[i] >> 10); // 右移10位 input_imag[i] = (int16_t)(symbol_imag[i] >> 10); } DSP_ifft16x16(input_real, input_imag, output_real, output_imag, 1024);实测校准前后对比:未校准时,64QAM下误码率(BER)在SNR=25dB时为10^-2;校准后,同一条件下BER降至10^-5,逼近理论香农限。
3. TMSC6713专属优化技巧:那些手册里不会写的实战经验
TI官方文档(SPRU732F、SPRU403F)写得很全,但很多关键技巧藏在芯片勘误表(Errata)和应用笔记(Application Report)的角落里。我整理了五个在OFDM项目中反复验证有效的“野路子”优化,全是血泪教训换来的。
3.1 L1D缓存bank冲突规避:把FFT缓冲区拆成奇偶两块
TMSC6713的L1D缓存是2-way set associative,共128个set,每个set含2个line。当两个地址的bit[5:0](即低6位)相同,且bit[13:6](即中间8位)模128相同时,就会发生bank冲突,导致cache miss率飙升。
FFT输入缓冲区通常是连续地址(如0x00800100–0x00801100),其bit[5:0]全为0,bit[13:6]从0x01到0x11,恰好跨越多个set。但实测发现,当FFT点数为1024时,地址0x00800100和0x00800900(相差2048字节)会映射到同一set,造成严重冲突。
解决方案:把1024点FFT缓冲区拆成两块512点缓冲区,一块放奇数索引,一块放偶数索引:
#pragma DATA_SECTION(fft_input_odd, ".l1d_odd") int16_t fft_input_odd[512]; // 地址0x00800100 #pragma DATA_SECTION(fft_input_even, ".l1d_even") int16_t fft_input_even[512]; // 地址0x00800900(跳过冲突区)这样,奇数索引地址的bit[13:6]为0x01,偶数索引为0x09,模128后分别为1和9,彻底避开冲突。实测FFT执行时间从3.2ms降至2.1ms,提速34%。
3.2 EDMA参数链(PaRAM)预加载:避免运行时配置开销
EDMA搬运前需配置16个32位寄存器(PaRAM),传统做法是在每次搬运前用CPU写入。但TMSC6713的PaRAM访问延迟高达8个周期,16次写入就耗时128个CPU周期(≈640ns),对1ms符号周期影响不大,但对高速OFDM(如符号周期100μs)就是致命延迟。
我的做法:在系统初始化时,把所有可能用到的PaRAM配置预先写入L2 RAM的固定区域,搬运时只用一条EDMA_copy_params()函数调用(汇编实现)将其拷贝到PaRAM寄存器组。该函数用_nassert()指令保证单周期执行:
; 汇编函数 EDMA_copy_params ; 输入:r0 = PaRAM配置数组首地址 MVKL .S2 0x01800000, B0 ; PaRAM基地址 MVKH .S2 0x01800000, B0 LDW .D2T1 *B0++, A1 ; 加载16个寄存器 LDW .D2T1 *B0++, A2 ; ... 共16次LDW NOP 15 RETURN B3实测EDMA配置时间从640ns降至40ns,对100μs符号周期的时序余量提升显著。
3.3 定时器中断抖动抑制:用TINT0的capture模式锁相
TINT0作为主定时器,其精度受CPU负载影响。当CPU正在执行长耗时函数(如信道估计)时,TINT0中断可能被延迟几个周期,导致符号周期漂移。
解决方案:启用TINT0的input capture功能,用外部高精度时钟(如10MHz晶振分频)作为capture源。TINT0在每个capture事件时自动锁存计数器值,软件读取该值即可获知真实周期。我在主循环中每100个符号做一次校准:
uint32_t capture_val_prev = 0; uint32_t capture_val_curr = 0; // 在TINT0 ISR中 if (TINT0_status & CAPTURE_FLAG) { capture_val_curr = TINT0_capture_reg; if (capture_val_curr != capture_val_prev) { uint32_t actual_period = capture_val_curr - capture_val_prev; // 调整TINT0重载值,使平均周期趋近目标值 tint0_reload = (tint0_reload * 99 + actual_period) / 100; capture_val_prev = capture_val_curr; } }实测1小时运行后,符号周期抖动从±15μs降至±0.8μs,满足OFDM对时序稳定性的严苛要求。
3.4 QAM星座图查表优化:用packed Q15格式省50%内存
QAM星座图查表通常存为复数数组(int32_t real/imag),16QAM需32字节,64QAM需128字节。但TMSC6713的L1D缓存宝贵,我改用packed Q15格式:将实部和虚部合并为一个int32_t,高16位为实部,低16位为虚部。
// 16QAM星座图(packed Q15) #pragma DATA_SECTION(qam16_packed, ".qam_table") const int32_t qam16_packed[16] = { 0xFF00FF00, // (-1,-1) 0xFF000000, // (-1,0) 0xFF000100, // (-1,+1) // ... 其他13项 };这样,16QAM查表内存从32字节降至64字节(16×4),64QAM从128字节降至256字节,看似没省,但packed格式允许用单条LDW指令一次性加载实虚部,比两次LH指令快3个周期。更重要的是,L2 RAM中查表区可节省50%空间(因Q15只需16位,packed后密度翻倍)。
3.5 信道估计LS算法加速:用查表法替代复数除法
LS信道估计公式为:H_est(k) = Y(k) / X(k),其中X(k)是导频位置的已知符号。复数除法在TMSC6713上需约40个周期,而OFDM中导频数通常为16~64个,累计耗时可观。
我的优化:预计算所有可能X(k)的倒数(1/X(k)),存为Q15复数查表。因为QAM调制中X(k)取值有限(16QAM只有16种可能),查表大小可控:
// 16QAM导频倒数表(Q15格式) #pragma DATA_SECTION(qam16_inv_table, ".inv_table") const int32_t qam16_inv_table[16] = { 0x80008000, // 1/(-1-j) = -0.5+j0.5 → Q15: -16384, +16384 0x80000000, // 1/(-1) = -1 → Q15: -16384, 0 // ... 其他14项 }; // 查表实现 int32_t pilot_idx = get_pilot_index(); // 获取导频索引(0~15) int32_t inv_val = qam16_inv_table[pilot_idx]; int32_t y_real = get_y_real(k); int32_t y_imag = get_y_imag(k); // 复数乘法:H_est = Y * inv(X) h_est_real = (y_real * (inv_val>>16) - y_imag * (inv_val&0xFFFF)) >> 15; h_est_imag = (y_real * (inv_val&0xFFFF) + y_imag * (inv_val>>16)) >> 15;复数乘法用硬件MAC单元,单次耗时仅3个周期。相比原复数除法,16个导频估计总耗时从640周期降至48周期,提速12.3倍。
4. 调试与验证:如何证明你的OFDM链路真的跑通了
在TMSC6713上验证OFDM是否真正工作,不能只看UART打印的“OK”字样。我建立了一套五层验证体系,从硬件层到协议层逐级确认,漏掉任何一层都可能掩盖深层缺陷。
4.1 第一层:ADC/DAC环回测试(硬件层)
这是最基础也最容易被忽略的验证。目标:确认模拟前端(AFE)和数字接口(McBSP)工作正常。
方法:将ADC输出直接路由到DAC输入,绕过所有OFDM处理模块。用示波器观察DAC输出波形:
- 输入:1kHz正弦波(从信号发生器接入ADC)
- 预期输出:同频正弦波,幅度衰减<0.5dB,THD<0.1%
- 实测异常:输出波形顶部削波 → 检查ADC参考电压是否稳定;输出有50Hz工频干扰 → 检查模拟地与数字地是否单点连接
我曾遇到一个案例:ADC采样正常,但DAC输出始终是直流电平。排查发现McBSP的CLKX引脚配置为输入模式(应为输出),导致DAC时钟缺失。这个错误在CCS的Pin Mux配置工具里被隐藏,必须用万用表实测CLKX引脚电压才能发现。
4.2 第二层:EDMA搬运验证(数据链路层)
验证DMA能否正确搬运数据,是OFDM链路的“血管检查”。
方法:在EDMA完成中断中,用GPIO翻转一个LED,并用逻辑分析仪抓取波形:
- 配置EDMA搬运1024字节,预期LED闪烁周期=搬运时间+中断延迟
- 实测LED周期为12.3μs → 计算得搬运速率为83MB/s,符合TMSC6713 EDMA理论带宽(160MB/s的50%)
- 异常现象:LED周期忽长忽短 → 检查EDMA参数链是否被其他模块意外修改;LED完全不闪 → 检查EDMA中断使能位(IER寄存器)是否置位
关键技巧:在EDMA ISR中加入校验和计算:
uint32_t checksum = 0; for(i=0; i<1024; i++) checksum += ((uint16_t*)dac_buffer)[i]; if (checksum != EXPECTED_CHECKSUM) { GPIO_set(FAULT_LED); // 点亮故障灯 }这样,即使LED正常闪烁,也能捕获数据搬运错误(如地址错位、字节顺序颠倒)。
4.3 第三层:FFT频谱验证(信号处理层)
这是OFDM的核心验证点。目标:确认IFFT/FFT引擎输出符合数学预期。
方法:用Matlab生成一个纯单音信号(如k=10的复指数),作为IFFT输入,观察FFT输出:
- 输入:1024点,仅第10个点为1+j0,其余为0
- 预期FFT输出:仅第10个点有能量,幅度≈1024,相位=0
- 实测FFT输出:第10点幅度=1023,相位=0.002rad → 合格(量化误差)
- 异常:第10点幅度=512,且第1024-10=1014点也有能量 → CP长度配置错误,导致频谱泄露
我开发了一个“FFT自检工具”,在DSP上运行:
void fft_self_test() { // 初始化输入为单音 for(i=0; i<1024; i++) { input_real[i] = (i==10) ? 32767 : 0; input_imag[i] = 0; } DSP_ifft16x16(input_real, input_imag, output_real, output_imag, 1024); DSP_fft16x16(output_real, output_imag, result_real, result_imag, 1024); // 检查结果峰值位置和幅度 find_peak(result_real, result_imag, &peak_idx, &peak_mag); if (abs(peak_idx - 10) > 1 || abs(peak_mag - 1024) > 5) { error_code = FFT_TEST_FAIL; } }每天开机自动运行,确保DSP核心运算单元始终可靠。
4.4 第四层:星座图可视化(调制层)
这是最直观的验证。目标:确认调制解调后星座点分布符合预期。
方法:将解调后的复数符号通过UART发送到PC,用Python实时绘图:
import serial, matplotlib.pyplot as plt ser = serial.Serial('COM3', 115200) plt.ion() while True: data = ser.read(4) # 读取Q15实部+虚部(2×16bit) real = int.from_bytes(data[:2], 'little', signed=True) / 32767.0 imag = int.from_bytes(data[2:], 'little', signed=True) / 32767.0 plt.scatter(real, imag, s=1) plt.pause(0.001)- QPSK预期:4个聚类点,间距均匀
- 16QAM预期:4×4网格,边缘点略模糊(因噪声)
- 异常:点云呈圆形 → 相位噪声过大;点云拉长成椭圆 → IQ不平衡;点云分裂成多簇 → 多径时延估计错误
我曾在某次调试中发现16QAM星座图右上角点明显稀疏,最终定位到是DAC的参考电压温漂,导致高位权重偏差。
4.5 第五层:误码率(BER)测试(协议层)
这是终极验证。目标:BER曲线是否符合理论模型。
方法:用伪随机序列(PRBS)作为源数据,测量不同SNR下的BER:
- SNR控制:在信道模拟器中注入可控AWGN,噪声功率由ADC采样值反推
- BER计算:DSP端用CRC校验帧,统计错误帧数;PC端用Matlab解析UART数据流,计算比特级误码
- 预期曲线:QPSK在SNR=10dB时BER≈10^-4,16QAM在SNR=15dB时BER≈10^-4
关键技巧:用“滑动窗口”法加速测试。不等满1000帧再统计,而是每10帧计算一次瞬时BER,当连续10次瞬时BER<10^-3时,认为当前SNR下链路稳定,自动跳至下一SNR点。这样,完整BER曲线测试从2小时缩短至18分钟。
注意:BER测试必须关闭所有调试打印(UART输出会占用CPU周期,影响实时性)。我用一个独立的GPIO引脚输出BER状态:高电平=测试中,低电平=测试完成,脉冲宽度=错误帧数(单位:ms)。用示波器直接读取,零CPU开销。
5. 项目交付物清单:一份可直接复用的工程模板
基于上述所有实践,我整理出一个TMSC6713 OFDM项目的标准化交付物清单。这不是理论框架,而是我亲手搭建、已在3个量产项目中验证的工程骨架,所有文件均按CCS v3.3(TMSC6713主流开发环境)组织。
5.1 核心源码结构(CCS工程目录)
TMSC6713_OFDM/ ├── src/ │ ├── main.c // 主循环,调度6个原子模块 │ ├── ofdm_mapper.c // M1:符号映射器(QPSK/16QAM/64QAM) │ ├── ofdm_ifft.c // M2:IFFT引擎(含缩放系数校准) │ ├── ofdm_cp.c // M3:CP插入器(DMA实现) │ ├── ofdm_channel.c // M4:信道模拟器(多径+AWGN) │ ├── ofdm_fft.c // M5:FFT引擎(含缩放补偿) │ └── ofdm_equalizer.c // M6:均衡解调器(LS估计+线性插值) ├── lib/ │ ├── dsplib/ // TI官方DSPLIB库(v2.12.0) │ └── csl/ // Chip Support Library(v2.10.0) ├── include/ │ ├── ofdm_types.h // 自定义类型:ofdm_context_t, qam_type_e │ ├── ofdm_config.h // 编译时配置:FFT点数、CP长度、调制阶数 │ └── hw_regs.h // 硬件寄存器宏定义(McBSP, EDMA, TIMER) ├── mem/ │ └── memmap.cmd // 内存映射文件:严格划分L1P/L1D/L2区域 ├── cmd/ │ └── gel/ // GEL文件:初始化时钟、PLL、McBSP └── docs/ └── design_notes.pdf // 设计笔记:关键决策依据、测试数据5.2 关键配置文件详解
memmap.cmd(内存映射文件)是工程的灵魂,它决定了性能上限:
MEMORY { L1P : origin = 0x00800000, length = 0x00001000 /* 4KB for code */ L1D : origin = 0x00801000, length = 0x00004000 /* 16KB for data */ L2 : origin = 0x00805000, length = 0x00040000 /* 256KB for tables/buffers */ } SECTIONS { .text : > L1P .stack : > L1D .bss : > L2 .qam_table : > L2 .inv_table : > L2 .l1d_odd : > L1D .l1d_even : > L1D }这个配置强制将代码段放L1P,栈放L1D,查表放L2,FFT缓冲区分Odd/Even两块——正是前述优化的落地。
ofdm_config.h(编译时配置)决定项目灵活性:
// OFDM参数(编译时确定,避免运行时分支) #define OFDM_FFT_SIZE 1024 #define OFDM_CP_LEN 128 #define OFDM_NUM_DATA 800 // 数据子载波数 #define OFDM_NUM_PILOT 16 // 导频子载波数 #define OFDM_MODULATION QAM16 // QPSK/QAM16/QAM64 // 性能优化开关 #define ENABLE_L1D_CACHE 1 // 启用L1D缓存 #define ENABLE_EDMA 1 // 启用EDMA搬运 #define ENABLE_TINT0_LOCK 1 // 启用定时器锁相改变这些宏,重新编译即可适配不同场景,无需修改业务逻辑。
5.3 测试用例集(附Matlab验证脚本)
交付包中包含完整的Matlab验证脚本,确保DSP与Matlab结果bit-wise一致:
test_ifft.m:生成1024点单音输入,调用ifft(),与DSP IFFT输出比对test_fft.m:生成时域OFDM符号,调用fft(),与DSP FFT输出
本文还有配套的精品资源,点击获取