//
// main.c
//
// Watt Hour Meter 0.5b / Feel AC Power
// Copyright (C) 2010-2011 Toyohiko Togashi tog001@nifty.com
//
// This program is free software; you can redistribute it and/or modify it under the terms of the
// GNU General Public License as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along with this program.
// If not, see
//
//
// Description
// AC power indicator lamp, and output data.
// Online data transfer to USB port, record to SD card memory.
//
// Setting
// PIC18
// Device : PIC18F2550
// IDE : MPLAB IDE v8.36, C18 C Compiler v3.33(academic version)
// USBstack: Microchip Application Libraries v2009-11-18
// USBstack: Microchip Application Libraries v2010-10-19
// SDcm : SDcard control module mini v0.2
//
// Usage
// Connect USB plug to PC, or USB power adapter.
// Lamp on color from AC power.
// Show PC virtual serial port.
// Insert SDcard memory.(FAT16 format)
//
// SDcard output file 1:
// File name : "/WH.CSV"
// =year =month
// Record unit : 1 text line / 1 second.
// Format : CSV(camma separated value)
// SDcard output file 2:
// File name : "/WHM.CSV"
// Record unit : 1 text line / 1 hour.
// Format : CSV(camma separated value)
// Parameter file:
// File name : "/WHM.TXT"
// Format :
// +------------------------------
// |yy= [#]
// |mm= [#]
// |dd= [#]
// |hh= [#]
// |mi= [#]
// |ss= [#]
// |v0= [#]
// |v1= [#]
// | :
// |v5= [#]
// |iv= [#]
// |cal= [#]
// +------------------------------
// : Set now date, year
// : Set now date, month#
// : Set now date, day of month
// : Set now time, hour
// : Set now time, minits
// : Set now time, second
// : Channel N line voltage[V] (default=100)
// : CSV file data interval time[sec] (default=60)
// : Clock count adjust value
// : Option text
//
//
// Update history
// ---------- ----- -------------------------------------------
// 2010.02.18 v0.5 First cording
// 2010.02.20 v0.5 Save program memory, sqrt(float) -> lsqrt(long)
// 2010.02.21 v0.5 2nd CSV file, 1-hour interval
// 2010.02.22 v0.5 Save program memory, not use stdio(printf...)
// 2010.02.23 v0.5 Split CSV file
// 2010.02.24 v0.5 Over sampling, line frequency free
// 2010.02.25 v0.5 Save program memory, reduce call count
// 2010.02.26 v0.5 Fail safe software, enable watch dog timer
// 2010.02.26 v0.5 Power down a countermeasure, take shelter EEPROM
// 2010.02.27 v0.5 CSV interval: sec -> min(default)
// 2010.02.27 v0.5 First release
// 2010.03.01 v0.5a Bug fix, close timing day->month
// 2010.03.08 v0.5a Bug fix, include path C18->AppLib/project option
// 2011.02.08 v0.5b Support USB bootloader
// 2011.02.08 v0.5b Change to "Extend mode"
// 2011.02.08 v0.5b Bug fix, Timer1 parameters
// 2011.02.08 v0.5b Update library, Microchip Application Libraries v2010-10-19
// 2011.02.08 v0.5b Update library, SDcm v0.2
// 2011.02.08 v0.5b Update library, delay() v0.1
// 2011.02.08 v0.5b Disable BOR, stable SDcard I/O
// 2011.03.08 v0.5b Improve accuracy of clock, change IRQ priority, add "cal" parameter.
// 2011.03.08 v0.5b Expanded EEPROM save area.
// 2011.03.08 v0.5b Reset history.
//
//#include
#include
#include
#define USE_OR_MASKS
#include // C18 library
#include // C18 library
#include // C18 library
#include // C18 library
#include // C18 library
#include "HardwareProfile.h" // Library & application
#include "GenericTypeDefs.h" // Microchip Application Libraries
#include "Compiler.h" // Microchip Application Libraries
#include "usb_config.h" // Microchip Application Libraries
#include "USB/usb.h" // Microchip Application Libraries
#include "USB/usb_device.h" // Microchip Application Libraries
#include "USB/usb_function_cdc.h" // Microchip Application Libraries
#include "sd.h" // Private
//#include "lsqrt.h" // Private
//
// Application constant
//
#define IN_V 100 // [V] AC RMS
#define CH_COUNT 6 //
#define ADC_VREF 5 // [V]
#define ADC_RESOLUTION 1024 // 10bit
#define TMR0_PERIOD (CLOCK_FREQ/4/256/1 * TM_ADJ) // Date time 1.0Hz (Tosc/4/Prescale/TargetFreq)
#define TMR1_PERIOD (CLOCK_FREQ/4/1/800) // LED display refresh (Tosc/4/Prescale/TargetFreq)
//#define SAMP_MODE_1C // 1cycle capture mode
//#define SAMP_COUNT 128 // 50Hz:20[ms]
//#define SAMP_COUNT 107 // 60Hz:16.7[ms]
#define SAMP_MODE_OS // over sampling mode
#define SAMP_COUNT 3223 // 100[ms]
//#define SUMMARY // Optional function, 2nd CSV file
#define EEP_SAVE // Optional function, Continue Wh & DateTime
//
// Configure
//
// PIC18F2550
// #pragma config PLLDIV = 1 // 4 MHz crystal
// #pragma config PLLDIV = 2 // 8 MHz crystal
#pragma config PLLDIV = 3 // 12 MHz crystal
// #pragma config PLLDIV = 4 // 16 MHz crystal
// #pragma config PLLDIV = 5 // 20 MHz crystal
// #pragma config PLLDIV = 6 // 24 MHz crystal
// #pragma config PLLDIV = 12 // 48 MHz crystal
#pragma config CPUDIV = OSC1_PLL2 // Primary oscillator direct or 96MHz PLL/2=48MHz
// #pragma config CPUDIV = OSC2_PLL3 // Primary oscillator/2 or 96MHz PLL/3=32MHz
// #pragma config CPUDIV = OSC3_PLL4 // Primary oscillator/3 or 96MHz PLL/4=24MHz
// #pragma config CPUDIV = OSC4_PLL6 // Primary oscillator/4 or 96MHz PLL/6=16MHz
// #pragma config USBDIV = 1 // Directly from the primary oscillator block with no postscale
#pragma config USBDIV = 2 // 96 MHz PLL divided by 2
// #pragma config FOSC = XT_XT // XT oscillator, XT used by USB
// #pragma config FOSC = XTPLL_XT // XT oscillator, PLL enabled, XT used by USB
// #pragma config FOSC = ECIO_EC // External clock, port function on RA6, EC used by USB
// #pragma config FOSC = EC_EC // External clock, CLKOUT on RA6, EC used by USB
// #pragma config FOSC = ECPLLIO_EC// External clock, PLL enabled, port function on RA6, EC used by USB
// #pragma config FOSC = ECPLL_EC // External clock, PLL enabled, CLKOUT on RA6, EC used by USB
// #pragma config FOSC = INTOSCIO_EC// Internal oscillator, port function on RA6, EC used by USB
// #pragma config FOSC = INTOSC_EC // Internal oscillator, CLKOUT on RA6, EC used by USB
// #pragma config FOSC = INTOSC_XT // Internal oscillator, XT used by USB
// #pragma config FOSC = INTOSC_HS // Internal oscillator, HS used by USB
// #pragma config FOSC = HS // HS oscillator, HS used by USB
#pragma config FOSC = HSPLL_HS // HS oscillator, PLL enabled, HS used by USB
// #pragma config FCMEN = ON // Fail-Safe Clock Monitor enable
// #pragma config IESO = ON // Oscillator Switchover mode enable
// #pragma config PWRT = ON // Power-up Timer enable
// #pragma config BOR = OFF // Brown-out Reset disable and on/off software control
#pragma config BOR = SOFT // Brown-out Reset enable and on/off software control
// #pragma config BOR = ON_ACTIVE // Brown-out Reset enable and disabled in Sleep mode
// #pragma config BOR = ON // Brown-out Reset enable never
// #pragma config BORV = 0 // 4.59V(typ)
#pragma config BORV = 1 // 4.33V(typ)
// #pragma config BORV = 2 // 2.79V(typ)
// #pragma config BORV = 3 // 2.05V(typ)
#pragma config VREGEN = ON // USB Voltage Regulator enable
#pragma config WDT = OFF // Watchdog Timer disable and on/off software control
#pragma config WDTPS = 2048 // 31kHz/128/<2048>=8.4sec
// #pragma config CCP2MX = OFF // CCP2 = RB3 (ON=RC3)
// #pragma config PBADEN = OFF // PORTB A/D disable
#pragma config LPT1OSC = ON // Low-Power Timer 1 Oscillator enable
#pragma config MCLRE = OFF // RE3 enable
// #pragma config STVREN = OFF // Stack full/underflow will cause Reset disable
#pragma config LVP = OFF // Single-Supply ICSP disable
#pragma config XINST = ON // Extended Instruction Set enable
// #pragma config DEBUG = ON // Background debugger enabled, RB6 and RB7 are dedicated to In-Circuit Debug
// #pragma config CP0 = ON // Block 0 (000800-001FFFh) is code-protected
// #pragma config CP1 = ON // Block 1 (002000-003FFFh) is code-protected
// #pragma config CP2 = ON // Block 2 (004000-005FFFh) is code-protected
// #pragma config CP3 = ON // Block 3 (006000-007FFFh) is code-protected
// #pragma config CPB = ON // Boot block (000000-0007FFh) is code-protected
// #pragma config CPD = ON // Data EEPROM is code-protected
// #pragma config WRT0 = ON // Block 0 (000800-001FFFh) or (001000-001FFFh) is write-protected
// #pragma config WRT1 = ON // Block 1 (002000-003FFFh) is write-protected
// #pragma config WRT2 = ON // Block 2 (004000-005FFFh) is write-protected
// #pragma config WRT3 = ON // Block 3 (006000-007FFFh) is write-protected
// #pragma config WRTC = ON // Configuration registers (300000-3000FFh) are write-protected
#pragma config WRTB = ON // Boot block (000000-0007FFh) is write-protected
// #pragma config WRTD = ON // Data EEPROM is write-protected
// #pragma config EBTR0 = ON // Block 0 (000800-001FFFh) is protected from table reads executed in other blocks
// #pragma config EBTR1 = ON // Block 1 (002000-003FFFh) is protected from table reads executed in other blocks
// #pragma config EBTR2 = ON // Block 2 (004000-005FFFh) protected from table reads executed in other blocks
// #pragma config EBTR3 = ON // Block 3 (006000-007FFFh) protected from table reads executed in other blocks
// #pragma config EBTRB = ON // Boot block (000000-0007FFh) is protected from table reads executed in other blocks
//
// Global area
//
#ifdef SAMP_MODE_1C
#pragma udata ad_buf //
int ad_buf[SAMP_COUNT]; // ADC capture buffer
#pragma udata //
#endif
char buf[26]; //
struct { // EEPROM save area
int now_v[CH_COUNT]; // [V]
int now_i[CH_COUNT]; // [mA]
int now_p[CH_COUNT]; // [W]
long ws[CH_COUNT]; // [Ws]
int iv; // Logging interval [sec]
int tmr0_period; //
int yy; // 2000-
char mm; // 1-12
char dd; // 1-31
char hh; // 0-23
char mi; // 0-59
char ss; // 0-59
} eep = {
{IN_V, IN_V, IN_V, IN_V, IN_V, IN_V},
{0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0},
60,
TMR0_PERIOD,
2011, 3, 8,
0, 0, 0
};
//==============================================================================
//
// Real time clock
//
//==============================================================================
char ddom[] = {0, 31,28,31,30,31,30,31,31,30,31,30,31};
char tm[20];
void rtc_updt(void) {
// DateTime count up
eep.ss++;
if (eep.ss > 59) {
eep.ss = 0;
eep.mi++;
if (eep.mi > 59) {
eep.mi = 0;
eep.hh++;
if (eep.hh > 23) {
eep.hh = 0;
if (((eep.yy % 4) == 0) && ((eep.yy % 100) != 0)) {
ddom[2] = 29;
} else {
ddom[2] = 28;
}
eep.dd++;
if (eep.dd > ddom[eep.mm]) {
eep.dd = 1;
eep.mm++;
if (eep.mm > 12) {
eep.mm = 1;
eep.yy++;
}
}
}
}
}
return;
}
//==============================================================================
//
// LED display
//
//==============================================================================
#define LED_COL_MASK_A 0b00010000 // RA4
#define LED_COL_MASK_B 0b11111000 // RB3-7
#define LED_ROW_MASK 0b00000111 // RC0-2
#define LED_INIT() (PORTA |= LED_COL_MASK_A, PORTB |= LED_COL_MASK_B, PORTC |= LED_ROW_MASK, \
TRISA &= ~LED_COL_MASK_A, TRISB &= ~LED_COL_MASK_B, TRISC &= ~LED_ROW_MASK)
#define LED_COL_NUM 6
const unsigned char led_col_a[LED_COL_NUM] = {0b11101111,0b11111111,0b11111111,0b11111111,0b11111111,0b11111111};
const unsigned char led_col_b[LED_COL_NUM] = {0b11111111,0b11110111,0b11101111,0b11011111,0b10111111,0b01111111};
const unsigned char led_row[16][2] = {
{0b11111111,0b11111111}, // 0:black
{0b11111110,0b11111111}, // 1:dark red
{0b11111110,0b11111110}, // 2:red
{0b11111100,0b11111110}, // 3:orange
{0b11111100,0b11111100}, // 4:yellow
{0b11111100,0b11111101}, // 5:lime
{0b11111101,0b11111101}, // 6:green
{0b11111101,0b11111001}, // 7:blue green
{0b11111001,0b11111001}, // 8:light blue
{0b11111001,0b11111011}, // 9:sky blue
{0b11111011,0b11111011}, // 10:blue
{0b11111010,0b11111010}, // 11:purple
{0b11111010,0b11111110}, // 12:pink
{0b11111010,0b11111100}, // 13:?
{0b11111000,0b11111000}, // 14:white
{0b11111000,0b11111111} // 15:gray
};
unsigned char led_disp[LED_COL_NUM];
void led_refresh(void){ // Entry ISR
static unsigned char led_col_ctr;
static unsigned char led_col_flg;
// Off all
PORTA |= LED_COL_MASK_A;
PORTB |= LED_COL_MASK_B;
PORTC |= LED_ROW_MASK;
// Increment counter
if (led_col_ctr++ >= LED_COL_NUM) {
led_col_ctr = 0;
if (led_col_flg) {
led_col_flg = 0;
} else {
led_col_flg = 1;
}
}
// On
PORTA &= led_col_a[led_col_ctr];
PORTB &= led_col_b[led_col_ctr];
PORTC &= led_row[led_disp[led_col_ctr]][led_col_flg];
return;
}
//==============================================================================
//
// USB Device support (Microchip library)
//
//==============================================================================
// Callbacks
void USBCBSuspend(void) {
}
void USBCBWakeFromSuspend(void) {
}
void USBCB_SOF_Handler(void) {
}
void USBCBErrorHandler(void) {
}
void USBCBCheckOtherReq(void) {
USBCheckCDCRequest();
}
void USBCBStdSetDscHandler(void) {
}
void USBCBInitEP(void) {
CDCInitEP();
}
void USBCBSendResume(void) {
WORD delay_count;
USBResumeControl = 1; // Start RESUME signaling
delay_count = 1800U; // Set RESUME line for 1-13 ms
do {
delay_count--;
} while(delay_count);
USBResumeControl = 0;
}
#if defined(ENABLE_EP0_DATA_RECEIVED_CALLBACK)
void USBCBEP0DataReceived(void) {
}
#endif
BOOL USER_USB_CALLBACK_EVENT_HANDLER(USB_EVENT event, void *pdata, WORD size) {
switch(event) {
case EVENT_CONFIGURED:
USBCBInitEP();
break;
case EVENT_SET_DESCRIPTOR:
USBCBStdSetDscHandler();
break;
case EVENT_EP0_REQUEST:
USBCBCheckOtherReq();
break;
case EVENT_SOF:
USBCB_SOF_Handler();
break;
case EVENT_SUSPEND:
USBCBSuspend();
break;
case EVENT_RESUME:
USBCBWakeFromSuspend();
break;
case EVENT_BUS_ERROR:
USBCBErrorHandler();
break;
case EVENT_TRANSFER:
Nop();
break;
default:
break;
}
return TRUE;
}
// Private API
void usb_puts(char *buf) {
if ((USBDeviceState >= CONFIGURED_STATE) && (USBSuspendControl != 1)) {
int i;
for(i = 1000; i > 0; --i) {
if(mUSBUSARTIsTxTrfReady()) break;
CDCTxService();
}
if (i > 0) {
putsUSBUSART(buf);
CDCTxService();
}
}
return;
}
#ifdef DELETE_CODE
// Redirect USB to stdio.h (Only C18, need main.c "stdout = _H_USER;")
int _user_putc(char c) {
char buf[2];
buf[0] = c;
buf[1] = '\0';
usb_puts(buf);
return(c);
}
#endif
//==============================================================================
//
// Intrrupt service routine
//
//==============================================================================
#pragma interrupt HighPriorityISRCode
void HighPriorityISRCode() {
if (INTCONbits.TMR0IE && INTCONbits.TMR0IF) {
WriteTimer0(-eep.tmr0_period);
rtc_updt();
INTCONbits.TMR0IF = 0;
} else {
USBDeviceTasks();
}
}
#pragma interruptlow LowPriorityISRCode
void LowPriorityISRCode() {
if (PIE1bits.TMR1IE && PIR1bits.TMR1IF) {
WriteTimer1(-TMR1_PERIOD);
led_refresh();
PIR1bits.TMR1IF = 0;
} else {
USBDeviceTasks();
}
}
// ADD START 2011.02.08
//==============================================================================
//
// Vector table
//
//==============================================================================
#ifdef DELETE_CODE
#pragma code REMAPPED_HIGH_INTERRUPT_VECTOR = 0x08
void Remapped_High_ISR (void) {
_asm goto HighPriorityISRCode _endasm
}
//#pragma code REMAPPED_LOW_INTERRUPT_VECTOR = 0x18
//void Remapped_Low_ISR (void) {
// _asm goto LowPriorityISRCode _endasm
//}
#pragma code
#endif // DELETE_CODE
#if defined(PROGRAMMABLE_WITH_USB_HID_BOOTLOADER)
#define REMAPPED_RESET_VECTOR_ADDRESS 0x1000
#define REMAPPED_HIGH_INTERRUPT_VECTOR_ADDRESS 0x1008
#define REMAPPED_LOW_INTERRUPT_VECTOR_ADDRESS 0x1018
#elif defined(PROGRAMMABLE_WITH_USB_MCHPUSB_BOOTLOADER)
#define REMAPPED_RESET_VECTOR_ADDRESS 0x800
#define REMAPPED_HIGH_INTERRUPT_VECTOR_ADDRESS 0x808
#define REMAPPED_LOW_INTERRUPT_VECTOR_ADDRESS 0x818
#else
#define REMAPPED_RESET_VECTOR_ADDRESS 0x00
#define REMAPPED_HIGH_INTERRUPT_VECTOR_ADDRESS 0x08
#define REMAPPED_LOW_INTERRUPT_VECTOR_ADDRESS 0x18
#endif
#if defined(PROGRAMMABLE_WITH_USB_HID_BOOTLOADER)||defined(PROGRAMMABLE_WITH_USB_MCHPUSB_BOOTLOADER)
extern void _startup (void); // See c018i.c in your C18 compiler dir
#pragma code REMAPPED_RESET_VECTOR = REMAPPED_RESET_VECTOR_ADDRESS
void _reset (void){
_asm goto _startup _endasm
}
#endif
#pragma code REMAPPED_HIGH_INTERRUPT_VECTOR = REMAPPED_HIGH_INTERRUPT_VECTOR_ADDRESS
void Remapped_High_ISR (void){
_asm goto HighPriorityISRCode _endasm
}
#pragma code REMAPPED_LOW_INTERRUPT_VECTOR = REMAPPED_LOW_INTERRUPT_VECTOR_ADDRESS
void Remapped_Low_ISR (void){
_asm goto LowPriorityISRCode _endasm
}
#if defined(PROGRAMMABLE_WITH_USB_HID_BOOTLOADER)||defined(PROGRAMMABLE_WITH_USB_MCHPUSB_BOOTLOADER)
#pragma code HIGH_INTERRUPT_VECTOR = 0x08
void High_ISR (void){
_asm goto REMAPPED_HIGH_INTERRUPT_VECTOR_ADDRESS _endasm
}
#pragma code LOW_INTERRUPT_VECTOR = 0x18
void Low_ISR (void){
_asm goto REMAPPED_LOW_INTERRUPT_VECTOR_ADDRESS _endasm
}
#endif
#pragma code
// ADD END 2011.02.08
//==============================================================================
//
// Etc subroutines
//
//==============================================================================
void utoan(unsigned int i, char *a, char n) {
char *p;
p = a + n;
*p = '\0';
while(--n >= 0) {
*(--p) = i % 10 + '0';
i /= 10;
if (i == 0) break;
}
while(--n >= 0) {
*(--p) = ' ';
}
return;
}
void ultoan(unsigned long i, char *a, char n) {
char *p;
p = a + n;
*p = '\0';
while(--n >= 0) {
*(--p) = i % 10 + '0';
i /= 10;
if (i == 0) break;
}
while(--n >= 0) {
*(--p) = ' ';
}
return;
}
int sd_gets(int fd, char *buff, int sz){
char c[1];
int n;
while((n = sd_read(fd, c, sizeof(c))) > 0) {
if (sz > 1) {
--sz;
*buff++ = c[0];
}
if (c[0] == '\n') {
break;
}
}
*buff = '\0';
return(n);
}
void sd_puthead(int fd) {
char ch;
memcpypgm2ram(buf, "TimeStamp", 9);
sd_write(fd, buf, 9);
buf[0] = ',';
buf[1] = 'C';
buf[2] = 'H';
for(ch = 0; ch < CH_COUNT; ch++) {
utoan(ch, &buf[3], 1);
buf[4] = '[';
buf[5] = 'V';
buf[6] = ']';
sd_write(fd, buf, 7);
// buf[4] = '[';
buf[5] = 'A';
// buf[6] = ']';
sd_write(fd, buf, 7);
// buf[4] = '[';
buf[5] = 'W';
// buf[6] = ']';
sd_write(fd, buf, 7);
// buf[4] = '[';
// buf[5] = 'W';
buf[6] = 'h';
buf[7] = ']';
sd_write(fd, buf, 8);
}
buf[0] = '\r';
buf[1] = '\n';
sd_write(fd, buf, 2);
return;
}
void sd_putdata(int fd) {
char ch;
buf[0] = '"';
memcpy((void *)&buf[1], (void *)tm, 19);
buf[20] = '"';
sd_write(fd, buf, 21);
for(ch = 0; ch < CH_COUNT; ch++) {
utoan(eep.now_v[ch], &buf[ 1], 3);
utoan(eep.now_i[ch]/1000, &buf[ 5], 2);
utoan(eep.now_i[ch]+1000, &buf[ 8], 3);
utoan(eep.now_p[ch], &buf[12], 4);
ultoan(eep.ws[ch]/3600, &buf[17], 7);
buf[ 0] = ',';
buf[ 4] = ',';
buf[ 7] = '.';
buf[11] = ',';
buf[16] = ',';
sd_write(fd, buf, 24);
}
buf[0] = '\r';
buf[1] = '\n';
sd_write(fd, buf, 2);
return;
}
//==============================================================================
//
// M A I N
//
//==============================================================================
void main(void){
static char cfg_fname[] = "WHM.TXT"; // static->save stack
static char csv_fname[13] = "WHyymmdd.CSV"; // static->save stack
static char mf; // static->save stack
static int fd; // static->save stack
static char mm_sv; // static->save stack
static char dd_sv; // static->save stack
static char hh_sv; // static->save stack
static int ic; // static->save stack
#ifdef SUMMARY
static char csv_fname2[] = "WHM.CSV"; // static->save stack
static int fd2; // static->save stack
#endif //SUMMARY
//
// Init
//
OSCCONbits.IDLEN = 1; // No stop peripherals clock on SLEEP
INTCONbits.GIE = 1; //
INTCONbits.PEIE = 1; //
RCONbits.SBOREN = 0; //
RCONbits.IPEN = 1; //
IPR1bits.TMR1IP = 0; //
IPR2bits.USBIP = 0; //
mf = 0;
fd = -1;
#ifdef SUMMARY
fd2 = -1;
#endif //SUMMARY
mm_sv = 0;
dd_sv = 0;
hh_sv = 0;
ic = 0;
// LED start
LED_INIT();
OpenTimer1(TIMER_INT_ON | T1_16BIT_RW | T1_SOURCE_INT | T1_PS_1_1 | T1_OSC1EN_OFF ); // Display refresh from intrrupt
// USB start
USBDeviceInit();
USBDeviceAttach();
// stdout = _H_USER; // CDC -> printf()
// RTC start
OpenTimer0(TIMER_INT_ON | T0_16BIT | T0_SOURCE_INT | T0_PS_1_256);
// Get saved data
#ifdef EEP_SAVE
if (Read_b_eep(0) != 0xff) {
unsigned char *p;
unsigned int i;
p = (unsigned char *)&eep;
for(i = 0; i < sizeof(eep); i++) {
Busy_eep();
*p++ = Read_b_eep(i);
}
mm_sv = eep.mm;
}
#endif
//
// 1sec interval loop
//
for(;;) {
unsigned char ch;
ClrWdt(); // Fail safe
//
// Hour task
//
{
if (hh_sv != eep.hh) {
hh_sv = eep.hh;
#ifdef EEP_SAVE
{
unsigned char *p;
unsigned int i;
p = (unsigned char *)&eep;
for(i = 0; i < sizeof(eep); i++) {
Busy_eep();
Write_b_eep(i, *p++);
}
}
#endif
}
}
//
// Daily task
//
// {
// if (dd_sv != eep.dd) {
// dd_sv = eep.dd;
// }
// }
//
// Monthly task
//
{
if (mm_sv != eep.mm) {
mm_sv = eep.mm;
// Change detail file
if (fd >= 0) {
sd_close(fd);
fd = -1;
}
// Clear Wh count
for(ch = 0; ch < CH_COUNT; ch++) {
eep.ws[ch] = 0;
}
}
}
//
// Measure
//
for(ch = 0; ch < CH_COUNT; ch++) {
const unsigned char in_ch[] = {ADC_CH0, ADC_CH1, ADC_CH2, ADC_CH3, ADC_CH4, ADC_CH8};
static float eo; // CT[V] static->save stack
static float io; // Line current[A] static->save stack
static int p; // Line power[W] static->save stack
// static int eo; // CT[mV] static->save stack
// static int io; // Line current[mA] static->save stack
// static int p; // Line power[W] static->save stack
static long sum; // ADC value static->save stack
int i;
// Capture CT output
OpenADC(ADC_FOSC_64 | ADC_RIGHT_JUST | ADC_2_TAD,
in_ch[ch] | ADC_INT_OFF | ADC_REF_VDD_VSS,
ADC_9ANA);
// Delay10TCYx(5);
#ifdef SAMP_MODE_1C
ConvertADC();
for(i = 0; i < SAMP_COUNT; i++) {
unsigned char k;
unsigned int a;
a = 0;
for(k = 0; k < 8; k++) {
while(BusyADC());
a += ReadADC();
ConvertADC();
}
ad_buf[j] = a >> 3; // a/8 (Average)
}
sum = 0;
for(i = 0; i < SAMP_COUNT; i++) {
sum += ((long)ad_buf[i] * (long)ad_buf[i]);
}
#endif
#ifdef SAMP_MODE_OS
sum = 0;
ConvertADC();
for(i = 0; i < SAMP_COUNT; i++) {
unsigned int a;
while(BusyADC());
a = ReadADC();
sum += ((long)a * a);
ConvertADC();
}
#endif
CloseADC();
// Calc Eo (A/D -> RMS voltage)
eo = sqrt((float)sum * 2.0 / SAMP_COUNT) * ADC_VREF / ADC_RESOLUTION;
// eo = lsqrt(sum * 2 / SAMP_COUNT) * 1000 * ADC_VREF / ADC_RESOLUTION; // [mV]
// Calc Io
io = (CT_N * eo) / (CT_K * CT_RL);
// io = ((long)CT_N * (long)eo) / (int)(CT_K * CT_RL); // [mA]
// Calc Power
p = io * eep.now_v[ch];
// p = (long)io * eep.now_v[ch] / 1000; // [W]
// Record
eep.now_i[ch] = io * 1000.0; // [mA]
// eep.now_i[ch] = io; // [mA]
eep.now_p[ch] = p;
eep.ws[ch] += p;
}
//
// Output LED
//
for(ch = 0; ch < CH_COUNT; ch++) {
const int watt[] = {0, 3, 10, 30,100,300,1000,3000,10000, -1};
const unsigned char color[] = {0, 8, 7, 6, 5, 4, 3, 2, 11, 10};
char x;
for(x = 0; watt[x] >= 0; x++) {
if (eep.now_p[ch] <= watt[x]) {
break;
}
}
led_disp[ch] = color[x];
}
//
// Output USB
//
{
// printf("%4d-%02d-%02d %02d:%02d:%02d", eep.yy, eep.mm, eep.dd, eep.hh, eep.mi, eep.ss);
utoan(eep.yy, &tm[ 0], 4);
utoan((unsigned char)eep.mm+100, &tm[ 5], 2);
utoan((unsigned char)eep.dd+100, &tm[ 8], 2);
utoan((unsigned char)eep.hh+100, &tm[11], 2);
utoan((unsigned char)eep.mi+100, &tm[14], 2);
utoan((unsigned char)eep.ss+100, &tm[17], 2);
tm[ 4] = '-';
tm[ 7] = '-';
tm[10] = ' ';
tm[13] = ':';
tm[16] = ':';
tm[19] = '\0';
usb_puts(tm);
for(ch = 0; ch < CH_COUNT; ch++) {
// printf(" CH%d %dW %ldWh", ch, eep.now_p[ch], eep.ws[ch]/3600);
utoan(ch, &buf[ 0], 5);
utoan(eep.now_p[ch], &buf[ 5], 5);
ultoan(eep.ws[ch]/3600, &buf[11], 8);
buf[ 2] = 'C';
buf[ 3] = 'H';
buf[10] = 'W';
buf[19] = 'W';
buf[20] = 'h';
buf[21] = '\0';
usb_puts(buf);
}
// printf("\r\n");
buf[0] = '\r';
buf[1] = '\n';
buf[2] = '\0';
usb_puts(buf);
}
//
// Output SDcard
//
// Count down
if ((ic <= 0) || (ic > eep.iv)) {
ic = eep.iv;
}
--ic;
// Insert
if (SD_DETECT) {
// Mount
if (!mf) {
if (sd_mount(0) >= 0) {
mf = 1;
// Read parameters
fd = sd_open(cfg_fname, 0, 0);
if (fd >= 0) {
while(sd_gets(fd, buf, sizeof(buf))){
const rom char *const prm[] = {
"v0", "v1", "v2", "v3", "v4", "v5",
"yy", "mm", "dd", "hh", "mi", "ss",
"iv",
"cal",
NULL };
char *p; // static->save stack
int v; // static->save stack
char i; // static->save stack
// p = strtokpgmram(buf, "= \t\n");
// v = atoi(strtokpgmram(NULL, ", \t\n#"));
p = buf; buf[2] = '\0';
v = atoi(&buf[3]);
for(i = 0; prm[i] != NULL; i++) {
if (!strcmppgm2ram(p, prm[i])) {
break;
}
}
if (prm[i] != NULL) {
switch(i) {
case 6:
eep.yy = v;
break;
case 7:
eep.mm = v;
break;
case 8:
eep.dd = v;
break;
case 9:
eep.hh = v;
break;
case 10:
eep.mi = v;
break;
case 11:
eep.ss = v;
break;
case 12:
eep.iv = v;
break;
case 13:
eep.tmr0_period = TMR0_PERIOD + v;
break;
default:
eep.now_v[i] = v;
break;
}
}
}
sd_close(fd);
fd = -1;
led_disp[1] = 6; // Green wink
continue; // Restart
}
}
}
SD_SET_DATE(eep.yy, eep.mm, eep.dd);
SD_SET_TIME(eep.hh, eep.mi, eep.ss);
// Open Detail file
if (fd < 0) {
csv_fname[2] = tm[0];
csv_fname[3] = tm[1];
csv_fname[4] = tm[2];
csv_fname[5] = tm[3];
csv_fname[6] = tm[5];
csv_fname[7] = tm[6];
fd = sd_open(csv_fname, SD_O_CREAT|SD_O_APPEND, 0);
if (fd >= 0) {
sd_puthead(fd);
sd_sync(fd);
led_disp[2] = 6; // Green wink
} else {
led_disp[2] = 2; // Red wink
}
}
// Write
if (fd >= 0) {
if (ic <= 0) {
sd_putdata(fd);
}
if (eep.ss == 0) {
sd_sync(fd);
}
}
#ifdef SUMMARY
// Summary data
if (fd2 < 0) {
fd2 = sd_open(csv_fname2, SD_O_CREAT|SD_O_APPEND, 0);
if (fd2 >= 0) {
sd_puthead(fd2);
sd_sync(fd2);
led_disp[5] = 6; // Green wink
} else {
led_disp[5] = 2; // Red wink
}
}
{
static char sv = 0;
if (sv != eep.hh) {
sv = eep.hh;
if (fd2 >= 0) {
sd_putdata(fd2);
sd_sync(fd2);
}
}
}
#endif //SUMMARY
// Eject
} else {
#ifdef SUMMARY
if (fd2 >= 0) {
sd_close(fd2);
fd2 = -1;
led_disp[5] = 9; // Sky blue wink
}
#endif //SUMMARY
if (fd >= 0) {
sd_close(fd);
fd = -1;
led_disp[2] = 9; // Sky blue wink
}
if (mf) {
sd_unmount();
mf = 0;
}
}
//
// Interval wait
//
{
char t;
t = eep.ss;
while(t == eep.ss) {
Sleep();
Nop();
}
}
}
// USBDeviceDetach();
// for(;;);
}