STM32F4 HAL库使用DMA进行ADC采样实时发送波形到串口显示(包含傅里叶变换)

1.总体逻辑

按下 STM32F4 的 KEY0 按键,通过外部中断的方式对按键进行检测,然后进行一次固定点数的 DMA ADC 采集,采集完成后在 DMA 的中断发送采集到的数据,然后清空数据区准备下一次的按键中断。电脑接受到串口数据后对数据进行简单处理和傅里叶变化,然后实时显示在电脑上。
开发板:正点原子探索者STM32F407ZG

2. STM32

源工程文件
可以拿着正点原子的官方例程的单通道ADC采集(DMA读取)实验进行修改
这里只展示
部分重要代码

2.1 外部中断处理函数

打开 exti.c 文件,修改为以下的代码。删掉了冗余的代码,在 KEY0 按下后的逻辑中加入了 adc_dma_enable(ADC_DMA_BUF_SIZE) 来启动一次ADC采集

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/KEY/key.h"
#include "./BSP/EXTI/exti.h"
#include "./BSP/ADC/adc.h"

/**
 * @brief       KEY0 外部中断服务程序
 * @param       无
 * @retval      无
 */
void KEY0_INT_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(KEY0_INT_GPIO_PIN);         /* 调用中断处理公用函数 清除KEY0所在中断线 的中断标志位 */
    __HAL_GPIO_EXTI_CLEAR_IT(KEY0_INT_GPIO_PIN);         /* HAL库默认先清中断再处理回调,退出时再清一次中断,避免按键抖动误触发 */
}

/**
 * @brief       中断服务程序中需要做的事情
 *              在HAL库中所有的外部中断服务函数都会调用此函数
 * @param       GPIO_Pin:中断引脚号
 * @retval      无
 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    delay_ms(20);      /* 消抖 */
    switch(GPIO_Pin)
    {
        case KEY0_INT_GPIO_PIN:
            if (KEY0 == 0)
            {
                LED0_TOGGLE();
                adc_dma_enable(ADC_DMA_BUF_SIZE);                   /* 启动一次ADC DMA采集 */
            }
            break;
        default : break;
    }
}

/**
 * @brief       外部中断初始化程序
 * @param       无
 * @retval      无
 */
void extix_init(void)
{
    GPIO_InitTypeDef gpio_init_struct;
    
    key_init();
    gpio_init_struct.Pin = KEY0_INT_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_IT_FALLING;            /* 下降沿触发 */
    gpio_init_struct.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(KEY0_INT_GPIO_PORT, &gpio_init_struct);    /* KEY0配置为下降沿触发中断 */

    HAL_NVIC_SetPriority(KEY0_INT_IRQn, 0, 2);               /* 抢占0,子优先级2 */
    HAL_NVIC_EnableIRQ(KEY0_INT_IRQn);                       /* 使能中断线4 */

}

2.2 DMA传输完成中断函数

adc.c 中,找到 ADC DMA 采集中断服务函数,也就是 ADC_ADCX_DMASx_IRQHandler ,然后修改为如下的代码。在 DMA 采集完成后触发的中断中,通过串口将采集到的数据发送出来

/**
 * @brief       ADC DMA采集中断服务函数
 * @param       无
 * @retval      无
 */
void ADC_ADCX_DMASx_IRQHandler(void)
{
    if (ADC_ADCX_DMASx_IS_TC())     /* 判断DMA数据传输完成 */
    {
        g_adc_dma_sta = 1;          /* 标记DMA传输完成 */
        for (int i = 0; i < ADC_DMA_BUF_SIZE; i++)  /* 累加 */
        {
            printf("%d\r\n",g_adc_dma_buf[i]);
        }
        ADC_ADCX_DMASx_CLR_TC();    /* 清除DMA2 数据流4 传输完成中断 */
    }
}

2.3 DMA缓存区大小设置

adc.h 中,修改 ADC_DMA_BUF_SIZE 为自己想要的大小,如下图所示
在这里插入图片描述

2.4主函数

main.c 修改为如下代码

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/ADC/adc.h"
#include "./BSP/EXTI/exti.h"
#include "string.h"

uint16_t g_adc_dma_buf[ADC_DMA_BUF_SIZE];   /* ADC DMA BUF */
extern uint8_t g_adc_dma_sta;               /* DMA传输状态标志, 0,未完成; 1, 已完成 */

int main(void)
{
    HAL_Init();                             /* 初始化HAL库 */
    sys_stm32_clock_init(336, 8, 2, 7);     /* 设置时钟,168Mhz */
    delay_init(168);                        /* 延时初始化 */
    usart_init(115200);                     /* 串口初始化为115200 */
    led_init();                             /* 初始化LED */
    lcd_init();                             /* 初始化LCD */
    extix_init();                           /* 初始化外部中断输入 */
    adc_dma_init((uint32_t)&g_adc_dma_buf); /* 初始化ADC DMA采集 */
    
    lcd_show_string(30,  50, 200, 16, 16, "STM32", RED);
    lcd_show_string(30,  70, 200, 16, 16, "ADC DMA TEST", RED);
    lcd_show_string(30,  90, 200, 16, 16, "ATOM@ALIENTEK", RED);
    lcd_show_string(30, 110, 200, 16, 16, "ADC1_CH5_VAL:", BLUE);
    lcd_show_string(30, 130, 200, 16, 16, "ADC1_CH5_VOL:0.000V", BLUE); /* 先在固定位置显示小数点 */
    
    while (1)
    {

    }
}

2.5采样率的计算

目前采用的是 21M 时钟,一个时钟周期是 0.047us,采集 3 个周期,转化要 12 个周期(STM32F4 参考手册有写),采样一次就是 15 个周期
采样时间:0.047x15=0.705us
采样率为:1/0.705=1.418M(这里由于计算的时候采用了近似值,若不使用近似值计算下来就是1.4M)
理论最高采集 0.700M 的信号,即 700K 的信号

3. Python实时显示

这里的傅里叶变化只会显示最后的 POINT 个点的傅里叶变化情况

#%%
import serial
import matplotlib.pyplot as plt
import numpy as np
import time
#%%
LINE = 1 # 是否用线的方式连接
OFFSET = 0 # 是否减去偏置值
POINT = 140 # 这里设置的大小和STM32中DMA缓存区的大小要一致
SAMPLE = 1.418e6
count = 0
# 设置画布大小
fig, (ax1, ax2) = plt.subplots(1, 2)
line1, = ax1.plot([], [])
line2, = ax2.plot([], [])
ax1.set_xlim(0, 100)
ax2.set_xlim(0,SAMPLE)
ax1.set_ylim(0, 5)
ax2.set_ylim(0, 100)
ax1.set_title('Time Domain')
ax2.set_title('Frequency Domain')

# 初始化数据
x = []
y = []
yfft = []
xfft = np.linspace(0,POINT-1,POINT)
xfft = xfft*(SAMPLE/POINT)

# 创建曲线对象
if LINE:
    line1, = ax1.plot([], [])
    line2, = ax2.plot([], [])
else:
    line1, = ax1.plot([], [],'.')
    line2, = ax2.plot([], [],'.')   

# 开始绘图
start_time = time.time()
ser = serial.Serial(port ='COM3', baudrate =115200,bytesize = serial.EIGHTBITS,parity = serial.PARITY_NONE,
                    stopbits = serial.STOPBITS_ONE,xonxoff = False,rtscts =False,dsrdtr =False) 
#%%
# 循环读取串口数据并绘图
while True:
    count+=1
    # print(ser.inWaiting())
    # 读取串口数据
    if(ser.inWaiting()):
        line = ser.readline()
        ser.flush()
        # print(line)
        if len(line) : 
            real_vol = int(line) * (3.3 / 4096)
            print(real_vol)
    else:
        real_vol = 0

    # 实时更新x轴
    t = time.time() - start_time

    # 更新数据
    x.append(t)
    y.append(real_vol)
    if count>POINT:
        #FFT
        temp = []
        # xfft = np.linspace(0,POINT,1)
        # xfft = xfft*(SAMPLE/POINT)
        if OFFSET:
            yfft = np.fft.fft(y[-POINT:]-np.mean(y[-POINT:]))
        else:
            yfft = np.fft.fft(y[-POINT:])
        line2.set_data(xfft, abs(yfft))
    
    # 更新曲线数据
    line1.set_data(x, y)
    ax1.set_xlim(max(0, t - 5), t)

    # 重新绘制图形
    fig.canvas.draw()
    fig.canvas.flush_events()
    plt.pause(0.01)  # 控制循环速率 

4. 结果展示

输入信号:700KHz 正弦波,幅度1V,偏置1V
显示的结果如下图所示,观察频域图发现与输入信号的频率一致

在这里插入图片描述文章来源地址https://uudwc.com/A/pJa0w

原文地址:https://blog.csdn.net/FuckerGod/article/details/130400379

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请联系站长进行投诉反馈,一经查实,立即删除!

h
上一篇 2023年08月05日 12:03
华为手机激活不了,提示设备证书损坏。
下一篇 2023年08月05日 12:06