你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【我爱DIY】基于STM32F446的交流真有效值电压表

[复制链接]
wenyangzeng 发布时间:2017-8-6 16:35
本帖最后由 wenyangzeng 于 2017-8-6 21:13 编辑

基于STM32F446的交流真有效值电压表

    一般的数字万用表的交流电压的测量是采用将交流电压经过整流后测量其有效值,由于整流二极管的非线性,使得该测量结果误差较大,不是真有效值,特别是对非正弦波的测量更是如此。
   本方案基于使用意法半导体的ARM CORTEX内核的STM32F446交流真有效值电压表,克服上述方案的缺点,使得对交流电的测量更加准确。
    交流电的有效值定义为:
无标题1.png


    对其一个周期内连续采样后,根据以下公式进行计算得到结果。
无标题3.png
硬件连接:
无标题2.png


220V交流电经过1:1电压互感器后与STM32F446的ADC0连接。


MX1.png
                             ADC通道配置
MX2.png
                        时钟配置
MX3.png
          ADC参数配置

关键代码:
  1. //MAIN.C

  2. #include "main.h"
  3. #include "stm32f4xx_hal.h"
  4. #include "adc.h"
  5. #include "dma.h"
  6. #include "tim.h"
  7. #include "gpio.h"
  8. #include "math.h"

  9. #include "oled.h"
  10. uint16_t   aADCxConvertedData[48][10];
  11. uint32_t Resture=0;
  12. uint8_t Disp_buf[8],Flag;
  13. uint8_t j=0;
  14. void SystemClock_Config(void);

  15. void Get_ADC(void)
  16. {uint8_t i;
  17.         static uint32_t total;
  18.         double temp;
  19.         

  20.         total=0;
  21.         for(i=0;i<48;i++)
  22.                 {
  23.                 total +=(aADCxConvertedData[i][0]*aADCxConvertedData[i][0]);
  24.                 }
  25.                 total /=48;
  26.                 temp=sqrt(total)        ;
  27.                 Resture +=(uint16_t)temp;
  28.                 j++;
  29.                 if(j==32)
  30.                 {
  31.                 Resture=Resture/64;
  32.                 Disp_buf[0]=(Resture%1000/100)+0x30;        
  33.                 Disp_buf[1]=(Resture%100/10)+0x30;        
  34.                 Disp_buf[2]=(Resture%10)+0x30;        
  35.                 Disp_buf[3]='\0';
  36.                 LCD_Print(40,46,Disp_buf,TYPE16X16,TYPE8X16);        
  37.                 Resture=0;        
  38.                 j=0;
  39.                 }
  40. }




  41. int main(void)
  42. {

  43.   HAL_Init();
  44.   SystemClock_Config();
  45.   MX_GPIO_Init();
  46.   MX_DMA_Init();
  47.   MX_ADC1_Init();
  48.   MX_TIM8_Init();
  49.         HAL_TIM_PWM_Start(&htim8, TIM_CHANNEL_1);

  50.                 LCD_Init();
  51.                 LCD_Print(0, 0,"STM32F446 Nucleo",TYPE16X16,TYPE8X16);
  52.                 LCD_Print(0, 16, "True effective",TYPE16X16,TYPE8X16);
  53.                 LCD_Print(74,46, "V",TYPE16X16,TYPE8X16);
  54.                
  55.         if(HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&aADCxConvertedData,480) != HAL_OK)
  56.   {
  57.     Error_Handler();
  58.   }        

  59.   while (1)
  60.   {
  61.         if(Flag==1)
  62.         {        
  63.                 Get_ADC();
  64.                 Flag=0;
  65.         }

  66.   }

  67. }


  68. //ADC.C
  69. #include "adc.h"
  70. #include "gpio.h"
  71. #include "dma.h"
  72. ADC_HandleTypeDef hadc1;
  73. DMA_HandleTypeDef hdma_adc1;
  74. void MX_ADC1_Init(void)
  75. {
  76.   ADC_ChannelConfTypeDef sConfig;
  77.   hadc1.Instance = ADC1;
  78.   hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV8;
  79.   hadc1.Init.Resolution = ADC_RESOLUTION_12B;
  80.   hadc1.Init.ScanConvMode = ENABLE;
  81.   hadc1.Init.ContinuousConvMode = ENABLE;
  82.   hadc1.Init.DiscontinuousConvMode = DISABLE;
  83.   hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  84.   hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  85.   hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  86.   hadc1.Init.NbrOfConversion = 10;
  87.   hadc1.Init.DMAContinuousRequests = ENABLE;
  88.   hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
  89.   if (HAL_ADC_Init(&hadc1) != HAL_OK)
  90.   {
  91.     _Error_Handler(__FILE__, __LINE__);
  92.   }
  93.   sConfig.Channel = ADC_CHANNEL_0;
  94.   sConfig.Rank = 1;
  95.   sConfig.SamplingTime = ADC_SAMPLETIME_480CYCLES;
  96.   if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  97.   {
  98.     _Error_Handler(__FILE__, __LINE__);
  99.   }

  100.   sConfig.Channel = ADC_CHANNEL_1;
  101.   sConfig.Rank = 2;
  102.   if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  103.   {
  104.     _Error_Handler(__FILE__, __LINE__);
  105.   }

  106.   sConfig.Channel = ADC_CHANNEL_2;
  107.   sConfig.Rank = 3;
  108.   if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  109.   {
  110.     _Error_Handler(__FILE__, __LINE__);
  111.   }

  112.   sConfig.Channel = ADC_CHANNEL_3;
  113.   sConfig.Rank = 4;
  114.   if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  115.   {
  116.     _Error_Handler(__FILE__, __LINE__);
  117.   }

  118.   sConfig.Channel = ADC_CHANNEL_4;
  119.   sConfig.Rank = 5;
  120.   if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  121.   {
  122.     _Error_Handler(__FILE__, __LINE__);
  123.   }

  124.   sConfig.Channel = ADC_CHANNEL_6;
  125.   sConfig.Rank = 6;
  126.   if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  127.   {
  128.     _Error_Handler(__FILE__, __LINE__);
  129.   }

  130.   sConfig.Channel = ADC_CHANNEL_7;
  131.   sConfig.Rank = 7;
  132.   if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  133.   {
  134.     _Error_Handler(__FILE__, __LINE__);
  135.   }

  136.   sConfig.Channel = ADC_CHANNEL_8;
  137.   sConfig.Rank = 8;
  138.   if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  139.   {
  140.     _Error_Handler(__FILE__, __LINE__);
  141.   }

  142.   sConfig.Channel = ADC_CHANNEL_9;
  143.   sConfig.Rank = 9;
  144.   if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  145.   {
  146.     _Error_Handler(__FILE__, __LINE__);
  147.   }

  148.   sConfig.Channel = ADC_CHANNEL_10;
  149.   sConfig.Rank = 10;
  150.   if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  151.   {
  152.     _Error_Handler(__FILE__, __LINE__);
  153.   }

  154. }

  155. void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle)
  156. {

  157.   GPIO_InitTypeDef GPIO_InitStruct;
  158.   if(adcHandle->Instance==ADC1)
  159.   {
  160.     __HAL_RCC_ADC1_CLK_ENABLE();

  161.     GPIO_InitStruct.Pin = GPIO_PIN_0;
  162.     GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
  163.     GPIO_InitStruct.Pull = GPIO_NOPULL;
  164.     HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
  165.     GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3
  166.                           |GPIO_PIN_4|GPIO_PIN_6|GPIO_PIN_7;
  167.     GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
  168.     GPIO_InitStruct.Pull = GPIO_NOPULL;
  169.     HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  170.     GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1;
  171.     GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
  172.     GPIO_InitStruct.Pull = GPIO_NOPULL;
  173.     HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
  174.     hdma_adc1.Instance = DMA2_Stream0;
  175.     hdma_adc1.Init.Channel = DMA_CHANNEL_0;
  176.     hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
  177.     hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
  178.     hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
  179.     hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
  180.     hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
  181.     hdma_adc1.Init.Mode = DMA_CIRCULAR;
  182.     hdma_adc1.Init.Priority = DMA_PRIORITY_LOW;
  183.     hdma_adc1.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
  184.     if (HAL_DMA_Init(&hdma_adc1) != HAL_OK)
  185.     {
  186.       _Error_Handler(__FILE__, __LINE__);
  187.     }

  188.     __HAL_LINKDMA(adcHandle,DMA_Handle,hdma_adc1);

  189.   }
  190. }






复制代码
http://player.youku.com/player.php/sid/XMjk0ODczODg5Ng==/v.swf

由于STM32F446的快速处理信号能力,本方案中同时开启10路带DMA的ADC处理10路50HZ交流电压绰绰有余。演示中在OLED屏幕上显示ADC0通道的转换结果。


收藏 3 评论24 发布时间:2017-8-6 16:35

举报

24个回答
Draway 回答时间:2017-10-19 08:43:25
wenyangzeng 发表于 2017-9-26 07:24
互感器次级把初级的负电压“垫”高到VSS了。

楼主,你没有用示波器实际量,采样电阻出来的是个交流正弦波,有正负。所以你直接采样ADC等于把负的忽略l了,这样采样玩玩还可以,会对单片机的ADC有影响的,实际工程项目不能这样做的。
Draway 回答时间:2017-9-27 11:11:40
wenyangzeng 发表于 2017-9-27 09:52
那个电阻应该在300K-470K以上,否则互感器会磁饱和而烧毁。

输入级的电阻我知道,我选用300K 的,我是说输出级的、后面我查阅相关资料,你选用的互感器没有标明线性取样电阻,我选用的标明了。
Draway 回答时间:2017-9-27 08:15:08
wenyangzeng 发表于 2017-9-26 21:02
精密整流后再积分,只能算是“有效值”,而非“真有效值”

相当于取了绝对值,求RMS无影响。。。。。。。。还有疑问你这个互感器DATASHEET上就是写采样电阻那么大?就我所知道的,都是采样都是100R以下的
zero99 回答时间:2017-8-7 11:15:11
谢谢支持啦~
csgtli5608 回答时间:2017-8-8 14:10:52
楼主你那是什么软件,配置MCU的那个
wenyangzeng 回答时间:2017-8-8 15:57:39
本帖最后由 wenyangzeng 于 2017-8-8 15:59 编辑
csgtli5608 发表于 2017-8-8 14:10
楼主你那是什么软件,配置MCU的那个

STM32CubeMX,到ST官方网站下载。
https://www.stmcu.org.cn/document/detail/index/id-218002


菜鸟&新手 回答时间:2017-8-11 08:05:27
顶一个
ToddYam 回答时间:2017-8-12 09:52:02
epochal 回答时间:2017-8-26 08:05:58
谢谢分享!
猎国倾城 回答时间:2017-9-2 16:39:44
ufo2012 回答时间:2017-9-25 10:51:08
Draway 回答时间:2017-9-25 18:01:59
最后ADC测得应该是半波,即在正弦波X轴上半部分?
wenyangzeng 回答时间:2017-9-25 20:47:10
本帖最后由 wenyangzeng 于 2017-9-25 20:56 编辑
Draway 发表于 2017-9-25 18:01
最后ADC测得应该是半波,即在正弦波X轴上半部分?

经过电压互感器后,次级是完整的正弦波。采样48次耗时20mS,就能够覆盖整个交流电的一个周期。
Draway 回答时间:2017-9-25 22:19:28
wenyangzeng 发表于 2017-9-25 20:47
经过电压互感器后,次级是完整的正弦波。采样48次耗时20mS,就能够覆盖整个交流电的一个周期。 ...

那互感器出来没有负电压么?我看的采样后直接接ADC了
wenyangzeng 回答时间:2017-9-26 07:24:32
Draway 发表于 2017-9-25 22:19
那互感器出来没有负电压么?我看的采样后直接接ADC了

互感器次级把初级的负电压“垫”高到VSS了。
斜阳 回答时间:2017-9-26 09:10:02
给楼主点赞。但是实用意义不大。
Draway 回答时间:2017-9-26 10:49:01
wenyangzeng 发表于 2017-9-26 07:24
互感器次级把初级的负电压“垫”高到VSS了。

噢,你用的是什么型号的互感器?
12下一页
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版