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

Problem understanding why my interrupt isn't working

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
eyewonder300



Joined: 09 Jun 2004
Posts: 52

View user's profile Send private message

Problem understanding why my interrupt isn't working
PostPosted: Thu Jan 13, 2011 9:50 pm     Reply with quote

I want to be able to enable/disable interrupt for Timer2 at different times in the program, to get a required delay (yes, I could use delay_us(), but I am having timing problems with that).

When Timer two overflows & interrupts, I want it to set a flag for me to wait on. What is supposed to happen is that I clear the flag I use to monitor the interrupt, enable the interrupt, start waiting, it overflows in 3.3mSec, enters the isr, and then sets the flag, and I do the other things I need.

My 'Set_Strobe' & 'Clr_Strobe' are lines that I can monitor with my scope.
What does appear to be happening is that as soon as I enable the interrupt, the isr fires, asserts my flag & does a 'Set_Strobe' without doing the 3.3mSec delay that I expected. The 'Set_Strobe ' signal gets asserted for only 15uSec, looking at my scope. And the other 'output_bit' pins are showing a time between interrupts of 20mSec, not the expected delay_ms(20) PLUS the 3.3mSec expected for the Timer2 overflow isr.

The chip is PIC16F876a, and compiler is 4.069.

Help is appreciated.

Cheers,
Steve


Code:

/////////////////////////////////////////////////////////////////////////



#include "Y:\CCS_Projects\GE_LOAD_876a\GE_LOAD_876a.h"

   
#int_Timer2
void Timer2_ISR()
   {
   set_strobe;
   T2_Delay_Complete = True;
   if (input(pin_b2))
      output_bit(pin_b2,0);
   else
      output_bit(pin_b2,1);
   }


void main()
   {

   Clr_Strobe;                            //clear sprocket signal
   setup_adc_ports(NO_ANALOGS);
   setup_adc(ADC_OFF);
   setup_spi(SPI_SS_DISABLED);
   setup_timer_0(RTCC_Internal);
   setup_timer_1(T1_DISABLED);
   setup_timer_2(T2_DIV_BY_16,37,16);  //gives delay of 3.3mSec
   setup_comparator(NC_NC_NC_NC);
   setup_vref(FALSE);

   set_tris_A(0x20);    //lower 4 bits output for 'lo_nibble', bit 4 output
                        //for 'strobe', bit 5 input for 'Read Tape' from GE
   set_tris_C(0x80);    //lower 4 bits outputs for 'hi_nibble', bit 4 'strobe',
                        //bit 5 output for 'pic_rts', bit 6 output for RS232,
                        //bit 7 input for RS232
   
   enable_interrupts(GLOBAL);

   
   while(true)
      {
      if(run_fwd)
         {
         clr_strobe;
         T2_Delay_Complete = False;
         enable_interrupts(int_timer2);   //start the 3.3mS delay
         while(T2_Delay_Complete == False);     //wait for flag to be set by isr
         disable_interrupts(int_timer2);
         delay_ms(20);
         }
      else
         disable_interrupts(int_timer2);
      }   
   }   




#include <16F876A.h>
#device *=16
//#device ICD=TRUE
#device adc=8

#FUSES NOWDT                    //No Watch Dog Timer
#FUSES HS                       //High speed Osc (> 4mhz for PCM/PCH) (>10mhz for PCD)
#FUSES NOPUT                    //No Power Up Timer
#FUSES NOPROTECT                //Code not protected from reading
//#FUSES DEBUG                    //Debug mode for use with ICD
#FUSES BROWNOUT                 //Reset when brownout detected
#FUSES NOLVP                    //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD                    //No EE protection
#FUSES NOWRT                    //Program memory not write protected

#use delay(clock=12000000)
#use rs232(baud=38400,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8,errors)
Boolean T2_Delay_Complete;                       


//------------------------------------------------------------

#define Set_Strobe output_bit(PIN_C4,1)   //simulates the 'sprocket' signal
                                          //generated by tape reader when
                                          //sprocket hole gets over detector
#define Clr_Strobe output_bit(PIN_C4,0)

#define Set_RB2 output_bit(pin_b2,1)
#define Clr_RB2 output_bit(pin_b2,0)

#define Run_Fwd input(PIN_A5)    //signal into PIC, which came from
                                 //GE PERI board to read next char. 
                                 //Logic '1' for true
asmallri



Joined: 12 Aug 2004
Posts: 1630
Location: Perth, Australia

View user's profile Send private message Send e-mail Visit poster's website

PostPosted: Fri Jan 14, 2011 1:54 am     Reply with quote

T2_Delay_Complete does not get initialized.

T2_Delay_Complete should be declared as volatile however I do not know if CCS requires this as it may internally treat variables as volatile by default.

Timer 2 is free running and you do not initialize timer 2 value before enabling the interrupt which means the delay is random.

You did not need to use interrupts. You could simply initialize the timer count and clear the timer interrupt flag then poll the timer interrupt flag until it is set.
_________________
Regards, Andrew

http://www.brushelectronics.com/software
Home of Ethernet, SD card and Encrypted Serial Bootloaders for PICs!!
eyewonder300



Joined: 09 Jun 2004
Posts: 52

View user's profile Send private message

Volatile?
PostPosted: Fri Jan 14, 2011 7:09 am     Reply with quote

I guess I am confused - wouldn't volatile mean that the value was lost after exiting an isr (or subroutine)?

T2_Delay_Complete initialization - it was there in the full code which didn't work, but inadvertently stripped it out for the test. I'll put it back in this evening, and try it, along with declaring "Volatile T2_Delay_Complete as Boolean".

Thanks,
Steve
Ttelmah



Joined: 11 Mar 2010
Posts: 19262

View user's profile Send private message

PostPosted: Fri Jan 14, 2011 10:14 am     Reply with quote

Volatile has no effect at all in CCS. It is used in some compilers to say that a variable _may_ be altered by something else, such as an interrupt handler. In CCS, you need to provide protection for this yourself. If you use a variable longer than a byte, and access it both in an interrupt, and in external code, you need to disable interrupts in the external code, while the variable is accessed, or you could get 'half the value coming from before an interrupt, and the other half from after......

Best Wishes
gpsmikey



Joined: 16 Nov 2010
Posts: 588
Location: Kirkland, WA

View user's profile Send private message

PostPosted: Fri Jan 14, 2011 11:37 am     Reply with quote

To expand a bit on what Ttelmah was saying - I got bitten with this one some years back using an older version of the CCS compiler with an 876a part. My ISR set a flag in memory. My "Main" sat in a tight loop looking at that variable and when it changed, it did something. Unfortunately, the compiler decided that since nothing in my main code modified that variable, it "helped" me by optimizing the variable into a register variable -- which never changed. The ISR changed the real one, but since the compiler had "helped" me by making it a register variable for the loop, it never saw it. Declaring the variable as "volatile" forced the compiler to re-read the variable from memory each time around the loop and hey, wonderful, now my code ran as expected !!! Rolling Eyes (every time the ISR went off, it would read some bits from the port and stuff them into the variable that Main() was supposed to be watching.

mikey
_________________
mikey
-- you can't have too many gadgets or too much disk space !
old engineering saying: 1+1 = 3 for sufficiently large values of 1 or small values of 3
John P



Joined: 17 Sep 2003
Posts: 331

View user's profile Send private message

PostPosted: Fri Jan 14, 2011 1:34 pm     Reply with quote

One thing to beware of if you're using interrupts which are disabled and then enabled again, is that you must clear the interrupt flag before the enable operation (for TMR2 on a PIC16F877A, that's TMR2IF, PIR1.1). If you don't, and if the interrupt would have occurred earlier if you hadn't disabled it, then you'll see an immediate interrupt as soon as you do enable it.

This is mentioned in the manual but I have failed to take note of it quite a few times.
eyewonder300



Joined: 09 Jun 2004
Posts: 52

View user's profile Send private message

PostPosted: Fri Jan 14, 2011 2:28 pm     Reply with quote

Good ideas, which I will be trying this weekend.

But a (simple) question - how do I clear the interrupt flag? I don't remember seeing anything specific like that in the compiler manual.

Thanks for the good suggestions. I will update upon success.

Cheers,
Steve
gpsmikey



Joined: 16 Nov 2010
Posts: 588
Location: Kirkland, WA

View user's profile Send private message

PostPosted: Fri Jan 14, 2011 2:39 pm     Reply with quote

Another reason not to have any delays inside an ISR. Crying or Very sad

One thing I have done in the past that works well is to set an unused bit high at the start of the ISR then clear it at the end of the ISR - makes it very easy to see just how much of your total time is being spend in the ISR with a scope probe.

mikey
_________________
mikey
-- you can't have too many gadgets or too much disk space !
old engineering saying: 1+1 = 3 for sufficiently large values of 1 or small values of 3
Ttelmah



Joined: 11 Mar 2010
Posts: 19262

View user's profile Send private message

PostPosted: Fri Jan 14, 2011 3:51 pm     Reply with quote

John P wrote:
One thing to beware of if you're using interrupts which are disabled and then enabled again, is that you must clear the interrupt flag before the enable operation (for TMR2 on a PIC16F877A, that's TMR2IF, PIR1.1). If you don't, and if the interrupt would have occurred earlier if you hadn't disabled it, then you'll see an immediate interrupt as soon as you do enable it.

This is mentioned in the manual but I have failed to take note of it quite a few times.


Except you really _do_ want this, if you are simply disabling the interrupt to read a number. The sequence wants to be something like:
Code:

int16 local_value;

disable_interrupts(GLOBAL);
local_value=global_value_updated_in_interrupt;
enable_interrupts(GLOBAL);
//Then use 'local value' for your operations.


This way you only delay the interrupt response by a very few machine cycles while the value is copied. Obviously similar code for putting numbers back as well.

Best Wishes
eyewonder300



Joined: 09 Jun 2004
Posts: 52

View user's profile Send private message

Finally got it to work
PostPosted: Fri Jan 14, 2011 7:55 pm     Reply with quote

It seems to have been a sequencing issue. The code posted below works as expected. The commented out interrupt enable in Main at that position would immediately clear the 'Strobe' signal, resulting in the 1.5uSec Strobe signal I was seeing with the scope.

Enabling the interrupt, then setting strobe & clearing my flag made everything work as expected.

Thanks to all for the help.

Cheers,
Steve


Code:

#include "Y:\CCS_Projects\GE_LOAD_876a\GE_LOAD_876a.h"

   
   
Short T2_Delay_Complete;       


   
#int_Timer2
void Timer2_ISR()
   {
   clr_strobe;
   T2_Delay_Complete = True;
   }


void main()
   {

   Clr_Strobe;                            //clear sprocket signal
   T2_Delay_Complete = 0;
   setup_adc_ports(NO_ANALOGS);
   setup_adc(ADC_OFF);
   setup_spi(SPI_SS_DISABLED);
   setup_timer_0(RTCC_Internal);
   setup_timer_1(T1_DISABLED);
   setup_timer_2(T2_DIV_BY_16,39,16);  //gives delay of 3.2mSec
   setup_comparator(NC_NC_NC_NC);
   setup_vref(FALSE);

   set_tris_A(0x20);    //lower 4 bits output for 'lo_nibble', bit 4 output
                        //for 'strobe', bit 5 input for 'Read Tape' from GE
   set_tris_C(0x80);    //lower 4 bits outputs for 'hi_nibble', bit 4 'strobe',
                        //bit 5 output for 'pic_rts', bit 6 output for RS232,
                        //bit 7 input for RS232
   
   enable_interrupts(GLOBAL);

   
   while(true)
      {
      if(run_fwd == True)
         {
         enable_interrupts(int_timer2);   //start the 3.3mS delay
         T2_Delay_Complete = False;
         set_strobe;
//         enable_interrupts(int_timer2);   //start the 3.3mS delay
         while (T2_Delay_Complete == False);
         disable_interrupts(int_timer2);
         delay_ms(7);
         }
      }   
   }   
asmallri



Joined: 12 Aug 2004
Posts: 1630
Location: Perth, Australia

View user's profile Send private message Send e-mail Visit poster's website

Re: Finally got it to work
PostPosted: Fri Jan 14, 2011 11:39 pm     Reply with quote

eyewonder300 wrote:
It seems to have been a sequencing issue. The code posted below works as expected. The commented out interrupt enable in Main at that position would immediately clear the 'Strobe' signal, resulting in the 1.5uSec Strobe signal I was seeing with the scope.

Enabling the interrupt, then setting strobe & clearing my flag made everything work as expected.

Thanks to all for the help.

Cheers,
Steve



It is still flawed for all the reasons already given.
_________________
Regards, Andrew

http://www.brushelectronics.com/software
Home of Ethernet, SD card and Encrypted Serial Bootloaders for PICs!!
Ken Johnson



Joined: 23 Mar 2006
Posts: 197
Location: Lewisburg, WV

View user's profile Send private message

PostPosted: Mon Jan 17, 2011 8:26 am     Reply with quote

One thing I have done in the past that works well is to set an unused bit high at the start of the ISR then clear it at the end of the ISR - makes it very easy to see just how much of your total time is being spend in the ISR with a scope probe.

mikey


Caveat: this shows how much time is spent in **your** code in the isr, but not the isr "overhead" of saving/restoring registers, which can easily be much more when your code is short and sweet . . .

Ken
gpsmikey



Joined: 16 Nov 2010
Posts: 588
Location: Kirkland, WA

View user's profile Send private message

PostPosted: Mon Jan 17, 2011 8:57 am     Reply with quote

Good point on the overhead - but if that pulse is at 80%, you probably should look at your ISR code Wink

I was used to writing my own ISR in assembly language for things like the Z-80 where I did all the overhead as well. Anybody remember the old TI 9900 processor ? One of the fastest context switches in it's day - all registers except the acc and workspace pointer were in RAM - you simply changed the workspace pointer to a new area of RAM and hey, presto 16 new registers Laughing There were two versions of the chip - TMS9900 that was a MOS device and the SBP9900 that was injector logic (and rad hard) which made it interesting to anybody doing military stuff back in the 70's.

mikey Laughing
_________________
mikey
-- you can't have too many gadgets or too much disk space !
old engineering saying: 1+1 = 3 for sufficiently large values of 1 or small values of 3
Ttelmah



Joined: 11 Mar 2010
Posts: 19262

View user's profile Send private message

PostPosted: Mon Jan 17, 2011 3:39 pm     Reply with quote

Yes, in fact a lot of the older processors were 'better' for fast ISR responses.

The Z80 itself had a duplicate register set, and a couple of the Motorola chips also allowed multiple register sets to be held 'in RAM'. Downside of that is of course that it makes the speed relatively slower than using internal registers that can be accessed at the same time as performing other memory operations, but it does seem 'daft', when old processors running at a very few MHz, can easily outperform 'modern' chips internally running hundreds of times faster for ISR responses.....

Generally, 'embedded' chips, are more likely to find systems allowing really fast ISR responses, especially those aimed at the RTOS market. Texas still produce embedded chips for which this is true.

Unfortunately, the PIC is particularly 'bad' in this regard, and CCS's implementation makes it worse....
Campaign for a bit of smartness in their interrupt handler, with it only saving registers that _are_ used in the interrupt code!. It shouldn't be that hard to actually do, and would make a massive difference.

Best Wishes
ckielstra



Joined: 18 Mar 2004
Posts: 3680
Location: The Netherlands

View user's profile Send private message

PostPosted: Tue Jan 18, 2011 12:57 am     Reply with quote

eyewonder300 wrote:
But a (simple) question - how do I clear the interrupt flag? I don't remember seeing anything specific like that in the compiler manual.
How about:
Code:
clear_interrupt(INT_TIMER2();
It is in the most recent manual (June 2010).

Code:
   setup_spi(SPI_SS_DISABLED);
This is an error in the CCS code wizard. To disable the SPI module it should be:
Code:
   setup_spi(FALSE);


Another suggestion is to make your program case sensitive by adding the '#case' directive. You now are mixing lower and upper casing in a way that is not according to the C standards...
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
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