Posting some example code for QG8 IIC

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

Posting some example code for QG8 IIC

5,030 Views
fm
Contributor I
Up to now I've failed to find good examples of I2C code for the QG8 (or other HCS08) 8-bit processors that fully take advantage of the IIC interrupts and work for both master and slave.  Most examples have been minimal (AN3048) or have not provided a complete master/slave solution (AN3291).  The closest example is AN2318 for the HCS12 (which uses the same IIC peripheral). 
 
It's taken me about 2 years (in my spare time), but I finally have some full-featured code based on the above app notes, code snippets on the internet and debugging with a scope.  It is used for an application where 3 QG8's, 1 QE128, and 1 24AA256SN Flash memory are all on the same bus (1 master, 3 slaves, 1 master & slave).  It uses an interrupt-driven approach, the master can transmit/receive data while continuing to process other work, an application can be a master and then turn around and act as a slave for another processor.  Code supports using program variables directly as buffers, or seperate buffers can be used.  In my application, data is transmitted as words (vs bytes), but this can be changed. 
 
To save others some of this trouble I thought I would post these example master and slave projects:
 
Labels (1)
0 Kudos
11 Replies

1,105 Views
FWFan
Contributor III

Hi,

 

I have my IIC code for an EEPROM.  It only saves to the eeprom, so the code is not a complete master and slave communication.  But it does follow the flow chart in the IIC section of the MC09SQE128 microcontroller correspondingly.  If you need more files that go with this code please let me know.

 

FWFan

0 Kudos

1,105 Views
mad_dad
Contributor II
fm : I have also developed something similar to your code. But although mine works fine with one master and one slave, the bus locks up with more than one slave. I'm sure this must be a hardware problem.
 
I was wondering how you connected your devices together. I have some  9S08AW60 demo boards wire directly together with pull up resistors.
 
How did you connect your I2C bus?
0 Kudos

1,105 Views
fm
Contributor I
The master SDA pin is connected to all slave SDA pins.  The master SCL pin is connected to all slave SCL pins.  There are 1K pull-up resistors on each, although the value is probably not that critical (1K to 10K).  Obviously you need a common ground.  Physically my boards are daisy chained within a few inches w/ 6 inches or so of wire (unshielded) connecting them.
 
I recall having a similar situation in my debugging, and you have to be sure each slave frees the bus.  I am guessing its a sw problem, unless you are using some long wire runs.  You may need a scope  to see whats going on.
 
The HCS08 IIC peripheral is very flexible (too flexible?) and so your code does most of the work on both the master and slave sides.  The shared bus makes debugging complicated and time consuming.
0 Kudos

1,105 Views
mad_dad
Contributor II
It turns out that I needed to set the drive strength for SDA and SCL to low drive. I set PTCDS to 0x7C and it all burts into life. This was covered in another post
 
 
I noww have a master and 5 slaves, each slave can have a different message size (unlike yours, message size is predefined). It operates much like a MILStd1553 bus. In fact it is to be used in a similar situation.
 
You are right about releasing the bus, I remember that the code was quite critical around that area.
 
And yes debugging was very tricky with the slaves when interrupts are constantly occuring.
 
If anyone is interested and your example is not suitable I could post my code.


Message Edited by mad_dad on 2008-10-14 10:15 AM
0 Kudos

1,105 Views
FWFan
Contributor III
Please post your code too.
 
Thank you,
FWFan
0 Kudos

1,105 Views
mad_dad
Contributor II
I2C MASTER DRIVER SPEC
tx,rx message size predefined
messages sent every 10ms
for read data send command and wait for not busy
 

#ifndef I2C_Master_Driver_h
#define I2C_Master_Driver_h 1
 
#include "Types.h" // definitions of bool,byte
#include "IO_Map.h"  // hardware definitions
 
// The Master I2C driver can command a TX or RX message to
// any slave.
// A Tx or Rx message is initiated with the slave address
// and then an interrupt is generated by the device after
// each byte is sent or received.
// The length of the messages can be different for each
// slave.
// Max message size 16 bytes.
// Get_Busy_Status returns True whilst bus active
// Use Get_Busy_Status to indicate message transaction
// complete before Get_Read_Bytes.
 
#define IIC_DIV 0x00  // mult 1, ICR 00 (SCL div = 20, SDA hold = 7)
// 4MHz / (1 * 20) = 200K, SDA hold time = (1/4Mhz) * 7 = 1.75us
 
    #define IIC_BUFFER_SIZE 16  
      // Initailise the I2C module for master mode
      extern void Init_I2CM(void );
      // Interrupt handler to manage all master events on the I2C
      // link
      extern interrupt void I2C_ISR(void );
      // Reports True during transmission
      extern bool I2CM_Get_Busy_Status(void );
      // Return the success of the last transmission.
      extern bool I2CM_Get_No_Resp_Status(void );
      // Starts an I2C transmission to read a number of bytes
      // from a slave at an address. When complete use Get_Read_
      // Bytes for data received.
      extern void I2CM_Read_Bytes(byte Slave_Addr, byte Number_Of_Bytes);
      // Copies data received to pointer location supplied.
      // Only copies the number of bytes that was requested in
      // the message.
      extern void I2CM_Get_Read_Data(byte* Data);
      // Starts an I2C transmission to write a number of bytes to
      // a slave at an address. Copies data from pointer
      // location, only copies number of bytes requested..
      extern void I2CM_Write_Bytes(byte Slave_Addr, byte Number_Of_Bytes, const byte* Data);
#endif
 
BODY
 

#include "I2C_Master_Driver.h"
      
#define IIC_ERROR_STATUS 0
#define IIC_READY_STATUS 1
#define IIC_HEADER_SENT_STATUS 2
#define IIC_DATA_TRANSMISSION_STATUS 3
#define IIC_DATA_SENT_STATUS 4
 
static byte I2CM_RX_Data[IIC_BUFFER_SIZE];
static byte I2CM_TX_Data[IIC_BUFFER_SIZE];
static byte I2CM_Counter = 0;
static byte I2CM_Step = IIC_READY_STATUS;
static byte I2CM_Length = 1;
static byte I2CM_Data_Direction = 0;
static byte Last_But_One = 0;
static bool No_Resp = TRUE;
 
void Init_I2CM(void )
{
    _IIC1C.Bits.IICEN = 1;  /* Enable IIC module        */
    _IIC1F.Byte = IIC_DIV;   /* Set IIC frequency = 100K Baud  */
    I2CM_Step = IIC_READY_STATUS;
    _IIC1C.Bits.IICIE = 1;  /* Enable IIC interrupts */
}
 
interrupt void I2C_ISR(void )
{
  byte Temp;
 
  /* Interrupts generated after the slave address has been sent and after each data byte
   ,write or read mode, when performing a read the IIC needs to be disabled straight after the data
   byte is read or another interrupt will be generated.*/
  
  Temp = _IIC1S.Byte;     /* ACK the interrupt */
  _IIC1S.Bits.IICIF=1;
  /* Verify the Arbitration lost status */
  if(_IIC1S.Bits.ARBL==1){        
      _IIC1S.Bits.ARBL = 1; // clear=1
      _IIC1C.Bits.MST = 0;  // reset master bit
      _IIC1C.Bits.IICEN = 0;
      I2CM_Step = IIC_ERROR_STATUS;
      return;    
  }       
  /* if State = Header Sent */               
   if(I2CM_Step == IIC_HEADER_SENT_STATUS){
      if(_IIC1S.Bits.RXAK==1){
       _IIC1C.Bits.MST = 0;  // reset master bit
       _IIC1C.Bits.IICEN = 0;
       I2CM_Step = IIC_READY_STATUS;
       return;  
     } 
      else {
        _IIC1C.Bits.TX = I2CM_Data_Direction;       // set direction
        I2CM_Step = IIC_DATA_TRANSMISSION_STATUS;      // set next state   
        if(_IIC1C.Bits.TX == 0){  /* If we are reading data clock in first slave byte */
        Temp = _IIC1D.Byte; // read data byte
        return;
        } 
      }
   }
    
  /* If state = byte transmision is in progress.*/    
   if(I2CM_Step == IIC_DATA_TRANSMISSION_STATUS){ 
      if(_IIC1C.Bits.TX == 1) { /* If Master is sending data to slave */                                          
        _IIC1D.Byte = I2CM_TX_Data[I2CM_Counter];  /* Send the next byte */
        I2CM_Counter += 1;
        /* Mark we are done sending Bytes */   
       if(I2CM_Length == I2CM_Counter) {
         I2CM_Step = IIC_DATA_SENT_STATUS;
        }
      }
      else {  // reading data from slave      
        if(I2CM_Counter == Last_But_One) {    // last byte ?                  
          I2CM_RX_Data[I2CM_Counter] = _IIC1D.Byte;    /* Read the byte */        
          _IIC1C.Bits.MST = 0;       // reset master bit to
                                   // Generate a stop condition
          _IIC1C.Bits.IICEN = 0;                     // disable IIC                 
          I2CM_Counter += 1;  // do not optimise by moving above
          I2CM_Step = IIC_READY_STATUS;         //finished
          No_Resp = FALSE;                  
        }
        else {
          I2CM_RX_Data[I2CM_Counter] = _IIC1D.Byte;    /* Read the next byte */
          I2CM_Counter += 1;
        }
                            
        /*  Master should not ACK the last byte */
        if(I2CM_Counter == Last_But_One) _IIC1C.Bits.TXAK = 1; /* If penultimate byte indicate end of transfer */
      }
                       
   }
   /* if state = We are done with the transmition.*/              
   else {
   
      _IIC1C.Bits.TX = 0;       // set RX mode       
      _IIC1C.Bits.MST = 0;      // reset master bit to
                             // Generate a stop condition
      _IIC1C.Bits.IICEN = 0;          // disable IIC
      I2CM_Step = IIC_READY_STATUS;         // finished
      No_Resp = FALSE; 
   }
}
 
bool I2CM_Get_Busy_Status(void )

  return (I2CM_Step > IIC_READY_STATUS);
}
 
bool I2CM_Get_No_Resp_Status(void )

   // return TRUE if no response from last requested slave
  return No_Resp;
}
 
void I2CM_Read_Bytes(byte Slave_Addr, byte Number_Of_Bytes)
{
  byte Temp;
 
  _IIC1C.Bits.IICEN = 0;
  I2CM_Length = Number_Of_Bytes;
  Last_But_One = Number_Of_Bytes - 1;     // save time calculating later
  I2CM_Counter = 0;    // reset counter
  I2CM_Step = IIC_HEADER_SENT_STATUS;
  I2CM_Data_Direction = 0;  // set for read
  No_Resp = TRUE;
  _IIC1C.Bits.IICEN = 1; 
  /* Format the Address to fit in the IICA register and place a 1 on the R/W bit. */
  Slave_Addr <<=1;    // shift left 1 bit
  Slave_Addr &= 0xFE;
  Slave_Addr |= 0x01;   /* Set the Read from slave bit. */
  Temp = _IIC1S.Byte;     /* Clear any pending interrupt  */
  _IIC1S.Bits.IICIF=1;
  _IIC1C.Bits.MST = 0;    // reset master bit
  _IIC1C.Bits.TX = 1;     /* Select Transmit Mode          */
  _IIC1C.Bits.MST = 1;    /* Select Master Mode (Send Start Bit)*/
  _IIC1D.Byte=Slave_Addr;    /* Send selected slave address   */
}
 
void I2CM_Get_Read_Data(byte* Data)
{
  byte Temp;  
 
  for(Temp=0; Temp<I2CM_Length; Temp++)
      Data[Temp] = I2CM_RX_Data[Temp]; 
}
 
void I2CM_Write_Bytes(byte Slave_Addr, byte Number_Of_Bytes, const byte* Data)
{
  byte Temp;
  _IIC1C.Bits.IICEN = 0;
  I2CM_Length = Number_Of_Bytes;
  I2CM_Counter =0;
  I2CM_Step = IIC_HEADER_SENT_STATUS;
  I2CM_Data_Direction = 1;
  No_Resp = TRUE;
  // copy data into buffer
  for(Temp=0; Temp<Number_Of_Bytes; Temp++)
   I2CM_TX_Data[Temp] = Data[Temp];
  _IIC1C.Bits.IICEN = 1; 
  /* Format the Address to fit in the IICA register and place a 0 on the R/W bit.*/
  Slave_Addr <<=1;     // shift left 1 bit
  Slave_Addr &= 0xFE;
  Temp = _IIC1S.Byte;                 /* Clear any pending interrupt */
  _IIC1S.Bits.IICIF = 1;
  _IIC1C.Bits.MST = 0;    // reset master bit
  _IIC1S.Bits.SRW = 0;
  _IIC1C.Bits.TX = 1;        /* Select Transmit Mode */
  _IIC1C.Bits.MST = 1;    /* Select Master Mode (Send Start Bit) */ 
  _IIC1D.Byte = Slave_Addr;   /* Send selected slave address */
}


Message Edited by mad_dad on 2009-01-06 03:42 PM

Message Edited by mad_dad on 2009-01-06 03:50 PM
0 Kudos

1,105 Views
FWFan
Contributor III
Thank you Mad Dad.
Much appreciated for your reply.
I'm trying to communicate controlller
to controller using i2c.
I'm also trying to communicate
to the eeprom later with i2c.
 
Thank you very much.
FWFan
0 Kudos

1,105 Views
mad_dad
Contributor II
I2C SLAVE DRIVER SPEC
tx,rx message length predefined
messages handled by ISR
driver polled every 10ms for new messages
 
#ifndef I2C_Slave_Driver_h
#define I2C_Slave_Driver_h 1
 
#include "Types.h" // definitions for bool,byte
#include "IO_Map.h"  // hardware definitions

// The Slave I2C driver will respond to its address on the
// I2C link and an interrupt will be generated for each
// byte of data sent or received.
// The number of bytes is predefined for Tx and Rx.
// Buffer size max 16
// Flag is set when new message is received.
  #define IIC_BUFFER_SIZE 16
 
      // Initialise the I2C driver as a slave at an address
      // Define the number of bytes to be Rx and Tx
      extern void Init_I2CS(byte Addr, byte Number_RX_Bytes, byte Number_TX_Bytes);
      // Interrupt handler to manage all slave events on the I2C
      // link
      extern interrupt void I2C_ISR(void );
      // Reports true when complete RX message received cleared
      // by reading the data
      extern bool I2CS_Get_RX_Message_Received(void );
      // Returns the last RX data received.
      // Copies bytes to pointer location supplied
      // Only copies number of bytes set by initialisation
      extern void I2CS_Get_RX_Data(byte* Data);
      // Stores the TX data for transmission. Copies to pointer
      // location supplied.
      // TX data is only copied when bus is inactive. Only copies
      // number of bytes set by initialisation
      extern void I2CS_Set_TX_Data(const byte* Data);
BODY

#include "I2C_Slave_Driver.h"
static byte I2CS_RX_Data[IIC_BUFFER_SIZE];
static byte I2CS_TX_Data[IIC_BUFFER_SIZE];
static byte I2CS_Counter = 0;
static byte I2CS_Number_RX_Bytes = 0;
static byte I2CS_Number_TX_Bytes = 0;
static bool I2CS_RX_Message_RXd = FALSE;
void Init_I2CS(byte Addr, byte Number_RX_Bytes, byte Number_TX_Bytes)
{
   byte i;
  
   // initialise with odd parity
   for (i=0;i<IIC_BUFFER_SIZE;i++){
     I2CS_RX_Data[i] = 0x80;
     I2CS_TX_Data[i] = 0x80;
   }
   I2CS_Number_TX_Bytes = Number_TX_Bytes;
   I2CS_Number_RX_Bytes = Number_RX_Bytes;
  
    _IIC1A.Byte = Addr<<1; /* IIC Address shift left 1 bit          */
    
    _IIC1C.Bits.IICEN = 1;  /* Enable IIC            */
    _IIC1C.Bits.IICIE = 1;  /* Enable IIC interrupts */
}
interrupt void I2C_ISR(void )
{
 byte Temp;
 Temp = _IIC1S.Byte;              /* ACK the interrupt */ 
 _IIC1S.Bits.IICIF = 1;             // clear flag
 
  if (_IIC1S.Bits.ARBL == 1) {        // Verify the Arbitration lost status     
   _IIC1S.Bits.ARBL= 1;
   _IIC1C.Bits.MST = 0; 
   return;    
 } 
 
 /* If Arbitration is OK continue */ 
      
  if (_IIC1S.Bits.IAAS == 1) {       /* If it is the first byte transmitted */
    _IIC1C.Bits.TX = _IIC1S.Bits.SRW;               /* Set the transmision reception status */
    I2CS_Counter = 0;      // reset byte counter          
    _IIC1S.Bits.IAAS = 1;              // clear flag
    /* If we are receiving data read IIC1D to free bus and get the next byte */
    if (_IIC1C.Bits.TX == 0) {
      Temp = _IIC1D.Byte;   // dummy read
      return;
    }
  }
             
  if (_IIC1S.Bits.TCF ==1 ) { // byte transfer complete
    if (_IIC1C.Bits.TX == 0) { // 0 = receive data
       
      /* If RX, store it on the buffer */                           
   I2CS_RX_Data[I2CS_Counter] = _IIC1D.Byte;
   I2CS_Counter += 1;       
   // set flag if all expected bytes received
   if (I2CS_Counter == I2CS_Number_RX_Bytes) {
     I2CS_RX_Message_RXd = TRUE;
   } 
    }
    else {
      //* Data sent by the slave */             
         
      // If all expected bytes sent end transmission.
      if (I2CS_Counter == I2CS_Number_TX_Bytes ) {   
       _IIC1C.Bits.TX = 0;      // clear transmit      
       Temp = _IIC1D.Byte;      // dummy read to free bus
     }
     else {
       _IIC1D.Byte = I2CS_TX_Data[I2CS_Counter];  // write data
       I2CS_Counter += 1;
     }
    }
 }
}
bool I2CS_Get_RX_Message_Received(void )
{
 // new message = 1
  return I2CS_RX_Message_RXd;
}
void I2CS_Get_RX_Data(byte* Data)
{
  byte Temp;
 
 // clear message recieved flag
  I2CS_RX_Message_RXd = FALSE;
 
  for(Temp=0; Temp<I2CS_Number_RX_Bytes; Temp++) {  
      Data[Temp] = I2CS_RX_Data[Temp]; 
  }
}
void I2CS_Set_TX_Data(const byte* Data)
{
  byte Temp;
 
  for(Temp=0; Temp<I2CS_Number_TX_Bytes; Temp++) {
       I2CS_TX_Data[Temp] = Data[Temp];
  }
}
0 Kudos

1,105 Views
JimDon
Senior Contributor III
Fm,
Thanks! I appreciate your efforts and willingness to share.
Do mind if it is re-distributed?



Message Edited by JimDon on 2008-03-10 11:18 AM
0 Kudos

1,105 Views
Richly
Contributor III

Hi JimDon.

 

Did you happen to save the files that Fm shared? The posted link is no longer valid.  I imagine lots of people would like to see a copy.

 

Thanks,

    Richly

0 Kudos

1,105 Views
fm
Contributor I


JimDon wrote:
Fm,
Thanks! I appreciate your efforts and willingness to share.
Do mind if it is re-distributed?



Message Edited by JimDon on 2008-03-10 11:18 AM

I don't see a problem with that.  It depends on a processor expert bean for IIC initialization available in the Special Edition version.
0 Kudos