ADE7756 Aktif Enerji Monitor Chip'i

Başlatan thenorthstar, 28 Ekim 2012, 11:31:02

thenorthstar

Merhaba Arkadaşlar;
Elektronik sayaçlarda kullanılan ADE7756 entegresi ile ilgili # ipek  Arkadaşımın yönlendirmesi ile slicon chip dergisindeki enerji ölçüm devresinin programını buldum, 16F88 e göre yapılmış fakat derlerken yetersiz ROM alanı diye uyarı veriyor. bende 18F4620 ye göre derlemek istedim fakat asm kodlarında hata veriyor bu konudayardımcı olabilirmisiniz?

http://www.siliconchip.com.au/cms/A_102045/article.html


http://geoffg.net/Downloads/Energy_Meter/Energy%20Meter%20Firmware%20V2_3%20Source.zip
;/////////////////////////////////////////////////////////////////////////////////////////////////////////
////                                            Main.c                                               ////
////                                                                                                 ////
////                     This is the main source code file for the Energy Meter                      ////
////                                                                                                 ////
////          Geoff Graham                                                                           ////
////          Version 1.0   Jul 2007                                                                 ////
////          Version 1.1   Aug 2007 - Calibrated the clock                                          ////
////          Version 1.2   Sep 2007 - Altered the input scaling to match measurements               ////
////          Version 2.3   Mar 2009 - Altered version number for publication                        ////
////                                                                                                 ////
/////////////////////////////////////////////////////////////////////////////////////////////////////////

/*************************************************************************************************************
Development Environment
   To compile this you need:
    - CCS C Compiler Version 4.0 or higher ([url=http://www.ccsinfo.com]www.ccsinfo.com[/url])
    - Microchip MPLAB IDE Version 8.0 or higher ([url=http://www.microchip.com]www.microchip.com[/url])

Program summary:
 - Based on a Silicon Chip design in 2004
 
 - Timer0 is setup to fire every 4mS (250 times a second).  As timer0 is the only interrupt this is handled 
   within an assembly language global interrupt.  The interrupts are counted and every 250th (i.e. once a
   second) the current power is read from the ADE chip.
   
 - The rest of the program is a continuous loop which:
   -  Checks if a second has gone by and processes the data from the ADE chip.
   -  Checks if a button has been pressed and if so, updates the counters that control the display.
   -  Updates the display if either a second has passed or a key pressed.

***************************************************************************************************************/


#define 18f4620

#ifdef 18f4620
   #include <18f4620.h>
   #device PASS_STRINGS=IN_RAM 
   //#fuses INTRC_IO,NOWDT,NOPROTECT,MCLR,BROWNOUT, NOLVP, NODEBUG            // for debug without a crystal
   #fuses XT,NOWDT,NOPROTECT,MCLR,BROWNOUT, NOLVP      // for production
   // Clock Related Fuses (select one):
   //        LP – Low-Power Crystal
   //        XT – Crystal/Resonator
   //        HS – High-Speed Crystal/Resonator
   //        RC – External Resistor-Capacitor (RC) with FOSC/4 output on RA6
   //        RC_IO – External Resistor-Capacitor with I/O on RA6
   //        INTRC - Internal Oscillator with FOSC/4 output on RA6 and I/O on RA7
   //        INTRC_IO - Internal Oscillator with I/O on RA6 and RA7
   //        EC_IO – External Clock with I/O on RA6.
   // Int/Ext Clock Switchover:     IESO,NOIESO
   // Fail-Safe Clock Monitor:      FCMEN,NOFCMEN
   // Watch Dog Timer:              WDT, NOWDT
   // Protect Data Memory:          CPD,NOCPD
   // Protect Code:                 PROTECT,NOPROTECT
   // Use MCLR Pin for Reset:       MCLR, NOMCLR
   // Power-up Timer:               PUT,NOPUT
   // Brown-out Detect:             BROWNOUT,NOBROWNOUT
   // Low-Volt Programming Enable:  LVP, NOLVP
   // In-Circuit Debugger Mode:     DEBUG,NODEBUG
   // CCP1 Pin Selection:           CCPB0, CCPB3

   // processor specific hardware registers
   #byte indf = 0               // Indirect register
   #byte tmr0 = 1               // timer count register
   #byte status = 3            // The status register
   #byte fsr = 4               // Index to RAM
   #byte porta = 5               // Port A I/O register
   #byte portb = 6               // Port B I/O register
   #byte pclath = 0x0a            // Program counter high bits
   #byte intcon = 0x0b            // Basic interrupt control

   #use delay(clock=4000000)
#endif

#use fast_io(A)   
#use fast_io(B)

#define TRISA 0b11110000
#define TRISB 0b00001000

#define bit short      // makes better reading that short
#define word int16

/************************************************************************************************
Global memory locations for access within C code
************************************************************************************************/

int32 sec;
   
char stmp[32];               // temp storage for building strings

byte key_code;
byte key_down;

float watts;
float signed_watts;
float watts_max = 0;
float watts_min = 999999;
float kwh;

byte fn;
byte fnx;

bit timer_running = false;

/************************************************************************************************
Global memory locations for access within assembly code
************************************************************************************************/

byte save_w;                  // where to save the w register in an interrupt
#locate save_w=0x70               // this must be in common memory
#reserve 0x70, 0xf0, 0x170, 0x1f0   // reserve this spot in the other pages

byte save_status;               // where to save the status register in an interrupt
#locate save_status=0x71         // this must be in common memory
#reserve 0x71, 0xf1, 0x171, 0x1f1   // reserve this spot in the other pages

byte save_fsr;                  // where to save the status register in an interrupt
#locate save_fsr=0x72            // this must be in common memory
#reserve 0x72, 0xf2, 0x172, 0x1f2   // reserve this spot in the other pages

byte save_pclath;               // where to save the program counter high bits in an interrupt
#locate save_pclath=0x73         // this must be in common memory
#reserve 0x73, 0xf3, 0x173, 0x1f3   // reserve this spot in the other pages

word key_cnt;                  // counter used in timing key presses
#locate key_cnt=0x20

#reserve 0x22:0x26               // reserve all 40 bits for incomming data
signed int32 rd;               // active power data read from chip
#locate rd=0x22
#byte rd1=0x22
#byte rd2=0x23
#byte rd3=0x24
#byte rd4=0x25
#byte rd5=0x26

byte store2;                  // temp store for assembly language
#locate store2=0x27

byte sec_flag;                  // flag that a new second has occured
#locate sec_flag=0x28

byte sec_cnt;                  // used to determine when a new second occurs
#locate sec_cnt=0x29

// RAM storage for the various configuration values
// these are defined together to make it easy to save them in eeprom
#define CONFIG_DATA_START 0x30
#define CONFIG_DATA_SIZE 8
signed word zero;
#locate zero = CONFIG_DATA_START
signed word cal;
#locate cal = CONFIG_DATA_START + 2
word cost;
#locate cost = CONFIG_DATA_START + 4
byte co2_rate;
#locate co2_rate = CONFIG_DATA_START + 6
byte bill_days;
#locate bill_days = CONFIG_DATA_START + 7


#include "LCD_Driver.c"


/************************************************************************************************
Declaration of functions
************************************************************************************************/
byte CheckKey();
void MsgLine(byte msg);
void MsgLineFloat(byte prefix_nbr, float nbr, byte decimal_points, byte suffix_nbr);
void UpdateDisplay();
void update_config_data();
byte CopyEEPROMString(byte msg_nbr, byte offset);
void PrintString(byte line);
void SendCommand(bit s16bit, byte reg, word data);

/************************************************************************************************
Strings stored in program flash memory or the eeprom
************************************************************************************************/

// initilise the eeprom configuration data
#rom  0x2100 = {0x00, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x63, 0x5a}                 

// to save space store strings in the eeprom
#define MSG_START 8               // offset for the start of messages
#rom  0x2108 =  {"",               // 0
            "Power Draw",         // 1
            "Recording Time",      // 2
            "Energy Consumed",      // 3
            "Predicted Usage",      // 4
            "CO2 Emissions",      // 5
            "Cost per kWh",         // 6
            "Billing Days",         // 7
            "Max: ",            // 8
            "Min: ",            // 9
            " W",               // 10
            " kWh",               // 11
            "$",               // 12
            " Kg CO2",            // 13
            "/bill",            // 14
            "/year",            // 15
            "/hour",            // 16
            " tonne/y",            // 17
            " Kg/kWh",            // 18
            "PRESS CLEAR TO",      // 19
            "START RECORDING",      // 20
            "ENERGY METER 2.3",      // 21
            "  Enhanced  ",         // 22
            "CLEAR",            // 23
            " kW/hour",            // 24
            " Watts"            // 25
          }


   
/************************************************************************************************
Global Interrupt
************************************************************************************************/
#INT_GLOBAL
void MyGlobal() {
   
   #asm
   ;
   ; first save the sensitive processor registers
   ;
      movwf   save_w      ; w to w_tmp storage
      swapf   status,w   ; status to w
      movwf   save_status   ; status in status_tmp 
      clrf      status      ; select bank 00 
      bcf       intcon,2   ; clear TMRO interrupt flag
      movf   tmr0,w      ; timer value   
                     ; freq is 4MHz/4/16/(256(-8+2))=250Hz
      nop
      nop               ; synchronise counter ie add two cycles
      addlw   0x8         ; add to timer register and takes 2 cycles to start counting
      movwf   tmr0      ; place in timer (the two cycles plus 2-cycle delay = 4cycles)
      
      movf   fsr,w      ; save fsr
      movwf   save_fsr
      movf   pclath,w   ; save fsr
      movwf   save_pclath
      clrf   pclath

   #endasm
      key_cnt++;
      
      sec_cnt++;
      if(sec_cnt == 252) {   // this number is used to calibrate the clock
                        // the value is determined experimentally, the standard value is 250
         sec_cnt = 0;
         sec_flag = 1;
            
   #asm
;   
; load the command to read RSTENERGY from the ADE7756
;   
      movlw   0x03      //; RSTENERGY register address (ms bit low for read)
      movwf   rd5
      movlw   0x08      //; number of bits
      movwf   store2
      bcf      portb,1     // ; cs low
NXT_BT:
      bcf      porta,3    //  ; data bit low initially
      rlf      rd5,f      //; get ms bit
      btfsc   status,0   //; check status bit
      bsf      porta,3     // ; set data bit if carry set
      bsf      portb,2     // ; clock high
      bcf      portb,2      //; clock data
      decfsz   store2,f   //; next bit
      goto   NXT_BT     // ; do all bits
//      bsf      portb,1      ; cs high
//;   
//; data read from the ADE7756
//;   
      movlw   40         ; we want to input 40 bits
      movwf   store2
//      bcf      portb,1      ; cs low
NXT_RD:
      bsf      portb,2      ; clock high
      bcf      portb,2      ; clock low
      bcf      status,0   ; clear carry
      btfsc   porta,4      ; check data
      bsf      status,0   ; set carry if data set
      rlf      rd1,f      ; ls register
      rlf      rd2,f
      rlf      rd3,f      ; 
      rlf      rd4,f
      rlf      rd5,f      ; data to ms register   
      decfsz   store2,f   ; do all bits
      goto   NXT_RD
      bsf      portb,1      ; cs high
   #endasm
   
      }      
   #asm

   ;
   ; restore registers and return
   ;
      movf   save_pclath, w
      movwf   pclath
      movf   save_fsr, w
      movwf   fsr
      swapf   save_status,w; status temp storage to w
      movwf   status      ; w to status register
      swapf   save_w,f   ; swap upper and lower 4-bits in w_tmp
      swapf   save_w,w   ; swap bits and into w register
      retfie            ; return from interrupt
   #endasm
}


/**********************************************************************************************
Main program
**********************************************************************************************/

void main() {
   byte i;
   
   //setup_oscillator(OSC_4MHZ);
   set_tris_a(TRISA);
   set_tris_b(TRISB);
   
   port_b_pullups(0);                        // disable pullups
   output_b(1);                           // init port b
   setup_comparator(NC_NC_NC_NC);               // disable comparitor

   delay_ms(100);
   lcd_init();                              // setup the lcd
   
   // show the startup message
   MsgLine(21);
   i = CopyEEPROMString(22, 0);
   PrintString(2);

   delay_ms(2000);

   // get stored values for zero, cal, cost, co2_rate, bill_days
   for(i = 0 ; i < CONFIG_DATA_SIZE ; i++) *(CONFIG_DATA_START + i) = read_eeprom(i);
   
   // setup the ADE chip
   SendCommand(0, 0x8a, 0b01010000);            // CH2 Gain = 4, FS = 0.25V, CH1 Gain = 1
   SendCommand(1, 0x86, 0x0004);               // High pass enabled
   SendCommand(1, 0x8b, cal);                  // Set the gain
   SendCommand(1, 0x8d, zero);                  // Set the zero

   setup_timer_0(RTCC_INTERNAL | RTCC_DIV_16);      // setup timer 0
   enable_interrupts(INT_TIMER0);               // enable timer 0 interrupt
   enable_interrupts(GLOBAL);                  // and global interrupt
   
   delay_ms(100);
   
   key_code = key_down = 0;
   fn = 1;
   fnx = 0;


   while (true) {

      // check the seconds flag and process if necessary
      if(sec_flag) {
         sec_flag = false;
         sec++;
         signed_watts = (float)rd / (float)472149; // 472149 is a calibration constant determined by experiment
         if(!bit_test(rd5, 7)) {                 // only accumulate power if positive reading
            watts = signed_watts;
            kwh += watts/3600000;
            if(watts > watts_max) watts_max = watts;
            if(watts < watts_min) watts_min = watts;
         } else 
            watts = 0;
         if(key_down != 4) UpdateDisplay();
      }

      // check for key press and process accordingly
      key_code = CheckKey();
      if(key_code) {
         switch(key_code) {
            case 3: fn++;                           // function key
               if(fn == 6 || fn == 11 || (fn == 3 & !timer_running)) fn = 1;
               fnx = 0;
               break;
         
            case 2: if(fn < 6)                         // down key
                  fnx--;
               else {
                  if(fn == 6) cost--;
                  if(fn == 7) bill_days--;
                  if(fn == 8) co2_rate--;
                  if(fn == 9) zero--;
                  if(fn == 10) cal--;
                  update_config_data();               // save the values
               }
               break;
         
            case 1: if(fn < 6)                         // up key
                  fnx++;
               else {
                  if(fn == 6) cost++;
                  if(fn == 7) bill_days++;
                  if(fn == 8) co2_rate++;
                  if(fn == 9) zero++;
                  if(fn == 10) cal++;
                  update_config_data();               // save the values
               }
               break;
         
            case 4:
               lcd_clear();
               MsgLine(23);                        // CLEAR
               break;
         
            case 5: fn = 6;                           // config key
               timer_running = false;
               break;
         }
         key_code = 0;
         UpdateDisplay();
      }
   }
}


      
// update the display if we have had a new second or keypress
void UpdateDisplay() {
   float kwh_per_hour;               // these temp values are used instead of recalculating
   float calc_hours;               // the same values - which takes up a lot of code space
   float kwh_per_hour_cost;
   float float_cost;
   
   if(key_down == 4) {               // clear key is held down
      kwh = 0;
      sec = 0;
      watts_max = watts;
      watts_min = watts;
      timer_running = true;
      if(fn >= 6) {fn = 1; fnx = 0;}
   } else {
      calc_hours = (float)sec / 3600;
      kwh_per_hour = kwh/calc_hours;
      float_cost = (float)cost/1000;
      kwh_per_hour_cost = kwh_per_hour * float_cost;
      
      switch(fn) {
         case 1: MsgLine(1);                              // Current Power
               if(fnx % 3 == 0)
                   MsgLineFloat(0, watts, 1, 25);            // current
               else if(fnx % 3 == 1)
                   MsgLineFloat(8, watts_max, 1, 10);         // max
               else
                   MsgLineFloat(9, watts_min, 1, 10);         // min
               break;
      
         case 2: if(!timer_running) {
                  MsgLine(19);                        // PRESS CLEAR
                  CopyEEPROMString(20, 0);
                  PrintString(2);
               } else {
                  MsgLine(2);
                  sprintf(stmp, "%Luh %02Lum", sec/3600, (sec/60) % 60);
                  PrintString(2);
               }
               break;
         
         case 3: MsgLine(3);                              // Energy Consumed
               if(fnx % 3 == 0)
                   MsgLineFloat(0, kwh, 3, 11);            // kwh
               else if(fnx % 3 == 1)
                   MsgLineFloat(12, kwh * float_cost, 2, 0);         // cost
               else
                   MsgLineFloat(0, kwh * (float)co2_rate/100, 0, 13);      // tonne C02
               break;
         
         case 4: MsgLine(4);                                             // Predicted Usage
               if(fnx % 4 == 0)
                   MsgLineFloat(12, kwh_per_hour_cost * (24 * 365), 2, 15);      // per year
               else if(fnx % 4 == 1)
                   MsgLineFloat(12, kwh_per_hour_cost * 24 * (float)bill_days, 2, 14);   // per bill
               else if(fnx % 4 == 2)
                   MsgLineFloat(12, kwh_per_hour_cost, 2, 16);                  // per hour
               else
                   MsgLineFloat(0, kwh_per_hour, 3, 24);                     // kWh
               break;
      
         case 5: MsgLine(5);                              // Cost per kWh
               MsgLineFloat(0, kwh_per_hour * (24 * 365) * (float)co2_rate/100000, 3, 17);   // tonne C02
               break;
         
         case 6: MsgLine(6);                              // Cost per kWh
               MsgLineFloat(12, float_cost, 3, 0);         // $ per kWh
               break;
      
         case 7: MsgLine(7);                              // Billing Days
               MsgLineFloat(0, (float)bill_days, 0, 0);
               break;
      
         case 8: MsgLine(5);                              // CO2 Emissions
               MsgLineFloat(0, (float)co2_rate/100, 2, 18);   // Kg/kWh
               break;
      
         case 9: lcd_clear();
               printf(lcd_putc, "Zero: %Ld", zero);         // Zero Set
               MsgLineFloat(0, signed_watts, 2, 10);         // current watts
               break;
      
         case 10:lcd_clear();
               printf(lcd_putc, "Cal: %Ld", cal);            // Calibration
               MsgLineFloat(0, signed_watts, 2, 10);         // current watts
               break;
      }
   }
}




/**************************************************************************************************
General subroutines
**************************************************************************************************/

// check if a key has been pressed and, if so, return the key nbr 
// 0 = no key, 1 to 4 indicating the key, or 5 = clear key held down
// designed to be called repeatedly within the main() loop
byte CheckKey() {
   byte i;
   bit got_key;
   static bit got_repeat;
   
   output_b(1);
   for(i = 1; i < 5; i++) {                                 // for each bit line check each key
      output_b((0b00001000 << i) | 1);                        // set the line high
      got_key = input(PIN_B3);                              // get key status
      output_b(1);                                       // and set low again
      if(got_key) {                                       // if we have a key
         if(key_down != 5) {
            if(key_down != i && key_cnt > 25) return (key_down = i);   // record a key press if passed debounce
            if(i < 3 && key_cnt > (got_repeat?60:250)) {
               got_repeat = true;
               goto no_key_exit;                            // will trigger a repeat key press
            }
            if(key_down == 4 && key_cnt > 1000) return (key_down = 5);   // record a configuration press
         }
         return 0;                                       // return no key at this stage
      }
   }
   got_repeat = false;
no_key_exit:
   key_down = 0;
   key_cnt = 0;                                          // reset counter if no key pressed
   return 0;
}


// Update the configuration data in the ADE chip and record in the eeprom
void update_config_data() {
   byte i;
   SendCommand(1, 0x8d, zero);         // Set the zero
   SendCommand(1, 0x8b, cal);         // Set the gain
   // write the configuration variables (stored in contiguous RAM) to the eeprom
   for(i = 0 ; i < CONFIG_DATA_SIZE ; i++) write_eeprom (i, *(CONFIG_DATA_START + i));
}


/**************************************************************************************************
SPI interface subroutines to send data to the ADE chip
**************************************************************************************************/

// send a byte to the ADE chip over the SPI interface
void SendByte(byte data) {
   byte i;
   for(i = 0 ; i < 8 ; i++) {                              // we will send 8 bits
      output_bit(PIN_A3, bit_test(data, 7));                  // output the bit on TX pin
      output_high(PIN_B2);                              // clock hi
      data <<= 1;                                       // get the next bit ready - also short delay
      output_low(PIN_B2);                                 // clock lo, the ADE chip will read the TX pin
   }
}


// send a command with data to be written to the ADE chip over the SPI interface
// the data can be 8 bit or 16 bit (s16bit = true)
void SendCommand(bit s16bit, byte reg, word data) {
   output_low(PIN_B1);                                    // chip select
   SendByte(reg);                                       // send the command
   if(s16bit) SendByte(data >> 8);                           // first byte if 16 bits
   SendByte(data);                                       // then the next byte
   output_high(PIN_B1);
}



/****************************************************************************************************
Display routines
These are involved with displaying data on the LCD and use a global array stmp[] in RAM as the buffer
****************************************************************************************************/

// generate enough spaces to ensure that the message will be centered
// can also be used after a message to blank any chars after the msg
void CenterMsg() {
   byte i;
   for(i = 0 ; stmp[i] != 0; i++);                  // find the length of the string in the global buffer
   if(i < 16) {
      i = 8 - (i >> 1);
      while(i--) lcd_putc(' ');                  // and put out enough spaces to center it
   }
}


// copy a string from eeprom into the global string buffer (stmp)
// offset is where, within stmp, to start copying
byte CopyEEPROMString(byte msg_nbr, byte offset) {
   byte i;
   for(i = MSG_START ; msg_nbr != 0 ; i++) if(read_eeprom(i) == 0) msg_nbr--;   // get the message
   for( ; read_eeprom(i) != 0; i++) stmp[offset++] = read_eeprom(i);         // copy the string
   stmp[offset] = 0;
   return offset;
}


// prints the string contained in the string buffer (stmp) on the lcd
void PrintString(byte line) {
   byte i;
   lcd_gotoxy(1, line);
   CenterMsg();                              // leading spaces
   for(i = 0 ; stmp[i] != 0; i++) lcd_putc(stmp[i]);   // the text
   CenterMsg();                              // trailing spaces to erase the rest of the line
}
   
      
// display a string in eeprom on the first line of the lcd (centered)
void MsgLine(byte msg_nbr) {
   byte i;
   i = CopyEEPROMString(msg_nbr, 0);
   PrintString(1);
}


// format a foating point number and display centered on the lcd
// the number can be prefixed or appended with strings from the eeprom
void MsgLineFloat(byte prefix_nbr, float nbr, byte decimal_points, byte suffix_nbr) {
   byte j;
   
   j = CopyEEPROMString(prefix_nbr, 0);            // copy the prefix
   sprintf(&stmp[j], "%01.3f", nbr);               // print the number into stmp[]
   while(stmp[j] != 0) j++;                     // find the end of the formatted string
   if(decimal_points == 0) j--;                  // also delete the decimal point
   j = CopyEEPROMString(suffix_nbr, j - (3 - decimal_points));            // copy the suffix
   PrintString(2);                              // display centered on line 2
}