1
0
mirror of https://github.com/richardghirst/PiBits.git synced 2024-11-28 12:24:11 +01:00

Add idle_timeout module parameter which automatically turns a servo

control line off some period after the last command.
This commit is contained in:
Richard Hirst 2012-12-15 13:29:30 +00:00
parent e8bdd9054b
commit 1a3f7c10a0
3 changed files with 63 additions and 29 deletions

View File

@ -27,14 +27,14 @@ is currently set. For example, after starting the driver and running the
previous command, you would see:
pi@raspberrypi ~ $ cat /dev/servoblaster
0=0
1=0
2=0
3=120
4=0
5=0
6=0
7=0
0 0
1 0
2 0
3 120
4 0
5 0
6 0
7 0
pi@raspberrypi ~ $
When the driver is first loaded the GPIO pins are configure to be outputs, and
@ -83,31 +83,11 @@ directory will also create the necessary files. Further to this, running
"make install_autostart" will create those files, plus perform the necessary
changes to make servoblaster be automatically loaded at boot.
Note that there are three different ways of referring to a specific servo
control pin: by servo number, by GPIO pin on the processor, or by pin number
on the P1 header on the Pi itself. The following table shows the mapping
between these number schemes:
Servo GPIO number P1 Pin
0 4 7
1 17 11
2 18 12
3 21 13
4 22 15
5 23 16
6 24 18
7 25 22
The driver uses DMA channel 0, and PWM channel 1. It makes no attempt to
protect against other code using those peripherals. It sets the relevant GPIO
pins to be outputs when the driver is loaded, so please ensure that you are not
driving those pins externally.
ServoBlaster currently uses the PWM hardware for timing purposes, so cannot be
used at the same time as PWM audio on the 3.5mm jack, and if you play PWM audio
after loading servoblaster.ko, you'll need to unload and reload servoblaster.ko
in order to recover.
I would of course recommend some buffering between the GPIO outputs and the
servo controls, to protect the Pi. That said, I'm living dangerously and doing
without :-) If you just want to experiment with a small servo you can probably
@ -121,6 +101,21 @@ on the wiki (http://elinux.org/RPi_Kernel_Compilation) to compile the kernel,
then edit the servoblaster Makefile to point at your kernel tree, then build
servoblaster.
Some people have requested that a servo output turns off automatically if no
new pulse width has been requested recently, and I've had two reports of
servos overheating when driven for long periods of time. To support this
request, ServoBlaster implements an idle timeout which can be specified at
module load time. The value is specified in milliseconds, so if you want
to drive your servos for 2 seconds following each new width request you would
do this:
sudo insmod ./servoblaster.ko idle_timeout=2000
Typical small servos take a few 100 milliseconds to rotate from one extreme
to the other, so for small values of idle_timeout you might find the control
pulse is turned off before your servo has reached the required position.
idle_timeout defaults to 0, which disables the feature.
NOTE: There is some doubt over how to configure the PWM clock at present. For
me the clock is 600KHz, which leads to a tick lenght of 10us. However at least
one person has reported that the pulses are out by about a factor of about 8,
@ -152,5 +147,15 @@ As of August 30th 2012 the servoblaster.ko module is built against a 2.6.27+
kernel source from github.
Related projects:
Ville has written a simple Qt wrapper for servoblaster, which you can find
here: https://github.com/vranki/kittinger/blob/master/servocontrol.cpp & .h
Todd wrote a nice script to provide a simple user interface to control your
servos, see his sbcontrol.sh script here:
http://www.raspberrypi.org/phpBB3/viewtopic.php?f=37&t=15011&start=25#p187675
Richard Hirst <richardghirst@gmail.com> August 2012

View File

@ -48,6 +48,7 @@
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/timer.h>
#include <mach/platform.h>
#include <asm/uaccess.h>
#include <mach/dma.h>
@ -123,6 +124,11 @@ static uint8_t servo2gpio[] = {
};
#define NUM_SERVOS (sizeof(servo2gpio)/sizeof(servo2gpio[0]))
// Per-servo timeouts, so we can shut down a servo output after some period
// without a new command - some people want this because the report servos
// overheating after a time.
static struct timer_list idle_timer[NUM_SERVOS];
// This struct is used to store all temporary data associated with a given
// open() of /dev/servoblaster
struct private_data
@ -161,12 +167,19 @@ static struct cdev my_cdev;
static int my_major;
static int cycle_ticks = 2000;
static int tick_scale = 6;
static int idle_timeout = 0;
// This records the written count values so we can display them on a read()
// call. Cannot derive data directly from DMA control blocks as current
// algorithm has a special case for a count of zero.
static int servo_pos[NUM_SERVOS] = { 0 };
static void servo_timeout(unsigned long servo)
{
// Clear GPIO output next time round
ctl->cb[servo*4+0].dst = ((GPIO_BASE + GPCLR0*4) & 0x00ffffff) | 0x7e000000;
}
// Wait until we're not processing the given servo (actually wait until
// we are not processing the low period of the previous servo, or the
// high period of this one).
@ -213,8 +226,17 @@ int init_module(void)
return res;
}
for (i = 0; i < NUM_SERVOS; i++)
setup_timer(idle_timer + i, servo_timeout, i);
if (idle_timeout && idle_timeout < 20) {
printk(KERN_WARNING "ServoBlaster: Increased idle timeout to minimum of 20ms\n");
idle_timeout = 20;
}
ctldatabase = __get_free_pages(GFP_KERNEL, 0);
printk(KERN_INFO "ServoBlaster: Control page is at 0x%lx, cycle_ticks %d, tick_scale %d\n", ctldatabase, cycle_ticks, tick_scale);
printk(KERN_INFO "ServoBlaster: Control page is at 0x%lx, cycle_ticks %d, tick_scale %d, idle_timeout %d\n",
ctldatabase, cycle_ticks, tick_scale, idle_timeout);
if (ctldatabase == 0) {
printk(KERN_WARNING "ServoBlaster: alloc_pages failed\n");
cdev_del(&my_cdev);
@ -330,6 +352,7 @@ void cleanup_module(void)
// Take care to stop servos with outputs low, so we don't get
// spurious movement on module unload
for (servo = 0; servo < NUM_SERVOS; servo++) {
del_timer(idle_timer + servo);
// Wait until we're not driving this servo
if (wait_for_servo(servo))
break;
@ -411,6 +434,9 @@ static int set_servo(int servo, int cnt)
if (wait_for_servo(servo))
return -EINTR;
if (idle_timeout)
mod_timer(idle_timer + servo, jiffies + msecs_to_jiffies(idle_timeout));
// Normally, the first GPIO transfer sets the output, while the second
// clears it after a delay. For the special case of a delay of 0, we
// ensure that the first GPIO transfer also clears the output.
@ -511,3 +537,6 @@ MODULE_PARM_DESC(cycle_ticks, "number of ticks per cycle, max pulse is cycle_tic
module_param(tick_scale, int, 0);
MODULE_PARM_DESC(tick_scale, "scale the tick length, 6 should be 10us");
module_param(idle_timeout, int, 0);
MODULE_PARM_DESC(idle_timeout, "Idle timeout, after which we turn off a servo output (ms)");

Binary file not shown.