View previous topic :: View next topic |
Author |
Message |
soong2020
Joined: 13 Feb 2008 Posts: 25
|
help needed for PWM for servos |
Posted: Fri Feb 15, 2008 2:04 am |
|
|
Hi fellow forumers, I'm a new member in the programming world here. I've got a problem here about setting up PWM signals for multiple servo motors for a robotic arm I've designed.
Servo motors : Hi-Tech HS series servomotors
Microcontroller : PIC16F877A
External Oscillator : 20Mhz
From browsing through the previous posts by other forumers, it seems that servos need a constant 50Hz of PWM signal. I've done some minor modifications to the ones posted by previous forumers to suit my circuit and this is how my code looks. But my servo does not respond to this somehow.
Code: |
#include <16F877A.h>
#fuses HS, NOLVP
#use delay(clock = 20000000)
#define PWM_PIN PIN_C0
#define LOOPCNT 390
int8 widthC; // center
//-------------------------------
void tick_interrupt(void);
void init_main();
//====================================
void init_main()
{
set_tris_a(0x00); // LED port
output_a(0x00);
set_tris_b(0xff); //button port set to all input
output_b(0x00);
set_tris_c(0x00); // servo port
output_c(0x00);
}
//================================================
main()
{
init_main();
widthC = 29; //1.5 ms duty cycle
setup_counters(RTCC_INTERNAL | RTCC_8_BIT, RTCC_DIV_1); // (20MHz / (256*4*1)) = 20KHz
enable_interrupts(INT_RTCC); // equals 0.05ms per interrupt
enable_interrupts(GLOBAL); // 0.05ms*390 = 19.5ms(almost 20ms)
while(1);
}
//====================================
#INT_RTCC
void tick_interrupt(void)
{
static int16 loop = LOOPCNT;
static int8 pulse;
if(--loop == 0 )
{
loop = LOOPCNT;
pulse = widthC;
if(pulse)
{
output_high(PWM_PIN);
pulse--;
}
else
{
output_low(PWM_PIN);
}
}
}
|
Please help me identify where have i gone wrong guys.
Thanks a lot.
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
|
John P
Joined: 17 Sep 2003 Posts: 331
|
|
Posted: Sat Feb 16, 2008 4:31 pm |
|
|
Thank you for using the code format, which so few people trouble themselves to do!
Just concentrating on the interrupt routine, it looks as if there's a disastrous mistake, which you might have avoided if you made more careful use of indents.
I've rewritten this the way I would do it (which not everyone agrees with, but we each have to find our own style):
Code: |
#INT_RTCC
void tick_interrupt(void)
{
static int16 loop = LOOPCNT;
static int8 pulse;
if(--loop == 0 )
{
loop = LOOPCNT;
pulse = widthC;
if(pulse)
{
output_high(PWM_PIN);
pulse--;
}
else
{
output_low(PWM_PIN);
}
}
}
|
What you can now see clearly is that the if(pulse) line is inside the if(--loop == 0 ) routine, and it definitely shouldn't be. All that should happen after if(--loop == 0 ) is initialiizing a servo pulse.
Like this:
Code: |
#INT_RTCC
void tick_interrupt(void)
{
static int16 loop = LOOPCNT;
static int8 pulse;
if(--loop == 0 )
{
loop = LOOPCNT;
pulse = widthC;
}
if(pulse)
{
output_high(PWM_PIN);
pulse--;
}
else
{
output_low(PWM_PIN);
}
}
|
|
|
|
soong2020
Joined: 13 Feb 2008 Posts: 25
|
|
Posted: Sun Feb 17, 2008 4:10 am |
|
|
Thanks PCM Programmer for the link. I'm trying to get to grips with the discussions in it as I'm still new in this.
Thanks John P for the correction. I think i've managed to generate a PWM signal but I'm not sure whether my calculations for the interrupts and timer part is correct though.
I've tested it by connecting the PWM pin into an LED and set the LOOPCNT and WidthC to much higher values and it blinked alternately. But it seems i cant get the calculations correct.
Can anyone comment on this?
Appreciate it |
|
|
Greg Richter
Joined: 15 Feb 2008 Posts: 18 Location: Atlanta, Tulsa, Asheville
|
|
Posted: Mon Feb 18, 2008 12:39 pm |
|
|
Search for Software PWM -- I just posted a tiny snippet that does a very elegant job of this. _________________ Madness takes it toll; please have exact change. |
|
|
soong2020
Joined: 13 Feb 2008 Posts: 25
|
|
Posted: Fri Feb 22, 2008 10:35 am |
|
|
Thanks for the recommendations guys. I've managed to get it sorted out as it finally worked after several tweaks. As I proceeded in expanding the programming, I've encountered another problem as in setting it to check button conditions. I've used Timer1 as a counting timer to check the buttons for a certain interval. But the motor failed to respond to the changes in the button states. Here is how i wrote it:
Code: |
#include <16F877A.h>
#fuses HS, NOLVP
#use delay(clock = 20000000)
#define XTAL_FREQUENCY 20000000
#define TIMER1_FREQUENCY (XTAL_FREQUENCY / 40)
#define PWM_PIN1 PIN_A0
#define PWM_PIN2 PIN_A1
#define PWM_PIN3 PIN_A2
#define BTN_CEN PIN_B7
#define BTN_R PIN_B6
#define BTN_L PIN_B5
#define LOOPCNT 400
int32 widthC, widthL, widthR, width0, Ticker;
static int32 loop = LOOPCNT;
static int32 pulse;
unsigned int fl_interrupt, fl_cen, fl_right, fl_left, fl_pulse;
//-------------------------------
void tick_interrupt(void);
void init_main();
void Initialize_RTC(void);
void move_servo();
void check_button();
void TIMER1_isr(void);
//void pwm();
void main();
//====================================
void init_main()
{
set_tris_a(0x00); // servo port
output_a(0x00);
set_tris_b(0b1111111); //button port set to all input
output_b(0x00);
set_tris_c(0x00); // xx
output_c(0x00);
fl_cen = TRUE;
fl_right = FALSE;
fl_left = FALSE;
fl_interrupt = 0;
fl_pulse = 0;
}
//***************************************************************************
void Initialize_RTC(void)
{
Ticker = TIMER1_FREQUENCY;
setup_timer_1( T1_INTERNAL | T1_DIV_BY_1 );
enable_interrupts( INT_TIMER1 );
}
//===========================================================================
void check_button()
{
if(BTN_CEN==0)
{
fl_cen = TRUE;
}
if(BTN_R==0)
{
fl_right = TRUE;
}
if(BTN_L==0)
{
fl_left = TRUE;
}
}
void move_servo()
{
if(fl_cen == TRUE)
{
fl_cen = FALSE;
widthC = width0;
}
else if(fl_right == TRUE)
{
fl_right = FALSE;
widthR = width0;
}
else if(fl_left == TRUE)
{
fl_left = FALSE;
widthL = width0;
}
}
//================================================
void main()
{
init_main();
widthL = 20; //1.0 ms duty cycle
widthC = 30; //1.5 ms duty cycle
widthR = 40; //2.0 ms duty cycle
width0 = widthC;
Initialize_RTC();
setup_counters(RTCC_INTERNAL | RTCC_8_BIT, RTCC_DIV_1);
enable_interrupts(INT_RTCC);
enable_interrupts(GLOBAL);
while(1)
{
while (fl_interrupt == 0)
{
check_button();
move_servo();
fl_interrupt = 0;
}
}
}
//====================================
#INT_RTCC
void tick_interrupt(void)
{
if(--loop == 0 )
{
loop = LOOPCNT;
pulse = width0;
}
if(pulse)
{
output_high(PWM_PIN1);
output_high(PWM_PIN2);
output_high(PWM_PIN3);
pulse--;
}
else
{
output_low(PWM_PIN1);
output_low(PWM_PIN2);
output_low(PWM_PIN3);
}
}
#INT_TIMER1 //////*************************************************************
void TIMER1_isr()
{
Ticker -= 65536; // Decrement ticker by clocks per interrupt
if ( Ticker < 65536 ) // If second has expired
{
Ticker += TIMER1_FREQUENCY;
fl_interrupt = 1;
}
}
//******************************************************************************
|
Is this a correct way to write it ? For now it is supposed to by default let the motor stay in central position upon being turned on. After that, it should be checking the buttons for it to move to different positions (Left and Right).
In the future I need it to receive feedback for its positions (as in how many degrees) using ADC and interface with a PC via RS-232 communications and perform some error corrections as well using the P.I.D algorithm.
Really appreciate it for all your comments and suggestions. |
|
|
|