本帖最后由 wenyangzeng 于 2017-8-6 21:13 编辑
基于STM32F446的交流真有效值电压表
一般的数字万用表的交流电压的测量是采用将交流电压经过整流后测量其有效值,由于整流二极管的非线性,使得该测量结果误差较大,不是真有效值,特别是对非正弦波的测量更是如此。
本方案基于使用意法半导体的ARM CORTEX内核的STM32F446交流真有效值电压表,克服上述方案的缺点,使得对交流电的测量更加准确。
交流电的有效值定义为:
对其一个周期内连续采样后,根据以下公式进行计算得到结果。
硬件连接:
220V交流电经过1:1电压互感器后与STM32F446的ADC0连接。
ADC通道配置
时钟配置
ADC参数配置
关键代码:
- //MAIN.C
- #include "main.h"
- #include "stm32f4xx_hal.h"
- #include "adc.h"
- #include "dma.h"
- #include "tim.h"
- #include "gpio.h"
- #include "math.h"
- #include "oled.h"
- uint16_t aADCxConvertedData[48][10];
- uint32_t Resture=0;
- uint8_t Disp_buf[8],Flag;
- uint8_t j=0;
- void SystemClock_Config(void);
- void Get_ADC(void)
- {uint8_t i;
- static uint32_t total;
- double temp;
-
- total=0;
- for(i=0;i<48;i++)
- {
- total +=(aADCxConvertedData[i][0]*aADCxConvertedData[i][0]);
- }
- total /=48;
- temp=sqrt(total) ;
- Resture +=(uint16_t)temp;
- j++;
- if(j==32)
- {
- Resture=Resture/64;
- Disp_buf[0]=(Resture%1000/100)+0x30;
- Disp_buf[1]=(Resture%100/10)+0x30;
- Disp_buf[2]=(Resture%10)+0x30;
- Disp_buf[3]='\0';
- LCD_Print(40,46,Disp_buf,TYPE16X16,TYPE8X16);
- Resture=0;
- j=0;
- }
- }
- int main(void)
- {
- HAL_Init();
- SystemClock_Config();
- MX_GPIO_Init();
- MX_DMA_Init();
- MX_ADC1_Init();
- MX_TIM8_Init();
- HAL_TIM_PWM_Start(&htim8, TIM_CHANNEL_1);
- LCD_Init();
- LCD_Print(0, 0,"STM32F446 Nucleo",TYPE16X16,TYPE8X16);
- LCD_Print(0, 16, "True effective",TYPE16X16,TYPE8X16);
- LCD_Print(74,46, "V",TYPE16X16,TYPE8X16);
-
- if(HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&aADCxConvertedData,480) != HAL_OK)
- {
- Error_Handler();
- }
- while (1)
- {
- if(Flag==1)
- {
- Get_ADC();
- Flag=0;
- }
- }
- }
- //ADC.C
- #include "adc.h"
- #include "gpio.h"
- #include "dma.h"
- ADC_HandleTypeDef hadc1;
- DMA_HandleTypeDef hdma_adc1;
- void MX_ADC1_Init(void)
- {
- ADC_ChannelConfTypeDef sConfig;
- hadc1.Instance = ADC1;
- hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV8;
- hadc1.Init.Resolution = ADC_RESOLUTION_12B;
- hadc1.Init.ScanConvMode = ENABLE;
- hadc1.Init.ContinuousConvMode = ENABLE;
- hadc1.Init.DiscontinuousConvMode = DISABLE;
- hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
- hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
- hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
- hadc1.Init.NbrOfConversion = 10;
- hadc1.Init.DMAContinuousRequests = ENABLE;
- hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
- if (HAL_ADC_Init(&hadc1) != HAL_OK)
- {
- _Error_Handler(__FILE__, __LINE__);
- }
- sConfig.Channel = ADC_CHANNEL_0;
- sConfig.Rank = 1;
- sConfig.SamplingTime = ADC_SAMPLETIME_480CYCLES;
- if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
- {
- _Error_Handler(__FILE__, __LINE__);
- }
- sConfig.Channel = ADC_CHANNEL_1;
- sConfig.Rank = 2;
- if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
- {
- _Error_Handler(__FILE__, __LINE__);
- }
- sConfig.Channel = ADC_CHANNEL_2;
- sConfig.Rank = 3;
- if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
- {
- _Error_Handler(__FILE__, __LINE__);
- }
- sConfig.Channel = ADC_CHANNEL_3;
- sConfig.Rank = 4;
- if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
- {
- _Error_Handler(__FILE__, __LINE__);
- }
- sConfig.Channel = ADC_CHANNEL_4;
- sConfig.Rank = 5;
- if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
- {
- _Error_Handler(__FILE__, __LINE__);
- }
- sConfig.Channel = ADC_CHANNEL_6;
- sConfig.Rank = 6;
- if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
- {
- _Error_Handler(__FILE__, __LINE__);
- }
- sConfig.Channel = ADC_CHANNEL_7;
- sConfig.Rank = 7;
- if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
- {
- _Error_Handler(__FILE__, __LINE__);
- }
- sConfig.Channel = ADC_CHANNEL_8;
- sConfig.Rank = 8;
- if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
- {
- _Error_Handler(__FILE__, __LINE__);
- }
- sConfig.Channel = ADC_CHANNEL_9;
- sConfig.Rank = 9;
- if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
- {
- _Error_Handler(__FILE__, __LINE__);
- }
- sConfig.Channel = ADC_CHANNEL_10;
- sConfig.Rank = 10;
- if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
- {
- _Error_Handler(__FILE__, __LINE__);
- }
- }
- void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle)
- {
- GPIO_InitTypeDef GPIO_InitStruct;
- if(adcHandle->Instance==ADC1)
- {
- __HAL_RCC_ADC1_CLK_ENABLE();
- GPIO_InitStruct.Pin = GPIO_PIN_0;
- GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
- GPIO_InitStruct.Pull = GPIO_NOPULL;
- HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
- GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3
- |GPIO_PIN_4|GPIO_PIN_6|GPIO_PIN_7;
- GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
- GPIO_InitStruct.Pull = GPIO_NOPULL;
- HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
- GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1;
- GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
- GPIO_InitStruct.Pull = GPIO_NOPULL;
- HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
- hdma_adc1.Instance = DMA2_Stream0;
- hdma_adc1.Init.Channel = DMA_CHANNEL_0;
- hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
- hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
- hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
- hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
- hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
- hdma_adc1.Init.Mode = DMA_CIRCULAR;
- hdma_adc1.Init.Priority = DMA_PRIORITY_LOW;
- hdma_adc1.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
- if (HAL_DMA_Init(&hdma_adc1) != HAL_OK)
- {
- _Error_Handler(__FILE__, __LINE__);
- }
- __HAL_LINKDMA(adcHandle,DMA_Handle,hdma_adc1);
- }
- }
复制代码 http://player.youku.com/player.php/sid/XMjk0ODczODg5Ng==/v.swf
由于STM32F446的快速处理信号能力,本方案中同时开启10路带DMA的ADC处理10路50HZ交流电压绰绰有余。演示中在OLED屏幕上显示ADC0通道的转换结果。
|
楼主,你没有用示波器实际量,采样电阻出来的是个交流正弦波,有正负。所以你直接采样ADC等于把负的忽略l了,这样采样玩玩还可以,会对单片机的ADC有影响的,实际工程项目不能这样做的。
输入级的电阻我知道,我选用300K 的,我是说输出级的、后面我查阅相关资料,你选用的互感器没有标明线性取样电阻,我选用的标明了。
相当于取了绝对值,求RMS无影响。。。。。。。。还有疑问你这个互感器DATASHEET上就是写采样电阻那么大?就我所知道的,都是采样都是100R以下的
STM32CubeMX,到ST官方网站下载。
https://www.stmcu.org.cn/document/detail/index/id-218002
经过电压互感器后,次级是完整的正弦波。采样48次耗时20mS,就能够覆盖整个交流电的一个周期。
那互感器出来没有负电压么?我看的采样后直接接ADC了
互感器次级把初级的负电压“垫”高到VSS了。
噢,你用的是什么型号的互感器?