⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 avr465.c

📁 使用AVR单片机设计的单相电表设计程序.不使用专用电表芯片,直接用AVR单片机AD采样交流电压电流,计算电压电流有效值和有功功率及有功电度和功率因数.
💻 C
📖 第 1 页 / 共 3 页
字号:
  // stored in TC1ext. The extended counter has a maximum pulse period of:
  //
  //         Presc
  // Tmax = ------- x ( FFFF x FFFF + FFFF ) = >1Ms = >305h = >12d
  //          fCLK
  //
  // ...but, in practice active power is either zero or above I_MIN x Unom.
  // Assuming I_MIN = 0.002 and Unom = 230V, and high meter constant:
  //
  //          4000000                  3600
  //  Tmax = --------- x ------------------------------- = 3057s = <1h
  //            1024      0.002A x 230V x 10000 imp/kWh

  #define Tmax  0xFFFE0000
  
  float T;
  unsigned int CtrTemp,ExtCtrTemp;
  char Mask;

  Power=fabs(Power);      // Pulse interval contains no sign information
  if (Power<0.0001)       // Divide by zero is not defined in this universe
    T=Tmax;
  else
    T=Tnum/Power;         // Precalculated numerator assumes power in watts
  
  if (T<(2*OCR1B))
    T=2*OCR1B;                      // Min interval = twice pulse high time

  if (T>Tmax)
  {
    ExtCtrTemp=0xFFFF;              // Max count
    CtrTemp=0xFFFF;                 // Max count
  }
  else
  {
    ExtCtrTemp=T/0xFFFF;            // Number of 16-bit timer cycles
    CtrTemp=T-(ExtCtrTemp*0xFFFF);  // Ticks in single/last cycle
  }

  Mask=TIMSK1;
  TIMSK1=0x00;                      // Disable T/C1 interrupts for a moment

  if (TC1extTOP>0)
  { // Currently timer is in the middle of an extended count
    TC1extTOP=ExtCtrTemp;         // Set new TOP for extended count
    if (TC1ext>TC1extTOP)
      TC1ext=TC1extTOP;           // Fit counter value in new frame
    if (TC1ext==TC1extTOP)
    { // Last extended count cycle
      OCR1A=CtrTemp;              // Set TOP value for last cycle
      TCCR1A=0xC0;                // Set mode: set OC1A on next compare match
      DPCIncrement=1;             // Do increment display counter on next match
    }
    else
    { // At least one more extended cycle to go
      OCR1A=0xFFFF;               // Set TOP value for last cycle
      TCCR1A=0x80;                // Set mode: clear OC1A on next match
      DPCIncrement=0;             // Do not increment displ cntr on next match
    }
  }
  else
  { // Currently timer runs unextended
    OCR1A=CtrTemp;                // Set new TOP value
    if (ExtCtrTemp>0)
    { // Next cycle must run extended
      TC1extTOP=ExtCtrTemp;       // Set new TOP for extended count
      TC1ext=TC1extTOP;           // Pretend we are at end of extended cycle    
    }
    TCCR1A=0xC0;                  // Set mode: set OC1A on next compare match
    DPCIncrement=1;               // Do increment display counter on next match
  }
  TC1extRem=CtrTemp;              // Set TOP value for future last cycles
  if (TCNT1>=OCR1A)
    TCNT1=(OCR1A-1);              // Rewind, if counter is larger than TOP

  TIMSK1=Mask;                    // Restore T/C1 interrupts
}


/////////////////////////////////////////////////////////////////////////////
//  M A I N
/////////////////////////////////////////////////////////////////////////////

void main(void)
{
  unsigned long	TempUL;
  
  // Check source of reset
  if (MCUSR & 1)               // Power-on Reset
  {
    // put POR handler here, if required
  }
  else if (MCUSR & 2)          // External Reset
  {
    // put external reset handler here, if required
  }
  else if (MCUSR & 4)          // Brown-Out Reset
  {
    // put BOR handler here, if required
  }
  else                          // Watchdog Reset
  {
    // put watchdog reset handler here, if required
  };
  MCUSR=0;

  Initialise();           // set up I/O registers, flags & variables
  ReadCalibration();      // interrupts must be disabled when accessing EEPROM
  InitGainControl();      // configure variable gain
  SetGain(ADC0,LOW);      // start from low gain
  SetGain(ADC1,LOW);      // start from low gain
  
  asm("wdr");                                 // reset WD counter
  WDTCSR |= (1<<WDCE) | (1<<WDE);             // enable watchdog interrupt 
  WDTCSR = (1<<WDE) | (1<<WDP2) | (1<<WDP1);  // set 128K cycles (~1.0 s)
  asm("sei");                                 // allow all enabled interrupts
  
  printf("\n\r");
  printf("AVR465\n\r");
  printf("\n\r");
  printf("PCC:   0x%04X   0x%04X   0x%04X\n\r",CalCoeff.PCC[0],CalCoeff.PCC[1],CalCoeff.PCC[2]);
  printf("ILG: %8.6f %8.6f %8.6f\n\r",CalCoeff.ILG[0],CalCoeff.ILG[1],CalCoeff.ILG[2]);
  printf("ING: %8.6f %8.6f %8.6f\n\r",CalCoeff.ING[0],CalCoeff.ING[1],CalCoeff.ING[2]);
  printf("UG:  %8.6f\n\r",CalCoeff.UG);
  printf("MC:  %8u\n\r",CalCoeff.MC);
  printf("DPC: %8u\n\r",CalCoeff.DPC);
  printf("\n\r");
  
  while (1)
  {
    if (Flags&KEY_PRESSED)
    {
      // Insert key service functions here
      switch (getchar())
      {
        case 'd':
        case 'D': if (Flags&DISPLAY)
                    Flags=Flags&(0xFF-DISPLAY);
                  else
                    Flags=Flags|DISPLAY;
                  break;
      }
    }

    // When a full cycle of data has been accumulated; make a copy of accumulated
    // data before it is overwritten by next sampling instance. Process accumulated
    // data as follows:
    //
    // Active power:	P = (accumulated data) / (number of samples)
    // Voltage:		U = SQRT [ (accumulated data) / (number of samples) ]
    // Current:		I = SQRT [ (accumulated data) / (number of samples) ]
    // Apparent power:	S = U * I
    //
    // All data processing must be complete before next accumulation cycle is
    // completed.
    if (Flags&CYCLE_FULL)
    {
      Flags=Flags&(0xFF-CYCLE_FULL);
      
      // Make a copy of accumulated data before it is overwritten
      Sum=Accumulator;
      
      // Clear accumulated data
      Accumulator.PL=0;
      Accumulator.PN=0;
      Accumulator.U2=0;
      Accumulator.IL2=0;
      Accumulator.IN2=0;
      
      // Save gain settings that was used with this data
      Result.ADC0_Gain=ADC0_Gain;
      Result.ADC1_Gain=ADC1_Gain;

      // Set gain for next set of measurements
      if (Flags&LESSGAIN0)
        switch (ADC0_Gain)
        {
          case MEDIUM:  SetGain(ADC0,LOW);
                        break;
          case HIGH:    SetGain(ADC0,MEDIUM);
                        break;
        }
      if (Flags&MOREGAIN0)
        switch (ADC0_Gain)
        {
          case LOW:     SetGain(ADC0,MEDIUM);
                        break;
          case MEDIUM:  SetGain(ADC0,HIGH);
                        break;
        }
      if (Flags&LESSGAIN1)
        switch (ADC1_Gain)
        {
          case MEDIUM:  SetGain(ADC1,LOW);
                        break;
          case HIGH:    SetGain(ADC1,MEDIUM);
                        break;
        }
      if (Flags&MOREGAIN1)
        switch (ADC1_Gain)
        {
          case LOW:     SetGain(ADC1,MEDIUM);
                        break;
          case MEDIUM:  SetGain(ADC1,HIGH);
                        break;
        }
      
      InitGainControl();

      // Calculate. Use accumulated data to derive active power and current
      // for live and neutral wires. Also derive voltage between live and
      // neutral wire.
      
      // Start by adding offset to accumulated data. This will improve non-
      // linearities at small amplitudes. Provided that the offset is
      // sufficiently small, that is!
      if (Sum.PL>0)
        Result.PL=Sum.PL*NORM+OFFSET;
      else
        Result.PL=Sum.PL*NORM-OFFSET;
      if (Sum.PN>0)
        Result.PN=Sum.PN*NORM+OFFSET;
      else
        Result.PN=Sum.PN*NORM-OFFSET;
      TempUL=(Sum.U2+OFFSET)*NORM;
      Result.U=sqrt(TempUL);
      TempUL=(Sum.IL2+OFFSET)*NORM;
      Result.IL=sqrt(TempUL);
      TempUL=(Sum.IN2+OFFSET)*NORM;
      Result.IN=sqrt(TempUL);

      // Calibrate. Take measurement results and adjust them according to
      // characteristics of this particular meter.
      Result.PL=Result.PL*CalCoeff.ILG[Result.ADC0_Gain];
      Result.IL=Result.IL*CalCoeff.ILG[Result.ADC0_Gain];
      Result.PN=Result.PN*CalCoeff.ING[Result.ADC1_Gain];
      Result.IN=Result.IN*CalCoeff.ING[Result.ADC1_Gain];
      Result.PL=Result.PL*CalCoeff.UG;
      Result.PN=Result.PN*CalCoeff.UG;
      Result.U=Result.U*CalCoeff.UG;

      // Anti-creep: reset power readings when current below threshold.
      // This is to make sure no pulses are emitted when signal is too low.
      // Don't want to bill the customer for noise in input channels...?
      if (Result.IL<I_MIN)
        Result.PL=0.0;
      if (Result.IN<I_MIN)
        Result.PN=0.0;

      // Tampering logic activated when either active power above threshold.
      // This is to prevent tamper indicators to go on and off at very low
      // power readings.
      if ((fabs(Result.PL)>P_MIN) | (fabs(Result.PN)>P_MIN))
      {
        // Check for earth fault, i.e. verify that all current which is going
        // into the load via the live wire also returns via the neutral wire.
        // Set tamper indicator as follows:
        //
        //    EARTH | Condition
        //   -------+-------------------------------------------
        //     Low  | P(L) and P(N) are almost same in magnitude
        //    High  | P(L) and P(N) are not same in magnitude
        //
        // It doesn't matter if the user swappes live and neutral wires.
        // Use the larger power reading for billing (energy pulse output).
        if (fabs(Result.PL)>fabs(Result.PN))
        { // |P(L)| > |P(N)|
          if (fabs(Result.PL)>(1.05*fabs(Result.PN)))
            PORTC=(PINC&DIRC)|EARTH;        // Magn. diff. > 5% => Set high
          else
            PORTC=(PINC&DIRC)&(0xFF-EARTH); // Magn. diff. < 5% => Set low
          SetPulse(Result.PL);        // Use Live wire measurements
        }
        else
        { // |P(L)| < |P(N)|
          if (fabs(Result.PN)>(1.05*fabs(Result.PL)))
            PORTC=(PINC&DIRC)|EARTH;        // Magn. diff. > 5% => Set high
          else
            PORTC=(PINC&DIRC)&(0xFF-EARTH); // Magn. diff. < 5% => Set low
          SetPulse(Result.PN);        // Use Neutral wire measurements
        }

        // Verify that current flows in the expected direction. Both live and
        // neutral current are positive if design is wired as expected. Set
        // tamper indicator as follows:
        //
        //   REVDIR | Condition
        //   -------+--------------------------------------
        //     Low  | P(L) is positive AND P(N) is positive
        //    High  | P(L) is negative OR P(N) is negative
        //
        // Measurement accuracy is not compromised by sign of current.
        if ((Result.PL<0) | (Result.PN<0))
          PORTC=(PINC&DIRC)|REVDIR;         // Current flow NOK => Set high
        else
          PORTC=(PINC&DIRC)&(0xFF-REVDIR);  // Current flow OK => Set low
      }
      else
      {
        // Tampering logic is disabled for now
        PORTC=(PINC&DIRC)&(0xFF-EARTH);
        PORTC=(PINC&DIRC)&(0xFF-REVDIR);
        SetPulse(Result.PL);                // Default
      }
      
      // Display measurement results. Connect TxD and RxD to any terminal
      // software or application. If properly calibrated, the output will
      // show active power in watts, current in amps and voltage in volts.
      if (Flags&DISPLAY)
      {
        printf(" %12.3f, %12.3f",Result.PL,Result.PN);
        printf(", %10.5f, %10.5f",Result.IL,Result.IN);
        printf(", %8.3f",Result.U);
        printf("\n\r");
      }
    }
  }
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -