![](templates/subSilver/images/CCSLogo.jpg) |
![CCS C Software and Maintenance Offers](templates/subSilver/images/forumAd6.jpg) |
View previous topic :: View next topic |
Author |
Message |
Guest
|
RS232 data receive when in sleep mode (16F688), help needed! |
Posted: Sun Aug 05, 2007 9:53 am |
|
|
I'm a complete PIC novice so apologies in advance for any misunderstandings on my part My development environment is the MPLAB ICE 2000 (with PCM16YL0 module) and CCS C compiler V4.049.
My problem is that I need the PIC to use as little power as possible as its battery has to last the maximum it can. The PIC device is the 16F688. The specification for it suggests the EUSART supports wake up from sleep on receipt of data.
I've extracted some code to make a self contained test. The following code is just a simple loop in main and a ISR for RS232 data reception. Each time a character is received, it's added to a software buffer. When a special character is received that buffer is then sent back down the line to the receiving party (this isn't really what my device will do but it serves its purpose to demonstrate the issues I'm seeing!).
Code: | #include <16F688.h>
#fuses INTRC_IO
#fuses NOMCLR
// #fuses WDT
#device adc=10
#use delay (clock=4000000)
#use rs232( baud=9600, parity=n, stop=1, xmit=PIN_C4, rcv=PIN_C5, errors )
#define MAX_BUF 20
char g_rs232data[MAX_BUF];
int g_rs232pos;
int1 g_sendData;
//-----------------------------------------------------------------------------
// Private: Empty Buffer
//-----------------------------------------------------------------------------
void EmptyBuffer( void )
{
int i;
for( i = 0; i < MAX_BUF; i++ )
{
// Just set the values to a known value
g_rs232data[i] = 0xCD;
}
// Reset the software buffer position to the start
g_rs232pos = 0;
}
//-----------------------------------------------------------------------------
// Main
//-----------------------------------------------------------------------------
void main( void )
{
int i;
g_sendData = 0;
// Clear the software buffer
EmptyBuffer();
// Setup the oscillator
setup_oscillator( OSC_4MHZ );
// Enable the watchdog timer
// setup_wdt( WDT_ON | WDT_1152MS | WDT_TIMES_4 );
// Configure the UART for wakeup on receipt of data
// If this line is enabled, the RS232 seems to 'echo' characters
// setup_uart( UART_WAKEUP_ON_RDA );
// Enable interrupts for the RS232 port
enable_interrupts( INT_RDA );
// Enable global interrupts
enable_interrupts( GLOBAL );
while( 1 )
{
// Process send request
if( g_sendData == 1 )
{
g_sendData = 0;
for( i = 0; i < g_rs232pos; i++ )
{
// Send 1 byte at a time
putc( g_rs232data[i] );
}
// Now empty the buffer
EmptyBuffer();
}
}
}
//-----------------------------------------------------------------------------
// Private: RS232 Interrupt Method
//-----------------------------------------------------------------------------
#int_rda
void rda_isr( void )
{
char rxData;
// Read the character from the hardware buffer
rxData = getch();
if( rxData == 0x24 )
{
// End of data stream, only set send bit if we've buffered something
g_sendData = ( g_rs232pos > 0 );
return;
}
if( g_rs232pos + 1 > MAX_BUF )
{
// No more data is going to fit into buffer, just discard it [for now]
return;
}
// Add the received data to the software receive buffer
g_rs232data[g_rs232pos++] = rxData;
} |
I may well have two problems:
1) The main issue is one of the sleep itself. When I uncomment the lines above:
#fuses WDT
setup_wdt( WDT_ON | WDT_1152MS | WDT_TIMES_4 );
setup_uart( UART_WAKEUP_ON_RDA );
I don't seem to wake up at all. Certainly the trace indicates that whilst data was sent, none was received:
Code: | Request: 05/08/2007 16:23:00.27964 (+42.5512 seconds)
06 00 00 00 01 24 .....$ |
The trace was provided by an external tool that monitors data being sent via RS232. In order to try and prove the code works, I commented out the parts of the code concerned with the sleep mode. However this is when I 'found' problem #2.
2) The code as shown configures the UART for Auto Wake Up using setup_uart( UART_WAKEUP_ON_RDA )
When this line of code is compiled in by itself (i.e. without the watchdog timer running and the PIC not being sent to sleep), I get characters seemingly echoed on the RS232 port. An example of this looks like:
Code: | Request: 05/08/2007 16:04:23.43564
06 00 00 00 01 24 .....$
Answer: 05/08/2007 16:04:23.43564 (+0.0000 seconds)
01 01 01 01 06 06 00 00 00 00 00 00 00 ............. |
The output isn't consistent (the number of characters and their content vary). Here it looks like the characters are echoed multiple times but at other times there are too few characters displayed.
With the code as provided as above, the trace shows what I'd expect it should: Code: | Request: 05/08/2007 16:42:07.23864 (+1.7525 seconds)
06 00 00 00 01 24 .....$
Answer: 05/08/2007 16:42:07.24864 (+0.0100 seconds)
06 00 00 00 01 ..... |
I'm clearly missing something [probably quite obvious!] so can I ask if anyone has any thoughts on what I've done wrong?
Thanks in advance! |
|
![](templates/subSilver/images/spacer.gif) |
adamhearn
Joined: 05 Aug 2007 Posts: 3
|
|
Posted: Sun Aug 05, 2007 9:54 am |
|
|
That was me! Somehow my cookie didn't save me logged in :( |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah Guest
|
|
Posted: Sun Aug 05, 2007 10:31 am |
|
|
Start with how you are trying to 'wake' the UART?.
The EUSART, wakes the chip, on a falling edge on the serial input, _but_ it takes time to wake up. The best way to wake the chip, is to send a 'break' from the source, and hold the line low for a few mSec. This allows time for the internal oscillator to start. Then raise the line, wait one character time, and start transmitting. The character that 'wakes' the chip, won't be received (the UART needs the oscillator to be running).
Second, you don't show how you are putting the chip to sleep?.
Then, you are enabling a watchdog, but don't at any point reset the watchdog. This will result in the code restarting, and is probably why it stops working.
Best Wishes |
|
![](templates/subSilver/images/spacer.gif) |
adamhearn
Joined: 05 Aug 2007 Posts: 3
|
|
Posted: Sun Aug 05, 2007 10:48 am |
|
|
Hi,
1st off, thanks for the post and apologies for missing out my call to sleep in the main loop!
Code: |
while( 1 )
{
// Sleep to save power
//sleep();
// Process send request
if( g_sendData == 1 )
{
g_sendData = 0;
for( i = 0; i < g_rs232pos; i++ )
{
// Send 1 byte at a time
putc( g_rs232data[i] );
}
// Now empty the buffer
EmptyBuffer();
}
}
|
I am waking the UART up just by sending a char from a PC. The PC app is dumb and just streams some chars to a communications port. I'm not a hardware person so I'm not sure how to translate "is to send a 'break' from the source, and hold the line low for a few mSec" into C# (the PC application's language!).
However, I get the impression from your response that the EUSART on the PIC may well be woken up by a char but it would not be able to receive any properly until the device has 'started-up' fully.
From what I've read on the watchdog (Page 125 of the 16F688 spec), it says the WDT is cleared when the devices wakes up from Sleep, regardless of the source of the wake-up. With this statement in mind, would I still need to reset the WDT?
Also I should have explained that I'm using the WDT as no other timer works on that specific PIC when in sleep mode (well, Timer 1 could if I had an external clock to drive it... which I don't!). |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah Guest
|
|
Posted: Mon Aug 06, 2007 2:14 am |
|
|
On the PC, 'sending break', should be documented in the language paperwork for your compiler. Normally '&B' is used. The compiler automatically replaces this with the sequence required. So you would send this, pause, and then send the data you want to receive.
You can happily use any character really, with a suitable pause.
What are you actually using the watchdog 'for'?... As it stands, the code will wake on the received characters anyway, so what is the point of it?. Without the sleep, it'd cause problems, because the chip would watchdog, and reset. With the sleep, it should just wake up.
There is a problem with the sleep as shown. Add a 'delay_cycles(1)', after the sleep, and before the test for data being received. The problem is that when you sleep, the next instruction, is 'prefetched'. Hence if you have things like logic tests, these can return the wrong status...
There is another problem, in that the sleep occurs in the loop every time. Now, if the chip 'wakes' on one character, it'll not receive this (because of the time taken for the oscillator to start), and will go to sleep again, as soon as it has 'read' the garbage from the UART. The same will happen on the next character, and so on. You need to _stay awake_, for long enough for the whole message to arrive.
There is a fault in your buffer limit test. Remember if you assign an array of 'MAX_BUF' characters, this can hold values up to MAX_BUF-1'. The array as written, can overflow by one character...
Best Wishes |
|
![](templates/subSilver/images/spacer.gif) |
adamhearn
Joined: 05 Aug 2007 Posts: 3
|
|
Posted: Mon Aug 06, 2007 5:01 am |
|
|
Thanks for the explaination re break character. I will add a pause after sending my break char.
I'm using the Watchdog as I need to perform certain functions over time. The Watchdog seems to be my only method of getting the device to sleep (save battery) but still wake-up and perform the necessary actions over a period of time. I understand the watchdog isn't that accurate (across devices) but it should be OK for what I'm doing.
Thanks for the tip about putting a statement after sleep (I should have read into and understood 'prefetching') and for pointing out the schoolboy coding error with the array bounds check!
Will try out the abve tonight! |
|
![](templates/subSilver/images/spacer.gif) |
|
|
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
|