写在前面
🪓突然意识到自己已经8个月没更新博客了,这段时间其实也没遇上啥太复杂的事,大概就是一些沉淀之类的,决定了之后的学业去向,推免了本校的研究生。进一步认识了一群未来志同道合的的同门,激情爽玩塞尔达旷野之息和王国之泪,狠狠地治愈了一阵子的电子阳痿,以及培养了健身的习惯。
当然,电赛什么的也是有在准备的,由于团队其他两个都是硬件佬,他们承受着画板与焊接调试的水深火热,我只需要安安静静的写代码就足够了(笑),每天实验室上下班,把自己的部分搞定,然后去健身房锻炼一会,回来美美加个餐洗个澡,然后打打机去睡觉,也算是某种意义上的平静的生活,求之不得。毕竟当初决定打电赛说到底也只是为了提升自己,探索一下电子世界的奥妙,这么久走过来也是重在坚持,也希望最后能有一个好结果。
截止到写博客的时候,我们组的成绩省赛以省一并列第一出线,综合测评应该也能过线,山东大学国测复测不出意外的话,应该是国二以上。
这篇博客会分为上下两部分,上篇主要是比赛的过程和一些前置的准备(没有营养的碎碎念),下篇主要是一些算法具体的实现(一些情急之下的灵机一动,大佬轻锤)。
比赛过程
- 准备阶段
我们队伍一直以来训练的是仪表和高频题,一开始侧重FPGA高速采集和处理这一块,但是后面逐渐意识到,对于电赛的指标范围内好像也不需要这么高的频率,高载波带来的高采样率问题可以通过下混频缓解,而高频直采带来是的FIR滤波器的资源消耗成倍增加,以及数字域处理带来的资源占用和量化噪声问题。
这是我们准备的ZYNQ7100的平台,可以外接高速ADDA,板载DDR3.
但是吧,ZYNQ平台的综合速度和固化难度以及Verilog代码的验证难度,在分秒必争的竞赛里,如果没有长期的训练和准备,在比赛那四天着急上头去写,恐怕会翻车。
于是在后期又重回STM32H743的单片机平台,侧重模块叠加后的健壮性和代码的可复用性。
从训练开始,🪓就有意识地把一些算法,尤其是FFT分析这一块,写成了独立的函数去进行一些参数的分析,然后把其他的像是屏幕显示,按键输入,外设驱动,都封装成主循环中的函数,用参数和标志位去控制。不得不说在电赛的信号分析题中还是一种很好用的代码架构。
本次代码的组织结构,可以清楚的看到,是首先进行ADC采集,FFT计算,然后将参数传入Judge_ModeType函数计算出调制类型,然后进入相应的部分去进行分析和显示。
2.正式开始
比赛的那几天,第一天上午8点队友一拿到题目基本上就确定了是D题,然后就开始画系统框图,找模块,搭系统。
🪓是九点钟到的实验室。然后一看题,哦豁,Ma和Mf! 这不是老朋友吗,去年省赛这两位重量级,特别是Mf,让无数队伍刹羽而归。
其奇妙的多值和莫名其妙的算不准问题,难到了一大批电赛壬。
不过算法方面🪓早有应对,具体请看后文分析。
队友把混频模块弄好,确定好中频频率后,把混频后的信号通入ADC模块,此时🪓进行了简单的测试,确定了ADC采样率。
然后就开始了算法的编写。在第一天的中午就把Ma的计算搞定了,精度误差大概在0.05左右。
然后下午把Mf的算法也基本调试正确了,精度误差在0.2左右,有少量的多值问题,这时候时间已经是第一天晚上的8点,🪓进行了第一次备份。(时常备份真是好习惯)
这是工程的几次备份图,可以看到从7月17日确定了基本平台之后,就是几天一备份,在比赛那几天更是一天备份几次。
然后就是ASK,FSK,PSK的判别、解调以及FSK的h参数计算问题,总的说来,这个花费了🪓一番功夫,也算是最核心的代码部分。
第二天的中午完成了模拟调制和数字调制两大类的初步判断,下午进一步完成了模拟的AM,FM,CW的进一步判断,以及数字的ASK,FSK,PSK的进一步判断和解调。(充实的一天)
第二天晚上回寝室的时候🪓在和队友闲扯的时候突然想到了FSK的h参数计算方法,然后在小本本上记了下来。
第三天,🪓一到实验室就开始着手验证自己的想法,发现完全可以,直接芜湖起飞。
然后下午的时间就是加入射频开关和滤波器,放大器等原件,完成了系统的整体级联和最后解调波形的实时切换。
第三天的晚上其实整个系统已经级联并且测试好了。
最后一天就是一些锦上添花的功能,比如实时频谱显示和QPSK的判断以及高频载波下的解调以及准度的修正,在下午三点左右完成了最后的测试和封箱。
总的来说这几天的流程还是稳扎稳打,逐步推进的。我们团队一直以来配合的也很不错,效率贼高,最后呈现出来的效果就是电赛这三天每天晚上11点都回寝室睡觉了,第二天9点再到实验室,也还能完成所有的基础和提高指标并做出3项其他指标。不知不觉中完成了我们团队刚开始接触电赛的时的一个玩笑-希望以后能不熬夜就打完电赛。
作为东道主,半夜交作品也是很合理的罢。
3.省赛测评
封箱后的第二天就是省测了,总的来说还是蛮顺利的,所有功能都演示出来了,测评表的指标也没有很难的点。
测完直接和朋友出去快乐吃喝,等待综合测评名单。
4.综合测评
由于🪓理论上是纯软件队员(其实🪓也会画板子和焊板子,这就是EEer的素养),综合测评的硬件大业当然就交给我的两位大爹队友了,🪓只需要在旁边写个报告算个参数就好。
在准备硬件测评器件,我们测试了各种555电路,二极管检波,微分积分器,加法器,带通滤波器的电路图。
综合测评那天,一进现场,发现题目竟然是模拟计算器,用来解一个微分方程。
不过稍加分析,就会发现其本质是文氏桥正弦发生器,三角波发生器,两个积分器,一个微分器,一个加法器的组合。
这一里面的每一项单独拎出来都很简单,但是在当时时间比较紧促的情况下,我们虽然把电路全部级联出来了,但是在零状态的时候,并没有像计算的那样,产生一个33Hz的自激波形。
也许是中间的某项RC常数没有配置正确,也许是某一段未修正相差,也许是某一段的偏置需要消除,总是零状态的时候就是妹有出波形。
说到模拟计算电路,其实这是个展开来讲有非常多可以讲的话题,想进一步研究的同学可以移步CNPP大佬的模拟计算 https://hackaday.io/project/191142-analog-lorenz-attractor-computer/details
我们也就停止了后面的输入激励。静待比赛时间结束,毕竟这只是一个达标测试,就不冒进去做进一步的冒险了。
有一说一中午学校提供的饭菜还可以,菜品有土豆烧牛肉,木耳炒蛋,水煮虾,还有酸奶和水果,我TM吃吃吃。
4.国赛评测
等🪓玩回来更新
题目分析
题目分析(*的数量为重要程度)
- 题目的输入信号有100mv,属于一个比较大的信号,从信号源直出,通过SMA线输入,信噪比非常高,不需要考虑信号在无线传输过程中被干扰和多径效应等问题 *
- 载波频率2MHz,说实话,是一个比较微妙的频率,刚好超过了市面上单片机内置ADC的最大采样频率,但是对于FPGA外挂的50M左右的高速ADC来说(AD9225:没错正是在下),又看上去是一个很合适的频率。所以使用单片机的组一般会选择下混频,而使用FPGA的组如果选择直采,其实会遇到很多问题,比如FFT后频率分辨率不够,FIR资源消耗大带来的综合慢,数字下混频相位不对齐造成的失真和误差(更别提Vivado的FFT的IP核要想用好其实并不容易,定点FFT很容易产生很大的舍入误差)。所以从这也可以看出电赛的出题并不一味地要求好的器件,而更多的是因地制宜选择方案 **
- 要计算调幅度Ma,这就要求获得调制后信号的频谱,也就是要做FFT。这就要求ADC的采样频率能够高于中心频点+最大频偏之和的两倍。当然,实际上为了频谱的可读性和频率精确度的考量,一般选择4至6倍的采样频率*
- 要计算调频度Mf和最大频偏Δf,同上,还涉及到一些算法的多值问题,在后面详细讨论**
- 要进行模拟调制的解调,可以先通过混频把信号混到10.7M高中频,使用ADL5511 ,NE564等芯片进行解调 **
- 要识别ASK,FSK,PSK等调制方式,这就要求把它们的频谱差异提取出来并做好特征区分,以及对于某些非常相似情况下引入多重判断维度来进行区分 *。
- 要通过频谱进行FSK的h参数的计算,其实是很难的,但是我们可以通过FM和FSK的相似性,来进行一些取巧的操作,这个后面详细讨论**
- 要识别待调制波的频率,其实就是计算调制后波形的FFT相邻频点之间的间隔,这一点是由调制的性质所导致的,所有调制方式都能通过这种方式判断待调制波的频率*
- 要进行数字调制波形的解调,要结合不同调制方式的特点,混合使用模拟模块和数字域判决的方式来进行解调 *,比如ASK使用的是先通过模拟AD8310检波后通过直流量高低来判断0,1波形。FSK使用了数字FIR把频率转变为包络的变化,进行0,1的判决。而PSK使用了先数字域下混频和FIR的方式,把相位的跳变导致的相乘后的包络变化通过滤波器检出
- 要综合上述的识别和计算,通过模拟开关将解调结果通过一个通道显示在示波器上,并通过AGC等手段,保证电压大于1Vpp*
系统架构
本系统硬件框图如图所示,乍一看非常复杂(实际上也确实很复杂,最后数了一下,作品上一共有26块板子)。别急,让我们慢慢来,一步步分析,
由于输入信号的峰峰值是100mV,换算一下也就是-16dbm,所以首先经过24dB增益的低噪放ERA-3SM进行放大。
此时的信号幅度有8dbm,也就是峰峰值1.5Vpp,正好适合后面的各种器件的电压范围。
然后将信号通过功分器,同时混频到50kHz中频和10.7MHz中频。这是为了一路用来分析,一路用来模拟解调。
50kHz 的中频信号,经过滤波、放大和电平搬移后,一路经过ADS8688变为数字信号送入单片机。另一路经过检波器 AD8310 和滤波器 UAF42 后检出 ASK 的直流,并送给单片机进一步完成 ASK 信号的定时抽判。
10.7MHz 的中频信号经过陶瓷滤波器、放大器和射频开关后,分别进行基于 ADL5511 的 AM 包络检波解调和基于 NE564 的FM 解调.
最后通过 MCU 控制的模拟开关TMUX1109,将解调结果送给示波器显示。在单片机内,通过对 50kHz 的中频信号的频谱分析,得到调制信号类型和调制参数,
以及FSK和PSK的解调结果的输出,并控制其他模块完成最后结果的汇总输出,以及控制液晶屏显示识别结果和参数。
一些准备工作和小寄巧
电压和dbm的换算
在射频电路中,为了表示比较小的电压,一般采用对数形式,其中dbm是一个常用的单位,它是在某一阻抗匹配系统下,功率相对于1mW的比值,而由于已经射频电路阻抗一般是50欧姆,所以相当于也知道了电压。
不过由于功率的计算还涉及到波形的有效值啥的,方波1,正弦0.707,三角波0.57,实际中一般不会自己去算,都是使用这种在线计算器。
可以记忆一下一些常见的值
正弦波 | 50欧姆阻抗下 |
---|---|
-116dbm | 1uVpp |
-56dbm | 1mVpp |
-36dbm | 10mVpp |
-16dbm | 100mVpp |
0dbm | 632mVpp |
4dbm | 1Vpp |
14dbm | 3.3Vpp |
实用小工具 电压和dbm的换算
https://www.analog.com/cn/design-center/interactive-design-tools/dbconvert.html
注意这里的VPeak是峰值,换算成峰峰值的话要乘以2
ADC的一些准备工作
ADC选型
我们本次使用的是ADS8688A作为系统的ADC,它是一个16bit,500KSPS采样率的SAR型ADC。
它的特点有双极性输入,可配置动态范围,内部基准,八通道MUX采样,误差和漂移都很低,对于本题来说肯定是够用的。
我们用它主要还是看上了它支持双极性输入和可变动态范围这一点,这样就不用自己做前级的搬移和放大。
而且自带过压保护,比较耐造。
PS.我们的STM32H743平台的内置ADC,在某次测试的时候,大幅值采样做FFT后,其二次谐波值会大的超出常理,理论上来说是不会有这么大的
所以换用了这个外置的ADC,以后可以开一篇文章讲一下ADC的各种参数,以及这种失真是怎么来的,此时就要请出另一位小信号领域的expert,FloydFish🐟
可以移步https://www.emoe.xyz/opamp-noise-analyze/ ,以及相关的小信号测量。
驱动代码
这个的驱动我已经打包好了,下载下来改一下管脚直接用就可以了 https://megrez-hong.oss-cn-shanghai.aliyuncs.com/blogs/ADS8688_Driver.zip
ADC的驱动要注意的两点是,一是这个ADC的数据Latch和吐出是根据SPI的速率来的
所以为了控制采样速率,需要自己控制其中的IO操作后的Delay函数,经过测试,在ARMCC6编译器,O2水平的优化下,使用volatile参数,下面这种delay方法依然可以达到延时效果。
二是采样的时候需要关闭中断响应,因为我们不希望采样的时候定时器中断什么的把采样操作打扰了。
这里的采样率根据实测大概在250K左右,达不到官方的标称的500K。应该是已经达到了软件SPI的IO速度上限了,250kSPS*16bit = 4Mbit的速度了。
如果要更快一点,可以使用硬件SPI+DMA的方式,但是比赛当前,就不折腾花活了,能用就好。
另外一点是STM32的H7系列的软SPI的会有的一个毛病,在CubeMX中需要把这些个管脚(CLK,MISO,MOSI,CS)的最大频率设置成Low,否则如果设置为High之类的。
较大的驱动电流会造成信号的过冲和振铃,造成读出的数据有时是对的,有时读出的会是全1或者全0。
这一点我是在调试RDA5820的时候发现的,之前用STM32U5驱动RDA5820是正常的,同样的代码在H7下就会有时正确有时不能读出。后来在思考信号完整性链路的时候有了新的想法。
我个人的看法是,可能由于是H7的驱动电流能力比较强,IO翻转的上升沿相比F1,F7这些要陡峭很多,而数字信号链路是通过一段比较长的XH2.54线接到了外部的模块上,
根据传输线模型的推论,当传输线长度和1/6上升沿波长可以相比拟时,就有可能造成信号完整性问题。
所以这时IO驱出来的数字波形会有过冲和振铃等信号完整性问题也是可以理解的。
验证ADC采集时候,一般会串口打印波形,再使用SerialPlot查看,是不是和理论符合,比如下图就是ASK波形的采样结果。
FFT的准备工作
- 使用Cmsis DSP库,在Keil的包管理里面勾选即可,最新版本有窗函数的需要从官网上下载(怎么感觉有点似曾相识,我去年暑假好像也写过)
- 在Keil的设置里面,加入ARM_MATH_CM7, ARM_MATH_LOOPUNROLL这两条宏定义,前面是Cortex版本,需要是MCU的内核版本,可以是CM1,CM4,CM7,后面的是控制数学舍入的,一般来说不用动。
然后在include的地方加入 #include “arm_math.h” 和 #include “arm_const_structs.h”,然后开辟一个fft的全局数组,就可以愉快的调用啦。
FFT的调用**
/* ADC采样并做FFT 结果放在全局数组fft_outputbuf中 */
/* 做FFT 结果放在全局数组fft_outputbuf中 一次4096个点 */
void FFT(unsigned short *ADC_Buffer, unsigned int SampleRate, unsigned int len, int debug, int serialplot)
{
for(int i=0;i<Sampling_CNT;i++)
Global_ADC_Value[i] = ADC_Buffer[i]*3.3/4096; // 将采样结果转化到0-3.3V
for(int i=0;i < FFT_LENGTH;i++)
{
fft_inputbuf[i*2] = Global_ADC_Value[i]; // 按手册要求的实部虚部交替的方法填充数组
fft_inputbuf[2*i +1] = 0;
}
arm_cfft_f32(&arm_cfft_sR_f32_len4096,fft_inputbuf,0,1); // 执行FFT变换,arm_cfft_sR_f32_len4096为宏,定义旋转因子
arm_cmplx_mag_f32(fft_inputbuf,fft_outputbuf,FFT_LENGTH); // 把运算结果复数求模得幅值
/* Debug打印区 */
if(debug == 1)
for(int i=0;i < FFT_LENGTH/2;i++) // 是否打印FFT每个频点的幅值信息
printf("%d %.3lf KHz Mag %.3f\n", i,SampleRate*1.0/len*i, fft_outputbuf[i] );
if(serialplot == 1)
for(int i=0;i < FFT_LENGTH;i++) // 是否打印fft结果到到SerialPlot
printf("%.3f\n", fft_outputbuf[i]);
}
做完FFT后,我们一般会通过SerialPlot软件来查看FFT的结果和和理论估计是否符合。
下图是1KHz载波,3KHz频偏的FM频谱的实测图
代码组织的思路
这题是一个经典的测量-分析-显示的题目,所以采用的思路就是先采集,判断调制类型后,进一步去对应的部分进行进一步的分析和显示。
由于FFT分析后给的参数值不止一个,所以使用了函数传地址的办法。
而且由于Keil没有比较方便的代码补全和快捷提示功能,如果所有的算法都在main中实现,会导致代码实现那一段到下面的main函数的里调用段,有一段非常长的距离,所以我个人的建议是把一些更基础的算法代码另外封装到一个Algorithm文件里。
下节预告
- 模拟调制和数字调制两个大类的区分(中心载频与相邻频点之间的距离)
- 三种模拟调制的区分(CW, AM, FM)
- 三种数字调制的区分(ASK, FSK, PSK)
- 各种调制载波频率的计算(频谱最近相邻谱线的距离)
- AM调制的Ma的计算(寻峰算法)
- FM调制的Mf的计算(基于模式匹配的思想)
- FSK调制的h的计算(基于和FM的相似带来的模式复用)
- ASK的抽判(基于直流量的定时抽判)
- FSK的抽判(两路FIR后的抽判)
- PSK的抽判(满足特定频率的相位突变点抽判法)