Light-link system tester

Discussion in 'Embedded Systems and Microcontrollers' started by nsaspook, Aug 7, 2015.

  1. nsaspook

    Thread Starter AAC Fanatic!

    Aug 27, 2009
    2,913
    2,181
    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.
    [​IMG]
    [​IMG]

    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.
    [​IMG]
    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.
    Code (C):
    1.  
    2. // High priority interrupt routine
    3. #pragma    tmpdata    ISRHtmpdata
    4. #pragma interrupt data_handler   nosave=section (".tmpdata")
    5.  
    6. void data_handler(void)
    7. {
    8.     static uint16_t cr1, cr2, ct1, ct2, ct_spi;
    9.     static union Timers timer;
    10.     static uint8_t b_data;
    11.  
    12.     DLED5 = LOW;
    13.     if (PIE1bits.SSPIE && PIR1bits.SSPIF) { // send data to SPI bus
    14.         spi_link.int_count++;
    15.         PIR1bits.SSPIF = LOW;
    16.         ct1 = SSPBUF; // read to clear the BF flag, don't care about the data with LCD but AUX might
    17.         /*
    18.         * the SPI data is not sent here, it's scheduled by timer0
    19.         * with about 63us for the whole process with LCD data byte to next byte with timer0 LCD delay
    20.         * fixed times: ~8us per byte 1MHz clock, 27.3us LCD instruction execution time
    21.         * and ~18us overhead for this part
    22.         */
    23.         if (spi_link.SPI_LCD) {
    24.             if (spi_link.tx1b->count == 0) { // data buffer is empty but maybe the delay is not done, 3.6us overhead
    25.                 PIE1bits.SSPIE = LOW; // stop data transmitter
    26.                 spi_link.LCD_TIMER = LOW;
    27.                 if (spi_link.LCD_DATA) INTCONbits.TMR0IF = HIGH; //set interrupt flag
    28.             } else {
    29.                 ct_spi = ringBufS_get(spi_link.tx1b); // get the 16 bit data
    30.                 spi_link.config = ct_spi >> 8;
    31.  
    32.                 if (spi_link.config & LCD_CMD_MASK) { // is this command data
    33.                     /*
    34.                     * check for clear and home commands
    35.                     */
    36.                     if ((ct_spi & 0xff) > LCD_CLEAR_HOME) { // check only the data bits for the last 2 bits
    37.                         spi_link.delay = LCD_LONG; // needs at least 1.08 ms LCD instruction execution time
    38.                     } else {
    39.                         spi_link.delay = LCD_SHORT; // needs at least 27.3us LCD instruction execution time
    40.                     }
    41.                     RS = LOW; // sending LCD command data
    42.                 } else {
    43.                     spi_link.delay = LCD_SHORT;
    44.                     RS = HIGH; // sending character data
    45.                 }
    46.                 CSB = LOW; // select the display SPI receiver
    47.                 /*
    48.                 * setup timer0 for SPI delay and buffer write
    49.                 */
    50.                 spi_link.LCD_TIMER = HIGH;
    51.                 spi_link.LCD_DATA = HIGH;
    52.                 INTCONbits.TMR0IF = HIGH; //set interrupt flag to update timer0 as we fall down the ISR
    53.             }
    54.         }
    55.         if (spi_link.SPI_AUX) {
    56.             CSA = LOW; // select the AUX SPI receiver
    57.         }
    58.     }
    59.  
    60.     if (PIE1bits.TX1IE && PIR1bits.TX1IF) { // send data to USART
    61.         if (L.tx1b->count == 0) { // buffer has been sent
    62.             if (TXSTA1bits.TRMT) { // last bit has been shifted out
    63.                 PIE1bits.TX1IE = LOW; // stop data xmit
    64.             }
    65.         } else {
    66.             ct1 = ringBufS_get(L.tx1b); // get the 9 bit data from 16 bit data buffer
    67.             TXSTA1bits.TX9D = (ct1 & 0b100000000) ? HIGH : LOW;
    68.             TXREG1 = ct1; // send data and clear FLAG
    69.             V.c1t_int++;
    70.         }
    71.     }
    72.  
    73.     if (PIE3bits.TX2IE && PIR3bits.TX2IF) {
    74.         if (L.tx2b->count == 0) {
    75.             if (TXSTA2bits.TRMT) {
    76.                 PIE3bits.TX2IE = LOW;
    77.             }
    78.         } else {
    79.             ct2 = ringBufS_get(L.tx2b);
    80.             TXSTA2bits.TX9D = (ct2 & 0b100000000) ? HIGH : LOW;
    81.             TXREG2 = ct2;
    82.             V.c2t_int++;
    83.         }
    84.     }
    85.  
    86.     if (PIR1bits.RC1IF) { // is data from network down-link 9n1 port
    87.         DLED0 = !DLED0; // link flasher
    88.         V.c1r_int++; // total count
    89.         if (RCSTA1bits.OERR) {
    90.             RCSTA1bits.CREN = LOW; // clear overrun
    91.             RCSTA1bits.CREN = HIGH; // re-enable
    92.             SLED = HIGH;
    93.             ll_flag.overrun1_error++;
    94.         }
    95.         if (RCSTA1bits.FERR) {
    96.             SLED = HIGH;
    97.             ll_flag.frame1_error++;
    98.         }
    99.  
    100.         /* clear com1 interrupt flag */
    101.         // a read clears the flag
    102.         cr1 = RCREG1; // read from port1 and clear PIR1bits.RC1IF
    103.         if (RCSTA1bits.RX9D) {
    104.             cr1 |= 0b100000000; // OR bit 9 for data buffer
    105.         }
    106.  
    107.         switch (L.omode) {
    108.         case LL_E220:
    109.             TXSTA2bits.TX9D = RCSTA1bits.RX9D;
    110.             TXREG2 = cr1;
    111.             break;
    112.         case LL_LOOP:
    113.             TXSTA1bits.TX9D = RCSTA1bits.RX9D;
    114.             TXREG1 = cr1;
    115.             break;
    116.         default:
    117.             break;
    118.         }
    119.  
    120.         ringBufS_put(L.rx1b, cr1);
    121.         if (cr1 == 0) DLED0 = S_OFF; // DEBUG flasher
    122.     }
    123.  
    124.     if (PIR3bits.RC2IF) { // is data from network up-link 9n1 port
    125.         DLED1 = !DLED1; // link flasher
    126.         V.c2r_int++;
    127.         if (RCSTA2bits.OERR) {
    128.             RCSTA2bits.CREN = LOW; //      clear overrun
    129.             RCSTA2bits.CREN = HIGH; // re-enable
    130.             SLED = HIGH;
    131.             ll_flag.overrun2_error++;
    132.         }
    133.         if (RCSTA2bits.FERR) {
    134.             SLED = HIGH;
    135.             ll_flag.frame2_error++;
    136.         }
    137.  
    138.         cr2 = RCREG2; // read from port2 and clear PIR3bits.RC2IF
    139.         if (RCSTA2bits.RX9D) {
    140.             cr2 |= 0b100000000;
    141.         }
    142.  
    143.         switch (L.omode) {
    144.         case LL_E220:
    145.             TXSTA1bits.TX9D = RCSTA2bits.RX9D;
    146.             TXREG1 = cr2;
    147.             break;
    148.         case LL_LOOP:
    149.             TXSTA2bits.TX9D = RCSTA2bits.RX9D;
    150.             TXREG2 = cr2;
    151.             break;
    152.         default:
    153.             break;
    154.         }
    155.  
    156.         ringBufS_put(L.rx2b, cr2);
    157.         if (cr2 == 0) DLED1 = S_OFF; // DEBUG flasher
    158.     }
    159.  
    160.     /*
    161.     * This is a little tricky, it normally runs at a very slow speed for a system heartbeat
    162.     * but it also times a delay for SPI data to a LCD display for data and commands
    163.     * ~36us for data and ~1.2ms for commands set in spi_link.delay
    164.     * with ~3us overhead for this part
    165.     */
    166.     if (INTCONbits.TMR0IF) { // check timer0 1 second timer & SPI delay int handler
    167.         DLED3 = S_ON;
    168.         if (spi_link.LCD_TIMER) {
    169.             spi_link.LCD_TIMER = LOW;
    170.             /*
    171.             * send the SPI data then reset timer0 for fast speed data delay
    172.             * send time per byte 11us
    173.             */
    174.             if (spi_link.LCD_DATA) {
    175.                 PIE1bits.SSPIE = LOW; // don't process the receive interrupt from the send yet
    176.                 SSPBUF = ct_spi; // send data
    177.             }
    178.             /*
    179.             * set the link delay
    180.             */
    181.             timer.lt = spi_link.delay; // Copy timer value into union
    182.             TMR0H = timer.bt[HIGH]; // Write high byte to Timer0
    183.             TMR0L = timer.bt[LOW]; // Write low byte to Timer0
    184.         } else {
    185.             if (spi_link.LCD_DATA) { // we are in a delay from sending a byte
    186.                 spi_link.LCD_DATA = LOW; // clear the data sent flag
    187.                 PIE1bits.SSPIE = HIGH; // enable to receive byte when ISR exits, ISR will be called again in ~5us to get it
    188.             }
    189.             // set normal delay
    190.             TMR0H = timer_long.bt[HIGH]; // Write high byte to Timer0
    191.             TMR0L = timer_long.bt[LOW]; // Write low byte to Timer0
    192.         }
    193.         INTCONbits.TMR0IF = LOW; //clear interrupt flag
    194.         DLED3 = S_OFF;
    195.     }
    196.  
    197.     if (PIR1bits.ADIF) { // ADC conversion complete flag
    198.         DLED2 = S_ON;
    199.         PIR1bits.ADIF = LOW;
    200.         adc_count++; // just keep count
    201.         if (L.ctmu_data) {
    202.             L.ctmu_adc = ADRES;
    203.             L.ctmu_data = LOW;
    204.         } else {
    205.             if (L.ctmu_data_temp) {
    206.                 L.pic_temp = ADRES;
    207.                 L.ctmu_data_temp = LOW;
    208.             }
    209.             adc_buffer[L.adc_chan] = ADRES;
    210.         }
    211.         DLED2 = S_OFF;
    212.     }
    213.  
    214.     /*
    215.     * trigger the ADC here
    216.     */
    217.     if (PIR3bits.CTMUIF) { // CTED (tx1) is high then CTED2 (rx2)went high
    218.         PIR3bits.CTMUIF = LOW; // clear CTMU flag
    219.         if (L.ctmu_data) {
    220.             ADCON0bits.GO = HIGH; // and begin A/D conv, will set adc int flag when done.
    221.             DLED4 = !DLED4;
    222.             CTMUCONHbits.CTMUEN = LOW;
    223.             CTMUICON = 0x00; // current off
    224.             PIE3bits.CTMUIE = LOW; // disable interrupt
    225.             CTMUCONHbits.EDGEN = LOW;
    226.             CTMUCONLbits.EDG1STAT = 0; // Set Edge status bits to zero
    227.             CTMUCONLbits.EDG2STAT = 0;
    228.         }
    229.     }
    230.  
    231.     if (INTCONbits.RBIF) {
    232.         b_data = PORTB;
    233.         INTCONbits.RBIF = LOW;
    234.     }
    235.  
    236.     if (INTCONbits.INT0IF) {
    237.         INTCONbits.INT0IF = LOW;
    238.         V.buttonint_count++;
    239.         hid0_ptr->bled_on = !hid0_ptr->bled_on;
    240.     }
    241.  
    242.     if (INTCON3bits.INT1IF) {
    243.         INTCON3bits.INT1IF = LOW;
    244.         V.buttonint_count++;
    245.         hid1_ptr->bled_on = !hid1_ptr->bled_on;
    246.     }
    247.  
    248.     DLED5 = HIGH;
    249. }
    250. #pragma    tmpdata
    251.  
     
    Last edited: Sep 11, 2015
Loading...