1. 内容简述
之前项目中需要对麦克传感器的mpm480隔爆压力变送器(4-20ma输出)的数据进行实时采集,使用STM32F407作为控制器,使用信号转换模块将压力变送器4-20ma的输出转换为0-3.3v的信号量,输入到STM32F407板子的ADC1的通道10,并使用DMA2通道0数据流0将采集的多个值从外设直接存入存储器,并对该次采集的值进行排序、去掉过大过小值后取平均值,最后在FreeRTOS实时操作系统的一个任务中计算出测得的气压值。
2. 电路连接
本文采用的是二线制压力变送器,连接如下图。
注意:黄色线(输出电压)连接板子的ADC1的通道10 (PC0)引脚、并需要共地!
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 转换数据输出周期就越长同时数据精度越高。
#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个取平均值。
/*--------排序、求平均值----------*/
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. 头文件以及宏定义等
#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测的数据计算得到的。文章来源:https://www.toymoban.com/news/detail-597012.html
/**********************************************************************
* @ 函数名 : 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数据采集,可以用二维数组保存数据。文章来源地址https://www.toymoban.com/news/detail-597012.html
到了这里,关于【STM32F407 ADC+DMA采集压力变送器数据(HAL库)】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!