C计算和铸造混合类型

时间:2014-09-23 20:20:42

标签: c types casting formula division

我想用这个公式计算我的微控制器的平均值:

uint16_t uAverage;
uint64_t counter;
uint16_t u;

uAverage = uAverage * (1 + 1 / counter) + u / counter;

但是我在过去的那些计算中遇到了问题,这也是行不通的,因为它们的类型不同,而且因为除法应该返回浮点值。

请告诉我如何使用这个公式的最佳方法。

编辑: 这是目前的全部来源。该程序可以使用ADC测量电功率。如果您有任何疑问,请询问。微控制器:ATmega2560

#include <avr/io.h>
#include <avr/interrupt.h>
#include <inttypes.h>
#include "LITECShieldDefinitions.h"
#include "ADC.h"
#include "Timer.h"
#include "HWTimer.h"
#include "LCD.h"
#include "USART.h"

volatile uint8_t setOffset = 0;
volatile uint8_t resetValues = 0;
volatile uint8_t resetSecondValues = 0;

volatile uint64_t counter = 1;
volatile uint64_t counterSecond = 1;
volatile uint16_t u = 0;
volatile uint16_t i = 0;
volatile int32_t p = 0;
volatile uint16_t uMin = 1023;
volatile uint16_t iMin = 1023;
volatile int32_t pMin = 1046529;
volatile uint16_t uMax = 0;
volatile uint16_t iMax = 0;
volatile int32_t pMax = 0;
volatile uint16_t uAverage = 0;
volatile uint16_t iAverage = 0;
volatile int32_t pAverage = 0;
volatile uint16_t uSecondAverage = 0;
volatile uint16_t iSecondAverage = 0;
volatile int32_t pSecondAverage = 0;
volatile uint16_t uOffset = 0;
volatile uint16_t iOffset = 0;

ISR(TIMER1_OVF_vect) {
    TimerLoadValue(TIMER_1, 3000);
    resetSecondValues = 1;

    //TODO: print values to LCD

    printf("u=%" PRIu16 "\n", u);
    printf("i=%" PRIu16 "\n", i);
    printf("p=%" PRId32 "\n", p);
    printf("uMin=%" PRIu16 "\n", uMin);
    printf("iMin=%" PRIu16 "\n", iMin);
    printf("pMin=%" PRId32 "\n", pMin);
    printf("uMax=%" PRIu16 "\n", uMax);
    printf("iMax=%" PRIu16 "\n", iMax);
    printf("pMax=%" PRId32 "\n", pMax);
    printf("uAverage=%" PRIu16 "\n", uAverage);
    printf("iAverage=%" PRIu16 "\n", iAverage);
    printf("pAverage=%" PRId32 "\n", pAverage);
    printf("uSecondAverage=%" PRIu16 "\n", uSecondAverage);
    printf("iSecondAverage=%" PRIu16 "\n", iSecondAverage);
    printf("pSecondAverage=%" PRId32 "\n", pSecondAverage);
    printf("uOffset=%" PRIu16 "\n", uOffset);
    printf("iOffset=%" PRIu16 "\n\n", iOffset);
}

ISR(USART0_RX_vect) {
    char data = UDR0;

    if (data == 'S') {
        setOffset = 1;
        printf("S\n");
    } else if (data == 'R') {
        resetValues = 1;
        printf("R\n");
    } else {
        printf("S, R\n");
    }
}

int main(void) {
    DDRD = 0x00;
    PORTD = 0xff;

    LCDInit();
    LCDClear();
    LCDString(1, 1, "Wattmeter ");
    USARTInit(0, 19200, 1, 1, 1, 0);
    printf("Wattmeter\n");
    sei();
    TimerEnableOVFInt(TIMER_1);
    TimerEnable(TIMER_1, PRE_DIV_256, TIMER_MODE0_NORMAL);
    TimerLoadValue(TIMER_1, 3000);
    ADCInit(ADC_VREF_TYPE_INTERNAL_AREF);

    while(1) {
        if (S1 || setOffset) {
            setOffset = 0;
            uOffset = u;
            iOffset = i;
        }

        if (S0 || resetValues) {
            resetValues = 0;
            counter = 1;
            counterSecond = 1;
            u = 0;
            i = 0;
            p = 0;
            uMin = 1023;
            iMin = 1023;
            pMin = 1046529;
            uMax = 0;
            iMax = 0;
            pMax = 0;
            uAverage = 0;
            iAverage = 0;
            pAverage = 0;
            uSecondAverage = 0;
            iSecondAverage = 0;
            pSecondAverage = 0;
        }

        if (resetSecondValues) {
            resetSecondValues = 0;
            counterSecond = 1;
            uSecondAverage = 0;
            iSecondAverage = 0;
            pSecondAverage = 0;
        }

        u = ADCReadChannel(0);  //max. 10 bit value
        i = ADCReadChannel(1);

        int16_t uReal = u - uOffset;
        int16_t iReal = i - iOffset;
        p = (int32_t) uReal * iReal;

        if (u < uMin) {
            uMin = u;
        }

        if (i < iMin) {
            iMin = i;
        }

        if (p < pMin) {
            pMin = p;
        }

        if (u > uMax) {
            uMax = u;
        }

        if (i > iMax) {
            iMax = i;
        }

        if (p > pMax) {
            pMax = p;
        }

        uAverage = uAverage * (1 - 1 / counter) + u / counter;
        iAverage = iAverage * (1 - 1 / counter) + i / counter;
        pAverage = pAverage * (1 - 1 / counter) + p / counter;
        uSecondAverage = uSecondAverage * (1 - 1 / counterSecond) + u / counterSecond;
        iSecondAverage = iSecondAverage * (1 - 1 / counterSecond) + i / counterSecond;
        pSecondAverage = pSecondAverage * (1 - 1 / counterSecond) + p / counterSecond;

        counter ++;
        counterSecond ++;
    }
}

3 个答案:

答案 0 :(得分:1)

是否需要非浮点答案:

执行一个整数除法。在分割之前添加counter/2来获得一个舍入商。

通过缩放uAverage,可以根据变量的允许范围提高精度。

uint16_t uAverage;
uint64_t counter;
uint16_t u;

// uAverage = uAverage * (1 + 1 / counter) + u / counter;
// uAverage = uAverage + uAverage / counter  + u / counter;
// uAverage = (uAverage*counter + uAverage + u) / counter;
uAverage = (uAverage*counter + uAverage + u + counter/2) / counter;
//  or 
const unsigned Scale = 16;  // Largest value that does not overflow computations.
uScAverage = (uScAverage*counter + uScAverage + scale*(u + counter/2)) / counter;
uAverage =  (uScAverage + Scale/2)/Scale;

答案 1 :(得分:0)

将整数除以整数会返回整数。

1 / counter为例:

  • 如果counter > 1,则返回0
  • 如果counter == 1,则会返回1
  • 如果counter == 0,则会生成异常

所以你基本上应该把至少一个操作数转换为floatdouble

话虽如此,这是您尝试执行的计算的解决方案:

uAverage += (double)(uAverage+u)/counter;

答案 2 :(得分:0)

这有效:

注意计算中的修正:你的1和2给出结果2.5 ......

float uAverage=0;
int counter=0;
int u=0;

while(scanf("%d",&u)) {//can type any no numeric to quit...
    counter++;
    uAverage = uAverage * (1.0 - 1.0 / counter) + (float)u / counter;
    printf("%f\n",uAverage);
}