mirror of
https://github.com/clockworkpi/WiringPi.git
synced 2025-12-12 16:08:49 +01:00
delayMicrosecondsHard re-written - again. Added a serialRead example program, and added in the okLed to the examples too. Updated/checked some of the GPIO/PWM code. Added in some experimental servo and tone generating code and and example or 2. Tweaks to the gpio command to correctly load the I2C modules too.
203 lines
5.8 KiB
C
203 lines
5.8 KiB
C
/*
|
|
* 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.
|
|
|
|
|
|
#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) ;
|
|
}
|