Electronic Projects, forums and more.

Go Back   Electronic Circuits Projects Diagrams Free > Electronics Categories > Micro Controllers


Micro Controllers Discuss all aspects of micro controllers - building them, coding them, etc. All controllers are welcome - PIC, BASIC, Z8 Encore!, etc.

Reply
 
Thread Tools Display Modes
Old 19th July 2006, 07:38 PM   (permalink)
Default

Speaking of interrupts, rock-solid PWM output, crazy ideas, etc., what do you guys think of this scheme?

Setup a nice hi-rez 1-msec PWM signal on your favorite PIC and feed it into the A0 address line of a 16-byte EPROM. Connect the other three EPROM address lines to the PIC. The EPROM is programmed so that the PWM signal driving A0 will toggle the corresponding data output line (servo line).

Invert the PWM output (this option available on some newer chips like the 12F683, see Errata) and setup TMR2 interrupts at the PWM 1-msec period. Your ISR will increment a counter [0..19], drive the EPROM A3:A1 lines with that value, then load the PWM duty cycle register with a value from a 20-byte Servo Duty Cycle array indexed by the 0..19 counter (array values 08 through 19 will contain 00h).

I suspect a little 12F683 could easily provide RS232 communications and drive up to 8 servos using 250 4-usec steps, 125 steps each side of center, using this scheme. Yes, no, maybe, huh?

I suppose it really comes down to finding a small inexpensive 99 cent EPROM of some sort, doesn't it?

Regards, Mike

<added>

Oops. I did it again. I forgot about the minimum 1-msec on-time for each servo. Gads! Perhaps if I left the Servo 01 bit (d0) set for both parts of the Servo 1 addresses to pick up the extra 1-msec? I'll have to brainstorm this more guys. Sorry
Attached Images
File Type: jpg Mike's Crazy Hi-Rez Servo Scheme.JPG (57.5 KB, 11 views)

Last edited by Mike, K8LH; 19th July 2006 at 07:48 PM.
Mike, K8LH is online now   Reply With Quote
Old 19th July 2006, 09:56 PM   (permalink)
Default

I did a few tests last night and I've noticed that the period really does effect the response times and torque greatly. I set it up for 20ms and 36ms and just as you might expect the response time was almost double along with about 1/2 as much holding power. In my opinion a 20ms period or less should always be used.
HakBot is offline   Reply With Quote
Old 19th July 2006, 10:12 PM   (permalink)
Default

Mike, you've just redesigned the '138 3-to-8 address selector with enable lines...
hjames is offline   Reply With Quote
Old 19th July 2006, 10:47 PM   (permalink)
Default

So since I don't do PIC's full time, here's the AVR approach:

The ATMEGA8 has a couple timers - This design would use TMR1, and it's two compare registers - OCR1A, and OCR1B. TMR1 is a 16 bit timer, so if the chip is running at 8MHz, TMR1 would overflow in ~8msec, which is longer than the 2msec pules we need to generate, so we won't need to use a prescaler. OCR1A, and OCR1B are configured to generate a pulse on compare matches, as well as trigger an interrupt.

The general idea is that the '164 is a 8 bit shift register. When the timing is operational, we're going to have 1 bit which ends up activating 1 servo at a time. Since this is a hardware timer with 0 clock/interrupt jitter, we don't need to play games scheduling interrupts.

C-style psuedo code

/*
U1Delays, U2Delays hold the pulse widths in CPU clocks - 1ms ->
8000, 2ms -> 16000.

*/
unsigned short U1Delays[8];
unsigned short U2Delays[8];
unsigned char bitCountU1, bitCountU2;

startPulseTrain(){
PB0 &= ~0x01; //set PB0 to '0';
clockShiftRegisters(8); //clear both shift registers

PB0 |= 0x01; //set PB0 to '1'
clockShiftRegisters(1); //push a single bit into the shift registers

PB0 &= ~0x01; //set PB0 to '0';

//Indicate which bit each shift register is currently at
bitCountU1=bitCountU2=1;

//get the current value of Timer1
unsigned short tmp = TCTR1;

//schedule when the compare registers trip
OCR1A = U1Delays[0] + tmp;
OCR1B = U2Delays[0] + tmp;
enableOCR1AInterrupt();
enableOCR1BInterrupt();
}

//OCR1A will automatically pulse the clock line, this ISR routine is here
//in order to schedule the following clock pulse
OCR1A_interrupt(){
if(bitCountU1==8) disableOCR1AInterrupt();
OCR1A += U1Delays[bitCountU1++]; // schedule the next interrupt
}

// ditto for the OCR1B
Attached Images
File Type: png bleh.png (12.2 KB, 4 views)
hjames is offline   Reply With Quote
Old 19th July 2006, 10:55 PM   (permalink)
Default

Quote:
Originally Posted by hjames
Mike, you've just redesigned the '138 3-to-8 address selector with enable lines...
Good observation but not exactly.

The '138 gives you a 1-of-8 output. The EPROM provides 8 outputs with only one of the 8 being modulated by the PWM signal.

Mike
Mike, K8LH is online now   Reply With Quote
Old 19th July 2006, 11:37 PM   (permalink)
Default

Quote:
Originally Posted by Mike, K8LH
Good observation but not exactly.

The '138 gives you a 1-of-8 output. The EPROM provides 8 outputs with only one of the 8 being modulated by the PWM signal.

Mike
Plug the PWM signal into the enable line.

I should've said '238 since the '138 apparently has idle-high outputs. I don't think I've ever actually used either of these chips in any of my designs, I usually end up using a '164 or '595 shift register for everything.
hjames is offline   Reply With Quote
Old 20th July 2006, 12:28 AM   (permalink)
Default

Ok, I think I figured out a workable version of Mike's Crazy EPROM 8 Servo Controller (partial schematic pictured in a previous post).

Using a 4-MHz clock, TMR2/PWM is set up for a 2.49-msec period (inverted PWM output). Each of the eight Servos has a programmable 0.4-msec to 2.4-msec "on-time" in 250 8-usec steps and an overall period of 19.9-msecs. 500 4-usec steps is possible with minor modification.

The ISR (below) handles everything in the background. You simply stuff a value between 000 and 250 for each Servo in the 8-byte ServoPWM array (SArray) in the Main program (I think my math code may be wrong, needs work, but you should get the idea what can done with 40 or so lines of ISR code).

Mike

Code:
;******************************************************************
;
;  clock 4-MHz, TMR2 prescaler 16, PR2=156, 2496-usec PWM period
;
EADR    equ     h'60'           ; EPROM address / 2 [0..7]
DutyLo  equ     h'61'
DutyHi  equ     h'62'
SArray  equ     h'68'           ; 8-bytes [68h..6Fh]
;
;  save main program context
;
ISRPROC 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
        bcf     PIR1,TMR2IF     ; clear TMR2 interrupt flag       |B0
;
;  EEPROM address counter / servo counter maintenance
;
        incf    EADR,f          ; increment counter               |B0
        bcf     EADR,3          ; force 0..7                      |B0
;
;  set eeprom address lines on RB0:RB2 during minimum 96-usec
;  window at beginning of interrupt where PWM output is low
;
        movf    PORTB,W         ;                                 |B0
        andlw   b'11111000'     ;                                 |B0
        iorwf   EADR,W          ;                                 |B0
        movwf   PORTB           ; set EPROM address lines         |B0
;
;  set Servo PWM output. duty cycle set in 8-usec steps
;
        movf    EADR,W          ; [0..7]                          |B0
        addlw   SArray          ; add index to array address      |B0
        movwf   FSR             ; setup indirect address          |B0
        clrf    DutyHi          ;                                 |B0
        movf    INDF,W          ; user ServoPWM value [000..250]  |B0
        addlw   d'50'           ; add minimum 0.4-msec on-time    |B0
        skpnc                   ;                                 |B0
        incf    DutyHi,f        ;                                 |B0
        sublw   low  d'312'     ; 2496-usec period /8-usecs       |B0
        movwf   DutyLo          ;                                 |B0
        movf    DutyHi,W        ;                                 |B0
        skpc                    ; borrow?                         |B0
        incf    DutyHi,W        ;                                 |B0
        sublw   high d'312'     ;                                 |B0
        movwf   DutyHi          ;                                 |B0
        rrf     DutyHi,f        ;                                 |B0
        rrf     DutyLo,W        ;                                 |B0
        movwf   CCPR1L          ; set 000..150 (16-usec ticks)    |B0
        bcf     CCP1CON,DC1B1   ;                                 |B0
        skpnc                   ;                                 |B0
        bsf     CCP1CON,DC1B1   ;                                 |B0
;
;  restore main program context
;
ISR_X   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?

Last edited by Mike, K8LH; 20th July 2006 at 12:52 PM.
Mike, K8LH is online now   Reply With Quote
Old 20th July 2006, 01:16 AM   (permalink)
Default

hjames,

Looking at the 74HCT164 Data Sheet, I'm having trouble figuring out how you clear a bit without performing a shift operation. Actually, I don't understand how you can use the shift register at all.

Also not familiar with AVR (yet) or the C language.

Mike

Last edited by Mike, K8LH; 20th July 2006 at 01:36 AM.
Mike, K8LH is online now   Reply With Quote
Old 20th July 2006, 01:55 AM   (permalink)
Default

The "clockShiftRegisters(#)" is supposed to mean create # clock pulses on PB1 and PB2. (okay, so it's really sloppy english-c code...)

Well, the idea is that it loops through all the servo's in order - skipping is okay, but no going back. The only important thing is that the shift register is initialized to all zero's except for the first one.

The main idea is that those 1-2msec pulses don't have to be aligned versus anything - this approach just takes them, squishes them all together in time, and runs through them in one pass.

The main benefits of the approach are:
1) only uses up 2 pins of the controller (per set of servos)
2) if there is a hardware timer, this thing will have *no* jitter and obscene resolution
3) (probably not too important) - it's easier to isolate or level-convert the outputs since you only have to level-translate or use a TTL threshold part like the HCT series.

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
hjames is offline   Reply With Quote
Old 20th July 2006, 02:27 AM   (permalink)
Default

James,

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

What happens if Servo 03 on-time changes? Wouldn't that affect time between pulses for Servo 04 through 08 during one cycle? For example, if Servo 03 on-time changes from 1.1-msecs to 2.0-msecs, wouldn't that effectively increase the period for Servo 04 through Servo 08 for one cycle from 20.0-msecs to 20.9-msecs? If so, is that important?

Mike

Last edited by Mike, K8LH; 20th July 2006 at 02:48 AM.
Mike, K8LH is online now   Reply With Quote
Old 20th July 2006, 02:46 AM   (permalink)
Default

Quote:
Originally Posted by Mike, K8LH
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.

Quote:
Originally Posted by Mike, K8LH


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 is offline   Reply With Quote
Old 20th July 2006, 03:03 AM   (permalink)
Default

Quote:
Originally Posted by hjames
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.


Your solution and perspective is appreciated. Thank you Sir.

Mike
Attached Images
File Type: jpg Mike's Crazy Hi-Rez Servo 12F683.JPG (52.9 KB, 29 views)

Last edited by Mike, K8LH; 20th July 2006 at 01:42 PM.
Mike, K8LH is online now   Reply With Quote
Old 20th July 2006, 03:16 AM   (permalink)
Default

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 is offline   Reply With Quote
Old 20th July 2006, 03:23 AM   (permalink)
Default

Quote:
Originally Posted by hjames
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.

Attached Images
File Type: jpg Cycle Times.JPG (20.7 KB, 30 views)
Mike, K8LH is online now   Reply With Quote
Old 20th July 2006, 06:47 AM   (permalink)
Default

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.
hjames is offline   Reply With Quote
Reply

Bookmarks

Thread Tools
Display Modes




All times are GMT. The time now is 04:18 PM.


Electronic Circuits  |  Electronics Wiki
Powered by vBulletin® Version 3.7.0
Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.