View previous topic :: View next topic |
Author |
Message |
mizi_suichiro
Joined: 23 Mar 2004 Posts: 7
|
Generating multiple PWM pulse on PIC16F877a |
Posted: Thu Jan 03, 2008 9:04 pm |
|
|
hi, i'm currently working on humanoid robot and now searching for the best microcontroller that can work with my algorithm.
i have experienced using PIC16F877a for the past projects. and as far as i'm concern, it just have 2 build in PWM generator inside it.
is it possible to create a multiple PWM generator for a port such as Port B (with 8 pins all together) that is indipendent for each other? (PinB0 duty cycle is different with PinB1 duty cycle, but may share the same period).
the design consist 6 servo motors that should run indipendently at the same time by each different PWMs that generate by different pins.
i have search throughout this forum and i didn't find anything related abt it, or maybe i did miss it. hope all u guys can give me any ideas and suggestions.
thanks. |
|
|
John P
Joined: 17 Sep 2003 Posts: 331
|
|
Posted: Fri Jan 04, 2008 6:48 am |
|
|
I've done what you're asking about, but maybe in a form that won't help you. It did use a PIC16F877A.
My method definitely wanted a very slow output waveform, at only 60Hz. We did it by setting up a 6KHz timer-based interrupt, which gave us 101 possible choices of duty cycle, including 0 and 100%. Each output with a non-zero duty cycle was switched on at time 0, then at each timer interrupt, the program basically said, "Do we want to turn off output 0? Output 1?..." and so forth for all 8 outputs. It's pretty obvious how this works, but it's also obvious that it has limitations if you want a frequency up in the kilohertz range. There's going to be a tradeoff between the operating frequency and the resolution of the duty cycle. For my design, the resolution was 1%. If you could live with 10% resolution, you could keep the 6KHz interrupt rate and push up the frequency to 600Hz, for example. You have to know what the design really needs. |
|
|
ELCouz
Joined: 18 Jul 2007 Posts: 427 Location: Montreal,Quebec
|
|
Posted: Fri Jan 04, 2008 8:22 am |
|
|
You may look at the PIC24 series or Dspic30 they have up to 8 pwm module on a single chip ... plus some dspic are built especially for motor control. (check dsPIC30F6015)
Laurent |
|
|
PICdawg
Joined: 13 Dec 2007 Posts: 17
|
|
Posted: Fri Jan 04, 2008 11:57 am |
|
|
You can certainly develop a multi-channel servo controller but I don't believe you'll be using the built in pwm function. I developed a 4 channel dedicated servo controller using the 16f876a. Basically you are creating the 1-2ms pulse widths one right after another and are keeping a 20ms timer going in the background to reset the process.
You should examine the Devantech SD21, a 21 channel servo controller based on a PIC that is communicated to via I2C. You send it a channel number, servo position, and speed(rate of change from present position to desired position) and it takes care of the rest. This is reasonably priced(about $60USD) and would give you lots of room for expansion. Then you can focus your efforts on the development of the rest of the robot program and leave the servo control to a dedicated micro. They have a version available as just the pre-programmed part w/o pcb even cheaper.
My servo controller behaves exactly as the SD21. My project involved two robots, one with the SD21, and the other with my home grown 4 channel. It could easily be expanded to add more channels. I'd be happy to share the code if you are interested. |
|
|
John P
Joined: 17 Sep 2003 Posts: 331
|
|
Posted: Fri Jan 04, 2008 3:42 pm |
|
|
PICdawg has introduced a problem of terminology, but maybe he's the one who's right. A "servo system" can consist of a motor that's controlled by a processor via an amplifier, with some kind of feedback element that lets the processor know what's happening, and in that case the output from the processor often is a PWM signal. Or there are "model aircraft servos" which are obviously what PICdawg is talking about, and they're controlled by a variable-length pulse, from 1 to 2 msec long, that recurs at 50Hz (If I'm remembering the details right) but the unit itself is self-contained and doesn't give feedback to the processor. It's not totally clear what the original question was about. I assumed that the PWM output was going to drive a motor and there'd be feedback as well, although he didn't mention it.
If it's the aircraft type servo, then I think it's not too hard to control 6 of them with plenty of resolution. The way I'd handle it would be to calculate each pulse width in turn, and that would be a total of 12msec max, with a bit extra for starting and ending each interrupt. If there's no need to do things simultaneously, it wouldn't be complicated at all. |
|
|
PICdawg
Joined: 13 Dec 2007 Posts: 17
|
|
Posted: Fri Jan 04, 2008 4:03 pm |
|
|
JohnP, excellent point! My solution is in fact a multi-channel hobby servo controller and is not doing the lower level contol of a servo motor itself. Sorry for any confusion I may have introduced and thanks for pointing out this important difference! Kelly |
|
|
gjs_rsdi
Joined: 06 Feb 2006 Posts: 468 Location: Bali
|
8 servo |
Posted: Sun Jan 06, 2008 3:25 pm |
|
|
tested it works
joseph
Code: | //pulses generation for the servoactuators, timer1 increment=1us
//servoXpulseout is the specific pulse length for the specific servo, minimum 1000us, maximum 2000us
//initialize the pulse length on startup as you robot needs
//the resolution 10 bit
//int pulses=0;
//long servo1pulseout=1000;
//long servo2pulseout=1000;
//long servo3pulseout=1000;
//long servo4pulseout=1000;
//long servo5pulseout=1000;
//long servo6pulseout=1000;
//long servo7pulseout=1000;
//long servo8pulseout=1000;
#int_TIMER1
void TIMER1_isr()
{
switch(pulses)
{
case 0:
{
set_timer1(65536-servo1pulseout);
output_high(pin_B7);
pulses++;
}
break;
case 1:
{
set_timer1(65536-servo2pulseout);
output_high(pin_B6);
output_low(pin_B7);
pulses++;
}
break;
case 2:
{
set_timer1(65536-servo3pulseout);
output_high(pin_B5);
output_low(pin_B6);
pulses++;
}
break;
case 3:
{
set_timer1(65536-servo4pulseout);
output_high(pin_B4);
output_low(pin_B5);
pulses++;
}
break;
case 4:
{
set_timer1(65536-servo5pulseout);
output_high(pin_B3);
output_low(pin_B4);
pulses++;
}
break;
case 5:
{
set_timer1(65536-servo6pulseout);
output_high(pin_B2);
output_low(pin_B3);
pulses++;
}
break;
case 6:
{
set_timer1(65536-servo7pulseout);
output_high(pin_B1);
output_low(pin_B2);
pulses++;
}
break;
case 7:
{
set_timer1(65536-servo8pulseout);
output_high(pin_B0);
output_low(pin_B1);
pulses++;
}
break;
case 8://to complement 20ms
{
output_low(pin_B0);
set_timer1(65536-(20000-(servo1pulseout+servo2pulseout+servo3pulseout+servo4pulseout+servo5pulseout+servo6pulseout+servo7pulseout+servo8pulseout)));
pulses=0;
}
break;
}
} |
|
|
|
John P
Joined: 17 Sep 2003 Posts: 331
|
|
Posted: Sun Jan 06, 2008 9:34 pm |
|
|
Not meaning any disrespect to gjs_rsdi, but there's a potential flaw there. The output pulses will begin exactly as required, but they'll end when the interrupt has already begun, after all the saving of registers occurs. There'll be some additional time involved which will make each pulse longer than expected, unless the time period that gets loaded already allows for the delay.
There's a potential way to deal with the problem if you grab the existing value of timer1 when each pulse begins, and use it to reduce the next time interval that gets set. It would represent the time elapsed since the interrupt occurred, and even if it's not perfectly consistent between the different outputs, it would improve the accuracy. Getting this exactly right actually seems quite tricky, at least to my brain. |
|
|
mizi_suichiro
Joined: 23 Mar 2004 Posts: 7
|
|
Posted: Sun Jan 06, 2008 9:49 pm |
|
|
thanks to all of u guys for given me an ideas. i will look into ur suggestion deeply.
for design requirement as ask by JohnP, i think i need 1% resolution since i will play with the servo position, drive by the servos for the robot movement. i think it is not suitable to have PWM with 10% resolution to drive them. position angle resolution is depends by the servo motor itself.
i'm using the PWM to drive all the servo motors attach to the biped humanoid. as refered to human movement, the motors are required to move simultaniously together (example- left leg move forward). all of the motors have their own degree of movement driven by the PWM that are individually driven. and yes, there are feedback that is the rotation angle. as far as i noticed, the position feedback (angle) is being control by the electronics inside the servo itself.
PICdawg, i will look into Devantech SD21 for more details. i'm still in process to look for the best electronic design for my work. if the combination of the PIC and Devantech is the best, i will choose it. then i will put the problems regarding to generate multiple PWM pulse aside and just concerntrate to generate the humanoid algorithm to drive the servos. i would be very happy and proud if i can share ur code.
laurent, u give me new idea. i never know abt dsPIC before. maybe this is the time for me to upgrade to more advance processor.
joseph, thanks for the code. will try it asap.
thanks to all of u. |
|
|
gjs_rsdi
Joined: 06 Feb 2006 Posts: 468 Location: Bali
|
pulse error |
Posted: Mon Jan 07, 2008 12:22 am |
|
|
If you will design the software correct, as should be in a real time system, your error will be constant 11us (in my case).
with 90 degree total servo traveling, 1us is 0.09 degree.
you can compensate if you want.
Joseph |
|
|
Dimlow_NOT lOGGED_IN Guest
|
|
Posted: Mon Jan 07, 2008 2:38 am |
|
|
I used this code in a fan controller for 8 PWM fans. it cut and pasted from the source code and may not be complete but you will get the idea. the time interrupts 100 times faster than the required PWM frequency. Anyway the code should speak for itself.....
Gary
Code: | #define FANMOTOR0 PIN_B7
#define FANMOTOR1 PIN_B6
#define FANMOTOR2 PIN_B5
#define FANMOTOR3 PIN_B4
#define FANMOTOR4 PIN_B3
#define FANMOTOR5 PIN_B2
#define FANMOTOR6 PIN_B1
#define FANMOTOR7 PIN_B0
#define PWMRESOLUTION 100
#define TIMER0OFFSET 73
int CurrentFanSpeedSetting0;
int CurrentFanSpeedSetting1;
int CurrentFanSpeedSetting2;
int CurrentFanSpeedSetting3;
int CurrentFanSpeedSetting4;
int CurrentFanSpeedSetting5;
int CurrentFanSpeedSetting6;
int CurrentFanSpeedSetting7;
void main()
{
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_8);
set_timer0(TIMER0OFFSET);
enable_interrupts(INT_TIMER0);
enable_interrupts(GLOBAL);
while(1);
}
#INT_TIMER0
void PwmTimerISR()
{
static int PwmCounter=0;
Set_Timer0(Get_Timer0()-TIMER0OFFSET);
if(PwmCounter==0)output_b(255);
if(PwmCounter>=CurrentFanSpeedSetting0) output_bit(FANMOTOR0,0);
if(PwmCounter>=CurrentFanSpeedSetting1) output_bit(FANMOTOR1,0);
if(PwmCounter>=CurrentFanSpeedSetting2) output_bit(FANMOTOR2,0);
if(PwmCounter>=CurrentFanSpeedSetting3) output_bit(FANMOTOR3,0);
if(PwmCounter>=CurrentFanSpeedSetting4) output_bit(FANMOTOR4,0);
if(PwmCounter>=CurrentFanSpeedSetting5) output_bit(FANMOTOR5,0);
if(PwmCounter>=CurrentFanSpeedSetting6) output_bit(FANMOTOR6,0);
if(PwmCounter>=CurrentFanSpeedSetting7) output_bit(FANMOTOR7,0);
PwmCounter++;
if(PWMcounter>=PWMRESOLUTION)PWMcounter=0;
} |
|
|
|
John P
Joined: 17 Sep 2003 Posts: 331
|
|
Posted: Mon Jan 07, 2008 9:34 am |
|
|
So it was model aircraft servos!
If there's only a trivial error introduced by ignoring the time between the interrupt and the resetting of the timer, then fine, ignore it. But if you work with servos, the objective is always to reduce the error to zero. |
|
|
gjs_rsdi
Joined: 06 Feb 2006 Posts: 468 Location: Bali
|
servo errors to zero |
Posted: Mon Jan 07, 2008 5:09 pm |
|
|
you are right, errors should be zero. In assembler the task very easy, even long to write, in c. not so, you have a lot of overhead. can be done of course, the question is the neccessity, if you really need that.
joseph |
|
|
|