|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 |
- /*
- * softServo.c:
- * Provide N channels of software driven PWM suitable for RC
- * servo motors.
- * Copyright (c) 2012 Gordon Henderson
- ***********************************************************************
- * This file is part of wiringPi:
- * https://projects.drogon.net/raspberry-pi/wiringpi/
- *
- * wiringPi is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * wiringPi is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with wiringPi.
- * If not, see <http://www.gnu.org/licenses/>.
- ***********************************************************************
- */
-
- //#include <stdio.h>
- #include <string.h>
- #include <time.h>
- #include <sys/time.h>
- #include <pthread.h>
-
- #include "wiringPi.h"
- #include "softServo.h"
-
- // RC Servo motors are a bit of an oddity - designed in the days when
- // radio control was experimental and people were tryin to make
- // things as simple as possible as it was all very expensive...
- //
- // So... To drive an RC Servo motor, you need to send it a modified PWM
- // signal - it needs anything from 1ms to 2ms - with 1ms meaning
- // to move the server fully left, and 2ms meaning to move it fully
- // right. Then you need a long gap before sending the next pulse.
- // The reason for this is that you send a multiplexed stream of these
- // pulses up the radio signal into the reciever which de-multiplexes
- // them into the signals for each individual servo. Typically there
- // might be 8 channels, so you need at least 8 "slots" of 2mS pulses
- // meaning the entire frame must fit into a 16mS slot - which would
- // then be repeated...
- //
- // In practice we have a total slot width of about 20mS - so we're sending 50
- // updates per second to each servo.
- //
- // In this code, we don't need to be too fussy about the gap as we're not doing
- // the multipexing, but it does need to be at least 10mS, and preferably 16
- // from what I've been able to determine.
-
- // WARNING:
- // This code is really experimental. It was written in response to some people
- // asking for a servo driver, however while it works, there is too much
- // jitter to successfully drive a small servo - I have tried it with a micro
- // servo and it worked, but the servo ran hot due to the jitter in the signal
- // being sent to it.
- //
- // If you want servo control for the Pi, then use the servoblaster kernel
- // module.
-
- #define MAX_SERVOS 8
-
- static int pinMap [MAX_SERVOS] ; // Keep track of our pins
- static int pulseWidth [MAX_SERVOS] ; // microseconds
-
-
- /*
- * softServoThread:
- * Thread to do the actual Servo PWM output
- *********************************************************************************
- */
-
- static PI_THREAD (softServoThread)
- {
- register int i, j, k, m, tmp ;
- int lastDelay, pin, servo ;
-
- int myDelays [MAX_SERVOS] ;
- int myPins [MAX_SERVOS] ;
-
- struct timeval tNow, tStart, tPeriod, tGap, tTotal ;
- struct timespec tNs ;
-
- tTotal.tv_sec = 0 ;
- tTotal.tv_usec = 8000 ;
-
- piHiPri (50) ;
-
- for (;;)
- {
- gettimeofday (&tStart, NULL) ;
-
- memcpy (myDelays, pulseWidth, sizeof (myDelays)) ;
- memcpy (myPins, pinMap, sizeof (myPins)) ;
-
- // Sort the delays (& pins), shortest first
-
- for (m = MAX_SERVOS / 2 ; m > 0 ; m /= 2 )
- for (j = m ; j < MAX_SERVOS ; ++j)
- for (i = j - m ; i >= 0 ; i -= m)
- {
- k = i + m ;
- if (myDelays [k] >= myDelays [i])
- break ;
- else // Swap
- {
- tmp = myDelays [i] ; myDelays [i] = myDelays [k] ; myDelays [k] = tmp ;
- tmp = myPins [i] ; myPins [i] = myPins [k] ; myPins [k] = tmp ;
- }
- }
-
- // All on
-
- lastDelay = 0 ;
- for (servo = 0 ; servo < MAX_SERVOS ; ++servo)
- {
- if ((pin = myPins [servo]) == -1)
- continue ;
-
- digitalWrite (pin, HIGH) ;
- myDelays [servo] = myDelays [servo] - lastDelay ;
- lastDelay += myDelays [servo] ;
- }
-
- // Now loop, turning them all off as required
-
- for (servo = 0 ; servo < MAX_SERVOS ; ++servo)
- {
- if ((pin = myPins [servo]) == -1)
- continue ;
-
- delayMicroseconds (myDelays [servo]) ;
- digitalWrite (pin, LOW) ;
- }
-
- // Wait until the end of an 8mS time-slot
-
- gettimeofday (&tNow, NULL) ;
- timersub (&tNow, &tStart, &tPeriod) ;
- timersub (&tTotal, &tPeriod, &tGap) ;
- tNs.tv_sec = tGap.tv_sec ;
- tNs.tv_nsec = tGap.tv_usec * 1000 ;
- nanosleep (&tNs, NULL) ;
- }
-
- return NULL ;
- }
-
-
- /*
- * softServoWrite:
- * Write a Servo value to the given pin
- *********************************************************************************
- */
-
- void softServoWrite (int servoPin, int value)
- {
- int servo ;
-
- servoPin &= 63 ;
-
- if (value < -250)
- value = -250 ;
- else if (value > 1250)
- value = 1250 ;
-
- for (servo = 0 ; servo < MAX_SERVOS ; ++servo)
- if (pinMap [servo] == servoPin)
- pulseWidth [servo] = value + 1000 ; // uS
- }
-
-
- /*
- * softServoSetup:
- * Setup the software servo system
- *********************************************************************************
- */
-
- int softServoSetup (int p0, int p1, int p2, int p3, int p4, int p5, int p6, int p7)
- {
- int servo ;
-
- if (p0 != -1) { pinMode (p0, OUTPUT) ; digitalWrite (p0, LOW) ; }
- if (p1 != -1) { pinMode (p1, OUTPUT) ; digitalWrite (p1, LOW) ; }
- if (p2 != -1) { pinMode (p2, OUTPUT) ; digitalWrite (p2, LOW) ; }
- if (p3 != -1) { pinMode (p3, OUTPUT) ; digitalWrite (p3, LOW) ; }
- if (p4 != -1) { pinMode (p4, OUTPUT) ; digitalWrite (p4, LOW) ; }
- if (p5 != -1) { pinMode (p5, OUTPUT) ; digitalWrite (p5, LOW) ; }
- if (p6 != -1) { pinMode (p6, OUTPUT) ; digitalWrite (p6, LOW) ; }
- if (p7 != -1) { pinMode (p7, OUTPUT) ; digitalWrite (p7, LOW) ; }
-
- pinMap [0] = p0 ;
- pinMap [1] = p1 ;
- pinMap [2] = p2 ;
- pinMap [3] = p3 ;
- pinMap [4] = p4 ;
- pinMap [5] = p5 ;
- pinMap [6] = p6 ;
- pinMap [7] = p7 ;
-
- for (servo = 0 ; servo < MAX_SERVOS ; ++servo)
- pulseWidth [servo] = 1500 ; // Mid point
-
- return piThreadCreate (softServoThread) ;
- }
|