// LCメーター　Ｖ0.95
// PIC16F1938  LCD SC1602BSLB用
// 2017/08/05
// XC8 Compiler V1.38  Warning level -2にすること。

#pragma config FOSC = HS, WDTE = OFF, PWRTE = ON, MCLRE = ON, CP = OFF, CPD = OFF
#pragma config BOREN = OFF, CLKOUTEN = OFF, IESO = OFF, FCMEN = OFF
#pragma config WRT = OFF, VCAPEN = OFF, PLLEN = OFF, STVREN = OFF, BORV = HI, LVP = OFF



#include <xc.h>
#include <math.h>
#include <pic.h>
#include <string.h>
#include <stdlib.h>





#define _XTAL_FREQ 20000000
//  制御信号アドレス設定
#define LCD_PORT LATC		/* set LCD port */
#define LCD_E LATCbits.LATC3		/* set E bit of I/O port */
#define LCD_RS LATCbits.LATC2		/* set RS bit of I/O port */
#define LCD_CMD 0
#define LCD_DAT 1
#define CALrely LATAbits.LATA1 //CAL時H（1000PFをadd）
#define LED LATAbits.LATA5
#define LCs LATAbits.LATA4//C/L切り替え　1：C　0：L
#define CALkey PORTBbits.RB1
#define LCkey PORTBbits.RB2
#define LCmode PORTAbits.RA2 //optionハード未対応

unsigned long int Freq; //最新周波数

unsigned char lcd_data[16];
unsigned char lastRB2;

 char NoCount = 0;//周波数カウント中に割り込みなし：0　有り：1



double Lref;
double Cref, Ccal,Cx,Lx; //内蔵L,C,Ccalの値
double PI = 3.14159;

char CALed=0;//未CAL=0 CAL済=1
char CALflg=0;//CALキーが押されたら１
unsigned int VB;//Batt電圧
long int fcount0, fcount1; //Timer1のカウント実数
long int timer0;
long int Fnormal, Fcal, Fmeasure; //周波数用
long int AVF[4];
double DFnormal,DFcal,DFmeasure,VBd;

void lcd_strobe() {//LCD E ストローブ
    LCD_E = 1;
    __delay_us(1);
    LCD_E = 0;
}

void Putc_LCD(char c, char rs) {
    __delay_us(52); /* >40us */
    LCD_RS = rs;
    rs = LCD_PORT & 0x0f;
    LCD_PORT = ((c & 0xf0) + rs); /* E=0, RS=rs, high addr */
    lcd_strobe();
    LCD_PORT = ((c << 4) + rs); /* E=0, RS=rs, low addr */
    lcd_strobe();
}

void Init_LCD(void) {//LCDイニシャライズ

    __delay_ms(15);
    LCD_PORT = 0x30; /* E=0, RS=0, command=0x30 */
    lcd_strobe();
    __delay_ms(5); /* > 4.1ms */
    lcd_strobe();
    __delay_us(128); /* >100us */
    lcd_strobe();
    __delay_us(128);
    /* set 4bit mode */
    LCD_PORT = 0x20; /* E=0, RS=0, command=0x20 */
    lcd_strobe();
    Putc_LCD(0x28, LCD_CMD); /* display mode */
    Putc_LCD(0x0C, LCD_CMD); /* display on */
    Putc_LCD(0x01, LCD_CMD); //LCD クリアー
    __delay_ms(10);
    Putc_LCD(0x06, LCD_CMD); /* entry mode */
}

void Puts_LCD(const unsigned char *ptr) {//文字列を表示
    while (*ptr) {
        Putc_LCD(*ptr++, LCD_DAT);
    }
}

void Set_Pos(unsigned char x) {//1行目左端：0　2行目左端：40
    Putc_LCD(((x)&0x7F) | 0x80, LCD_CMD);
}


/*******************************************************************************
 *  LC Meter                                                                    *
 *                                                                              *
 *                                                                              *
 *  PIC 16F1939                                                                 *
 *  MPLAB X IDE(V3.35)                                                          *
 *  MPLAB(R) XC8 C Compiler Version 1.38                                        *
 *******************************************************************************/

/***************************************
 * int整数をASCII文字に変換
 ****************************************/
void itostring(char digit, unsigned long int data, char *buffer) {
    char i;

    buffer += digit;
    *buffer = 0; // 最後の数字位置
    for (i = digit; i > 0; i--) { // 変換は下位から上位へ
        buffer--; // ポインター１
        *buffer = (data % 10) + '0'; // ASCIIへ
        data = data / 10; // 次の桁へ
    }
}

void strset(const unsigned char *as, unsigned char *buf) {//文字列をLCD表示用ASCII文字に変換
    while (*as) *buf++ = *as++;
    *buf = 0x00;
}

unsigned char getdigits(unsigned long int s) {
    unsigned char dg = 1;
    unsigned long int su, sm;
    sm = 1;
    su = s;
    while (su > 9) {
        dg++;
        sm = sm * 10;
        su = s / sm;
    }
    return dg;
}
void VBdisp() {
     char dg;
    long int V1,V2;
    V1 = VB;
    V2 = V1/100;//整数部分
    V1 = V1-(V2*100);//小数点以下
    
    strset("Bat=      ", lcd_data);
    Set_Pos(0x47);
    Puts_LCD(lcd_data);
    strset("    V", lcd_data);
    Set_Pos(0x4B);
    Puts_LCD(lcd_data);
   // dg = getdigits(V2);
    itostring(1, V2, lcd_data); //V2をASCIIに変換
    Set_Pos(0x4B);
    Puts_LCD(lcd_data);
    strset(".",lcd_data);
    Set_Pos(0x4C);
    Puts_LCD(lcd_data);
    itostring(2, V1, lcd_data); //V1をASCIIに変換
    Set_Pos(0x4D);
    Puts_LCD(lcd_data);
     
  
}
void freqdisp(long int fq) {
    char dg;
    strset("Freq.       ", lcd_data);
    Set_Pos(0x40);
 //   Puts_LCD(lcd_data);
    strset(" Hz  ", lcd_data);
    Set_Pos(0x4B);
 //   Puts_LCD(lcd_data);
    dg = getdigits(fq);
    itostring(dg, fq, lcd_data); //FREQ値をASCIIに変換
    Set_Pos(0x40 + 7 - dg);
    Puts_LCD(lcd_data);
}
void Cxdisp1(double C) {
    char dg;
    long int C1,C2;
    C1 = ((long int)C + 50)/100;
    C2 = C1/10000;//整数部分
    C1 = C1 - (C2*10000);//小数点以下
    
    strset("Cx=       ", lcd_data);
    Set_Pos(0x00);
    Puts_LCD(lcd_data);
    strset("         uF", lcd_data);
    Set_Pos(0x05);
    Puts_LCD(lcd_data);
    dg = getdigits(C2);
    itostring(dg, C2, lcd_data); //C2をASCIIに変換
    Set_Pos(0x04 + 4 - dg);
    Puts_LCD(lcd_data);
    strset(".",lcd_data);
    Set_Pos(0x08);
    Puts_LCD(lcd_data);
    itostring(4, C1, lcd_data); //C1をASCIIに変換
    Set_Pos(0x09);
    Puts_LCD(lcd_data);
     
}
void Cxdisp0(double C) {
    char dg;
    long int C1,C2;
    C1 = (C * 1000. + 5) / 10;
    C2 = C1/100;//整数部分
    C1 = C1-(C2*100);//小数点以下
    
    strset("Cx=       ", lcd_data);
    Set_Pos(0x00);
    Puts_LCD(lcd_data);
    strset("         PF", lcd_data);
    Set_Pos(0x05);
    Puts_LCD(lcd_data);
    dg = getdigits(C2);
    itostring(dg, C2, lcd_data); //C2をASCIIに変換
    Set_Pos(0x04 + 6 - dg);
    Puts_LCD(lcd_data);
    strset(".",lcd_data);
    Set_Pos(0x0A);
    Puts_LCD(lcd_data);
    itostring(2, C1, lcd_data); //C1をASCIIに変換
    Set_Pos(0x0B);
    Puts_LCD(lcd_data);
     
}
void Lxdisp1(double L) {
    char dg;
    long int L1,L2;
    L1 = ((long int)L * 10 + 5) / 10;
    L2 = L1/1000;//整数部分
    L1 = L1-(L2*1000);//小数点以下
    strset("Lx=       ", lcd_data);
    Set_Pos(0x00);
    Puts_LCD(lcd_data);
    strset("         mH", lcd_data);
    Set_Pos(0x05);
    Puts_LCD(lcd_data);
    dg = getdigits(L2);
    itostring(dg, L2, lcd_data); //L2をASCIIに変換
    Set_Pos(0x05 + 4 - dg);
    Puts_LCD(lcd_data);
    strset(".",lcd_data);
    Set_Pos(0x09);
    Puts_LCD(lcd_data);
    itostring(3, L1, lcd_data); //L1をASCIIに変換
    Set_Pos(0x0A);
    Puts_LCD(lcd_data);
}

void Lxdisp0(double L) {
    char dg;
    long int L1,L2;
    L1 = (L * 1000. + 5) / 10;
    L2 = L1/100;
    L1 = L1-(L2*100);
    strset("Lx=       ", lcd_data);
    Set_Pos(0x00);
    Puts_LCD(lcd_data);
    strset("         uH", lcd_data);
    Set_Pos(0x05);
    Puts_LCD(lcd_data);
    dg = getdigits(L2);
    itostring(dg, L2, lcd_data); //L2をASCIIに変換
    Set_Pos(0x04 + 6 - dg);
    Puts_LCD(lcd_data);
    strset(".",lcd_data);
    Set_Pos(0x0A);
    Puts_LCD(lcd_data);
    itostring(2, L1, lcd_data); //L1をASCIIに変換
    Set_Pos(0x0B);
    Puts_LCD(lcd_data);
}
void Nocaldisp() {
    strset("No Caliblation  ", lcd_data);
    Set_Pos(0x00);
    Puts_LCD(lcd_data);
}
void caldisp() {
    strset("Caliblation     ", lcd_data);
    Set_Pos(0x00);
    Puts_LCD(lcd_data);
}

void Notcaldisp() {
    strset("Switch to Cx     ", lcd_data);
    Set_Pos(0x00);
    Puts_LCD(lcd_data);
}
//void LCDdisplay(char p,char io) {
//    unsigned char dg;
//        itostring(3,io,lcd_data);
//    	Set_Pos(p);	Puts_LCD(lcd_data);
//}

unsigned int adconv() {
     unsigned int vadc;
     double vadcd;
	ADRESH = 0;
	ADRESL = 0;
	
	ADCON0 = 0b10000001; //AN0 AD変換設定

	 __delay_us(100) ;
	GO_nDONE = 1;  			//AD変換開始
     	while(GO_nDONE) ;		// AD変換完了まで待つ
     	vadc = ADRESH ; 	 	// ADRESHの読み取り
     	vadc = ( vadc << 8 ) | ADRESL ; // ADRESLを下位につなぐ
        vadcd = vadc;
        VBd = vadcd * 475 / 1024;
     	VB=VBd;
}


unsigned long int fcounter() {
    unsigned int time0 = 0;
    unsigned long int time1 = 0;
    unsigned long int time2 = 0;
    unsigned long int t1max = 65536;
    unsigned int time1H;
    unsigned char time1L;
    unsigned long int lastfreq;
    lastfreq =Freq;

    TMR0IE = 0;
    TMR0IF = 0;


    TMR1IF = 0;
    TMR1IE = 0;
    TMR0 = 0; //Timer0に０をセット
    TMR1H = 0; //Timer1 クリアー
    TMR1L = 0;
    TMR1ON = 1; //Timer1カウント開始
    while (time0 < 76) {
 
        if (TMR0IF == 1) {
            time0++;
            TMR0IF = 0;
         } //Timer0がオーバーフローするごとに+１(1uS)
        if (TMR1IF == 1) {
            time1++;
            TMR1IF = 0;
        }  //Timer1がオーバーフローするごとに+１(1.2uS)
    }
    TMR0IF = 0;
    TMR0 = 181; //Timer0に残り時間をセット
    while (TMR0IF == 0) { //Timer0がオーバーフローするまで待つ
    if (TMR1IF == 1) {time1++;TMR1IF = 0;} //Timer1がオーバーフローするごとに+１(1.2us)
    }
	TMR0IF = 0;
    asm ("nop");
    asm ("nop");
    asm ("nop");
    asm ("nop");
    asm ("nop");
    asm ("nop");
    asm ("nop");
    asm ("nop");
    asm ("nop");
    asm ("nop");
//    asm ("nop");
    TMR1ON = 0;
    if (TMR1IF == 1) {time1++;TMR1IF = 0;}//Timer1がオーバーフローするごとに+１
    
    TMR0IF = 0;
    TMR1IE = 0;

    time1H = TMR1H; //TMR1H上位8ビット読み取り
    time1L = TMR1L;
    fcount1 = (time1H << 8) | time1L; // 下位8ビットをつなぐ
    fcount0 = time1*t1max;
    unsigned long int Fq = fcount1 + fcount0;
    if (NoCount == 1) {Fq = lastfreq;NoCount = 0;}
//    freqdisp(Fq);
    return Fq;
}






void GO_cal() {//自動校正を行う
    char i;
	if (LCs == 1) {
		caldisp();
    	NoCount = 0;

        LCs = 1;
        CALrely = 1;
        __delay_ms(500);
         Fcal=fcounter();
        DFcal=Fcal;
        
       CALrely = 0;
       __delay_ms(100);
        Fnormal=fcounter();
        DFnormal=Fnormal;
        
    
        

      
        
        Cref = (Ccal * DFcal * DFcal) / ((DFnormal * DFnormal) - (DFcal * DFcal));
        Lref=1/(4 * PI * PI * DFnormal * DFnormal * Cref * pow(10,-12));


        CALed =1;
	} else {
        Notcaldisp();
        __delay_ms(1000);
    }
        LED =0;

}




void Cmeasure(double Fq) {
    Cx=Cref*(((DFnormal*DFnormal)/(Fq*Fq))-1);
    if (Cx < 10000. ) {
        Cxdisp0(Cx);
    } else {
        Cxdisp1(Cx);
    }
}
void Lmeasure(double Fq) {
    if (Fq > 1000.) {
        Lx=Lref*pow(10,6)*(((DFnormal*DFnormal)/(Fq*Fq))-1);
    } else {
        Lx=0.;
    }
    

    if (Lx < 1000.) {
        Lxdisp0(Lx);
    } else {
        Lxdisp1(Lx);
    }
}

void selectLC() {
	if (lastRB2 == RB2 ) {return;}
        if (RB2 == 1) {
            LCs = 1;//コンデンサ負荷
            Cx = 0.;
            Cxdisp0(0);
        } else {
            LCs = 0;//コイル負荷
            Lx = 0.;
            Lxdisp0(0);
        }
	lastRB2 = RB2;
        __delay_ms(300);
    
}

/*******************************************************************************
 *  InterFunction()   割り込みの処理                                            *
 *******************************************************************************/
void interrupt InterFunction( void ) {
    INTF = 0;
    INTE = 0;
    NoCount = 1;

   
        LED = 1;
        CALflg =1;

    
    __delay_ms(10);
        INTE = 1;
}

/*******************************************************************************
 *  メインの処理                                                                *
 *******************************************************************************/
void main() {
 //   unsigned char i = 0;
    OSCCON = 0b00111000; // Config設定通り
    OPTION_REG = 0b10010111; //プルアップなし Timer0 fosc/4 プリスケーラ1/256　RB0立下りで割り込み

    T1CON = 0b10000100; //Timer1 外部入力、プリスケーラ無し、TMR1ON=0
    TRISA = 0b00000101; //RA1-7出力 RA0,2:入力
    ANSELA = 0b00000001; //AN0使用
    ADCON1= 0b10100000	; //右詰め　ADCS 1/32 Vref=VDD
    PORTA = 0x00;
    TRISC = 0b00000001; //RC0,カウンター入力に設定
    ANSELB = 0b00000000;
	TRISB = 0b00000111;//RB0:INT RB1:CAL RB2:C/L
    LATC = 0x00;
    GIE = 1;//全割り込み許可
    INTE = 1;//RB0外部割込み許可
    lastRB2 = 2;
    selectLC();
    //LCs=1;//初期設定：Cxモード
    __delay_ms(100);
    Init_LCD();

    __delay_ms(300);

    Ccal=1000;//キャリブレ用コンデンサ(PF)
  
    while (1) {
        if (CALflg == 1) {
            GO_cal();
            CALflg = 0;
        }
        Freq = fcounter();
        DFmeasure = Freq;
        if (CALed == 1) {
            if (LCs == 1) {
                Cmeasure(DFmeasure);
            } else {
                Lmeasure(DFmeasure);
            }

        }
        else {
           if (CALflg == 0) {Nocaldisp();}
        }

        selectLC();
        adconv();
        VBdisp();
    }
}

