mcf52259 I2C routines - an extra (dummy) read?

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

mcf52259 I2C routines - an extra (dummy) read?

2,416 Views
FridgeFreezer
Senior Contributor I

I am currently writing an I2C interrupt-routine for MCF52259, based on Freescale's own I2C driver (which can only Tx/Rx single bytes). There seems to be an extra (dummy) read going on in their code though, just before the STOP condition, and I can't work out what it's for or if I'm reading the code/data sheet wrong?

 

/* * I2CreceiveByte: I2C read byte * * Parameters: address: address to read *      id: I2C device to read * * Return : data: byte read it from device */uint8 I2CreceiveByte(uint8 address, uint8 id){ uint8 data;  /* setting in Tx mode */ MCF_I2C_I2CR |= MCF_I2C_I2CR_MTX;     /* send start condition */ MCF_I2C_I2CR |= MCF_I2C_I2CR_MSTA;  /* devide ID to write */ MCF_I2C_I2DR = id;         /* wait until one byte transfer completion */ while( !(MCF_I2C_I2SR & MCF_I2C_I2SR_IIF )) ; /* clear the completion transfer flag */ MCF_I2C_I2SR &= ~MCF_I2C_I2SR_IIF;  /* memory address */ MCF_I2C_I2DR = address;        /* wait until one byte transfer completion */ while( !(MCF_I2C_I2SR & MCF_I2C_I2SR_IIF )) ; /* clear the completion transfer flag */ MCF_I2C_I2SR &= ~MCF_I2C_I2SR_IIF;    /* resend start */  MCF_I2C_I2CR |= MCF_I2C_I2CR_RSTA;    /* device id to read */  MCF_I2C_I2DR = (uint8)(id | 0x01);       /* wait until one byte transfer completion */ while( !(MCF_I2C_I2SR & MCF_I2C_I2SR_IIF )) ; /* clear the completion transfer flag */ MCF_I2C_I2SR &= ~MCF_I2C_I2SR_IIF;  /* setting in Rx mode */ MCF_I2C_I2CR &= ~MCF_I2C_I2CR_MTX;   /* send NO ACK */  MCF_I2C_I2CR |= MCF_I2C_I2CR_TXAK;   /* dummy read */   data = MCF_I2C_I2DR;        /* wait until one byte transfer completion */ while( !(MCF_I2C_I2SR & MCF_I2C_I2SR_IIF )) ; /* clear the completion transfer flag */ MCF_I2C_I2SR &= ~MCF_I2C_I2SR_IIF;  /* read data received */ data = MCF_I2C_I2DR;        /* wait until one byte transfer completion */ while( !(MCF_I2C_I2SR & MCF_I2C_I2SR_IIF )) ;        /*** ^^^ WHY??? ^^^ ***/ /* clear the completion transfer flag */ MCF_I2C_I2SR &= ~MCF_I2C_I2SR_IIF;  /* generates stop condition */ MCF_I2C_I2CR &= ~MCF_I2C_I2CR_MSTA;  /* send the received data */ return data;}

 

As soon as you read from I2DR (data = MCF_I2C_I2DR:smileywink: you trigger a data transfer, which means you're sending out an extra 9 pulses on SCL after the last byte is Rx'd. This seems like a dodgy thing to do, even if it shouldn't cause problems in real life.

Labels (1)
0 Kudos
Reply
6 Replies

1,147 Views
angelo_d
Senior Contributor I

hi fridge,

 

the routine seems to read 2 bytes, but seems a mistake since rewrite "data" 2 times. If you remove the second read does it works ?

 

 

0 Kudos
Reply

1,147 Views
mjbcswitzerland
Specialist V

Hi

 

I have in fact just completed a polling based I2C driver for the M52259 reading and writing an EEPROM (M24M01) and it does look as though the final read in your refence is not correct. Below is the code - it supports multiple devices (hence the calculation of the device to address at the start) and it also polls the device in case it doesn't acknowlege its address since it is still busy with an internal write cycle (hence the start condition being sent using a subroutine (shared with the write).

 

Previously I had always used a (general purpose) interrupt driven one whereby the code is very similar apart from the ISR rather than the polling. [http://www.utasker.com/docs/uTasker/uTaskerIIC.PDF ]

 

static void fnSendStart(unsigned char ucSlaveAddress, unsigned long device_address){    int iPoll = 0;    do {        I2SR = 0;        if (iPoll != 0) {                                                // if the device is initially busy (due to previous write not yet completed)            I2CR = (IIC_IEN | IIC_MSTA | IIC_MTX | IIC_RSTA);            // send repeated start        }        else {            I2CR = (IIC_IEN | IIC_MTX);                                  // set transmit mode            I2CR = (IIC_IEN | IIC_MSTA | IIC_MTX);                       // set master mode to cause start condition to be sent        }        I2DR = ucSlaveAddress;                                           // the slave address (including read/write and extended address bit)        iPoll++;                                                         // count the number of times that the device needed to be polled        while (!(I2SR & IIC_IIF)) {                                      // wait for transfer to complete        }    } while (I2SR & IIC_RXACK);                                          // no acknowledge returned means that the device is busy completing a previous write operation    if (!(ucSlaveAddress & 0x01)) {                                      // if write, send also the internal address        I2SR = 0;        I2DR = (unsigned char)(device_address >> 8);                     // set the address - msb        while (!(I2SR & IIC_IIF)) {                                      // wait for transfer to complete        }        I2SR = 0;        I2DR = (unsigned char)(device_address);                          // set the address - lsb        while (!(I2SR & IIC_IIF)) {                                      // wait for transfer to complete        }    }}static int fnReadI2C_eeprom(unsigned char *ptrAddress, unsigned char *ptrBuffer, MAX_FILE_LENGTH DataLength){    // Read the amount of data specified from the starting address to a return buffer    //    CAST_POINTER_ARITHMETIC device_address = (CAST_POINTER_ARITHMETIC)ptrAddress;    int iDeviceNumber = 0;    device_address -= I2C_EEPROM_START_ADDRESS;    while (device_address >= I2C_EEPROM_DEVICE_SIZE) {                   // determine the device in which the address starts        device_address -= I2C_EEPROM_DEVICE_SIZE;        iDeviceNumber++;    }        while (DataLength != 0) {                                            // support read in multiple devices        MAX_FILE_LENGTH data_read = DataLength;        if (data_read > (I2C_EEPROM_DEVICE_SIZE - device_address)) {     // check how much data could be read from the present device            data_read = (I2C_EEPROM_DEVICE_SIZE - device_address);       // limit the read to maximum in present device        }        if (ucI2C_EEPROM_Address[iDeviceNumber] == 0) {                  // check that device exists            uMemset(ptrBuffer, 0x00, DataLength);            return -1;                                                   // error - no device exists so buffer filled with 0        }        while (I2SR & IIC_IBB) {}                                        // while the bus is busy (could still be sending stop condition from previous operation)        if (ulI2C_EEPROM_pointer[iDeviceNumber] != device_address) {     // check whether a sequential read can be performed            ulI2C_EEPROM_pointer[iDeviceNumber] = device_address;        // synchronise pointer so that sequential read may be possible in the future            fnSendStart((unsigned char)(ucI2C_EEPROM_Address[iDeviceNumber] | (unsigned char)(0x02 & (device_address >> 15))), device_address); // send start condition and address for write            I2SR = 0;            I2CR = (IIC_IEN | IIC_MSTA | IIC_MTX | IIC_RSTA);            // send a repeated start            I2DR = (ucI2C_EEPROM_Address[iDeviceNumber] | 0x01);         // the slave address for read            while (!(I2SR & IIC_IIF)) {                                  // wait for transfer to complete            }        }        else {                                                           // sequential read is possible            fnSendStart((unsigned char)(ucI2C_EEPROM_Address[iDeviceNumber] | 0x01), 0); // send start condition and address for read        }        I2SR = 0;        if (data_read == 1) {            I2CR = (IIC_IEN | IIC_MSTA | IIC_TXAK);                      // single read byte will not be acknowledged        }        else {            I2CR = (IIC_IEN | IIC_MSTA);                                 // multi-byte reads are initially acknowledge        }        *ptrBuffer = I2DR;                                               // dummy read to initiate the read data transfer                    while (data_read--) {            device_address++;            if (++ulI2C_EEPROM_pointer[iDeviceNumber] >= I2C_EEPROM_DEVICE_SIZE) {                ulI2C_EEPROM_pointer[iDeviceNumber] = 0;                 // pointer overflow occurs at last address in device                iDeviceNumber++;                                         // move to next device                device_address = 0;            }            DataLength--;            while (!(I2SR & IIC_IIF)) {                                  // wait for transfer to complete            }            I2SR = 0;            if (data_read == 1) {                I2CR = (IIC_IEN | IIC_MSTA | IIC_TXAK);                  // final read byte will not be acknowledged            }            *ptrBuffer++ = I2DR;                                         // read received byte        }        while (!(I2SR & IIC_IIF)) {                                      // wait for transfer to complete        }        I2CR = (IIC_IEN | IIC_TXAK);                                     // send stop condition    }    return 0;                                                            // success}

 

Regards

 

Mark

 

0 Kudos
Reply

1,147 Views
JimDon
Senior Contributor III

Mark,

It seems you have the dummy read as well, so I am not sure what you find wrong with his code.

In any event, the dummy read is required. 

 

0 Kudos
Reply

1,147 Views
mjbcswitzerland
Specialist V

Hi Jim

 

A dummy read is required to "initiate" the read transfer but in the other code there are 2 dummy reads - there is also one before sending the stop condition (after reading all bytes) which is probably superfluous.

 

Regards

 

Mark

 

0 Kudos
Reply

1,147 Views
FridgeFreezer
Senior Contributor I

The issue is not so much the read itself, as it is reading the RX'd data value from I2DR, it's the following wait state for "transfer complete" (1 byte of SCL pulses) when the stop condition is the next thing that should be sent to terminate the transfer. To me it seems like the port should be set up not to do anything other than send the STOP command, and THEN the (last RX'd) byte should be read out.

 

I'm going to get the crayons out and re-RTFM to see if I can work out what it's supposed to be doing, it may be that it's just very badly explained in the FS documentation (god forbid!:smileyvery-happy:) and is right or doesn't matter.

0 Kudos
Reply

1,147 Views
FridgeFreezer
Senior Contributor I

Freescale's MCF52259RM.pdf seems to suggest doing things in the opposite order:

 

For a master receiver to terminate a data transfer, it must inform the slave transmitter by notacknowledging the last data byte. This is done by setting I2CRn[TXAK] before reading the next-to-lastbyte. Before the last byte is read, a STOP signal must be generated, as in the following example.1. Decrement RXCNT.2. If last byte (RXCNT = 0) go to step #4.3. If next to last byte (RXCNT = 1), set I2CRn[TXAK] to disable ACK and go to step #5.4. This is last byte, so clear I2CRn[MSTA] to generate a STOP signal. 5. Read data from I2DRn.6. If there is more data to be read (RXCNT ≠ 0), go to step #1 if desired.

 This makes more sense - set the I2C interface to send a STOP, and only then read I2DR.

0 Kudos
Reply