/*
 * File:   main.c
 * Author: etrauschke
 *
 * Created on September 27, 2017, 8:16 AM
 */
#pragma config RSTOSC = 2, FEXTOSC = 7
//#pragma config RSTOSC = 0
#pragma config WDTE = OFF, XINST = 1, DEBUG = 1, WRTD = 1
//#pragma config BOREN = OFF, FCMEN = OFF, LVP=OFF

#define _XTAL_FREQ      12800000         // this is used by the __delay_ms(xx) and __delay_us(xx) functions

#define I2C_ADDRESS     0x30

#define I2C_REG_PERIOD  8
#define I2C_REG_WRITE   9
#define I2C_REG_READ    10

#define I2C_CFG_REG_CNT 9
#define I2C_REG_COUNT   11

#include <xc.h>
#include <pic18f25k42.h>

short c = 0;
unsigned char buf[64];
unsigned char i2c_reg = 0xff;
unsigned short i2c_regs[I2C_CFG_REG_CNT];
unsigned short tick;
char trig_en[4] = {0,0,0,0};


inline void write_cfg_reg();
inline unsigned char read_cfg_reg(unsigned char byte);
void store_cfg_regs();
void load_cfg_regs();

void __interrupt(irq(IRQ_TMR1),high_priority) tmr1Int(void) {
    PORTCbits.RC1 = 1;
    TMR1ON = 0;
    TMR1IF = 0;
}

void __interrupt(irq(IRQ_TMR3),high_priority) tmr3Int(void) {
    PORTCbits.RC2 = 1;
    TMR3ON = 0;
    TMR3IF = 0;
}

void __interrupt(irq(IRQ_TMR5),high_priority) tmr5Int(void) {
    PORTCbits.RC3 = 1;
    TMR5ON = 0;
    TMR5IF = 0;
}

void __interrupt(irq(IRQ_TMR0),high_priority) tmr0Int(void) {
    PORTCbits.RC4 = 1;
    T0EN = 0;
    TMR0IF = 0;
}

void __interrupt(irq(IRQ_TMR2),low_priority) tmr2Int(void) {
   
    TMR2IF = 0;
    if (tick++ >= i2c_regs[I2C_REG_PERIOD]) {
        tick = 0;
        TMR1H = (i2c_regs[0] >> 8) & 0xff;
        TMR1L = i2c_regs[0] & 0xff;
        TMR3H = (i2c_regs[2] >> 8) & 0xff;
        TMR3L = i2c_regs[2] & 0xff;
        TMR5H = (i2c_regs[4] >> 8) & 0xff;
        TMR5L = i2c_regs[4] & 0xff;
        TMR0H = (i2c_regs[6] >> 8) & 0xff;
        TMR0L = i2c_regs[6] & 0xff;
        
        if (trig_en[3]) T0EN = 1;
        if (trig_en[0]) TMR1ON = 1;
        if (trig_en[1]) TMR3ON = 1;
        if (trig_en[2]) TMR5ON = 1;
        PORTCbits.RC0 = 1;
    } else {
        PORTCbits.RC0 = 0;
    }
    if (tick >= i2c_regs[1]) {
            PORTCbits.RC1 = 0;
    }
    if (tick >= i2c_regs[3]) {
            PORTCbits.RC2 = 0;
    }
    if (tick >= i2c_regs[5]) {
            PORTCbits.RC3 = 0;
    }
    if (tick >= i2c_regs[7]) {
            PORTCbits.RC4 = 0;
    }
    return;
}


void __interrupt(irq(IRQ_I2C2),high_priority) i2c2Int(void) {

    I2C2IF = 0;    
    if (I2C2PIRbits.ACKTIF) {
        I2C2PIRbits.ACKTIF = 0;
        if (I2C2STAT0bits.D){
            // write to PIC
            if (!I2C2STAT0bits.R) {
                if (i2c_reg == 0xff) {
                    i2c_reg = I2C2RXB;
                    goto out;
                } else {
                    buf[c++] = I2C2RXB;
                }
                if (c == 2) {
                    if (i2c_reg < I2C_CFG_REG_CNT) {
                        write_cfg_reg();
                    }
                    //i2c_regs[i2c_reg] = (buf[0] << 8) | buf[1];
                }
                if (i2c_reg == I2C_REG_WRITE) {
                    store_cfg_regs();
                }
                if (i2c_reg == I2C_REG_READ) {
                    load_cfg_regs();
                }
            } else {
                //unsigned char *tmp;
                if (i2c_reg == 0xff || c > 1) {
                    goto out;
                }
                //tmp = &(i2c_regs[i2c_reg]);
                //I2C2TXB = *(tmp + 1 - c++);
                I2C2TXB = read_cfg_reg(c++);
            }
        } else {
            // a new connection, clean out old status
            c = 0;
            I2C2STAT1bits.CLRBF = 1;
            
            if (!I2C2STAT0bits.R){
                i2c_reg = 0xff;
            } else {
                I2C2TXB = i2c_reg;
            }
        }
    }
out:
    I2C2CON0bits.CSTR = 0;
    return;
}

void loop() {
    int i;
    while(1);
}

inline void write_cfg_reg() {
    /*
     * To keep the math for calculating the timer values and the ticks when a
     * trigger gets turned off out of the interrupt routines we just save the
     * actual value to be written into the TMRxH and TMRxL into the values in
     * i2c_regs. We also calculate the offtick value on which the TMR2 period 
     * timer disables the output.
     * 
     * The values which go into the TMRx registers are the TMRs max value minus
     * the requested delay so it rolls over after the delay. Note that 0xffff as
     * a TMRx value doesn't create reliable interrupts so the smallest delay is
     * 10 us. Since all timers are delayed by that amount the relative delay
     * between the timers is still 0.
     * 
     * The offtick is calculated as follows:
     * 
     * offtick = ontick/10 + 1 + len
     * 
     * ontick is the delay of TMRx which has a 10x time resolution (10us vs.
     * 100us for the period timer). The len is the duration of the pulse in
     * 100us ticks. The +1 is necessary since we want to make sure that we have
     * always at least one full tick before we turn the output off.
     */
    unsigned short val;
    unsigned char base_reg;
    unsigned short len;
    
    // No special magic required for writing period register, just subtract 1 to
    // account for the way we reset the period timer.
    if (i2c_reg == I2C_REG_PERIOD) {
        i2c_regs[I2C_REG_PERIOD] = ((buf[0] << 8) | buf[1]) - 1;
        return;
    }
    
    val = (buf[0] << 8) | buf[1];
    base_reg = i2c_reg & 0xfe;
    
    if (base_reg == i2c_reg) {
        // update delay, calculate len from previous delay and current offtick
        len = i2c_regs[base_reg + 1] - 1 - (0xfffe - i2c_regs[base_reg]) / 10;
        i2c_regs[i2c_reg] = 0xfffe - val;
    } else {
        //char en = 0;
        if (val) {
            trig_en[base_reg>>1] = 1;
            len = val;
        } else {
            i2c_regs[base_reg + 1] = 0;
            trig_en[base_reg>>1] = 0;
            return;
        }
        //trig_en[base_reg>>1] = en;
    }
    // offtick always has to be changed, either because len or delay has changed
    i2c_regs[base_reg + 1] = (0xfffe - i2c_regs[base_reg]) / 10 + 1 + len;
}

inline unsigned char read_cfg_reg(unsigned char byte) {
    unsigned char base_reg;
    unsigned short val;
    
    if (i2c_reg == I2C_REG_PERIOD) {
        val = i2c_regs[I2C_REG_PERIOD] + 1;
    } else {  
        base_reg = i2c_reg & 0xfe;
        if (base_reg == i2c_reg) {
            // return delay value
            val =  0xfffe - i2c_regs[i2c_reg];
        } else {
            val = i2c_regs[base_reg + 1] - 1 - (0xfffe - i2c_regs[base_reg]) / 10;
        }
    }
    return *((unsigned char *)&val + 1 - byte);    
}

void store_cfg_regs() {
    unsigned char i;
    unsigned char *ptr = (unsigned char*)i2c_regs;
    GIEH = 0;
    GIEL = 0;
    NVMCON1 = 0x00;
    NVMCON1bits.WREN = 1;
    for (i = 0; i < I2C_CFG_REG_CNT * 2; i++) {
        NVMADRL = i;
        NVMDAT = *(ptr++);
        asm("MOVLW 0x55;");
        asm("MOVWF NVMCON2;");
        asm("MOVLW 0xAA;");
        asm("MOVWF NVMCON2;");
        asm("BSF NVMCON1, 1;");
        while (NVMCON1bits.WR);
        NVMIF = 0;
    }
    NVMCON1bits.WREN = 0;
    GIEH = 1;
    GIEL = 1;
}

void store_test() {
    GIEH = 0;
    GIEL = 0;
    NVMCON1 = 0x00;
    NVMADRL = 5;
    NVMDAT = 0xa5;
    NVMCON1bits.WREN = 1;

    asm("MOVLW 0x55;");
    asm("MOVWF NVMCON2;");
    asm("MOVLW 0xAA;");
    asm("MOVWF NVMCON2;");
    asm("BSF NVMCON1, 1;");
    
    while (NVMCON1bits.WR);
    NVMIF = 0;
    NVMCON1bits.WREN = 0;
    GIEH = 1;
    GIEL = 1;
}

void load_test() {
    unsigned char tmp;
    
    NVMCON1 = 0x00;
    NVMADRL = 5;
    NVMCON1bits.RD = 1;
    tmp = NVMDAT;
    
}

void load_cfg_regs() {
    unsigned char i;
    unsigned char *ptr = (unsigned char *)i2c_regs;
    for (i = 0; i < I2C_CFG_REG_CNT * 2; i++) {
        NVMADRL = i;
        NVMCON1 = 0;
        NVMCON1bits.RD = 1;
        *(ptr++) = NVMDAT;
    }
    // by default, no delay
    for (i = 0; i < 8; i+=2) {
        if (i2c_regs[i] == 0xffff) {
            i2c_regs[i] = 0xfffe; 
        }
    }
    // by default, pulse off
    for (i = 1; i < 8; i+=2) {
        if (i2c_regs[i] == 0xffff) {
            i2c_regs[i] = 0x0; 
        }
        if (i2c_regs[i] != 0) {
            trig_en[i/2-1] = 1; 
        }
    }
    
    // default period 10ms
    if (i2c_regs[I2C_REG_PERIOD] == 0xffff) {
            i2c_regs[I2C_REG_PERIOD] = 99;
    }
}

void main(void) {
    int i;
    
    TRISB = 0x00;         //I2C on PORTB as output
    ODCONB = 0xff;
    TRISC = 0x00;
    ANSELB = 0x00;
    ANSELC = 0x00;

    PPSLOCK = 0x55;
    PPSLOCK = 0xAA;
    PPSLOCKbits.PPSLOCKED = 0x00;

    RB1PPS = 0x23;
    RB2PPS = 0x24;
    I2C2SDAPPS = 0x0a;
    I2C2SCLPPS = 0x09;

    PPSLOCK = 0x55;
    PPSLOCK = 0xAA;
    PPSLOCKbits.PPSLOCKED = 0x01;
 

    /* set up NCO 
     * NCO divider: 256 
     * counter toggles output -> additional divider by 2
     * 51.2MHz / 512 = 100 kHz -> 10us clock 
     */
    NCO1CLK = 0x00;
    NCO1INCU = 0x00;
    NCO1INCH = 0x10;
    NCO1INCL = 0x00;
    NCO1EN = 1;
    
    /* set up REFCLOCK to provide input for TMR1,3,5
     * source: NCO
     * divider: 1:1
     */
    CLKRCON = 0x90;
    CLKRCLK = 0x06;
    CLKREN = 1;

    /* TMR0 trigger timer
     * 16 bit mode
     * Fosc/4 1:128 -> 10us tick
     */
    T0CON0 = 0x10;
    T0CON1 = 0x57;
    TMR0IF = 0;
    TMR0IE = 1;
    T0EN = 1;

    /* TMR1 trigger timer
     * source CLKREF
     * 10us tick
     */
    T1CLK = 0x08;
    T1CON = 0x02;
    TMR1IF = 0;
    TMR1IE = 1;
    TMR1ON = 1;

    /* TMR3 trigger timer
     * source CLKREF
     * 10us tick
     */
    T3CLK = 0x08;
    T3CON = 0x02;
    TMR3IF = 0;
    TMR3IE = 1;
    TMR3ON = 1;

    /* TMR5 trigger timer
     * source CLKREF
     * 10us tick
     */
    T5CLK = 0x08;
    T5CON = 0x02;
    TMR5IF = 0;
    TMR5IE = 1;
    TMR5ON = 1;
    
    /* TMR2 period timer
     * source: CLKREF
     * divider 1:1
     * period 9+1 -> 100us tick
     */
    T2CLK = 0x08;
    T2PR = 9;
    T2CON = 0x00;
    TMR2IE = 1;
    T2CONbits.ON = 1;

    /* setup I2C
     */
    I2C2ADR0 = I2C_ADDRESS;
    I2C2CON0 = 0x00;
    I2C2CON1 = 0x00;
    I2C2CON2 = 0x28;
    I2C2CON0bits.EN = 1;
    RB1I2C = 0x21;
    RB2I2C = 0x21;
    PIR6bits.I2C2IF = 0;
    I2C2PIR = 0x00;
    PIE6bits.I2C2IE = 1;
    I2C2PIEbits.ACKT2IE = 1;
    I2C2STAT1 = 0;
    
    //store_test();
    //initialize timer offsets for trigger delay 
    load_cfg_regs();
//    for(i = 0; i < 4; i++) {
//        if (i2c_regs[i*2+1] !=
//    }
    //load_test();
     
    GIEH = 1;
    GIEL = 1;
    I2C2CON0bits.EN = 1;
  
    loop();
    
    return;
}


