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

SOLVED:timer interrupt pwm resolution
Goto page 1, 2  Next
 
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

SOLVED:timer interrupt pwm resolution
PostPosted: Wed Aug 07, 2013 7:41 pm     Reply with quote

Hello everyone,
I am designing a code to control 4 servo motor using timer interrupt pwm,
but there is a problem, in the main routine there is a communication protocol designed (handle_read()) this function is to be called in an endless loop.
But in order to increase the resolution of the pwm, frequency of counter is increased, but when the timer frequency is very high, the handle_read routine does not get enough time to execute.

So is there another solution without decreasing the timer frequency ?
Here is the code :
Maybe a kernel to organize the program counter.
Code:

#include <18F452.h>
#device PASS_STRINGS = IN_RAM

#FUSES HS                       //High speed Osc (> 4mhz)
#FUSES NOWDT                    //No Watch Dog Timer
#FUSES NOPROTECT                //disable memory protection (TEMPORARY)
#FUSES NOLVP                    //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O

#use delay(clock=20Mhz)
#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8)


Code:

#include "main.h"
#include "flex_lcd_4x16.c"
#include "spio.h"
#include "stepmotor.h"

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

long interrupt_counter= 0;
int16 duty_count[4] = {80, 50, 50, 50};
int16 count_upper_bound = 2000;  //resolution


#int_TIMER1
void TIMER1_isr(void)   //interrupt handler routine
{
   interrupt_counter++;
   
   if(interrupt_counter == duty_count[0]) //duty cycle of channel 1 acheived
      pwm_1 = 0;
   if(interrupt_counter == duty_count[1]) //duty cycle of channel 2 acheived
      pwm_2 = 0;
   if(interrupt_counter == duty_count[2]) //duty cycle of channel 3 acheived
      pwm_3 = 0;
   if(interrupt_counter == duty_count[3]) //duty cycle of channel 4 acheived
      pwm_4 = 0;
   if(interrupt_counter == count_upper_bound)//end of pulse period acheived
   {
      output_b(MCU_PORTB + 15);  //15 is [1111] in binary
      interrupt_counter = 0;
   }
   set_timer1(65000);
}

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


Thanks in advance,
z3ngew


Last edited by z3ngew on Sat Aug 10, 2013 12:15 pm; edited 1 time in total
temtronic



Joined: 01 Jul 2010
Posts: 9186
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Thu Aug 08, 2013 5:17 am     Reply with quote

ideas
1) when using the #use rs232(...) ALWAYS add 'errors' to the options.
2) use an ISR and circular buffer when doing serial communications.

Though you don't say what or how handle_read() is/works, if we assume it's using the UART for 'communications', you should do both 1 and 2.

also
without knowing what 'servos' you're using, I can't help with the 'update' rate.'servos' like RC style are slow(20ms) and 'forgiving',easy to control wheras 'industrial' servos can be very fast and need 'tight' control.

hth
jay
z3ngew



Joined: 20 Apr 2013
Posts: 50

View user's profile Send private message

PostPosted: Thu Aug 08, 2013 6:03 am     Reply with quote

thanks for the feedback jay,
handle_read(); uses UART, but about step(2) will you explain it little more, what i have understood is that i should put (handle_read) in another event handler maybe of timer 2 but didn't get circular buffer part.

I am using simple pwm rc_servo motor 50hz

Thanks for your effort,
z3ngew
temtronic



Joined: 01 Jul 2010
Posts: 9186
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Thu Aug 08, 2013 6:31 am     Reply with quote

Have a look at the example 'ex_sisr.c' that CCS supplies in the examples folder.

The UART has it's own ISR, you do not need timer2.

In a 'nutshell', whenever a character comes in the UART, it is sent into a circular buffer.You 'main' program then decides what to do with the data in the buffer. Use the 'search' feature here...lots of threads about using it, buffer size(binary seems to be best,maybe...).If you expect data to be say 12 bytes in length, then a 16 byte buffer may be 'best'.

RC servos are slow( 50Hz) so any PIC can easily handle 4-8 of them as well as a UART depending on what else 'main' has to do.

Using ISRs will free up a lot of PIC time.

hth
jay
z3ngew



Joined: 20 Apr 2013
Posts: 50

View user's profile Send private message

PostPosted: Fri Aug 09, 2013 6:34 am     Reply with quote

Hello again,
i want to get the maximum resolution of the rc _servo motor, 2000 step,
I used the following function:
crystal = 20Mhz, and no prescaler(divided by 1)
Code:
set_timer1(65531);


The timer output frequency should be = 65536-65531 * 0.2us = 1us
now the timer interrupt frequency should be = 1us.

now if make
Code:
int16 count_upper_bound = 20000;  //resolution


I should make the duty cycle ranges from 1000 to 2000 but, it is not working this way, i get much smaller frequency with the same duty cycle
and about the serial interrupt,
the handle_read() function as i said before is communication protocol between this software and another one designed in c#,
in general Handle_read() use kbhit function to check for incoming string and when the processor is not busy of doing something it sends a flag to say i am ready for next command.

1- how to get high resolution (robotic_arm) for interrupt timer pwm (2000 step)
2- how to make handle_read() work while the pwm is running too, like in the background

Thanks in advance,
z3new
Ttelmah



Joined: 11 Mar 2010
Posts: 19396

View user's profile Send private message

PostPosted: Fri Aug 09, 2013 7:47 am     Reply with quote

At 20MHz, the chip executes just 5 million instructions per second.
Each single line of C code, can be anything from 1 to many tens of thousands of machine instructions. A single function like 'sin' for example, can easily take 5000 instructions to execute. Even just the act of reading a character from an array, will typically take perhaps a dozen instructions. Just 5 instructions per uSec.
It takes typically about 60 instructions just to get into, and out of an ISR. Doing nothing else.
The 'set_timer' instruction, will also take a handful of instructions.
So start. 1uSec passes. Interrupt triggers. Perhaps 6.5uSec later the chip actually arrives in the ISR. Then it takes a couple of uSec to load the timer counter. Then you exit the ISR, another 6uSec later you get back to the main code, execute one instruction, the interrupt has already triggered perhaps 6 times, so back to the ISR. The ISR service rate will be perhaps 12 to 15uSec, while basically nothing else will occur.
You just cannot do things at this sort of speed.
Even on GHz processor like the pC, it takes several uSec to get into and out of an ISR.....

This is why we keep repeating, for accurate timings, you _need_ (not optional...), to be using the hardware. This is what it is _for_.

It is like the guy complaining that he can't get more than 1mph out of his car - it turns out he is pushing it....

Best Wishes
Mike Walne



Joined: 19 Feb 2004
Posts: 1785
Location: Boston Spa UK

View user's profile Send private message

PostPosted: Fri Aug 09, 2013 8:13 am     Reply with quote

It's back to the drawing board time.

Ttelmah's explained why your current approach can't work, as it stands.

Make a list of what you have to have, with constraints.
You may need to ask, and answer, some awkward questions!

Mike
z3ngew



Joined: 20 Apr 2013
Posts: 50

View user's profile Send private message

PostPosted: Fri Aug 09, 2013 8:31 am     Reply with quote

I have a robotic arm 5 DOF with 4 servo motors and 1 stepper 1 dc motor 1FSR sensor.

- the robot arm and the control circuits have already been designed and performed, so i cannot use other pins(hardware pwm) unless designed another pcb's,

- the servo motors pins (b0, b1, b2, b3)

- the pic must stay connected with a pc through serial port, for the user give order to the robotic arm,

- clock speed 20Mhz,

- the servo motor should be controlled with high resolution as possible (for inverse kinematics algorithm)

- i can change the crystal to a higher one.

Thanks in advance,
z3ngew
Ttelmah



Joined: 11 Mar 2010
Posts: 19396

View user's profile Send private message

PostPosted: Fri Aug 09, 2013 8:57 am     Reply with quote

A typical servo, has _at best_ 1% resolution. If you are lucky....
Now the servo pulse is (basically) between 1mSec, and 2mSec long, for the range of this output.
So, potentially, if you used a 10uSec interrupt, and went to the fastest clock rate your chip supports (the 452 supports 40Mhz, while the 45K22, will fit in the same socket, and goes to 64MHz, and can be programmed to be basically 'pin compatible'.
Then write careful code for the ISR, and you have a chance....
You'd still be spending perhaps 50% of your time in the ISR, but it becomes possible.
Don't do the ISR by setting the timer 'to' a value. Instead use timer2, and program this to give the required interrupt rate (less instructions in the ISR).

Best Wishes
temtronic



Joined: 01 Jul 2010
Posts: 9186
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Fri Aug 09, 2013 9:04 am     Reply with quote

Please post the servo mfr/model info.
I'm guessing it doesn't have any 'feedback' available to the controller so you're just sending a pulse and hoping it goes to the position you've requested.
Also the specs of the servo will say how 'fine' or 'resolution' the servo is capable of. No sense designing for 1/10th us if it's only good to 20us !
temtronic



Joined: 01 Jul 2010
Posts: 9186
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Fri Aug 09, 2013 9:16 am     Reply with quote

some numbers to consider...

typical RC servos...1-2ms

range is 1ms=1000us ( 2-1)

rotation=180*(std RC servo)

1000 us/180 *=5.5us/*

so every degree is 5.5us

It's unlikely any RC servo is good to 1* resolution( at least not 'hobby ones)

As Mr. T says, you could drop in a 46k22, crank up the cycles BUT I think you're really wasting valuable time(yours) in trying to obtain the unobtainable.

Even IF you get it 'close', the code will be for a static arm(one without a load).Those numbers will cjange dramatically when you have the arm grab something then try to move it !

hth
jay
z3ngew



Joined: 20 Apr 2013
Posts: 50

View user's profile Send private message

PostPosted: Fri Aug 09, 2013 9:51 am     Reply with quote

i found a solution, it is dummy in a way, but it should do it.

if i do the following the code in an infinite loop
Code:
      output_high(PIN_B0);
      delay_us(a);
      output_low(PIN_B0);
      delay_us(20000 - a);
     
      output_high(PIN_B1);
      delay_us(b);
      output_low(PIN_B1);
      delay_us(20000 - b);
     
      output_high(PIN_B2);
      delay_us(c);
      output_low(PIN_B2);
      delay_us(20000 - c);
     
      output_high(PIN_B3);
      delay_us(d);
      output_low(PIN_B3);
      delay_us(20000 - d);


the 4 servo motor is working great despite of the predicted timer delay,
This routine can give all the resolution i have ever dreamed of.

now let's say i have two modes:
mode1: in_action mode
mode2: parking mode

in_action mode is the mode where the robot arm is in action,
parking mode: mode where the robot arm is stand_by

i will use this routine for in action mode,
and the timer interrupt pwm for the parking mode

any suggestions please, is this method poor or good,

Thanks for your efforts
z3ngew
temtronic



Joined: 01 Jul 2010
Posts: 9186
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Fri Aug 09, 2013 10:43 am     Reply with quote

Use of delay_xx(nnn) is very,very poor programming for controlling RC servos( or other control programs)

You must understand that while the PIC is doing EVERY delay_xx(nnn) that it cannot do ANYTHING else!Worst case for you is 4 servos x 2ms , so for a bit over 8ms the PIC cannot do anything( like receive new commands from the PC via UART).8ms is an 'eternity'..

That's why interrupts were created.They allow the PIC to do several things 'at one time'.

You're not the first one using a PIC to control several RC servos. There is tons of code example on the Web...all good ones use interrupts/timers/etc.


hth
jay
Mike Walne



Joined: 19 Feb 2004
Posts: 1785
Location: Boston Spa UK

View user's profile Send private message

PostPosted: Fri Aug 09, 2013 2:12 pm     Reply with quote

Quote:
i found a solution, it is dummy in a way, but it should do it.

The penny hasn't dropped yet, has it?

Take note of what both Mr. T's are telling you.

Mike
RLScott



Joined: 10 Jul 2007
Posts: 465

View user's profile Send private message

Re: timer interrupt pwm resolution
PostPosted: Fri Aug 09, 2013 8:54 pm     Reply with quote

I noticed that you are generating all four servo pulses simultaneously. This is unnecessary and it stands in the way of a more elegant solution. You have 20 msec. between servo pulses. Each servo pulse is at most 2 msec. long. Therefore four servo pulses can easily be generated in series in only 8 msec, which gives you plenty of time to generate them serially instead of simultaneously. In actual RC practice multiple servo signals are multiplexed and sent over a single channel. Then the receiver demultiplexes them and sends them to individual servos. I know you don't have a demultiplexor in your system, but that is OK. You can still share the CPU in a serial fashion.

Write an timer interrupt service routine that manages a state machine. This state machine generates pulses for one servo at a time. It does so by loading the timer with the proper value so that the next interrupt will be when you want it. That length of time depends on where you are in the state machine sequence. 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. and load the timer to overflow that much later. So you can do it with 5 states in the state machine.

For this method to work you need to ensure dependable constant latency in timer interrupts. You can do this by structuring the rest of your program to NOT use any interrupts and never disable interrupts. This may make it difficult (but not impossible) to handle your UART, or whatever else you do. But if you want reliable timing in the servo pulses, that is what you must do (since you don't want to use hardware).
_________________
Robert Scott
Real-Time Specialties
Embedded Systems Consulting
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 1, 2  Next
Page 1 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