/*********************************************************************** 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();}
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
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
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
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