CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

[SOLVED] PIC24EP512 wake-up on serial data problem
Goto page Previous  1, 2
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
newguy



Joined: 24 Jun 2004
Posts: 1900

View user's profile Send private message

PostPosted: Mon Aug 19, 2019 11:35 am     Reply with quote

I think the extra serial setup you have in your initialization routine is unnecessary. Leave it out entirely, but add the wakeup part to the original #use rs232 line. Then remove the CN setup for the UART's RX pin and you should be fine.

Edit: bear in mind Ttelmah's warning (something I alluded to as well): the first one or more characters received by the UART when the processor is asleep will be corrupted.
benoitstjean



Joined: 30 Oct 2007
Posts: 543
Location: Ottawa, Ontario, Canada

View user's profile Send private message

PostPosted: Mon Aug 19, 2019 11:45 am     Reply with quote

I don't care if the first or all characters are corrupted, all I want is that as soon as _a_ character (any charachter) arrives, it wakes-up the unit. That's it.

Sorry for my ignorance here but *how* do I add <UART_WAKEUP_ON_RDA> to the #use rs232 command? Adding it to the end of the directive after a comma following <ERRORS> yields an error and the documentation (March 2019) does not explicitely show how to use it.

Sorry :(
newguy



Joined: 24 Jun 2004
Posts: 1900

View user's profile Send private message

PostPosted: Mon Aug 19, 2019 12:00 pm     Reply with quote

Version 5.087, in the setup_uart() help file entry has it. Sorry, I thought it was part of the #use rs232, but it's not.

Your other alternative is to leave the #use rs232 as is, then manually set the WAKE bit of the UxMODE register.
benoitstjean



Joined: 30 Oct 2007
Posts: 543
Location: Ottawa, Ontario, Canada

View user's profile Send private message

PostPosted: Mon Aug 19, 2019 12:06 pm     Reply with quote

Ok, so I'm doing something wrong definitely because...

1) I've commented-out all references to <INT_CN | UART_RX_PIN> and I'm only using <INT_RDA>, MCU never wakes-up on serial data

2) Using it with <setup_uart( UART_WAKEUP_ON_RDA, MODEM_SERIAL );> does not change a thing either with INT_RDA only;

3) Using the above, the unit can wake-up with every external interrupts except incoming serial data;

4) Re-enabling <enable_interrupts( INTR_CN_PIN | PIN_UART1_RX );> before going to sleep wakes-up the unit on serial data but again, even if INT_RDA is enabled, it never fires afterwards, even if <setup_uart( UART_WAKEUP_ON_RDA, MODEM_SERIAL );> is used...

So I'm not sure what else is going-in here... This should be the simplest of things...

I'll see how I can use your other suggestion using the WAKE bit of the UxMODE register.

The dirty solution is to call <reset_cpu();> but that'll take around 30 seconds to reset everything as opposed to now which takes 8 seconds.
benoitstjean



Joined: 30 Oct 2007
Posts: 543
Location: Ottawa, Ontario, Canada

View user's profile Send private message

PostPosted: Mon Aug 19, 2019 1:02 pm     Reply with quote

I guess I'm on the right path, I disabled all references to <INT_CN | UART_RX_PIN> and I added the following code just before going to sleep:

fprintf( MONITOR_SERIAL, "\n\rUxMODE: %04X", U1MODE );
#bit UART1_WAKE=getenv("BIT:U1MODE.WAKE")
UART1_WAKE = TRUE;
fprintf( MONITOR_SERIAL, "\n\rUxMODE: %04X", U1MODE );

In Tera Term, this prints-out the following:

UxMODE: 8000 (initial read: WAKE = 0)
UxMODE: 8080 (modified: WAKE = 1)

Now I can send a single character to the modem which will wake-up the MCU. But here again, after the unit wakes-up, #INT_RDA will no longer fire and by boot sequence eventually times-out due to the MCU not detecting incoming characters.

Here's my code BEOFRE sleep and AFTER sleep:

// Disable the global interrupt
disable_interrupts( GLOBAL );

// Disable all interrupts that will not be used for the wake-up process.
disable_interrupts( INT_UART1E );
disable_interrupts( INT_TBE );
disable_interrupts( INT_TIMER1 );
disable_interrupts( INT_DMA0 );
disable_interrupts( INT_RDA2 );
disable_interrupts( INTR_CN_PIN | CODEC_PIN_WCLK );

// Clear any pending interrupt flag
clear_interrupt( INT_UART1E );
clear_interrupt( INT_TBE );
clear_interrupt( INT_TIMER1 );
clear_interrupt( INT_DMA0 );
clear_interrupt( INT_RDA2 );

// Clear CN interrupt flag due to compiler bug
#BIT CNIF=getenv("BIT:CNIF")
CNIF = FALSE;

clear_interrupt( INT_RDA );

// Stop the watchdog timer
setup_wdt( WDT_OFF );

// Disable all peripherral power
#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")

PMD1 = 0xFF;
PMD2 = 0xFF;
PMD3 = 0xFF;
PMD4 = 0xFF;
PMD5 = 0xFF;
PMD6 = 0xFF;
PMD7 = 0xFF;

#byte NVMCON=getenv("SFR:NVMCON");
#bit NVMSIDL=NVMCON.12;
NVMSIDL=TRUE;

#byte RCON = getenv("SFR:RCON")
#bit VREGS = RCON.8
#bit VREGSF = RCON.11
VREGS = FALSE;
VREGSF = FALSE;

#byte OSCCON = getenv("SFR:OSCCON");
#bit LPOSCEN = OSCCON.1;
LPOSCEN = FALSE;


//*****************************************
// Enable interrupts that will wake-up unit
//*****************************************
enable_interrupts( INT_RDA ); // Enable serial receive interrupt
enable_interrupts( INT_EXT0 ); // Enable input 0 change detection
enable_interrupts( INT_EXT1 ); // Enable input 1 change detection
enable_interrupts( INT_EXT2 ); // Enable input 2 change detection
enable_interrupts( INT_EXT3 ); // Enable input 3 change detection
enable_interrupts( INT_EXT4 ); // Enable input 4 change detection

clear_interrupt( INT_RDA );
clear_interrupt( INT_EXT0 );
clear_interrupt( INT_EXT1 );
clear_interrupt( INT_EXT2 );
clear_interrupt( INT_EXT3 );
clear_interrupt( INT_EXT4 );

// Sleep NOW
sleep( SLEEP_FULL );

//**************************************************************************
// Wake-up from sleep
// TODO: Rather than a full reboot of the unit, maybe it would be quicker
// to simply re-enable everything but testing will be required.
//**************************************************************************
// Wake-up - wait a few cycles
delay_cycles( 100 );

// Clear RAM **don't set any variables above this line**
#zero_ram

// Wake-up modem from sleep
output_low( MODEM_PIN_DTR_SLEEP );

delay_ms( 250 );

// Get state of each interrupt
unsigned long InterruptState = ( interrupt_active( INT_RDA ) << 6 ) | // UART
( interrupt_active( INT_EXT0 ) << 5 ) | // Input 0
( interrupt_active( INT_EXT1 ) << 4 ) | // Input 1
( interrupt_active( INT_EXT2 ) << 3 ) | // Input 2
( interrupt_active( INT_EXT3 ) << 2 ) | // Input 3
( interrupt_active( INT_EXT4 ) << 1 ); // Input 4

fprintf( MONITOR_SERIAL, "\n\rINT_RDA : %u -- UART RX data", InterruptState >> 6 & 0x01);
fprintf( MONITOR_SERIAL, "\n\rINT_EXT0 : %u -- Input 0 triggered", InterruptState >> 5 & 0x01);
fprintf( MONITOR_SERIAL, "\n\rINT_EXT1 : %u -- Input 1 triggered", InterruptState >> 4 & 0x01);
fprintf( MONITOR_SERIAL, "\n\rINT_EXT2 : %u -- Input 2 triggered", InterruptState >> 3 & 0x01);
fprintf( MONITOR_SERIAL, "\n\rINT_EXT3 : %u -- Input 3 triggered", InterruptState >> 2 & 0x01);
fprintf( MONITOR_SERIAL, "\n\rINT_EXT4 : %u -- Input 4 triggered", InterruptState >> 1 & 0x01 );

delay_ms( 500 );

clear_interrupt( INT_RDA );
enable_interrupts( GLOBAL );

Device_InitializeBase();

The last command Device_InitializeBase(); sends a few modem AT commands to which the modem should respond 'OK'. As soon as the first command is sent, Tera Term sees the OK but that's where the MCU doesn't fire.
newguy



Joined: 24 Jun 2004
Posts: 1900

View user's profile Send private message

PostPosted: Mon Aug 19, 2019 1:09 pm     Reply with quote

Put in similar code for the UxSTA register. As I said before, if any of the UART errors are active, the UART will lock up. I suspect that you're getting errors which, even though you have ERRORS in your #use rs232, are not being properly cleared. Also do the same for the baud rate register(s), i.e. similar to what you've just done with the UxMODE register. Just to assure yourself that the UART's critical setup parameters aren't being changed.

One more thing to try: comment out the CN code (setup, interrupt enable, etc) completely.

Edit:

Code:
delay_ms( 500 );

clear_interrupt( INT_RDA );
enable_interrupts( GLOBAL );


Just taking a look at this with fresh eyes. Between wake up event and these lines is basically an eternity in terms of embedded development. Yes, clearing the INT_RDA interrupt flag is part of what you should be doing, but think about it: you're blocking and ignoring any data which is inbound on the serial receive pin. The UART can buffer 2 or 3 characters - that's it. If you don't remove (read) them, that's an overrun error and the UART will lock. It's looking very certain that the compiler isn't clearing that error.

Another way round this is to look at it like this: something woke me up, and from what you've said, you don't care what. Since you don't care about grabbing a modem message (if that's what woke you up), then disable the UART once the processor is fully awake. Any inbound characters are completely ignored. Then when you're ready to speak to the modem (after your 500ms delay above), simply re-initialize the UART between your clear interrupt line and the subsequent enable GLOBAL line. Only little "gotcha" to watch if you go this route is to ensure that your TRIS are set so that the receive line defaults to actually being an input once you disable the UART.


Last edited by newguy on Mon Aug 19, 2019 1:22 pm; edited 2 times in total
benoitstjean



Joined: 30 Oct 2007
Posts: 543
Location: Ottawa, Ontario, Canada

View user's profile Send private message

PostPosted: Mon Aug 19, 2019 1:14 pm     Reply with quote

ha! Yes, that's what I did!

The output of U1STA is 0x0517! First three bits point to errors.

So should I just clear them as soon as the device wakes-up?

I guess I'll try it but you may have another suggestion.

Benoit
newguy



Joined: 24 Jun 2004
Posts: 1900

View user's profile Send private message

PostPosted: Mon Aug 19, 2019 1:21 pm     Reply with quote

benoitstjean wrote:
ha! Yes, that's what I did!

The output of U1STA is 0x0517! First three bits point to errors.

So should I just clear them as soon as the device wakes-up?

I guess I'll try it but you may have another suggestion.

Benoit


Heh, we posted simultaneously. Yes, definitely, absolutely clear the errors before clearing the interrupt flag and then enabling GLOBAL flag.

Just have to read the datasheet to ensure that writing 0's into the appropriate places will actually clear the errors. Sometimes there's extra things you have to do to clear some things.
benoitstjean



Joined: 30 Oct 2007
Posts: 543
Location: Ottawa, Ontario, Canada

View user's profile Send private message

PostPosted: Mon Aug 19, 2019 1:27 pm     Reply with quote

Success! Works!

So immediately after the unit wakes-up, I check all interrupts to see who triggered it.

Then:

fprintf( MONITOR_SERIAL, "\n\rU1STA: %04X", U1STA );
U1STA = U1STA & 0xFFF8;
fprintf( MONITOR_SERIAL, "\n\rU1STA: %04X", U1STA );

This prints the following:
U1STA: 0513 <-- With the two errors set (BIT1: OERR and BIT2: FERR) + BIT0: URXDA --> Data received
U1STA: 0510 <-- With the two error and data bits cleared using the 0xFFF8 mask

Then I enable the GLOBAL interrupts, then the rest boots fine and now INT_RDA fires like it should.

Leaving work but will re-test carefully tomorrow and mark this as success, perhaps add a little text explaining the problem, the symptoms and the solution.

Many thanks!! Forums like this are awesome!

Ben
Ttelmah



Joined: 11 Mar 2010
Posts: 19215

View user's profile Send private message

PostPosted: Mon Aug 19, 2019 1:31 pm     Reply with quote

Use a test for 'data available', and if it is read this. Repeat until the UART buffer
is empty. This is a PIC24, so actually up to five characters can be waiting
(four deep FIFO, and one in the receive register).
Then clear the interrupt.

Reading the characters should clear the error status.
newguy



Joined: 24 Jun 2004
Posts: 1900

View user's profile Send private message

PostPosted: Mon Aug 19, 2019 1:32 pm     Reply with quote

Glad you have it sorted. You owe Ttelmah and I a virtual beer. Very Happy
benoitstjean



Joined: 30 Oct 2007
Posts: 543
Location: Ottawa, Ontario, Canada

View user's profile Send private message

[SOLVED] PIC24EP512 wake-up on serial data problem
PostPosted: Tue Aug 20, 2019 7:39 am     Reply with quote

To summarize, here's the problem:

PIC24's UART1 is tied to a modem.

PIC puts modem to sleep, a bunch of external interrupts on the PIC get enabled, then PIC goes to sleep using <sleep( SLEEP_FULL );>.

The enabled interrupts are the ones that can wake-up the PIC from sleep, one of them being serial data received on UART 1 (INT_RDA).

The modem, although sleeping, can still send serial data to the MCU in the case it receives an SMS or an incoming call. This is one of the wake-up conditions.

PROBLEM:
At wake-up, the PIC has to send a few serial commands to the modem. Although MCU gets woken-un by the incoming serial data, after being fully awake, the PIC does not fire the #INT_RDA interrupt when the modem responds to the PIC's message. For instance, one of the first commands is <AT> to which the modem responds <OK>. I am able to see the <OK> response using a logic analyzer on the UART pins but the PIC does not react.

Basically, what happens is that the UART encounters errors and blocks there until the errors are cleared.


SOLUTION:
Before going to sleep, I added the following lines of code:

Step 1:
#bit UART1_WAKE=getenv("BIT:U1MODE.WAKE")
UART1_WAKE = TRUE;

The first line gets the status of the WAKE bit in the U1MODE register, the second line sets that bit to TRUE. As stated in page 355 from the DS70616 datasheet from Microchip:

WAKE: Wake-up on Start Bit Detect During Sleep Mode Enable bit
1 = UARTx continues to sample the UxRX pin; interrupt is generated on falling edge; bit is cleared in hardware on following rising edge
0 = No wake-up is enabled

Therefore, just before going to sleep, the WAKE pin is set to true to ensure that a H-to-L transition on the RDA pin will trigger an interrupt when sleeping.


Step 2:
At wakeup, after checking which interrupt got triggerd (using <interrupt_active( interrupt_name)>), I printed the status of the U1STA register and noticed that the first three bits (0, 1 and 2) were set to '1'.

"U1STA" is already defined somewhere in the compiler. This is the 16-bit UART 1 STATUS REGISTER.
BIT0: URXDA: Receive Buffer Data Available bit <-- It is set to '1' because data is available
BIT1: OERR: Receive Buffer Overrun Error Status bit <-- It is set to '1' because an overrun error occured
BIT1: FERR: Framing Error Status bit <-- It is also set to '1' because a framing error occured

So by adding this line of code:

U1STA = U1STA & 0xFFF8;

It is forcing these three first bits back to 0, clearing the errors therefore making the UART RDA interrupt working.

Here's my simplified code:

Code:


   // Get status of UART1_WAKE bit from the U1MODE register
   #bit UART1_WAKE=getenv("BIT:U1MODE.WAKE")

   // Force UART1_WAKE bit to TRUE
   UART1_WAKE = TRUE;

   //****************************************
   // Enable interrupts that can wake-up unit
   //****************************************
   enable_interrupts( INT_RDA );   // Enable incoming UART data detection
   enable_interrupts( INT_EXT0 );  // Enable input 0 change detection
   enable_interrupts( INT_EXT1 );  // Enable input 1 change detection
   enable_interrupts( INT_EXT2 );  // Enable input 2 change detection
   enable_interrupts( INT_EXT3 );  // Enable input 3 change detection
   enable_interrupts( INT_EXT4 );  // Enable input 4 change detection

   //****************************************
   // Not sure if necessarry but clear interrupt flags
   //****************************************
   clear_interrupt( INT_RDA );
   clear_interrupt( INT_EXT0 );
   clear_interrupt( INT_EXT1 );
   clear_interrupt( INT_EXT2 );
   clear_interrupt( INT_EXT3 );
   clear_interrupt( INT_EXT4 );

   // Sleep NOW
   sleep( SLEEP_FULL );

   //**************************************************************************
   // Wake-up from sleep
   //**************************************************************************

   // Wake-up - wait a few cycles
   delay_cycles( 100 );

   // Check which interrupt got triggered
   unsigned long InterruptState = ( interrupt_active( INT_RDA )  << 6 ) |
                                  ( interrupt_active( INT_EXT0 ) << 5 ) |
                                  ( interrupt_active( INT_EXT1 ) << 4 ) |
                                  ( interrupt_active( INT_EXT2 ) << 3 ) |
                                  ( interrupt_active( INT_EXT3 ) << 2 ) |
                                  ( interrupt_active( INT_EXT4 ) << 1 );

   // Force FERR (BIT2), OERR (BIT1) and UR1DA (BIT0) to '0' to clear them
   U1STA = U1STA & 0xFFF8;

   ...continue here, #INT_RDA will now work


Enjoy and thanks NewGuy and TTelmah!

Ben
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page Previous  1, 2
Page 2 of 2

 
Jump to:  
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