《STM32H7S78-DK 开发套件二周目评测:简单声音采集之频率检测与显示》
[复制链接]
在实现adc基础上,实现对音频信号频率的检测。读取模拟信号并通过 FFT(快速傅里叶变换)分析频率成分。同时,在串口输出检测到的频率,并通过 LED 显示结果。
一、硬件连接
- 引脚连接:
- 将 MAX4466 传感器的输出引脚连接到 STM32 的 ADC 输入引脚(PC0,即 ADC1_IN10)。
- LED选取PO5。在高电平时候点亮。
二、使用 CubeMX 进行配置
在 CubeMX 中进行如下设置:
2.1 ADC 模块配置
- 时钟设置:配置 ADC 时钟为 84MHz,选择 ADC 的时钟分频器为 1,以达到较高的采样精度。
- 采样时间:设置 ADC 的采样时间为 15.5 个 ADC 时钟周期,以提高采样精度。
- 通道设置:将 ADC 通道设置为 ADC1_IN10(PC0),确保能够读取传感器输出。
2.2 TIM 模块配置
- 定时器配置:配置 TIM2 为定时器,用于生成定时中断。
- 设置预分频器为 7999,自动重装载值为 999,以设定频率采样率(例如 1kHz)。
2.3 GPIO 设置
将 PO5 配置为推挽输出模式,用于控制 LED。
生成代码并导入 Keil,在 CubeMX 中生成代码,并导入 Keil 进行后续的代码开发。
四、代码实现
4.1 初始化模块
在 Keil 中,初始化 ADC、定时器和 LED 控制的代码如下:
#include "arm_math.h"
#include "stm32h7xx_hal.h"
#define FFT_SIZE 1024 // FFT 输入数组的大小
float32_t input[FFT_SIZE];
float32_t output[FFT_SIZE];
arm_cfft_instance_f32 fft_instance;
TIM_HandleTypeDef htim2;
ADC_HandleTypeDef hadc1;
void ADC_Init(void) {
ADC_ChannelConfTypeDef sConfig = {0};
__HAL_RCC_ADC12_CLK_ENABLE(); // 使能 ADC 时钟
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.ScanConvMode = DISABLE;
hadc1.Init.ContinuousConvMode = ENABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
HAL_ADC_Init(&hadc1);
sConfig.Channel = ADC_CHANNEL_10; // 选择通道 10
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_15CYCLES; // 设置采样时间
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
}
uint32_t Read_ADC_Value(void) {
uint32_t adc_value = 0;
HAL_ADC_Start(&hadc1); // 启动 ADC
HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY); // 等待转换完成
adc_value = HAL_ADC_GetValue(&hadc1); // 获取 ADC 值
HAL_ADC_Stop(&hadc1); // 停止 ADC
return adc_value;
}
void TIM_Init(void) {
__HAL_RCC_TIM2_CLK_ENABLE(); // 使能 TIM2 时钟
htim2.Instance = TIM2;
htim2.Init.Prescaler = 7999; // 对应 1kHz
htim2.Init.Period = 999; // 对应 1ms
htim2.Init.CounterMode = TIM_COUNTERMODE_UP; // 向上计数
HAL_TIM_Base_Init(&htim2); // 初始化定时器
HAL_TIM_Base_Start_IT(&htim2); // 启动定时器中断
}
void FFT_Init(void) {
arm_cfft_init_f32(&fft_instance, FFT_SIZE); // 初始化 FFT 实例
}
4.2 FFT 处理
使用 CMSIS DSP 库实现 FFT 分析信号频率:
void Perform_FFT(void) {
arm_cfft_f32(&fft_instance, input, 0, 1); // 执行 FFT
arm_cmplx_mag_f32(input, output, FFT_SIZE); // 计算幅度
}
4.3 频率检测与 LED 显示
计算频率并控制 LED 的开关状态的代码如下:
void Display_Frequency(void) {
float max_value = output[0];
uint32_t max_index = 0;
for (uint32_t i = 1; i < FFT_SIZE / 2; i++) {
if (output[i] > max_value) {
max_value = output[i];
max_index = i;
}
}
// 控制 LED 的开关状态
if (max_index > 10) {
HAL_GPIO_WritePin(GPIOO, GPIO_PIN_5, GPIO_PIN_SET); // 点亮 LED
} else {
HAL_GPIO_WritePin(GPIOO, GPIO_PIN_5, GPIO_PIN_RESET); // 熄灭 LED
}
}
4.4 主程序
在主循环中进行 ADC 采集、FFT 处理和 LED 控制的代码如下:
int main(void) {
HAL_Init();
SystemClock_Config();
ADC_Init();
TIM_Init();
FFT_Init();
while (1) {
UART_Send_Data(corrected_value);
}
}
// 定时器溢出回调
void HAL_TIM_PERIOD_ELAPSED_CALLBACK(TIM_HandleTypeDef *htim) {
if (htim->Instance == TIM2) {
// 每次定时器溢出时读取 ADC 值
uint32_t adc_value = Read_ADC_Value(); // 获取 ADC 值
static uint32_t sample_index = 0;
input[sample_index] = (float32_t)adc_value; // 存储到 FFT 输入数组
sample_index++;
// 如果达到 FFT_SIZE 大小,则进行 FFT 计算
if (sample_index >= FFT_SIZE) {
sample_index = 0; // 重置索引
Perform_FFT();
Display_Frequency();
}
}
}
|