Hola amigos de TodoPIC.
Hace algún tiempo prometi crear un nuevo tema para el dieño de un medidor de energía domiciliar, en esta ocasión decidí que el dispositivo que utilizaré como Analog Front-End (AFE) será el MCP3909 del fabricante Microchip.
Mi objetivo es utilizar este AFE en conjunto con un PIC18FXX5X que posee comunicación USB con la PC, lo que nos permite transmitir los datos almacenados durante un período de medición (Por lo general 1 mes) desde el medidor hacia la PC.
Estoy tomando como base el medidor trifásico propuesto en la Aplication-Note de Microchip que se encuentra en el siguiente link.
http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=1406&dDocName=en520559Hasta el momento solo he logrado decifrar una pequeña parte del código que nos brinda Microchip y me gustaría que me ayuden ya que tengo muchas dudas al respecto. A continuación muestro el código que he desarrollado hasta el momento.
Archivo: "Medidor de Energía MCP3909.h"Este archivo se utiliza para la declaración de los bits de configuración y la frecuencia de reloj que utilizaremos.
#FUSES NOWDT //No Watch Dog Timer
#FUSES WDT128 //Watch Dog Timer uses 1:128 Postscale
#FUSES XT //Crystal osc <= 4mhz for PCM/PCH , 3mhz to 10 mhz for PCD
#FUSES NOPROTECT //Code not protected from reading
#FUSES NOBROWNOUT //No brownout reset
#FUSES BORV20 //Brownout reset at 2.0V
#FUSES NOPUT //No Power Up Timer
#FUSES NOCPD //No EE protection
#FUSES STVREN //Stack full/underflow will cause reset
#FUSES NODEBUG //No Debug mode for ICD
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOWRT //Program memory not write protected
#FUSES NOWRTD //Data EEPROM not write protected
#FUSES IESO //Internal External Switch Over mode enabled
#FUSES FCMEN //Fail-safe clock monitor enabled
#FUSES PBADEN //PORTB pins are configured as analog input channels on RESET
#FUSES NOWRTC //configuration not registers write protected
#FUSES NOWRTB //Boot block not write protected
#FUSES NOEBTR //Memory not protected from table reads
#FUSES NOEBTRB //Boot block not protected from table reads
#FUSES NOCPB //No Boot Block code protection
#FUSES MCLR //Master Clear pin enabled
#FUSES LPT1OSC //Timer1 configured for low-power operation
#FUSES NOXINST //Extended set extension and Indexed Addressing mode disabled (Legacy mode)
#FUSES PLL1 //No PLL PreScaler
#FUSES CPUDIV4 //System Clock by 4
#FUSES USBDIV //USB clock source comes from PLL divide by 2
#FUSES VREGEN //USB voltage regulator enabled
#FUSES ICPRT //ICPRT enabled
#use delay(clock=48000000)
Archivo: "Variables_64bits.c"En este archivo he desarrollado con ayuda de Suky unas librerias que me seran utiles para la manipulacion de variables de 40 y 64 bits respectivamente. Aunque creo que sería mejor utilizar variables tipo float para la manipulación de los datos pero aún no estoy seguro.
///////////////////////////////////////////////////////////////////////////
//// ////
//// ////
//// (C) Copyright 2010 www.micros-designs.com.ar ////
//// Este código puede ser usado, modificado y distribuido libremente ////
//// sin eliminar esta cabecera y sin garantía de ningún tipo. ////
//// ////
//// ////
///////////////////////////////////////////////////////////////////////////
/*- Version Log --------------------------------------------------------------*
* Fecha Autor Comentarios *
*----------------------------------------------------------------------------*
* 16/08/10 Suky Original (Rev 1.0) *
*----------------------------------------------------------------------------* */
typedef union{
unsigned int8 mbyte[8];
unsigned int16 Word[4];
unsigned int32 Dword[2];
}UINT64;
typedef union{
unsigned int8 mbyte[5];
unsigned int16 Word[2];
unsigned int32 Dword[1];
}UINT40;
#define Zero64(x) x.Dword[0]=0;x.Dword[1]=0
#define IsZero64(x) ((x.Dword[0]==0)&&(x.Dword[1]==0))
#define Bit64_set(x,i) x.Byte[i>>3]|=(1<<(i&7))
void ui64Add64x64(uint64 &a, uint64 &b, uint64 &result1){
result1.Dword[0]=a.Dword[0]+b.Dword[0];
#ASM
MOVF b.mbyte[4],W
ADDWFC a.mbyte[4],W
MOVWF result1.mbyte[4]
MOVF b.mbyte[5],W
ADDWFC a.mbyte[5],W
MOVWF result1.mbyte[5]
MOVF b.mbyte[6],W
ADDWFC a.mbyte[6],W
MOVWF result1.mbyte[6]
MOVF b.mbyte[7],W
ADDWFC a.mbyte[7],W
MOVWF result1.mbyte[7]
#ENDASM
}
void ui64Sub64x64(UINT64 &a, UINT64 &b,UINT64 &result1){
result1.Dword[0]=a.Dword[0]-b.Dword[0];
#ASM
MOVF b.mbyte[4],W
SUBWFB a.mbyte[4],W
MOVWF result1.mbyte[4]
MOVF b.mbyte[5],W
SUBWFB a.mbyte[5],W
MOVWF result1.mbyte[5]
MOVF b.mbyte[6],W
SUBWFB a.mbyte[6],W
MOVWF result1.mbyte[6]
MOVF b.mbyte[7],W
SUBWFB a.mbyte[7],W
MOVWF result1.mbyte[7]
#ENDASM
}
void ui64Mult32x32(UINT64 &_a, UINT64 &_b, UINT64 &_result){
_result.Dword[0]=_a.Dword[0] * _b.Dword[0];
}
Archivo: "Declaraciones.h"En este archivo defino y declaro las variables que se utilizarán en el código.
#BIT MCP_MCLR = PORTA.0 // MCLR for all three MCP3909s
#BIT MCP_MCLR_dir = TRISA.0
#BIT MCP_HPF = PORTA.1
#BIT MCP_HPF_dir = TRISA.1
#BIT MCP_CS = PORTA.2 // Chip select for MCP3909
#BIT MCP_CS_dir = TRISA.2
#BIT MCP_G0 = PORTA.3 // Gain selection G0 for all three MCP3909s
#BIT MCP_G0_dir = TRISA.3
#BIT MCP_G1 = PORTA.4 // Gain selection G1 for all three MCP3909s
#BIT MCP_G1_dir = TRISA.4
int16 V,I, //Voltaje y corriente instantaneos
V_D1,I_D1, //Voltaje y corriente retrasado una muestra
V_P,I_P; //Voltaje y corriente para el calculo de Potencia
UINT40 V_WAcc, //40-bit working accumulator for voltage squared
I_WAcc; //40-bit working accumulator for current squared
Archivo: "Medidor de Energía MCP3909.c"Este es el archivo principal del programa.
#include <18f4550.h>
#include <Medidor Energia MCP3909.h>
#include <Puertos.c>
#include <variables_64bits.c>
#include <declaraciones.h>
#include <math.h>
#use spi(FORCE_HW, BITS=16)
void MCP_init(void){
MCP_CS=0;
MCP_MCLR=1;
delay_us(4);
spi_write(0xAC);
delay_us(2);
MCP_CS=1;
}
void Hardware_init(void){
TRISA=0;
TRISB=0;
TRISC=0;
DISABLE_INTERRUPTS(GLOBAL);
SETUP_ADC_PORTS(NO_ANALOGS);
PORTA=0;
PORTB=0;
PORTC=0;
MCP_CS=1;
SETUP_TIMER_0(T0_8_BIT|T0_INTERNAL|T0_DIV_4);
SET_TIMER0(0x78);
SETUP_TIMER_3(T3_INTERNAL|T3_DIV_BY_8);
SET_TIMER3(0);
SETUP_TIMER_2(T2_DIV_BY_1,5,1);
SET_PWM2_DUTY(3);
SETUP_CCP2(CCP_PWM);
setup_spi(SPI_MASTER|SPI_CLK_DIV_4|SPI_L_TO_H|SPI_SAMPLE_AT_END);
}
void main(){
Hardware_init();
MCP_init();
while(1);
}
A continuación muestro algunas dudas:
- Según la Nota de Aplicación de Microchip se deben medir el voltaje y la corriente instantaneos y a partir de estos valores calcular el desfase, la potencia activa, la potencia reactiva y la potencia aparente, por lo tanto me gustaría saber como se realizan estos cálculos.
- Será suficiente que utilice el PIC18Fxx5x sin necesidad de utilizar un segundo PIC como muestra la Nota de Aplicación.
- Sería más eficiente realizar cálculos con variables tipo Float de 32 bits que con variables de 40bits o 64bits
- Según la nota de aplicación se deben recojer aproximadamente 128 muestras del AFE, y si la frecuencia de la línea es de 60Hz entonces deberíamos recoger muestras cada 130us aproximadamente, entonces ¿Hay suficiente tiempo para realizar los cálculos?
- Para realizar el cálculo de la raíz cuadrada de un número se necesitan aproximadamente 1ms ¿Será necesario utilizar algún tipo de RTOS?
A continuación dejo los archivos adjuntos.