IIC problems with HCS08

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

IIC problems with HCS08

9,374 Views
Splinter_Cat
Contributor I
   I am trying to get my IIC bus to work on an HCS08 microporcessor.  I am using GB60_IIC.mcp that was provided with HCS08QRUG.pdf.  I can get one transmission from slave to master to work, but RXAK (IIC1S_RXAK) bit is being set high at the end of transmission, throwing my status register, I2C_STEP, into error status.  If everything was going smoothly, it would be set low.

What I would like to do is repeatedly send changing data from slave to master, but this error keeps me from sending a second transmission

This happens in the interrupt handler, with the following code:

        if(IIC1S_RXAK==1){      /* Verify if byte sent was ACK */
          IIC1C_MST = 0;
        I2C_STEP = IIC_ERROR_STATUS;
        return;
        }

I tried commenting this out, but the second data transfer would not pass.  Does anybody know why RXAK is being set high? I greatly appreciate any help.
Labels (1)
0 Kudos
7 Replies

860 Views
LordQ
Contributor I
Well, in fact I copy these lines from a generic code in HC08.net and works great !!! for the I2C
Code:
/*********************************************************************** FileName:        I2C.h* Dependencies:    none* Processor:       MC68HC908AS60A* Compiler:        CodeWarrior HC08 v5.00 or higher** REVISION HISTORY: 1.0* ADDITIONAL NOTES:* This driver works only for any I2C (IIC) interface.**********************************************************************/#ifndef I2C_INCLUDED#define I2C_INCLUDED#define I2C_SEND_ACK 0x01       // send acknowledge to slave#define I2C_NOT_SEND_ACK 0x00   // not send acknowledge to slavevoid          I2C_init(void);void          I2C_start(void);void          I2C_stop(void);void          I2C_general_reset( void );unsigned char I2C_tx_byte(unsigned char B);unsigned char I2C_rx_byte(unsigned char acknowledge);#endif=========== End of I2C.h file ==================================/*********************************************************************** FileName:        I2C.c* Dependencies:    I2C.h* Processor:       MC68HC908AS60A* Compiler:        CodeWarrior HC08 v5.00 or higher** REVISION HISTORY: 1.0** ADDITIONAL NOTES:*   This module is processor-independent**  I2C_init()           initializes the IIC driver*  I2C_start()          issues a START condition*  I2C_stop()           issues a STOP  conditiom*  I2C_tx_byte( data )  transmits a byte, returns acknowledge*  I2C_rx_byte( ack)    receives a byte, if ack=TRUE acknowledges the sender** The port PTF2 is the SDA line* The port PTF3 is the SCL line* change it at your wish to set the properly lines for I2C communications* the amazing of this code is you can create your own I2C port doesn't care* if your processor is capable of I2C communications.**********************************************************************/#include <mc68hc908as60a.h>#include "i2c.h"static void I2C_tx_bit_1( void );        //transmits a logic 1static void I2C_tx_bit_0( void );        //transmits a logic 0static unsigned char I2C_rx_bit( void ); //receives a bit////////////////////////////////////////////////////////////////////////////////// CONFIGURATION SECTION// the following code provides the methods for setting the IIC SDA (data) and// SCL (clock) lines, and a routine to time the bus speed.// Change them to configure for the processor/pins you are using////////////////////////////////////////////////////////////////////////////////#define SCL_high   {PTF_PTF3 = 1; I2C_wait();}#define SCL_low    {PTF_PTF3 = 0; I2C_wait();}// #define SCL_pullup_off {PTFPUE_PTFPUE5 = 0; }    // because our processor don't have a pull-up register#define SCL_output {DDRF_DDRF3 = 1;}#define SDA_high   {PTF_PTF2 = 1; I2C_wait();}#define SDA_low    {PTF_PTF2 = 0; I2C_wait();}// #define SDA_pullup {PTFPUE_PTFPUE4 = 1; }        // because our processor don't have a pull-up register#define SDA_output {DDRF_DDRF2 = 1;}#define SDA_input  {DDRF_DDRF2 = 0;}#define SDA_value  (PTF_PTF2)static void I2C_wait( void ){    unsigned char i;    for ( i = 0; i < 10; i++ )     ;;}////////////////////////////////////////////////////////////////////////////////// END OF CONFIGURATION SECTION// you should not need to change anything below this line/////////////////////////////////////////////////////////////////////////////////*---------------------------------------------------------------------  Function Name: I2C_init  Description:   initializes IIC system  Inputs:        None  Returns:       None----------------------------------------------------------------------- */void I2C_init( void ) //initializes IIC system{    //    SCL_pullup_off;   // because our processor don't have a pull-up register    //    SDA_pullup;       // because our processor don't have a pull-up register    SCL_output;    SDA_output;    I2C_stop();}/*---------------------------------------------------------------------  Function Name: I2C_start  Description:   issues a START condition  Inputs:        None  Returns:       None----------------------------------------------------------------------- */void I2C_start( void ) //issues a START condition{    SDA_output;    SDA_high;    SCL_high;    SDA_low;}/*---------------------------------------------------------------------  Function Name: I2C_stop  Description:   issues a STOP condition  Inputs:        None  Returns:       None----------------------------------------------------------------------- */void I2C_stop( void ) //issues a STOP condition{    SDA_output;    SDA_low;    SCL_high;    SDA_high;}/*---------------------------------------------------------------------  Function Name: I2C_tx_byte  Description:   transmits a byte  Inputs:        the byte to transmit  Returns:       returns device acknowledge----------------------------------------------------------------------- */unsigned char I2C_tx_byte( unsigned char B ){    unsigned char i;    for ( i = 0; i < 8; i++ )    {        if( B & 0x80 )I2C_tx_bit_1();        else I2C_tx_bit_0();        B = B << 1;    };    i = I2C_rx_bit();    return (i);}/*---------------------------------------------------------------------  Function Name: I2C_rx_byte  Description:   receives a byte  Inputs:        if acknowledge = 1 generates the ACK bit  Returns:       returns device acknowledge----------------------------------------------------------------------- */unsigned char I2C_rx_byte( unsigned char acknowledge ){    unsigned char i, retval;    retval = 0;    for ( i = 0; i < 8; i++ )    {        retval = (retval << 1);        if( I2C_rx_bit() )retval++;    };    if( acknowledge )    {        I2C_tx_bit_0();    }    else    {        I2C_tx_bit_1();    };    return retval;}////////////////////////////////////////////////////////////////////////////////// BIT-LEVEL, INTERNAL USE ROUTINES FOLLOW/////////////////////////////////////////////////////////////////////////////////*---------------------------------------------------------------------  Function Name: I2C_tx_bit_1  Description:   transmits a logic 1  Inputs:        None  Returns:       None----------------------------------------------------------------------- */static void I2C_tx_bit_1( void ) //transmits a logic 1{    SCL_low;    SDA_output;    SDA_high;    SCL_high;    SCL_low;}/*---------------------------------------------------------------------  Function Name: I2C_tx_bit_0  Description:   transmits a logic 0  Inputs:        None  Returns:       None----------------------------------------------------------------------- */static void I2C_tx_bit_0( void ) //transmits a logic 0{    SCL_low;    SDA_output;    SDA_low;    SCL_high;    SCL_low;}/*---------------------------------------------------------------------  Function Name: I2C_rx_bit  Description:   receives a bit  Inputs:        None  Returns:       returns ACK status----------------------------------------------------------------------- */static unsigned char I2C_rx_bit( void ) //receives a bit,returns ACK status{    unsigned char retval;    SDA_input;    SCL_low;    SCL_high;    retval = SDA_value;    SCL_low;    return retval;}/*---------------------------------------------------------------------  Function Name: I2C_general_reset  Description:   Send a General Reset command to all I2C devices  Inputs:        None  Returns:       None----------------------------------------------------------------------- */void I2C_general_reset( void ) // reset all devices into the IIC system{  unsigned char dummy;    //    SCL_pullup_off;   // because our processor don't have a pull-up register    //    SDA_pullup;       // because our processor don't have a pull-up register    I2C_start();    dummy = I2C_tx_byte(0x00);  // All addresses listen to me    dummy = I2C_tx_byte(0x06);  // I'm sending the general reset command        I2C_stop();}

 
0 Kudos

860 Views
Splinter_Cat
Contributor I
FC, thanks for your help.  I tried reseetting the IIC_IICEN reg like you said in one of your previous posts, but I still can't complete more than one transfer.

Is there a problem with the HCS08 IIC bus similar to what FC found in this post: http://forums.freescale.com/freescale/board/message?board.id=8BITCOMM&message.id=5590#M5590  ?
0 Kudos

860 Views
FC
Contributor III
Hi,
 
I think there is a bug in the HCS08 IIC module because the busy flag never changes indicating an idle bus.  The bus must be idle prior to any start bit
 
I reset the bus by enabling and disabling the enable bits.
 
Search for my post for details.
0 Kudos

860 Views
nosubstitute
Contributor I

I haven't been able to get a response from the RXAK bit - it is set whether the MC9S08 (always in slave mode) receives an ACK or not from the master.  To get around this, I changed the program so that:

 

1) master addresses slave in write mode

2) master sends starting slave memory address for data transfer

3) master sends number of bytes to be transferred

4) master either proceeds to write the specified number of bytes, or switches to read mode and reads the specified number of bytes.

 

This method requires an extra byte to be sent each time, but it's faster than waiting for the i2c bus to be reset by toggling the IIC_IICEN bit.

 

Comments from anyone are definitely welcome, especially from Freescale reps!

 

-NS

0 Kudos

860 Views
nosubstitute
Contributor I

After taking a closer look at the iic bus with a scope, I see that the master properly acknowledges each byte up until the last byte, which is not acknowledged.  I also added code in the interrupt to set GPIO pins on the MC9S08QG8:  PTBD6 is set high upon entering the interrupt and PTBD7 is set high when RXAK is low (ACK detected), and PTBD7 is set low when RXAK is high (NACK detected).  Both of these are set low upon exiting the interrupt.  This showed the microcontroller entering the interrupt as expected, but the RXAK bit is set low even though a NACK is received from the master on the last byte.

 

Is there a problem in the MC9S08 with setting the RXAK bit?  What would cause this bit to be set low even when an ACK is not received?

 

-NS

0 Kudos

860 Views
Summerville
Contributor I

I have exactly the same problem.  Programming a simple test of a polled-I/O IIC slave in assembly on a QG4.  The subroutine is repeatedly called form main.  It implements a 1 byte memory.  The slave receive works just fine.  When the slave has to transmit, though, it holds SDA and SCL low and BUSY remains high unless IICEN is toggled.  My code is shown below.  With the fudge it works fine.  Should not be, though. It seems that if there is a bug, Freescale should acknowledge.  If not, then what am I doing wrong??

 

IIC_SLAVE   psha
            brclr IICIF,IICS,END_IIC_S  ;if no event do nothing
            bset  IICIF,IICS            ;else clear IICIF flag
            brclr IAAS,IICS,END_IIC_S   ;also do nothing if not addressed            
            brset SRW,IICS,SLAVE_READ   ;choose read or write
SLAVE_WRITE bclr  TX,IICC      ;mode is RX, also clears IAAS
            lda   IICD         ;initiate slave read 
            brclr IICIF,IICS,* ;wait for end of tranmission or arbl
            bset  IICIF,IICS   ;ensure flag clear
            brset ARBL,IICS,END_IIC_S ;if arbitration lost, skip rest
            bclr  TX,IICC      ;get out of RX mode so load does not start new xfer
            lda   IICD         ;get RX data
            sta   IICDATA      ;and save to global var
            bra   END_IIC_S    ;and done
SLAVE_READ  bset  TX,IICC      ;mode is TX, also clears IAAS
            lda   IICDATA      ;get value from global var
            sta   IICD         ;initiate TX
            brclr IICIF,IICS,* ;wait for end of tranmission or arbl
            bset  IICIF,IICS   ;ensure flag clear
            bclr  TX,IICC      ;may not be needed 
            bclr   IICEN,IICC  ;this is the *fudge* needed to make it work
            bset   IICEN,IICC

END_IIC_S   bset  ARBL,IICS    ;in case we got here because ARBL=1
            pula               
            rts

0 Kudos

860 Views
nosubstitute
Contributor I

I am having the same problem that is described above:

 

I'm using the MC9S08QG as a slave only.  The master is an I2C communication module from NI and is only able to complete one read/write session.  All subsequent attempts at the same communication fail.  After watching the SDA and SCL lines with a scope, everything looks as it should:  the master issues a start, calls the slave in write mode, sends a starting address, issues a restart, calls the slave in read mode, acknowledges each byte up until the last, which is not acknolwedged.  The MC9S08QG then holds the SDA line low for some reason and no stop signal is seen on the scope.

 

The code I am using for the iic interrupt is posted below.  It is almost the same as that provided in Freescale's AN3048.

 

 

__interrupt void isrViic(void)
{
  /* Write your interrupt code here ... */
   
    // clear interrupt bit
    IICS_IICIF = 1;
    // Always in slave operation
   
    // check arbitration lost bit
    if(IICS_ARBL)
    {
        // if ARBL set, clear it and check IAAS bit
        IICS_ARBL = 1;
        if(IICS_IAAS)        // start and addressed as slave
        {
            iic_count = 0;
            // if IAAS set, check SRW for address transfer
            check_SRW();
        }
        else
        {
            // if IAAS not set, return from interrupt

        }

    }
   
    else
    {
        // if ARBL not set, check IAAS bit
        if(IICS_IAAS)        // start and addressed as slave
        {
            iic_count = 0;
            // if IAAS set, check SRW for address transfer
            check_SRW();
        }
        else
        {
            // if IAAS not set, check TX or RX for data transfer
            if(IICC_TX)
            {
                // if TX, check ACK from receiver
                if(~IICS_RXAK)
                {
                    // if ACK detected, transmit next byte
                    IICD = send_data(iic_regaddress++);
                    iic_count++;
                }
                else
                {
                    // if ACK not detected, switch to RX mode, dummy read from IICD
                    IICC_TX = 0;                    // clears IAAS bit
                    IICD;
                }
            }
            else
            {

                // if RX, read data from IICD and store
                if(iic_count == 0)
                {
                    // receive an address
                    iic_regaddress = IICD;
                    iic_count++;
                }
                else
                {
                    // receive data
                    receive_data(iic_regaddress++);
                    iic_count++;                   
                }
               
            }
        }
    }
   
    // need to leave SDA high so the bus will be free!

}
 

 

 

void check_SRW(void)
{
    if(IICS_SRW)
    {
        // if set, set to transmit mode and write data to IICD
        IICC_TX = 1;
        IICD = send_data(iic_regaddress++);
        iic_count++;
    }
    else
    {
        // if not set, set to receive mode and read dummy data from IICD
        IICC_TX = 0;
        IICD;
    }
}

 

 

The "send_data" and "receive_data" functions just return the value to be written or save the received data.

 

As suggested by others, I'm able to basically reset the IIC module by toggling the IICC_IICEN bit in main(), but there must be a way to keep from getting into the situation where the SDA line is held low.  Any help would be appreciated.

 

-NS

0 Kudos