如前篇所介绍,用Shell Lab测试台软件配合之前介绍的任意款实验板,都能方便地实现ADC电压测量,但遇到两个问题:
- 示例代码虽然众多,但大都默认ShellLab类型的控制器,需要手动改为Mcush.Mcush类型才能运行,比较繁琐。
- 基础的adc命令只能进行低速测量,不能控制采样率实现高速测量(需要更高阶的ShellLab采集器的daq指令)。
为了解决上述问题,将Shell Lab T1采集器固件做了部分移植,适配一批资源较为充裕的实验板,定制成专用固件(简称“Shell Lab社区版固件”系列)解决上述两个问题,方便大家实验。
优先移植到STM32F401CC/411CE实验板上,参考介绍:
PengShulin:用Python控制硬件33-基于STM32F401CC和STM32F411CE的迷你实验板zhuanlan.zhihu.com移植完成的“Shell Lab社区版固件”下载地址:
F401CC板子:
http://www.linkongsoft.com/shell-lab/firmware/ShellLabCommunity_WeActF401CC.bin
http://www.linkongsoft.com/shell-lab/firmware/ShellLabCommunity_WeActF401CC.hex
F411CE板子
http://www.linkongsoft.com/shell-lab/firmware/ShellLabCommunity_WeActF411CE.bin
http://www.linkongsoft.com/shell-lab/firmware/ShellLabCommunity_WeActF411CE.hex
烧入固件(注意BIN二进制代码需指定写入地址0x08000000),完成后连上USB串口终端,看看有哪些变化(以F401CC为例)?
$ dmesg
...
[683194.776689] usb 1-4: new full-speed USB device number 117 using xhci_hcd
[683194.962409] usb 1-4: New USB device found, idVendor=ffff, idProduct=ffff
[683194.962422] usb 1-4: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[683194.962429] usb 1-4: Product: Shell Lab Community Version
[683194.962436] usb 1-4: Manufacturer: Linkong Software
[683194.962442] usb 1-4: SerialNumber: 31003B001351353234363331
[683194.963681] cdc_acm 1-4:1.0: ttyACM0: USB ACM device
$
USB设备类型变成:“Shell Lab Community Version”
=>*idn?
ShellLab-WeAct-F401CC,1.0
31003B001351353234363331
=>
设备类型识别变成ShellLab。
=>daq --help
usage: daq [-c <command>] [-i <index>] [-v <value>]
options:-c/--cmd info|(de)init|start|stop|reset|done|read|freq|vref|channel_mask-i/--idx index param-v/--val value param
=>
支持daq命令,用于指定采样率测量波形。
下面打开Shell Lab测试台软件,调出示例脚本“daq”-->“FFT analysis”:
准备信号发生器,生成1KHz正弦波(偏置1.5V,幅度1V),输入PA0引脚,点击“运行”:
可以看到采到了0.016秒的波形(包含4096个采样点),下图是FFT分析结果(分析带宽是采样率的一半:256kHz/2=128kHz),并在较大的几个峰位置标注了幅度。
点击导航栏的方向箭头按钮,然后用鼠标右键可以拖动图表区,实现缩放移动功能,以便观察细节。动态效果:
256k采样率采集波形并FFT分析https://www.zhihu.com/video/1182229714634170368继续换几种波形看看:
三角波,可以看到谐波的组成与方波相似(1/3/5/7...),只是衰减地很快:
锯齿波,可以看到谐波组成的变化(1/2/3/4...):
调幅波,中心频率和两个边频:
更多的波形请读者自行实验了。下面分析一下完整的示范脚本代码(加了中文注释):
# 6 - daq - FFT analysis
# 脚本开始先确定采样参数(通道、采样率、点数)
CHANNEL = 0 # input channel
SAMPLE_RATE = 256000 # HZ
SAMPLE_LENGTH = 4096
SAMPLE_TIME = SAMPLE_LENGTH / float(SAMPLE_RATE)
# FFT峰值检测标注的最大数量限制
PEAKS_LIMIT = 10
# FFT是否需要加窗函数的开关
ADD_WINDOW = False
WINDOW_TYPE = 'hamming'# 下面切换至绘图区域,准备几个字图,每个都有命名,方便后续控制
# 这是FFT相位子图的开关,默认关闭,开启后会增加一个子图
FFT_PHASE_PLOT = False # True
p = getPlotPanel()
p.addPlot( 'wave', 211, label_y='Signal voltage' )
p.setLimit( 'wave', top=3.3, right=SAMPLE_TIME, auto=False )
p.setLegend( 'wave','Channel %d'% CHANNEL )
if FFT_PHASE_PLOT:p.addPlot( 'fft', 413, label_y='FFT Amplitude', label_x="Frequency (Hz)" )p.setLimit( 'fft', right=SAMPLE_RATE/2, auto=False )p.addPlot( 'fft2', 414, label_y='FFT Phase', label_x="Frequency (Hz)" )p.setLimit( 'fft2', right=SAMPLE_RATE/2, auto=False )
else:p.addPlot( 'fft', 212, label_y='FFT Amplitude', label_x="Frequency (Hz)" )p.setLimit( 'fft', right=SAMPLE_RATE/2, auto=False )# 创建控制器对象并初始化
s = ShellLab(PORT)
s.daq_init( freq=SAMPLE_RATE, length=SAMPLE_LENGTH, channels=[CHANNEL] )while True:# 启动测量s.daq_start()# 循环等待测量结束while True:done = s.daq_done()info('Sampling (%d / %d)'% (done, SAMPLE_LENGTH))if done >= SAMPLE_LENGTH:breaktime.sleep(0.01)# 测量完成,读数据(因为只有一个通道,传入第0通道的参数),返回数据列表info('Reading...')dat = s.daq_read(0) # read the first channel# 开始画图info('Ploting...')# 先绘制原始波形dat_x = numpy.linspace(0, SAMPLE_TIME, SAMPLE_LENGTH, endpoint=False)p.resetData( 'wave' )p.addData( 'wave', dat, dat_x )# 根据需要,数据加窗if ADD_WINDOW:window = scipy.signal.get_window( WINDOW_TYPE, len(dat) )dat_offset = numpy.mean(dat)dat = (dat-dat_offset) * window + dat_offset# 快速傅立叶变换fft_x = numpy.linspace(0, SAMPLE_RATE, SAMPLE_LENGTH, endpoint=False)fft_raw = numpy.fft.fft(dat)/(SAMPLE_LENGTH/2)fft_abs = abs(fft_raw)fft_abs[0] = 0 # reset the waveform offset#fft_abs_max = max(fft_abs)p.resetData( 'fft' )#p.addData( 'fft', fft_abs, fft_x ) # plot the whole spectrum rangep.addData( 'fft', fft_abs[:SAMPLE_LENGTH/2], fft_x[:SAMPLE_LENGTH/2] )if FFT_PHASE_PLOT:fft_phase = numpy.angle(fft_raw) / math.pi * 180p.resetData( 'fft2' )p.addData( 'fft2', fft_phase[:SAMPLE_LENGTH/2], fft_x[:SAMPLE_LENGTH/2] )# 查找谱图峰值并排序fft_max = max(fft_abs[:SAMPLE_LENGTH/2])peaks, props = scipy.signal.find_peaks(fft_abs[:SAMPLE_LENGTH/2],height=fft_max/5)def peak_sort( fa, fb ):global fft_absreturn 1 if (fft_abs[fa] >= fft_abs[fb]) else -1peaks = list(peaks)peaks.sort( peak_sort, reverse=True )# 添加峰值标注p.resetAnnotation('fft') # dynamic marker needs to be clear for next roundfor idx in peaks[:PEAKS_LIMIT]:# 默认标注幅度,可以改为频率或自定义txt = '%f'%fft_abs[idx] # add amplitude#txt = '%f'%fft_x[idx] # add freq value#txt = peaks.index(idx) + 1 # add sequencep.addAnnotation( 'fft', txt, xy=(fft_x[idx],fft_abs[idx]) )# 复位,准备下一循环 s.daq_reset()
上面代码的理解需要一定的数字信号处理基础,这里就不展开了。
熟悉Python的同学应该已经发现了, Shell Lab测试台软件集成了数值分析库numpy,才能方便地进行矩阵分析,快速傅立叶变换。
没错,除此之外还集成了scipy库进行科学计算,集成了matplotlib库用于绘图。
至此,你可以用二三十元的成本获得一个简易的4通道12位精度的信号测量采集控制系统,你可以改动脚本把数据存下来,还具备数据记录功能。
对比F401CC和F411CE两种板子有什么区别呢?主要是后者SRAM容量大了64K字节,所以单次的测量深度更长。
实测:前者最大12000点,后者最大39000点,推荐后者。
注:Shell Lab测试台软件对个人用户免费,目前处于公测阶段,欢迎试用。