学习笔记(一)stm32 ADC+DMA+FFT 标准库+hal库

        stm32提供了许多种的ADC模式,在电赛中做信号处理使用到的大多是定时器驱动的ADC采样,这样会有更加准确的采样率,便于做fft时进行分析,规避频谱泄露等问题。现对f103(标准库)和f407(hal库)的相关配置和代码做介绍和比较。

f103

 f103的ADCCLK最大为14Mhz,挂载在APB2总线上。实际在标准库中一般采用6分频,就是72M/6=12M。

这里设置了ADC的采样周期是55.5cycle,再加上12.5周期的ADC转换时间,总共是68个周期。一共是(1/12M)*68=5.7 μs → 最大触发频率约 175 kHz。也就是说,启用外部时钟触发的时候时钟的频率不能超过175khz.否则ADC还没有转换完下一个时钟上升沿就又来了。

DMA能够加速数据的传输效率,从外设到内存。缓存区域的大小与希望缓存的ADC采样个数有关,这里nn是1024。

然后进行ADC的电压校准。

void fre_calcu(void)
{
	signed short lX,lY;//存放变换后信号的实部和虚部
    float X,Y;//存放变换为浮点型数据的信号的实部和虚部
	float Mag;//存放信号的模值
    unsigned short i;
	
	fus=72000000/(arr1*psc1); //9 71 //采样频率计算公式:fus=ck_psc/(psc+1)/(arr+1),ck_psc=72Mhz

	for(i=0;i<nn;i++)
	{
		fftin[i] = 0;
		fftin[i] =((u32)(AD_Value[i] ))<< 16;//将AD_Value[i]左移16位存入fftin数组中,实部为AD_Value[i],虚部为0
		//为什么是左移16位?因为fftin数组是一个32位的数组,左移16位后,低16位为0,高16位为AD_Value[i],符合FFT变换的输入格式。
		fftbuffer[i] = 0; //清空fftbuffer数组

	}
  cr4_fft_1024_stm32(fftout ,fftin ,nn);//调用FFT变换函数,进行FFT变换,输出的内容是频域
  for(i=0; i<nn/2; i++)
  {
      lX  = (fftout [i] << 16) >> 16;//取变换后数据的虚部
      lY  = (fftout [i] >> 16);			 //取变换后数据的实部
    /*将信号变换为浮点型数据*/  
      X = nn * ((float)lX) / 32768; //定点数还原为浮点数 Q15格式 一位是符号位
      Y = nn * ((float)lY) / 32768;
		
      Mag = sqrt(X * X + Y * Y) / nn;
      fftbuffer[i] = (unsigned long)(Mag * 32768);//将模值变回长整型存入数组中,存入的是频谱幅度信息
  }
  Mag=fm =0;
  for(i=1;i<nn/2;i++)//找到模值最大的点
  //为什么从1开始?因为第0个点是直流分量,频率为0Hz,不需要计算
  //为什么是nn/2?因为FFT变换后,频率范围是0~fs/2,fs为采样频率,nn为采样点数,所以只需要计算前一半的点
  {
    if(Mag<fftbuffer [i])
		{
			Mag=fftbuffer [i];
			fm =i;
		}
  }
	F=(u32)(fm*fus/nn);//计算频率,公式为F=fm*fs/nn,其中fs为采样频率,fm为模值最大的点的索引,nn为采样点数
	//找到能量最大的(幅值最大的)频率
}

首先要对ADC收集到的数据进行处理。f103是12位逐次逼近形ADC,采集到的数据为16位(对应DMA中的halfword。void cr4_fft_1024_stm32(void *pssOUT, void *pssIN, uint16_t Nbin);应该要填入32位参数,左移16位满足数据要求。执行fft之后的fftout[k] = Real + jImag。也就是说还是输入进去的数据格式,高16位是实数,低16位是虚数。

(例如:1010 1010 1010 1010 1111 1111 1111 1111 左移16位后,变成 1111 1111 1111 1111 0000 0000 0000 0000 再右移16位,变成 1111 1111 1111 1111,即提取了虚部。实部直接右移16位即可)。

由于采用的是Q15的定点数格式(避免使用浮点数),要将它还原。nn是缩放因子。

Q15 格式特点:

  • 使用 16 位有符号整数(int16_t) 表示一个小数。

  • 数值范围是:

    接下来就是找到幅值最大的频谱。

f407

对hal库的参数配置做如下解释:Mode 只开启一个ADC就是独立模式  ADCCLK 最大值是36Mhz,把时钟倍频到最大值168Mhz后,APB2上的外设时钟84Mhz,四分频后21Mhz。这里先计算一下。采样时间3cycle+12cycle(区别于f103的12.5cycle),一共是15个周期。(1/21M)*15=0.714微秒,大约允许最大值1.4M的采样频率。

数据对齐方式为右对齐。

扫描模式只有多个adc通道才开启。

持续转换模式不用开启,这里使用的是定时器外部触发,即硬件触发。

不连续转换模式不用开启。有特定的触发事件才会开启。

DMA连续请求不用开启。开启的意思是:软件或者硬件触发一次,ADC连续采集,DMA也连续传输数据。而这里是每个时钟的上升沿触发。触发一次,DMA转运一次。

选择ADC的规则通道,外部时钟触发。

开启DMA,模式选择为循环。地址自增不要选外设。因为ADC的地址没有变化。半字对应ADC的16位位宽.


  /* USER CODE BEGIN 2 */
  HAL_TIM_Base_Start(&htim3);
  HAL_ADC_Start_DMA(&hadc1, (uint32_t *)adc_buffer, sample_num);
  while(!adc_flag){}
  adc_flag = 0; // Reset the flag for next ADC conversion
  /* USER CODE END 2 */




/* USER CODE BEGIN 4 */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc){
  if (hadc == &hadc1){
    adc_flag = 1; // Set the flag to indicate ADC conversion is complete

  }
}
/* USER CODE END 4 */

这里加了ADC转换完成后的标志位,实际影响不大。因为采样时填满缓冲区即可。

hal库中的函数都封装好了,也无需去自己寻找最大幅值。逻辑和标准库是一样的。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值