📄 avr465.c
字号:
/*****************************************************************************
*
* Atmel Corporation
*
* File : AVR465.c
* Compiler : IAR EWAAVR 3.10C
* Revision : $Revision: 1.31 $
* Date : $Date: Friday, July 02, 2004 11:12:00 UTC $
* Updated by : $Author: kmeyer $
*
* Support mail : avr@atmel.com
*
* Supported devices : This example is written for the ATmega88. Firmware
* as such should fit in any other AVR with 8kB Flash.
*
* AppNote : AVR465: Single-Phase Power/Energy Meter with
* Tamper Detection.
*
* Description : Main file.
*
****************************************************************************/
/// DETAILS: ////////////////////////////////////////////////////////////////
//
// Author............................ Kim Meyer, Atmel Finland Design Center
// Compiler.................................... IAR Embedded Workbench 3.10C
// Chip type (Flash/SRAM/EEPROM).................... ATmega88 (8kB/1kB/512B)
// Program type................................................. Application
// Clock frequency............................................. 4.000000 MHz
// Average usage of program stack (RSTACK)......................0x0016 bytes
// Average usage of data stack (CSTACK).........................0x00E0 bytes
//
// Project Options for "Release":
// General-Target-Processor Configuration........................ ATmega88
// General-Target-Memory Model...................................... Small
// General-Library Configuration-Enable bit definitions................ ON
// General-Library Configuration-printf formater..................... Full
// General-System Configuration-Data stack-Size..................... 0x0F0
// General-System Configuration-Return address stack-Size.............. 24
// ICCAVR-Optimisations........................................ Size, High
// ICCAVR-Optimisations-Enabled-Common subexpression elimination....... NO
// ICCAVR-Optimisations-Enabled-Function inlining...................... NO
// ICCAVR-Optimisations-Enabled-Code motion........................... YES
// ICCAVR-Optimisations-Enabled-Cross call............................ YES
// ICCAVR-Optimisations-Enabled-Clustering of variables................ NO
//
// Memory Usage for "Release":
// Program (Code)...................................... 7743 bytes (94.5%)
// Variables (Data)..................................... 709 bytes (69.2%)
//
/////////////////////////////////////////// All your base are belong to us //
/////////////////////////////////////////////////////////////////////////////
// I N C L U D E S
/////////////////////////////////////////////////////////////////////////////
#include <iom88.h>
#include <stdio.h>
#include <math.h> // This is to be able to calculate square roots
#include <inavr.h> // This is for EEPROM routines
#include "defines.c" // Definitions
#include "AVR465.h"
/////////////////////////////////////////////////////////////////////////////
// G L O B A L V A R I A B L E S
/////////////////////////////////////////////////////////////////////////////
struct AccDataStructure { signed long PL, PN, U2, IL2, IN2;
} Accumulator, Sum;
struct ResultDataStructure { float PL, PN, U, IL, IN;
char ADC0_Gain, ADC1_Gain;
} Result;
struct CalDataStructure { unsigned int PCC[3];
float ILG[3], ING[3];
float UG;
unsigned int MC, DPC;
} CalCoeff={{0,24576,49152},
{0.01,0.001,0.0002},
{0.01,0.001,0.0002},
0.2,10000,100};
struct SampleStructure {
signed int Fresh, Previous;
signed long Filtered,PreviousFiltered,Calibrated;
} Sample[3];
signed long Temp[5];
unsigned int TC1ext,TC1extTOP,TC1extRem;
unsigned int DisplayCounter,DPCActive,DPCIncrement;
unsigned int GainHysteresis;
unsigned char Index,ADC0_Gain=0xFF,ADC1_Gain=0xFF,Flags;
int SampleCounter,RxBuffer;
float Tnum;
/////////////////////////////////////////////////////////////////////////////
// I N T E R R U P T S E R V I C E R O U T I N E S
/////////////////////////////////////////////////////////////////////////////
// Watchdog timeout handler
#pragma vector=WDT_vect
__interrupt void WFT_ISR(void)
{
// put timeout handler here
}
// USART Receiver interrupt service routine
#pragma vector=USART_RX_vect
__interrupt void USART_RX_ISR(void)
{
char Status,Data;
Status=UCSR0A;
Data=UDR0;
if ((Status & ((1<<FE0)|(1<<UPE0)|(1<<DOR0)))==0)
{
RxBuffer=Data;
Flags=Flags|KEY_PRESSED;
}
else
RxBuffer=0x00;
}
// USART Transmitter interrupt service routine
#pragma vector=USART_TX_vect
__interrupt void USART_TX_ISR(void)
{
// use this ISR if buffered data output is needed
}
// Timer 1 overflow interrupt service routine
#pragma vector=TIMER1_OVF_vect
__interrupt void TC1_OVF_ISR(void)
{
// this should never occur
}
// Timer 1 output compare A ISR: output is set or cleared on compare match
#pragma vector=TIMER1_COMPA_vect
__interrupt void TC1_COMPA_ISR(void)
{
// DPCIncrement should be zero when OC1A is cleared and one when OC1A is set
DisplayCounter=DisplayCounter+DPCIncrement;
// Prepare to set or clear output, if counter extension active
if (TC1extTOP>0)
{
TC1ext++; // Increase extended counter
if (TC1ext<TC1extTOP) // Extended counter not yet reached TOP
{
TCCR1A=0x80; // CLEAR output on next match
DPCIncrement=0; // Do not increment display counter on next match
}
else // Extended counter has reached TOP
{
if (TC1ext>TC1extTOP)
{ // Output has now been set: start all over
TC1ext=0; // Clear extended counter
OCR1A=0xFFFF; // Count full 16-bit cycles until extended TOP
TCCR1A=0x80; // CLEAR output on next match
DPCIncrement=0; // Do not increment display counter on next match
}
else
{ // All 16-bit full cycles done: now do remainder and then set output
OCR1A=TC1extRem; // Remainder to count on last extended cycle
TCCR1A=0xC0; // SET output on next match
DPCIncrement=1; // Do increment display counter on next match
}
}
}
else
TC1ext=0;
if (CalCoeff.DPC==DisplayCounter)
{
DisplayCounter=0;
PORTD=(PIND&DIRD)|DPP; // Set DPP high, other outputs remain static
DPCActive=1; // Start counting active time for DPP/DPN
}
}
// Timer 1 output compare B interrupt service routine
#pragma vector=TIMER1_COMPB_vect
__interrupt void TC1_COMPB_ISR(void)
{
unsigned char Temp;
// OC1A was set on OCR1A compare match, now clear output
Temp=TCCR1A; // Save current register (set/clear on next match?)
TCCR1A=0x80; // Set mode: clear OC1A on compare match
TCCR1C=0x88; // Now force a compare match to clear output
TCCR1A=Temp; // Set mode: as was (set/clear on next match)
}
// ADC interrupt service routine with auto input scanning
#pragma vector=ADC_vect
__interrupt void ADC_ISR(void)
{
signed int TempI;
signed long TempL;
PORTB=(PINB&DIRB)|DUTY; // For duty cycle monitoring
// Sampled data from ADC: ADCW Range=[0000...03FF]
// Copy of sampled data: Sample Range=[0000...03FF]
// Scaled copy of data: Temp Range=[00000000...0003FB01]
// DC filtered data: Filtered Range=[FFFE0280...0001FD80]
// Save copy of fresh sample. Current samples are inverted; invert back.
// Index variable keeps track of sampled channel, as follows:
//
// ISR: |XXX| |XXX| |XXX| |XXX| |XXX| |XXX| |XXX| |XXX| |
// Index/MUX: XXX|000000|111111|222222|000000|111111|222222|000000|1111
// Sampling: |XXXXXX|000000|111111|222222|000000|111111|222222|000000|
// Processing: |XXXXXX|XXXXXX|000000|111111|222222|000000|111111|222222|
// --------------+------+------+------+------+------+------+------+------+--
// Conversion: N N+1 N+2 N+3 N+4 N+5 N+6 N+7 N+8
//
Sample[Index].Previous=Sample[Index].Fresh; // x[n+1] <- x[n]
if (0==Index) // x[n] <- DATA
Sample[Index].Fresh=ADC; // save voltage sample as is
else
Sample[Index].Fresh=(0x03FF-ADC); // save inverted current sample
// Apply filter for DC offset removal. Must use long data types for
// precision! The transfer function is as follows:
//
// y[n] = 0.996*y[n-1] + 0.996*x[n] - 0.996*x[n-1]
Sample[Index].PreviousFiltered=Sample[Index].Filtered; // y[n] <- y[n-1]
TempL=255*(long)Sample[Index].Filtered;
TempL=TempL>>8;
TempI=Sample[Index].Fresh-Sample[Index].Previous;
TempL=TempL+255*(long)TempI;
Sample[Index].Filtered=TempL;
// Don't change gain settings until signals have settled
if (GainHysteresis)
{
Flags=Flags&(0xFF-MOREGAIN0);
Flags=Flags&(0xFF-MOREGAIN1);
GainHysteresis--;
}
else
// Gain control. Index=1 => ADC0, Index=2 => ADC1
switch (Index)
{
case 1: // Step down gain, if RAW sample has saturated at high limit
if (Sample[Index].Fresh>SAT_HI)
Flags=Flags|LESSGAIN0;
// Step down gain, if RAW sample has saturated at low limit
if (Sample[Index].Fresh<SAT_LO)
Flags=Flags|LESSGAIN0;
// Don't increase gain if FILTERED amplitude above threshold
if (Sample[Index].Filtered>AMP_LO)
Flags=Flags&(0xFF-MOREGAIN0);
break;
case 2: // Step down gain, if RAW sample has saturated at high limit
if (Sample[Index].Fresh>SAT_HI)
Flags=Flags|LESSGAIN1;
// Step down gain, if RAW sample has saturated at low limit
if (Sample[Index].Fresh<SAT_LO)
Flags=Flags|LESSGAIN1;
// Don't increase gain if FILTERED amplitude above threshold
if (Sample[Index].Filtered>AMP_LO)
Flags=Flags&(0xFF-MOREGAIN1);
}
// Apply phase calibration. Due to multiplexing, there will be a delay
// between subsequent data chennels, as follows:
//
// Delay = [ 360 degrees * f(mains) ] / [ 3 * f(sampling) ]
//
// At 2400Hz sampling rate, this means that fresh samples from ADC0
// and ADC1, for example, will actually have a time difference of
// 1/2400 = 0.42ms, which corresponds to (50*360)/2400 = 7.5 degrees
// in a 50Hz mains environment. In addition, current transformers in
// the analogue front end may typically contribute up to six degrees
// of additional phase lag. Phase distortions are calibrated using
// linear interpolation, as follows:
//
// Calibrated = x[n-1] + CalibCoefficient * ( x[n] - x[n-1] )
//
// Note that linear interpolation is the same as a fixed time delay.
// This means that higher frequency componentes will not be adjusted
// correctly. Typically, though, not enough energy is present in the
// higher frequency components to cause this to degrade accuracy below
// acceptable levels.
//
// Also note that the higher the sampling rate, the lower is the
// calibration range in degrees. If the sampling rate is too high,
// then the linear interpolation as such can not compensate for the
// phase lag of some DC immuned current transformers. Such
// transformers can have a phase lag in excess of six degrees.
TempL=Sample[Index].Filtered-Sample[Index].PreviousFiltered;
TempL=TempL*CalCoeff.PCC[Index];
TempL=TempL>>16;
Sample[Index].Calibrated=Sample[Index].Filtered-TempL;
// Save measurement data for voltage, current and active power on all
// channels. For active power measurements; accumulate instantaneous
// power product. For current and voltage measurements: accumulate
// square of samples
//
// Range check:
//
// Filtered data: Filtered Range=[FFFE0280...0001FD80]
// Prescaled data (>>5): TempX Range=[FFFFF014...00000FEC]
// Multiplication result: (n/a) Range=[FF027E70...00FD8190]
//
// Prescaled data (>>6): TempX Range=[FFFFF80A...000007F6]
// Multiplication result: (n/a) Range=[FFC09F9C...003F6064]
//
// Assuming that sampled data is stuck at full-scale (which it can't
// be, since the high-pass filter would bias such a signal to zero),
// then for accumulation purposes the maximum number of samples that
// can be integrated is:
//
// MaxSamples(presc=32) = 7FFFFFFFh / 00FD8190h = 0081h = 129d
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -