在MSP430 Launchpad下利用PWM控制舵机
[复制链接]
RC Servos are pretty useful devices, they allow us to move things in a mechanical manner. They are cheap and readily available, so it only makes sense to use them!
A quick intro:
Servos are actually rather simple to interface with, they merely require a pulse of a certain width to determine the next position.
Because of this, we can use hardware PWM to generate these pulses. The frequency is typically 50Hz, which means we’ll be able to obtain a great deal of granularity with regard to the duty cycle, given our clock speed of 1MHz. Which is required since we’ll be dealing with pulses ranging ~1mS to ~2mS (typically)
Different servos will require a different minimum and maximum duty cycle, however in general the given code should just-work, in case your servo goes off range simply find the best minimum and maximum values for it
#include "msp430g2553.h" // make sure you change the header to suit your particular device. // Connect the servo SIGNAL wire to P1.2 through a 1K resistor. #define MCU_CLOCK 1000000 #define PWM_FREQUENCY 46 // In Hertz, ideally 50Hz. #define SERVO_STEPS 180 // Maximum amount of steps in degrees (180 is common) #define SERVO_MIN 650 // The minimum duty cycle for this servo #define SERVO_MAX 2700 // The maximum duty cycle unsigned int PWM_Period = (MCU_CLOCK / PWM_FREQUENCY); // PWM Period unsigned int PWM_Duty = 0; // % void main (void){ unsigned int servo_stepval, servo_stepnow; unsigned int servo_lut[ SERVO_STEPS+1 ]; unsigned int i; // Calculate the step value and define the current step, defaults to minimum. servo_stepval = ( (SERVO_MAX - SERVO_MIN) / SERVO_STEPS ); servo_stepnow = SERVO_MIN; // Fill up the LUT for (i = 0; i < SERVO_STEPS; i++) { servo_stepnow += servo_stepval; servo_lut[i] = servo_stepnow; } // Setup the PWM, etc. WDTCTL = WDTPW + WDTHOLD; // Kill watchdog timer TACCTL1 = OUTMOD_7; // TACCR1 reset/set TACTL = TASSEL_2 + MC_1; // SMCLK, upmode TACCR0 = PWM_Period-1; // PWM Period TACCR1 = PWM_Duty; // TACCR1 PWM Duty Cycle P1DIR |= BIT2; // P1.2 = output P1SEL |= BIT2; // P1.2 = TA1 output // Main loop while (1){ // Go to 0° TACCR1 = servo_lut[0]; __delay_cycles(1000000); // Go to 45° TACCR1 = servo_lut[45]; __delay_cycles(1000000); // Go to 90° TACCR1 = servo_lut[90]; __delay_cycles(1000000); // Go to 180° TACCR1 = servo_lut[179]; __delay_cycles(1000000); // Move forward toward the maximum step value for (i = 0; i < SERVO_STEPS; i++) { TACCR1 = servo_lut[i]; __delay_cycles(20000); } // Move backward toward the minimum step value for (i = SERVO_STEPS; i > 0; i--) { TACCR1 = servo_lut[i]; __delay_cycles(20000); } }
The actual code was written for a quick and dirty servo testing unit I put together, however it could easily be expanded to other uses.
By using a LUT (Look-Up Table) we are able to position the servo on a per-degree basis, which makes the code a lot more intuitive and readable.
A bunch of notes and tips:
- Filter your servo power supply properly; servos are electrically noisy!
- Make sure your micro-controller is protected from external devices, such as the servos. For instance never connect the signal wire directly to the GPIO pins. Instead, use a buffer or at least a resistor. For this example we used a 1k Resistor, but since the “signal” interface is relatively high impedance, we could even use 4k7 or higher without much hassle.
- As long as you keep sending a signal to the servo, the servo will keep it’s position due to it’s internal feedback. However this consumes power so if you’ve got a very lightweight load or no load at all, you can always stop the PWM output for the periods of inactivity to save power.
- If you aren’t sure about your servo connections, here’s a small diagram to help you find the pinouts. In my case I had a couple cheap “Tower Pro” blue servos that use the JR pinout — These work fine at both 5v and 3.3v, however do not power them from the Launchpad power supply pins! — Servos consume a lot of current, always use an external PSU.
- Only one connection is required to interface the servo with the launchpad, find the pin P1.2 and connect it to the SIGNAL wire from your servo.
|