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 CCS Technical Support

SOLVED:timer interrupt pwm resolution
Goto page Previous  1, 2
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
z3ngew



Joined: 20 Apr 2013
Posts: 50

View user's profile Send private message

PostPosted: Sat Aug 10, 2013 7:42 am     Reply with quote

After following RLScott the code is working great and with no problems
here is the code, but it need some extra work to be finished:
Code:
#byte MCU_PORTB = 0xF81
#bit pwm_0 = MCU_PORTB.0
#bit pwm_1 = MCU_PORTB.1
#bit pwm_2 = MCU_PORTB.2
#bit pwm_3 = MCU_PORTB.3

unsigned int8 state_counter = 0;
enum cycle_state{t0_high = 1, t1_high = 2, t2_high = 3, t3_high = 4, t_low = 5};
int16 interrupt_us[4+1] = {63036, 63036, 63036, 63036, 15536}; //initial condidtion

#int_TIMER1
void TIMER1_isr(void)   //interrupt handler routine
{
   set_timer1(interrupt_us[state_counter]);
   state_counter ++;
   switch(state_counter)
   {
      case t0_high:
         pwm_0 = 0;
         pwm_1 = 1;
         break;
      case t1_high:
         pwm_1 = 0;
         pwm_2 = 1;
         break;
      case t2_high:
         pwm_2 = 0;
         pwm_3 = 1;
         break;
      case t3_high:
         pwm_3 = 0;
         pwm_0 = 1;
         break;
      case t_low:
         state_counter = 0;
         output_b(MCU_PORTB + 15);
         break;
   }
}


but i have question;
Quote:
On the first interrupt you set servo #1 pulse high and load the timer so that servo #1 can be turned off at the right time. One the second interrupt you set servo #1 low and set servo #2 high and load the timer according to the duty cycle for servo #2. Continue like this until the 5th interrupt, when you turn off servo #4 and set the timer to - what? Well, you want the overall period to be 20 msec. So as you were generating pulses 1-4, you add up the pulse lengths. Now that all four pulses are done, subtract that sum from 20 msec.

the time low is the same for all time high, the frequency may vary little bit but this should not make a problem from 20ms to 18ms.

MANY THANKS FOR ALL THE MEMBERS HERE Very Happy
z3ngew
RLScott



Joined: 10 Jul 2007
Posts: 465

View user's profile Send private message

PostPosted: Sat Aug 10, 2013 8:18 am     Reply with quote

z3ngew wrote:

but i have question;
Quote:
On the first interrupt you set servo #1 pulse high and load the timer so that servo #1 can be turned off at the right time. One the second interrupt you set servo #1 low and set servo #2 high and load the timer according to the duty cycle for servo #2. Continue like this until the 5th interrupt, when you turn off servo #4 and set the timer to - what? Well, you want the overall period to be 20 msec. So as you were generating pulses 1-4, you add up the pulse lengths. Now that all four pulses are done, subtract that sum from 20 msec.

the time low is the same for all time high, the frequency may vary little bit but this should not make a problem from 20ms to 18ms.

MANY THANKS FOR ALL THE MEMBERS HERE Very Happy
z3ngew


No, that should not matter. Here is a little secret about RC servos: they are not really sensitive to duty cycle per se. They are sensitive only to the pulse width (the active portion, 1-2 msec.) So any repetition rate within reason around 20 msec. is fine. But why can't you make it exactly 20 msec? The last element in your interrupt_us array is 15536, presumably to make the total period come out to 20 msec. But when you modify any of the first 4 elements of that array, just modify the last element too so that the total period will be 20 msec.

Here is another idea. Instead of loading timer1 to make the overflow happen at just the right time, how about using the Output Compare mode of a CCP module? Does your PIC have one? You don't need to use the output pin associated with that CCP module. You can configure the CCP module so it just creates an interrupt when there is a match. That way you can leave timer1 free-running. At each interrupt you reprogram the comparison register by adding a certain offset to that comparison register. Then when it comes time for the 20 msec. period pulse, you can use a comparison value that you calculated way back before the current 4 pulses. The total period will always be exactly 20 msec, regardless of how long the individual servo pulses are. Using the CCP module this way gets away from the messiness of having to take interrupt latency into account when loading timer1.
_________________
Robert Scott
Real-Time Specialties
Embedded Systems Consulting
z3ngew



Joined: 20 Apr 2013
Posts: 50

View user's profile Send private message

PostPosted: Sat Aug 10, 2013 12:14 pm     Reply with quote

Hi there RLScott ,
first thank you and the other members for helping me through this problem,
i have to say that there was a lot of things in pic microchip i didn't use before, but thanks to you and your brilliant ideas, i have expanded my knowledge about this subject

here is the code:
Code:
#include "main.h"
#include "flex_lcd_4x16.c"
#include "spio.h"
#include "stepmotor.h"

#byte MCU_PORTB = 0xF81
#bit pwm_0 = MCU_PORTB.0
#bit pwm_1 = MCU_PORTB.1
#bit pwm_2 = MCU_PORTB.2
#bit pwm_3 = MCU_PORTB.3

unsigned int8 state_counter = 0, i;
enum cycle_state{t0_high = 1, t1_high = 2, t2_high = 3, t3_high = 4, t_low = 5};
int16 interrupt_us[4+1] = {2500, 2500, 2500, 2500, 47500}; //initial condidtion

#int_CCP1
void CCP1_isr()  //interrupt handler routine
{
   CCP_1 = interrupt_us[state_counter];
   set_timer1(0);
   state_counter++;
   switch(state_counter)
   {
      case t0_high:
         pwm_0 = 0;
         pwm_1 = 1;
         break;
      case t1_high:
         pwm_1 = 0;
         pwm_2 = 1;
         break;
      case t2_high:
         pwm_2 = 0;
         pwm_3 = 1;
         break;
      case t3_high:
         pwm_3 = 0;
         pwm_0 = 1;
         break;
      case t_low:
         state_counter = 0;
         output_b(MCU_PORTB + 15);
         break;
   }
}

void main() //main routine
{
   lcd_init();
   delay_ms(10);
   lcd_gotoxy(1, 1); //(col, row)
   printf(lcd_putc("Hello World!"));
   
   setup_ccp1(CCP_COMPARE_INT);
   setup_timer_1 ( T1_INTERNAL | T1_DIV_BY_2 );
   enable_interrupts(INT_CCP1);
   enable_interrupts(GLOBAL);
   
   //lcd_gotoxy(1, 4);
   //printf(lcd_putc, "%ld", counter);
   
   while(true)
   {
      handle_read();
   }
}


timer 1 has a prescaler of 2 in order to get 20000us.
the initial condition is as follow
1ms = 1000us , 1000 / 0.4 = 2500 counts
20000 - 1000 = 19000 / 0.4 = 47500 counts.

MANY THANKS TO ALL THE MEMBERS HERE Very Happy
Sincerely
z3ngew
z3ngew



Joined: 20 Apr 2013
Posts: 50

View user's profile Send private message

PostPosted: Fri Sep 13, 2013 12:42 pm     Reply with quote

Hello again,
i am sorry i am reopening this thread, but if i want to drive digital servo, 300~400 Hz, how can i do that with the same concept, (after implementing the code inspired by your idea) ? I have no time left for 4 servo with max frequency 100 Hz.

i tried to do it simultaneously but it does not respond when i change the pulse width
i
Code:
nt8 state_counter;
volatile int32 interrupt_us[5] = {2500, 2500, 2500, 2500,  8333};
//RC servo motor state machine
#int_CCP1
void CCP1_isr()   //interrupt handler routine
{
   skip:
   state_counter ++;
   if(interrupt_us[state_counter] == interrupt_us[state_counter - 1]){goto skip;}
   //if(state_counter){set_timer1(0);}
   
   if(CCP_1 == interrupt_us[0])
   {
      output_low(servo_ar[0].pin);
   }
   
   if(CCP_1 == interrupt_us[1])
   {
      output_low(servo_ar[1].pin);
   }
   
   if(CCP_1 == interrupt_us[2])
   {
      output_low(servo_ar[2].pin);
   }
   
   if(CCP_1 == interrupt_us[3])
   {
      output_low(servo_ar[3].pin);
   }
   
   if(CCP_1 == interrupt_us[4])
   {
      set_timer1(0);
      output_high(servo_ar[0].pin);
      output_high(servo_ar[1].pin);
      output_high(servo_ar[2].pin);
      output_high(servo_ar[3].pin);
      state_counter = 0;
   }
   CCP_1 = interrupt_us[state_counter];
}


18F452 and 20Mhz crystal oscillator and timer1 is DIV by 2

Maybe i can use PLL to boost the clock cycle.
Thanks in advance,
z3ngew
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page Previous  1, 2
Page 2 of 2

 
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