>I need to do some timing code - for some basic kind of sequencing.. >Since ive not written this kind of stuff before, i'm unsure as to the best app >roach for accurate timing. > >I've played around with using signals for this - with setitimer(..) and then c >atching the signal, but it doesnt look too good - it can be 1000s of usecs out >.. > >What approaches have people used, and how successful were they?
poll(2) on /dev/rtc. You will need to be root, or have CAP_RESOURCE, to set the frequency of the clock to a useful value, and you will need to run SCHED_FIFO to not have the kernel scheduler mess things up. ardour and softwerk (which doesn't compile right now) both have code for the RTC. In fact, I'll include ardour's below - its very simple really. Any parts that are not clear are almost certainly not necessary :) alternatively, use the ALSA sequencer and/or timer APIs. --p /* Copyright (C) 2001 Paul Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. $Id: rtc.h,v 1.1 2001/11/24 00:15:23 pbd Exp $ */ #ifndef __ardour_rtc_h__ #define __ardour_rtc_h__ #include <pbd/thread.h> #include <sigc++/signal_system.h> class RealTimeClock : public QMThread, public SigC::Object { public: RealTimeClock (); virtual ~RealTimeClock(); bool set_interval (unsigned int usecs); SigC::Signal0<void> tick; private: int rtc_fd; int current_hz; bool rtc_running; bool start_periodic_interrupts (); bool stop_periodic_interrupts (); static void *start_thread (void *); void *do_work (); }; #endif /* __ardour_rtc_h__ */ /* Copyright (C) 2001 Paul Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. $Id: rtc.cc,v 1.2 2002/04/10 17:42:57 pbd Exp $ */ #include <stdio.h> #include <linux/rtc.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <poll.h> #include <fcntl.h> #include <math.h> #include <sys/ioctl.h> #include <asm/timex.h> #include <ardour/ardour.h> #include <ardour/rtc.h> using namespace ARDOUR; RealTimeClock::RealTimeClock () : QMThread ("realtime clock", start_thread, this, true, 8) { if ((rtc_fd = open ("/dev/rtc", O_RDONLY)) < 0) { error << "RealTimeClock: cannot open /dev/rtc (" << strerror (errno) << ')' << endmsg; throw failed_constructor(); } if (fcntl (rtc_fd, F_SETOWN, getpid()) < 0) { error << "RealTimeClock: cannot set ownership of /dev/rtc (" << strerror (errno) << ')' << endmsg; throw failed_constructor(); } rtc_running = false; current_hz = 0; } RealTimeClock::~RealTimeClock () { stop_periodic_interrupts (); close (rtc_fd); } void * RealTimeClock::start_thread (void *arg) { RealTimeClock *rtc = (RealTimeClock *) arg; return rtc->main (); } void * RealTimeClock::do_work () { unsigned long rtc_data; start_periodic_interrupts (); while (!work_no_more()) { struct pollfd pfd; pfd.fd = rtc_fd; pfd.events = POLLIN | POLLERR; if (poll (&pfd, 1, 100000) < 0) { if (errno == EINTR) { // this happens mostly when run // under gdb, or when exiting due to a signal continue; } error << "RealTimeClock: poll call failed (" << strerror (errno) << ')' << endmsg; return (void *) -1; } read (rtc_fd, &rtc_data, sizeof (rtc_data)); tick (); } return 0; } bool RealTimeClock::set_interval (guint32 usecs) { int req_hz = (int) floor ((1000000.0/usecs)); int hz = 2; bool restart; hz = 2; while (hz < req_hz) { hz *= 2; } if (hz == current_hz) { return true; } restart = stop_periodic_interrupts (); if (ioctl(rtc_fd, RTC_IRQP_SET, hz) < 0) { error << "RealTimeClock: cannot set periodic interval (" << strerror (errno) << ')' << endmsg; return -1; } current_hz = hz; if (restart) { start_periodic_interrupts (); } return true; } bool RealTimeClock::start_periodic_interrupts () { if (!rtc_running) { if (ioctl (rtc_fd, RTC_PIE_ON, 0) < 0) { error << "RealTimeClock: cannot start periodic interrupts (" << strerror (errno) << ')' << endmsg; return false; } rtc_running = true; } return rtc_running; } bool RealTimeClock::stop_periodic_interrupts () { bool was_running = rtc_running; if (rtc_running) { if (ioctl (rtc_fd, RTC_PIE_OFF, 0) < 0) { error << "RealTimeClock: cannot stop periodic interrupts (" << strerror (errno) << ')' << endmsg; return rtc_running; } rtc_running = false; } return was_running; }