 |
 |
View previous topic :: View next topic |
Author |
Message |
benoitstjean
Joined: 30 Oct 2007 Posts: 587 Location: Ottawa, Ontario, Canada
|
PIC24EP512GP806 - I2C not working after sleep/wake-up |
Posted: Mon Sep 29, 2025 11:58 am |
|
|
Device: PIC24EP512GP806
Compiler: 5.026 (all of a sudden my 5.116 version no longer opens for some reason)
Hi guys,
Its been a while since I posted here and this pertains to a piece of code written a long time ago and I'm just curious about the behavior.
I believe this was actually TTelmah who had given it to me.
Basically, my device goes to sleep and the following code is called:
Code: | // Get peripherral disable module snapshot
#byte PMD1 = getenv("byte:PMD1")
#byte PMD2 = getenv("byte:PMD2")
#byte PMD3 = getenv("byte:PMD3")
#byte PMD4 = getenv("byte:PMD4")
#byte PMD5 = getenv("byte:PMD5")
#byte PMD6 = getenv("byte:PMD6")
#byte PMD7 = getenv("byte:PMD7")
fprintf( MONITOR_SERIAL, "\n\rPMD1: 0x%02X -- PMD2: 0x%02X -- PMD3: 0x%02X -- PMD4: 0x%02X -- PMD5: 0x%02X -- PMD6: 0x%02X -- PMD7: 0x%02X", PMD1, PMD2, PMD3, PMD4, PMD5, PMD6, PMD7 );
delay_ms( 1000 );
// Disable all peripheral disable modules
PMD1 = 0xFF;
PMD2 = 0xFF;
PMD3 = 0xFF;
PMD4 = 0xFF;
PMD5 = 0xFF;
PMD6 = 0xFF;
PMD7 = 0xFF;
fprintf( MONITOR_SERIAL, "\n\rPMD1: 0x%02X -- PMD2: 0x%02X -- PMD3: 0x%02X -- PMD4: 0x%02X -- PMD5: 0x%02X -- PMD6: 0x%02X -- PMD7: 0x%02X", PMD1, PMD2, PMD3, PMD4, PMD5, PMD6, PMD7 );
delay_ms( 1000 );
fprintf( MONITOR_SERIAL, "\n\r\n\r*** Unit now sleeping ***" );
// SOME CODE
// Sleep NOW
sleep( SLEEP_FULL );
//----------------------------
// From here, only a UART or external interrupt will wake-up the device.
//----------------------------
// Wake-up - wait a few cycles
delay_cycles( 10 );
// Clear RAM **don't add anything above this line**
#zero_ram
fprintf( MONITOR_SERIAL, "\n\r\n\r*** Unit waking-up ***");
// SOME CODE
fprintf( MONITOR_SERIAL, "\n\rPMD1: 0x%02X -- PMD2: 0x%02X -- PMD3: 0x%02X -- PMD4: 0x%02X -- PMD5: 0x%02X -- PMD6: 0x%02X -- PMD7: 0x%02X", PMD1, PMD2, PMD3, PMD4, PMD5, PMD6, PMD7 );
|
The printout looks like this:
*** Unit now sleeping ***
PMD1: 0x0E -- PMD2: 0x1E -- PMD3: 0x2E -- PMD4: 0x3E -- PMD5: 0x4E -- PMD6: 0x5E -- PMD7: 0x6E
PMD1: 0x0E -- PMD2: 0x1E -- PMD3: 0x2E -- PMD4: 0x3E -- PMD5: 0x4E -- PMD6: 0x5E -- PMD7: 0x6E
*** Unit waking-up ***
PMD1: 0x0E -- PMD2: 0x1E -- PMD3: 0x2E -- PMD4: 0x3E -- PMD5: 0x4E -- PMD6: 0x5E -- PMD7: 0x6E
I've never used much #byte and getenv() other than for power management so my impression was that where it says #byte, it creates that variable then gets the value of each PMD then it sets the value of each to 0xFF.... but clearly, it does not because the printout is the same for both. Then when the MCU wakes-up, the values are still the same but I2C is not working because I guess I2C is still disabled after wake-up because my I2C sensors are not responding.
So what's wrong here and what would be the proper usage/implementation of the PMD code so that everything shuts-down to the lowest current draw possible but when it wakes-up, the peripheral work like if I was to do a cold power-up?
Thanks!
Ben
Last edited by benoitstjean on Tue Sep 30, 2025 3:32 pm; edited 1 time in total |
|
 |
gaugeguy
Joined: 05 Apr 2011 Posts: 348
|
|
Posted: Mon Sep 29, 2025 12:08 pm |
|
|
Before you initialize the I2C module (or any other module you want to use) you need to turn it back on in the appropriate PMD location. The data sheet will detail which bits disable/enable which peripheral. |
|
 |
benoitstjean
Joined: 30 Oct 2007 Posts: 587 Location: Ottawa, Ontario, Canada
|
|
Posted: Mon Sep 29, 2025 12:14 pm |
|
|
Actually, I may have just realized that I may be confused:
1) I call getenv(), print-out the value;
2) I set all the values to 0xFF then I print them again without calling getenv() and the values are the same;
Unit goes to sleep.
-wait-
Unit wakes-up.
Then I don't call getenv(), I simply print the values again and they are again the same.
So I guess I'm really confused as to how I should be getting/setting values. |
|
 |
gaugeguy
Joined: 05 Apr 2011 Posts: 348
|
|
Posted: Mon Sep 29, 2025 12:26 pm |
|
|
Read section 10 of the data sheet on Peripheral Module Disable. specifically 10.4 and 10.6
Your existing code is also suspect as the PMD registers are 16 bit and your code is treating them as 8-bit. |
|
 |
benoitstjean
Joined: 30 Oct 2007 Posts: 587 Location: Ottawa, Ontario, Canada
|
|
Posted: Mon Sep 29, 2025 12:54 pm |
|
|
Oh yeah, good catch on that one but I'm still trying to wrap my head around the usage of getenv and setting these value registers. This code has been there for years and I never looked back at it....
I changed #byte to #word just to see if it would maybe change something but it does not...
Code: |
#word PMD1 = getenv("SFR:PMD1")
#word PMD2 = getenv("SFR:PMD2")
#word PMD3 = getenv("SFR:PMD3")
#word PMD4 = getenv("SFR:PMD4")
#word PMD5 = getenv("SFR:PMD5")
#word PMD6 = getenv("SFR:PMD6")
#word PMD7 = getenv("SFR:PMD7")
fprintf( MONITOR_SERIAL, "\n\rPMD1: 0x%04X -- PMD2: 0x%04X -- PMD3: 0x%04X -- PMD4: 0x%04X -- PMD5: 0x%04X -- PMD6: 0x%04X -- PMD7: 0x%04X", PMD1, PMD2, PMD3, PMD4, PMD5, PMD6, PMD7 );
|
PMD1: 0x0009 -- PMD2: 0x0019 -- PMD3: 0x0029 -- PMD4: 0x0039 -- PMD5: 0x0049 -- PMD6: 0x0059 -- PMD7: 0x0069
As for the documentation, I have MCP and CCS opened but the help I am seeking is with the CCS functionality of how to get and set the PMD registers and I think we all agree that CCS's documentation is not the greatest. So how would I disable all peripheral registers before going to sleep, how can I confirm that it actually worked and how do I set them when the MCU wakes-up?
Sorry, I'm not a student and I'm not a basement tinkerer, this is for work and I just want to get this out of the way and move-on to something else.
Thank you for your understanding.
Ben |
|
 |
benoitstjean
Joined: 30 Oct 2007 Posts: 587 Location: Ottawa, Ontario, Canada
|
|
Posted: Mon Sep 29, 2025 6:04 pm |
|
|
UPDATE: So I've commented-out all the code pertaining to PMD registers and after waking-up from sleep, I2C is still off because my sensors aren't working contrary to when I do a real power-up.
In all honesty, I have no idea how the use the getenv() function for what I want to do and CCS docs, well, it's CCS docs. It's good for what it's worth.
The problem here is that I know what my end goal is, I just don't know how to achieve it. I've looked at section 10.4 and 10.6 of the PIC manual and that's all nice and fine but it doesn't explain how to do what I want to do using the CCS compiler.
So:
1) How do I use getenv() to get/read the current status of PMD registers (so that I can set each PMD register back to these values upon waking-up);
2) How do I disable all PMD registers to save on power;
3) When the MCU wakes-up, how do I set-back all PMD registers to the same states as they were before going to sleep;
4) How do I confirm that what I've set the PMD registers to has worked (using just an fprintf is fine);
5) How do I force I2C to be up and running after waking-up from sleep, if that has anything to do with the PMD registers;
Thanks again and sorry for the confusion.
Ben |
|
 |
gaugeguy
Joined: 05 Apr 2011 Posts: 348
|
|
Posted: Tue Sep 30, 2025 6:12 am |
|
|
You have many different issues.
If the I2C is not working after sleep, and the PMD register access code is commented out, then the problem is in code you have kept hidden. There may also be hardware issues. What happens to the I2C pullups during sleep?
genenv("FSR:xxx") returns the address of a FSR. It can be used with any FSR.
Your chip uses PMD to refer to the Peripheral Module Disable as well as the Parallel Master port Data. Check the assembly code to make sure getenv is returning an address for the correct register.
The Peripheral Module Disable registers can be read and written at any time. If a module is disabled all of its setup registers are shut down. When the module is enabled again all of the setup registers must be set correctly again. It does not pick up where it left off. |
|
 |
benoitstjean
Joined: 30 Oct 2007 Posts: 587 Location: Ottawa, Ontario, Canada
|
|
Posted: Tue Sep 30, 2025 9:06 am |
|
|
Thanks for your response.
> You have many different issues.
Nope, only I2C is not working after a wake-up from sleep, everything else works as expected (e.g. both UARTS, SPI1 and SPI2, timers, DMA etc). So to re-iterate, when the device does a normal boot-up or reboots via the reset_cpu() function, the entire circuit works as it should. When the device goes goes to sleep then wakes-up, only I2C is not working... and as I am writing this message, the code for the PMD registers has been commented-out and the issue of I2C not working is still there at wake-up.
This leads me to the following: for testing as I am writing this message, in the wake-up sequence, rather than running the standard wake-up code, I added reset_cpu() and this caused I2C to be properly initialized and the sensors now work.... so considering that my sleep code does not disable PMD's at all because I removed the code, just the action of sleeping and waking-up affects I2C... which brings me back to my initial post's subject: usage/syntax/implementation of getenv()...
> There may also be hardware issues. What happens to the I2C pullups during sleep?
I wouldn't call them hardware issues because the hardware works fine at regular power-up or after a call to reset_cpu(). The wake-up sequence follows pretty much the same path as the standard power-up sequence other than the modem is not initialized - it is simply taken out of airplane mode (circuit has embedded modem, SPI and I2C devices). In that wake-up sequence, the same I2C code from the standard boot sequence is executed because it is in a function and that function is called only on two occasions: at regular power-up and at MCU wake-up.
Therefore, in that 'boot function', to prevent the I2C bus from locking-up in the case where the sensors did not respond for whatever reason (not present or faulty), this is what happens to SCL and SDA:
Code: |
In my project header file, I have the following:
#use i2c( MASTER, FAST, sda=PIN_G3, scl=PIN_G2, FORCE_HW, noinit ) <<<<<<<<<<<------- Note the 'noinit' here
In my MCU boot sequence, this is what I do:
// Float both I2C pins
output_float( PIN_G2 ); //SCL
output_float( PIN_G3 ); //SDA
// Force SDA and SCL to low
output_low( PIN_G3 );
output_low( PIN_G2 );
delay_ms( 100 );
// Force SDA and SCL back to floating
output_float( PIN_G3 );
output_float( PIN_G2 );
// Read pin values
bool TEST_SDA = input( PIN_G3 );
bool TEST_SCL = input( PIN_G2 );
//-----------------------------------------------
if( TEST_SDA == TRUE && TEST_SCL == TRUE )
{
// Pin reads both return '1' therefore resistors are installed
i2c_init( TRUE );
// Initialize I2C sensors
}
else
{
// Don't initialize sensors and set their respective failed flags.
}
|
So if both pins were high at the time of reading them, then it means that the I2C sensors are present with the pull-ups therefore initialize them. This works flawlessly at regular power-up or after a reset_cpu() command (including when I added reset_cpu() at wake-up for testing purpose). But that same code does not appear to work when the MCU wakes-up using the regular wake-up (without the reset_cpu() command).
Note here that the reason why I have devices with and without sensors (hence the reason I test with the code above) is because the firmware runs on two versions of the device: one with sensors and a basic one without sensors but also in the case where one of the sensors ever decides to fail.
Back to the sleep/wake-up code, honestly, there's not much going-on; all I do is stop some timers that are used for other stuff, clear a few of my own flags, disable interrutps that are not needed for the wake-up, turn-off the WDT and call sleep( SLEEP_FULL ). At wake-up, I do this:
// Wake-up - wait a few cycles
delay_cycles( 10 );
// Clear RAM **don't add anything above this line**
#zero_ram
then re-enable timers and run the standard power-up sequence that is executed when the entire device boots from scratch.
> genenv("FSR:xxx") returns the address of a FSR. It can be used with any FSR.
> Your chip uses PMD to refer to the Peripheral Module Disable as well as the Parallel Master port Data. Check the assembly code to make sure getenv is returning an address for the correct register.
At the time of writing this message, I am not using getenv() for anything because this is where I'm stuck:
1) I'm trying to wrap my head around its usage, syntax and implementation, hence this post. If getenv() gets a variable, then how do I set it? There's no setenv().
2) I've never used the assembly code and have no idea how that works. I've always programmed in C.
Let's use PMD1 for example considering the others will be the same but also because PMD1 bit 7 is what controls the power module for I2C1 as per the datasheet:
bit 7 I2C1MD: I2C1 Module Disable bit
1 = I2C1 module is disabled
0 = I2C1 module is enabled
If you were to use getenv() to disable and enable I2C1, how would you do it?
And if you had to use getenv() to get the entire 16 bits and set the entire 16 bits?
Thanks again,
Ben |
|
 |
gaugeguy
Joined: 05 Apr 2011 Posts: 348
|
|
Posted: Tue Sep 30, 2025 9:21 am |
|
|
I think you need to take a step back from getenv and read up on what #byte and #word actually do and how they are used. |
|
 |
benoitstjean
Joined: 30 Oct 2007 Posts: 587 Location: Ottawa, Ontario, Canada
|
|
Posted: Tue Sep 30, 2025 10:11 am |
|
|
getenv("SFR:PMD1") tells the compiler to replace this with the absolute address of the PMD1 special function register. So this returns something like 0x0700 for instance.
#word PMD1 = ... tells the compiler to "create a new 16-bit variable called PMD1 and map it directly to that address". The code will treat PMD1 like a normal C variable but it reads/writes that hardware register.
Basically, it's a direct access to that register via #word PMD1. Fair enough. I understand.
This said, I've re-added the following at the beginning of my power down sequence:
Code: | #word PMD1 = getenv("SFR:PMD1")
fprintf( MONITOR_SERIAL, "\n\rPMD1: 0x%04X", PMD1 ); |
And the print-out on my serial console is PMD1: 0x0000.
I figured that at wake-up, I'd need the same thing but force PMD1 to all enabled so I've added the following:
Code: | #word PMD1 = getenv("SFR:PMD1")
PMD1 = 0x0000;
fprintf( MONITOR_SERIAL, "\n\rPMD1: 0x%04X", PMD1 ); |
The print-out on my serial console is PMD1: 0x0000
So in both cases at power-down and at wake-up, it appears that all modules in PMD1 are enabled.
Still, I2C is not working. But if I call reset_cpu() at wake-up, then that works. But I don't want to do that because it takes-up to 30 seconds to boot the modem versus about 4 seconds to wake-up and take the modem out of airplane mode.
I'm at a loss here.
Thanks.
Ben |
|
 |
gaugeguy
Joined: 05 Apr 2011 Posts: 348
|
|
Posted: Tue Sep 30, 2025 10:23 am |
|
|
You have not shown your complete code so I am going to make an educated guess with my crystal ball.
You initialize the I2C module but then when you go to sleep you do not de-initialize it so you can start over again fresh on wake up. |
|
 |
benoitstjean
Joined: 30 Oct 2007 Posts: 587 Location: Ottawa, Ontario, Canada
|
|
Posted: Tue Sep 30, 2025 10:40 am |
|
|
Yea, that is in fact what seems to be happening...
So then how would I "de-initialize" I2C at sleep and "re-initialize" at wake-up?
Because clearly my boot/reboot code does something to the I2C module because it works consistently and flawlessly until it goes to sleep and wakes-up....
Thanks again,
Ben |
|
 |
gaugeguy
Joined: 05 Apr 2011 Posts: 348
|
|
Posted: Tue Sep 30, 2025 12:48 pm |
|
|
Have you looked up what i2c_init does? |
|
 |
benoitstjean
Joined: 30 Oct 2007 Posts: 587 Location: Ottawa, Ontario, Canada
|
|
Posted: Tue Sep 30, 2025 1:11 pm |
|
|
From the manual:
Quote: | i2c_init()
Syntax:
i2c_init([stream], baud);
Parameters:
stream – optional parameter specifying the stream defined in #USE I2C.
baud
If baud is 0 → I2C peripheral will be disabled.
If baud is 1 → I2C peripheral is initialized and enabled with the baud rate specified in #USE I2C directive.
If baud is > 1 → I2C peripheral is initialized and enabled to the specified baud rate.
Returns:
-----
Function:
To initialize the I2C peripheral at run time to the specified baud rate.
Availability:
All devices.
Requires:
#USE I2C
Examples:
// Built-in Functions
#USE I2C(MASTER, I2C1, FAST, NOINIT)
// initialize and enable to baud rate specified in #USE I2C
i2c_init(TRUE);
// initialize and enable to a baud rate of 500 kbps
i2c_init(500000);
See Also:
(related I2C functions would be listed here) |
My code uses:
#use i2c( MASTER, sda=PIN_G3, scl=PIN_G2, FAST, FORCE_HW, noinit )
I2C_Init( TRUE ); |
|
 |
|
|
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
|