 |
 |
View previous topic :: View next topic |
Author |
Message |
allenhuffman
Joined: 17 Jun 2019 Posts: 643 Location: Des Moines, Iowa, USA
|
PIC24 PCD compiler and MPLAB X compiler I2C? |
Posted: Thu Oct 02, 2025 4:01 pm |
|
|
I have been trying to make common source files that work with our CCS PCD projects and a new one that is using the MPLAB X compiler.
I have been unable to find examples of this "Foundation Services Library" I2C code, and neither A.I. I have tried to use has produced code that fully works.
Original:
Code: | bool AD5272WriteCommandData (unsigned int address, uint8_t command, uint16_t data)
{
bool status = false;
uint16_t value = (((uint16_t)command & AD5272_CMD_MASK) << 10) | (data & AD5272_DATA_MASK);
//AD5272_PRINTF ("AD5272WriteCommandData (0x%x, 0x%x, 0x%x)\r\n", address, command, data);
#if defined(__PCD__) // CCS PCD compiler.
unsigned int ackStatus = 0;
// Send start condition
i2c_start (AD5272_I2C_STREAM);
// Send address with write bit (0)
ackStatus = i2c_write (AD5272_I2C_STREAM, address << 1);
if (ackStatus == 0)
{
// ACK'd, so something is there, continue writing.
i2c_write (AD5272_I2C_STREAM, value >> 8);
i2c_write (AD5272_I2C_STREAM, value & 0xff);
status = true;
}
// Send stop condition
i2c_stop (AD5272_I2C_STREAM);
#else // assume MPLAB X compiler.
...snip...
#endif
return status;
} |
Does anyone here work with both systems?
The code that was presented to me in MPLABs uses "void i2c2_waitForEvent(uint16_t*);" over there, and looks like it may require some steps to have interrupts working.
I am using a PIC24FJ1024GA606 for this project, but I am happy with a polled I2C approach like we do in CCS. This MPLAB code looks like it wants interrupts, since that wait function is looking at slave and master IRQ bits. _________________ Allen C. Huffman, Sub-Etha Software (est. 1990) http://www.subethasoftware.com
Embedded C, Arduino, MSP430, ESP8266/32, BASIC Stamp and PIC24 programmer.
http://www.whywouldyouwanttodothat.com ?
Using: 24FJ256GA106, 24EP256GP202 and 24FJ64GA002. |
|
 |
allenhuffman
Joined: 17 Jun 2019 Posts: 643 Location: Des Moines, Iowa, USA
|
|
Posted: Fri Oct 03, 2025 11:36 am |
|
|
Wow, the "Foundation Services" functions in MPLAB are basically just C wrappers to I2C register calls. I've chatted with Bing Copilot, Google Gemini, and even Claude and none of them can produce working I2C code.
I have learned you need to manually clear IRQs (polled mode) and such, so I can at least see traffic and get responses from the chip, but it leaves the bus in a weird state according to the Saleae Logic captures I have done.
"The grass is always greener on the other side."
This is a good example of how easy the CCS routines are compared to what this MPLAB stuff seems to require -- and the MPLAB stuff is still not working the same way:
Code: | uint16_t AD5272ReadCommandData (unsigned int address, uint8_t command, uint16_t data)
{
uint16_t retValue = 0;
uint16_t value = ((command & AD5272_CMD_MASK) << 10) | (data & AD5272_DATA_MASK);
//AD5272_PRINTF ("AD5272ReadCommandData (0x%x, 0x%x, 0x%x)\r\n", address, command, data);
#if defined(__PCD__) // CCS PCD compiler.
unsigned int ackStatus = 0;
// Send start condition
i2c_start (AD5272_I2C_STREAM);
// Send address with write bit (0)
ackStatus = i2c_write (AD5272_I2C_STREAM, address << 1);
if (ackStatus == 0)
{
// ACK'd, so something is there, continue writing.
uint8_t msb = 0;
uint8_t lsb = 0;
i2c_write (AD5272_I2C_STREAM, value >> 8); // MSB
i2c_write (AD5272_I2C_STREAM, value & 0xff); // LSB
// Send repeated start condition.
i2c_start (AD5272_I2C_STREAM);
// Send address with write bit (1)
i2c_write (AD5272_I2C_STREAM, (address << 1) | 1);
msb = i2c_read (AD5272_I2C_STREAM, 1); // ACK
lsb = i2c_read (AD5272_I2C_STREAM, 0); // No ACK
retValue = (msb << 8) | lsb;
}
// Send stop condition
i2c_stop (AD5272_I2C_STREAM);
#else // assume MPLAB X compiler.
// i2c2_driver_driver_open() is done during system startup.
unsigned int ackStatus = 0;
uint16_t event;
i2c2_driver_clearBusCollision ();
i2c2_driver_resetBus (); // Currently an empty function.
i2c2_clearIRQ (); // Clear the interrupt flag
// Send start condition
i2c2_driver_start ();
i2c2_waitForEvent (&event); // Wait for the event flag
i2c2_clearIRQ (); // Clear the interrupt flag
i2c2_driver_TXData (address << 1); // Address + Write
i2c2_waitForEvent (&event); // Wait for the event flag
i2c2_clearIRQ (); // Clear the interrupt flag
if (!i2c2_driver_isNACK())
{
ackStatus = true;
// ACK'd, so something is there, continue writing.
i2c2_driver_TXData (value >> 8); // MSB
i2c2_waitForEvent (&event); // Wait for the event flag
i2c2_clearIRQ (); // Clear the interrupt flag
i2c2_driver_TXData (value & 0xFF); // LSB
i2c2_waitForEvent (&event); // Wait for the event flag
i2c2_clearIRQ (); // Clear the interrupt flag
// Send repeated start condition.
i2c2_driver_restart ();
i2c2_waitForEvent (&event); // Wait for the event flag
i2c2_clearIRQ (); // Clear the interrupt flag
// Send address with write bit (1)
i2c2_driver_TXData ((address << 1) | 1);
i2c2_waitForEvent (&event); // Wait for the event flag
i2c2_clearIRQ (); // Clear the interrupt flag
}
// Only proceed to read if the initial write was ACK'd AND the Read address was ACK'd
if (ackStatus && !i2c2_driver_isNACK())
{
uint8_t msb = 0;
uint8_t lsb = 0;
// Prepare to receive
// BSET.B I2C2CON.RCEN ; Enable receive mode
i2c2_driver_startRX (); // RCEN=1
while (I2C2CONLbits.RCEN); // Wait for receive to complete
// BCLR.B I2C2CON.ACKDT ; ...send ACK
// BSET.B I2C2CON.ACKEN ; Send ACK/NACK
i2c2_driver_sendACK (); // ACKDT=0, ACKEN=1
// TODO: Wait for ACKDT/RCEN bits to clear?
while(I2C2CONLbits.ACKEN);
// BTSS.B I2C2STAT.RBF ; Wait for RBF (Receive Buffer Full)
while (!i2c2_driver_isBuferFull ());
// MOV I2C2RCV,W0 ; Read received byte into W0
msb = i2c2_driver_getRXData ();
// Clear overflow if needed.
if (I2C2STATbits.I2COV)
{
I2C2STATbits.I2COV = 0; // Clear overflow
}
i2c2_driver_startRX ();
while (I2C2CONLbits.RCEN); // Wait for receive to complete
i2c2_driver_sendNACK ();
while(I2C2CONLbits.ACKEN); // ACKDT=1, ACKEN=0
while (!i2c2_driver_isBuferFull ());
lsb = i2c2_driver_getRXData ();
if (I2C2STATbits.I2COV)
{
I2C2STATbits.I2COV = 0; // Clear overflow
}
retValue = (msb << 8) | lsb;
}
i2c2_driver_stop (); // STOP condition
i2c2_waitForEvent (&event); // Wait for the event flag
i2c2_clearIRQ (); // Clear the interrupt flag
// Ensure the bus is fully released by polling the PEN/TRSTAT bits.
while(I2C2CONLbits.PEN);
while(I2C2STATbits.TRSTAT);
#endif
return retValue;
} |
_________________ Allen C. Huffman, Sub-Etha Software (est. 1990) http://www.subethasoftware.com
Embedded C, Arduino, MSP430, ESP8266/32, BASIC Stamp and PIC24 programmer.
http://www.whywouldyouwanttodothat.com ?
Using: 24FJ256GA106, 24EP256GP202 and 24FJ64GA002. |
|
 |
Ttelmah
Joined: 11 Mar 2010 Posts: 19957
|
|
Posted: Sat Oct 04, 2025 11:38 am |
|
|
One thing might give an issue on the MPLAB stuff you show. Remember that
on a read from a device, there will be an ACK, not a NACK on the last byte
what you show does not seem to handle this. Might be why it still has
problems. |
|
 |
allenhuffman
Joined: 17 Jun 2019 Posts: 643 Location: Des Moines, Iowa, USA
|
|
Posted: Sat Oct 04, 2025 8:17 pm |
|
|
Ttelmah wrote: | One thing might give an issue on the MPLAB stuff you show. Remember that
on a read from a device, there will be an ACK, not a NACK on the last byte
what you show does not seem to handle this. Might be why it still has
problems. |
I gave up on that "Foundation Services" stuff. They are just wrappers to poll or set I2C bits. And, they are missing many of the ones you still need, so you do a command, then have to manually poll a bit to make sure it has completed. Stuff like that was way too much work -- might as well write my own!
They have a second I2C thing built in that gives basically two functions. It was a brain twister to figure out, and STILL has an issue with a phantom start/stop bit before a transmission. But, it can handles writing a basic message, or reading a basic message, or queueing up transactions so it will do a repeated start between them. It is ... interesting.
The grass is MUCH greener on the CCS side of the fence when it comes to I2C. I am hoping to figure this out and write up a blog post showing how to do all of this, since no one else seems to have done it. _________________ Allen C. Huffman, Sub-Etha Software (est. 1990) http://www.subethasoftware.com
Embedded C, Arduino, MSP430, ESP8266/32, BASIC Stamp and PIC24 programmer.
http://www.whywouldyouwanttodothat.com ?
Using: 24FJ256GA106, 24EP256GP202 and 24FJ64GA002. |
|
 |
allenhuffman
Joined: 17 Jun 2019 Posts: 643 Location: Des Moines, Iowa, USA
|
|
Posted: Mon Oct 06, 2025 8:01 am |
|
|
In case anyone else goes down this path, there are two methods of I2C provided in the "MCC" tool of MPLAB. The first one is basically C wrappers to register access, and it is missing many things you need so you still have to manually poll register bits (or so it seems).
The other is more complex, and lets you write a buffer or bytes, or read a buffer of bytes. If you want to write/repeated start/read, it uses some type of buffer system where you chain them together, then it does it automatically. Sadly, I cannot really find any documentation, and there is a phantom start/stop bit seen on my analyzer which is being done inside their routines for some reason. My CCS original code does not do that.
I wanted to share a simple example, in case it helps anyone else understand how much easier the CCS tool is. This is my work-in-progress test code:
Code: | bool AD5272WriteCommandData (unsigned int address, uint8_t command, uint16_t data)
{
bool status = false;
uint16_t value = (((uint16_t)command & AD5272_CMD_MASK) << 10) | (data & AD5272_DATA_MASK);
#if defined(__PCD__) // CCS PCD compiler.
unsigned int ackStatus = 0;
// Send start condition
i2c_start (AD5272_I2C_STREAM);
// Send address with write bit (0)
ackStatus = i2c_write (AD5272_I2C_STREAM, address << 1);
if (ackStatus == 0)
{
// ACK'd, so something is there, continue writing.
i2c_write (AD5272_I2C_STREAM, value >> 8);
i2c_write (AD5272_I2C_STREAM, value & 0xff);
status = true;
}
// Send stop condition
i2c_stop (AD5272_I2C_STREAM);
#else // assume MPLAB X compiler.
/*volatile*/ I2C2_MESSAGE_STATUS status_mcc; // Must be volatile
uint8_t writePayload[2];
// 1. Pack the command and data (which is in 'value') into the buffer
writePayload[0] = (uint8_t)(value >> 8); // Command/Data MSB
writePayload[1] = (uint8_t)(value & 0xFF); // Command/Data LSB
// 2. Execute the entire transaction in one blocking call.
// This single call performs: START, Address+W, Data TX (2 bytes), STOP.
I2C2_MasterWrite(
writePayload, // Pointer to data to send
sizeof(writePayload), // Length of data (2 bytes)
address, // Slave address (7-bit, the function shifts it)
&status_mcc // Pointer to the status variable
);
// 3. Wait for the transaction to complete
while(status_mcc == I2C2_MESSAGE_PENDING); // Requires interrupts!
// 4. Check the final status
if (status_mcc == I2C2_MESSAGE_COMPLETE)
{
while (I2C2CONLbits.PEN); // Wait for STOP to finish
// This implicitly means the Address and Data were ACK'd
status = true;
}
else // status is I2C2_MESSAGE_ADDRESS_NO_ACK or I2C2_MESSAGE_FAIL, etc.
{
I2C2CONLbits.PEN = 1; // Force STOP
while (I2C2CONLbits.PEN); // Wait for STOP to complete
}
#endif
return status;
}
uint16_t AD5272ReadCommandData (unsigned int address, uint8_t command, uint16_t data)
{
uint16_t retValue = 0;
uint16_t value = ((command & AD5272_CMD_MASK) << 10) | (data & AD5272_DATA_MASK);
#if defined(__PCD__) // CCS PCD compiler.
unsigned int ackStatus = 0;
// Send start condition
i2c_start (AD5272_I2C_STREAM);
// Send address with write bit (0)
ackStatus = i2c_write (AD5272_I2C_STREAM, address << 1);
if (ackStatus == 0)
{
// ACK'd, so something is there, continue writing.
uint8_t msb = 0;
uint8_t lsb = 0;
i2c_write (AD5272_I2C_STREAM, value >> 8); // MSB
i2c_write (AD5272_I2C_STREAM, value & 0xff); // LSB
// Send repeated start condition.
i2c_start (AD5272_I2C_STREAM);
// Send address with write bit (1)
i2c_write (AD5272_I2C_STREAM, (address << 1) | 1);
msb = i2c_read (AD5272_I2C_STREAM, 1); // ACK
lsb = i2c_read (AD5272_I2C_STREAM, 0); // No ACK
retValue = (msb << 8) | lsb;
}
// Send stop condition
i2c_stop (AD5272_I2C_STREAM);
#else // assume MPLAB X compiler.
/*volatile*/ I2C2_MESSAGE_STATUS status_mcc = I2C2_MESSAGE_PENDING;
I2C2_TRANSACTION_REQUEST_BLOCK combinedTRB[2]; // Two TRBs needed for W/R
// Data buffer for the two bytes we WRITE (Command + Data MSB/LSB)
uint8_t writePayload[2];
// Data buffer for the two bytes we READ (MSB and LSB)
uint8_t readPayload[2] = {0, 0};
// 1. Pack the command and data (from 'value') into the write buffer
writePayload[0] = (uint8_t)(value >> 8); // Command/Data MSB
writePayload[1] = (uint8_t)(value & 0xFF); // Command/Data LSB
#if 1 // Faster way (repeated start)
// --- Write Phase TRB (TRB [0]) ---
// This TRB performs: START, Address+W, Data TX (2 bytes)
I2C2_MasterWriteTRBBuild(
&combinedTRB[0], // TRB structure
writePayload, // Pointer to data to send
sizeof(writePayload), // Length of data (2 bytes)
address // Slave address
);
// --- Read Phase TRB (TRB [1]) ---
// This TRB performs: Repeated START, Address+R, Data RX (2 bytes), NACK, STOP
I2C2_MasterReadTRBBuild(
&combinedTRB[1], // TRB structure
readPayload, // Pointer to data buffer to receive bytes
sizeof(readPayload), // Length of data (2 bytes)
address // Slave address
);
// 2. Insert the two TRBs into the queue as one sequential transaction
I2C2_MasterTRBInsert(
2, // count: 2 TRBs in the list
combinedTRB, // pointer to the TRB array
&status_mcc // pointer to the status variable
);
// 3. Wait for the entire transaction to complete
while(status_mcc == I2C2_MESSAGE_PENDING);
// 4. Check the final status and compile the result
if (status_mcc == I2C2_MESSAGE_COMPLETE)
{
while (I2C2CONLbits.PEN); // Wait for STOP to finish
// Data has been successfully read into readPayload[0] (MSB) and readPayload[1] (LSB)
retValue = (readPayload[0] << 8) | readPayload[1];
}
else // status is I2C2_MESSAGE_ADDRESS_NO_ACK or I2C2_MESSAGE_FAIL, etc.
{
I2C2CONLbits.PEN = 1; // Force STOP
while (I2C2CONLbits.PEN); // Wait for STOP to complete
}
#else // Slower way (two separate start/stop transactions)
// Write phase
I2C2_MasterWrite(writePayload, 2, address, &status_mcc);
while (status_mcc == I2C2_MESSAGE_PENDING);
if (status_mcc != I2C2_MESSAGE_COMPLETE) return 0;
while (I2C2CONLbits.PEN); // Wait for STOP to finish
// Read phase
status_mcc = I2C2_MESSAGE_PENDING;
I2C2_MasterRead(readPayload, 2, address, &status_mcc);
while (status_mcc == I2C2_MESSAGE_PENDING);
if (status_mcc == I2C2_MESSAGE_COMPLETE)
{
while (I2C2CONLbits.PEN); // Wait for STOP to finish
retValue = (readPayload[0] << 8) | readPayload[1];
}
else // status is I2C2_MESSAGE_ADDRESS_NO_ACK or I2C2_MESSAGE_FAIL, etc.
{
I2C2CONLbits.PEN = 1; // Force STOP
while (I2C2CONLbits.PEN); // Wait for STOP to complete
}
#endif // fast or slow
#endif
return retValue;
}
|
The MPLAB routine was generated by one of the AIs, and works, but inside the MPLAB functions something is creating a phantom start/stop bit that my analyzer sees. I do not know how robust these routines are.
It also requires interrupts and has a handler that sets some flag which you will see polled in this code.
Anyone else tried this? _________________ Allen C. Huffman, Sub-Etha Software (est. 1990) http://www.subethasoftware.com
Embedded C, Arduino, MSP430, ESP8266/32, BASIC Stamp and PIC24 programmer.
http://www.whywouldyouwanttodothat.com ?
Using: 24FJ256GA106, 24EP256GP202 and 24FJ64GA002. |
|
 |
gaugeguy
Joined: 05 Apr 2011 Posts: 349
|
|
Posted: Mon Oct 06, 2025 9:20 am |
|
|
Software routines generated be AI are 'A' but definitely not 'I' |
|
 |
temtronic
Joined: 01 Jul 2010 Posts: 9585 Location: Greensville,Ontario
|
|
Posted: Mon Oct 06, 2025 2:00 pm |
|
|
I treat AI the same as I do 'simulators' and 'wizards'.....
Not worth wasting MY time on them... |
|
 |
allenhuffman
Joined: 17 Jun 2019 Posts: 643 Location: Des Moines, Iowa, USA
|
|
Posted: Mon Oct 06, 2025 2:11 pm |
|
|
temtronic wrote: | I treat AI the same as I do 'simulators' and 'wizards'.....
Not worth wasting MY time on them... |
When they work, they are magic.
But the other 90%...
What stuns me is how I can find no documentation, no forum posts, etc. about how this stuff works -- other than posts from others also asking how they work ;-) _________________ Allen C. Huffman, Sub-Etha Software (est. 1990) http://www.subethasoftware.com
Embedded C, Arduino, MSP430, ESP8266/32, BASIC Stamp and PIC24 programmer.
http://www.whywouldyouwanttodothat.com ?
Using: 24FJ256GA106, 24EP256GP202 and 24FJ64GA002. |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|