Jump to content
Sign in to follow this  
scorpio

Reference To Volatile Variable

Recommended Posts

Hi, I was trying to do something like:

void doFunctionLoop0(bit & enable) {
  while(enable) {
	// .... blinking led or something
  }
}

void doFunctionLoop1(bit & enable) {
  while(enable) {
	// .... beeping
  }
}


void main() {
  while(1) {
  enabledFlag = 1;

  if(state == 0)
	 doFunctionLoop0(enabledFlag);
  else
	 doFunctionLoop1(enabledFlag);

  }
}

 

where enabledFlag is set to zero by the interrupt (reaction to button), what causes new call to one of the functions according to state.

 

The problem is, that in the assembler code the variable enable is duplicated and the while loops in functionLoops never end. Is there any nice way to specify that the reference is to volatile variable?

 

The simple way to solve this problem is to declare global variable enabledFlag and throw the references away, but I'd like to know...

Share this post


Link to post
Share on other sites
Hi, I was trying to do something like:

void doFunctionLoop0(bit & enable) {
  while(enable) {
	// .... blinking led or something
  }
}

void doFunctionLoop1(bit & enable) {
  while(enable) {
	// .... beeping
  }
}


void main() {
  while(1) {
  enabledFlag = 1;

  if(state == 0)
	 doFunctionLoop0(enabledFlag);
  else
	 doFunctionLoop1(enabledFlag);

  }
}

 

where enabledFlag is set to zero by the interrupt (reaction to button), what causes new call to one of the functions according to state.

 

The problem is, that in the assembler code the variable enable is duplicated and the while loops in functionLoops never end. Is there any nice way to specify that the reference is to volatile variable?

 

The simple way to solve this problem is to declare global variable enabledFlag and throw the references away, but I'd like to know...

 

 

I don't see enabledFlag declared in main(), so it is global to this file already? I take it these routines are all in the same file?

 

This is C++, I believe? Just a thought, but maybe using ordinary C pointers would propagate the volatile attribute:

 

 

void doFunctionLoop0(bit *enable) {
  while(*enable) {
	// .... blinking led or something
  }
}

void doFunctionLoop1(bit & enable) {
  while(*enable) {
	// .... beeping
  }
}


void main() {
  while(1) {
  enabledFlag = 1;

  if(state == 0)
	 doFunctionLoop0(&enabledFlag);
  else
	 doFunctionLoop1(&enabledFlag);

  }
}

 

 

 

If this does work, then maybe there's a bug in the C++ (alpha) which doesn't propagate that volatile attribute. If it doesn't work, then it's just my shallow understanding of C. :)

 

John

Share this post


Link to post
Share on other sites

Hi scorpio, the variable 'enabledFlag' will have to be declared globally for it to be changed in interupt routine and used in functions, and should be declared as volatile as it can be changed outside of the normal program flow by the interupt routine.

 

volatile bit enabledFlag; // let the compiler assign address of variable

or

volatile bit enabledFlag@0x??.?; // use fixed address

Share this post


Link to post
Share on other sites

Hi,

 

Reference to a volatile won't work as you expect it to, it only gets updated on return from the function (ideally this should generate a compiler error but it currently does not).

 

Using a pointer would do what you expect, but won't work as a pointer to a bit type is not supported.

 

Regards

Dave

Share this post


Link to post
Share on other sites

The problem is with SourceBoost's implementation of call by reference.

 

The referenced variable is copied to a variable local to the function. When the function returns it copies its local variable back to the referenced variable.

 

I'm guessing it is done this way to save a lot of fiddling with pointers, something that PICs are not well equipped for.

 

This seems to work well for most cases however it does fall over when the referenced variable is accessed from another thread (been there) and can make a function that is calling by reference a large struct somewhat larger in code size and slower than expected (been there).

Share this post


Link to post
Share on other sites
This seems to work well for most cases however it does fall over when the referenced variable is accessed from another thread (been there) and can make a function that is calling by reference a large struct somewhat larger in code size and slower than expected (been there).
Quite correct.

 

Regards

Dave

Share this post


Link to post
Share on other sites
The problem is with SourceBoost's implementation of call by reference.

 

The referenced variable is copied to a variable local to the function. When the function returns it copies its local variable back to the referenced variable.

 

I'm guessing it is done this way to save a lot of fiddling with pointers, something that PICs are not well equipped for.

 

This seems to work well for most cases however it does fall over when the referenced variable is accessed from another thread (been there) and can make a function that is calling by reference a large struct somewhat larger in code size and slower than expected (been there).

 

 

If I'd read the manual first, I'd have known references are a feature of BoostC, then I wouldn't have asked Scorpio if this was C++. Flat footed or what? Sorry!

 

This write-back on exit could be a very useful feature in my view, more useful than the true C++ reference since pointers can be used for that (probably). It is pretty normal at the assembler level, after all.

 

So it looks like a rethink, Scorpio. It's trade-off time between between factorising and efficiency.

 

John_E

Share this post


Link to post
Share on other sites
The problem is with SourceBoost's implementation of call by reference.

 

The referenced variable is copied to a variable local to the function. When the function returns it copies its local variable back to the referenced variable.

 

I'm guessing it is done this way to save a lot of fiddling with pointers, something that PICs are not well equipped for.

 

This seems to work well for most cases however it does fall over when the referenced variable is accessed from another thread (been there) and can make a function that is calling by reference a large struct somewhat larger in code size and slower than expected (been there).

 

 

 

 

If I'd read the manual first, I'd have known references are a feature of BoostC, then I wouldn't have asked Scorpio if this was C++. Flat footed or what? Sorry! I'm reading it now :)

 

This write-back on exit could be a very useful feature in my view, more useful (at least, for slow and tiny MCU's) than the true C++ reference since pointers can be used for that (probably). It is pretty normal at the assembler level, after all.

 

So it looks like a rethink, Scorpio. It's trade-off time between between factorising and efficiency.

 

John_E

Share this post


Link to post
Share on other sites

I am having the same problem and the discussion has not helped me. The problem is that I have a volatile variable

 

volatile char ApplicationState; //stores the current state of the application

 

my application boils down to

 

void main(void){

..

ApplicationState = JustStarted; //initialise the application's state

..

while (ApplicationState != Running){ //do not continue until the app is running

} //so loop here until we can proceed

..

..

..

}

 

void interrupt(){

..

..

ApplicationState= Running; //got correct interrupt so we can let the app run now

..

}

 

This worked fine with Hi-Tech but now that I have started using SourceBoost the loop in Main is never exited. The interrupt is being called as test code in the interrupt (Toggeling a PIC I/O) shows it is.

 

Please could you explain how I can make it so that my main is stopped until a certain interrupt is received.

 

 

I have tried the changes

volatile char ApplicationState;

volatile char *pAppState = &ApplicationState;

 

in interrupt

*pAppState = Running;

 

but that does not work

 

Thanks

Share this post


Link to post
Share on other sites
I am having the same problem and the discussion has not helped me. The problem is that I have a volatile variable

 

volatile char ApplicationState; //stores the current state of the application

 

my application boils down to...

 

Works fine for me (tried on PIC16 target):

 

#include <system.h>

volatile char ApplicationState; //where 0 - just started
							//		1 - running

void interrupt( void )
{
ApplicationState = 1; //change state to 'Running'

clear_bit( intcon, T0IF );  //clear TMR0 overflow flag
}

void main()
{
ApplicationState = 0; //set state to 'Just Started'

option_reg = 7;	//set prescaler

// enable interrupts
set_bit( intcon, T0IE ); //enable TMR0 overflow bit	
set_bit( intcon, GIE );

while( !ApplicationState ); //do not continue until the app is running

while( 1 ); //endless loop
}

 

Regards,

Pavel

Share this post


Link to post
Share on other sites

Found it. My mistake. I was enabeling the interrupt within the interrup service routine. This was highlighted as a bad thinkg to do in another post.

 

Great compiler guys.

 

Glad I bought it.

 

Chris

Share this post


Link to post
Share on other sites

Oops, there is a problem starting interrupts gain within an interrupthandler?

 

I'm using a common interrupt handler for TMR1 and RB0. At the beginning of the handler I stop both interrupts and at the end I start them both again.

 

This can give problems?

Is there an easy way to get around that?

 

I can't use flags to start them again later somewhere in main() because there 's a chance to miss an interrupt or run into timing problems then.

 

[edit]hmm sorry Chris, I hope I'm not stealing your topic now..![/edit]

Edited by fred

Share this post


Link to post
Share on other sites
Oops, there is a problem starting interrupts gain within an interrupthandler?

 

I'm using a common interrupt handler for TMR1 and RB0. At the beginning of the handler I stop both interrupts and at the end I start them both again.

 

This can give problems?

Is there an easy way to get around that?

 

I can't use flags to start them again later somewhere in main() because there 's a chance to miss an interrupt or run into timing problems then.

 

[edit]hmm sorry Chris, I hope I'm not stealing your topic now..![/edit]

 

What do you mean by 'starting' or 'stopping' interrupts? There shouldn't be any need to do anything as the interrupt routine itself can't be interrupted.

 

I usually use something of the form:

void
interrupt()
{
nextInterrupt:
if ( xxIF )
{
xxIF = 0;

// Handle xx

goto nextInterrupt;
}

if ( yyIF )
{
yyIF = 0;

// Handle yy

goto nextInterrupt;
}

}

 

It implements a priority scheme where interrupt xx takes priority over interrupt yy.

 

If you want to disable an interrupt temporarily, then set the IE bit to zero. Change the tests to if ( xxIF && xxIE ) if you don't want to handle an interrupt that you have 'disabled'.

 

Orin.

Share this post


Link to post
Share on other sites

The routine can be called from within two interrupts:

1/ timer1

2/ rb0 falling edge

 

during handling from a timer1 interrupt a rb0 interrupt might occur or a timer1 interrupt in the situation it's handling the falling edge interrupt.

To prevent this I'm just disabling both interrupts at the start of the routine and reactivate them at the end

Share this post


Link to post
Share on other sites
The routine can be called from within two interrupts:

1/ timer1

2/ rb0 falling edge

 

during handling from a timer1 interrupt a rb0 interrupt might occur or a timer1 interrupt in the situation it's handling the falling edge interrupt.

To prevent this I'm just disabling both interrupts at the start of the routine and reactivate them at the end

 

Which PIC?

 

For most, you cannot take a second interrupt while handling the first unless you were to deliberately set the global interrupt enable flag in the interrupt routine and what a can of worms that would be...

 

Or are you talking about a routine that runs as a result of an interrupt, but not in the interrupt context? In that case, it would be perfectly reasonable to disable both interrupt enables during the routine.

 

Orin.

Share this post


Link to post
Share on other sites

Hi Orin

 

it's a 16F648A receiving an infrared RC5 signal on rb0

 

 

What basically happens is this:

 

	
void interrupt( void ) {

//Handle timer1 interrupt
if( pir1 & (1<<TMR1IF) ) {
	if (( IRstreambit > 0 ) &&  ( IRstreambit != 0xff )) { 
		tmr1overflow+= 1;									
		IR_ISR_handler();													
	}
	clear_bit( pir1, TMR1IF );
}

//falling edge portB.0
if( intcon & (1<<INTF) ) {
	if ( tmr1overflow )	resetIR= 1;
	else 			IR_ISR_handler();
	clear_bit( intcon, INTF ); 
}
return;
}



void IR_ISR_handler( void ) {
uint 	tmr1int;

if ( (IRstreambit == 0xff) || (resetIR==1)) return;	
if ( IRstreambit > IRMAXPULSES )  {			
	resetIR= 1;					
	return;
}

clear_bit( intcon, INTE );
tmr1int= StopTmr1();	

if ( tmr1overflow > 0 ) {	
	if ( tmr1overflow == 1 ) {	
		StartTmr1( 0xD8FF );	
	}
	else {
		if ( IRstreambit >= IRMINPULSES ) {
			IRstreambit= 0xff;	
		}
		else {		
			resetIR= 1;
		}
	}
	return;
}

if ( IRstreambit > 0 ) {	
	IRstream[IRstreambit- 1]= tmr1int;
}

IRstreambit++;	
StartTmr1( TMR1BASE );	
set_bit( intcon, INTE );	
clear_bit( intcon, INTF );
return;
}

 

IR_ISR_handler() is called within each interrupt. During the time that one of the interrupts is handled I want to have a status quo situation.

 

 

I'm pretty struckling with this 'project' for some time now because I'm getting some strange timing results and really can't find what's happening until now. When I use stimilus (MPLAB) to simulate a RC5 pulse train on rb0 and which is generating these interrupts, everything looks pretty well but in 'real live' it isn't and I don't think it's a circuit problem either.

 

So when I read something like

"Found it. My mistake. I was enabeling the interrupt within the interrup service routine. This was highlighted as a bad thinkg to do in another post."
then I'm more than curious if that's relevant here aswell :(

Share this post


Link to post
Share on other sites
Hi Orin

 

it's a 16F648A receiving an infrared RC5 signal on rb0

 

IR_ISR_handler() is called within each interrupt. During the time that one of the interrupts is handled I want to have a status quo situation.

 

 

I'm pretty struckling with this 'project' for some time now because I'm getting some strange timing results and really can't find what's happening until now. When I use stimilus (MPLAB) to simulate a RC5 pulse train on rb0 and which is generating these interrupts, everything looks pretty well but in 'real live' it isn't and I don't think it's a circuit problem either.

 

I don't think you need to do anything with INTE during IR_ISR_handler unless it is intentional to return without setting it at the end of the third if. Do you reset TMR1IF before enabling the timer in your StartTmr1 function?

 

Orin.

Share this post


Link to post
Share on other sites

I think you're right about INTE. This is because I added

		if ( tmr1overflow )	resetIR= 1;
	else			 IR_ISR_handler();

recently during it's interrupt. Before that it could be relevant. I'll put it on comment.

 

StartTmr1 en StopTmr1 looks like this

void	StartTmr1( unsigned int numval ) {
_cnvint_	value;

value.num= numval;	
tmr1h= value.hl.hi; 
tmr1l= value.hl.lo; 
set_bit( t1con, TMR1ON ); 
return;
}

inline uint	StopTmr1(void) {
_cnvint_	value;
uint		numval;

clear_bit( t1con, TMR1ON );
value.hl.hi = tmr1h; 
value.hl.lo = tmr1l;
numval=  value.num;
return(numval);
}

 

fred

Share this post


Link to post
Share on other sites
I think you're right about INTE. This is because I added
		if ( tmr1overflow )	resetIR= 1;
	else			 IR_ISR_handler();

recently during it's interrupt. Before that it could be relevant. I'll put it on comment.

 

StartTmr1 en StopTmr1 looks like this

void	StartTmr1( unsigned int numval ) {
_cnvint_	value;

value.num= numval;	
tmr1h= value.hl.hi; 
tmr1l= value.hl.lo; 
set_bit( t1con, TMR1ON ); 
return;
}

inline uint	StopTmr1(void) {
_cnvint_	value;
uint		numval;

clear_bit( t1con, TMR1ON );
value.hl.hi = tmr1h; 
value.hl.lo = tmr1l;
numval=  value.num;
return(numval);
}

 

fred

 

I think you should clear TMR1IF immediately before setting TMR1ON. There is a small window of opportunity between testing INTF in interrupt() and calling StopTmr1() in IR_ISR_handler() where the timer could overflow and set TMR1IF. I suspect under normal circumstances, it couldn't happen (as in your MPLAB simulation) but a noisy IR signal could cause it to happen.

 

Orin.

Share this post


Link to post
Share on other sites

I will make that change. Unfortunately I don't beleave this is what is going on in my situation and solving my problem however.

 

I'll try to illustrate what's happening (oops this is getting a bit off topic now. Hopefully this is not a big problem and the moderator will let it go or move it to a new topic because I get bogged on this..!)

 

With this code I'm capturing (at least I'm trying to.. :( ) the time intervals of falling edges when receiving an IR RC% pulse train. When the stream is valid I can only distinguish 3 type of intervals: 1778us (1 period), 2667us (1.5period) or 3556us (2 periods)

 

My IRsender sends IR using machine code 16. Here I illustrate what pulses theoratical are on rb0 when pressing the button '0' two times. (the toggle bit - bit3 - flips when the same key is pressed once more)

post-2116-1201777212_thumb.jpg

 

Now what''s happening in real live...

I press 3 times. After each time the captured array (containing the timer1 values) is displayed. (this results include the adjustments Orion suggested)

 

The results are shown in this picture

post-2116-1201778650_thumb.jpg

after subtracting the offset (TMR1BASE in de code which has the value of 4000) the values represents the usecs:

(1712 is the average value to let it look like a bit less messy. the least min value is 0x1643 and the max is 0x165E. so the variation is max 27 which is pretty accurate I think)

 

 

This looks bizar to me and I must make somewhere a verry fundamental mistake...

- The very first element seems to be pretty unreliable..

- each next indiviual element just look to be valid which can't be occasional or a noise issue..

- after the '2' type the stream looks very good but except I'm missing one more pulse everytime..

- when the toggle bit is '1' the '3' and '2' interval types are reversed

 

When I repeat the same captures more times the results stay the same! Except the very first element varies. I also tried another remote control and the results are similar. I get a bit frustrated by now because I think (uptil now :D ) the alorithm I made is pretty nice but apparently I can't get it implemented.

Share this post


Link to post
Share on other sites
This looks bizar to me and I must make somewhere a verry fundamental mistake...

- The very first element seems to be pretty unreliable..

- each next indiviual element just look to be valid which can't be occasional or a noise issue..

- after the '2' type the stream looks very good but except I'm missing one more pulse everytime..

- when the toggle bit is '1' the '3' and '2' interval types are reversed

 

When I repeat the same captures more times the results stay the same! Except the very first element varies. I also tried another remote control and the results are similar. I get a bit frustrated by now because I think (uptil now :) ) the alorithm I made is pretty nice but apparently I can't get it implemented.

 

Well, lines 1 and 3 look OK apart from the first value... which tends to indicate a problem with the initial values of your variables when portb.0 goes low for the first time. At this point, is the timer on? What would the value of IRstreambit be at this point?

 

Orin.

Share this post


Link to post
Share on other sites

the timer is only enabled in StartTmr1() which is only called from within IR_ISR_handler() so it isn't running at the start. And if it would then the whole stream starts with timeroverflow set which will end the capturing immediately.

 

in the main loop

 

- IRstreambit is checked. when it has the value of 0xff (set in IR_ISR_handler()) the capturing has finished succesfully (hm.:) ) and the array content will be decoded outside the interrupts. At the end of the decoding resetIR is set

 

- resetIR is checked. when it's set all used variables and the array will be reset, timer1 stopped (possibly again) and finally enabling INTE. that's why I was stopping INTE at the start of IR_ISR to prevent new interrupts coming in between leaving the interrupt handling and resetting the array before the content could be decoded when relevant.

 

You are right : At a first glance it doesn't look so bad. That's the bizar thing... If you look at the picture you will see that there should be 9 times 1712us after the longer pulse however. there are only 8 so one is missing ervery time..!

 

Another strange one which theroretical is impossible but a bit harder to explain: the value of 3515 can never be follwed by 2667 at this time. the value of 3515 (=2 periods with only a falling edge at the end) can never start at the beginning of a period!! in a period you always have to deal with a 0->1 or 1->0. if this value starts at the start of a period it would be 0->0 which is impossible in a biphase coding..!

As you can see in the picture there should always be a 2667 (1.5 period) and a 3515 combination with toggle '1' and machine code x10 after the two initial startbits

 

ps.

I'm very happy that someboby is looking with me at this problem because the whole ting is driving me crazy.. ;) It's probably a simple and stupid thing but I just don;t see it. Thanks!!

 

Fred

Share this post


Link to post
Share on other sites
the timer is only enabled in StartTmr1() which is only called from within IR_ISR_handler() so it isn't running at the start. And if it would then the whole stream starts with timeroverflow set which will end the capturing immediately.

 

in the main loop

 

- IRstreambit is checked. when it has the value of 0xff (set in IR_ISR_handler()) the capturing has finished succesfully (hm.:) ) and the array content will be decoded outside the interrupts. At the end of the decoding resetIR is set

 

- resetIR is checked. when it's set all used variables and the array will be reset, timer1 stopped (possibly again) and finally enabling INTE. that's why I was stopping INTE at the start of IR_ISR to prevent new interrupts coming in between leaving the interrupt handling and resetting the array before the content could be decoded when relevant.

 

You are right : At a first glance it doesn't look so bad. That's the bizar thing... If you look at the picture you will see that there should be 9 times 1712us after the longer pulse however. there are only 8 so one is missing ervery time..!

 

Another strange one which theroretical is impossible but a bit harder to explain: the value of 3515 can never be follwed by 2667 at this time. the value of 3515 (=2 periods with only a falling edge at the end) can never start at the beginning of a period!! in a period you always have to deal with a 0->1 or 1->0. if this value starts at the start of a period it would be 0->0 which is impossible in a biphase coding..!

As you can see in the picture there should always be a 2667 (1.5 period) and a 3515 combination with toggle '1' and machine code x10 after the two initial startbits

 

ps.

I'm very happy that someboby is looking with me at this problem because the whole ting is driving me crazy.. ;) It's probably a simple and stupid thing but I just don;t see it. Thanks!!

 

Fred

 

Fred,

 

Are you absolutely sure you are interrupting on the falling edge of the signal? Looking at your first diagram, if you were interrupting on the RISING edge, then the type 2 and 3 intervals would indeed be swapped.

 

Orin.

Share this post


Link to post
Share on other sites

Orin

 

the only references I have to opion_reg are in the init part at the start of main()

 

set_bit( option_reg, PSA );

clear_bit( option_reg, T0CS );

clear_bit( option_reg, 7 ); // disable pullups

clear_bit( option_reg, INTEDG); // falling edge

 

I realize that the beginning might be destroyed by noise. What happens when I have a 'low' situation due to noise at the time a RC5 pulse train starts.. Pretty unpredicatble I think!

What I'll do is validating the very first pulse duration which always should be a 1778us one. If that happens I'll ignore the rest of the train that has started apparently until the first overflow occurs and set IRreset. Hopefully this pulse train is immeditaley followed by a clean one.. :)

 

I will check this first and see what the new results will be.

fred

 

[update edit]

I introduced a variable streamValid. This one is set to false if the interval between the first two falling edges is outside the tollerance of 1778us. (GetIRSampleType( tmr1int ) != 1 )

From that point the values are not captured anymore and after the overflows occurs (end of pulse train) the sequence is to be considered as invalid at the start of the decode procedure.

 

the ISR code and first lines of RC5decode() now looks like this

void IR_ISR_handler( void ) {
uint 	tmr1int;

if ( (IRstreambit == 0xff) || (resetIR==1)) return;	
if ( IRstreambit > IRMAXPULSES )  {					
	resetIR= 1;										
	return;
}

tmr1int= StopTmr1()- TMR1BASE;

if ( tmr1overflow == 1 )  {	
	StartTmr1( 0xD8FF );	
	return;
}

clear_bit( intcon, INTE );	
if ( tmr1overflow > 1 ) {	
	if ( IRstreambit >= IRMINPULSES ) {
		IRstreambit= 0xff;
	}
	else {					
		resetIR= 1;	
	}
	return;
}

if ( IRstreambit > 0)  {
	if (( IRstreambit == 1 ) && ( GetIRSampleType( tmr1int ) != 1 )) streamValid= 0;
	if ( streamValid ) {
		IRstream[IRstreambit- 1]= tmr1int; 
							}
			}	

IRstreambit++;	
StartTmr1( TMR1BASE );
	return;
}


// TMR1BASE  is more or less  the calculated value of  0xFFFF - (2*1778) - ( 1778 / 4 )


void RC5decode() {		

if ( IRstreambit != 0xff )	return;	
if (( resetIR ) || ( !streamValid ))  {
	resetIR= 1;	
	return;
}

//time to decode the captured stream of falling edges at this point!

 

The first byte of the IR train pulses now look valid. In case when the toggle bit is '0' the long and medium sized interval are still reversed which is an invalid situation so decoding this one will always fail. I'm also still missing the last pulse. the result of this is that for most values the captured stream is valid as ok but decoding will give wrong results. for instance: A '0' press will return a command '1', a '1' press =>command 3, press'4' => command 0xd, press'6' => command also 0x0d.

 

[/update]

Edited by fred

Share this post


Link to post
Share on other sites

Join the conversation

You are posting as a guest. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
Sign in to follow this  

×
×
  • Create New...