冈板日川 发表于 2020-10-22 21:12:48

求助均方根计算公式,用于计算交流有效电压值

用单片机的ADC采样128个点,现在程序已经采样到128个点,通过串口输出至电脑看波形也正常,现在要如何计算出电压值?也就是均方根公式,要如何计算?用的STC15W4K32S4的单片机。

BOERLBH 发表于 2020-10-22 21:25:20

均方根要什么公式?直接算就是了。平方和在平均再开方。调用C库函数。 我以前用EFM32 计算过交流电流。

AWEN2000 发表于 2020-10-22 21:34:31

要求不高的话直接绝对值求和取平均乘个系数就是有效值。

冈板日川 发表于 2020-10-22 21:53:48

AWEN2000 发表于 2020-10-22 21:34
要求不高的话直接绝对值求和取平均乘个系数就是有效值。

这样不准的,还是均方根值比较准确。

冈板日川 发表于 2020-10-22 21:54:57

BOERLBH 发表于 2020-10-22 21:25
均方根要什么公式?直接算就是了。平方和在平均再开方。调用C库函数。 我以前用EFM32 计算过交流电流。 ...

我之前就在坛内看过该公式,找了好久没有找到。

Elex 发表于 2020-10-22 22:06:42

很久很久以前做的,电压中心值在VDD/2左右,用整形计算
void calcOutputVoltage(void)
{
        int8 i;
        int32 tempV,OutVoltageSum;
        if (!OutputVoltageCalcDone)
        {
                OutVoltageSum = 0;
                for(i = 0;i<OutVoltageSumCounter; i++)
                {
                        if (tempOutVoltage> adcValueZeroOutVoltage)
                        {
                                tempV = tempOutVoltage - adcValueZeroOutVoltage;
                        }
                        else
                        {
                                tempV = adcValueZeroOutVoltage - tempOutVoltage;
                        }
                        tempV = tempV * tempV;
                        OutVoltageSum +=tempV;
                }
                OutVoltageSum = (OutVoltageSum /MAX_ADC_SAMPLE_TIMES);
                OutVoltageSum = sqrt(OutVoltageSum);
                OutVoltage = (OutVoltageSum* 2075)>>8;
                OutVoltageSumCounter = 0;
                OutputVoltageCalcDone = 1;       
        }
}

BOERLBH 发表于 2020-10-22 22:15:59

冈板日川 发表于 2020-10-22 21:54
我之前就在坛内看过该公式,找了好久没有找到。

void adc_process(void)
{
    uint8_t cha = 0, cm = 0;

    int adv = 0;
    float iav = 0;

    // 计算电流
    for(cha = 0; cha < CHANNEL_SUM; cha++)
    {
      ADC_ZER = 0;
      ADC_ADV = 0;
      ADC_RMS = 0;
                       
      for(cm = 0; cm < 128; cm++)                                 // 采集128次 求均方根值
      {
            adv = ADC_ARR - adc_init_val;
            ADC_ADV += adv * adv;
      }
      ADC_ADV = ADC_ADV >> 7;
      ADC_RMS = isqrt32((uint32_t)ADC_ADV);
      iav = (float)ADC_RMS / 4095 * 2.5 / RK / IK ;   // 电流 单位 mA
      current = (int)iav;
    }
}

BOERLBH 发表于 2020-10-22 22:16:33

//开根号的函数
uint16_t isqrt32(uint32_t x)
{
    uint32_t m, y, b;
    m = 0x40000000;
    y = 0;
    while (m != 0)
    {
      b = y | m;
      y = y >> 1;
      if (x >= b)
      {
            x = x - b;
            y = y | m;
      }
      m >>= 2;
    }
    return y;
}

冈板日川 发表于 2020-10-22 22:40:48

BOERLBH 发表于 2020-10-22 22:16
//开根号的函数
uint16_t isqrt32(uint32_t x)
{


我用的是51啊。{:cry:}

冈板日川 发表于 2020-10-22 22:44:03

BOERLBH 发表于 2020-10-22 22:15
void adc_process(void)
{
    uint8_t cha = 0, cm = 0;


采集到的数据是10位的,不是8位的数据。要怎么处理?

AWEN2000 发表于 2020-10-22 22:46:12

本帖最后由 AWEN2000 于 2020-10-22 22:47 编辑

冈板日川 发表于 2020-10-22 21:53
这样不准的,还是均方根值比较准确。

不准?还可以吧。普通万用表就是测的也是平均值。
计算一般市电交流电压足够了,如果波形畸变厉害是不够准的。
也用不了128次采样的,32次都够了。
但是老大,你是51啊,算开根号?开玩笑吧

冈板日川 发表于 2020-10-22 22:51:51

AWEN2000 发表于 2020-10-22 22:46
不准?还可以吧。普通万用表就是测的也是平均值。
计算一般市电交流电压足够了,如果波形畸变厉害是不够 ...

就是因为波形有畸变,所以才需要用均方根计算。

wye11083 发表于 2020-10-22 23:09:19

AWEN2000 发表于 2020-10-22 22:46
不准?还可以吧。普通万用表就是测的也是平均值。
计算一般市电交流电压足够了,如果波形畸变厉害是不够 ...

没毛病啊。整数开平方根很容易的。先猜根,当最高位1后面有偶数个位时,丢一半,就是假根,如果是奇数,丢一半+1个(即留一半-1个),把最高位1后面添个0。然后用这个猜出来的假根做两次牛顿迭代,就可以得到精度+-1的整数二次方根。这个算法已经验证了的。

不仅仅是这个算法,以2为底的指数对数都有快速计算公式。整数部分直接数位,小数部分直接查表。对log2(x),最高位位置-1即为整数值,小数直接查表。对2^x,整数部分直接移位,溢出另外处理,然后小数部分查表,然后直接和整数部分相乘。精度和你的表。项有关,表越细精度越高。

冈板日川 发表于 2020-10-23 00:22:10

奇怪了,用上面的公式,计算出来的结果和实际相差非常的大。

冈板日川 发表于 2020-10-23 00:37:26

以下是完整的采样以及计算公式,两个开平方返回的结果是一样的,说明公式应该没有问题,主要是我自己写的这部分程序哪里不对,平均值是正确的,就是均方根值不正确,两种公式得出的结果是一样的,但是结果不正确。
unsigned int insqrt(unsigned long a)
{
    unsigned long i,c;
    unsigned long b=0;
    unsigned int dat;
    for(i=0x40000000;i!=0;i>>=2)
    {
      c =i+b;
      b>>=1;
      if(c<= a)
      {
            a-=c;
            b+=i;
      }
    }
    dat=b;
    return dat;
}


unsigned intisqrt32(unsigned long x)
{
    unsigned long m, y, b;
    m = 0x40000000;
    y = 0;
    while (m != 0)
    {
      b = y | m;
      y = y >> 1;
      if (x >= b)
      {
            x = x - b;
            y = y | m;
      }
      m >>= 2;
    }
    return y;
}

void READ_SYS_Voltage(void)//读取系统电压
{
    unsigned char i,DL;
    unsigned int Dat=0;
    unsigned intxdata a;
    unsigned long b=0,p=0;
    for(i=138;i>0;i--)//清空数组内数据
    {
      a=0;   
    }
    TF1 = 0;       //清除TF1标志
    ET1 = 0;       //关闭定时器1中断
    TR1 = 0;       //定时器1关闭计时   
    TH1 = 0x00;    //初始化计时值
    TH1 = 0xb8;       //初始化计时值 定时20毫秒@11.0592M外置晶振
    TF1 = 0;       //清除TF1标志   
    ET1 = 1;       //使能定时器1中断
    TR1 = 1;       //定时器1开始计时
    READ_Voltage_EN=1;//将定时时间到置1
    Dat=0;
    while(READ_Voltage_EN)//到达20毫秒后,此变量通过定时器1中断置0,退出采样
    {
      ADC_CONTR = 0xc0; //清除ADC    180个时钟转换一次
      ADC_CONTR = 0Xcc; //P1.4
      while(!(ADC_CONTR&0x10));
      a=ADC_RES;
      DL=ADC_RESL;
      a<<=2;
      a|=DL;
      ADC_CONTR = 0xc0; //清除ADC
      a>>=1;
      if(i<139)
      {
            i++;
      }
      Delay_Read();//延时      
      Dat++;   
    }
    Send_ASCII("共计采样:",0);
    WR_DAT(Dat);
   
    DL=i;
    for(i=0;i<DL;i++)
    {
      b+=(a*a);//计算平方和
      p+=a;//累加      
    }
    b/=DL;//计算平方和后再平均
    Dat=insqrt(b);//开平方
    a=isqrt32(b);

    Send_ASCII("均方根值1:",0);//第一种均方根计算方式
    WR_DAT(Dat);

    Send_ASCII("均方根值2:",0);//第二种均方根计算方式
    WR_DAT(a);

    Dat=p/DL;//直接平均
    Send_ASCII("平均值:",0);
    WR_DAT(Dat);
    ENT();ENT();
}

electricit 发表于 2020-10-23 00:57:26

这样是不行的,电网的频率是变化的,你需要等时采样后均方根才最准,0.1V没有问题

冈板日川 发表于 2020-10-23 01:28:19

electricit 发表于 2020-10-23 00:57
这样是不行的,电网的频率是变化的,你需要等时采样后均方根才最准,0.1V没有问题
...

等时?现在的采样时间是固定的20毫秒,你说的等时是指需要与电网的频率同步?那要怎么做?

冈板日川 发表于 2020-10-23 01:30:35

数据错误的问题解决了
        for(i=0;i<DL;i++)
        {
                d=a;//强制将数据转换为long类型
                b+=d*d;
                p+=a;//累加               
        }

albert_w 发表于 2020-10-23 08:02:24

冈板日川 发表于 2020-10-23 01:28
等时?现在的采样时间是固定的20毫秒,你说的等时是指需要与电网的频率同步?那要怎么做? ...

哈,你一个周期采一个点?
那不各点基本一样的,周期波形哪里来呢。

BOERLBH 发表于 2020-10-23 08:12:17

冈板日川 发表于 2020-10-22 22:44
采集到的数据是10位的,不是8位的数据。要怎么处理?

我的 ADC是 12位采集8个通道。 产品都用了5年了。 你照着改一下肯定能用。

s1j2h3 发表于 2020-10-23 08:21:13

四倍频采样法,可直接计算出幅度

liujian6f 发表于 2020-10-23 08:22:06

冈板日川 发表于 2020-10-23 01:28
等时?现在的采样时间是固定的20毫秒,你说的等时是指需要与电网的频率同步?那要怎么做? ...

做过交流电压表,先计算出电压的频率(如果频率固定可省略),例如50HZ那么一个周期20mS在20ms内采集128个点(间隔20mS/128可以不用等待过零随时开始)每个点先计算平方数在相加等待取完128个点时累加和/128 再开方 就得到结果

liujian6f 发表于 2020-10-23 08:40:32

还有交流有正负周期 算两个幅值128个点的数据只算一半就可以了, 如果要精确的算正周期/负周期 就要做过零了
简易的方式 二极管取半波 电阻分压 直接送入单片机ADC取128个点数据平均时/64 因为没有负半周{:lol:}{:lol:}{:lol:}

MYQQ2018 发表于 2020-10-23 08:54:20

虽然两个词是通用的,还是建议使用“方均根”,这里面是有计算顺序的

冈板日川 发表于 2020-10-23 09:09:02

liujian6f 发表于 2020-10-23 08:22
做过交流电压表,先计算出电压的频率(如果频率固定可省略),例如50HZ那么一个周期20mS在20ms内采集 ...

如果不是50HZ,要如何处理?

modbus 发表于 2020-10-23 09:10:23

AWEN2000 发表于 2020-10-22 22:46
不准?还可以吧。普通万用表就是测的也是平均值。
计算一般市电交流电压足够了,如果波形畸变厉害是不够 ...

STC8浮点数开根号也就几十微秒,算市电交流电也足够了

zhuyi25762 发表于 2020-10-23 09:13:24

软件不行,就硬件上,速度不快就直接硬件转换,,

liujian6f 发表于 2020-10-23 09:42:06

冈板日川 发表于 2020-10-23 09:09
如果不是50HZ,要如何处理?

定频就好办例如60Hz周期就是16.6666mS   如果用的是 例如发电机上时 频率都是有误差的 所以要先计算频率

冈板日川 发表于 2020-10-23 09:54:17

s1j2h3 发表于 2020-10-23 08:21
四倍频采样法,可直接计算出幅度

那样太占系统时间和资源了。

冈板日川 发表于 2020-10-23 09:56:04

liujian6f 发表于 2020-10-23 09:42
定频就好办例如60Hz周期就是16.6666mS   如果用的是 例如发电机上时 频率都是有误差的 所以要先计 ...

是用在工业设备里的,正常的情况下,国内的电网频率都是50HZ的吧,变化应该不会太大的吧。这个主要是用来测量电机电流是不是失衡,是不是过流等。

AWEN2000 发表于 2020-10-23 09:59:27

冈板日川 发表于 2020-10-23 09:56
是用在工业设备里的,正常的情况下,国内的电网频率都是50HZ的吧,变化应该不会太大的吧。这个主要是用来 ...

电机过流?精度应该要求不高吧,似乎没必要用真有效值

冈板日川 发表于 2020-10-23 10:00:56

AWEN2000 发表于 2020-10-23 09:59
电机过流?精度应该要求不高吧,似乎没必要用真有效值

系统还要做PID调整,对电流有一定的精度要求。

electricit 发表于 2020-10-23 18:34:26

冈板日川 发表于 2020-10-23 01:28
等时?现在的采样时间是固定的20毫秒,你说的等时是指需要与电网的频率同步?那要怎么做? ...

过零检测一个周波的时长,采样32点时将时长32等份写入定时器,定时器触发采样,每个周波都要更新一下定时器溢出值,然后均方根非常准

mcu5i51 发表于 2020-10-24 17:15:53

冈板日川 发表于 2020-10-23 09:09
如果不是50HZ,要如何处理?

过零时开始,下次或N次过零时结束,之后计算,
或用一个很长的时间平均下减少不是整周期的误差
页: [1]
查看完整版本: 求助均方根计算公式,用于计算交流有效电压值