系列文章目录
提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 例如:第一章 Python 机器学习入门之pandas的使用
提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
在之前的文章中有提到使用STM32H750VB进行ADC+FFT的事,但是由于给出的采样点数过高,直接导致上电进入hardfault,放弃了一段时间想使用FPGA+SPI的方式进行数据交换然后进行FFT,之前用Verilog尝试写了一下SPI的时序,感觉问题比较多,上周考完业余无线电A证,现在又对H750做DSP念念不忘,于是乎采用较少点数进行了尝试,本文就介绍了基于ARM的DSP的基础内容。
提示:以下是本篇文章正文内容,下面案例可供参考
一、STM32H750 ADC
使用STM32CUBEMX配置工程,ADC并没有采用DMA的方式,只是想简单地得到数据然后进行信号处理。 主频配置为480MHz,直接拉满,ADC时钟为150MHz,采样深度16bit,采样时间1.5个时钟周期。
static void MX_ADC3_Init(void)
{
ADC_ChannelConfTypeDef sConfig = {0};
hadc3.Instance = ADC3;
hadc3.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
hadc3.Init.Resolution = ADC_RESOLUTION_16B;
hadc3.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc3.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc3.Init.LowPowerAutoWait = DISABLE;
hadc3.Init.ContinuousConvMode = DISABLE;
hadc3.Init.NbrOfConversion = 1;
hadc3.Init.DiscontinuousConvMode = DISABLE;
hadc3.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc3.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc3.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DR;
hadc3.Init.Overrun = ADC_OVR_DATA_PRESERVED;
hadc3.Init.LeftBitShift = ADC_LEFTBITSHIFT_NONE;
hadc3.Init.OversamplingMode = DISABLE;
if (HAL_ADC_Init(&hadc3) != HAL_OK)
{
Error_Handler();
}
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
sConfig.SingleDiff = ADC_SINGLE_ENDED;
sConfig.OffsetNumber = ADC_OFFSET_NONE;
sConfig.Offset = 0;
sConfig.OffsetSignedSaturation = DISABLE;
if (HAL_ADC_ConfigChannel(&hadc3, &sConfig) != HAL_OK)
{
Error_Handler();
}
}
在主函数中采集128次,并将数据存在数组里
for(j=0;j<128;j++)
{
HAL_ADC_Start(&hadc3);
HAL_ADC_PollForConversion(&hadc3,10);
u1=HAL_ADC_GetValue(&hadc3);
b[j]=(float32_t)u1*/65536*3.3;
}
随后进行FFT处理。
二、FFT相关
1. fft
与fft相关的函数可以在keil的文档中找到,本文中使用到的为
void arm_cfft_f32 ( const arm_cfft_instance_f32 * S,
float32_t * p1,
uint8_t ifftFlag,
uint8_t bitReverseFlag
)
解释如下: Parameters [in] S points to an instance of the floating-point CFFT structure [in,out] p1 points to the complex data buffer of size 2*fftLen. Processing occurs in-place [in] ifftFlag flag that selects transform direction value = 0: forward transform value = 1: inverse transform [in] bitReverseFlag flag that enables / disables bit reversal of output value = 0: disables bit reversal of output value = 1: enables bit reversal of output Returns none
具体用法如下,只摘部分关键代码,代码来自于官方example
arm_status status;
float32_t maxValue;
status = ARM_MATH_SUCCESS;
status=arm_cfft_init_f32(&varInstCfftF32,fftSize);
arm_cfft_f32(&varInstCfftF32, testInput_f32_10khz, ifftFlag, doBitReverse);
延续了arm的传统,首先还是先初始化,在初始化中根据fft的长度初始化一个复浮点fft结构varInstCfftF32,然后 arm_cfft_f32进行fft变换。在文档中没有办法对所有的函数进行溯源,只能进行推测。 设想: 由于是fft,初始化应该是生成蝶形网络,方便进行快速运算。
2. 数据处理
由于在stm32上数据可视化不太行,所以想使用keil导出数据再进行分析。在板子上将ADC通道连接到3.3V电源,采集到的数据乘1000,得到下图所示的数据。 在keil的command窗口输入,就能将数组b中的数据以txt的形式进行导出。
save E:\v.txt &b[0],&b[128]
保存的文件打开如下图所示,从keil中导出数据save中对数据的格式进行了分析,而且本例中所有数据都一样,所以相对较容易分析,可以看到从第二行开始,掐去开头几位和最后两位,中间的数据多次重复,每16个字符为一组,而这16个字符正好构成32bit的uint32_t的数据长度,可以看出数据为reverse排列。例如0x454e3f32,0x45被放到了最后,0x32在最前。
3. python处理导出数据
二进制与十进制整数,浮点数相互转换这篇文章讲了如何将二进制转换为浮点数,下面的代码实现了32bit二进制数转单精度浮点数
def str2int(a):
if((a<='9')and(a>='0')):
return int(a);
else:
if((a >= 'a' ) and ( a <= 'f' )) :
return 10+ord(a)-ord('a');
else:
if ((a >= 'A') and (a <= 'F')):
return 10 + ord(a) - ord('A');
def str2float(a):
changdu=len(a)
c=str2int(a[1])
if(str2int( a[0] )>= 8):
flag=-1;
b=str2int(a[0])-8;
else:
flag=1;
b=str2int(a[0]);
d=(str2int(a[2])&0x8)>>3;
exponent=(b<<5)+(c<<1)+d-127;
weishu=0
for i in range(3,changdu-1):
exp=((str2int(a[i-1])&0x7)<<1)+(str2int(a[i])>>3)
weishu=exp*pow(16,-(i-2))+weishu;
exp=(str2int(a[changdu-2])&0x7)<<1
weishu = exp*pow(16, -6) + weishu;
result=pow(2,exponent)*(1+weishu)*flag;
return result;
def print_hi(name):
word_size=4;
print(f'Hi, {name}')
de_f=open("de_f.txt","w")
with open("v.txt","r") as f:
data=f.readlines()
row=len(data)
for i in range(1,row-2):
length=len(data[i])
num=int((length-12)/8);
for j in range(0,num-1):
temp=data[i][15+j*8]+data[i][16+j*8]+data[i][13+j*8]+data[i][14+j*8]+data[i][11+j*8]+data[i][12+j*8]+data[i][9+j*8]+data[i][10+j*8]+'\n'
temp=str2float(temp)
print(temp)
if __name__ == '__main__':
print_hi('PyCharm');
3. 数据处理
在keil中继续运行,得到fft变换之后数据的模,可以发现,仅在数组的前两项中有较大值存在,而其他相都为0,说明信号基本为直流信号。 将ADC采集端口连接到GND进行采集,使用上述方法将信号进行导出,并使用matlab求得频谱,得到如下图所示结果 H750求得的频谱为下图,并没有对横坐标进行处理,但是基本与使用matlab的结果一致。 附matlab程序
clear all;
data=textread('de_f.txt');
Sample_clock=150e6/1.5;
sample_rate=1/Sample_clock;
a=size(data);
D=2*pi/(a(1)*sample_rate);
k=floor((-(a(1)-1)/2):(a(1)-1)/2);
size=128;
a=fftshift(fft(data,size));
figure(1)
subplot(2,1,1)
plot(data)
subplot(2,1,2)
axis([min(k*D),max(k*D),0,inf]);
plot(k*D,abs(a));
总结
本文使用STM32H750进行了ADC数据采集以及快速傅里叶变换,中间涉及到了keil数据导出、python二进制转单精度浮点、matlab求fft等多方面内容,下一步考虑使用FPGA+ARM实现同样功能。
|