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

📄 avr465.c

📁 使用AVR单片机设计的单相电表设计程序.不使用专用电表芯片,直接用AVR单片机AD采样交流电压电流,计算电压电流有效值和有功功率及有功电度和功率因数.
💻 C
📖 第 1 页 / 共 3 页
字号:
  // 	MaxSamples(presc=64) = 7FFFFFFFh / 003F6064h = 0205h = 517d
  //
  // Note: Index=0 -> ADC2, Index=1 -> ADC0, Index=2 -> ADC1
  Temp[Index]=Sample[Index].Calibrated>>6;
  switch (Index)
  {
    case 0: // Index=0 => ADC2 (Voltage)
            Accumulator.U2=Accumulator.U2+(Temp[0]*Temp[0]);
            break;
    case 1: // Index=1 => ADC0 (Current L)
            Accumulator.IL2=Accumulator.IL2+(Temp[1]*Temp[1]);
            Temp[3]=Temp[0]*Temp[1];
            Accumulator.PL=Accumulator.PL+Temp[3];
            break;
    case 2: // Index=2 => ADC1 (Current N)
            Accumulator.IN2=Accumulator.IN2+(Temp[2]*Temp[2]);
            Temp[4]=Temp[0]*Temp[2];
            Accumulator.PN=Accumulator.PN+Temp[4];
            SampleCounter++;
            break;
  }
  if (SampleCounter==NMAX)
  {
    SampleCounter=0;
    if (Flags&GROGGY)
    {
      // Clear unsettled, accumulated data
      Accumulator.PL=0;
      Accumulator.PN=0;
      Accumulator.U2=0;
      Accumulator.IL2=0;
      Accumulator.IN2=0;
      Flags=Flags&(0xFF-GROGGY);          // Clear flag; we are OK now
    }
    else
      Flags=Flags|CYCLE_FULL;
  }
  
  // Maintain multiplexing of input channels
  if (++Index > 2)
    Index=0;
  ADMUX=ADC_VREF_TYPE+Index;		// Select next ADC input

  // Maintain display counter pulse, if activated. Toggle pulse as follows:
  //          ________
  // DPP:  __/        \___________________________________
  //                            ________
  // DPN:  ____________________/        \_________________
  //
  // TIME: --+--------+--------+--------+------------------>
  //         0      DPon    2*DP_ON  3*DP_ON      1T = 1/fs
  //
  // Use DP_ON constant to set pulse lentgh according to display counter specs
  if (DPCActive>0)
  {
    DPCActive++;
    switch (DPCActive)
    {
      case  DP_ON:    PORTD=(PIND&DIRD)&(0xFF-DPP);
                      break;
      case  2*DP_ON:  PORTD=(PIND&DIRD)|DPN;
                      break;
      case  3*DP_ON:  PORTD=(PIND&DIRD)&(0xFF-DPN);
                      DPCActive=0;
                      break;
    }
  }
  PORTB=(PINB&DIRB)&(0xFF-DUTY);
  asm("wdr");				// Watchdog reset
}                                       // End of ADC_ISR


/////////////////////////////////////////////////////////////////////////////
//  S U B F U N C T I O N S
/////////////////////////////////////////////////////////////////////////////

// Fundamental function for character-based input
int getchar(void)
{
  Flags=Flags&(0xFF-KEY_PRESSED);
  return RxBuffer;
}

// Fundamental function for character-based output (printf, etc)
int putchar(int Data)
{
  while ((UCSR0A & (1<<UDRE0))==0);
  UDR0=Data;
  return(Data);
}

// Calculate CRC16 Checksum based on initial checksum. Return new checksum.
unsigned int CRC(unsigned int chksum, unsigned char buffer)
{
  unsigned char j=8;
  
  chksum ^= (unsigned int)(buffer);
  do
  {
    if(chksum&0x8000)
      chksum=(chksum<<1) ^ CRC_POLYNOME;
    else 
      chksum=chksum<<1;
  }
  while(--j);
  return(chksum);
}

// Called after a device reset; initialises only registers which are required
// to have a non-default value
void Initialise(void)
{
  ACSR=0x80;      // Disable analogue comparator; save power

  DDRB=DIRB;
  DDRC=DIRC;
  DDRD=DIRD;

  TCCR1A=0x80;    // CLEAR output on next match (no EP output yet)
  TCCR1B=0x0D;	  // CTC Mode, Top=OCR1A, CLK/1024
  OCR1A=0xFFFF;   // Assume zero power; set max pulse interval
  OCR1B=0x00C3;   // Length of EP; T = (1024*195)/4000000 = 49.92ms
  TIMSK1=0x07;    // T/C1: Output compare match A & B + overflow

  UCSR0A=0x02;     // USART 2x speed enabled, Multi-processor mode disabled
  UCSR0B=0xD8;     // Rx and Tx enabled, Rx and tx interrupts enabled
  UCSR0C=0x06;     // Asynchronous, no parity, 8 data bits, 1 stop bit
  UBRR0=0x000C;    // f=4.000MHz, U2X=1: 38400 baud +/- 0.2%

  // ADC initialization:
  // Enable ADC interrupt and use internal voltage reference with stabilising
  // capacitor on AREF. Set multiplexer input to first channel and do not set
  // ADC to High Speed Mode. Set Auto Trigger Source to Free Running Mode.
  //
  //  fCLK | ADPS | Prescaler | ADCCLK | Sampl.Rate
  // ------+------+-----------+--------+-----------
  // 4.000 |  111 |       128 | 31250  |       2403

  ADMUX=ADC_VREF_TYPE;
  ADCSRA=0xEF;    // Enable ADC, start, auto trigger, int enable, presc = 128

  Flags=Flags|DISPLAY;            // Set flag
  Flags=Flags|GROGGY;             // Just woke up, hold on for a while...
  TC1extTOP=0xFFFF;               // Assume zero power; set max count
  DPCIncrement=0;                 // No increment; no displ counter pulses yet

  SampleCounter=-1200+NMAX;       // Allow HPF to settle to within 1%
  PORTC=(PINC&DIRC)|EARTH|REVDIR; // Set signals high to start with
}

void InitGainControl(void)
{
  // The LessGain flags will be set when any sample reaches high limit
  Flags=Flags&(0xFF-LESSGAIN0);
  Flags=Flags&(0xFF-LESSGAIN1);
  
  // The MoreGain flags will be cleared when any sample reaches threshold
  Flags=Flags|MOREGAIN0;
  Flags=Flags|MOREGAIN1;
}

void ReadCalibration(void)
{
  // Remember that interrupts must be disabled when accessing EEPROM!
  //
  // ADDR | +0x00 | +0x01 | +0x02 | +0x03 | +0x04 | +0x05 | +0x06 | +0x07
  // -----+-------+-------+-------+-------+-------+-------+-------+-------
  // 0x00 | PCC0H | PCC0L | PCC1H | PCC1L | PCC2H | PCC2L | ILG0H | ILG0L
  // 0x08 | ILG1H | ILG1L | ILG2H | ILG2L | ING0H | ING0L | ING1H | ING1L
  // 0x10 | ING2H | ING2L |   UGH |   UGL |   MCH |   MCL |  DPCH |  DPCL
  // 0x18 |       |       |       |       | CRCWH | CRCWL | CRC16 | CRC16
  //
  // FEATURE: DO NOT FILL EEPROM WITH ZEROS, SINCE IT WILL GIVE A VALID
  // CHECKSUM BUT INVALID CALIBRATION COEFFICIENTS!
  
  unsigned char n=0,Hi,Lo;
  unsigned int crc=0;
  
  // Calculate checksum for first 24 bytes of EEPROM  
  for (n=0;n<0x18;n++)
  {
    __EEGET(Hi,n);
    crc = CRC(crc,Hi);
  }
  
  // Write checksum to EEPROM if CRCW = 0x4357
  __EEGET(Hi,0x1C);
  if (0x43==Hi)
  {
    __EEGET(Lo,0x1D);
    if (0x57==Lo)
    {
      __EEPUT(0x1C,0x00);
      __EEPUT(0x1D,0x00);
      __EEPUT(0x1E,(crc>>8));
      __EEPUT(0x1F,(crc&0xFF));
    }
  }
  
  // Read calibration coefficients from EEPROM, if checksum holds
  __EEGET(Hi,0x1E);
  __EEGET(Lo,0x1F);
  if (crc-(Hi<<8)-Lo)
  {
    // put error handler here
  }
  else
  {
    for (n=0;n<3;n++)
    {
      __EEGET(Hi,0x00 + n*2);
      __EEGET(Lo,0x01 + n*2);
      CalCoeff.PCC[n]=(Hi<<8) + Lo;
  
      __EEGET(Hi,0x06 + n*2);
      __EEGET(Lo,0x07 + n*2);
      CalCoeff.ILG[n]=1.0 / ((Hi<<8) + Lo);
  
      __EEGET(Hi,0x0C + n*2);
      __EEGET(Lo,0x0D + n*2);
      CalCoeff.ING[n]=1.0 / ((Hi<<8) + Lo);
    }
  
    __EEGET(Hi,0x12);
    __EEGET(Lo,0x13);
    CalCoeff.UG=(unsigned int)((Hi<<8) + Lo) / 65536.0;
    
    __EEGET(Hi,0x14);
    __EEGET(Lo,0x15);
    CalCoeff.MC=(Hi<<8) + Lo;
    
    __EEGET(Hi,0x16);
    __EEGET(Lo,0x17);
    CalCoeff.DPC=(Hi<<8) + Lo;
  }

  // Save clock cycles; calculate numerator now, rest when adjusting EP T/C:
  //
  //   Tnum = (3600 x fCLK)/(Prescaler x P[kW])
  //        = (3600 x 4000000 x 1000)/(1024 x MC)
  //        = 14 062 500 000 / MC
  Tnum=14062500000.0/CalCoeff.MC;
}

void SetGain(unsigned char Channel, unsigned char Level)
{
  //  ADC |        |      |    PD    |   PORTD  |   DDRD  
  //  Ch. |  Range | Gain | 76543210 | 76543210 | 76543210
  // -----+--------+------+----------+----------+----------
  //   0  |    Low |    1 | xxxxLLxx | xxxx00xx | xxxx11xx
  //   0  | Medium |    8 | xxxxHLxx | xxxx10xx | xxxx11xx
  //   0  |   High |   64 | xxxxLHxx | xxxx01xx | xxxx11xx
  //   1  |    Low |    1 | xxLLxxxx | xx00xxxx | xx11xxxx
  //   1  | Medium |    8 | xxHLxxxx | xx10xxxx | xx11xxxx
  //   1  |   High |   64 | xxLHxxxx | xx01xxxx | xx11xxxx
  
  #define   GC0   0x0C
  #define   GC1   0x30
  
  switch (Level)
  {
    case LOW:	switch (Channel)
                {
                  case ADC0:  // set gain control pins LOW, only
                              PORTD=(PIND&DIRD)&(0xFF-GC0);
                              ADC0_Gain=LOW;
                              break;
                  case ADC1:  // set both gain control pins LOW
                              PORTD=(PIND&DIRD)&(0xFF-GC1);
                              ADC1_Gain=LOW;
                              break;
                }
                break;
    case MEDIUM:  switch (Channel)
                  {
                    case ADC0:  // set PD2 LOW and PD3 HIGH
                                PORTD=((PIND&DIRD)&(0xFF-GC0))|(1<<PD3);
                                ADC0_Gain=MEDIUM;
                                break;
                    case ADC1:  // set PD4 LOW and PD5 HIGH
                                PORTD=((PIND&DIRD)&(0xFF-GC1))|(1<<PD5);
                                ADC1_Gain=MEDIUM;
                                break;
                  }
                  break;
    case HIGH:	  switch (Channel)
                  {
                    case ADC0:  // set PD2 HIGH and PD3 LOW
                                PORTD=((PIND&DIRD)&(0xFF-GC0))|(1<<PD2);
                                ADC0_Gain=HIGH;
                                break;
                    case ADC1:  // set PD4 HIGH and PD5 LOW
                                PORTD=((PIND&DIRD)&(0xFF-GC1))|(1<<PD4);
                                ADC1_Gain=HIGH;
                                break;
                  }
                  break;
  }
  GainHysteresis=GAIN_HOLD;
}

void SetPulse(float Power)
{
  // Set output frequency of energy pulse as follows:
  //
  //       fCLK         3600    
  //  T = ------ x ------------ 
  //      Presc.    P[kW] x MC  
  //
  // Defaults are 4MHz system clock and 1024 prescaler. The default meter
  // constant, MC, is 10000 pulses/kWh.
  //
  // The energy pulse output is controlled by hardware T/C, which is driven 
  // by a prescaled system clock. Using the above settings, the T/C output is
  // good for 0.2% (i.e. 1/500) accuracy, since:
  //
  //  Tmin = 14062500000 / (265V*10A*10000imp/kWh) = 530
  //
  // Since T/C1 is only 16 bit in length, an extension to the counter is 

⌨️ 快捷键说明

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