Jump to content
emklaus

Hobby servo motor project / tutorial

Recommended Posts

This is an RC Servo demo project for the Stellaris LaunchPad Evaluation Board

This particular demo will drive 3 motors with commands from a serial terminal.

 

I've attempted to set this demo project up for those just starting out with 
the LaunchPad.  The code could be optimized quite a bit but I was trying to keep it
as uncomplicated as possible.
There are plenty of comments in the code to help as well.   
Send your comments my way but be kind as I'm really just starting out myself.
Enjoy.
Eric
 

 

servo3.zip

Share this post


Link to post
Share on other sites

Thanks  - I learned a bit about Timers by using your code. Also works great with a Parrallax continual rotation servo.

Thanks for the info.  Glad it was helpfull, that was the intent.

Share this post


Link to post
Share on other sites

I downloaded this and got it working somewhat. I am running into an issue though where after a couple of commands it stops working. I also cant connect to my launchpad with Putty but I can connect to it with tera term. Also, I was looking through the code cause I wanted to try and modify it so that the two buttons on the launchpad will move the servo, but I can't figure out which line of code is actually causing the servo to move. Any help would be greatly appreciated. 

Share this post


Link to post
Share on other sites

I downloaded this and got it working somewhat. I am running into an issue though where after a couple of commands it stops working. I also cant connect to my launchpad with Putty but I can connect to it with tera term. Also, I was looking through the code cause I wanted to try and modify it so that the two buttons on the launchpad will move the servo, but I can't figure out which line of code is actually causing the servo to move. Any help would be greatly appreciated. 

 

Hi  Spikerbond;

Thanks for taking a look at this demo project.

 

The key to getting the servos to move is all in the values held in the global array g_ulServoPulse[ ]

It contains the values that the interrupt routine will use to send pulses to the servos.

In this demo the array contains values for 3 servos (servo# 0, servo# 1 and Servo# 2) as well as a fourth 20ms value.

So when you add your code to support the button press, you only need to add or subtract from one of the first

3 values in this array to move any of the 3 supported  servos.

 

If you look at the original code (around line# 244) you’ll find the bits of code that handle the pressing of the “+” or “-“

keys from the terminal program input (see “case ‘+’:”  and case ‘-‘:”)  that’s where I actually update these values.

The iSelectedServoNo variable is set when you press “0”, “1” or “2” from the terminal (it defaults to zero)

Those keys also read the current pulse width value from the array for the selected servo into the local iPulseWidth variable.

So pressing “+” or “-“ just adds or subtracts the current step value and updates the global array.

 

The values in g_ulServoPulse[ ]  are in microseconds of pulse width.  So 1500 puts a standard servo at about mid-point.

Be sure to keep the numbers you put in there within a reasonable range for your servo (see MAX_SERVO_PULSE_WIDTH and MIN_SERVO_PULSE_WIDTH)

 

Hope this helps you with your modifications.

 

Please send me more details about what and how it stops working. 

Which command are you sending and about how many before it stops?

Is the whole application crashing or only the servos movement?

I’ve heard of  terra term but I’ve never used it.  Shouldn’t make a difference for this application.

I might be able to help you with making putty work though.

 

Good Luck

Eric

Share this post


Link to post
Share on other sites

I got it working with the buttons, now I just need to figure out how to make it move more smoothly. I tried messing around with some of the numbers related to the timer but nothing worked (btw I have never used C but I am fairly good with Java so I can kind of understand the code, but most of it makes no sense). Any ideas?

//*****************************************************************************
// Servo3.c
// A demo to drive 3 RC servo motors using TIMER1A and PORTF PF1-PF3
//
// NOTE: PORTF was chosen so we could get some visual feedback from the LEDs
// ***************************************************************************
// Notes on setting up a new CSS project:
// Example projects have these settings hidden and they are not automatically
//   set when a new project is created.

// In: Arm Compiler - Advanced Options - Predefined Symbols
//  ccs="ccs"
//  TARGET_IS_BLIZZARD_RA1
//  PART_LM4F120H5QR
//
// Add CSS Build Variable SW_ROOT = C:\StellarisWare (or wherever StellarisWare is installed)
// Add "${SW_ROOT}" (including quotes) to Compiler include path
// Add ARM Linker Include Library file: "${SW_ROOT}/driverlib/ccs-cm4f/Debug/driverlib-cm4f.lib"
// Add reference to any utils/ files you may use  (this one uses utils/uartstdio.c)
//*****************************************************************************

#include "inc/hw_gpio.h"
#include "inc/hw_sysctl.h"

#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/debug.h"
#include "driverlib/gpio.h"
#include "driverlib/pin_map.h"
#include "driverlib/rom.h"
#include "driverlib/sysctl.h"
#include "driverlib/timer.h"
#include "utils/uartstdio.h"

//*****************************************************************************
//Servo Motor Demo
//Drive 3 RC Servo motors using TIMER1 and PORTF PF1, PF2 & PF3
//
// UART0, connected to the Stellaris Virtual Serial Port and running at 115,200,
// 8-N-1, is used to display messages from this application.
//
//*****************************************************************************


// Define the limits of the pulse widths and number of servos
#define MAX_SERVO_PULSE_WIDTH 3000
#define MIN_SERVO_PULSE_WIDTH 300
#define MAX_SERVO_NO 3
#define BUTTON_1 GPIO_PIN_0
#define BUTTON_2 GPIO_PIN_4


unsigned long g_ulPeriod, g_ulPulseWidth;

// Using 3 arrays here to keep it simple (should be a struct)
// 1st element in each array is the Port,Pin & Pulse Width for Servo 0, then Servo 1 and Servo 2
// The final element is a placeholder for the 20ms period delay. use PORT=0 PIN=0
// ******************************************** Servo 0          Servo 1          Servo 2    (Period)
unsigned long g_ulServoPorts[MAX_SERVO_NO+1] = {GPIO_PORTF_BASE, GPIO_PORTF_BASE, GPIO_PORTF_BASE, 0};
unsigned long g_ulServoPins[MAX_SERVO_NO+1]  = {GPIO_PIN_1,      GPIO_PIN_2,      GPIO_PIN_3,      0};
unsigned long g_ulServoPulse[MAX_SERVO_NO+1] = {1500,            1500,            1500,        20000};

volatile int g_iServoNo=0; // The index into the arrays

unsigned long g_ulCountsPerMicrosecond;  // Holds the calculated value

//*****************************************************************************
//
// The error routine that is called if the driver library encounters an error.
//
//*****************************************************************************
#ifdef DEBUG
void __error__(char *pcFilename, unsigned long ulLine)
{
}
#endif

//*****************************************************************************
//
// The timeout interrupt handler for timer1
//
// On each TIMER1A timeout, start another pulse and end the previous one.
// If all Servos have been sent a pulse, set timer for the final 20ms period delay
//*****************************************************************************
void Timer1IntHandler(void)
{
	unsigned long ulNewPin, ulOldPin, ulNewPort, ulOldPort, ulPulseWidth;

    // Clear the timer interrupt.
    ROM_TimerIntClear(TIMER1_BASE, TIMER_TIMA_TIMEOUT);

    // Get the pulse width value for the current servo from the array
    // if we have already serviced all servos (g_iServoNo = MAX_SERVO_NO)
    // then this value should be the 20ms period value
  	ulPulseWidth = g_ulCountsPerMicrosecond * g_ulServoPulse[g_iServoNo];

	// Re-Load the timer with the new pulse width count value
	ROM_TimerLoadSet(TIMER1_BASE, TIMER_A, ulPulseWidth);

    
    // End the servo pulse set previously (if any)
  	if(g_iServoNo > 0)  // If not the 1st Servo....
   	 {
      // Read 2 PREVIOUS Servo Values from the 2 Arrays PORT & PIN
   	  ulOldPort= g_ulServoPorts[g_iServoNo-1];
      ulOldPin = g_ulServoPins[g_iServoNo-1];

   	  // Set the PREVIOUS  Servo pulse signal pin LOW
   	  ROM_GPIOPinWrite(ulOldPort, ulOldPin, 0);
   	 }


    // Set the current servo pin HIGH
    if(g_iServoNo < MAX_SERVO_NO)
      {
    	// Read 2 Servo Values from the 3 Arrays PORT & PIN
    	ulNewPort=g_ulServoPorts[g_iServoNo];
    	ulNewPin=g_ulServoPins[g_iServoNo];

    	// Set the Selected Servo pulse signal pin HIGH
    	ROM_GPIOPinWrite(ulNewPort, ulNewPin, ulNewPin);

      	g_iServoNo++;  // Advance to next servo for processing next time
      }
    else
     {
        g_iServoNo=0; // Start all over again
     }

}


//*****************************************************************************
// This demo show how to drive 3 RC Servos  using TIMER1 & PORTF P1, P2 & P3
// Note: Using the same pins as the LED for visual effect.
//       Any GPIO PORT may be used.
//
//*****************************************************************************
int main(void)
{
	//unsigned char k, p;
	volatile int iStep=1, iPulseWidth=1500, iSelectedServoNo=0;

	//
    // Enable lazy stacking for interrupt handlers.  This allows floating-point
    // instructions to be used within interrupt handlers, but at the expense of
    // extra stack usage.
    //
    ROM_FPULazyStackingEnable();

    //
    // Set the clocking to run directly from the crystal. (16MHZ)
    //
    ROM_SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN |
                       SYSCTL_XTAL_16MHZ);



    // Enable PORT F GPIO
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
    // Unlock PF0 so we can change it to a GPIO input
    // Once we have enabled (unlocked) the commit register then re-lock it
    // to prevent further changes.  PF0 is muxed with NMI thus a special case.
    HWREG(GPIO_PORTF_BASE + GPIO_O_LOCK) = GPIO_LOCK_KEY_DD;
    HWREG(GPIO_PORTF_BASE + GPIO_O_CR) |= 0x01;
    HWREG(GPIO_PORTF_BASE + GPIO_O_LOCK) = 0;
    // set user switches as inputs
    GPIOPinTypeGPIOInput(GPIO_PORTF_BASE, BUTTON_1|BUTTON_2);
    // turn weak pull-ups on
    GPIOPadConfigSet(GPIO_PORTF_BASE, BUTTON_1|BUTTON_2, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU);



    //
    // Initialize the UART and write status.
    //
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
    ROM_GPIOPinConfigure(GPIO_PA0_U0RX);
    ROM_GPIOPinConfigure(GPIO_PA1_U0TX);
    ROM_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);

    UARTStdioInit(0);  // Baud=115200

    // Print the menu ...  TODO: make this a function and add a "Redisplay Menu" menu choice.
    UARTprintf("\r\n **** Servo Motor Demo ****\r\n");
    UARTprintf(" S or s   = Step=100 / Step=10 \r\n");
    UARTprintf(" + or -   = PulseWidth +/- Step \r\n");
    UARTprintf("      ?   = Show Values \r\n");
    UARTprintf(" 0,1 or 2 = Set Active Servo \r\n");


    // Enable ALL the GPIO port(s) used for EACH Servo
    // (Only using PORTF in this example)
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);


    // Enable the GPIO ports and reset the output pins
    //
    for(iSelectedServoNo=0; iSelectedServoNo < MAX_SERVO_NO; iSelectedServoNo++)
       {
        // Enable the GPIO pins specified in the arrays for each servo
        ROM_GPIOPinTypeGPIOOutput(g_ulServoPorts[iSelectedServoNo], g_ulServoPins[iSelectedServoNo]);

        // Set all the outputs LOW
        ROM_GPIOPinWrite(g_ulServoPorts[iSelectedServoNo], g_ulServoPins[iSelectedServoNo], 0);
       }


    // Enable TIMER1
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER1);

    // Enable processor interrupts.
    ROM_IntMasterEnable();

    // Configure the TIMER1
    ROM_TimerConfigure(TIMER1_BASE, TIMER_CFG_PERIODIC);

    // Calculate the number of timer counts/microsecond
    g_ulCountsPerMicrosecond = ROM_SysCtlClockGet()/1000000;

    g_ulPeriod= g_ulCountsPerMicrosecond * 20000;   // 20ms = Standard Servo refresh delay

    // Initially load the timer with 20ms interval time
    ROM_TimerLoadSet(TIMER1_BASE, TIMER_A, g_ulPeriod);

    // Setup the interrupt for the TIMER1A timeout.
    ROM_IntEnable(INT_TIMER1A);
    ROM_TimerIntEnable(TIMER1_BASE, TIMER_TIMA_TIMEOUT);


    // Enable the timer.
    ROM_TimerEnable(TIMER1_BASE, TIMER_A);

    // *************** Menu Command Processing ************************************
    // UART0, connected to the Stellaris Virtual Serial Port and running at 115,200,
    // 8-N-1, is used to display messages from this application.
    //
    iSelectedServoNo=1;  // Start with Servo 0
    iPulseWidth = g_ulServoPulse[iSelectedServoNo];

    // Loop forever while checking for input commands from terminal
    while(1)
    {
    	if(!GPIOPinRead(GPIO_PORTF_BASE, BUTTON_1))
    	{
    		if(iPulseWidth <= (MAX_SERVO_PULSE_WIDTH - iStep))
    		{
    			iPulseWidth += iStep;
    			// Save the new pulse width value in the pulse array
    			// it will be applied on the appropriate timer interrupt
    			g_ulServoPulse[iSelectedServoNo]=iPulseWidth;
    		}
    	}

    	if(!GPIOPinRead(GPIO_PORTF_BASE, BUTTON_2))
    	{
    		if(iPulseWidth >= (MIN_SERVO_PULSE_WIDTH + iStep))
    		{
    			iPulseWidth -= iStep;
    			// Save the new pulse width value in the pulse array
    			// it will be applied on the appropriate timer interrupt
    			g_ulServoPulse[iSelectedServoNo]=iPulseWidth;
    		}
    	}

    	/*
       // Any input from serial terminal?
    	if(ROM_UARTCharsAvail(UART0_BASE))
    	  {
    		p=0;              //Print values indicator = don't print
    		k = UARTgetc();   //Read 1 byte

    		switch(k)
    		 {
    		  case 's':       //Set Small Step value
    			 p++;
    			 iStep=100;
    		  break;

    		  case 'S':      //Set LARGE Step value
    			  p++;
    			  iStep=500;
    		  break;

    		  case '+':      // Increase Pulse Width
    			  p++;
    			  if(iPulseWidth <= (MAX_SERVO_PULSE_WIDTH - iStep))
    			    {
    			     iPulseWidth += iStep;
      			     // Save the new pulse width value in the pulse array
      			     // it will be applied on the appropriate timer interrupt
    			     g_ulServoPulse[iSelectedServoNo]=iPulseWidth;
    			    }
    		  break;

    		  case '-':     // Decrease pulse width
    			  p++;
    			  if(iPulseWidth >= (MIN_SERVO_PULSE_WIDTH + iStep))
    			    {
      			     iPulseWidth -= iStep;
      			     // Save the new pulse width value in the pulse array
      			     // it will be applied on the appropriate timer interrupt
      			     g_ulServoPulse[iSelectedServoNo]=iPulseWidth;
    			    }
    		  break;

    		  case '0':  // Set Active ServoNo
    		  case '1':
    		  case '2':
    			  iSelectedServoNo = (int)(k - 0x30);
    			  // ** Get the Pulse Width for the newly selected Servo
    			  iPulseWidth = g_ulServoPulse[iSelectedServoNo];
    			  p++;
    		  break;

    		  case '?':    // Just display the values
    			  p++;
    		  break;

    		 }

    		if(p)
    		  {
    			UARTprintf("\r\n Step:%d  ServoNo:%d  Pulse Width:%d", iStep, iSelectedServoNo, iPulseWidth);
    		  }
    	  }


    	// Maybe do some other work here...
	*/
    }
}

Share this post


Link to post
Share on other sites

 

I got it working with the buttons, now I just need to figure out how to make it move more smoothly. I tried messing around with some of the numbers related to the timer but nothing worked (btw I have never used C but I am fairly good with Java so I can kind of understand the code, but most of it makes no sense). Any ideas?

//*****************************************************************************
// Servo3.c
// A demo to drive 3 RC servo motors using TIMER1A and PORTF PF1-PF3
//
// NOTE: PORTF was chosen so we could get some visual feedback from the LEDs
// ***************************************************************************
// Notes on setting up a new CSS project:
// Example projects have these settings hidden and they are not automatically
//   set when a new project is created.

// In: Arm Compiler - Advanced Options - Predefined Symbols
//  ccs="ccs"
//  TARGET_IS_BLIZZARD_RA1
//  PART_LM4F120H5QR
//
// Add CSS Build Variable SW_ROOT = C:\StellarisWare (or wherever StellarisWare is installed)
// Add "${SW_ROOT}" (including quotes) to Compiler include path
// Add ARM Linker Include Library file: "${SW_ROOT}/driverlib/ccs-cm4f/Debug/driverlib-cm4f.lib"
// Add reference to any utils/ files you may use  (this one uses utils/uartstdio.c)
//*****************************************************************************

#include "inc/hw_gpio.h"
#include "inc/hw_sysctl.h"

#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/debug.h"
#include "driverlib/gpio.h"
#include "driverlib/pin_map.h"
#include "driverlib/rom.h"
#include "driverlib/sysctl.h"
#include "driverlib/timer.h"
#include "utils/uartstdio.h"

//*****************************************************************************
//Servo Motor Demo
//Drive 3 RC Servo motors using TIMER1 and PORTF PF1, PF2 & PF3
//
// UART0, connected to the Stellaris Virtual Serial Port and running at 115,200,
// 8-N-1, is used to display messages from this application.
//
//*****************************************************************************


// Define the limits of the pulse widths and number of servos
#define MAX_SERVO_PULSE_WIDTH 3000
#define MIN_SERVO_PULSE_WIDTH 300
#define MAX_SERVO_NO 3
#define BUTTON_1 GPIO_PIN_0
#define BUTTON_2 GPIO_PIN_4


unsigned long g_ulPeriod, g_ulPulseWidth;

// Using 3 arrays here to keep it simple (should be a struct)
// 1st element in each array is the Port,Pin & Pulse Width for Servo 0, then Servo 1 and Servo 2
// The final element is a placeholder for the 20ms period delay. use PORT=0 PIN=0
// ******************************************** Servo 0          Servo 1          Servo 2    (Period)
unsigned long g_ulServoPorts[MAX_SERVO_NO+1] = {GPIO_PORTF_BASE, GPIO_PORTF_BASE, GPIO_PORTF_BASE, 0};
unsigned long g_ulServoPins[MAX_SERVO_NO+1]  = {GPIO_PIN_1,      GPIO_PIN_2,      GPIO_PIN_3,      0};
unsigned long g_ulServoPulse[MAX_SERVO_NO+1] = {1500,            1500,            1500,        20000};

volatile int g_iServoNo=0; // The index into the arrays

unsigned long g_ulCountsPerMicrosecond;  // Holds the calculated value

//*****************************************************************************
//
// The error routine that is called if the driver library encounters an error.
//
//*****************************************************************************
#ifdef DEBUG
void __error__(char *pcFilename, unsigned long ulLine)
{
}
#endif

//*****************************************************************************
//
// The timeout interrupt handler for timer1
//
// On each TIMER1A timeout, start another pulse and end the previous one.
// If all Servos have been sent a pulse, set timer for the final 20ms period delay
//*****************************************************************************
void Timer1IntHandler(void)
{
	unsigned long ulNewPin, ulOldPin, ulNewPort, ulOldPort, ulPulseWidth;

    // Clear the timer interrupt.
    ROM_TimerIntClear(TIMER1_BASE, TIMER_TIMA_TIMEOUT);

    // Get the pulse width value for the current servo from the array
    // if we have already serviced all servos (g_iServoNo = MAX_SERVO_NO)
    // then this value should be the 20ms period value
  	ulPulseWidth = g_ulCountsPerMicrosecond * g_ulServoPulse[g_iServoNo];

	// Re-Load the timer with the new pulse width count value
	ROM_TimerLoadSet(TIMER1_BASE, TIMER_A, ulPulseWidth);

    
    // End the servo pulse set previously (if any)
  	if(g_iServoNo > 0)  // If not the 1st Servo....
   	 {
      // Read 2 PREVIOUS Servo Values from the 2 Arrays PORT & PIN
   	  ulOldPort= g_ulServoPorts[g_iServoNo-1];
      ulOldPin = g_ulServoPins[g_iServoNo-1];

   	  // Set the PREVIOUS  Servo pulse signal pin LOW
   	  ROM_GPIOPinWrite(ulOldPort, ulOldPin, 0);
   	 }


    // Set the current servo pin HIGH
    if(g_iServoNo < MAX_SERVO_NO)
      {
    	// Read 2 Servo Values from the 3 Arrays PORT & PIN
    	ulNewPort=g_ulServoPorts[g_iServoNo];
    	ulNewPin=g_ulServoPins[g_iServoNo];

    	// Set the Selected Servo pulse signal pin HIGH
    	ROM_GPIOPinWrite(ulNewPort, ulNewPin, ulNewPin);

      	g_iServoNo++;  // Advance to next servo for processing next time
      }
    else
     {
        g_iServoNo=0; // Start all over again
     }

}


//*****************************************************************************
// This demo show how to drive 3 RC Servos  using TIMER1 & PORTF P1, P2 & P3
// Note: Using the same pins as the LED for visual effect.
//       Any GPIO PORT may be used.
//
//*****************************************************************************
int main(void)
{
	//unsigned char k, p;
	volatile int iStep=1, iPulseWidth=1500, iSelectedServoNo=0;

	//
    // Enable lazy stacking for interrupt handlers.  This allows floating-point
    // instructions to be used within interrupt handlers, but at the expense of
    // extra stack usage.
    //
    ROM_FPULazyStackingEnable();

    //
    // Set the clocking to run directly from the crystal. (16MHZ)
    //
    ROM_SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN |
                       SYSCTL_XTAL_16MHZ);



    // Enable PORT F GPIO
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
    // Unlock PF0 so we can change it to a GPIO input
    // Once we have enabled (unlocked) the commit register then re-lock it
    // to prevent further changes.  PF0 is muxed with NMI thus a special case.
    HWREG(GPIO_PORTF_BASE + GPIO_O_LOCK) = GPIO_LOCK_KEY_DD;
    HWREG(GPIO_PORTF_BASE + GPIO_O_CR) |= 0x01;
    HWREG(GPIO_PORTF_BASE + GPIO_O_LOCK) = 0;
    // set user switches as inputs
    GPIOPinTypeGPIOInput(GPIO_PORTF_BASE, BUTTON_1|BUTTON_2);
    // turn weak pull-ups on
    GPIOPadConfigSet(GPIO_PORTF_BASE, BUTTON_1|BUTTON_2, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU);



    //
    // Initialize the UART and write status.
    //
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
    ROM_GPIOPinConfigure(GPIO_PA0_U0RX);
    ROM_GPIOPinConfigure(GPIO_PA1_U0TX);
    ROM_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);

    UARTStdioInit(0);  // Baud=115200

    // Print the menu ...  TODO: make this a function and add a "Redisplay Menu" menu choice.
    UARTprintf("\r\n **** Servo Motor Demo ****\r\n");
    UARTprintf(" S or s   = Step=100 / Step=10 \r\n");
    UARTprintf(" + or -   = PulseWidth +/- Step \r\n");
    UARTprintf("      ?   = Show Values \r\n");
    UARTprintf(" 0,1 or 2 = Set Active Servo \r\n");


    // Enable ALL the GPIO port(s) used for EACH Servo
    // (Only using PORTF in this example)
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);


    // Enable the GPIO ports and reset the output pins
    //
    for(iSelectedServoNo=0; iSelectedServoNo < MAX_SERVO_NO; iSelectedServoNo++)
       {
        // Enable the GPIO pins specified in the arrays for each servo
        ROM_GPIOPinTypeGPIOOutput(g_ulServoPorts[iSelectedServoNo], g_ulServoPins[iSelectedServoNo]);

        // Set all the outputs LOW
        ROM_GPIOPinWrite(g_ulServoPorts[iSelectedServoNo], g_ulServoPins[iSelectedServoNo], 0);
       }


    // Enable TIMER1
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER1);

    // Enable processor interrupts.
    ROM_IntMasterEnable();

    // Configure the TIMER1
    ROM_TimerConfigure(TIMER1_BASE, TIMER_CFG_PERIODIC);

    // Calculate the number of timer counts/microsecond
    g_ulCountsPerMicrosecond = ROM_SysCtlClockGet()/1000000;

    g_ulPeriod= g_ulCountsPerMicrosecond * 20000;   // 20ms = Standard Servo refresh delay

    // Initially load the timer with 20ms interval time
    ROM_TimerLoadSet(TIMER1_BASE, TIMER_A, g_ulPeriod);

    // Setup the interrupt for the TIMER1A timeout.
    ROM_IntEnable(INT_TIMER1A);
    ROM_TimerIntEnable(TIMER1_BASE, TIMER_TIMA_TIMEOUT);


    // Enable the timer.
    ROM_TimerEnable(TIMER1_BASE, TIMER_A);

    // *************** Menu Command Processing ************************************
    // UART0, connected to the Stellaris Virtual Serial Port and running at 115,200,
    // 8-N-1, is used to display messages from this application.
    //
    iSelectedServoNo=1;  // Start with Servo 0
    iPulseWidth = g_ulServoPulse[iSelectedServoNo];

    // Loop forever while checking for input commands from terminal
    while(1)
    {
    	if(!GPIOPinRead(GPIO_PORTF_BASE, BUTTON_1))
    	{
    		if(iPulseWidth <= (MAX_SERVO_PULSE_WIDTH - iStep))
    		{
    			iPulseWidth += iStep;
    			// Save the new pulse width value in the pulse array
    			// it will be applied on the appropriate timer interrupt
    			g_ulServoPulse[iSelectedServoNo]=iPulseWidth;
    		}
    	}

    	if(!GPIOPinRead(GPIO_PORTF_BASE, BUTTON_2))
    	{
    		if(iPulseWidth >= (MIN_SERVO_PULSE_WIDTH + iStep))
    		{
    			iPulseWidth -= iStep;
    			// Save the new pulse width value in the pulse array
    			// it will be applied on the appropriate timer interrupt
    			g_ulServoPulse[iSelectedServoNo]=iPulseWidth;
    		}
    	}

    	/*
       // Any input from serial terminal?
    	if(ROM_UARTCharsAvail(UART0_BASE))
    	  {
    		p=0;              //Print values indicator = don't print
    		k = UARTgetc();   //Read 1 byte

    		switch(k)
    		 {
    		  case 's':       //Set Small Step value
    			 p++;
    			 iStep=100;
    		  break;

    		  case 'S':      //Set LARGE Step value
    			  p++;
    			  iStep=500;
    		  break;

    		  case '+':      // Increase Pulse Width
    			  p++;
    			  if(iPulseWidth <= (MAX_SERVO_PULSE_WIDTH - iStep))
    			    {
    			     iPulseWidth += iStep;
      			     // Save the new pulse width value in the pulse array
      			     // it will be applied on the appropriate timer interrupt
    			     g_ulServoPulse[iSelectedServoNo]=iPulseWidth;
    			    }
    		  break;

    		  case '-':     // Decrease pulse width
    			  p++;
    			  if(iPulseWidth >= (MIN_SERVO_PULSE_WIDTH + iStep))
    			    {
      			     iPulseWidth -= iStep;
      			     // Save the new pulse width value in the pulse array
      			     // it will be applied on the appropriate timer interrupt
      			     g_ulServoPulse[iSelectedServoNo]=iPulseWidth;
    			    }
    		  break;

    		  case '0':  // Set Active ServoNo
    		  case '1':
    		  case '2':
    			  iSelectedServoNo = (int)(k - 0x30);
    			  // ** Get the Pulse Width for the newly selected Servo
    			  iPulseWidth = g_ulServoPulse[iSelectedServoNo];
    			  p++;
    		  break;

    		  case '?':    // Just display the values
    			  p++;
    		  break;

    		 }

    		if(p)
    		  {
    			UARTprintf("\r\n Step:%d  ServoNo:%d  Pulse Width:%d", iStep, iSelectedServoNo, iPulseWidth);
    		  }
    	  }


    	// Maybe do some other work here...
	*/
    }
}

 

 

 

You've got the right idea there!

I can see a couple things that would help you out.

keep in mind that the while() loop you have your button code in is running at full speed as fast as the

processor can go.  So when you press and hold the button it’s going to increment the iPulseWidth

variable as fast as it can. I imagine it gets to it’s limit by the time you let up on the button.

(in my demo the timing of the “+” key signals was controlled by the keyboard repeat rate

 the buttons on the board don’t have that built in )

 

Looks like you’re trying to learn a bit of C here so I won’t give you actual code right away.

(if you don’t figure it out with a reasonable effort let me know and I’ll send some actual code)

 

So first you need to de-bounce those buttons.

See if you can code something like this:

Define a variable for a de-bounce counter and set it to zero

Read the button.

 Is the button Pressed?

   No =reset the de-bounce counter and exit this loop

    Yes = increment the de-bounce counter

   delay 5 milliseconds

   has the de-bounce counter reached 20?

   yes = exit this loop

  no = back to read the button again

When you exit the loop, if the counter = 20 it’s s good button press 

(this means you’ve held it down for at least 100ms or 1/10 sec.)

otherwise just go on to check the other button the same way.

Adjust the delay value and max de-bounce count values to get the results you like.

Once you’ve verified a “Good” button press you may also want to check for a “good” button release

(say for at least 10ms)  That depends of course on if you want to have the servos move while you hold down

a button or if you want to require the button to be released before registering the next movement.

 

Get the idea?   You can find many articles on de-bouncing buttons online.

 

Here’s a good delay routine:

ROM_SysCtlDelay(ROM_SysCtlClockGet() / speed / 3);

(when speed=1 delay = 1sec.;    speed=2 delay=1/2sec.;   speed =4 delay = ¼ sec; speed=10 delay=1/10 sec and so on…)

 

Good Luck

Eric

Share this post


Link to post
Share on other sites

 

This is an RC Servo demo project for the Stellaris LaunchPad Evaluation Board

This particular demo will drive 3 motors with commands from a serial terminal.

 

I've attempted to set this demo project up for those just starting out with 
the LaunchPad.  The code could be optimized quite a bit but I was trying to keep it
as uncomplicated as possible.
There are plenty of comments in the code to help as well.   
Send your comments my way but be kind as I'm really just starting out myself.
Enjoy.
Eric

 

you can help your dc servo motor controller is not? I am using lm3s1968 kit, I have not mastered programming. I do not know how to read from the motor encoder, and PID algorithm to use for stable engine operation, wish you more help. Thank you very much

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×