前言
有关ADC的介绍已经很多了,理论部分可以看博客的另一派文章《DSP的成仙之路》本文只针对cubeide如何配置ADC进行解释说明

1. ADC基础配置

  • 先配置RCC-HSE-CRYSTAL

  • 再于SYS中配置serial wire

  • 启用float打印:在cubeIDE菜单栏中,Project Properties -> C/C++ Build -> Settings -> Tool Settings -> MCU Settings,勾选Use float with printf … -nano

  • 打开ADC1:在Pinout&Configuration页面,设置为ADC1_IN2 Differential

  • 配置ADC:在Pinout&Configuration -> Analog -> ADC1 -> Configuration中

    • ADC_Settings -> Continuous Conversion Mode设为Enable,使ADC转换持续进行,不需要每次获取之前手动触发转换
    • ADC_Regular_ConversionMode -> Rank -> Sampling Time设为810.5 Cycles,最长采样时间,可以获得更稳定的转换结果
  • 引用头文件:
    由于需要使用 sprintf 打印输出,以及数学函数,在 main.c 引用头文件:
    #include "stdio.h"#include "string.h"#include "math.h"

  • 启动连续ADC转换
    *// 启动连续ADC转换*HAL_ADC_Start(&hadc1);
    *// 等待ADC稳定*HAL_Delay(500);

  • 打开串口2外设:
    Pinout&Configuration -> Connectivity -> USART2,将Mode选择为Asynchronous

  • 读取ADC结果
    *// 获取ADC值*result = HAL_ADC_GetValue(&hadc1);
    *// 计算电压值:电压 = ADC结果 × 3.3V ÷ 4095*voltage = result * 3.3f / 4095;
    *// 将变量打印为字符串*sprintf(send_buf, "原始值: %d,电压值: %.3f V\r\n", result, voltage);
    *// 通过串口2发送*HAL_UART_Transmit(&huart2, (uint8_t*) send_buf, strlen(send_buf), 20);

  • h7及以上配置代码时可能出现warning: Main Config: These peripherals still have some not configured or wrong parameter values:[Clock]不影响必要功能

2.timer计时器测频率

通常,你应该先启动定时器的计数(HAL_TIM_Base_Start),然后才能启动输入捕获中断(HAL_TIM_IC_Start_IT)。这是因为输入捕获依赖于定时器的计数器在运行。

如果目的是配置输入捕获中断,并且希望在外部信号触发时响应中断,那么在启动输入捕获中断之后不要立即停止它。如果你不希望响应中断,那么只需要调用HAL_TIM_Base_Start来启动定时器的计数。

假设你希望定时器开始计数,并且当外部信号触发时响应中断:

1
2
3
4
__HAL_TIM_SET_COUNTER(&htim3, 0);// 设置定时器计数值为0
HAL_TIM_Base_Start(&htim3);// 启动定时器计数
__HAL_TIM_SET_CAPTUREPOLARITY(&htim3, TIM_CHANNEL_3, TIM_INPUTCHANNELPOLARITY_RISING);// 设置捕获极性为上升沿
HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_3);// 启动输入捕获中断

如果你不希望使用中断,只是想让定时器开始计数,那么应该省略中断相关的函数调用:

1
2
__HAL_TIM_SET_COUNTER(&htim3, 0);// 设置定时器计数值为0
HAL_TIM_Base_Start(&htim3);// 启动定时器计数

请根据你的具体需求调整代码。如果你确实需要使用中断,请确保你的中断服务例程(ISR)已经正确配置,并且能够处理输入捕获事件。

main()中的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
__HAL_TIM_SET_COUNTER(&htim1, 0);

// 开始捕获

__HAL_TIM_SET_CAPTUREPOLARITY(&htim1, TIM_CHANNEL_3, TIM_INPUTCHANNELPOLARITY_RISING);

HAL_TIM_IC_Start_IT(&htim1, TIM_CHANNEL_3);

// 等待一段时间以捕获足够数量的周期

HAL_Delay(1000);

// 停止输入捕获中断

HAL_TIM_IC_Stop_IT(&htim1, TIM_CHANNEL_3);

// 输出测量到的频率

if (period_count > 1) {

char buf[64];

sprintf(buf, "Frequency: %lu Hz\r\n", frequency);

HAL_UART_Transmit(&huart2, (uint8_t *)buf, **strlen**(buf), 1000);

} else {

char buf[64];

sprintf(buf, "Not enough periods captured to calculate frequency.\r\n");

HAL_UART_Transmit(&huart2, (uint8_t *)buf, **strlen**(buf), 1000);

}

中断回调函数中的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) {

if (htim->Instance == TIM1) {

if (HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_3) < capture_value1) {

// 捕获到下降沿,记录周期结束

period_count++;

// 更新周期时间

frequency = (uint32_t)((SystemCoreClock / 1000000) / period_count); // 计算频率(单位:Hz)

capture_value2 = capture_value1; // 上一个捕获值用于下一次上升沿的比较

} else {

// 捕获到上升沿,记录当前捕获值

capture_value1 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_3);

}
}
}

3.多个ADC的同步采样

我们已经有一个可控采样率的ADC,可用于很多单一信号的低速数据采集分析应用。但往往我们需要采集的信号不止一个,例如通信系统接收机的基带ADC需要采集I、Q两路信号,并且要求两路ADC执行严格 同步采样。同步采样意即2个或多个ADC的动作严格同步,就像复制粘贴出来的一样。

配置好32的基本项,然后打开ADC1的CH1,再打开ADC2的CH2,回到ADC1的配置页面中,ADC工作模式栏发生了变化——原先只有Independent Mode Only,现在多出了2个ADC配合工作的一些模式。在这里我们选择双ADC同步采样模式,使能DMA access,并且在这里可以调整2个ADC同步采样的延迟周期,这里我们尽量希望它们没有延迟,选择最短的1 cycles,此处的cycle对应ADC的主时钟周期

此时ADC1作为主ADC,ADC2作为从ADC,构成了主-从协同同步采样ADC,主从采样的信号间存在1个cycle的延迟。接下来的配置都在主ADC(ADC1)的配置选项卡内完成,随便选个定时器作为触发源,并且触发源DMA只添加ADC1的就可以了。

在mx中进行基础配置后参考代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/* USER CODE BEGIN PV */
#include
// Sample Length
#define ADC_SAMPLE_LEN 256
// Sample Buffer
// adc_sample for DMA access, n = SAMPLE_LEN
// data storage: [adc1_s0, adc2_s0, adc1_s1, adc2_s1, ... , adc1_sn, adc2_sn]
// so adc_sample's size is ADC_SAMPLE_LEN * 2
uint16_t adc_sample[ADC_SAMPLE_LEN*2];
// adc1/2_buff for data splitting
uint16_t adc1_buff[ADC_SAMPLE_LEN];
uint16_t adc2_buff[ADC_SAMPLE_LEN];
// buffer for sum
uint16_t sum_result[ADC_SAMPLE_LEN];
// Convert Complete flag
volatile uint8_t adc_conv_cplt_flag = 0;
/* USER CODE END PV */

/* USER CODE BEGIN 2 */

// Calibration Start & Initialize ADC
HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED);
HAL_ADCEx_Calibration_Start(&hadc2, ADC_SINGLE_ENDED);
HAL_Delay(100);

// ADCEx function, ADC MultiMode Start
HAL_ADCEx_MultiModeStart_DMA(&hadc1, adc_sample, ADC_SAMPLE_LEN*2);
// Start TIM8 (Sampling Clock Source)
HAL_TIM_Base_Start(&htim8);

/* USER CODE END 2 */

/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
if (adc_conv_cplt_flag == 1)
{
adc_conv_cplt_flag = 0;

for (uint16_t i = 0; i < ADC_SAMPLE_LEN; i++)
{
// Split ADC Raw Data into 2 Arrays
adc1_buff[i] = adc_sample[i*2];
adc2_buff[i] = adc_sample[i*2+1];
// Make a sum, printf them
sum_result[i] = adc1_buff[i] + adc2_buff[i];
printf("a=%d,b=%d,c=%d\r\n",
adc1_buff[i], adc2_buff[i], sum_result[i]);
}
adc_conv_cplt_flag = 0;
// Restart next sample cycle
HAL_ADCEx_MultiModeStart_DMA(&hadc1, adc_sample, ADC_SAMPLE_LEN*2);

HAL_TIM_Base_Start(&htim8);
}
HAL_Delay(500);
/* USER CODE END WHILE */

/* USER CODE BEGIN 3 */
}

// Conversion Complete Callback
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
// ADC MultiMode Stop
HAL_ADCEx_MultiModeStop_DMA(&hadc1);
// Stop TIM8(Sampling Clock)
HAL_TIM_Base_Stop(&htim8);
// Set conversion complete flag
adc_conv_cplt_flag = 1;
}

4.添加DSP库对ADC采样值进行FFT处理

https://blog.csdn.net/qq_34022877/article/details/123190943

一般把dsp库配置好,再增加个ARM_MATH_CM3或ARM_MATH_CM4定义不报错就行了

注意:有的方案只能适用于M3和M4的核,也就是我们常见的F1,F4等。对于H7,F7这样采用M7的核就不适用了。

5.ADC+TIM+DMA采集交流信号

https://blog.csdn.net/qq_34022877/article/details/121941236

6.调用自己编写的FFT库

以下仓库给出几种案例
https://github.com/xiaodengwang01/dian-sai/tree/master/Lib.FFT
(期待小星星ing.jpg)