上篇文章写了硬件部分的实现思路,通过采样电阻的到小电压后经过二级放大电路得到单片机可处理的交流电压,此文介绍了如何采用单片机采集交流电压以及stm32ADC外设的使用。首先是硬件电路部分。

电路没有采用核心板,而是直接将芯片焊接到主板上,采用type-c接口供电,调参采用五轴按键,参数及测量结果显示采用0.96寸OLED显示,采用有源蜂鸣器作为报警电路。PCB如图所示

交流电压经放大后到达ADC口,此时即可进行ADC采样。

ADC采样采用DMA的方式,初始化主要有两个方面:

一:GPIO的初始化

static void ADCx_GPIO_Config(void)

{

GPIO_InitTypeDef GPIO_InitStructure;

//打开 ADC IO 端口时钟

ADC_GPIO_APBxClock_FUN(ADC_GPIO_CLK,ENABLE);

//配置ADC IO 引脚模式

//必须为模拟输入

GPIO_InitStructure.GPIO_Pin = ADC_PIN;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;

//初始化ADC IO

GPIO_Init(ADC_PORT,&GPIO_InitStructure);

}

二:DMA的初始化

static void ADCx_Mode_Config(void)

{

DMA_InitTypeDef DMA_InitStructure;

//打开DMA时钟

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);

//复位DMA

DMA_DeInit(ADC_DMA_CHANNEL);

DMA_InitStructure.DMA_PeripheralBaseAddr = ( uint32_t ) ( & (ADC_x->DR ) );

DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADC_ConvertedValue;

DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;

DMA_InitStructure.DMA_BufferSize = 1;

DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;

DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;

DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;

DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//Normal是一直传输

DMA_InitStructure.DMA_Priority = DMA_Priority_High;

DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;

DMA_Init(ADC_DMA_CHANNEL,&DMA_InitStructure);

DMA_Cmd(ADC_DMA_CHANNEL,ENABLE);

ADC_InitTypeDef ADC_InitStruct;

ADC_APBxClock_FUN ( ADC_CLK, ENABLE );

ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;

ADC_InitStruct.ADC_ScanConvMode = DISABLE;

ADC_InitStruct.ADC_ContinuousConvMode = ENABLE; //一直转换

ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //软件触发

ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right; //右对齐

ADC_InitStruct.ADC_NbrOfChannel = 1; //转换通道为一个

ADC_Init(ADC_x, &ADC_InitStruct);

RCC_ADCCLKConfig(RCC_PCLK2_Div8);

ADC_RegularChannelConfig(ADC_x, ADC_CHANNEL,1, ADC_SampleTime_55Cycles5);

//使能ADC DMA请求

ADC_DMACmd(ADC_x,ENABLE);

ADC_Cmd(ADC_x,ENABLE);

//校准ADC

ADC_StartCalibration(ADC_x);

//等待校准完成

while(ADC_GetCalibrationStatus(ADC_x));

//采用软件触发

ADC_SoftwareStartConvCmd(ADC_x,ENABLE);

}

此时采用DMA传输,ADC采样使用软件触发。在这里我的理解是只要ADC一个周期转换完成即开始下一次转换。DMA时刻在更新变量的值。

u16 ch_rms_value(void)

{

u32 sum = 0;

u32 value[200] = {0};

u16 rms = 0;

u16 i = 0;

float squ = 0;

for(i = 0; i < 200; i++) //20ms 采样200个点

{

value[i] = ADC_ConvertedValue;

delay_us(100);

}

for(i = 0; i < 200; i++)

{

squ= __fabs(value[i] - 2085.236);

sum += squ*squ;

}

rms = mySqrt(sum / 200); //求均方根值

return rms;

}

计算交流电压主要是均方根算法,因交流电压为市电50Hz,故我们选择在20ms的周期内采样200个点。采用的算法是简单的delay 100us然后采样二百个。在精度要求不高的情况下是可以接受的,若精度要求较高可以自行写在中断中自动读取。具体的工程可在主页中获取