This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

I2C routine inside an UART interrupt

Other Parts Discussed in Thread: PCF8575C, TM4C123GH6PZ, PCF8575

Hello,

I try implement a I2C routine inside an uart interrupt.

In the main program the microcontroller (TM4C123GH6PZ) wait to a interrupt by UART. In this ISR, the data are identi and call a function with a routine I2C, this routine is used for communicate with a expansor (PCF8575C). My problem is that the slave device (PCF8575C) don't work correctly in this logic. I see the signal of the I2C protocol but the data in the output port is incorrectly.

The routine for communicate with an expansor, works correctly if call in the main program, but if called in the ISR the expansor don't work.

what could be the problem?

Thanks

Juan Carlos

  • These are the functions and the ISR that I'm use:

    void PCF8575_Write(uint8_t porta, uint8_t portb)
    {
    I2CMasterSlaveAddrSet(I2C0_BASE, 0x20, false);
    I2CMasterDataPut(I2C0_BASE, porta); //Escritura del valor en puerto A
    I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_START);
    while(I2CMasterBusy(I2C0_BASE));
    I2CMasterDataPut(I2C0_BASE, portb); //Escritura del valor en puerto B
    I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_FINISH);
    }

    void UART_CPU(void)
    {
    uint32_t ui32Status;
    uint8_t data_buffer[10]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
    int buffer_cpu[10]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
    uint16_t checksum=0x0000;
    int i=0;
    int j=0;
    int header=0;
    int flag_crc=0;

    ui32Status = UARTIntStatus(UART3_BASE, true);
    UARTIntClear(UART3_BASE, ui32Status);
    j=0;
    while(UARTCharsAvail(UART3_BASE))
    {
    buffer_cpu[j]=UARTCharGet(UART3_BASE);
    j=j+1;
    }
    header=buffer_cpu[0];

    if(header == 0x31)
    {
    for(i=0; i<6; i++)
    {
    data_buffer[i]=buffer_cpu[i];
    }
    checksum=((data_buffer[4]<<8)|data_buffer[5]);
    flag_crc=CRC_Check(data_buffer,checksum,4);
    flag_crc=0;
    if(flag_crc==0)
    {
    PCF8575_Write(data_buffer[1], data_buffer[2]);
    }
    else
    {
    UARTSend(UART0_BASE,(const unsigned char *)&"Trama inválida",14);
    UARTSend(UART0_BASE,(const unsigned char *)&"\r\n",3);
    }
    }
    }
  • Why are you doing this in the ISR?

    It's generally a Bad Thing to have code like this in an ISR - especially with busy-wait loops!

    Usually, it's best to have the ISR do just the bare minimum to recognise the interrupt, and leave any extended processing to the "main line" code...

  • Thank Andy.

    Well, in my system have a CPU that sends instructions to TIVA. This instructions indicated a subsystem to will be activated by one output (bit in the output port) of the expansor. This activation is important, because this expansor controls a circuit for activated of the relays the low voltage power supplies the other system.
  • That doesn't answer the question, "Why are you doing this in the ISR?"
  • Andy Neil said:
    That doesn't answer the question

    Mon (long departed) ami - Bonjour - and so nice to note (once more) your, "Stickler for detail" presence...

    While you're surely correct in urging, "Short/sweet" (i.e. "ram/scram") interrupt coding - such is not, "intuitively clear" to so many new users.

    Of course I agree (completely) w/you - yet perhaps your further explanation of the "negative impact" of poster's use of ISR as, "kitchen sink" would (better) win his cooperation.    Mais non?

    Again greetings - San Jose (not that I'm there) misses...

  • Let me chime in to agree with Andy and CB1.  This is the wrong approach.  It would be questionable even for dedicated SPI never mind IIC.  The trouble with IIC in particular is that it is slow and non-deterministic1, so its finish will not be certain.

    The usual method for handling this would be separate your parsing and response to the serial interrupt from the actual I/O.  Similarly for the IIC.

    Robert

    1 - Note this line in your code while(I2CMasterBusy(I2C0_BASE));

  • Hello Sanchez,

    Agree with cb1, Andy and Robert.

    You must set a flag in the UART ISR to indicate to your main code that a data has been received and send the data via your main code. Do note of a potential hazard. Since the main execution is lower priority then ISR, if during I2C processing there is another UART data received, the Cortex Core would go to the ISR and update the variables for the data which may not have been sent out yet.

    You would need a software lock to prevent an over write of the existing data buffers.

    Regards
    Amit
  • May I state, "Well done" Amit? None of we earlier commenters thought deep enough - thus "missed" that (likely) "new UART data arrival.

    Poster may require not only the "software lock" you identify - but the ability to store or best process that "earlier arrived" UART data as well.
  • The TivaWare Utils include "uartstdio" - this gives you a simple, interrupt-driven "driver" for the UART so that your main-line code can just enter a UARTgetc() call and that will return the next character from the UART as soon as it is available ...
  • I cannot tell - from the brevity of your writing - if you believe your, "uartstdio" suggestion superior to that method suggested by Amit.
    Minus the interlock which Amit notes - does not your method subject the user to missed data or other weakness?
  • The "uartstdio" provides a ring buffer between the UART and the "main" application - so that the problem of the over-write does not arise.

    (unless, of course, the UART data is arriving so fast that the code cannot handle it at all - in which case no amount of software interlocks or other clever tricks can help).
  • Thanks for that clarification - as my small firm employs many ARM MCUs (multiple vendors) these concerns have been noted:

    a) might the UART's FIFO impact that ring buffer - or vice versa?

    b) your, "arrives so fast ... code cannot handle it at all" is a rather "extreme" case - is it not?    I'm not so sure that, "uartstdio" rises to "optimal" solution class...

  • a) The FIFO just means that you can load/unload multiple bytes to/from the ring buffer in a single interrupt.

    b) Yes, it is the limiting case. No, "uartstdio" doesn't claim to be "optimal" - but it's not significantly bloated, either.
  • Hello,

    Sorry to respond so late , I was very busy trying to solve other problems that emerged in other areas .
    Implement what you said to me , to reduce the processes within the interruption , which got a correct answer of my system.

    Greetings and thanks