一、作用
打电赛用于FFT解析信号频率分量,定时器触发可以最大限度避免频谱泄露,用dma可以提高效率;H750adc16bit精度很高;
二、CubeMX配置
1.基础配置(略讲)
时钟(24M,分频后48M)
2.定时器配置(TIM4)
内部时钟,通道1选择输出比较但不输出
PSC = 1-1,ARR = 235-1—— (48M/204800=234.375);
自动重装载打开;
Trigger Event Selection TRGO 选择UPdate Event;
定时器不需要中断;
其余默认即可
3. adc配置(adc1——PA6)
通道3 单端输入
dma点add加adc1,,mode选择Circular,数据长度选择WORD(32bit)!!!!敲黑板
按照图片配置(大部分默认即可),mode选择DMA Circular Mode ;External Trigger Conversion Source选择TIM4 Trigger Out event;
打开中断
4.DSP库添加
勾选DSP库
5. 其余配置跟一般应用一样即可
三、keil配置
1.魔术棒配置!!!!!!!!敲黑板
IRAM配置及IROM打勾,H7总线设置问题,默认的IRAM1无法使用dma——adc;(具体原因网上资料很多)
C/C++这里加句话:USE_HAL_DRIVER,STM32H750xx,ARM_MATH_CM4,ARM_MATH_MATRIX_CHECK,ARM_MATH_ROUNDING (间隔的符号是逗号不是点)
keil内添加DSP库,打勾即可
四、代码
1、头文件添加
#include "arm_math.h"
#include "math.h"
2、代码
///定义变量
#define FFT_LEN 1024 //fft点数1024
arm_cfft_radix4_instance_f32 scfft;
float FFT_INPUT [FFT_LEN *2]; //fft输入数组 ,大小是二倍的点数
float FFT_OUTPUT[FFT_LEN]; ///fft输出数组
float adc_value[FFT_LEN]; /// adc读取数组
float fs; /// 采样频率 204800(cubemx配置)
float fre; ///测得的频率
float value_max; ///fft幅值的最大值
uint8_t dma_flag;///进adc中断的 计数器
uint16_t count_max;////定位最大幅值的位置(0-1024)
/////////////////////adc中断 ,进中断后关闭dma,数据处理完后再打开
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
if(hadc->Instance == ADC1 )
{
dma_flag++;
HAL_TIM_Base_Stop (&htim4);
HAL_ADC_Stop_DMA (&hadc1);
}
}
////////////////////////////main函数
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MPU Configuration--------------------------------------------------------*/
MPU_Config();
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_USART2_UART_Init();
MX_TIM4_Init();
MX_ADC1_Init();
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start(&htim4); ////打开定时器4
HAL_ADC_Start_DMA( &hadc1 ,(uint32_t*)adc_value,FFT_LEN); //打开adc采集
arm_cfft_radix4_init_f32(&scfft, FFT_LEN, 0, 1);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
//HMI_send_floatnum("x0.val",adc_get(&hadc1));
HAL_Delay(1000);
fs = 204800; ////////////////采样频率
/////////////////数据处理,16bit转为0-3.3V(减0.05因为我的单片机基准电压问题
/////////////没问题的可以不减)
//////// ((uint32_t*) adc_value)[i] —dma采集出来是32bit的整数,但adc_value是float类型
///////
for(int i =0;i<FFT_LEN;i++)
{
adc_value [i]=((uint32_t*) adc_value)[i]*3.3/65536 - 0.05;
}
//////////////////实部虚部分开,虚部赋0即可
for (int i = 0; i < FFT_LEN; i++)
{
FFT_INPUT[2 * i] = adc_value[i]; // shi bu
FFT_INPUT[2 * i + 1] = 0; // xu bu
}
//////////////////FFT计算
arm_cfft_radix4_f32(&scfft, FFT_INPUT );
arm_cmplx_mag_f32(FFT_INPUT , FFT_OUTPUT , FFT_LEN);
value_max = 0;
//FFT后的第一个赋值很大,最好跳过,
for(int j=5 ;j<FFT_LEN;j++)
{
if(value_max<FFT_OUTPUT[j])
{
count_max = j;
value_max = FFT_OUTPUT[j];
}
}
value_max = 0;
//////////fre 计算公式 理解即可
fre = fs*(count_max*1.0)/(FFT_LEN*1.0);
////////////串口屏幕的显示代码(忽略即可)
HMI_send_num("n0.val",dma_flag);
HMI_send_floatnum("x1.val",adc_value[10]);
HMI_send_floatnum("x0.val",fre);
///一次处理完后再次打开
HAL_TIM_Base_Start(&htim4);
HAL_ADC_Start_DMA( &hadc1 , (uint32_t*)adc_value , FFT_LEN);
}/////while(1)
/* USER CODE END 3 */
}
五、效果展示
信号发生器(66K)
有误差可以接受