Light-link system tester

Thread Starter

nsaspook

Joined Aug 27, 2009
7,179
I'm building a prototype (I love Vector BOARD point to point wiring with 30 gauge wire) life-extension tester box for some old equipment (with few good spares) with sometimes marginal reliability from slowly degrading light transceiver LEDs and bad cables in the rats-nest of comm links with tens of devices talking. The end product will emulate the host controller and hopefully pin-point problems before they become hard downs instead of just flaky operation.
I'm at the point of going "black' with a internal NDA for the equipment protocols but the existing framework is generic and might have some example code others can use for MPLABX 3.05, C18 and PIC18F45K80 from this debugging code for the project.



SPI LCD (EA DOGM 163) display code using an ISR with a timer/interrupt and ring-buffer to update the display in a batched sequence in background with the triggers.
CTMU internal diode temperature calibration and reading. 338 is the temperature of the internal chip diode in 0.1C.
CTMU precision time delay function to 0.5ns for light-link length/quality measurements. 48 (23ns delay at the transceiver chips from sent edge to received edge) is the value for a 6 inch jumper (zero reference calibration value) with 10 counts per meter of cable from that reading.

RS232 send/receive ISR interrupt code with triggers to send and receive link data.
Misc glue and testing code with lots of debug toggle I/O for test points.

Full code and hardware docs. https://github.com/nsaspook/e220
The existing code base is too large to post here but this is a sample. (yes the ISR does a lot of processing but usually only one or two sections per call.
C:
// High priority interrupt routine
#pragma    tmpdata    ISRHtmpdata
#pragma interrupt data_handler   nosave=section (".tmpdata")

void data_handler(void)
{
    static uint16_t cr1, cr2, ct1, ct2, ct_spi;
    static union Timers timer;
    static uint8_t b_data;

    DLED5 = LOW;
    if (PIE1bits.SSPIE && PIR1bits.SSPIF) { // send data to SPI bus
        spi_link.int_count++;
        PIR1bits.SSPIF = LOW;
        ct1 = SSPBUF; // read to clear the BF flag, don't care about the data with LCD but AUX might
        /*
        * the SPI data is not sent here, it's scheduled by timer0
        * with about 63us for the whole process with LCD data byte to next byte with timer0 LCD delay
        * fixed times: ~8us per byte 1MHz clock, 27.3us LCD instruction execution time
        * and ~18us overhead for this part
        */
        if (spi_link.SPI_LCD) {
            if (spi_link.tx1b->count == 0) { // data buffer is empty but maybe the delay is not done, 3.6us overhead
                PIE1bits.SSPIE = LOW; // stop data transmitter
                spi_link.LCD_TIMER = LOW;
                if (spi_link.LCD_DATA) INTCONbits.TMR0IF = HIGH; //set interrupt flag
            } else {
                ct_spi = ringBufS_get(spi_link.tx1b); // get the 16 bit data
                spi_link.config = ct_spi >> 8;

                if (spi_link.config & LCD_CMD_MASK) { // is this command data
                    /*
                    * check for clear and home commands
                    */
                    if ((ct_spi & 0xff) > LCD_CLEAR_HOME) { // check only the data bits for the last 2 bits
                        spi_link.delay = LCD_LONG; // needs at least 1.08 ms LCD instruction execution time
                    } else {
                        spi_link.delay = LCD_SHORT; // needs at least 27.3us LCD instruction execution time
                    }
                    RS = LOW; // sending LCD command data
                } else {
                    spi_link.delay = LCD_SHORT;
                    RS = HIGH; // sending character data
                }
                CSB = LOW; // select the display SPI receiver
                /*
                * setup timer0 for SPI delay and buffer write
                */
                spi_link.LCD_TIMER = HIGH;
                spi_link.LCD_DATA = HIGH;
                INTCONbits.TMR0IF = HIGH; //set interrupt flag to update timer0 as we fall down the ISR
            }
        }
        if (spi_link.SPI_AUX) {
            CSA = LOW; // select the AUX SPI receiver
        }
    }

    if (PIE1bits.TX1IE && PIR1bits.TX1IF) { // send data to USART
        if (L.tx1b->count == 0) { // buffer has been sent
            if (TXSTA1bits.TRMT) { // last bit has been shifted out
                PIE1bits.TX1IE = LOW; // stop data xmit
            }
        } else {
            ct1 = ringBufS_get(L.tx1b); // get the 9 bit data from 16 bit data buffer
            TXSTA1bits.TX9D = (ct1 & 0b100000000) ? HIGH : LOW;
            TXREG1 = ct1; // send data and clear FLAG
            V.c1t_int++;
        }
    }

    if (PIE3bits.TX2IE && PIR3bits.TX2IF) {
        if (L.tx2b->count == 0) {
            if (TXSTA2bits.TRMT) {
                PIE3bits.TX2IE = LOW;
            }
        } else {
            ct2 = ringBufS_get(L.tx2b);
            TXSTA2bits.TX9D = (ct2 & 0b100000000) ? HIGH : LOW;
            TXREG2 = ct2;
            V.c2t_int++;
        }
    }

    if (PIR1bits.RC1IF) { // is data from network down-link 9n1 port
        DLED0 = !DLED0; // link flasher
        V.c1r_int++; // total count
        if (RCSTA1bits.OERR) {
            RCSTA1bits.CREN = LOW; // clear overrun
            RCSTA1bits.CREN = HIGH; // re-enable
            SLED = HIGH;
            ll_flag.overrun1_error++;
        }
        if (RCSTA1bits.FERR) {
            SLED = HIGH;
            ll_flag.frame1_error++;
        }

        /* clear com1 interrupt flag */
        // a read clears the flag
        cr1 = RCREG1; // read from port1 and clear PIR1bits.RC1IF
        if (RCSTA1bits.RX9D) {
            cr1 |= 0b100000000; // OR bit 9 for data buffer
        }

        switch (L.omode) {
        case LL_E220:
            TXSTA2bits.TX9D = RCSTA1bits.RX9D;
            TXREG2 = cr1;
            break;
        case LL_LOOP:
            TXSTA1bits.TX9D = RCSTA1bits.RX9D;
            TXREG1 = cr1;
            break;
        default:
            break;
        }

        ringBufS_put(L.rx1b, cr1);
        if (cr1 == 0) DLED0 = S_OFF; // DEBUG flasher
    }

    if (PIR3bits.RC2IF) { // is data from network up-link 9n1 port
        DLED1 = !DLED1; // link flasher
        V.c2r_int++;
        if (RCSTA2bits.OERR) {
            RCSTA2bits.CREN = LOW; //      clear overrun
            RCSTA2bits.CREN = HIGH; // re-enable
            SLED = HIGH;
            ll_flag.overrun2_error++;
        }
        if (RCSTA2bits.FERR) {
            SLED = HIGH;
            ll_flag.frame2_error++;
        }

        cr2 = RCREG2; // read from port2 and clear PIR3bits.RC2IF
        if (RCSTA2bits.RX9D) {
            cr2 |= 0b100000000;
        }

        switch (L.omode) {
        case LL_E220:
            TXSTA1bits.TX9D = RCSTA2bits.RX9D;
            TXREG1 = cr2;
            break;
        case LL_LOOP:
            TXSTA2bits.TX9D = RCSTA2bits.RX9D;
            TXREG2 = cr2;
            break;
        default:
            break;
        }

        ringBufS_put(L.rx2b, cr2);
        if (cr2 == 0) DLED1 = S_OFF; // DEBUG flasher
    }

    /*
    * This is a little tricky, it normally runs at a very slow speed for a system heartbeat
    * but it also times a delay for SPI data to a LCD display for data and commands
    * ~36us for data and ~1.2ms for commands set in spi_link.delay
    * with ~3us overhead for this part
    */
    if (INTCONbits.TMR0IF) { // check timer0 1 second timer & SPI delay int handler
        DLED3 = S_ON;
        if (spi_link.LCD_TIMER) {
            spi_link.LCD_TIMER = LOW;
            /*
            * send the SPI data then reset timer0 for fast speed data delay
            * send time per byte 11us
            */
            if (spi_link.LCD_DATA) {
                PIE1bits.SSPIE = LOW; // don't process the receive interrupt from the send yet
                SSPBUF = ct_spi; // send data
            }
            /*
            * set the link delay
            */
            timer.lt = spi_link.delay; // Copy timer value into union
            TMR0H = timer.bt[HIGH]; // Write high byte to Timer0
            TMR0L = timer.bt[LOW]; // Write low byte to Timer0
        } else {
            if (spi_link.LCD_DATA) { // we are in a delay from sending a byte
                spi_link.LCD_DATA = LOW; // clear the data sent flag
                PIE1bits.SSPIE = HIGH; // enable to receive byte when ISR exits, ISR will be called again in ~5us to get it
            }
            // set normal delay
            TMR0H = timer_long.bt[HIGH]; // Write high byte to Timer0
            TMR0L = timer_long.bt[LOW]; // Write low byte to Timer0
        }
        INTCONbits.TMR0IF = LOW; //clear interrupt flag
        DLED3 = S_OFF;
    }

    if (PIR1bits.ADIF) { // ADC conversion complete flag
        DLED2 = S_ON;
        PIR1bits.ADIF = LOW;
        adc_count++; // just keep count
        if (L.ctmu_data) {
            L.ctmu_adc = ADRES;
            L.ctmu_data = LOW;
        } else {
            if (L.ctmu_data_temp) {
                L.pic_temp = ADRES;
                L.ctmu_data_temp = LOW;
            }
            adc_buffer[L.adc_chan] = ADRES;
        }
        DLED2 = S_OFF;
    }

    /*
    * trigger the ADC here
    */
    if (PIR3bits.CTMUIF) { // CTED (tx1) is high then CTED2 (rx2)went high
        PIR3bits.CTMUIF = LOW; // clear CTMU flag
        if (L.ctmu_data) {
            ADCON0bits.GO = HIGH; // and begin A/D conv, will set adc int flag when done.
            DLED4 = !DLED4;
            CTMUCONHbits.CTMUEN = LOW;
            CTMUICON = 0x00; // current off
            PIE3bits.CTMUIE = LOW; // disable interrupt
            CTMUCONHbits.EDGEN = LOW;
            CTMUCONLbits.EDG1STAT = 0; // Set Edge status bits to zero
            CTMUCONLbits.EDG2STAT = 0;
        }
    }

    if (INTCONbits.RBIF) {
        b_data = PORTB;
        INTCONbits.RBIF = LOW;
    }

    if (INTCONbits.INT0IF) {
        INTCONbits.INT0IF = LOW;
        V.buttonint_count++;
        hid0_ptr->bled_on = !hid0_ptr->bled_on;
    }

    if (INTCON3bits.INT1IF) {
        INTCON3bits.INT1IF = LOW;
        V.buttonint_count++;
        hid1_ptr->bled_on = !hid1_ptr->bled_on;
    }

    DLED5 = HIGH;
}
#pragma    tmpdata
 
Last edited:

LesJones

Joined Jan 8, 2017
2,598
You will need to give a LOT more detail of EXACTLY what you are trying to do. If this is for measuring times for a TDR then I don't think any microcontroller will be capable of measuring such short times directly.

Les.
 

LesJones

Joined Jan 8, 2017
2,598
Hi Eric,
I think I must have replied at about the same time as you created the new thread as it looked like I was replying to a new thread by "Vaidehi roy" I had not read it correctly as I did not notice it was a question directed at "nsaspook" when I read it the first time.

Les.
 
Top