我的第一篇文章——stm32的ADC+DMA+滤波算法

一 工程目的

利用stm32的ADC1外设实现双通道循环测量电压

(1)外设配置

1.GPIO
2.ADC
3.DMA

(2)源码

头文件

#ifndef __ADC_H
#define __ADC_H
#include “sys.h”

#define N 20//adc保存数组行
#define M 2//列

void adc_Init(void);
u16 Get_Adc(ADC_TypeDef* ADCx,u8 ch);
u16 Get_Adc_Average(ADC_TypeDef* ADCx,u8 ch,u8 times);
void adc_filter(u16 index[N][M]);

#endif

主文件

#include “adc.h”
#include “delay.h”
#include “LED.h”

volatile u16 adc_convert[N][M]; //ADC存放数值
volatile u16 After_Filter[M];//滤波后数组

void adc_Init(void)
{

GPIO_InitTypeDef GPIO_InitStruct;
ADC_InitTypeDef  ADC_InitStruct;
DMA_InitTypeDef	DMA_InitStruct;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_ADC1, ENABLE);	 //使能PA端口时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
		
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2|GPIO_Pin_1;				 
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN; 		 
GPIO_Init(GPIOA, &GPIO_InitStruct);					 //根据设定参数初始化GPIOA.0.1

DMA_DeInit(DMA1_Channel1);
DMA_InitStruct.DMA_MemoryBaseAddr = (u32) adc_convert;
DMA_InitStruct.DMA_PeripheralBaseAddr = (u32) &ADC1->DR;
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC ;
DMA_InitStruct.DMA_BufferSize = M*N;//根据adc采集通道数量变化
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  //外设地址寄存器不变
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;  //内存地址寄存器递增
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //数据宽度为16位
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;  //数据宽度为16位
DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;  //工作在循环模式,buffer写满,自动回初始地址开始传输
DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;  //DMA通道x没有设置为内存到内存传输
DMA_InitStruct.DMA_Priority = DMA_Priority_High;
DMA_Init(DMA1_Channel1, &DMA_InitStruct);  //根据DMA_InitStruct中指定的参数初始化DMA的通道
DMA_Cmd(DMA1_Channel1, ENABLE);

ADC_DeInit(ADC1);  //复位ADC1,将外设 ADC1 的全部寄存器重设为缺省值//复位ADC1,同时设置ADC1分频因子
ADC_InitStruct.ADC_DataAlign=ADC_DataAlign_Right;//ADC数据右对齐
ADC_InitStruct.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;//转换由软件而不是外部触发启动
ADC_InitStruct.ADC_Mode=ADC_Mode_Independent;//ADC工作模式:ADC1和ADC2工作在独立模式
ADC_InitStruct.ADC_ScanConvMode=ENABLE;//扫描模式
ADC_InitStruct.ADC_ContinuousConvMode = ENABLE;//连续转换 
ADC_InitStruct.ADC_NbrOfChannel = M;
ADC_Init(ADC1,&ADC_InitStruct);//根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
	
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_13Cycles5 );
ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 2, ADC_SampleTime_13Cycles5 );


ADC_DMACmd(ADC1,ENABLE);//adc_dma使能
ADC_Cmd(ADC1, ENABLE);	//使能指定的ADC1



ADC_ResetCalibration(ADC1);	//使能复位校准
while(ADC_GetResetCalibrationStatus(ADC1));	//等待复位校准结束
ADC_StartCalibration(ADC1);	 //开启AD校准
while(ADC_GetCalibrationStatus(ADC1));	 //等待校准结束
	
ADC_SoftwareStartConvCmd(ADC1, ENABLE);	//这句话一定要有,就算这里不写,主函数也要写,否者开不了adc

}
//获得ADC值
//ch:通道值 0~3
u16 Get_Adc(ADC_TypeDef* ADCx,u8 ch)
{
//设置指定ADC的规则组通道,一个序列,采样时间
ADC_RegularChannelConfig(ADCx, ch, 1, ADC_SampleTime_13Cycles5 ); //ADC1,ADC通道,采样时间为13.5周期

ADC_SoftwareStartConvCmd(ADCx, ENABLE);		//使能指定的ADC1的软件转换启动功能	
 
while(!ADC_GetFlagStatus(ADCx, ADC_FLAG_EOC ));//等待转换结束

return ADC_GetConversionValue(ADCx);	//返回最近一次ADC1规则组的转换结果

}

u16 Get_Adc_Average(ADC_TypeDef* ADCx,u8 ch,u8 times)
{
u32 temp_val=0;
u8 t;
for(t=0;t<times;t++)
{
temp_val+=Get_Adc(ADCx,ch);
delay_ms(5);
}
return temp_val/times;
}

void adc_filter(u16 index[N][M])//202的中值平均滤波算法
{
/软件滤波模块
/
u16 filter_temp;
u8 i,j,k,flag=1;

for(k=0;k<M;k++)
{	
	for(i=0;i<N && flag;i++)//冒泡排序法
		{	
					for(j=0;j<N-1-i;j++)
					{
						flag=0;
					if(index[j][k]>=index[j+1][k])
						{
							filter_temp = index[j][k];
							index[j][k] = index[j+1][k];
							index[j+1][k] = filter_temp;
							flag=1;
						}
					}	
		}
		After_Filter[k]=(index[(N/2)+1][k]+index[(N/2)][k])/2;
}		

}

二 调试过程遇到的问题

1 如果打开中断

使用了ADC开了DMA中断,主函数会被频繁打断,除非你主函数不想执行功能,否者你就别打开DMA传输完成中断。

2 无法打开ADC

程序中adc读数始终不正常,经过软件仿真,一步一步执行,发现是ADC没有读数据,进一步发现,并没正常打开ADC,通过软件打开ADC要加下面这句话。
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //这句话一定要有,就算这里不写,主函数也要写,否者开不了adc

3 变量无法赋值

MDK这个软件会故意优化,你要定义volatile(谷歌翻译为易变)型变量。具体情况百度

三 参考资料

https://blog.csdn.net/i792439187/article/details/8825397
https://blog.csdn.net/TanTrey/article/details/82992605
正点原子例程

四 写在最后

这篇文章主要写一些我在使用ADC过程遇到的问题,希望给看到的各位提供到帮助

  • 15
    点赞
  • 137
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
STM32 ADC DMA例程是一种使用STM32微控制器的模拟到数字转换器(ADC)和直接内存访问(DMA)功能的代码示例。该例程旨在帮助开发人员快速了解如何配置和使用ADCDMA功能来高效地采集模拟信号并将其传输到内存中。 代码示例中通常包括以下步骤: 1. 初始化ADC模块和DMA模块:通过配置相关的寄存器,设置ADC通道和采样速率、分辨率等参数,同时配置DMA通道和内存地址。 2. 配置ADC触发源:选择合适的触发源,例如定时器、外部触发或软件触发。设置触发源后,ADC将在触发事件发生时启动转换。 3. 配置DMA传输:设置DMA传输方向和传输大小,选择源和目的地址等。这些设置将确保在ADC转换完成后,数据能够正确地传输到指定的内存地址。 4. 启动ADCDMA:启动ADCDMA模块,使它们开始采集和传输数据。可以通过启动ADC转换和DMA传输来触发整个过程。 5. 处理数据:在DMA传输完成后,可以在中断服务程序中访问采集到的数据。这些数据可以进行进一步的处理和分析,例如滤波、计算统计指标或用于控制其他硬件设备。 通过使用ADCDMA模块,可以实现高效的模拟信号采集,并减少CPU的负载。相比于使用中断方式来处理ADC转换,DMA可以自动将数据传输到内存中,同时允许CPU继续执行其他任务。 总之,STM32 ADC DMA例程提供了一种简单而强大的方法来配置和使用ADCDMA功能,使开发人员能够轻松实现高效的模拟信号采集和数据处理。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值