Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

Concurrent threads on a pic?

Status
Not open for further replies.
Mike said:
hjames,

So you're shifting that '1' bit after each servo 'on-time' then delaying 20-msecs minus the cumulative on-times before starting over?

Yeah, that sounds about right - except there isn't really any need to calculate that last 20msec - (sum of all pulse times). Just have another timer running at 50Hz (i.e. every 20msec) that actually starts the whole sequence - less math, and no jitter there either.

Mike said:
What happens if Servo 03 on-time changes? Wouldn't that affect time between pulses for Servo 04 through 08 during one sequencial cycle?

Mike

The ISR should be doing relative scheduling - set the new deadline for the next pulse relative to the old one. The ISR should only be looking at the pulse width of the servo it's currently pulsing, there's no need to go through all the other pulse times.

James
 
hjames said:
The fun part is that this is probably doable on one of the 5 or 8 pin PICs / AVR's. How's that for a weekend project?

James
I was thinking of using a tiny 12F683 and an old monster 2764 EPROM (beauty and the beast, grin) but I don't even have a Servo here on the Lab bench to play with so this has really more of a mental exercise than anything else.
mikes-crazy-hi-rez-servo-12f683-jpg.8777


Your solution and perspective is appreciated. Thank you Sir.

Mike
 

Attachments

  • Mike's Crazy Hi-Rez Servo 12F683.JPG
    Mike's Crazy Hi-Rez Servo 12F683.JPG
    52.9 KB · Views: 2,521
Last edited:
No prob. I've gotten an inkling to build the *smallest* 16 port servo driver just for bragging rights. Maybe 1/2" on a side. Unfortunately the pin headers for the servo's would bulk it up by just a bit...

James
 
hjames said:
The ISR should be doing relative scheduling - set the new deadline for the next pulse relative to the old one. The ISR should only be looking at the pulse width of the servo it's currently pulsing, there's no need to go through all the other pulse times.

James
I mean the time between the start of Servo 04 on-time from one cycle to the next if Servo 03 on-time changes? If Servo 03 on-time changes from 1.0-msecs to 2.0-msecs it pushes the overall period for Servo 04 from 20-msecs to 21-msecs from the previous cycle.

cycle-times-jpg.8774
 

Attachments

  • Cycle Times.JPG
    Cycle Times.JPG
    20.7 KB · Views: 674
Okay, I see what you're getting at.

If we look at the last servo in a set and and we say that all the servo's are sitting at 1msec pulse widths, and on the next frame we suddenly increas all the pulse widths to 2msec, the last (say 8th one) will end up having a 20 + 7 msec gap between pulses. If we then decrease all the pulse widths back to 1msec, then the 8th servo will end up having a 20-7msec gap between pulses.

So yeah, there will be a pulse gap variation - but as long as the servo is happy with a worst-case 27msec update period, everything should still be okay. Plus this is definitely worst-case since suddenly railing servo's isn't exactly a useful thing to be doing a first place.
 
BTW Mike, you're posting your graphics as large, low quality, JPG's, if you use GIF or PNG they will be much smaller and perfect quality. You should only use JPG for photo's.
 
hjames said:
Okay, I see what you're getting at.

If we look at the last servo in a set and and we say that all the servo's are sitting at 1msec pulse widths, and on the next frame we suddenly increas all the pulse widths to 2msec, the last (say 8th one) will end up having a 20 + 7 msec gap between pulses. If we then decrease all the pulse widths back to 1msec, then the 8th servo will end up having a 20-7msec gap between pulses.

So yeah, there will be a pulse gap variation - but as long as the servo is happy with a worst-case 27msec update period, everything should still be okay. Plus this is definitely worst-case since suddenly railing servo's isn't exactly a useful thing to be doing a first place.
So it seems that 20-msec overall period isn't nearly as important or critical as the pulse width. Cool. Thanks for explaining.

Mike
 
I am very new to RC stuff as well, but I noticed on my sons nitro car servos that there is a throttle trim, this is added to the 1-2ms pulse :(

How are the registers than control the servos manipulated? This will need to be taken into consideration when calculating the timings if a non-interupt method of control is implimented.

As I understand it a servo window is defined as a 20MS time period (space) with the position information (nominally 1-2MS) (mark) contained within this time period, is this correct?

I have an idea, but a bit more info on how you want to control the servos, i.e. rs232 etc and the correct timings would be good.
 
James,

Just got a chance to look at the 'HCT238 Data Sheet and you're right. It's a perfect match for modulating one of the eight output lines with a PWM signal driving E1.

Thank you for the tip. My Crazy-8 PWM Servo controller concept just got a whole lot smaller by dumping that clumsy EPROM in favor of an 'HCT238. Cool!

Regards, Mike

~~~~~~~~~~~~~

Nigel,

I apologize for wasting server space. I just figured out why my PNG graphics were larger than JPG graphics. I was saving them from JPG to PNG. When I load the original graphics and save them immediately as PNG they're half the size of the JPG files.
crazy-8-servo-controller-v1-png.8795

I suspect a 12F683 Servo controller with a bit-banged 1200 or 2400 baud serial interface might be somewhat limiting. An 'F628A or an 'F88 with built-in serial port is probably a better choice with the option of implementing the crystal oscillator if needed for stability.

Here's the ISR "engine" with comments to help you understand the concept. So, would this work guys? Is it worth the extra chip?

Code:
        org     h'0004'

ISR_Vector
;
;  save main program context
;
        movwf   W_ISR           ; save W-reg                      |B?
        swapf   STATUS,W        ; doesn't change STATUS bits      |B?
        movwf   S_ISR           ; save STATUS reg                 |B?
        clrf    STATUS          ; force bank 0                    |B0
        movf    FSR,W           ;                                 |B0
        movwf   F_ISR           ; save FSR                        |B0
        bcf     PIR1,TMR2IF     ; clear TMR2 interrupt flag       |B0
;
;  setup 74HCT238 address lines to select this periods active 
;  Servo (duty cycle was set in previous PWM interrupt cycle).
;
;  a minimum 96-usec window at the beginning of each interrupt
;  where PWM is high and the 74HCT238 outputs are low provides 
;  more than enough time to setup the address lines before the
;  PWM output goes active low.
;
        movf    PORTA,W         ; read PORTA                      |B0
        andlw   b'11111000'     ; preserve PORTA b7..b3 bits      |B0
        iorwf   Servo,W         ; Servo b2..b0 bits are used as   |B0
        movwf   PORTA           ; 74HCT238 a2..a0 address bits    |B0
;
;  increment Servo [0..7] for next PWM cycle
;
        incf    Servo,f         ; increment servo                 |B0
        bcf     Servo,3         ; mod(Servo/8), [0..7]            |B0
;
;  we use the PWM off-time to drive the 74HCT238 E1 gate so
;  we invert the PWM duty cycle by subtracting the servo duty
;  cycle value of 50..300 (8-usec ticks) from the PWM period
;  value of 312 (2496-usec PWM period / 8-usec).
;
;  calculate inverse duty cycle for next PWM cycle
;
        movf    Servo,W         ; servo number [0..7]             |B0
        addlw   SArray          ; add to array address            |B0
        movwf   FSR             ; setup indirect address          |B0
        clrf    DutyHi          ;                                 |B0
        movf    INDF,W          ; the Servo PWM value [000..250]  |B0
        addlw   d'50'           ; add 0.4-msec offset [050..300]  |B0
        skpnc                   ; carry? no, skip, else           |B0
        incf    DutyHi,f        ; increment duty cycle hi         |B0
        sublw   low  d'312'     ; subtract from PWM period        |B0
        movwf   DutyLo          ; save inverse duty cycle lo      |B0
        movf    DutyHi,W        ;                                 |B0
        skpc                    ; borrow?                         |B0
        incf    DutyHi,W        ;                                 |B0
        sublw   high d'312'     ;                                 |B0
        movwf   DutyHi          ; save inverse duty cycle hi      |B0
;
;  inverse duty cycle result is a value between 12 (PWM hi for
;  96-usecs and active low for 2400-usecs) and 262 (PWM hi for
;  2096-usecs and active low for 400-usecs).
;
;  setup duty cycle registers for next PWM cycle
;
        rrf     DutyHi,f        ; shift result                    |B0
        rrf     DutyLo,W        ;                                 |B0
        movwf   CCPR1L          ; set 006..131 (16-usec ticks)    |B0
        bcf     CCP1CON,CCP1X   ;                                 |B0
        skpnc                   ; odd 8-us tick? no, skip, else   |B0
        bsf     CCP1CON,CCP1X   ; set the odd 8-usec tick         |B0
;
;  restore main program context
;
        movf    F_ISR,W         ;                                 |B0
        movwf   FSR             ; restore FSR                     |B0
        swapf   S_ISR,W         ;                                 |B0
        movwf   STATUS          ; restore STATUS                  |B?
        swapf   W_ISR,f         ; don't screw up STATUS           |B?
        swapf   W_ISR,W         ; restore W-reg                   |B?
        retfie                  ; return from interrupt           |B?
 

Attachments

  • Crazy-8 Servo Controller v1.PNG
    Crazy-8 Servo Controller v1.PNG
    35.9 KB · Views: 869
Last edited:
Mike said:
Nigel,

I apologize for wasting server space. I just figured out why my PNG graphics were larger than JPG graphics. I was saving them from JPG to PNG. When I load the original graphics and save them immediately as PNG they're half the size of the JPG files.

You can't take an omelette and recover the eggs from it :p same as a graphic, once you've ruined it, it's gone for ever (unless you do a LOT of editting).
 
I am using a PIC18F252 to control 12 servos(for a 4 legged bot). I am using MCC-18 and thought that I would just share my method -

Code:
#include <p18f252.h>
#include <timers.h>
#include <delays.h>

unsigned int servo[12];
char count=0, count1=0;

void interruptVectorHigh (void);

void main(void)
{
	char i;

	TRISB = 0x00;
	TRISA = 0x00;
	TRISC = 0x00;
	
	PORTB = 0x00;
	PORTA = 0x00;
	PORTC = 0x00;
	
	RCON = 0b000000000; 
	INTCON = 0b10100000;

	OpenTimer0( TIMER_INT_ON & T0_16BIT & T0_SOURCE_INT & T0_PS_1_2 );
	OpenTimer1( TIMER_INT_ON & T1_16BIT_RW & T1_SOURCE_INT & T1_PS_1_2 & T1_OSC1EN_OFF & T1_SYNC_EXT_OFF );
	OpenTimer3( TIMER_INT_ON & T3_16BIT_RW & T3_SOURCE_INT & T3_PS_1_2 & T3_OSC1EN_OFF & T3_SYNC_EXT_OFF );

	for(i=0;i<12;i++)
		servo[i]=59400;
	
	WriteTimer0(15535);		//Trigger Interrupted after 20mS
	WriteTimer1(65534);		//This is just a small initial delay chosen at random.
	WriteTimer3(65534);

	while(1)
	{
	// centre - 59400
	// left(anticlockwise)   - 62770
	// right(clockwise)  - 56030



	//add any code to move the servos here by assigning a value between 56030 and 62770 to servo[0] to servo[11]. the code enables automatic holding



	}
}


#pragma code interruptVectorHigh = 0x08
void interruptVectorHigh (void)
{
	_asm
	goto interruptHandlerHigh
	_endasm
}

#pragma code
#pragma interrupt interruptHandlerHigh

void interruptHandlerHigh()
{
	if(INTCONbits.TMR0IF)
	{
		WriteTimer0(15535);
		WriteTimer1(65534);
		WriteTimer3(65534);
	
		count = 0;
		count1 = 0;
		INTCONbits.TMR0IF = 0;
	}
	if(PIR1bits.TMR1IF == 1 && PIE1bits.TMR1IE == 1)
	{
		switch(count)
		{		
			case 0:	PORTBbits.RB0=1;
					WriteTimer1(servo[0]);	
					break;
			case 1:	PORTBbits.RB0=0;
					PORTBbits.RB1=1;
					WriteTimer1(servo[1]);
					break;
			case 2:	PORTBbits.RB1=0;
					PORTBbits.RB2=1;
					WriteTimer1(servo[2]);
					break;
			case 3:	PORTBbits.RB2=0;
					PORTBbits.RB3=1;
					WriteTimer1(servo[3]);
					break;
			case 4:	PORTBbits.RB3=0;
					PORTBbits.RB4=1;
					WriteTimer1(servo[4]);
					break;
			case 5:	PORTBbits.RB4=0;
					PORTBbits.RB5=1;
					WriteTimer1(servo[5]);
					break;
			case 6:	PORTBbits.RB5=0;
					break;
		}
		count++;	
		PIR1bits.TMR1IF = 0;
		PIE1bits.TMR1IE = 1;
	}
	if(PIR2bits.TMR3IF == 1 && PIE2bits.TMR3IE == 1)    
	{
		switch(count1)
		{		
			case 0:	PORTAbits.RA5=1;
					WriteTimer3(servo[6]);
					break;
			case 1:	PORTAbits.RA5=0;
					PORTAbits.RA4=1;
					WriteTimer3(servo[7]);
					break;
			case 2:	PORTAbits.RA4=0;
					PORTAbits.RA3=1;
					WriteTimer3(servo[8]);
					break;
			case 3:	PORTAbits.RA3=0;
					PORTAbits.RA2=1;
					WriteTimer3(servo[9]);
					break;
			case 4:PORTAbits.RA2=0;
					PORTAbits.RA1=1;
					WriteTimer3(servo[10]);
					break;
			case 5:PORTAbits.RA1=0;
					PORTAbits.RA0=1;
					WriteTimer3(servo[11]);
					break;
			case 6:PORTAbits.RA0=0;
					break;
		}
		count1++;	
		PIR2bits.TMR3IF = 0;
		PIE2bits.TMR3IE = 1;
	}
	INTCONbits.GIE = 1;
}
 
Status
Not open for further replies.

New Articles From Microcontroller Tips

Back
Top