View previous topic :: View next topic |
Author |
Message |
palyancodr
Joined: 06 Jan 2014 Posts: 31
|
INT_RDA causes strange pwm jiggling. |
Posted: Mon Jan 06, 2014 8:08 pm |
|
|
Hi,
I am using ccs c ide version v4.110 programming 16f877a.
I generate single pwm signal using timer1 with 500us each interrupt no problem with that.
I added delay to make precise sets to high cycle of pwm, no problem with that either.
I need serial communication between two pics that's why I need an int_rda interrupt. The pic which generates pwm will read a value from the other one when the data is available or presents.
When i add int_rda to code, high cycle of the pwm jiggles and this jiggling effects the servo, I want rock solid pwm signal. Why do you think this jiggle happens?
I added a video link below belonging to virtual oscilloscope reading of that pwm. You can see the high duty jiggle as I zoom in through it.
Here is the link of the video: http://www.youtube.com/watch?v=lO2SNWzSeV8
Here is the code:
Code: | #include <16f877a.h>
#device ADC = 10
#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 NODEBUG //No Debug mode for ICD
#FUSES NOBROWNOUT //No brownout reset
#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 = 20000000)
#use rs232 (baud = 9600, xmit = pin_c6, rcv = pin_c7, parity = N, stop = 1,ERRORS)
int16 preload = 63036;
int16 count = 0;
int16 serialCheck = 0;
int16 motorPwm = 0;
int16 sigThr = 0;
#int_timer1
void timer1_interrupt (){
set_timer1(preload + get_timer1());
count = count + 1;
}
#int_rda
void serial_interrupt(){
serialCheck = 1;
motorPwm = getc();
if(motorPwm != 0){
sigThr = 4 * motorPwm;
}
}
void main()
{
setup_spi(SPI_SS_DISABLED);
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);
setup_timer_2(T2_DISABLED,0,1);
setup_comparator(NC_NC_NC_NC);
SETUP_ADC_PORTS( NO_ANALOGS );
SETUP_ADC(ADC_OFF);
output_low(pin_d4);
output_low(pin_d5);
output_low(pin_d6);
output_low(pin_d2);
output_high(pin_d2);
delay_ms(2000);
output_low(pin_d2);
enable_interrupts(INT_TIMER1);
enable_interrupts(int_rda);
enable_interrupts(GLOBAL);
rs232_errors = 0;
for(;;){
if (count == 37){
output_high(pin_d5);
}
if(count == 39){
delay_us(500);
output_low(pin_d5);
count = 0;
}
}
} |
_________________ Hardware-Software Developer
Physics Engineer
Ceo airVision Aerial Video and Photography
Ceo Massive Robotics
ccs c ide version: 4.110
Pic: 16F877 |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Mon Jan 06, 2014 11:59 pm |
|
|
The "jiggle" is caused by the random delay injected by the #int_rda
interrupt. The CCS interrupt handler code takes time to execute, and
the user code inside your #int_rda routine also takes time. You can
count up the instruction cycles used to do this, by looking at the .LST
file. You can pretty much see where the jiggle comes from when you
do that.
Using the #priority statement won't fix this problem, because #int_rda
could already be in process when a Timer1 interrupt occurs.
Here is a possible fix:
Your Timer1 interrupt occurs every 500us. Your #int_rda interrupt
can occur every 1042us (at 9600 baud). So, you could remove the
separate #int_rda routine code and move it into the #int_timer1 routine.
You would now poll for the RDA interrupt inside the Timer1 routine and
then handle it there, if it occurs. You're polling it at a rate faster than
the RDA interrupt can occur, so it should work.
Handle the timer stuff first, inside the #int_timer1 routine. Then check for
the RDA interrupt. You will also have to clear the RDA interrupt flag if it's set. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19517
|
|
Posted: Tue Jan 07, 2014 1:46 am |
|
|
and remember that simply 'if (kbhit())' checks the RDA flag. |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Tue Jan 07, 2014 2:48 pm |
|
|
I assume you also understand,
that due to the way you create the "pwm" in PIC "SOFT" code,
making almost ANY change or addition in the structure of your
main() core loop - will also upset the output pattern as well.
If the timing of that "pwm" is critical, i would consider generating
it some other way.
For instance:
Using the PIC to control an 82C54 with a bit of added HCT logic to glue
2/3 of the chips' timers together comes quickly to mind.
Best of all - if using a crystal clock source-
the jitter will be amazingly small - not to mention consistent-
and you can get greater precision
in the duty cycle than coding for the pic by itself,
will ever provide. |
|
|
asmallri
Joined: 12 Aug 2004 Posts: 1635 Location: Perth, Australia
|
|
Posted: Tue Jan 07, 2014 11:45 pm |
|
|
asmboy wrote: | I assume you also understand,
that due to the way you create the "pwm" in PIC "SOFT" code,
making almost ANY change or addition in the structure of your
main() core loop - will also upset the output pattern as well.
If the timing of that "pwm" is critical, i would consider generating
it some other way.
For instance:
Using the PIC to control an 82C54 with a bit of added HCT logic to glue
2/3 of the chips' timers together comes quickly to mind.
Best of all - if using a crystal clock source-
the jitter will be amazingly small - not to mention consistent-
and you can get greater precision
in the duty cycle than coding for the pic by itself,
will ever provide. |
Or using a virtually pin compatible PIC18F4620 allowing you to use hardware high and low priority interrupts. _________________ Regards, Andrew
http://www.brushelectronics.com/software
Home of Ethernet, SD card and Encrypted Serial Bootloaders for PICs!! |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Wed Jan 08, 2014 11:06 am |
|
|
Code: | setup_spi(SPI_SS_DISABLED); | This is invalid CCS Wizard generated code and could lead to undefined results. Please replace by: |
|
|
palyancodr
Joined: 06 Jan 2014 Posts: 31
|
|
Posted: Wed Jan 08, 2014 7:34 pm |
|
|
Removing the int_rda interrupt and instead pulling it inside the timer1 or while loop was logical however tried it with the code below still no fix:S
Code: |
#include <16f877a.h>
#device ADC = 10
#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 NODEBUG //No Debug mode for ICD
#FUSES NOBROWNOUT //No brownout reset
#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 = 20000000)
#use rs232 (baud = 9600, xmit = pin_c6, rcv = pin_c7, parity = N, stop = 1,ERRORS,stream = PWMDIFF)
int16 preload = 63036;
int16 count = 0;
int16 serialCheck = 0;
int16 motorPwm = 0;
int16 sigThr = 0;
#int_timer1
void timer1_interrupt (){
set_timer1(preload + get_timer1());
count = count + 1;
}
void main()
{
setup_spi(FALSE);
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);
setup_timer_2(T2_DISABLED,0,1);
setup_comparator(NC_NC_NC_NC);
SETUP_ADC_PORTS( NO_ANALOGS );
SETUP_ADC(ADC_OFF);
output_low(pin_d4);
output_low(pin_d5);
output_low(pin_d6);
output_low(pin_d2);
output_high(pin_d2);
delay_ms(2000);
output_low(pin_d2);
enable_interrupts(INT_TIMER1);
enable_interrupts(GLOBAL);
rs232_errors = 0;
for(;;){
if(kbhit(PWMDIFF)){
serialCheck = 1; // 0 through pic pins.
motorPwm = getc();
if(motorPwm != 0){
sigThr = 4 * motorPwm;
}
}
if (count == 37){
output_high(pin_d5);
}
if(count == 39){
delay_us(1000);
output_low(pin_d5);
count = 0;
}
}
}
|
_________________ Hardware-Software Developer
Physics Engineer
Ceo airVision Aerial Video and Photography
Ceo Massive Robotics
ccs c ide version: 4.110
Pic: 16F877 |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Wed Jan 08, 2014 8:23 pm |
|
|
IF the code you have now was to become functional JUST as you wish,
would it then be COMPLETE ??
meaning NO MORE code to be added ??
or is this a reduced example of part of some larger project ??
how low does the timing jitter have to be , for it to be considered
working properly ??
what is the allowable jitter time ?? how many usecs?
( i assume the design can already have at least several hundred nsecs - even when working "correctly" )
i can't say it more clearly than this:
the way you generate the ultimate output pulse train
is STRUCTURED too poorly to be trusted for real stability.
using that HUGE delay statement
just makes my head spin.
Last edited by asmboy on Wed Jan 08, 2014 8:27 pm; edited 1 time in total |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed Jan 08, 2014 8:25 pm |
|
|
Quote: | Removing the int_rda interrupt and instead pulling it inside the timer1 or while loop was logical however tried it with the code below still no fix
#int_timer1
void timer1_interrupt (){
set_timer1(preload + get_timer1());
count = count + 1;
}
|
You didn't do it. There is no RDA interrupt code inside #int_timer1. |
|
|
palyancodr
Joined: 06 Jan 2014 Posts: 31
|
|
Posted: Wed Jan 08, 2014 8:29 pm |
|
|
I tried both like this and like at the previous post. There is still jiggle.
Code: | #include <16f877a.h>
#device ADC = 10
#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 NODEBUG //No Debug mode for ICD
#FUSES NOBROWNOUT //No brownout reset
#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 = 20000000)
#use rs232 (baud = 9600, xmit = pin_c6, rcv = pin_c7, parity = N, stop = 1,ERRORS,stream = PWMDIFF)
int16 preload = 63036;
int16 count = 0;
int16 serialCheck = 0;
int16 motorPwm = 0;
int16 sigThr = 0;
#int_timer1
void timer1_interrupt (){
set_timer1(preload + get_timer1());
count = count + 1;
if(kbhit(PWMDIFF)){
serialCheck = 1; // 0 through pic pins.
motorPwm = getc();
if(motorPwm != 0){
sigThr = 4 * motorPwm;
}
}
}
void main()
{
setup_spi(FALSE);
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);
setup_timer_2(T2_DISABLED,0,1);
setup_comparator(NC_NC_NC_NC);
SETUP_ADC_PORTS( NO_ANALOGS );
SETUP_ADC(ADC_OFF);
output_low(pin_d4);
output_low(pin_d5);
output_low(pin_d6);
output_low(pin_d2);
output_high(pin_d2);
delay_ms(2000);
output_low(pin_d2);
enable_interrupts(INT_TIMER1);
enable_interrupts(GLOBAL);
rs232_errors = 0;
for(;;){
if (count == 37){
output_high(pin_d5);
}
if(count == 39){
delay_us(1000);
output_low(pin_d5);
count = 0;
}
}
} |
_________________ Hardware-Software Developer
Physics Engineer
Ceo airVision Aerial Video and Photography
Ceo Massive Robotics
ccs c ide version: 4.110
Pic: 16F877 |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed Jan 08, 2014 8:59 pm |
|
|
Quote: | I tried both like this and like at the previous post. There is still jiggle. |
I didn't see it. I tested your original program and your latest program
with vs. 5.016. With the original program, if I open a TeraTerm window
and hold down any key so it auto-repeats, I definitely see a jiggle of at
least 10us on the falling edge of the PWM signal. If I install your latest
posted program and run it, I do not see any jiggle.
Also, I said in my previous post that if you get the RDA interrupt, then
you need to clear it. I have added a line to do this as shown below:
Code: |
#int_timer1
void timer1_interrupt (){
set_timer1(preload + get_timer1());
count = count + 1;
if(kbhit(PWMDIFF)){
serialCheck = 1; // 0 through pic pins.
motorPwm = getc();
if(motorPwm != 0){
sigThr = 4 * motorPwm;
}
clear_interrupt(INT_RDA); // *** CLEAR THE INTERRUPT ***
}
} |
|
|
|
palyancodr
Joined: 06 Jan 2014 Posts: 31
|
|
Posted: Wed Jan 08, 2014 9:09 pm |
|
|
With your correction at your latest post, i still have jiggle around 10us. Your suggestion must have solved the problem,however; mine is still have jiggle. Do you think that this can occur because of my ccs version 4.110? _________________ Hardware-Software Developer
Physics Engineer
Ceo airVision Aerial Video and Photography
Ceo Massive Robotics
ccs c ide version: 4.110
Pic: 16F877 |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed Jan 08, 2014 11:05 pm |
|
|
I installed vs. 4.110 and I see a little bit of jiggle. Maybe 4us at most.
So I re-installed vs. 5.016 and I am seeing it there too. I think it's
because you update your PWM pin outside of the #int_timer1 routine.
Normally, software PWM will do the pin update inside the interrupt routine.
If I move the code that's inside the for(;;) loop, so it's inside the
#int_timer1 routine, then I don't even see the 4us of jiggle. It's stable. |
|
|
palyancodr
Joined: 06 Jan 2014 Posts: 31
|
|
Posted: Wed Jan 08, 2014 11:21 pm |
|
|
Here is the latest version of code that i am having problem with if some you having the same problem, thanks to PCM programmer, the code is jiggle free. Thanks for helping me out PCM programmer and all of the fellas posting suggestions here.
Code: |
#include <16f877a.h>
#device ADC = 10
#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 NODEBUG //No Debug mode for ICD
#FUSES NOBROWNOUT //No brownout reset
#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 = 20000000)
#use rs232 (baud = 9600, xmit = pin_c6, rcv = pin_c7, parity = N, stop = 1,ERRORS,stream = PWMDIFF)
int16 preload = 63036;
int16 count = 0;
int16 serialCheck = 0;
int16 motorPwm = 0;
int16 sigThr = 0;
#int_timer1
void timer1_interrupt (){
set_timer1(preload + get_timer1());
count = count + 1;
if(kbhit(PWMDIFF)){
serialCheck = 1;
motorPwm = getc();
if(motorPwm != 0){
sigThr = 4 * motorPwm;
}
clear_interrupts(int_rda);
}
if (count == 37){
output_high(pin_d5);
}
if(count == 39){
delay_us(1000);
output_low(pin_d5);
count = 0;
}
}
}
void main()
{
setup_spi(FALSE);
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);
setup_timer_2(T2_DISABLED,0,1);
setup_comparator(NC_NC_NC_NC);
SETUP_ADC_PORTS( NO_ANALOGS );
SETUP_ADC(ADC_OFF);
output_low(pin_d4);
output_low(pin_d5);
output_low(pin_d6);
output_low(pin_d2);
output_high(pin_d2);
delay_ms(2000);
output_low(pin_d2);
enable_interrupts(INT_TIMER1);
enable_interrupts(GLOBAL);
rs232_errors = 0;
for(;;){
} |
_________________ Hardware-Software Developer
Physics Engineer
Ceo airVision Aerial Video and Photography
Ceo Massive Robotics
ccs c ide version: 4.110
Pic: 16F877 |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19517
|
|
Posted: Thu Jan 09, 2014 2:01 am |
|
|
Your serial check should be after the PWM. Otherwise there will be a few uSec of jiggle (tiny, but still there).
Note PCM_programmers comment:
"Handle the timer stuff first, inside the #int_timer1 routine. Then check for
the RDA interrupt. You will also have to clear the RDA interrupt flag if it's set."
Note the 'then'.
Best Wishes |
|
|
|