当前位置:   article > 正文

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

dma滤波

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

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

  1. #define ADC_PIN ( GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 )
  2. static void ADCx_GPIO_Config(void);
  3. static void ADCx_Mode_Config(void);
  4. static void ADCx_GPIO_Config(void)
  5. {
  6. GPIO_InitTypeDef GPIO_InitStucture;
  7. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
  8. //模拟输入
  9. GPIO_InitStucture.GPIO_Pin = ADC_PIN;
  10. GPIO_InitStucture.GPIO_Mode = GPIO_Mode_AIN;
  11. GPIO_Init(GPIOA, &GPIO_InitStucture);
  12. }
  13. static void ADCx_Mode_Config(void) //ADC1
  14. {
  15. ADC_InitTypeDef ADC_InitStucture;
  16. RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
  17. ADC_InitStucture.ADC_Mode = ADC_Mode_Independent; //模式
  18. ADC_InitStucture.ADC_ScanConvMode = ENABLE; //扫描模式
  19. ADC_InitStucture.ADC_ContinuousConvMode = ENABLE; //连续转换模式
  20. ADC_InitStucture.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //触发模式
  21. ADC_InitStucture.ADC_DataAlign = ADC_DataAlign_Right; //数据对齐模式
  22. ADC_InitStucture.ADC_NbrOfChannel = 4; //通道数量
  23. ADC_Init(ADC1, &ADC_InitStucture);
  24. RCC_ADCCLKConfig(RCC_PCLK2_Div8); //8分频 72M/8 = 9M
  25. ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1 , ADC_SampleTime_55Cycles5); //规则通道转换
  26. ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2 , ADC_SampleTime_55Cycles5); //规则通道转换
  27. ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3 , ADC_SampleTime_55Cycles5); //规则通道转换
  28. ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4 , ADC_SampleTime_55Cycles5); //规则通道转换
  29. //ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 5 , ADC_SampleTime_55Cycles5); //规则通道转换
  30. //ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 6 , ADC_SampleTime_55Cycles5); //规则通道转换
  31. //ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 7 , ADC_SampleTime_55Cycles5); //规则通道转换
  32. //ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 8 , ADC_SampleTime_55Cycles5); //规则通道转换
  33. ADC_DMACmd(ADC1, ENABLE);//DMA启动//ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE);//开启中断
  34. ADC_Cmd(ADC1, ENABLE); //使能ADCx
  35. ADC_StartCalibration(ADC1);//开始校准
  36. while(ADC_GetCalibrationStatus(ADC1));//等待校准完成
  37. ADC_SoftwareStartConvCmd(ADC1, ENABLE);//软件中断
  38. }
  39. void ADC_InitConfig(void)
  40. {
  41. ADCx_GPIO_Config();
  42. ADCx_Mode_Config();
  43. }

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

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

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

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

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

  1. static void DMA_Config(void);
  2. static void DMA_Config(void)
  3. {
  4. DMA_InitTypeDef DMA_InitStruct;
  5. RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1 ,ENABLE);
  6. DMA_DeInit(DMA1_Channel1);
  7. DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&( ADC1->DR );
  8. DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)ADC_Value.adc_Buff_value;
  9. DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;//从外设读
  10. DMA_InitStruct.DMA_BufferSize = 4;
  11. DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  12. DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
  13. DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
  14. DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
  15. DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;
  16. DMA_InitStruct.DMA_Priority = DMA_Priority_High;
  17. DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;
  18. DMA_Init(DMA1_Channel1, &DMA_InitStruct);
  19. DMA_Cmd(DMA1_Channel1, ENABLE);
  20. DMA_ClearFlag(DMA1_FLAG_TC1);
  21. }
  22. void DMA1_Init(void)
  23. {
  24. DMA_Config();
  25. }

有几点需要注意的:

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

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

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

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

  1. typedef struct
  2. {
  3. uint16_t adc0_value[8];
  4. uint16_t adc1_value[8];
  5. uint16_t adc2_value[8];
  6. uint16_t adc3_value[8];
  7. uint16_t adc4_value[8];
  8. uint16_t adc5_value[8];
  9. uint16_t adc6_value[8];
  10. uint16_t adc7_value[8];
  11. uint16_t adc_Buff_value[4]; //缓存池
  12. float adc_add_convertvalue[4]; //8位累加过滤
  13. float adc_convertvalue[4]; //电压值
  14. float adc_res_convertvalue[4]; //电阻值
  15. float adc_tem_convertvalue[4]; //温度
  16. }ADC_ValueConfig;

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

主要计算(Main.C)

  1. ADC_ValueConfig ADC_Value;
  2. int main()
  3. {
  4. uint8_t i;
  5. DMA1_Init();
  6. ADC_InitConfig();
  7. while(1)
  8. {
  9. 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;
  10. for( i = 0; i < 8; i++ )
  11. {
  12. ADC_Value.adc0_value[i] = lowV_0(ADC_Value.adc_Buff_value[0]);
  13. ADC_Value.adc1_value[i] = lowV_1(ADC_Value.adc_Buff_value[1]);
  14. ADC_Value.adc2_value[i] = lowV_2(ADC_Value.adc_Buff_value[2]);
  15. ADC_Value.adc3_value[i] = lowV_3(ADC_Value.adc_Buff_value[3]);
  16. }
  17. for( i = 0; i < 8; i++ )
  18. {
  19. ADC_Value.adc_add_convertvalue[0] += ADC_Value.adc0_value[i];
  20. ADC_Value.adc_add_convertvalue[1] += ADC_Value.adc1_value[i];
  21. ADC_Value.adc_add_convertvalue[2] += ADC_Value.adc2_value[i];
  22. ADC_Value.adc_add_convertvalue[3] += ADC_Value.adc3_value[i];
  23. }
  24. ADC_Value.adc_convertvalue[0] = ( float )( ( ADC_Value.adc_add_convertvalue[0] )/4096*3300 )/8;
  25. ADC_Value.adc_convertvalue[1] = ( float )( ( ADC_Value.adc_add_convertvalue[1] )/4096*3300 )/8;
  26. ADC_Value.adc_convertvalue[2] = ( float )( ( ADC_Value.adc_add_convertvalue[2] )/4096*3300 )/8;
  27. ADC_Value.adc_convertvalue[3] = ( float )( ( ADC_Value.adc_add_convertvalue[3] )/4096*3300 )/8;
  28. ADC_Value.adc_res_convertvalue[0] = ( 200000*ADC_Value.adc_convertvalue[0] ) / ( 3300 - ADC_Value.adc_convertvalue[0] );
  29. ADC_Value.adc_res_convertvalue[1] = ( 200000*ADC_Value.adc_convertvalue[1] ) / ( 3300 - ADC_Value.adc_convertvalue[1] );
  30. ADC_Value.adc_res_convertvalue[2] = ( 200000*ADC_Value.adc_convertvalue[2] ) / ( 3300 - ADC_Value.adc_convertvalue[2] );
  31. ADC_Value.adc_res_convertvalue[3] = ( 200000*ADC_Value.adc_convertvalue[3] ) / ( 3300 - ADC_Value.adc_convertvalue[3] );
  32. 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;
  33. 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;
  34. 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;
  35. 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;
  36. DMA_ClearFlag(DMA1_FLAG_TC1);
  37. }
  38. }

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

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

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

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

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

低通滤波器

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

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

算法实现的公式如下:

 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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/236653
推荐阅读
相关标签
  

闽ICP备14008679号