[其他ST产品] 基于STM32F103C8T6ADC检测交流电压

[复制链接]
 楼主| 结合国际经验 发表于 2023-5-30 15:49 | 显示全部楼层 |阅读模式
上篇文章写了硬件部分的实现思路,通过采样电阻的到小电压后经过二级放大电路得到单片机可处理的交流电压,此文介绍了如何采用单片机采集交流电压以及stm32ADC外设的使用。首先是硬件电路部分。


QQ截图20230530154753.jpg


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

交流电压经放大后到达ADC口,此时即可进行ADC采样。
 楼主| 结合国际经验 发表于 2023-5-30 15:49 | 显示全部楼层
ADC采样采用DMA的方式,初始化主要有两个方面:

一:GPIO的初始化

  1. static void ADCx_GPIO_Config(void)
  2. {
  3.     GPIO_InitTypeDef GPIO_InitStructure;
  4.        
  5.           //打开 ADC IO 端口时钟
  6.     ADC_GPIO_APBxClock_FUN(ADC_GPIO_CLK,ENABLE);
  7.   
  8.     //配置ADC IO 引脚模式
  9.     //必须为模拟输入
  10.     GPIO_InitStructure.GPIO_Pin = ADC_PIN;
  11.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
  12.    
  13.     //初始化ADC IO
  14.     GPIO_Init(ADC_PORT,&GPIO_InitStructure);       
  15. }
复制代码
 楼主| 结合国际经验 发表于 2023-5-30 15:49 | 显示全部楼层
二:DMA的初始化
  1. static void ADCx_Mode_Config(void)
  2. {
  3.     DMA_InitTypeDef DMA_InitStructure;
  4.           //打开DMA时钟
  5.           RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);  
  6.           
  7.           //复位DMA
  8.           DMA_DeInit(ADC_DMA_CHANNEL);
  9.           DMA_InitStructure.DMA_PeripheralBaseAddr = ( uint32_t ) ( & (ADC_x->DR ) );
  10.           DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADC_ConvertedValue;
  11.           DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
  12.           DMA_InitStructure.DMA_BufferSize = 1;
  13.           DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  14.           DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;
  15.           DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
  16.           DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
  17.           DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//Normal是一直传输
  18.           DMA_InitStructure.DMA_Priority = DMA_Priority_High;
  19.           DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
  20.           DMA_Init(ADC_DMA_CHANNEL,&DMA_InitStructure);
  21.           DMA_Cmd(ADC_DMA_CHANNEL,ENABLE);
  22.        
  23.           ADC_InitTypeDef ADC_InitStruct;
  24.           
  25.           ADC_APBxClock_FUN ( ADC_CLK, ENABLE );
  26.           ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;
  27.           ADC_InitStruct.ADC_ScanConvMode = DISABLE;
  28.           ADC_InitStruct.ADC_ContinuousConvMode = ENABLE; //一直转换
  29.     ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;   //软件触发
  30.     ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;  //右对齐
  31.     ADC_InitStruct.ADC_NbrOfChannel = 1;    //转换通道为一个       
  32.           
  33.           ADC_Init(ADC_x, &ADC_InitStruct);
  34.           
  35.           RCC_ADCCLKConfig(RCC_PCLK2_Div8);
  36.           ADC_RegularChannelConfig(ADC_x, ADC_CHANNEL,1, ADC_SampleTime_55Cycles5);

  37.     //使能ADC DMA请求
  38.                 ADC_DMACmd(ADC_x,ENABLE);

  39.           ADC_Cmd(ADC_x,ENABLE);
  40.           //校准ADC
  41.           ADC_StartCalibration(ADC_x);
  42.           //等待校准完成
  43.           while(ADC_GetCalibrationStatus(ADC_x));

  44.           //采用软件触发
  45.                 ADC_SoftwareStartConvCmd(ADC_x,ENABLE);
  46. }
复制代码
 楼主| 结合国际经验 发表于 2023-5-30 15:50 | 显示全部楼层
此时采用DMA传输,ADC采样使用软件触发。在这里我的理解是只要ADC一个周期转换完成即开始下一次转换。DMA时刻在更新变量的值。
  1. u16  ch_rms_value(void)
  2. {
  3.     u32 sum = 0;
  4.     u32 value[200] = {0};
  5.     u16 rms = 0;
  6.     u16 i = 0;
  7.                 float squ = 0;
  8.     for(i = 0; i < 200; i++)                   //20ms  采样200个点
  9.     {
  10.         value[i] = ADC_ConvertedValue;
  11.         delay_us(100);
  12.     }
  13.                 for(i = 0; i < 200; i++)
  14.     {
  15.                                 squ= __fabs(value[i] - 2085.236);
  16.                                 sum += squ*squ;
  17.     }
  18.     rms = mySqrt(sum / 200);                        //求均方根值
  19.     return rms;
  20. }
复制代码
 楼主| 结合国际经验 发表于 2023-5-30 15:50 | 显示全部楼层
计算交流电压主要是均方根算法,因交流电压为市电50Hz,故我们选择在20ms的周期内采样200个点。采用的算法是简单的delay 100us然后采样二百个。在精度要求不高的情况下是可以接受的,若精度要求较高可以自行写在中断中自动读取。具体的工程可在主页中获取
sdlls 发表于 2023-6-7 21:21 | 显示全部楼层
可以测量负电压吗?              
maqianqu 发表于 2023-6-8 15:34 | 显示全部楼层
有效电压是你怎么测量的              
uptown 发表于 2023-6-8 16:49 | 显示全部楼层
如何检测交流电源的幅值?              
kissdb 发表于 2023-6-10 15:37 | 显示全部楼层
学习一下,多发点资料
AloneKaven 发表于 2023-6-10 23:42 | 显示全部楼层
精度怎么样啊?
Jacquetry 发表于 2023-6-11 23:06 | 显示全部楼层
sdlls 发表于 2023-6-7 21:21
可以测量负电压吗?

反相一下就可以
sesefadou 发表于 2023-6-13 23:01 | 显示全部楼层
测量的精度怎么样?
              
macpherson 发表于 2023-6-14 15:08 | 显示全部楼层
这个需要电容转换电压的吗?              
modesty3jonah 发表于 2023-6-15 22:06 | 显示全部楼层
比较常用的方法是使用变压器来将交流电压降低到单片机可以接受的范围内,然后使用模拟输入口读取电压信号,并通过程序进行处理和计算。
Bowclad 发表于 2023-6-15 22:43 | 显示全部楼层
maqianqu 发表于 2023-6-8 15:34
有效电压是你怎么测量的

每采样一个数据后,先对这个数据取平方,然后计算累加和,采样一定数量数据之后,对累加的平方和求平均值,然后再开方
Jacquetry 发表于 2023-6-18 23:30 | 显示全部楼层
测量精度怎么样啊
您需要登录后才可以回帖 登录 | 注册

本版积分规则

64

主题

773

帖子

1

粉丝
快速回复 在线客服 返回列表 返回顶部