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

How to slow down interrupt rate

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



Joined: 18 Jan 2010
Posts: 17

View user's profile Send private message

How to slow down interrupt rate
PostPosted: Sat Dec 31, 2011 12:50 am     Reply with quote

I'm trying to pwm on and then off in succession some leds and for days I've been trying to get rid of the flicker at the beginning of the pwm. In researching the forums I understand that my interrupt rate is faster than the time it takes for my pic to execute the code.

When I first start the program the first led runs fine and then by the third led it flickers before the pwm begins. I am assuming that the delay in executing the code in the main loop is causing the flicker. I need some assistance on where to start to adjust the timer. I've tried div_4 up to _256 and changing the preload however it just seems to speed up the leds and then they start to pulsate.

Is it even possible to do with this code or should I be doing it a different way, any help would be greatly appreciated.

I'm using compiler 4.114 and the circuit is on the breadboard

Code:

#include <16F628A.h>

#FUSES NOWDT                    //No Watch Dog Timer
#FUSES INTRC_IO                 //Internal RC Osc, no CLKOUT
#FUSES NOPUT                    //Power Up Timer
#FUSES NOPROTECT                //Code not protected from reading
#FUSES NOBROWNOUT               //Reset when brownout detected
#FUSES MCLR                     //Master Clear pin used for I/O
#FUSES NOLVP                    //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD                    //No EE protection

#use delay(clock=4000000)

#define TIMER0_PRELOAD 193  // Gives 10 KHz interrupt rate
//#define TIMER0_PRELOAD //63   // Gives  5 KHz interrupt rate
#define LOOPCNT 39           // Gives 50 Hz PWM update rate

int8 PWM_PIN[]=
{
   PIN_B0,
   PIN_B1,
   PIN_B2,
   PIN_B3
};

int8 width;
signed int8 x;

//====================================
// Function prototypes

//#INT_RTCC 
//void tick_interrupt(void);
//-------------------------------
void main()
{
   width = 0;
   x=0;

   setup_timer_0(RTCC_INTERNAL | RTCC_DIV_1);
   set_timer0(TIMER0_PRELOAD);
   enable_interrupts(INT_RTCC);
   enable_interrupts(GLOBAL);

   while(1)
   {
         
      for (x=0;x<=3;x++)
      {
         for(width = 0; width <= LOOPCNT; width++)
         {
         delay_ms(100);
         }
      }
      for (x=3;x>=0;x--)
      {
         for(width =39 ; width <= LOOPCNT; width--)
         {
            delay_ms(100);
         }
      }
   }
   
}
//====================================
#INT_RTCC
void tick_interrupt(void)
{
static int8 loop = LOOPCNT;
static int8 pulse;

//set_timer0(get_timer0() + TIMER0_PRELOAD);

if(--loop == 0)
  {
   loop = LOOPCNT;
   pulse = width;
  }

if(pulse)
  {
   output_high(PWM_PIN[x]);
   pulse--;
  }
else
   {
    output_low(PWM_PIN[x]);
   }

}
Ttelmah



Joined: 11 Mar 2010
Posts: 19447

View user's profile Send private message

PostPosted: Sat Dec 31, 2011 5:38 am     Reply with quote

There are several things taking a lot of time in your code, which may be making things worse.

The first is pulling the pin to use from an array. Array accesses take a lot of machine cycles. If you realise that the pin numbers are just numbers, and consecutive, then where you use:

PWM_PIN[x]

can be solved about 5* faster, by using

(PIN_B0+x)

Then in fact accessing a pin by a variable is also very slow. Takes a similar time to an array access. Much faster, to use:
Code:

if (pulse) {
   pulse--;
   switch (x) {
   case 0:
      output_high(PIN_B0);
      break;
   case 1:
      output_high(PIN_B1);
      break;
   case 2:
      output_high(PIN_B1);
      break;
   case 3:
      output_high(PIN_B1);
      break;
   }
}
else {
   switch (x) {
   case 0:
      output_low(PIN_B0);
      break;
   case 1:
      output_low(PIN_B1);
      break;
   case 2:
      output_low(PIN_B1);
      break;
   case 3:
      output_low(PIN_B1);
      break;
   }
}

Though much longer, the code will run very much faster, taking 24 instruction cycles, versus 86 for the original code!.....

You can also save a couple of cycles in your counter handling, with:
Code:

if(loop) loop--;
else  {
   loop = LOOPCNT;
   pulse = width;
  }


You can also save two machine cycles on every single input/output instruction, by switching to using fast_io, and setting the tris yourself. Normally not needed, but where speed is vital, well worth using. Takes the code down to just 21 cycles using the switch.

Also to handle multiple pins, consider outputting the whole byte, and using a mask, rather than using single pin I/O.

Best Wishes
danen



Joined: 18 Jan 2010
Posts: 17

View user's profile Send private message

Your a god Ttelmah
PostPosted: Sat Dec 31, 2011 9:29 am     Reply with quote

Thank you for that information, I would of never gotten to that point myself

Another quick question, what method do you use to figure out the time it takes for the instruction cycles, I'm guessing a debugger.
Ttelmah



Joined: 11 Mar 2010
Posts: 19447

View user's profile Send private message

PostPosted: Sat Dec 31, 2011 9:44 am     Reply with quote

Stopwatch in MPLAB. Free, and very good.
For short things just count the instructions!....

Best Wishes
danen



Joined: 18 Jan 2010
Posts: 17

View user's profile Send private message

PostPosted: Sun Jan 01, 2012 5:41 pm     Reply with quote

This is my new code, however I still have the same problem with the flicker on the subsequent leds after the first one.

I'm guessing I have to find a preload value to offset the interrupt now to match the code. Now I'm even questioning if my code will even work for more than one led. Any more help would be greatly appreciated.


Code:

#include <16F628A.h>

#FUSES NOWDT                    //No Watch Dog Timer
#FUSES INTRC_IO                 //Internal RC Osc, no CLKOUT
#FUSES NOPUT                    //Power Up Timer
#FUSES NOPROTECT                //Code not protected from reading
#FUSES NOBROWNOUT               //Reset when brownout detected
#FUSES MCLR                     //Master Clear pin used for I/O
#FUSES NOLVP                    //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD                    //No EE protection

#use delay(clock=4000000)
#use fast_io(B)
 
#define LOOPCNT 10          // Gives 100 Hz PWM update rate

int8 width;
signed int8 x;

//================================================
void main()
{
   width=0;
   x=0;
   SET_TRIS_B( 0x00 );
   setup_timer_0(RTCC_INTERNAL | RTCC_DIV_1);
   //set_timer0(TIMER0_PRELOAD);
   enable_interrupts(INT_RTCC);
   enable_interrupts(GLOBAL);

   while(1)
   { 
      for(x=0; x<=3; x++)
      {
         for(width = 0; width <= LOOPCNT; width++)
         {
            delay_ms(100);
         }
      }
   }   
}

//====================================
#INT_RTCC
void tick_interrupt(void)
{
   static int8 loop = LOOPCNT;
   static int8 pulse;


   if(loop) loop--;
   else 
   {
      loop = LOOPCNT;
      pulse = width;
   }

   if (pulse)
   {
      pulse--;
      switch (x)
      {
      case 0:
         output_b(0x01);
         break;
         
      case 1:
         output_b(0x03);
         break;
   
      case 2:
         output_b(0x07);
         break;
   
      case 3:
         output_b(0x0F);
         break;
      }   
   }
   else
   {
      switch (x)
      {
      case 0:
         output_b(0x00);
         break;
   
      case 1:
         output_b(0x01);
         break;
   
      case 2:
         output_b(0x03);
         break;
 
      case 3:
         output_b(0x07);
        break;
      }
   }
}
asmboy



Joined: 20 Nov 2007
Posts: 2128
Location: albany ny

View user's profile Send private message AIM Address

PostPosted: Wed Jan 04, 2012 5:12 pm     Reply with quote

you never initialize pulse !!
it will have some random value - often 0xff - if your don't explicitly set a value for it

and the way you handle it in the ISR makes no sense either
as you must know that - with 3900 ISR INTS per second ( 4mhz )
you are not having much time LEFT to do anything BUT service the ISR .

LOOK at your LST file and try to get a sense of the trouble you are in here. Ttelmah pointed you in a good direction - but you have to try to UNDERSTAND what people are telling you , for it to matter or be worth their time to bother .... ;-))

another way to look at this is - that for every 256 full instruct cycles - you are having to branch to the ISR - and using a LOT of those precious cycles to haul your ashes around INSIDE the ISR to boot.

IMHO - you have deep conceptual trouble haunting your effort in the
good old time domain.



Arrow Idea Arrow Exclamation
danen



Joined: 18 Jan 2010
Posts: 17

View user's profile Send private message

PostPosted: Sat Jan 07, 2012 9:18 pm     Reply with quote

asmboy wrote:
you never initialize pulse !!
it will have some random value - often 0xff - if your don't explicitly set a value for it

and the way you handle it in the ISR makes no sense either
as you must know that - with 3900 ISR INTS per second ( 4mhz )
you are not having much time LEFT to do anything BUT service the ISR .

LOOK at your LST file and try to get a sense of the trouble you are in here. Ttelmah pointed you in a good direction - but you have to try to UNDERSTAND what people are telling you , for it to matter or be worth their time to bother .... ;-))

another way to look at this is - that for every 256 full instruct cycles - you are having to branch to the ISR - and using a LOT of those precious cycles to haul your ashes around INSIDE the ISR to boot.

IMHO - you have deep conceptual trouble haunting your effort in the
good old time domain.


I am a novice when it comes to pics and especially c, and the way I learn is to start with a template and try to work with it. So in my defence I found some code to pwm 1 led and tried to expand on it. Obviously not the best way to do it but I am learning slowly. Trial and error. I am grateful for all the experts on here that are always helping people out. My goal here is to actually get a working "pwm led chaser" program on here for others to use as well. So thank you
ckielstra



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

View user's profile Send private message

PostPosted: Sun Jan 08, 2012 6:41 am     Reply with quote

asmboy wrote:
you never initialize pulse !!
it will have some random value - often 0xff - if your don't explicitly set a value for it
Pulse is a static variable and as such it will always be initialized to zero at startup, hence it is no bug. A preferred coding style however is to always initialize variables in an explicit way.

Learning by doing is a great way for learning a programming language. Downside is you will learn things in a random order causing you to spent a lot of time on things that are not import for the current project at hand. For example, you have now learnt a lot of nitty gritty details about interrupts, but you have no clue as to when to use interrupts in your project or use other algorithms. Doing a little bit more research before grabbing some random example code will save you a lot of time later on. It is very well possible to create a PWM LED controller without interrupts and heaps of information can be found for this topic on the internet.

Back to your project.
You are running the RTCC interrupt at maximum frequency (RTCC_DIV_1), meaning one interrupt every 256 instructions, or about 3900 times per second at 4MHz.
This is a lot but with careful design possible. On a PIC16 the interrupt has about 50 instructions overhead for saving registers on entry and restoring at exit, leaving about 200 instruction times for your interrupt function.
You could save some more processing time by reducing the interrupt rate. 1000 interrupts per second will still yield a great software PWM.

I tested your code with the MPLAB Simulator and with the Stopwatch function clocked 29 instruction times. In your latest version timing isn't the issue anymore. The LED flicker is caused by your algorithm in main. Every 100ms you change the value of width, i.e. the pulse width and LED intensity.

I'm not really sure what kind of LED pattern you want to achieve, but current flicker is by design.
danen



Joined: 18 Jan 2010
Posts: 17

View user's profile Send private message

PostPosted: Sun Jan 08, 2012 7:54 am     Reply with quote

ckielstra wrote:
Pulse is a static variable and as such it will always be initialized to zero at startup, hence it is no bug. A preferred coding style however is to always initialize variables in an explicit way.

Learning by doing is a great way for learning a programming language. Downside is you will learn things in a random order causing you to spent a lot of time on things that are not import for the current project at hand. For example, you have now learnt a lot of nitty gritty details about interrupts, but you have no clue as to when to use interrupts in your project or use other algorithms. Doing a little bit more research before grabbing some random example code will save you a lot of time later on. It is very well possible to create a PWM LED controller without interrupts and heaps of information can be found for this topic on the internet.

Back to your project.
You are running the RTCC interrupt at maximum frequency (RTCC_DIV_1), meaning one interrupt every 256 instructions, or about 3900 times per second at 4MHz.
This is a lot but with careful design possible. On a PIC16 the interrupt has about 50 instructions overhead for saving registers on entry and restoring at exit, leaving about 200 instruction times for your interrupt function.
You could save some more processing time by reducing the interrupt rate. 1000 interrupts per second will still yield a great software PWM.

I tested your code with the MPLAB Simulator and with the Stopwatch function clocked 29 instruction times. In your latest version timing isn't the issue anymore. The LED flicker is caused by your algorithm in main. Every 100ms you change the value of width, i.e. the pulse width and LED intensity.

I'm not really sure what kind of LED pattern you want to achieve, but current flicker is by design.


Thank you for your input and your are right I do waste a lot of time trying to learn things by trial and error Embarassed .
The main goal I have is to light up 12 separate led light strips going down a set of stairs, right now I have written a program that lights them up sequentially when a sensor is tripped. I figured it would be a nice addition to pwm then on and off to give it more of an effect and so you're not blinded by them turning on in front of you.

In regards to the flicker, it looks like the led turns on and then pwm up, as opposed to being off and then slowly goes on like the first one.
danen



Joined: 18 Jan 2010
Posts: 17

View user's profile Send private message

Finally got it
PostPosted: Sun Jan 22, 2012 11:23 am     Reply with quote

I'd just like to say that I finally got it working, took a lot of time but I am happy with the end results. I'd just like to thank everyone for helping me and pointing me in the right direction.
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