上一篇讲的是简单版的采样。这一篇讲要求比较高的采样。比较精准。
1、定时器初始化
void TimerAdc0Init(void)
{
timer_oc_parameter_struct timer_ocintpara;
timer_parameter_struct timer_initpara;
rcu_periph_clock_enable(RCU_TIMER1);
/*************************TIMER1_CH0配置为互补输出******************************/
/* TIMER1 configuration */
timer_deinit(TIMER1);
timer_initpara.prescaler = 0; //预分频系数 不分频
timer_initpara.alignedmode = TIMER_COUNTER_EDGE; //对齐方式 边沿对齐
timer_initpara.counterdirection = TIMER_COUNTER_UP; //计数方向 向上计数
timer_initpara.period = ADC0_TRIGGER_PWM_PTPER; //PWM周期 20K
timer_initpara.clockdivision = TIMER_CKDIV_DIV1; //系统时钟分频 不分频
timer_initpara.repetitioncounter = 0; //重复计数值 不重复计数
timer_init(TIMER1,&timer_initpara);
/* CH0--trigger ADC config*/
timer_ocintpara.ocpolarity = TIMER_OC_POLARITY_LOW; //通道输出极性低有效
timer_ocintpara.outputstate = TIMER_CCX_ENABLE; //CH通道使能
timer_ocintpara.ocidlestate = TIMER_OC_IDLE_STATE_HIGH; //空闲输出低,只有上升沿才能触发ADC采样,
//若空闲输出高只能在周期开始触发ADC采样
timer_channel_output_config(TIMER1,TIMER_CH_0,&timer_ocintpara);
/* PWMHL---CH0--trigger ADC*///采样点在充电主开关管占空比一半
timer_channel_output_pulse_value_config(TIMER1,TIMER_CH_0,ADC0_DEFAULT_TRIGGER_TIME); //定时器通道脉冲值设置
timer_channel_output_mode_config(TIMER1,TIMER_CH_0,TIMER_OC_MODE_PWM0); //定时器输出模式
timer_channel_output_shadow_config(TIMER1,TIMER_CH_0,TIMER_OC_SHADOW_ENABLE); //定时器影子寄存器设置
;
/* TIMER1 primary output function enable */
timer_primary_output_config(TIMER1,ENABLE);//定时器输出使能
/* auto-reload preload enable */
timer_auto_reload_shadow_enable(TIMER1);//定时器自动重装载使能
timer_counter_value_config(TIMER1,0);//定时器初始计数值
/* TIMER1 counter enable */
timer_enable(TIMER1);
}
2、ADC初始化
void Adc0Init(void)
{
dma_parameter_struct dma_data_parameter;
rcu_periph_clock_enable(RCU_ADC0);
rcu_periph_clock_enable(RCU_DMA0);
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_GPIOB);
//采样通道GPIO引脚配置
gpio_init(GPIOA, GPIO_MODE_AIN, GPIO_OSPEED_50MHZ, GPIO_PIN_4); //VLA1
gpio_init(GPIOA, GPIO_MODE_AIN, GPIO_OSPEED_50MHZ, GPIO_PIN_5); //ILB1
gpio_init(GPIOA, GPIO_MODE_AIN, GPIO_OSPEED_50MHZ, GPIO_PIN_6); //VLB1
gpio_init(GPIOA, GPIO_MODE_AIN, GPIO_OSPEED_50MHZ, GPIO_PIN_7); //ILA1
gpio_init(GPIOB, GPIO_MODE_AIN, GPIO_OSPEED_50MHZ, GPIO_PIN_0); //VoutA1
gpio_init(GPIOB, GPIO_MODE_AIN, GPIO_OSPEED_50MHZ, GPIO_PIN_1); //VoutB1
adc_deinit(ADC0); // reset ADC
/* config ADC clock */
rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV4); // 120M/4 = 30MHZ
/*inserted channel NVIC configure */
NVIC_SetPriority(ADC0_1_IRQn,0);
NVIC_EnableIRQ(ADC0_1_IRQn);
/* ADC DMA_channel configuration */
dma_deinit(DMA0,DMA_CH0);
/* initialize DMA single data mode */
dma_data_parameter.periph_addr = (uint32_t)(&ADC_RDATA(ADC0)); // DMA传输数据源地址
dma_data_parameter.periph_inc = DMA_PERIPH_INCREASE_DISABLE; // DMA传输地址不增加
dma_data_parameter.memory_addr = (uint32_t)cAdc0Sample; // DMA传输目的地
dma_data_parameter.memory_inc = DMA_MEMORY_INCREASE_ENABLE; // DMA传输内存地址增加
dma_data_parameter.periph_width = DMA_PERIPHERAL_WIDTH_16BIT; // 外设数据宽度为16bit
dma_data_parameter.memory_width = DMA_MEMORY_WIDTH_16BIT; // 内存数据宽度为16bit
dma_data_parameter.direction = DMA_PERIPHERAL_TO_MEMORY; // 传输方向:外设到内存
dma_data_parameter.number = 2; // 传输次数
dma_data_parameter.priority = DMA_PRIORITY_HIGH; // 优先级
dma_init(DMA0,DMA_CH0,&dma_data_parameter);
/* configure DMA mode */
dma_circulation_enable(DMA0,DMA_CH0);
dma_memory_to_memory_disable(DMA0,DMA_CH0);
adc_special_function_config(ADC0,ADC_SCAN_MODE |ADC_CONTINUOUS_MODE, ENABLE); // adc连续功能使能
adc_external_trigger_source_config(ADC0,ADC_REGULAR_CHANNEL, ADC0_1_2_EXTTRIG_REGULAR_NONE); //软件触发
adc_external_trigger_source_config(ADC0,ADC_INSERTED_CHANNEL, ADC0_1_EXTTRIG_INSERTED_T1_CH0); //T1_CH0触发
adc_data_alignment_config(ADC0,ADC_DATAALIGN_RIGHT); //数据对齐方式
adc_channel_length_config(ADC0,ADC_REGULAR_CHANNEL, 2); //规则通道长度
adc_channel_length_config(ADC0,ADC_INSERTED_CHANNEL, 4);//注入通道长度
adc_regular_channel_config(ADC0,0, CH0_ADC_OUT_A_VOLT,ADC_SAMPLETIME_71POINT5);//规则通道设置
adc_regular_channel_config(ADC0,1, CH0_ADC_OUT_B_VOLT,ADC_SAMPLETIME_71POINT5);
adc_inserted_channel_config(ADC0,0,CH0_ADC_IN_A_VOLT , ADC_SAMPLETIME_7POINT5);//注入通道设置
adc_inserted_channel_config(ADC0,1,CH0_ADC_IN_B_VOLT , ADC_SAMPLETIME_7POINT5);
adc_inserted_channel_config(ADC0,2,CH0_ADC_IN_A_CURRENT , ADC_SAMPLETIME_7POINT5);
adc_inserted_channel_config(ADC0,3,CH0_ADC_IN_B_CURRENT, ADC_SAMPLETIME_7POINT5);
//adc_oversample_mode_config(ADC_OVERSAMPLING_ALL_CONVERT,ADC_OVERSAMPLING_SHIFT_3B,ADC_OVERSAMPLING_RATIO_MUL8);
//adc_oversample_mode_enable();
adc_external_trigger_config(ADC0,ADC_REGULAR_CHANNEL, ENABLE); // 外部触发使能
adc_external_trigger_config(ADC0,ADC_INSERTED_CHANNEL, ENABLE);
adc_dma_mode_enable(ADC0);//DMA模式使能
adc_interrupt_enable(ADC0,ADC_INT_EOIC);//ADC中断使能
adc_enable(ADC0);//ADC使能
Afx_Delay10us(1000);
adc_calibration_enable(ADC0);//ADC复位
Afx_Delay10us(1000);
dma_channel_enable(DMA0,DMA_CH0);//DMA通道使能
adc_software_trigger_enable(ADC0,ADC_REGULAR_CHANNEL);//使能ADC软件触发
}
经过上面两个部分,采样的底层已经配置的差不多了。接下来就是获取采样值和计算采样值。
3、ADC基准系数
我这边硬件的基准为1.5v,所以,采样基准系数为1.5/3.3*4096=1862,所以采样基准为1862
AdcSampleRef.i16LineAInVolAd = 1862;
4、ADC采样系数
ADC采样值的计算为3.3/4096/硬件的采样系数
AdcSampleKscal.i16LineAInVolAd = 0.118654*4096;
5、获取ADC采样值
ADC通道采样值-ADC基准系数
AdcSampleData.i16LineAInVolAd = ADC_IDATA0(ADC0) - AdcSampleRef.i16LineAInVolAd;//注入组0
AdcSampleData.i16LineAOutVolAd = cAdc0Sample[0] - AdcSampleRef.i16LineAOutVolAd;//规则组0
6、获取ADC真实值
ADC采样值*ADC采样系数 = ADC真实值
采样系数*4096,真实值得/4096(>>12)
i32AdcRealDataCal = AdcSampleData.i16LineAInVolAd * AdcSampleKscal.i16LineAInVolAd;
AdcSampleRealData.i16LineAInVolAd = (i32AdcRealDataCal>>12);
7、计算ADC真实值平均值
void sGetSampleDataSum(void)
{
AdcDataSum.i32LineAInVol += AdcSampleRealData.i16LineAInVolAd * AdcSampleRealData.i16LineAInVolAd;
}
int16_t sAvergerSqrtCal(int32_t i32RealDataSum, int32_t i32SumCounter)
{
int16_t i16RealDataSqrt = 0;
i16RealDataSqrt = (int16_t)(sqrt(i32RealDataSum / i32SumCounter));
return i16RealDataSqrt;
}
void sGetEffectiveDataCal(void)//此任务最好放在过零点捕获中断中
{
AdcEffectiveData.i16LineAInVolAd = sAvergerSqrtCal(AdcDataSum.i32LineAInVol,AdcDataSum.i32AdSumTimes);
}
这样就可以计算出ADC采样电压了。
8、最后补一个ADC中断
void ADC0_1_IRQHandler(void)
{
//PWM每周期触发一次ADC采样,触发周期是PWM的两倍
//故1个中断进行一次PI运算
if(adc_interrupt_flag_get(ADC0,ADC_INT_FLAG_EOIC))
{
adc_interrupt_flag_clear(ADC0,ADC_INT_FLAG_EOIC); //清除中断标志位
sGetAdc0ScanSampleData(); //ADC0规则、注入组采样
sGetAdc0SampleRealData(); //基准采样和计算
sGetSampleDataSum(); //ADC真实值累加
}
}