ADC+DMA+多通道+平均/低通滤波器

最近在学习ADC的采样,目前学的还是比较顺利的,没有遇到过于困难的问题。按照野火的教程按部就班的学就行,在滤波方面比较欠缺,咨询了一群大佬。然后在上面做一些改良。

直入主题,先来看看ADC的配置

#define ADC_PIN	( GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 )

static void ADCx_GPIO_Config(void);
static void ADCx_Mode_Config(void);

static void ADCx_GPIO_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStucture;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	//模拟输入
	GPIO_InitStucture.GPIO_Pin = ADC_PIN;
	GPIO_InitStucture.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_Init(GPIOA, &GPIO_InitStucture);
	
}

static void ADCx_Mode_Config(void)	//ADC1
{
	ADC_InitTypeDef ADC_InitStucture;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
	
	ADC_InitStucture.ADC_Mode = ADC_Mode_Independent;	//模式
	ADC_InitStucture.ADC_ScanConvMode = ENABLE;			//扫描模式
	ADC_InitStucture.ADC_ContinuousConvMode = ENABLE;	//连续转换模式
	ADC_InitStucture.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //触发模式
	ADC_InitStucture.ADC_DataAlign = ADC_DataAlign_Right; //数据对齐模式
	ADC_InitStucture.ADC_NbrOfChannel = 4;	//通道数量	
	ADC_Init(ADC1, &ADC_InitStucture);
	
	RCC_ADCCLKConfig(RCC_PCLK2_Div8);  //8分频  72M/8 = 9M
	ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1 , ADC_SampleTime_55Cycles5); //规则通道转换
	ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2 , ADC_SampleTime_55Cycles5); //规则通道转换
	ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3 , ADC_SampleTime_55Cycles5); //规则通道转换
	ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4 , ADC_SampleTime_55Cycles5); //规则通道转换
	//ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 5 , ADC_SampleTime_55Cycles5); //规则通道转换
	//ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 6 , ADC_SampleTime_55Cycles5); //规则通道转换
	//ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 7 , ADC_SampleTime_55Cycles5); //规则通道转换
	//ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 8 , ADC_SampleTime_55Cycles5); //规则通道转换
	
	ADC_DMACmd(ADC1, ENABLE);//DMA启动//ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE);//开启中断
	
	ADC_Cmd(ADC1, ENABLE); //使能ADCx
	
	ADC_StartCalibration(ADC1);//开始校准
	while(ADC_GetCalibrationStatus(ADC1));//等待校准完成
	
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);//软件中断
	
}

void ADC_InitConfig(void)
{
	ADCx_GPIO_Config();
	ADCx_Mode_Config();
}

这边是采用了四个ADC通道A0、A1、A2、A3。

由于是四通道的ADC,在配置上就需要开启连续转换模式、扫描模式,通道数量也要明确标识。

然后是数据转换,每个通道需要设定转换顺序(顺序要求根据项目)。

DAM传输的需要开启DAM使能,如果中断的要开启中断使能。(这边不清楚什么时候开始DMA传输,需要看一下规格书)

这样ADC初始化基本就结束了。下面看一下DMA的初始化吧

static void DMA_Config(void);

static void DMA_Config(void)
{
	DMA_InitTypeDef DMA_InitStruct;
	
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1 ,ENABLE);	
	
	DMA_DeInit(DMA1_Channel1);
	DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&( ADC1->DR );
	DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)ADC_Value.adc_Buff_value;
	DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;//从外设读
	DMA_InitStruct.DMA_BufferSize = 4;
	DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
	DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
	DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
	DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
	DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;
	DMA_InitStruct.DMA_Priority = DMA_Priority_High;
	DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;
	DMA_Init(DMA1_Channel1, &DMA_InitStruct);
	
	DMA_Cmd(DMA1_Channel1, ENABLE);
	DMA_ClearFlag(DMA1_FLAG_TC1);
}

void DMA1_Init(void)
{
	DMA_Config();
}

有几点需要注意的:

1、DMA的读取方向:外设->寄存器

2、数据数量:几个ADC通道就有几个(这里是4)

3、外设地址不增加;寄存器地址增加

为了方便存储放置了一个缓存池

typedef struct
{
	uint16_t adc0_value[8];
	uint16_t adc1_value[8];
	uint16_t adc2_value[8];
	uint16_t adc3_value[8];
	uint16_t adc4_value[8];
	uint16_t adc5_value[8];
	uint16_t adc6_value[8];
	uint16_t adc7_value[8];
	uint16_t adc_Buff_value[4];	//缓存池
	float adc_add_convertvalue[4]; //8位累加过滤
	float adc_convertvalue[4]; //电压值
	float adc_res_convertvalue[4]; //电阻值
	float adc_tem_convertvalue[4];	//温度
}ADC_ValueConfig;

因为ADC的DR寄存器每次只能缓存一组数据,当数据进来后会保存到缓存adc_Buff_value中。4组ADC一次保存进去,随后ADC进入轮寻模式。

主要计算(Main.C)

ADC_ValueConfig ADC_Value;

int main()
{
	uint8_t i;
	
	DMA1_Init();
	ADC_InitConfig();
	
	while(1)
	{
		ADC_Value.adc_add_convertvalue[0] = ADC_Value.adc_add_convertvalue[1] = ADC_Value.adc_add_convertvalue[2] =ADC_Value.adc_add_convertvalue[3] = 0;
		
		for( i = 0; i < 8; i++ )
		{
			ADC_Value.adc0_value[i] = lowV_0(ADC_Value.adc_Buff_value[0]);
			ADC_Value.adc1_value[i] = lowV_1(ADC_Value.adc_Buff_value[1]);
			ADC_Value.adc2_value[i] = lowV_2(ADC_Value.adc_Buff_value[2]);
			ADC_Value.adc3_value[i] = lowV_3(ADC_Value.adc_Buff_value[3]);
		}
		
		for( i = 0; i < 8; i++ )
		{
			ADC_Value.adc_add_convertvalue[0] += ADC_Value.adc0_value[i];
			ADC_Value.adc_add_convertvalue[1] += ADC_Value.adc1_value[i];
			ADC_Value.adc_add_convertvalue[2] += ADC_Value.adc2_value[i];
			ADC_Value.adc_add_convertvalue[3] += ADC_Value.adc3_value[i];
		}
		
		ADC_Value.adc_convertvalue[0] = ( float )( ( ADC_Value.adc_add_convertvalue[0] )/4096*3300 )/8;
		ADC_Value.adc_convertvalue[1] = ( float )( ( ADC_Value.adc_add_convertvalue[1] )/4096*3300 )/8;
		ADC_Value.adc_convertvalue[2] = ( float )( ( ADC_Value.adc_add_convertvalue[2] )/4096*3300 )/8;
		ADC_Value.adc_convertvalue[3] = ( float )( ( ADC_Value.adc_add_convertvalue[3] )/4096*3300 )/8;
		
		ADC_Value.adc_res_convertvalue[0] = ( 200000*ADC_Value.adc_convertvalue[0] ) / ( 3300 - ADC_Value.adc_convertvalue[0] );
		ADC_Value.adc_res_convertvalue[1] = ( 200000*ADC_Value.adc_convertvalue[1] ) / ( 3300 - ADC_Value.adc_convertvalue[1] );
		ADC_Value.adc_res_convertvalue[2] = ( 200000*ADC_Value.adc_convertvalue[2] ) / ( 3300 - ADC_Value.adc_convertvalue[2] );
		ADC_Value.adc_res_convertvalue[3] = ( 200000*ADC_Value.adc_convertvalue[3] ) / ( 3300 - ADC_Value.adc_convertvalue[3] );
		
		ADC_Value.adc_tem_convertvalue[0] = 1/( (log(ADC_Value.adc_res_convertvalue[0]/10000))/3950.0 + 1.0/(273.15+25.0) ) - 273.15;
		ADC_Value.adc_tem_convertvalue[1] = 1/( (log(ADC_Value.adc_res_convertvalue[1]/10000))/3950.0 + 1.0/(273.15+25.0) ) - 273.15;
		ADC_Value.adc_tem_convertvalue[2] = 1/( (log(ADC_Value.adc_res_convertvalue[2]/10000))/3950.0 + 1.0/(273.15+25.0) ) - 273.15;
		ADC_Value.adc_tem_convertvalue[3] = 1/( (log(ADC_Value.adc_res_convertvalue[3]/10000))/3950.0 + 1.0/(273.15+25.0) ) - 273.15;
		
		
		DMA_ClearFlag(DMA1_FLAG_TC1);
	}
}

adc0_value每次会存储通道0的八个数据

adc_add_convertvalue[ 0]通道0的八个数据求和

adc_convertvalue[0 ]求出数据的平均数

adc_res_convertvalue[0]将平均数转换为电阻值(根据实际电路计算,下面给出这个的电路参考)

adc_tem_convertvalue[0 ] 根据公式换算出温度

低通滤波器

unsigned int lowV_0( unsigned int com )
{
    static unsigned int iLastData;    //上一次值
    unsigned int iData;               //本次计算值
    float dPower = 0.3;               //滤波系数
    iData = ( com * dPower ) + ( 1 - dPower ) * iLastData; //计算
    iLastData = iData;                                     //存贮本次数据
    return iData;                                         //返回数据
}

直接套用就行,理论知识大致如下

算法实现的公式如下:

 y(n) = q*x(n) + (1-q)*y(n-1)                                    

其中Y(n)为输出,x(n)为输入,y(n-1)为上一次输出值,其中q为滤波系数。取值范围为0--1.

也就是说若q=0.5时,这个公式代表的意思就是取本次采样值的50%,加上上一次采样值的50%,做为本次的采样结果。也就是说每次的采样结果都和上一次的采样结果相关。

NTC电路图以及换算公式

 根据电压值反推NTC当前的阻值即可。

NTC 热敏电阻温度计算公式:Rt = R *EXP(B*(1/T1-1/T2))

其中,T1和T2指的是K度,即开尔文温度。

Rt 是热敏电阻在T1温度下的阻值。

R是热敏电阻在T2常温下的标称阻值。100K的热敏电阻25℃的值为100K(即R=100K)。T2=(273.15+25)

EXP是e的n次方

B值是热敏电阻的重要参数

通过转换可以得到温度T1与电阻Rt的关系T1=1/(ln(Rt/R)/B+1/T2)   (所以对应只有一个Rt未知数即可求出T1实时温度)

对应的摄氏温度t=T1-273.15,同时+0.5的误差矫正。

OK,ADC这块基本就算结束了

还有电源方面、485通讯以及内部FLASH要学习一下

加油鱼仔,你是最棒的

NTC原文链接:https://blog.csdn.net/qq_42660303/article/details/84145382
低通滤波器原文链接:https://blog.csdn.net/qq_20222919/article/details/105098071

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
ADC DMA传输多通道采集程序是用于实现多通道模拟信号采集和传输的程序。ADC代表模数转换器,用于将模拟信号转换为数字信号。DMA代表直接存储器访问,是一种数据传输方式,用于高效地将数据从一个地方传输到另一个地方,而不需要CPU的干预。 在多通道采集程序中,通常会使用多个ADC模块,每个模块对应一个通道。每个通道采集到的模拟信号会经过相应的ADC模块进行模数转换,转换后的数字信号会通过DMA传输到内存中进行存储或进一步处理。 程序的运行过程一般包括以下几个步骤: 1. 配置并启动ADC模块:设置采样频率、采样精度和通道数等参数,并启动ADC模块开始采样。 2. 配置DMA:设置DMA通道、数据传输方向和缓冲区等参数,并启动DMA传输。 3. 等待DMA传输完成中断:等待DMA传输完成中断的触发,表示数据已经传输完毕。 4. 处理采集到的数据:在DMA传输完成中断中,可以读取内存中的数据进行进一步处理,例如计算平均值或进行频谱分析等。 5. 重复以上步骤进行连续采集:根据需求,可以设置程序的循环次数或条件,以实现连续的多通道采集。 ADC DMA传输多通道采集程序的优势在于,通过DMA传输,可以减少CPU的使用率,提高数据传输的效率;同时,多通道采集可以同时获取多个通道的数据,方便进行多通道信号处理和分析。这种程序在很多领域都有广泛的应用,例如声音处理、图像采集和实时控制等。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值