NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Basic Electronics » Using pushbuttons to increment position of servo

November 22, 2011
by Jacp
Jacp's Avatar


I am a Norwegian Electronics student and I have some questions related to changing the position of a servo using pushbuttons. I have a pushbutton much alike the one called Tactile Switch on this link:

I tried using two pushbuttons to increment and decrement the position of a small servo. However regardless if I pushed the button once or held it in for a while the servo would only go to it's full position. Eighter 1ms or 2 ms. Therefore I was wondering if this button emits any kind of noise when pressed? And if this is not the problem what is?

Sorry for my bad English! Hope to get some answers.

Here is the code I used:

#define F_CPU 14745600

#include <stdio.h>

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <inttypes.h>
#include "../libnerdkits/delay.h"

#define PWM_MIN 1842 //1299
#define PWM_MAX 3685 //4449
#define PWM_START 2763 //2764

void pwm_set(uint16_t x) {
  OCR1B = x;

void pwm_init(){

    OCR1A = 36864;


     TCCR1A = (1<<COM1B1) | (1<<WGM11) | (1<<WGM10);
    TCCR1B = (1<<WGM13) | (1<<WGM12) | (1<<CS11);


unsigned int GetSW1(){
    unsigned int SW1;

            SW1 = (PINC & (1<<PC0)); //SW1=PC0 = aktiv lav
            return SW1; 

unsigned int GetSW2(){
    unsigned int SW2;

            SW2 = (PINC & (1<<PC1)); //SW2=PC3 = aktiv lav
            return SW2; 

int main() {

uint16_t pos = PWM_START;

    // Set PB2 as output
    DDRB |= (1<<PB2);

     //Set switches to input
     DDRC &= ~(1<<PC0); //Set PC0 to input
     DDRC &= ~(1<<PC1); //Set PC1 to input

     PORTC |= (1<<PC0); // turn on internal pull up resistor for PC0
     PORTC |= (1<<PC1); // turn on internal pull up resistor for PC1


    while(1) {

        if(!GetSW1()==1) pos+= 25; //pos = PWM_MAX; //pos+= 1;
           if(!GetSW2()==1) pos-= 25; //pos = PWM_MIN; //pos-= 1;

        // bounds checking
        if(pos > PWM_MAX) pos = PWM_MAX;
        if(pos < PWM_MIN) pos = PWM_MIN;

  return 0;
November 22, 2011
by treymd
treymd's Avatar

Probably. I believe you've encountered "switch bouncing". Appropriatly enough, the solution is called debouncing, where you use any number of methods to filter out the multiple on/off states that a switch may ancounter when transitioning from off to on. The easiest method would be to insert a short delay after you detect a button press, so that your program basically ignores the repeated bounces and acts only on the first one.

November 23, 2011
by Jacp
Jacp's Avatar

Thanks Treymd!

I added "delay_ms(5)" after "pos+=25;" and now it all works pretty well!

November 23, 2011
by SpaceGhost
SpaceGhost's Avatar

I have some code for two servo motor control that uses 4 tactile-type push buttons (left & right for each servo) for incremental control that works pretty well -

// 2servo01
// Pushbutton servo control - two servos

#define F_CPU 14745600

#include <stdio.h>

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <inttypes.h>

#include "../libnerdkits/delay.h"
#include "../libnerdkits/lcd.h"
#include "../libnerdkits/uart.h"

void pwm_setx(uint16_t x) {
  OCR1B = x;

void pwm_sety(uint16_t y) {
  OCR1A = y;

#define PWM_MIN 1300
#define PWM_MAX 4450
#define PWM_START 2765
void pwm_init() {
  // setup Timer1 for Fast PWM mode, 16-bit
  // COM1B1 -- for non-inverting output
  // WGM13, WGM12, WGM11, WGM10 -- for Fast PWM with OCR1A as TOP value
  // CS11 -- for CLK/8 prescaling

  ICR1 = 36864; // sets PWM to repeat pulse every 20.0ms
  TCCR1A = (1<<COM1A1) | (1<<COM1B1) | (1<<WGM11);
  TCCR1B = (1<<WGM13) | (1<<WGM12) | (1<<CS11);

  // each count is 8/14745600 = 0.5425us.
  // so 1.0ms = 1843.2
  //    1.5ms = 2764.8
  //    2.0ms = 3686.4
  //   20.0ms = 36864

int main() {

  // init LCD
  FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);

  // set PB2,PB3 as output
  DDRB |= (1<<PB1) | (1<<PB2);

  // init PWM

  uint16_t posx = PWM_START;
  uint16_t posy = PWM_START;

  DDRC &= ~(1<<PC5); // input pins 28 & 27
  DDRC &= ~(1<<PC4);

  DDRC &= ~(1<<PC3); // input pins 26 & 25
  DDRC &= ~(1<<PC2);

  PORTC |= (1<<PC5); // internal pull up resistors pins
  PORTC |= (1<<PC4); 
  PORTC |= (1<<PC3); 
  PORTC |= (1<<PC2);

  while(1) {
    // Print the current x servo position to the LCD.
    fprintf_P(&lcd_stream, PSTR("x position: %d "), posx);

    // Print the current y servo position to the LCD.
    fprintf_P(&lcd_stream, PSTR("y position: %d "), posy);

   if (!(PINC & (1<<PC5)))

   posx+= 6;

   if (!(PINC & (1<<PC4)))

   posx-= 6;

   if (!(PINC & (1<<PC3)))

   posy+= 6;

   if (!(PINC & (1<<PC2)))

   posy-= 6;

    // bounds checking
    if(posx > PWM_MAX) posx = PWM_MAX;
    if(posx < PWM_MIN) posx = PWM_MIN;

    if(posy > PWM_MAX) posy = PWM_MAX;
    if(posy < PWM_MIN) posy = PWM_MIN;


  return 0;

The LCD is pretty useful here to "map out" servo positions. For example, you could run the program to see the values of posx and posy, then re-write the program adding something like -

   if (!(PINB & (1<<PB5))) {// center both

    posx = 2820; // best center position for horizontal axis

    posy = 2815; // best center position for vertical axis


that would add another pushbutton that by pressing will set the positions of the servos to your predetermined positions.

Using 3 for "posx+= 3;",etc. gives my servos a slow, steady travel - and I don't need a delay. Holding a button down causes the servo to turn until it reaches PMW_MAX or PMW_MIN. Quick, short presses of a button cause a servo to move in small increments.

Maybe your speed of 25 was a bit too fast, and maybe the delay has now slowed the reaction of the button press? I may be wrong though. However, I did not find the need for a pushbutton delay for switch debounce in my code.


Post a Reply

Please log in to post a reply.

Did you know that you can use printf and scanf functions to talk to your computer from your USB NerdKit? Learn more...