适用于嵌入式ADC RC低通滤波

最近在做MPPT,在电流采集滤波卡了很久,一直做不好 得到的结果可能会出乎意料,上下抖动让人抓狂,万用表一测量基本不变,试着怀疑硬件电路设计问题;参考GITHUB 大师们的算法:GitHub - danjulio/MPPT-Solar-Charger: Supporting documentation and software for the MPPT Solar Charger

这里我们不需要得到一个变化很快的电压、电流值,可以采用RC低通滤波算法来解决,话不多说,算法上链接:

https://www.edn.com/design/systems-design/4320010/A-simple-software-lowpass-filter-suits-embedded-system-applications  

本次实验采用mm32 作为主控,adc采用PWM 比较触发,得到的结果通过DMA 存储到 临时变量;

单独使用TIM3 作为采样的定时器:

硬件初始化:


#define		ADC_SMPILE_NUM		4
#define		ADC_CHANNEL_NUM		6
uint16_t adc1Value[ADC_SMPILE_NUM][ADC_CHANNEL_NUM];

// Filtered measurement variables
uint32_t  adcFilterSum[ADC_SMPILE_NUM];
uint16_t  adcFilteredAdcVal[ADC_SMPILE_NUM];

uint16_t _ADC_GetSingleReading(uint8_t adcChannel)
{
	uint32_t adcval =adc1Value[0][adcChannel]+
					adc1Value[1][adcChannel]+
					adc1Value[2][adcChannel]+
					adc1Value[3][adcChannel];
	return 	adcval>>2;
}

void hdl_adcInit(void){

	GPIO_InitTypeDef GPIO_InitStruct;
	RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE); 
	RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOB, ENABLE);

	GPIO_StructInit(&GPIO_InitStruct);
	GPIO_InitStruct.GPIO_Pin  =  GPIO_Pin_1|GPIO_Pin_4/*|GPIO_Pin_5*/|GPIO_Pin_6|GPIO_Pin_7;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStruct.GPIO_Mode =GPIO_Mode_AIN;
	GPIO_Init(GPIOA, &GPIO_InitStruct);
	GPIO_InitStruct.GPIO_Pin  =  GPIO_Pin_0|GPIO_Pin_1;
	GPIO_Init(GPIOB, &GPIO_InitStruct);
	
	DMA_InitTypeDef DMA_InitStruct;
	RCC_AHBPeriphClockCmd(RCC_AHBENR_DMA1, ENABLE);
	DMA_DeInit(DMA1_Channel1);
	DMA_StructInit(&DMA_InitStruct);
	//DMA transfer peripheral address
	DMA_InitStruct.DMA_PeripheralBaseAddr = (u32) & (ADC1->DR);
	//DMA transfer memory address
	DMA_InitStruct.DMA_MemoryBaseAddr = (u32)adc1Value;
	//DMA transfer direction from peripheral to memory
	DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;
	//DMA cache size
	DMA_InitStruct.DMA_BufferSize = ADC_SMPILE_NUM*ADC_CHANNEL_NUM;
	//After receiving the data, the peripheral address is forbidden to move backward
	DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
	//After receiving the data, the memory address is shifted backward
	DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
	//Define the peripheral data width to 16 bits
	DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
	//Define the memory data width to 16 bits
	DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
	//Cycle conversion mode
	DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;
	//DMA priority is high
	DMA_InitStruct.DMA_Priority = DMA_Priority_High;
	//M2M mode is disabled
	DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;
	DMA_InitStruct.DMA_Auto_reload = DMA_Auto_Reload_Disable;
	DMA_Init(DMA1_Channel1, &DMA_InitStruct);
	DMA_Cmd(DMA1_Channel1, ENABLE);
		
	ADC_DeInit(ADC1);
	ADC_InitTypeDef  ADC_InitStruct;
	ADC_StructInit(&ADC_InitStruct);
	//Enable ADC clock
	RCC_APB2PeriphClockCmd(RCC_APB2ENR_ADC1, ENABLE);
	ADC_InitStruct.ADC_Resolution = ADC_Resolution_12b;
	//ADC prescale factor
	ADC_InitStruct.ADC_PRESCARE = ADC_PCLK2_PRESCARE_2;
	//Set ADC mode to continuous conversion mode
	ADC_InitStruct.ADC_Mode = ADC_Mode_Continue;
	//AD data right-justified
	ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;
    ADC_InitStruct.ADC_ExternalTrigConv = ADC1_ExternalTrigConv_T2_TRIG;
	ADC_Init(ADC1, &ADC_InitStruct);

	//Enable the channel
	ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 0, ADC_Samctl_8_5);//1
	ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 0, ADC_Samctl_8_5);//2
	ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 0, ADC_Samctl_8_5);//3
	ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 0, ADC_Samctl_8_5);//4
	ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 0, ADC_Samctl_8_5);//5
	ADC_RegularChannelConfig(ADC1, ADC_Channel_9, 0, ADC_Samctl_8_5);//6
  //Enable ADCDMA
	ADC_DMACmd(ADC1, ENABLE);
	//Enable AD conversion
	ADC_Cmd(ADC1, ENABLE);
	ADC_ExternalTrigConvConfig(ADC1,ADC1_ExternalTrigConv_T1_CC1);
	ADC1->CR |=(1<<24)|(3<<19);//下降沿触发 ,512 个延时周期
	ADC_ExternalTrigConvCmd(ADC1, ENABLE);
}

// adc samp 
void TIM3_init(void){
   RCC_APB1PeriphClockCmd(RCC_APB1ENR_TIM3, ENABLE);
	uint32_t arr =72000000L /100 -1;//100hz adc采样率
	TIM3->CR1 =(1<<7)|(1<<2);
	TIM3->PSC =0;
	TIM3->ARR =arr;
	TIM3->DIER =(1<<0);
		
	NVIC_InitTypeDef NVIC_InitStruct;
    NVIC_InitStruct.NVIC_IRQChannel = TIM3_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelPriority = 2;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);
	TIM3->CR1|=(1<<0);//enable tim1
}

void ADCs_Init(void)
{
	hdl_adcInit();
	delay_ms(5000);
	// Setup the filtered ADC value filters
	adcFilteredAdcVal[ADC_MEAS_VS_INDEX] = _ADC_GetSingleReading(_ADC_VS_CH);
	adcFilterSum[ADC_MEAS_VS_INDEX] = (adcFilteredAdcVal[ADC_MEAS_VS_INDEX] << ADC_V_FILTER_SHIFT);
	adcFilteredAdcVal[ADC_MEAS_IS_INDEX] = _ADC_GetSingleReading(_ADC_IS_CH);
	adcFilterSum[ADC_MEAS_IS_INDEX] = (adcFilteredAdcVal[ADC_MEAS_IS_INDEX] << ADC_I_FILTER_SHIFT);
	adcFilteredAdcVal[ADC_MEAS_VB_INDEX] = _ADC_GetSingleReading(_ADC_VB_CH);
	adcFilterSum[ADC_MEAS_VB_INDEX] = (adcFilteredAdcVal[ADC_MEAS_VB_INDEX] << ADC_V_FILTER_SHIFT);
	adcFilteredAdcVal[ADC_MEAS_IB_INDEX] = _ADC_GetSingleReading(_ADC_IB_CH);
	adcFilterSum[ADC_MEAS_IB_INDEX] = (adcFilteredAdcVal[ADC_MEAS_IB_INDEX] << ADC_I_FILTER_SHIFT);
	TIM3_init();
}

H 文件:

// Measurement indices (first 4 must be measured ADC values)
#define ADC_NUM_MEASUREMENTS 6

#define _ADC_VS_CH 4
#define _ADC_IS_CH 2
#define _ADC_VB_CH 3
#define _ADC_IB_CH 1
#define _ADC_TE_CH 6
#define _ADC_TI_CH 5

#define ADC_MEAS_VS_INDEX    (_ADC_VS_CH-1)//0
#define ADC_MEAS_IS_INDEX    (_ADC_IS_CH-1)//1
#define ADC_MEAS_VB_INDEX    (_ADC_VB_CH-1)//2
#define ADC_MEAS_IB_INDEX    (_ADC_IB_CH-1)//3
#define ADC_MEAS_TI_INDEX    (_ADC_TI_CH-1)//4
#define ADC_MEAS_TE_INDEX    (_ADC_TE_CH-1)//5
//
// Low-pass digital filter parameters
//  From: https://www.edn.com/design/systems-design/4320010/A-simple-software-lowpass-filter-suits-embedded-system-applications
//
//  K        Bandwidth (normalized to 1Hz)     Rise Time (Samples)
//  1        0.1197                            3
//  2        0.0466                            8
//  3        0.0217                            16
//  4        0.0104                            34
//  5        0.0051                            69
//  6        0.0026                            140
//  7        0.0012                            280
//  8        0.0007                            561
//
#define ADC_V_FILTER_SHIFT  3
#define ADC_I_FILTER_SHIFT  6

// ADC Interrupt control macros
#define _ADC_DIS_INT() TIM3->CR1&=~(1<<0) 
#define _ADC_EN_INT()  TIM3->CR1|=(1<<0)

中断读取DMA 采集缓存:


void _ADC_PushFilteredVal(uint16_t val, uint8_t index)
{
	// Even indices are voltage, odd are current
	if (index < 0x02) {//0 .1
		// Update current filter with current sample
		adcFilterSum[index] = adcFilterSum[index] - (adcFilterSum[index] >> ADC_I_FILTER_SHIFT) + val;
		// Scale for unity gain
		adcFilteredAdcVal[index] = adcFilterSum[index] >> ADC_I_FILTER_SHIFT;
	} else {//2 . 3
		// Update current filter with current sample
		adcFilterSum[index] = adcFilterSum[index] - (adcFilterSum[index] >> ADC_V_FILTER_SHIFT) + val;
		// Scale for unity gain
		adcFilteredAdcVal[index] = adcFilterSum[index] >> ADC_V_FILTER_SHIFT;
	}
}

void TIM3_IRQHandler(void){
	TIM3->SR =0;
// Store the ADC result
	uint16_t ADC0_VAl;
	for(int i=0;i

获取ADC 电压电流:


// Designed to be called by code from the main loop thread
int16_t ADC_GetValue(uint8_t index)
{
	uint16_t t1;

	// Atomically sample current value to prevent ISR from updating it halfway
	// through our access
	switch (index) {
	case ADC_MEAS_VS_INDEX:
		_ADC_DIS_INT();
		t1 = adcFilteredAdcVal[ADC_MEAS_VS_INDEX];
		_ADC_EN_INT();
		return(_adc2mV_vs(t1));
	case ADC_MEAS_IS_INDEX:
		_ADC_DIS_INT();
		t1 = adcFilteredAdcVal[ADC_MEAS_IS_INDEX];
		_ADC_EN_INT();
		return(_adc2mA_is(t1));
	case ADC_MEAS_VB_INDEX:
		_ADC_DIS_INT();
		t1 = adcFilteredAdcVal[ADC_MEAS_VB_INDEX];
		_ADC_EN_INT();
		return(_adc2mV_vb(t1));
	case ADC_MEAS_IB_INDEX:
		_ADC_DIS_INT();
		t1 = adcFilteredAdcVal[ADC_MEAS_IB_INDEX];
		_ADC_EN_INT();
		return(_adc2mA_il(t1));
	case ADC_MEAS_TI_INDEX:
//		_ADC_DIS_INT();
//		t1 = adcTempIntAvgAdcVal;
//		_ADC_EN_INT();
		return(_adc2IntT10(t1));
	case ADC_MEAS_TE_INDEX:
//		_ADC_DIS_INT();
//		t1 = adcTempExtAvgAdcVal;
//		_ADC_EN_INT();
		return(_adc2ExtT10(t1));
	default:
		return(0);
	}
}

通过 ADC_GetValue()函数直接获取到 ADC 电压。电流值 ;这里TIM3 的定时值 为100Hz,当选择10Hz 时输出基本无变化。

你可能感兴趣的:(单片机,stm32,嵌入式硬件)