View previous topic :: View next topic |
Author |
Message |
danen
Joined: 18 Jan 2010 Posts: 17
|
How to slow down interrupt rate |
Posted: Sat Dec 31, 2011 12:50 am |
|
|
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: 19499
|
|
Posted: Sat Dec 31, 2011 5:38 am |
|
|
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
|
Your a god Ttelmah |
Posted: Sat Dec 31, 2011 9:29 am |
|
|
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: 19499
|
|
Posted: Sat Dec 31, 2011 9:44 am |
|
|
Stopwatch in MPLAB. Free, and very good.
For short things just count the instructions!....
Best Wishes |
|
|
danen
Joined: 18 Jan 2010 Posts: 17
|
|
Posted: Sun Jan 01, 2012 5:41 pm |
|
|
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
|
|
Posted: Wed Jan 04, 2012 5:12 pm |
|
|
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.
|
|
|
danen
Joined: 18 Jan 2010 Posts: 17
|
|
Posted: Sat Jan 07, 2012 9:18 pm |
|
|
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
|
|
Posted: Sun Jan 08, 2012 6:41 am |
|
|
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
|
|
Posted: Sun Jan 08, 2012 7:54 am |
|
|
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 .
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
|
Finally got it |
Posted: Sun Jan 22, 2012 11:23 am |
|
|
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. |
|
|
|