最近在做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 时输出基本无变化。