【STM32F407 ADC+DMA采集压力变送器数据(HAL库)】

1. 内容简述

之前项目中需要对麦克传感器的mpm480隔爆压力变送器(4-20ma输出)的数据进行实时采集,使用STM32F407作为控制器,使用信号转换模块将压力变送器4-20ma的输出转换为0-3.3v的信号量,输入到STM32F407板子的ADC1的通道10,并使用DMA2通道0数据流0将采集的多个值从外设直接存入存储器,并对该次采集的值进行排序、去掉过大过小值后取平均值,最后在FreeRTOS实时操作系统的一个任务中计算出测得的气压值。

2. 电路连接

本文采用的是二线制压力变送器,连接如下图。
注意:黄色线(输出电压)连接板子的ADC1的通道10 (PC0)引脚、并需要共地!

二线制压力变送器电路连接图

二线制压力变送器电路连接图

信号转换模块电路连接图

信号转换模块电路连接图

ADC通道图

ADC通道

3. GPIO、ADC、DMA初始化

ADC采集后通过DMA传输到目标内存缓冲区地址,即定义的数组ADC_ConvertedValue[ADC_SAMPLE_PNUM],本文在.h文件中设置ADC_SAMPLE_PNUM的参数为50,即一次性传输50个数据。
注意:
ADC_Handle.Init.Resolution = ADC_RESOLUTION_12B; 代表用12位数值代表采集的值,即最大2的12次方,4096;
ADC_Config.SamplingTime = ADC_SAMPLETIME_56CYCLES; 采样周期选择,采样周期越短,ADC 转换数据输出周期就越短但数据精度也越低,采样周期越长,ADC 转换数据输出周期就越长同时数据精度越高。

bsp_adc.c
#include "./adc/bsp_adc.h"

/*--------定义变量、结构体----------*/
volatile int16_t ADC_ConvertedValue[ADC_SAMPLE_PNUM] = {0};//存储采集的数据
DMA_HandleTypeDef DMA_Init_Handle;
ADC_HandleTypeDef ADC_Handle;//ADC句柄
ADC_ChannelConfTypeDef ADC_Config;


/*--------GPIO初始化----------*/
static void Rheostat_ADC_GPIO_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    // 使能 GPIO 时钟
    RHEOSTAT_ADC_GPIO_CLK_ENABLE();
    // 配置 IO
    GPIO_InitStructure.Pin = RHEOSTAT_ADC_GPIO_PIN;
    GPIO_InitStructure.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStructure.Pull = GPIO_NOPULL ; //不上拉不下拉
    HAL_GPIO_Init(RHEOSTAT_ADC_GPIO_PORT, &GPIO_InitStructure);
}


/*--------DMA、ADC参数初始化----------*/
static void Rheostat_ADC_Mode_Config(void)
{

    /*-----------DMA Init 结构体参数 初始化------------*/
    // ADC1使用DMA2,数据流0,通道0,这个是手册固定死的
    // 开启DMA时钟
    RHEOSTAT_ADC_DMA_CLK_ENABLE();
    // 数据传输通道
    DMA_Init_Handle.Instance = RHEOSTAT_ADC_DMA_STREAM;
    // 数据传输方向为外设到存储器
    DMA_Init_Handle.Init.Direction = DMA_PERIPH_TO_MEMORY;
    // 外设寄存器只有一个,地址不用递增
    DMA_Init_Handle.Init.PeriphInc = DMA_PINC_DISABLE;
    // 存储器地址固定
    DMA_Init_Handle.Init.MemInc = DMA_MINC_ENABLE;
    // // 外设数据大小为半字,即两个字节
    DMA_Init_Handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    //	存储器数据大小也为半字,跟外设数据大小相同
    DMA_Init_Handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
    // 循环传输模式
    DMA_Init_Handle.Init.Mode = DMA_CIRCULAR;
    // DMA 传输通道优先级为高,当使用一个DMA通道时,优先级设置不影响
    DMA_Init_Handle.Init.Priority = DMA_PRIORITY_HIGH;
    // 禁止DMA FIFO	,使用直连模式
    DMA_Init_Handle.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    // FIFO 大小,FIFO模式禁止时,这个不用配置
    DMA_Init_Handle.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_HALFFULL;
    DMA_Init_Handle.Init.MemBurst = DMA_MBURST_SINGLE;
    DMA_Init_Handle.Init.PeriphBurst = DMA_PBURST_SINGLE;
    // 选择 DMA 通道,通道存在于流中
    DMA_Init_Handle.Init.Channel = RHEOSTAT_ADC_DMA_CHANNEL;
    //初始化DMA流,流相当于一个大的管道,管道里面有很多通道
    HAL_DMA_Init(&DMA_Init_Handle);
    /*---------------开启DMA传输,需要修改一次传输的个数---------------*/
    HAL_DMA_Start(&DMA_Init_Handle, RHEOSTAT_ADC_DR_ADDR, (uint32_t)&ADC_ConvertedValue, ADC_SAMPLE_PNUM);

    // 开启ADC时钟
    RHEOSTAT_ADC_CLK_ENABLE();
    /*-------------ADC Init 结构体 参数 初始化------------*/
    // ADC1
    ADC_Handle.Instance = RHEOSTAT_ADC;
    // 时钟为fpclk 4分频
    ADC_Handle.Init.ClockPrescaler = ADC_CLOCKPRESCALER_PCLK_DIV4;
    // ADC 分辨率
    ADC_Handle.Init.Resolution = ADC_RESOLUTION_12B;
    // 禁止扫描模式,多通道采集才需要
    ADC_Handle.Init.ScanConvMode = DISABLE;
    // 连续转换
    ADC_Handle.Init.ContinuousConvMode = ENABLE;
    // 非连续转换
    ADC_Handle.Init.DiscontinuousConvMode = DISABLE;
    // 非连续转换个数
    ADC_Handle.Init.NbrOfDiscConversion   = 0;
    //禁止外部边沿触发
    ADC_Handle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
    //使用软件触发,外部触发不用配置,注释掉即可
    //ADC_Handle.Init.ExternalTrigConv      = ADC_EXTERNALTRIGCONV_T1_CC1;
    //数据右对齐
    ADC_Handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;
    //转换通道 1个
    ADC_Handle.Init.NbrOfConversion = 1;
    //使能连续转换请求
    ADC_Handle.Init.DMAContinuousRequests = ENABLE;
    //转换完成标志
    ADC_Handle.Init.EOCSelection          = DISABLE;
    // 初始化ADC
    HAL_ADC_Init(&ADC_Handle);
    //---------------------------------------------------------------------------
    ADC_Config.Channel      = RHEOSTAT_ADC_CHANNEL;
    ADC_Config.Rank         = 1;
    // 采样时间间隔
    ADC_Config.SamplingTime = ADC_SAMPLETIME_56CYCLES;
    ADC_Config.Offset       = 0;
    // 配置 ADC 通道转换顺序为1,第一个转换,采样时间为3个时钟周期
    HAL_ADC_ConfigChannel(&ADC_Handle, &ADC_Config);

    HAL_ADC_Start_DMA(&ADC_Handle, (uint32_t*)&ADC_ConvertedValue, 1);
}

4. 排序、求平均值

必须先将保存采集数据的数组ADC_ConvertedValue赋值给数组isarray,使用冒泡排序的方法,两个数比较大小,较大的数下沉,较小的数冒起来,再对采集的50个值的中间10个取平均值。

bsp_adc.c
/*--------排序、求平均值----------*/
int16_t ADC1_Proc_Value(void)
{
    uint16_t temp = 0;
    uint32_t sum = 0;
    uint32_t value = 0;
    uint16_t isarray[ADC_SAMPLE_PNUM] = {0};

    for(int i = 0; i < ADC_SAMPLE_PNUM; i++)
    {
        isarray[i] = ADC_ConvertedValue[i];
    }
    //排序
    for(int i = 0; i < ADC_SAMPLE_PNUM - 1; i++)
    {
        for(int j = i + 1; j < ADC_SAMPLE_PNUM; j++)
        {
            if(isarray[j] < isarray[i])
            {
                temp = isarray[i];
                isarray[i] = isarray[j];
                isarray[j] = temp;
            }
        }
    }

//		printf("\r\nisarray[0]=%d\r\n",isarray[0]);
//		printf("\r\nisarray[1]=%d\r\n",isarray[1]);
//		printf("\r\nisarray[2]=%d\r\n",isarray[2]);
//		printf("\r\nisarray[3]=%d\r\n",isarray[3]);
//		printf("\r\nisarray[4]=%d\r\n",isarray[4]);
//		printf("\r\nisarray[5]=%d\r\n",isarray[5]);
//		printf("\r\nisarray[6]=%d\r\n",isarray[6]);
//		printf("\r\nisarray[7]=%d\r\n",isarray[7]);
//		printf("\r\nisarray[8]=%d\r\n",isarray[8]);
//		printf("\r\nisarray[9]=%d\r\n",isarray[9]);
//		printf("\r\nisarray[10]=%d\r\n",isarray[10]);
//		printf("\r\nisarray[11]=%d\r\n",isarray[11]);
//		printf("\r\nisarray[23]=%d\r\n",isarray[23]);
//		printf("\r\nisarray[24]=%d\r\n",isarray[24]);
//		printf("\r\nisarray[25]=%d\r\n",isarray[25]);
//		printf("\r\nisarray[26]=%d\r\n",isarray[26]);
//		printf("\r\nisarray[27]=%d\r\n",isarray[27]);
//		printf("\r\nisarray[28]=%d\r\n",isarray[28]);
//		printf("\r\nisarray[29]=%d\r\n",isarray[29]);

//去掉过大过小值,求平均值
    for(int k = 20; k < ADC_SAMPLE_PNUM - 20; k++)
    {
        sum += isarray[k];
    }
    printf("\r\nsum=%d\r\n", sum);
    value = sum / (ADC_SAMPLE_PNUM - 40);

    printf("\r\nvalue=%d\r\n", value);

    return value;
}

5. 头文件以及宏定义等

bsp_adc.h
#ifndef __BSP_ADC_H
#define	__BSP_ADC_H

#include "stm32f4xx.h"
#define ADC_SAMPLE_PNUM             50//AD 采样点数数  


// ADC GPIO 宏定义
#define RHEOSTAT_ADC_GPIO_PORT              GPIOC
#define RHEOSTAT_ADC_GPIO_PIN               GPIO_PIN_0
#define RHEOSTAT_ADC_GPIO_CLK_ENABLE()      __GPIOC_CLK_ENABLE()

// ADC 序号宏定义
#define RHEOSTAT_ADC                        ADC1
#define RHEOSTAT_ADC_CLK_ENABLE()           __ADC1_CLK_ENABLE()
#define RHEOSTAT_ADC_CHANNEL                ADC_CHANNEL_10

// ADC DR寄存器宏定义,ADC转换后的数字值则存放在这里
#define RHEOSTAT_ADC_DR_ADDR                ((uint32_t)ADC1+0x4c)

// ADC DMA 通道宏定义,这里我们使用DMA传输
#define RHEOSTAT_ADC_DMA_CLK_ENABLE()       __DMA2_CLK_ENABLE()
#define RHEOSTAT_ADC_DMA_CHANNEL            DMA_CHANNEL_0
#define RHEOSTAT_ADC_DMA_STREAM             DMA2_Stream0

void Rheostat_ADC_GPIO_Config(void);
void Rheostat_ADC_Mode_Config(void);
int16_t ADC1_Proc_Value(void);
#endif 

6. 主函数任务程序

本文是建立在移植FreeRTOS实时操作系统,所以创建的一个任务代表ADC的采集,如果是裸板,那么while(1)中前4行即该传感器的对应关系计算方式,该对应关系是通过之前在4ma->0v、20ma->3.3v测的数据计算得到的。

main.c
/**********************************************************************
  * @ 函数名  : ADC_Task
  * @ 功能说明: ADC_Task任务主体
  * @ 参数    :   
  * @ 返回值  : 无
  ********************************************************************/
static void ADC_Task(void* parameter)
{	
    while (1)
    {
		float value=ADC1_Proc_Value();
		float ADC_P=244.140625f*value+100000;
		printf("\r\nvalue=%f\r\n",value);
		printf("\r\nADC_P=%0.2fPa\r\n" ,ADC_P);	    

		vTaskDelay(500);   /* 延时500个tick */
    }
}

7. 总结

以单通道可以类似地写出多通道的ADC+DMA数据采集,可以用二维数组保存数据。

  • 8
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BEAT 丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值