3 * Provide N channels of software driven PWM suitable for RC
5 * Copyright (c) 2012 Gordon Henderson
6 ***********************************************************************
7 * This file is part of wiringPi:
8 * https://projects.drogon.net/raspberry-pi/wiringpi/
10 * wiringPi is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU Lesser General Public License as
12 * published by the Free Software Foundation, either version 3 of the
13 * License, or (at your option) any later version.
15 * wiringPi is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with wiringPi.
22 * If not, see <http://www.gnu.org/licenses/>.
23 ***********************************************************************
33 #include "softServo.h"
35 // RC Servo motors are a bit of an oddity - designed in the days when
36 // radio control was experimental and people were tryin to make
37 // things as simple as possible as it was all very expensive...
39 // So... To drive an RC Servo motor, you need to send it a modified PWM
40 // signal - it needs anything from 1ms to 2ms - with 1ms meaning
41 // to move the server fully left, and 2ms meaning to move it fully
42 // right. Then you need a long gap before sending the next pulse.
43 // The reason for this is that you send a multiplexed stream of these
44 // pulses up the radio signal into the reciever which de-multiplexes
45 // them into the signals for each individual servo. Typically there
46 // might be 8 channels, so you need at least 8 "slots" of 2mS pulses
47 // meaning the entire frame must fit into a 16mS slot - which would
48 // then be repeated...
50 // In practice we have a total slot width of about 20mS - so we're sending 50
51 // updates per second to each servo.
53 // In this code, we don't need to be too fussy about the gap as we're not doing
54 // the multipexing, but it does need to be at least 10mS, and preferably 16
55 // from what I've been able to determine.
60 static int pinMap [MAX_SERVOS] ; // Keep track of our pins
61 static int pulseWidth [MAX_SERVOS] ; // microseconds
66 * Thread to do the actual Servo PWM output
67 *********************************************************************************
70 static PI_THREAD (softServoThread)
72 register int i, j, k, m, tmp ;
73 int lastDelay, pin, servo ;
75 int myDelays [MAX_SERVOS] ;
76 int myPins [MAX_SERVOS] ;
78 struct timeval tNow, tStart, tPeriod, tGap, tTotal ;
82 tTotal.tv_usec = 8000 ;
88 gettimeofday (&tStart, NULL) ;
90 memcpy (myDelays, pulseWidth, sizeof (myDelays)) ;
91 memcpy (myPins, pinMap, sizeof (myPins)) ;
93 // Sort the delays (& pins), shortest first
95 for (m = MAX_SERVOS / 2 ; m > 0 ; m /= 2 )
96 for (j = m ; j < MAX_SERVOS ; ++j)
97 for (i = j - m ; i >= 0 ; i -= m)
100 if (myDelays [k] >= myDelays [i])
104 tmp = myDelays [i] ; myDelays [i] = myDelays [k] ; myDelays [k] = tmp ;
105 tmp = myPins [i] ; myPins [i] = myPins [k] ; myPins [k] = tmp ;
112 for (servo = 0 ; servo < MAX_SERVOS ; ++servo)
114 if ((pin = myPins [servo]) == -1)
117 digitalWrite (pin, HIGH) ;
118 myDelays [servo] = myDelays [servo] - lastDelay ;
119 lastDelay += myDelays [servo] ;
122 // Now loop, turning them all off as required
124 for (servo = 0 ; servo < MAX_SERVOS ; ++servo)
126 if ((pin = myPins [servo]) == -1)
129 delayMicroseconds (myDelays [servo]) ;
130 digitalWrite (pin, LOW) ;
133 // Wait until the end of an 8mS time-slot
135 gettimeofday (&tNow, NULL) ;
136 timersub (&tNow, &tStart, &tPeriod) ;
137 timersub (&tTotal, &tPeriod, &tGap) ;
138 tNs.tv_sec = tGap.tv_sec ;
139 tNs.tv_nsec = tGap.tv_usec * 1000 ;
140 nanosleep (&tNs, NULL) ;
149 * Write a Servo value to the given pin
150 *********************************************************************************
153 void softServoWrite (int servoPin, int value)
159 /**/ if (value < -250)
161 else if (value > 1250)
164 for (servo = 0 ; servo < MAX_SERVOS ; ++servo)
165 if (pinMap [servo] == servoPin)
166 pulseWidth [servo] = value + 1000 ; // uS
172 * Setup the software servo system
173 *********************************************************************************
176 int softServoSetup (int p0, int p1, int p2, int p3, int p4, int p5, int p6, int p7)
180 if (p0 != -1) { pinMode (p0, OUTPUT) ; digitalWrite (p0, LOW) ; }
181 if (p1 != -1) { pinMode (p1, OUTPUT) ; digitalWrite (p1, LOW) ; }
182 if (p2 != -1) { pinMode (p2, OUTPUT) ; digitalWrite (p2, LOW) ; }
183 if (p3 != -1) { pinMode (p3, OUTPUT) ; digitalWrite (p3, LOW) ; }
184 if (p4 != -1) { pinMode (p4, OUTPUT) ; digitalWrite (p4, LOW) ; }
185 if (p5 != -1) { pinMode (p5, OUTPUT) ; digitalWrite (p5, LOW) ; }
186 if (p6 != -1) { pinMode (p6, OUTPUT) ; digitalWrite (p6, LOW) ; }
187 if (p7 != -1) { pinMode (p7, OUTPUT) ; digitalWrite (p7, LOW) ; }
198 for (servo = 0 ; servo < MAX_SERVOS ; ++servo)
199 pulseWidth [servo] = 1500 ; // Mid point
201 return piThreadCreate (softServoThread) ;