前言
电源方向资料免费开源到QQ群280730348,欢迎进群交流沟通。博客地址edadong.com,博文同步发布在知乎、bilibili,其中bilibili主要以视频为主。建议去B站听详细视频解说。
交流电压检测需要通过单片机ADC分点采集交流电压周期内多点电压,求均方根后才能求得。STM32F4系列单片机内部有DSP库,所以直接调用DSP库内函数就可以得到交流信号有效值,今天就是对F4系列单片机的交流电压采样进行代码分享。
交流电压检测代码思路
交流电压存在周期,我们需要设定如下思路,才能够还原交流电压信号的有效值:
- 确认PWM驱动波频率,如20KHz驱动情况下,采集交流信号的频率必须是这个频率的1/整数倍。通常我们在定时器中断中完成这个功能。
- 确认采样点数,一个周期内采样点数越高,得到的有效值就更稳定可靠。比如一个周期内采集200个点做均方根,但这个过程会出现一个问题,采样点数过高,系统运行过于缓慢,会影响到波形的产生,拉宽波形频率,并且在最终均方根运算的时候占用过多的时间。
- 确认ADC采样路数,采样时间,计算单次采样所需要的时间,避免时间不够无法获得最新的电压数据。
- 确认基准电压大小,减去对应基准电压值。
- 确认交流电压缩放倍数,用于还原对应真实交流电压大小。
- 采集到足够多的数据后,执行均方根算法。
以上就是采集交流电压的代码思路,那我们开始介绍每一段代码
(1)确认PWM驱动频率和采样点数,开启采集交流信号的对应频率中断,此处我们采取同频定时器,中断内部计数的方式。由于我们采样点数确认为200,交流正弦波输出为50Hz,交流信号频率为20KHz,所以一个完整周期会执行400次中断。每进入2次中断执行一次交流单点电压采集。
Inverter_SPWM_Iint(20000,0);
void Inverter_SPWM_Iint(uint32_t pfreq, uint16_t psc) { GPIO_InitTypeDef GPIO_InitStruct; TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct; TIM_OCInitTypeDef TIM_OCInitStruct; TIM_BDTRInitTypeDef TIM_BDTRInitStruct; NVIC_InitTypeDef NVIC_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOA, ENABLE);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6; GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz; GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7; GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_PinAFConfig(GPIOC, GPIO_PinSource6, GPIO_AF_TIM8); GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_TIM8);
TimerPeriod = (SystemCoreClock / pfreq) - 1;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8, ENABLE);
TIM_DeInit(Inverter_TIMX);
TIM_TimeBaseInitStruct.TIM_ClockDivision = 0; TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInitStruct.TIM_Period = TimerPeriod; TIM_TimeBaseInitStruct.TIM_Prescaler = psc; TIM_TimeBaseInit(Inverter_TIMX, &TIM_TimeBaseInitStruct);
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStruct.TIM_OutputNState = TIM_OutputNState_Enable; TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OCInitStruct.TIM_OCNPolarity = TIM_OCNPolarity_High; TIM_OCInitStruct.TIM_OCIdleState = TIM_OCIdleState_Set; TIM_OCInitStruct.TIM_OCNIdleState = TIM_OCNIdleState_Reset; TIM_OCInitStruct.TIM_Pulse = TimerPeriod / 2 - 1; TIM_OC1Init(Inverter_TIMX, &TIM_OCInitStruct); TIM_OC1PreloadConfig(Inverter_TIMX, TIM_OCPreload_Enable);
TIM_BDTRInitStruct.TIM_OSSIState = TIM_OSSIState_Disable; TIM_BDTRInitStruct.TIM_OSSRState = TIM_OSSRState_Disable; TIM_BDTRInitStruct.TIM_LOCKLevel = TIM_LOCKLevel_1; TIM_BDTRInitStruct.TIM_DeadTime = 0; TIM_BDTRInitStruct.TIM_BreakPolarity = TIM_BreakPolarity_Low; TIM_BDTRInitStruct.TIM_Break = TIM_Break_Disable; TIM_BDTRInitStruct.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable; TIM_BDTRConfig(Inverter_TIMX, &TIM_BDTRInitStruct);
TIM_ITConfig(Inverter_TIMX, TIM_IT_Update, ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = TIM8_UP_TIM13_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure);
TIM_ARRPreloadConfig(Inverter_TIMX, ENABLE);
TIM_Cmd(Inverter_TIMX, ENABLE);
TIM_CtrlPWMOutputs(Inverter_TIMX, ENABLE); }
void TIM8_UP_TIM13_IRQHandler(void) { if(TIM_GetITStatus(Inverter_TIMX, TIM_IT_Update) == SET) { static u32 count=0; if(count++%2) { calculate_rms(); }
} TIM_ClearITPendingBit(Inverter_TIMX, TIM_IT_Update); }
|
(2)配置ADC和高速DMA采集代码,一个通道输出,同时配置采样精度(也就是配置采样时间)
adc.c
#include "adc.h" #include "stm32f4xx_adc.h" #include "stm32f4xx_dma.h"
#define BufferSize (6*10)
static u16 DMA_Buf[BufferSize]={0}; u16 *adc_result=DMA_Buf; static void DMA_Config(void); static void GPIO_Config(void); static void ADC1_CH3_CH4_CH14_Config(void); static void ADC2_CH5_CH6_CH15_Config(void);
void adc_init(void) { ADC_CommonInitTypeDef ADC_CommonInitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC2, ENABLE); DMA_Config(); GPIO_Config();
ADC_CommonInitStructure.ADC_Mode = ADC_DualMode_RegSimult; ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4; ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_1; ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles; ADC_CommonInit(&ADC_CommonInitStructure); ADC1_CH3_CH4_CH14_Config(); ADC2_CH5_CH6_CH15_Config(); ADC_MultiModeDMARequestAfterLastTransferCmd(ENABLE); ADC_Cmd(ADC1, ENABLE); ADC_Cmd(ADC2, ENABLE); ADC_SoftwareStartConv(ADC1); } static void DMA_Config(void) { DMA_InitTypeDef DMA_InitStructure; DMA_InitStructure.DMA_Channel = DMA_Channel_0; DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&DMA_Buf; DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(ADC1_BASE+0x300+0x8); DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; DMA_InitStructure.DMA_BufferSize = BufferSize; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; DMA_Init(DMA2_Stream0, &DMA_InitStructure); DMA_Cmd(DMA2_Stream0, ENABLE); }
static void GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin=GPIO_Pin_4 | GPIO_Pin_5; GPIO_Init(GPIOC, &GPIO_InitStructure); }
static void ADC1_CH3_CH4_CH14_Config(void) { ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; ADC_InitStructure.ADC_ScanConvMode = ENABLE; ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1; ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfConversion = 3; ADC_Init(ADC1, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 1, ADC_SampleTime_56Cycles); ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 2, ADC_SampleTime_56Cycles); ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 3, ADC_SampleTime_56Cycles); }
static void ADC2_CH5_CH6_CH15_Config(void) { ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; ADC_InitStructure.ADC_ScanConvMode = ENABLE; ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1; ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfConversion = 3; ADC_Init(ADC2, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC2, ADC_Channel_5, 1, ADC_SampleTime_56Cycles); ADC_RegularChannelConfig(ADC2, ADC_Channel_6, 2, ADC_SampleTime_56Cycles); ADC_RegularChannelConfig(ADC2, ADC_Channel_15, 3, ADC_SampleTime_56Cycles); }
|
adc.h
#ifndef ADC_H #define ADC_H
#include "stm32f4xx.h"
void adc_init(void); extern u16 *adc_result;
#endif
|
(3)定义基准电压和交流电压缩放倍数
float AC_Vout_vref=1.54650f; float AC_Vout_ratio=50.4557f;
float AC_Iout_vref=1.51600f; float AC_Iout_ratio=3.3333f;
float AC_Vin_verf=1.52400f; float AC_Vin_ratio=51.2035f;
float DC_Vin_verf=0.0000f; float DC_Vin_ratio=33.0655f;
float DC_Iin_verf=0.0000f; float DC_Iin_ratio=1.2280f;
|
(4)定义采集函数
measure.c
#include "measure.h" #include "adc.h" #include "main.h" #include "math.h" #include "arm_math.h"
#define COLS 6 #define ROWS 10 #define SAMPLE_LEN 200
rms_type AC_rms={0, 0, 0, 0, 0, 0}; float AC_sample_mat[6][SAMPLE_LEN]={0};
void calculate_rms(void) { static u32 count=0; u32 temp=count %SAMPLE_LEN; count++; AC_sample_mat[0][temp]= get_dcv_in(); AC_sample_mat[1][temp]= get_dci_in(); AC_sample_mat[2][temp]= get_acv_out(); AC_sample_mat[3][temp]= get_aci_out(); AC_sample_mat[4][temp]= get_acv_in(); AC_sample_mat[5][temp]= get_aci_in(); if(SAMPLE_LEN-1==temp) { count=0; arm_rms_f32(AC_sample_mat[0], SAMPLE_LEN, &AC_rms.VDC_rms); arm_rms_f32(AC_sample_mat[1], SAMPLE_LEN, &AC_rms.IDC_rms); arm_rms_f32(AC_sample_mat[2], SAMPLE_LEN, &AC_rms.Vout_rms); arm_rms_f32(AC_sample_mat[3], SAMPLE_LEN, &AC_rms.Iout_rms); arm_rms_f32(AC_sample_mat[4], SAMPLE_LEN, &AC_rms.Vin_rms); arm_rms_f32(AC_sample_mat[5], SAMPLE_LEN, &AC_rms.Iin_rms); } }
float get_dcv_in(void) { float temp=0; u32 sum=0; int i; for(i=0;i<ROWS;i++) { sum+=adc_result[i*COLS +0]; } temp =(double)sum/ROWS/4095*3.3; return (temp*DC_Vin_ratio); }
float get_dci_in(void) { float temp=0; u32 sum=0; int i; for(i=0;i<ROWS;i++) { sum+=adc_result[i*COLS +1]; } temp =(double)sum/ROWS/4095*3.3; return (temp*DC_Iin_ratio); }
float get_acv_out(void) { float temp=0; u32 sum=0; int i; for(i=0;i<ROWS;i++) { sum+=adc_result[i*COLS +2]; } temp =(double)sum/ROWS/4095*3.3; return (temp-AC_Vout_vref)*AC_Vout_ratio; }
float get_aci_out(void) { float temp=0; u32 sum=0; int i; for(i=0;i<ROWS;i++) { sum+=adc_result[i*COLS +3]; } temp =(double)sum/ROWS/4095*3.3; return (temp-AC_Iout_vref)*AC_Iout_ratio; }
float get_acv_in(void) { float temp=0; u32 sum=0; int i; for(i=0;i<ROWS;i++) { sum+=adc_result[i*COLS +4]; } temp =(double)sum/ROWS/4095*3.3; return (temp-AC_Vin_verf)*AC_Vin_ratio; }
float get_aci_in(void) { float temp=0; u32 sum=0; int i; for(i=0;i<ROWS;i++) { sum+=adc_result[i*COLS +5]; } temp =(double)sum/ROWS/4095*3.3; return (temp-AC_Vin_verf)*AC_Vin_ratio; }
float measure_rms(uint8_t i,uint8_t size) { float sum=0,result; int j;
for(j=0;j<i;j++) { sum+=(float)(AC_sample_mat[size][j]*AC_sample_mat[size][j]); } sum=(double)(sum/i); result=(float)(1.0*sqrt(sum)); return result; }
|
measure.h
#ifndef __MEASURE_H #define __MEASURE_H #include "stm32f4xx.h"
typedef struct _AC_RMS //定义有效值结构体 { float Vin_rms; float Vout_rms; float Iout_rms; float VDC_rms; float IDC_rms; float Iin_rms; }rms_type;
extern rms_type AC_rms; void calculate_rms(void); float get_acv_in(void); float get_dcv_in(void); float get_acv_out(void); float get_dci_in(void); float get_aci_out(void); float get_aci_in(void); float measure_rms(uint8_t i,uint8_t size);
#endif #ifndef __MEASURE_H #define __MEASURE_H #include "stm32f10x.h"
typedef struct _AC_RMS //定义有效值结构体 { float V1_rms; }rms_type;
extern rms_type AC_rms; void calculate_rms(void); u16 get_acv1_in(void); float measure_rms(uint8_t i);
#endif
|
总结
代码资料如下
post9资料合集
链接: https://pan.baidu.com/s/12rAj4YzHQUONdo3RPmqAUA
提取码: c2em –来自百度网盘超级会员v6的分享