📄 avr465.c
字号:
// 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 + -