softServo.c 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. /*
  2. * softServo.c:
  3. * Provide N channels of software driven PWM suitable for RC
  4. * servo motors.
  5. * Copyright (c) 2012 Gordon Henderson
  6. ***********************************************************************
  7. * This file is part of wiringPi:
  8. * https://projects.drogon.net/raspberry-pi/wiringpi/
  9. *
  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.
  14. *
  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.
  19. *
  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. ***********************************************************************
  24. */
  25. //#include <stdio.h>
  26. #include <string.h>
  27. #include <time.h>
  28. #include <sys/time.h>
  29. #include <pthread.h>
  30. #include "wiringPi.h"
  31. #include "softServo.h"
  32. // RC Servo motors are a bit of an oddity - designed in the days when
  33. // radio control was experimental and people were tryin to make
  34. // things as simple as possible as it was all very expensive...
  35. //
  36. // So... To drive an RC Servo motor, you need to send it a modified PWM
  37. // signal - it needs anything from 1ms to 2ms - with 1ms meaning
  38. // to move the server fully left, and 2ms meaning to move it fully
  39. // right. Then you need a long gap before sending the next pulse.
  40. // The reason for this is that you send a multiplexed stream of these
  41. // pulses up the radio signal into the reciever which de-multiplexes
  42. // them into the signals for each individual servo. Typically there
  43. // might be 8 channels, so you need at least 8 "slots" of 2mS pulses
  44. // meaning the entire frame must fit into a 16mS slot - which would
  45. // then be repeated...
  46. //
  47. // In practice we have a total slot width of about 20mS - so we're sending 50
  48. // updates per second to each servo.
  49. //
  50. // In this code, we don't need to be too fussy about the gap as we're not doing
  51. // the multipexing, but it does need to be at least 10mS, and preferably 16
  52. // from what I've been able to determine.
  53. #define MAX_SERVOS 8
  54. static int pinMap [MAX_SERVOS] ; // Keep track of our pins
  55. static int pulseWidth [MAX_SERVOS] ; // microseconds
  56. /*
  57. * softServoThread:
  58. * Thread to do the actual Servo PWM output
  59. *********************************************************************************
  60. */
  61. static PI_THREAD (softServoThread)
  62. {
  63. register int i, j, k, m, tmp ;
  64. int lastDelay, pin, servo ;
  65. int myDelays [MAX_SERVOS] ;
  66. int myPins [MAX_SERVOS] ;
  67. struct timeval tNow, tStart, tPeriod, tGap, tTotal ;
  68. struct timespec tNs ;
  69. tTotal.tv_sec = 0 ;
  70. tTotal.tv_usec = 8000 ;
  71. piHiPri (50) ;
  72. for (;;)
  73. {
  74. gettimeofday (&tStart, NULL) ;
  75. memcpy (myDelays, pulseWidth, sizeof (myDelays)) ;
  76. memcpy (myPins, pinMap, sizeof (myPins)) ;
  77. // Sort the delays (& pins), shortest first
  78. for (m = MAX_SERVOS / 2 ; m > 0 ; m /= 2 )
  79. for (j = m ; j < MAX_SERVOS ; ++j)
  80. for (i = j - m ; i >= 0 ; i -= m)
  81. {
  82. k = i + m ;
  83. if (myDelays [k] >= myDelays [i])
  84. break ;
  85. else // Swap
  86. {
  87. tmp = myDelays [i] ; myDelays [i] = myDelays [k] ; myDelays [k] = tmp ;
  88. tmp = myPins [i] ; myPins [i] = myPins [k] ; myPins [k] = tmp ;
  89. }
  90. }
  91. // All on
  92. lastDelay = 0 ;
  93. for (servo = 0 ; servo < MAX_SERVOS ; ++servo)
  94. {
  95. if ((pin = myPins [servo]) == -1)
  96. continue ;
  97. digitalWrite (pin, HIGH) ;
  98. myDelays [servo] = myDelays [servo] - lastDelay ;
  99. lastDelay += myDelays [servo] ;
  100. }
  101. // Now loop, turning them all off as required
  102. for (servo = 0 ; servo < MAX_SERVOS ; ++servo)
  103. {
  104. if ((pin = myPins [servo]) == -1)
  105. continue ;
  106. delayMicroseconds (myDelays [servo]) ;
  107. digitalWrite (pin, LOW) ;
  108. }
  109. // Wait until the end of an 8mS time-slot
  110. gettimeofday (&tNow, NULL) ;
  111. timersub (&tNow, &tStart, &tPeriod) ;
  112. timersub (&tTotal, &tPeriod, &tGap) ;
  113. tNs.tv_sec = tGap.tv_sec ;
  114. tNs.tv_nsec = tGap.tv_usec * 1000 ;
  115. nanosleep (&tNs, NULL) ;
  116. }
  117. return NULL ;
  118. }
  119. /*
  120. * softServoWrite:
  121. * Write a Servo value to the given pin
  122. *********************************************************************************
  123. */
  124. void softServoWrite (int servoPin, int value)
  125. {
  126. int servo ;
  127. servoPin &= 63 ;
  128. /**/ if (value < -250)
  129. value = -250 ;
  130. else if (value > 1250)
  131. value = 1250 ;
  132. for (servo = 0 ; servo < MAX_SERVOS ; ++servo)
  133. if (pinMap [servo] == servoPin)
  134. pulseWidth [servo] = value + 1000 ; // uS
  135. }
  136. /*
  137. * softServoSetup:
  138. * Setup the software servo system
  139. *********************************************************************************
  140. */
  141. int softServoSetup (int p0, int p1, int p2, int p3, int p4, int p5, int p6, int p7)
  142. {
  143. int servo ;
  144. if (p0 != -1) { pinMode (p0, OUTPUT) ; digitalWrite (p0, LOW) ; }
  145. if (p1 != -1) { pinMode (p1, OUTPUT) ; digitalWrite (p1, LOW) ; }
  146. if (p2 != -1) { pinMode (p2, OUTPUT) ; digitalWrite (p2, LOW) ; }
  147. if (p3 != -1) { pinMode (p3, OUTPUT) ; digitalWrite (p3, LOW) ; }
  148. if (p4 != -1) { pinMode (p4, OUTPUT) ; digitalWrite (p4, LOW) ; }
  149. if (p5 != -1) { pinMode (p5, OUTPUT) ; digitalWrite (p5, LOW) ; }
  150. if (p6 != -1) { pinMode (p6, OUTPUT) ; digitalWrite (p6, LOW) ; }
  151. if (p7 != -1) { pinMode (p7, OUTPUT) ; digitalWrite (p7, LOW) ; }
  152. pinMap [0] = p0 ;
  153. pinMap [1] = p1 ;
  154. pinMap [2] = p2 ;
  155. pinMap [3] = p3 ;
  156. pinMap [4] = p4 ;
  157. pinMap [5] = p5 ;
  158. pinMap [6] = p6 ;
  159. pinMap [7] = p7 ;
  160. for (servo = 0 ; servo < MAX_SERVOS ; ++servo)
  161. pulseWidth [servo] = 1500 ; // Mid point
  162. return piThreadCreate (softServoThread) ;
  163. }