0

I have 50 and more of the same devices. The main materials found in the device:

  • 1 x Display: 2x16 LCD
  • 1 x I/O expander for I2C Bus: PCF8574T
  • 1 x Regulator: L78M05CDT-TR
  • 1 x PIC: PIC16F1765-I/SL
  • 1 x Rotary encoder
  • 1 x Switch for ON/OFF

The problem is one out of every 10 or 15 is faulty. The LCD doesn't launch correctly.

When I close the switch, 12 volts is applied to the circuit. Most of the devices launch and work perfectly, but a few of them are unable to initialize the LCD but rest of the code works fine (encoder readings, PWM outputs, etc.)

The PIC runs PCF8574T. The PCF8574T runs LCD.

All of these devices were manufactured in machine string. They were programmed with the same code at the same time.

Some determinations:

  1. I think, faulty devices can't start I2C with PCF8574T. When I close the switch, meaningless characters appear on a single line of screen and slide when I change the values via encoder. Second line is always empty. Normally there should be values that are updated occasionally on both lines of the LCD.
  2. The interesting one: When I touch the MCLR pin of the PIC with tweezers the PIC is reset and then the LCD starts fine. When you turn the switch on and off the same problem appears. MCLR pin has external pull ups. On the flawless devices when I touch the MCLR pin the PIC does reset as expected.
  3. On faulty devices only when I change the PIC and replace with a new one and reprogram it the problem goes away. When I change the rest of the components with new ones the problem stays.
  4. On faulty devices when I close the switch while the MCLR pin of the PIC is grounded, the LCD backlight turns on as expected and everything waits. When I set the MCLR high, the same problem occurs, same meaningless slideable chars. When I reset the PIC the same way again but without opening switch this time, problem goes away as I said previously.
  5. Tried to ramp-up the supply voltage several times while switch is closed. Ramp-up from 4V to 12V. Some times while ramp-up the PIC is reset several times then the problem goes away.

Do you think that in such a case do we need to suspect the code or should we look for the solution elsewhere?

enter image description here

enter image description here

The regulation

These are the decoupling and bypass caps located at nearest location to ICs

These are the decoupling and bypass caps located at nearest location to ICs.

enter image description here

enter image description here Yes this is what happens, these characters can shift right whenever I change a value.

PIC CONFIGS

#pragma config FOSC = INTOSC 
#pragma config WDTE = OFF    
#pragma config PWRTE = OFF    
#pragma config MCLRE = ON   
#pragma config CP = ON    
#pragma config BOREN = ON    
#pragma config CLKOUTEN = OFF  
#pragma config IESO = ON   
#pragma config FCMEN = ON   

#pragma config WRT = OFF 
#pragma config PPS1WAY = ON
#pragma config ZCD = OFF 
#pragma config PLLEN = ON    
#pragma config STVREN = ON  
#pragma config BORV = LO   
#pragma config LPBOR = OFF   
#pragma config LVP = OFF   

This is the I2C code from MCC:


#ifndef _I2C_H
#define _I2C_H


#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <xc.h>

#warning "This version of the I2C driver will be removed soon and the correct driver to use is the Foundation Services Library driver"

#ifdef __cplusplus  // Provide C++ Compatibility

    extern "C" {

#endif


typedef enum
{
    I2C_MESSAGE_COMPLETE,
    I2C_MESSAGE_FAIL,
    I2C_MESSAGE_PENDING,
    I2C_STUCK_START,
    I2C_MESSAGE_ADDRESS_NO_ACK,
    I2C_DATA_NO_ACK,
    I2C_LOST_STATE
} I2C_MESSAGE_STATUS;

typedef struct
{
    uint16_t  address;          // Bits <10:1> are the 10 bit address.
                                // Bits <7:1> are the 7 bit address
                                // Bit 0 is R/W (1 for read)
    uint8_t   length;           // the # of bytes in the buffer
    uint8_t   *pbuffer;         // a pointer to a buffer of length bytes
} I2C_TRANSACTION_REQUEST_BLOCK;


void I2C_Initialize(void);


void I2C_MasterWrite(
                                uint8_t *pdata,
                                uint8_t length,
                                uint16_t address,
                                I2C_MESSAGE_STATUS *pstatus);


void I2C_MasterRead(
                                uint8_t *pdata,
                                uint8_t length,
                                uint16_t address,
                                I2C_MESSAGE_STATUS *pstatus);


void I2C_MasterTRBInsert(
                                uint8_t count,
                                I2C_TRANSACTION_REQUEST_BLOCK *ptrb_list,
                                I2C_MESSAGE_STATUS *pflag);


void I2C_MasterReadTRBBuild(
                                I2C_TRANSACTION_REQUEST_BLOCK *ptrb,
                                uint8_t *pdata,
                                uint8_t length,
                                uint16_t address);


void I2C_MasterWriteTRBBuild(
                                I2C_TRANSACTION_REQUEST_BLOCK *ptrb,
                                uint8_t *pdata,
                                uint8_t length,
                                uint16_t address);

bool I2C_MasterQueueIsEmpty(void);

bool I2C_MasterQueueIsFull(void);

void I2C_BusCollisionISR( void );
void I2C_ISR ( void );


#ifdef __cplusplus  // Provide C++ Compatibility

    }

#endif

#endif //_I2C_H

#include "i2c.h"

typedef union
{
    struct
    {
            uint8_t full:1;
            uint8_t empty:1;
            uint8_t reserved:6;
    }s;
    uint8_t status;
}I2C_TR_QUEUE_STATUS;

typedef struct
{
    uint8_t                             count;          // a count of trb's in the trb list
    I2C_TRANSACTION_REQUEST_BLOCK *ptrb_list;     // pointer to the trb list
    I2C_MESSAGE_STATUS            *pTrFlag;       // set with the error of the last trb sent.
                                                        // if all trb's are sent successfully,
                                                        // then this is I2C_MESSAGE_COMPLETE
} I2C_TR_QUEUE_ENTRY;

typedef struct
{
    /* Read/Write Queue */
    I2C_TR_QUEUE_ENTRY          *pTrTail;       // tail of the queue
    I2C_TR_QUEUE_ENTRY          *pTrHead;       // head of the queue
    I2C_TR_QUEUE_STATUS         trStatus;       // status of the last transaction
    uint8_t                         i2cDoneFlag;    // flag to indicate the current
                                                    // transaction is done
    uint8_t                         i2cErrors;      // keeps track of errors


} I2C_OBJECT ;

typedef enum
{
    S_MASTER_IDLE,
    S_MASTER_RESTART,
    S_MASTER_SEND_ADDR,
    S_MASTER_SEND_DATA,
    S_MASTER_SEND_STOP,
    S_MASTER_ACK_ADDR,
    S_MASTER_RCV_DATA,
    S_MASTER_RCV_STOP,
    S_MASTER_ACK_RCV_DATA,
    S_MASTER_NOACK_STOP,
    S_MASTER_SEND_ADDR_10BIT_LSB,
    S_MASTER_10BIT_RESTART,

} I2C_MASTER_STATES;

#ifndef I2C_CONFIG_TR_QUEUE_LENGTH
        #define I2C_CONFIG_TR_QUEUE_LENGTH 1
#endif

#define I2C_TRANSMIT_REG                       SSP1BUF                 // Defines the transmit register used to send data.
#define I2C_RECEIVE_REG                        SSP1BUF                 // Defines the receive register used to receive data.

// The following control bits are used in the I2C state machine to manage
// the I2C module and determine next states.
#define I2C_WRITE_COLLISION_STATUS_BIT         SSP1CON1bits.WCOL     // Defines the write collision status bit.
#define I2C_MODE_SELECT_BITS                   SSP1CON1bits.SSPM     // I2C Master Mode control bit.
#define I2C_MASTER_ENABLE_CONTROL_BITS         SSP1CON1bits.SSPEN    // I2C port enable control bit.

#define I2C_START_CONDITION_ENABLE_BIT         SSP1CON2bits.SEN      // I2C START control bit.
#define I2C_REPEAT_START_CONDITION_ENABLE_BIT  SSP1CON2bits.RSEN     // I2C Repeated START control bit.
#define I2C_RECEIVE_ENABLE_BIT                 SSP1CON2bits.RCEN     // I2C Receive enable control bit.
#define I2C_STOP_CONDITION_ENABLE_BIT          SSP1CON2bits.PEN      // I2C STOP control bit.
#define I2C_ACKNOWLEDGE_ENABLE_BIT             SSP1CON2bits.ACKEN    // I2C ACK start control bit.
#define I2C_ACKNOWLEDGE_DATA_BIT               SSP1CON2bits.ACKDT    // I2C ACK data control bit.
#define I2C_ACKNOWLEDGE_STATUS_BIT             SSP1CON2bits.ACKSTAT  // I2C ACK status bit.

#define I2C_7bit    true

void I2C_FunctionComplete(void);
void I2C_Stop(I2C_MESSAGE_STATUS completion_code);

static I2C_TR_QUEUE_ENTRY                  i2c_tr_queue[I2C_CONFIG_TR_QUEUE_LENGTH];
static I2C_OBJECT                          i2c_object;
static I2C_MASTER_STATES                   i2c_state = S_MASTER_IDLE;
static uint8_t                             i2c_trb_count = 0;

static I2C_TRANSACTION_REQUEST_BLOCK      *p_i2c_trb_current = NULL;
static volatile I2C_TR_QUEUE_ENTRY         *p_i2c_current = NULL;


void I2C_Initialize(void)
{
    i2c_object.pTrHead = i2c_tr_queue;
    i2c_object.pTrTail = i2c_tr_queue;
    i2c_object.trStatus.s.empty = true;
    i2c_object.trStatus.s.full = false;

    i2c_object.i2cErrors = 0;

    // R_nW write_noTX; P stopbit_notdetected; S startbit_notdetected; BF RCinprocess_TXcomplete; SMP Standard Speed; UA dontupdate; CKE disabled; D_nA lastbyte_address; 
    SSP1STAT = 0x80;
    // SSPEN enabled; WCOL no_collision; CKP Idle:Low, Active:High; SSPM FOSC/4_SSPxADD_I2C; SSPOV no_overflow; 
    SSP1CON1 = 0x28;
    // ACKTIM ackseq; SBCDE disabled; BOEN disabled; SCIE disabled; PCIE disabled; DHEN disabled; SDAHT 300ns; AHEN disabled; 
    SSP1CON3 = 0x08;
    // SSP1ADD 79; 
    SSP1ADD = 0x4F;

    // clear the interrupt flags
    PIR1bits.SSP1IF = 0;
    PIR2bits.BCL1IF = 0;

    // enable the interrupts
    PIE1bits.SSP1IE = 1;
    PIE2bits.BCL1IE = 1;

}


uint8_t I2C_ErrorCountGet(void)
{
    uint8_t ret;

    ret = i2c_object.i2cErrors;
    return ret;
}

void I2C_ISR ( void )
{

    static uint8_t  *pi2c_buf_ptr;
    static uint16_t i2c_address         = 0;
    static uint8_t  i2c_bytes_left      = 0;
    static uint8_t  i2c_10bit_address_restart = 0;

    PIR1bits.SSP1IF = 0;

    // Check first if there was a collision.
    // If we have a Write Collision, reset and go to idle state */
    if(I2C_WRITE_COLLISION_STATUS_BIT)
    {
        // clear the Write colision
        I2C_WRITE_COLLISION_STATUS_BIT = 0;
        i2c_state = S_MASTER_IDLE;
        *(p_i2c_current->pTrFlag) = I2C_MESSAGE_FAIL;

        // reset the buffer pointer
        p_i2c_current = NULL;

        return;
    }

    /* Handle the correct i2c state */
    switch(i2c_state)
    {
        case S_MASTER_IDLE:    /* In reset state, waiting for data to send */

            if(i2c_object.trStatus.s.empty != true)
            {
                // grab the item pointed by the head
                p_i2c_current     = i2c_object.pTrHead;
                i2c_trb_count     = i2c_object.pTrHead->count;
                p_i2c_trb_current = i2c_object.pTrHead->ptrb_list;

                i2c_object.pTrHead++;

                // check if the end of the array is reached
                if(i2c_object.pTrHead == (i2c_tr_queue + I2C_CONFIG_TR_QUEUE_LENGTH))
                {
                    // adjust to restart at the beginning of the array
                    i2c_object.pTrHead = i2c_tr_queue;
                }

                // since we moved one item to be processed, we know
                // it is not full, so set the full status to false
                i2c_object.trStatus.s.full = false;

                // check if the queue is empty
                if(i2c_object.pTrHead == i2c_object.pTrTail)
                {
                    // it is empty so set the empty status to true
                    i2c_object.trStatus.s.empty = true;
                }

                // send the start condition
                I2C_START_CONDITION_ENABLE_BIT = 1;

                // start the i2c request
                i2c_state = S_MASTER_SEND_ADDR;
            }

            break;

        case S_MASTER_RESTART:

            /* check for pending i2c Request */

            // ... trigger a REPEATED START
            I2C_REPEAT_START_CONDITION_ENABLE_BIT = 1;

            // start the i2c request
            i2c_state = S_MASTER_SEND_ADDR;

            break;

        case S_MASTER_SEND_ADDR_10BIT_LSB:

            if(I2C_ACKNOWLEDGE_STATUS_BIT)
            {
                i2c_object.i2cErrors++;
                I2C_Stop(I2C_MESSAGE_ADDRESS_NO_ACK);
            }
            else
            {
                // Remove bit 0 as R/W is never sent here
                I2C_TRANSMIT_REG = (i2c_address >> 1) & 0x00FF;

                // determine the next state, check R/W
                if(i2c_address & 0x01)
                {
                    // if this is a read we must repeat start
                    // the bus to perform a read
                    i2c_state = S_MASTER_10BIT_RESTART;
                }
                else
                {
                    // this is a write continue writing data
                    i2c_state = S_MASTER_SEND_DATA;
                }
            }

            break;

        case S_MASTER_10BIT_RESTART:

            if(I2C_ACKNOWLEDGE_STATUS_BIT)
            {
                i2c_object.i2cErrors++;
                I2C_Stop(I2C_MESSAGE_ADDRESS_NO_ACK);
            }
            else
            {
                // ACK Status is good
                // restart the bus
                I2C_REPEAT_START_CONDITION_ENABLE_BIT = 1;

                // fudge the address so S_MASTER_SEND_ADDR works correctly
                // we only do this on a 10-bit address resend
                i2c_address = 0x00F0 | ((i2c_address >> 8) & 0x0006);

                // set the R/W flag
                i2c_address |= 0x0001;

                // set the address restart flag so we do not change the address
                i2c_10bit_address_restart = 1;

                // Resend the address as a read
                i2c_state = S_MASTER_SEND_ADDR;
            }

            break;

        case S_MASTER_SEND_ADDR:

            /* Start has been sent, send the address byte */

            /* Note: 
                On a 10-bit address resend (done only during a 10-bit
                device read), the original i2c_address was modified in
                S_MASTER_10BIT_RESTART state. So the check if this is
                a 10-bit address will fail and a normal 7-bit address
                is sent with the R/W bit set to read. The flag
                i2c_10bit_address_restart prevents the  address to
                be re-written.
             */
            if(i2c_10bit_address_restart != 1)
            {
                // extract the information for this message
                i2c_address    = p_i2c_trb_current->address;
                pi2c_buf_ptr   = p_i2c_trb_current->pbuffer;
                i2c_bytes_left = p_i2c_trb_current->length;
            }

            // check for 10-bit address
            if(!I2C_7bit && (0x0 != i2c_address))
            {  
                if (0 == i2c_10bit_address_restart)
                {
                    // we have a 10 bit address
                    // send bits<9:8>
                    // mask bit 0 as this is always a write                    
                    I2C_TRANSMIT_REG = 0xF0 | ((i2c_address >> 8) & 0x0006);
                    i2c_state = S_MASTER_SEND_ADDR_10BIT_LSB;
                }
                else
                {
                    // resending address bits<9:8> to trigger read
                    I2C_TRANSMIT_REG = i2c_address;
                    i2c_state = S_MASTER_ACK_ADDR;
                    // reset the flag so the next access is ok
                    i2c_10bit_address_restart = 0;
                }
            }
            else
            {
                // Transmit the address
                I2C_TRANSMIT_REG = i2c_address;
                if(i2c_address & 0x01)
                {
                    // Next state is to wait for address to be acked
                    i2c_state = S_MASTER_ACK_ADDR;
                }
                else
                {
                    // Next state is transmit
                    i2c_state = S_MASTER_SEND_DATA;
                }
            }
            break;

        case S_MASTER_SEND_DATA:

            // Make sure the previous byte was acknowledged
            if(I2C_ACKNOWLEDGE_STATUS_BIT)
            {
                // Transmission was not acknowledged
                i2c_object.i2cErrors++;

                // Reset the Ack flag
                I2C_ACKNOWLEDGE_STATUS_BIT = 0;

                // Send a stop flag and go back to idle
                I2C_Stop(I2C_DATA_NO_ACK);

            }
            else
            {
                // Did we send them all ?
                if(i2c_bytes_left-- == 0U)
                {
                    // yup sent them all!

                    // update the trb pointer
                    p_i2c_trb_current++;

                    // are we done with this string of requests?
                    if(--i2c_trb_count == 0)
                    {
                        I2C_Stop(I2C_MESSAGE_COMPLETE);
                    }
                    else
                    {
                        // no!, there are more TRB to be sent.
                        //I2C_START_CONDITION_ENABLE_BIT = 1;

                        // In some cases, the slave may require
                        // a restart instead of a start. So use this one
                        // instead.
                        I2C_REPEAT_START_CONDITION_ENABLE_BIT = 1;

                        // start the i2c request
                        i2c_state = S_MASTER_SEND_ADDR;

                    }
                }
                else
                {
                    // Grab the next data to transmit
                    I2C_TRANSMIT_REG = *pi2c_buf_ptr++;
                }
            }
            break;

        case S_MASTER_ACK_ADDR:

            /* Make sure the previous byte was acknowledged */
            if(I2C_ACKNOWLEDGE_STATUS_BIT)
            {

                // Transmission was not acknowledged
                i2c_object.i2cErrors++;

                // Send a stop flag and go back to idle
                I2C_Stop(I2C_MESSAGE_ADDRESS_NO_ACK);

                // Reset the Ack flag
                I2C_ACKNOWLEDGE_STATUS_BIT = 0;
            }
            else
            {
                I2C_RECEIVE_ENABLE_BIT = 1;
                i2c_state = S_MASTER_ACK_RCV_DATA;
            }
            break;

        case S_MASTER_RCV_DATA:

            /* Acknowledge is completed.  Time for more data */

            // Next thing is to ack the data
            i2c_state = S_MASTER_ACK_RCV_DATA;

            // Set up to receive a byte of data
            I2C_RECEIVE_ENABLE_BIT = 1;

            break;

        case S_MASTER_ACK_RCV_DATA:

            // Grab the byte of data received and acknowledge it
            *pi2c_buf_ptr++ = I2C_RECEIVE_REG;

            // Check if we received them all?
            if(--i2c_bytes_left)
            {

                /* No, there's more to receive */

                // No, bit 7 is clear.  Data is ok
                // Set the flag to acknowledge the data
                I2C_ACKNOWLEDGE_DATA_BIT = 0;

                // Wait for the acknowledge to complete, then get more
                i2c_state = S_MASTER_RCV_DATA;
            }
            else
            {

                // Yes, it's the last byte.  Don't ack it
                // Flag that we will nak the data
                I2C_ACKNOWLEDGE_DATA_BIT = 1;

                I2C_FunctionComplete();
            }

            // Initiate the acknowledge
            I2C_ACKNOWLEDGE_ENABLE_BIT = 1;
            break;

        case S_MASTER_RCV_STOP:                
        case S_MASTER_SEND_STOP:

            // Send the stop flag
            I2C_Stop(I2C_MESSAGE_COMPLETE);
            break;

        default:

            // This case should not happen, if it does then
            // terminate the transfer
            i2c_object.i2cErrors++;
            I2C_Stop(I2C_LOST_STATE);
            break;

    }
}

void I2C_FunctionComplete(void)
{

    // update the trb pointer
    p_i2c_trb_current++;

    // are we done with this string of requests?
    if(--i2c_trb_count == 0)
    {
        i2c_state = S_MASTER_SEND_STOP;
    }
    else
    {
        i2c_state = S_MASTER_RESTART;
    }

}

void I2C_Stop(I2C_MESSAGE_STATUS completion_code)
{
    // then send a stop
    I2C_STOP_CONDITION_ENABLE_BIT = 1;

    // make sure the flag pointer is not NULL
    if (p_i2c_current->pTrFlag != NULL)
    {
        // update the flag with the completion code
        *(p_i2c_current->pTrFlag) = completion_code;
    }

    // Done, back to idle
    i2c_state = S_MASTER_IDLE;

}

void I2C_MasterWrite(
                                uint8_t *pdata,
                                uint8_t length,
                                uint16_t address,
                                I2C_MESSAGE_STATUS *pflag)
{
    static I2C_TRANSACTION_REQUEST_BLOCK   trBlock;

    // check if there is space in the queue
    if (i2c_object.trStatus.s.full != true)
    {
        I2C_MasterWriteTRBBuild(&trBlock, pdata, length, address);
        I2C_MasterTRBInsert(1, &trBlock, pflag);
    }
    else
    {
        *pflag = I2C_MESSAGE_FAIL;
    }

}

void I2C_MasterRead(
                                uint8_t *pdata,
                                uint8_t length,
                                uint16_t address,
                                I2C_MESSAGE_STATUS *pflag)
{
    static I2C_TRANSACTION_REQUEST_BLOCK   trBlock;


    // check if there is space in the queue
    if (i2c_object.trStatus.s.full != true)
    {
        I2C_MasterReadTRBBuild(&trBlock, pdata, length, address);
        I2C_MasterTRBInsert(1, &trBlock, pflag);
    }
    else
    {
        *pflag = I2C_MESSAGE_FAIL;
    }

}


inline void I2C_WaitForLastPacketToComplete()
{
    while(i2c_state != S_MASTER_IDLE)
    {
        // If your code gets stuck here it is because the last packet is never completing
        // Most likely cause is that your interrupt is not firing as it should. Check if you have
        //   correctly enabled all MSSP, Peripheral and GIE interrupt settings.
    }
}

void I2C_MasterTRBInsert(
                                uint8_t count,
                                I2C_TRANSACTION_REQUEST_BLOCK *ptrb_list,
                                I2C_MESSAGE_STATUS *pflag)
{

    // check if there is space in the queue
    if (i2c_object.trStatus.s.full != true)
    {
        *pflag = I2C_MESSAGE_PENDING;

        i2c_object.pTrTail->ptrb_list = ptrb_list;
        i2c_object.pTrTail->count     = count;
        i2c_object.pTrTail->pTrFlag   = pflag;
        i2c_object.pTrTail++;

        // check if the end of the array is reached
        if (i2c_object.pTrTail == (i2c_tr_queue + I2C_CONFIG_TR_QUEUE_LENGTH))
        {
            // adjust to restart at the beginning of the array
            i2c_object.pTrTail = i2c_tr_queue;
        }

        // since we added one item to be processed, we know
        // it is not empty, so set the empty status to false
        i2c_object.trStatus.s.empty = false;

        // check if full
        if (i2c_object.pTrHead == i2c_object.pTrTail)
        {
            // it is full, set the full status to true
            i2c_object.trStatus.s.full = true;
        }

    }
    else
    {
        *pflag = I2C_MESSAGE_FAIL;
    }

    // for interrupt based
    if (*pflag == I2C_MESSAGE_PENDING)
    {
        I2C_WaitForLastPacketToComplete();

        // The state machine has to be started manually because it runs only in the ISR.
        // If we called the ISR function here function duplication would double the code size
        //    because this function would be called both from interrupt and from mainline code.
        PIR1bits.SSP1IF = true;

    }   // block until request is complete

}

void I2C_MasterReadTRBBuild(
                                I2C_TRANSACTION_REQUEST_BLOCK *ptrb,
                                uint8_t *pdata,
                                uint8_t length,
                                uint16_t address)
{
    ptrb->address  = address << 1;
    // make this a read
    ptrb->address |= 0x01;
    ptrb->length   = length;
    ptrb->pbuffer  = pdata;
}

void I2C_MasterWriteTRBBuild(
                                I2C_TRANSACTION_REQUEST_BLOCK *ptrb,
                                uint8_t *pdata,
                                uint8_t length,
                                uint16_t address)
{
    ptrb->address = address << 1;
    ptrb->length  = length;
    ptrb->pbuffer = pdata;
}

bool I2C_MasterQueueIsEmpty(void)
{
    return(i2c_object.trStatus.s.empty);
}

bool I2C_MasterQueueIsFull(void)
{
    return(i2c_object.trStatus.s.full);
}        

void I2C_BusCollisionISR( void )
{
    PIR2bits.BCL1IF = 0;
}            
JRE
  • 67,678
  • 8
  • 104
  • 179
Lenel
  • 43
  • 5
  • 3
    Where is your MCLR power-up timing capacitor? – Transistor Feb 20 '20 at 20:03
  • If reprogramming doesn't work and you actually have to replace the PIC, would that not count as a hardware issue? Unless you are asking if it's a hardware variability that could be, but isn't, accounted for in the code. – DKNguyen Feb 20 '20 at 20:03
  • 3
    It is possible that the PIC powers-up *before the LCD* or possibly *before the PCF8574*. Code may send LCD initialization sequence too early. The solution is to add a **do-nothing** delay from a reset or power-up. – glen_geek Feb 20 '20 at 20:04
  • No MCLR power-up timing capacitor was a mistake, I will pay attention after that, i tried to add the cap but didnt affect the problem. – Lenel Feb 20 '20 at 20:33
  • 1
    Maybe LCD init code has timing or logic issue and it does not work properly? Maybe I2C code is not fail-safe and some glitch prevents I2C comms? Post the code so we can see. Hardware issues are possible too; there is not a single bypass capacitor drawn on the schematics, and the regulator is not seen in the schematics so it is difficult to pinpoint a problem. – Justme Feb 20 '20 at 20:34
  • @Justme noooooo the lack of bypass caps! on 50 boards! – DKNguyen Feb 20 '20 at 20:41
  • I tried to put a __delay_ms(500) [XC8] before and after SYSTEM_Initialize but didnt affect the problem.Is it possible that the timing is suitable for most devices but not for a few of them? – Lenel Feb 20 '20 at 20:44
  • Bypass capacitors used only did not fit for screenshot – Lenel Feb 20 '20 at 20:45
  • I will share code and shematics when i able to do. – Lenel Feb 20 '20 at 20:53
  • 2
    What I2C speed you use? Are you aware that at powerup the IO expander sets all pins high with internal pull-ups so without proper handling of the IO expander the LCD might see falling E edge so it sees random command/data? When resetting the MCU, the expander keeps state between MCU resets, and resetting the MCU during middle of I2C transaction can leave the bus in a state where the IO expander is sending out ack bit or zero bit so SDA is low and MCU can't send start without special recovery sequence. Note that this IO expander does not have a reset pin so there is no easy way out of this. – Justme Feb 20 '20 at 20:58
  • 3
    Also beware that LCDs have two operating modes, 4bit and 8 bit. If you do not use the right start sequence they will stay in the same mode. If it comes up in 8 bit mode it will not work. – Oldfart Feb 20 '20 at 21:04
  • 2
    @Oldfart true, that's why it is important there is no glitches from the expander init, or if there is, the sequence must be designed so that it works in every case (I2C bus issue, expander init issue, LCD init issue). Proper sequence is to set the LCD forcibly back into 8-bit mode at start no matter at what state it is, and then return back to 4-bit mode and continue with the init. – Justme Feb 20 '20 at 21:13
  • I will start researching on the subject you have pointed out. But does this explain how it works properly when I replace and reprogram the PIC? – Lenel Feb 20 '20 at 22:13
  • 2
    Power-up issues, clock issues, start-up timer issues? Slow supply voltage ramp-up time, so the chip goes out of reset and starts to run even though voltage is too low to run at that frequency? Different PICs have variance/tolerance at how high speeds it can run vs supply voltage? What is the measured supply voltage? Does the LCD backlight clamp the supply voltage as there is no external current limiting resistor on the backlight? – Justme Feb 20 '20 at 23:00
  • 2
    Oh no, those schematics are abysmal! [Thankfully, here is a community wiki to help you with that!](https://electronics.stackexchange.com/questions/28251/rules-and-guidelines-for-drawing-good-schematics) By the way, did you know you can use net labels in Eagle to only connect some things by name, not all by actually running a line between them? It can significantly improve readability too, when used correctly. – Richard the Spacecat Feb 21 '20 at 11:52
  • Yep you are right and sorry for that. I ll follow your advides.. – Lenel Feb 21 '20 at 11:57
  • 4
    `I2C_ISR` is pretty much a school book example of how to never write an ISR. Apart from it being massive, it uses numerous variables that don't seem to be protected from race conditions: `i2c_state`, `p_i2c_current` etc. And you even write to these from the caller? If they aren't protected, the software will behave randomly. It doesn't look like you can salvage anything from that ISR, I would rewrite this from scratch. – Lundin Feb 21 '20 at 12:15
  • Make sure you have BOR enabled and the voltage configured to an appropriate voltage. – Spehro Pefhany Feb 21 '20 at 12:18

1 Answers1

1

The solution is putting this code before "lcd_init()"

void I2C_Test() {

    uint8_t writeBuffer[10] = {0,0,0,0,0,0,0,0,0,0};
    I2C_MESSAGE_STATUS status;
    uint8_t timeOut=0;

    while (status != I2C_MESSAGE_FAIL) {

        I2C_MasterWrite(writeBuffer,10,pcf_adres,&status);
        while (status == I2C_MESSAGE_PENDING);

        if (status == I2C_MESSAGE_COMPLETE) break;
        if (timeOut == 10)break; else timeOut++;
    }
}
```
JRE
  • 67,678
  • 8
  • 104
  • 179
Lenel
  • 43
  • 5