--- /dev/null
+CC = gcc
+AR = ar
+RANLIB = ranlib
+SIZE = size
+
+CFLAGS = -O3 -Wall
+
+all: libpigpio.a checklib demolib pig2vcd pigpiod pigs
+
+checklib: checklib.o libpigpio.a
+ $(CC) -o checklib checklib.c -L. -lpigpio -lpthread -lrt
+
+demolib: demolib.o libpigpio.a
+ $(CC) -o demolib demolib.c -L. -lpigpio -lpthread -lrt
+
+pig2vcd: pig2vcd.o
+ $(CC) -o pig2vcd pig2vcd.c
+
+pigpiod: pigpiod.o libpigpio.a
+ $(CC) -o pigpiod pigpiod.c -L. -lpigpio -lpthread -lrt
+
+pigs: pigs.o command.o
+ $(CC) -o pigs pigs.c command.c
+
+.c.o:
+ $(CC) -c $(CFLAGS) $<
+
+clean:
+ rm -f *.o *.i *.s *~ libpigpio.a checklib demolib pigpiod pigs pig2vcd
+
+install: $(LIB)
+ sudo install -m 0755 -d /usr/local/bin
+ sudo install -m 0755 -d /usr/local/include
+ sudo install -m 0755 -d /usr/local/lib
+ sudo install -m 0755 pig2vcd /usr/local/bin
+ sudo install -m 0755 pigpiod /usr/local/bin
+ sudo install -m 0755 pigs /usr/local/bin
+ sudo install -m 0644 pigpio.h /usr/local/include
+ sudo install -m 0644 libpigpio.a /usr/local/lib
+
+uninstall:
+ sudo rm -f /usr/local/bin/pig2vcd
+ sudo rm -f /usr/local/bin/pigpiod
+ sudo rm -f /usr/local/bin/pigs
+ sudo rm -f /usr/local/include/pigpio.h
+ sudo rm -f /usr/local/lib/libpigpio.a
+
+LIB = libpigpio.a
+OBJ = pigpio.o command.o
+
+$(LIB): $(OBJ)
+ $(AR) rcs $(LIB) $(OBJ)
+ $(RANLIB) $(LIB)
+ $(SIZE) $(LIB)
+
+
+# DO NOT DELETE
+
+checklib.o: checklib.c pigpio.h
+demolib.o: demolib.c pigpio.h
+pig2vcd: pigpio.h
+pigpiod: pigpiod.c pigpio.h
+pigs: pigs.c command.c pigpio.h command.h
--- /dev/null
+INSTALL
+
+Extract the archive to a directory.
+
+IN THAT DIRECTORY
+
+Enter the following two commands (in this order)
+
+make
+make install
+
+This will install:
+ the library (libpigpio.a) in /usr/local/lib
+ the header file (pigpio.h) in /usr/local/include
+ the daemon (pigpiod) in /usr/local/bin
+ the socket interface (pigs) in /usr/local/bin
+ the utility pig2vcd in /usr/local/bin
+
+TEST
+
+To test the library do
+
+sudo ./checklib
+
+checklib.c, demolib.c, pig2vcd.c, pigpiod.c, and pigs.c show examples
+of interfacing with the library.
+
+DAEMON
+
+To launch the daemon do
+
+sudo pigpiod (pigpiod -? for options)
+
+Once the daemon is launched the socket and fifo interfaces will be
+available.
+
+When the library starts it locks
+
+/var/run/pigpio.pid
+
+The file should be deleted when the library terminates.
+
+SOCKET INTERFACE
+
+Use pigs for the socket interface (pigs help for help).
+
+FIFO INTERFACE
+
+The fifo interface accepts commands written to /dev/pigpio.
+
+Results are read from /dev/pigout.
+
+Errors are output on /dev/pigerr.
+
+To test the fifo interface perhaps do
+
+cat /dev/pigout &
+cat /dev/pigerr &
+
+echo "help" >/dev/pigpio
+
+STOP DAEMON
+
+To stop the daemon
+
+sudo killall pigpiod
+
--- /dev/null
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <http://unlicense.org/>
+
--- /dev/null
+/*
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <http://unlicense.org/>
+*/
+
+/*
+This version is for pigpio version 3+
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include "pigpio.h"
+
+#define GREENLED 16
+#define SDDET 47
+#define SDCLK 48
+
+int test =1;
+int passes=0;
+int expect=0;
+
+struct timeval libInitTime;
+
+int GPIO=4;
+
+unsigned inited, count, onMicros, offMicros;
+
+void tick(void)
+{
+ /* count ticks
+ */
+
+ static struct timeval lastTime;
+ struct tm tmp;
+ struct timeval nowTime;
+ char buf[32];
+
+ gettimeofday(&nowTime, NULL);
+
+ localtime_r(&nowTime.tv_sec, &tmp);
+ strftime(buf, sizeof(buf), "%F@%T", &tmp);
+
+ printf("%s.%03d\n", buf, (int)nowTime.tv_usec/1000);
+
+ /*timersub(&nowTime, &lastTime, &diffTime);*/
+
+ lastTime = nowTime;
+
+ if (inited)
+ {
+ count++;
+ }
+ else
+ {
+ count = 1;
+
+ gettimeofday(&lastTime, NULL);
+
+ inited = 1;
+ }
+}
+
+void tickEx(void * userdata)
+{
+}
+
+void alert(int gpio, int level, uint32_t tick)
+{
+ /* accumulate number of level changes and average time gpio
+ was on and off. Hopefully the ratio should reflect the
+ selected pulsewidth.
+ */
+
+ static uint32_t lastTick;
+
+ uint32_t diffTick;
+
+ if (inited)
+ {
+ count++;
+
+ diffTick = tick - lastTick;
+
+ if (level == 0)
+ {
+ /* elapsed time was on */
+ onMicros = onMicros + diffTick;
+ }
+ else
+ {
+ /* elapsed time was off */
+ offMicros = offMicros + diffTick;
+ }
+ lastTick = tick;
+ }
+ else
+ {
+ count = 1;
+ lastTick = tick;
+ onMicros = 0;
+ offMicros = 0;
+ inited = 1;
+ }
+}
+
+void alertEx(int gpio, int level, uint32_t tick, void * userdata)
+{
+}
+
+static void timerTest(unsigned waitfor, unsigned ms)
+{
+ unsigned ep, ep1, ep2;
+
+ ep= (waitfor*1000)/ms; ep1=ep-1; ep2=ep+1;
+
+ printf("Timer ticktest (%d ms), wait %d seconds\n", ms, waitfor);
+ printf("Expect %d to %d ticks\n", ep1, ep2);
+
+ inited = 0;
+ gpioSetTimerFunc(0, ms, tick);
+ sleep(waitfor);
+ gpioSetTimerFunc(0, ms, NULL);
+
+ /* and the stats were? */
+
+ printf("ticks=%d\n", count);
+
+ if ((count>=ep1) && (count<=ep2))
+ {
+ printf("TEST %d: PASS\n\n", test);
+ ++passes;
+ }
+ else
+ {
+ printf("TEST %d: FAILED\n\n", test);
+ }
+
+ ++test;
+}
+
+static void servoTest(unsigned waitfor, unsigned pulsewidth)
+{
+ int ticks, on, off;
+ unsigned expectedPulses, ep1, ep2;
+ float expectedRatio, er1, er2;
+ float ratio;
+
+ expectedPulses=(500*waitfor)/10; ep1=(490*waitfor)/10; ep2=(510*waitfor)/10;
+ expectedRatio = (float)(20000-pulsewidth)/(float)pulsewidth;
+ er1=expectedRatio*0.9; er2=expectedRatio*1.1;
+
+ printf("Servo pulse test (%d micros), wait %d seconds\n",
+ pulsewidth, waitfor);
+ printf("Expect %d pulses and an off/on ratio of %.1f\n",
+ expectedPulses, expectedRatio);
+
+ gpioServo(GPIO, pulsewidth);
+
+ inited = 0;
+ gpioSetAlertFunc(GPIO, alert);
+ sleep(waitfor);
+ gpioSetAlertFunc(GPIO, NULL);
+
+ gpioServo(GPIO, 0);
+
+ /* and the stats were? */
+
+ ticks = count/2; on = onMicros/1000; off = offMicros/1000;
+ ratio = (float)off/(float)on;
+
+ printf("servo pulses=%d on ms=%d off ms=%d ratio=%.1f\n", ticks, on, off, ratio);
+
+ if ( ((ticks>ep1) && (ticks<ep2)) && ((ratio>er1) && (ratio<er2)) )
+ {
+ printf("TEST %d: PASS\n\n", test);
+ ++passes;
+ }
+ else
+ {
+ printf("TEST %d: FAILED\n\n", test);
+ }
+
+ ++test;
+}
+
+static void pwmTest(unsigned waitfor, unsigned pulsewidth)
+{
+ int on, off;
+ float expectedRatio, er1, er2;
+ float ratio;
+
+ expectedRatio = (float)(255-pulsewidth)/255.0;
+ er1=expectedRatio*0.9; er2=expectedRatio*1.1;
+
+ printf("PWM test (%d), wait %d seconds\n", pulsewidth, waitfor);
+ printf("Expect an off/on ratio of %.3f\n", expectedRatio);
+
+ inited = 0;
+ gpioSetAlertFunc(GPIO, alert);
+ gpioPWM(GPIO, pulsewidth);
+ sleep(waitfor);
+ gpioPWM(GPIO, 0);
+ gpioSetAlertFunc(GPIO, NULL);
+
+ /* and the stats were? */
+
+ on = onMicros/1000; off = offMicros/1000;
+ ratio = (float)off/((float)on+(float)off);
+
+ printf("pwm on ms=%d off ms=%d ratio=%.3f\n", on, off, ratio);
+
+ if ((ratio>er1) && (ratio<er2))
+ {
+ printf("TEST %d: PASS\n\n", test);
+ ++passes;
+ }
+ else
+ {
+ printf("TEST %d: FAILED\n\n", test);
+ }
+
+ ++test;
+}
+
+void expectExpected(int expected)
+{
+ if (expect==expected)
+ {
+ printf("TEST %d: PASS\n\n", test);
+ ++passes;
+ }
+ else printf("TEST %d: FAILED\n\n", test);
+
+ expect = 0;
+
+ ++test;
+}
+
+void checkValidation(void)
+{
+ int secs, micros;
+
+ /* check function parameter validation */
+
+ printf("Function parameter validation tests\n");
+
+ printf("Expect ERROR messages\n\n");
+
+ if (gpioSetMode(PI_MAX_GPIO+1, 0) == PI_BAD_GPIO) expect++;
+ if (gpioSetMode(PI_MIN_GPIO-1, 0) == PI_BAD_GPIO) expect++;
+ if (gpioSetMode(PI_MIN_GPIO, PI_ALT3+1) == PI_BAD_MODE) expect++;
+ if (gpioSetMode(PI_MAX_GPIO, PI_INPUT-1) == PI_BAD_MODE) expect++;
+
+ expectExpected(4);
+
+ if (gpioGetMode(PI_MAX_GPIO+1) == PI_BAD_GPIO) expect++;
+ if (gpioGetMode(PI_MIN_GPIO-1) == PI_BAD_GPIO) expect++;
+
+ expectExpected(2);
+
+ if (gpioSetPullUpDown(PI_MAX_GPIO+1, 0) == PI_BAD_GPIO) expect++;
+ if (gpioSetPullUpDown(PI_MIN_GPIO-1, 0) == PI_BAD_GPIO) expect++;
+ if (gpioSetPullUpDown(PI_MIN_GPIO, PI_PUD_UP+1) == PI_BAD_PUD) expect++;
+ if (gpioSetPullUpDown(PI_MAX_GPIO, PI_PUD_OFF-1) == PI_BAD_PUD) expect++;
+
+ expectExpected(4);
+
+ if (gpioRead(PI_MIN_GPIO-1) == PI_BAD_GPIO) expect++;
+ if (gpioRead(PI_MAX_GPIO+1) == PI_BAD_GPIO) expect++;
+
+ expectExpected(2);
+
+ if (gpioWrite(PI_MAX_GPIO+1, 0) == PI_BAD_GPIO) expect++;
+ if (gpioWrite(PI_MIN_GPIO-1, 0) == PI_BAD_GPIO) expect++;
+ if (gpioWrite(PI_MIN_GPIO, PI_ON+1) == PI_BAD_LEVEL) expect++;
+ if (gpioWrite(PI_MAX_GPIO, PI_OFF-1) == PI_BAD_LEVEL) expect++;
+
+ expectExpected(4);
+
+ if (gpioPWM(PI_MAX_USER_GPIO+1, 0) == PI_BAD_USER_GPIO) expect++;
+ if (gpioPWM(PI_MIN_GPIO-1, 0) == PI_BAD_USER_GPIO) expect++;
+ if (gpioPWM(PI_MIN_GPIO, PI_DEFAULT_DUTYCYCLE_RANGE+1) ==
+ PI_BAD_DUTYCYCLE) expect++;
+ if (gpioPWM(PI_MAX_USER_GPIO, -1) == PI_BAD_DUTYCYCLE) expect++;
+
+ expectExpected(4);
+
+ if (gpioSetPWMrange(PI_MAX_USER_GPIO+1, 0) == PI_BAD_USER_GPIO) expect++;
+ if (gpioSetPWMrange(PI_MIN_GPIO-1, 0) == PI_BAD_USER_GPIO) expect++;
+ if (gpioSetPWMrange(GPIO, 24) == PI_BAD_DUTY_RANGE) expect++;
+ if (gpioSetPWMrange(GPIO, 40001) == PI_BAD_DUTY_RANGE) expect++;
+
+ expectExpected(4);
+
+ if (gpioGetPWMrange(PI_MAX_USER_GPIO+1) == PI_BAD_USER_GPIO) expect++;
+ if (gpioGetPWMrange(PI_MIN_GPIO-1) == PI_BAD_USER_GPIO) expect++;
+
+ expectExpected(2);
+
+ if (gpioGetPWMrealRange(PI_MAX_USER_GPIO+1) == PI_BAD_USER_GPIO) expect++;
+ if (gpioGetPWMrealRange(PI_MIN_GPIO-1) == PI_BAD_USER_GPIO) expect++;
+
+ expectExpected(2);
+
+ if (gpioSetPWMfrequency(PI_MAX_USER_GPIO+1, 0) == PI_BAD_USER_GPIO) expect++;
+ if (gpioSetPWMfrequency(PI_MIN_GPIO-1, 0) == PI_BAD_USER_GPIO) expect++;
+
+ expectExpected(2);
+
+ if (gpioGetPWMfrequency(PI_MAX_USER_GPIO+1) == PI_BAD_USER_GPIO) expect++;
+ if (gpioGetPWMfrequency(PI_MIN_GPIO-1) == PI_BAD_USER_GPIO) expect++;
+
+ expectExpected(2);
+
+ if (gpioServo(PI_MAX_USER_GPIO+1, 0) == PI_BAD_USER_GPIO) expect++;
+ if (gpioServo(PI_MIN_GPIO-1, 0) == PI_BAD_USER_GPIO) expect++;
+ if (gpioServo(GPIO, 1) == PI_BAD_PULSEWIDTH) expect++;
+ if (gpioServo(GPIO,-1) == PI_BAD_PULSEWIDTH) expect++;
+ if (gpioServo(GPIO, 499) == PI_BAD_PULSEWIDTH) expect++;
+ if (gpioServo(GPIO, 2501) == PI_BAD_PULSEWIDTH) expect++;
+
+ expectExpected(6);
+
+ if (gpioSetAlertFunc(PI_MAX_USER_GPIO+1, alert) == PI_BAD_USER_GPIO) expect++;
+ if (gpioSetAlertFunc(PI_MIN_GPIO-1, alert) == PI_BAD_USER_GPIO) expect++;
+
+ expectExpected(2);
+
+ if (gpioSetAlertFuncEx(PI_MAX_USER_GPIO+1, alertEx, 0) == PI_BAD_USER_GPIO) expect++;
+ if (gpioSetAlertFuncEx(PI_MIN_GPIO-1, alertEx, 0) == PI_BAD_USER_GPIO) expect++;
+
+ expectExpected(2);
+
+ if (gpioSetWatchdog(PI_MAX_USER_GPIO+1, 0) == PI_BAD_USER_GPIO) expect++;
+ if (gpioSetWatchdog(PI_MIN_GPIO-1, 0) == PI_BAD_USER_GPIO) expect++;
+ if (gpioSetWatchdog(GPIO, 60001) == PI_BAD_WDOG_TIMEOUT) expect++;
+ if (gpioSetWatchdog(GPIO, -1) == PI_BAD_WDOG_TIMEOUT) expect++;
+ if (gpioSetWatchdog(GPIO, 0) == 0) expect++;
+
+ expectExpected(5);
+
+ if (gpioSetTimerFunc(10, 20, tick) == PI_BAD_TIMER) expect++;
+ if (gpioSetTimerFunc(-1, 20, tick) == PI_BAD_TIMER) expect++;
+ if (gpioSetTimerFunc(0, 9, tick) == PI_BAD_MS) expect++;
+ if (gpioSetTimerFunc(0, 60001, tick) == PI_BAD_MS) expect++;
+
+ expectExpected(4);
+
+ if (gpioSetTimerFuncEx(10, 20, tickEx, 0) == PI_BAD_TIMER) expect++;
+ if (gpioSetTimerFuncEx(-1, 20, tickEx, 0) == PI_BAD_TIMER) expect++;
+ if (gpioSetTimerFuncEx(0, 9, tickEx, 0) == PI_BAD_MS) expect++;
+ if (gpioSetTimerFuncEx(0, 60001, tickEx, 0) == PI_BAD_MS) expect++;
+
+ expectExpected(4);
+
+ if (gpioTime(-1, &secs, µs) == PI_BAD_TIMETYPE) expect++;
+ if (gpioTime(2, &secs, µs) == PI_BAD_TIMETYPE) expect++;
+
+ expectExpected(2);
+
+ if (gpioSleep(-1, 1, 0) == PI_BAD_TIMETYPE) expect++;
+ if (gpioSleep(2, 1, 0) == PI_BAD_TIMETYPE) expect++;
+ if (gpioSleep(PI_TIME_ABSOLUTE, -1, 0) == PI_BAD_SECONDS) expect++;
+ if (gpioSleep(PI_TIME_ABSOLUTE, 0, -1) == PI_BAD_MICROS) expect++;
+ if (gpioSleep(PI_TIME_ABSOLUTE, 0, 1000000) == PI_BAD_MICROS) expect++;
+ if (gpioSleep(PI_TIME_RELATIVE, -1, 0) == PI_BAD_SECONDS) expect++;
+ if (gpioSleep(PI_TIME_RELATIVE, 0, -1) == PI_BAD_MICROS) expect++;
+ if (gpioSleep(PI_TIME_RELATIVE, 0, 1000000) == PI_BAD_MICROS) expect++;
+
+ expectExpected(8);
+}
+
+void checkGpioTime(void)
+{
+ struct timeval nowA, nowR, tvR, tvA, diffR, diffA;
+ int diffMicrosA, diffMicrosR;
+ int i, ok;
+
+ int secR, micR, secA, micA;
+
+ printf("Library timer tests.\n");
+
+ ok = 0;
+
+ for (i=0; i<10; i++)
+ {
+ gettimeofday(&nowA, NULL); /* reference absolute time */
+
+ gpioTime(PI_TIME_ABSOLUTE, &secA, &micA); /* absolute library time */
+ gpioTime(PI_TIME_RELATIVE, &secR, &micR); /* relative library time */
+
+ timersub(&nowA, &libInitTime, &nowR); /* reference relative time */
+
+ tvA.tv_sec = secA; tvA.tv_usec = micA;
+ tvR.tv_sec = secR; tvR.tv_usec = micR;
+
+ /* difference between reference and library absolute time */
+ timersub(&tvA, &nowA, &diffA);
+
+ /* difference between reference and library relative time */
+ timersub(&tvR, &nowR, &diffR);
+
+
+ diffMicrosA = (diffA.tv_sec*1000000)+diffA.tv_usec;
+
+ diffMicrosR = (diffR.tv_sec*1000000)+diffR.tv_usec;
+
+ if (diffMicrosA < 0) diffMicrosA = -diffMicrosA;
+ if (diffMicrosR < 0) diffMicrosR = -diffMicrosR;
+
+ if ((diffMicrosA < 10) && (diffMicrosR < 500)) ok++;
+
+ printf("ABS time diff=%d, REL time diff=%d\n",
+ diffMicrosA, diffMicrosR);
+ }
+
+ if (ok == 10)
+ {
+ printf("TEST %d: PASS\n\n", test);
+ ++passes;
+ }
+ else printf("TEST %d: FAILED\n\n", test);
+
+ ++test;
+}
+
+void checkGpioSleep(unsigned timetype)
+{
+ struct timeval t1, t2, tD;
+ int i, ok, secs, micros, diffMicros, expMicros, errMicros;
+
+ if (timetype == PI_TIME_ABSOLUTE)
+ printf("Library gpioSleep ABSOLUTE tests.\n");
+ else
+ printf("Library gpioSleep RELATIVE tests.\n");
+
+ ok = 0;
+
+ for (i=15; i>0; i--)
+ {
+ expMicros = i * 100000;
+
+ if (timetype == PI_TIME_ABSOLUTE)
+ {
+ gpioTime(PI_TIME_ABSOLUTE, &secs, µs);
+
+ secs += (i / 10);
+ micros += (i % 10) * 100000;
+
+ if (micros > 999999) { secs++; micros -= 1000000; }
+ }
+ else
+ {
+ secs = (i / 10);
+ micros = (i % 10) * 100000;
+ }
+
+ gettimeofday(&t1, NULL);
+
+ gpioSleep(timetype, secs, micros);
+
+ gettimeofday(&t2, NULL);
+
+ timersub(&t2, &t1, &tD);
+
+ diffMicros = (tD.tv_sec*1000000)+tD.tv_usec;
+
+ errMicros = diffMicros - expMicros;
+
+ if (errMicros < 500) ok++;
+
+ printf("secs=%d micros=%d err=%d\n", secs, micros, errMicros);
+ }
+
+ if (ok == 15)
+ {
+ printf("TEST %d: PASS\n\n", test);
+ ++passes;
+ }
+ else printf("TEST %d: FAILED\n\n", test);
+
+ ++test;
+}
+
+int countBank2PinChanges(int pin, int loops)
+{
+ static uint32_t old=0;
+
+ uint32_t new, changes;
+ int i, count;
+
+ count = 0;
+
+ for (i=0;i<loops;i++)
+ {
+ new = gpioRead_Bits_32_53();
+
+ changes = new ^ old;
+
+ if (changes & (1<<(pin-32))) count++;
+ }
+ return count;
+}
+
+void checkReadWriteBits(void)
+{
+ uint32_t bank1, bank2;
+
+ int i, ok, count1, count2;
+
+ printf("Library gpioRead/Write_Bits_x_x_Set/Clear Tests\n");
+ printf("Expect 0 for pin 47 and >200000 for pin 48.\n");
+ printf("Expect the green LED to flash.\n\n");
+
+ ok = 0;
+
+ for (i=0; i<20; i++)
+ {
+ gpioWrite_Bits_0_31_Set(1<<GREENLED);
+
+ count1 = countBank2PinChanges(SDDET, 1000000);
+
+ gpioWrite_Bits_0_31_Clear(1<<GREENLED);
+
+ count2 = countBank2PinChanges(SDCLK, 1000000);
+
+ bank1 = gpioRead_Bits_0_31();
+ bank2 = gpioRead_Bits_32_53();
+
+ printf("bank1=%08X, bank2=%08X, 47=%d 48=%d\n",
+ bank1, bank2, count1, count2);
+
+ if ((count1 == 0) && (count2 > 200000)) ok++;
+ }
+
+ if (ok == 20)
+ {
+ printf("TEST %d: PASS\n\n", test);
+ ++passes;
+ }
+ else printf("TEST %d: FAILED\n\n", test);
+
+ ++test;
+}
+
+void checkGpioTick(void)
+{
+ uint32_t startTick, endTick;
+ int diffTick;
+
+ printf("Library gpioTick Test\n");
+ printf("Expect approximately 2 million ticks to have elapsed.\n\n");
+
+ startTick = gpioTick();
+ sleep(2); /* 2 seconds being 2 million ticks */
+ endTick = gpioTick();
+
+ diffTick = endTick - startTick;
+
+ printf("%d ticks have elapsed\n", diffTick);
+
+ if ((diffTick >= 1990000) && (diffTick <= 2010000))
+ {
+ printf("TEST %d: PASS\n\n", test);
+ ++passes;
+ }
+ else printf("TEST %d: FAILED\n\n", test);
+
+ ++test;
+}
+
+int main(int argc, char *argv[])
+{
+
+ int waitfor;
+
+ int version, micros=5, millis=100;
+
+ if (argc > 1) GPIO = atoi(argv[1]);
+
+ fprintf(stderr,
+"*****************************************************************\n"\
+"* WARNING: This program sends pulses to gpio #%02d *\n"\
+"* Make sure that nothing which could be damaged is *\n"\
+"* connected to this gpio. A LED or similar should be OK *\n"\
+"* although nothing needs to be connected. *\n"\
+"* *\n"\
+"* NOTE: many of the tests are statistical in nature, assuming *\n"\
+"* that events of a short nature will on average be detected *\n"\
+"* by sampling. Don't fret if a particular test fails, try *\n"\
+"* running the tests again. *\n"\
+"* *\n"\
+"* You may choose another gpio by specifying its number on *\n"\
+"* the command line, e.g. sudo ./checklib 17 will use gpio 17. *\n"\
+"* *\n"\
+"* Press y (RETURN) to continue, any other character to cancel. *\n"\
+"*****************************************************************\n", GPIO);
+
+ if (getchar() != 'y') return 0;
+
+ printf("Initialisation test\n");
+
+ if (argc > 2) micros = atoi(argv[2]);
+
+ if (argc > 3) millis = atoi(argv[3]);
+
+ gpioCfgBufferSize(millis);
+
+ gpioCfgClock(micros, PI_CLOCK_PCM, PI_CLOCK_PLLD);
+
+ gettimeofday(&libInitTime, NULL);
+
+ version = gpioInitialise();
+
+ if (version<0)
+ {
+ printf("TEST %d: FAILED\nFATAL ERROR\n", test);
+ return 1;
+ }
+ else
+ {
+ printf("TEST %d: PASS, pigpio version is %d\n\n", test, version);
+ ++passes;
+ }
+
+ ++test;
+
+ waitfor = 2;
+
+ printf("Alert function test, wait %d seconds\n", waitfor);
+ printf("No detected events on gpio 4 expected\n");
+
+ inited = 0;
+ gpioSetAlertFunc(GPIO, alert);
+ sleep(waitfor);
+ gpioSetAlertFunc(GPIO, NULL);
+
+ printf("Events=%d\n", count);
+
+ if (count) printf("TEST %d: FAILED\n\n", test);
+ else
+ {
+ printf("TEST %d: PASS\n\n", test);
+ ++passes;
+ }
+
+ ++test;
+
+ servoTest(10, 500);
+ servoTest(10, 1500);
+ servoTest(10, 2500);
+
+ pwmTest(5, 50);
+ pwmTest(5, 100);
+ pwmTest(5, 150);
+ pwmTest(5, 200);
+
+ timerTest(5, 100);
+ timerTest(5, 250);
+ timerTest(5, 333);
+ timerTest(5, 1000);
+
+ checkValidation();
+
+ checkGpioTime();
+
+ checkGpioSleep(PI_TIME_RELATIVE);
+
+ checkGpioSleep(PI_TIME_ABSOLUTE);
+
+ checkReadWriteBits();
+
+ checkGpioTick();
+
+ printf("Hardware revision is %d\n\n", gpioHardwareRevision());
+
+ printf("Summary: %d tests, %d passes\n", test-1, passes);
+
+ gpioTerminate(); /* stop DMA and free memory */
+
+ return (passes - (test-1));
+}
+
--- /dev/null
+/*
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <http://unlicense.org/>
+*/
+
+/*
+This version is for pigpio version 4+
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "pigpio.h"
+#include "command.h"
+
+cmdInfo_t cmdInfo[]=
+{
+ {PI_CMD_BR1, "BR1", 1, 3},
+ {PI_CMD_BR2, "BR2", 1, 3},
+ {PI_CMD_BC1, "BC1", 7, 1},
+ {PI_CMD_BC2, "BC2", 7, 1},
+ {PI_CMD_BS1, "BS1", 7, 1},
+ {PI_CMD_BS2, "BS2", 7, 1},
+ {PI_CMD_HWVER, "HWVER", 1, 4},
+ {PI_CMD_MODES, "MODES", 8, 0},
+ {PI_CMD_MODES, "M", 8, 0},
+ {PI_CMD_MODEG, "MODEG", 2, 2},
+ {PI_CMD_MODEG, "MG" , 2, 2},
+ {PI_CMD_NO, "NO", 1, 2},
+ {PI_CMD_NB, "NB", 4, 0},
+ {PI_CMD_NP, "NP", 2, 0},
+ {PI_CMD_NC, "NC", 2, 0},
+ {PI_CMD_PWM, "PWM", 3, 0},
+ {PI_CMD_PWM, "P", 3, 0},
+ {PI_CMD_PFS, "PFS", 3, 2},
+ {PI_CMD_PFG, "PFG", 2, 2},
+ {PI_CMD_PRS, "PRS", 3, 2},
+ {PI_CMD_PRG, "PRG", 2, 2},
+ {PI_CMD_PRRG, "PRRG", 2, 2},
+ {PI_CMD_PUD, "PUD", 9, 0},
+ {PI_CMD_READ, "READ", 2, 2},
+ {PI_CMD_READ, "R", 2, 2},
+ {PI_CMD_SERVO, "SERVO", 3, 0},
+ {PI_CMD_SERVO, "S", 3, 0},
+ {PI_CMD_WRITE, "WRITE", 3, 0},
+ {PI_CMD_WRITE, "W", 3, 0},
+ {PI_CMD_WDOG, "WDOG", 3, 0},
+ {PI_CMD_TICK, "TICK", 1, 4},
+ {PI_CMD_TICK, "T", 1, 4},
+ {PI_CMD_HELP, "HELP", 6, 5},
+ {PI_CMD_HELP, "H", 6, 5},
+};
+
+char * cmdUsage = "\
+BR1 read gpios bank 1\n\
+BR2 read gpios bank 2\n\
+BC1 x clear gpios in bank 1\n\
+BC2 x clear gpios in bank 2\n\
+BS1 x set gpios in bank 1\n\
+BS2 x set gpios in bank 2\n\
+HWVER return hardware version\n\
+MODES/M g m set gpio mode\n\
+MODEG/MG g get gpio mode\n\
+NO request notification handle\n\
+NB h x start notification\n\
+NP h pause notification\n\
+NC h close notification\n\
+PWM/P u d set PWM value for gpio\n\
+PFS u d set PWM frequency for gpio\n\
+PFG u get PWM frequency for gpio\n\
+PRS u d set PWM range for gpio\n\
+PRG u get PWM range for gpio\n\
+PRRG u get PWM real range for gpio\n\
+PUD g p set gpio pull up/down\n\
+READ/R g read gpio\n\
+SERVO/S u d set servo value for gpio\n\
+WRITE/W g d write value to gpio\n\
+WDOG u d set watchdog on gpio\n\
+TICK/T return current tick\n\
+HELP/H displays command help\n\
+\n\
+d = decimal value\n\
+g = gpio (0-53)\n\
+h = handle (0-31)\n\
+m = mode (RW540123)\n\
+p = pud (ODU)\n\
+u = user gpio (0-31)\n\
+x = hex value\n\
+";
+
+typedef struct
+{
+ int error;
+ char * str;
+} errInfo_t;
+
+static errInfo_t errInfo[]=
+{
+ {PI_INIT_FAILED , "pigpio initialisation failed"},
+ {PI_BAD_USER_GPIO , "gpio not 0-31"},
+ {PI_BAD_GPIO , "gpio not 0-53"},
+ {PI_BAD_MODE , "mode not 0-7"},
+ {PI_BAD_LEVEL , "level not 0-1"},
+ {PI_BAD_PUD , "pud not 0-2"},
+ {PI_BAD_PULSEWIDTH , "pulsewidth not 0 or 500-2500"},
+ {PI_BAD_DUTYCYCLE , "dutycycle not 0-255"},
+ {PI_BAD_TIMER , "timer not 0-9"},
+ {PI_BAD_MS , "ms not 10-60000"},
+ {PI_BAD_TIMETYPE , "timetype not 0-1"},
+ {PI_BAD_SECONDS , "seconds < 0"},
+ {PI_BAD_MICROS , "micros not 0-999999"},
+ {PI_TIMER_FAILED , "gpioSetTimerFunc failed"},
+ {PI_BAD_WDOG_TIMEOUT , "timeout not 0-60000"},
+ {PI_NO_ALERT_FUNC , "DEPRECATED"},
+ {PI_BAD_CLK_PERIPH , "clock peripheral not 0-1"},
+ {PI_BAD_CLK_SOURCE , "clock source not 0-1"},
+ {PI_BAD_CLK_MICROS , "clock micros not 1, 2, 4, 5, 8, or 10"},
+ {PI_BAD_BUF_MILLIS , "buf millis not 100-10000"},
+ {PI_BAD_DUTY_RANGE , "dutycycle range not 25-40000"},
+ {PI_BAD_SIGNUM , "signum not 0-63"},
+ {PI_BAD_PATHNAME , "can't open pathname"},
+ {PI_NO_HANDLE , "no handle available"},
+ {PI_BAD_HANDLE , "unknown notify handle"},
+ {PI_BAD_IF_FLAGS , "ifFlags > 3"},
+ {PI_BAD_CHANNEL , "DMA channel not 0-14"},
+ {PI_BAD_SOCKET_PORT , "socket port not 1024-30000"},
+ {PI_BAD_FIFO_COMMAND , "unknown fifo command"},
+ {PI_BAD_SECO_CHANNEL , "DMA secondary channel not 0-6"},
+ {PI_NOT_INITIALISED , "function called before gpioInitialise"},
+ {PI_INITIALISED , "function called after gpioInitialise"},
+ {PI_BAD_WAVE_MODE , "waveform mode not 0-1"},
+ {PI_BAD_CFG_INTERNAL , "bad parameter in gpioCfgInternals call"},
+ {PI_BAD_WAVE_BAUD , "baud rate not 100-250000"},
+ {PI_TOO_MANY_PULSES , "waveform has too many pulses"},
+ {PI_TOO_MANY_CHARS , "waveform has too many chars"},
+ {PI_NOT_SERIAL_GPIO , "no serial read in progress on gpio"},
+
+};
+
+static char * fmtMdeStr="RW540123";
+static char * fmtPudStr="ODU";
+
+static int cmdMatch(char * str)
+{
+ int i;
+
+ for (i=0; i<(sizeof(cmdInfo)/sizeof(cmdInfo_t)); i++)
+ {
+ if (strcasecmp(str, cmdInfo[i].name) == 0) return i;
+ }
+ return -1;
+}
+
+int cmdParse(char * buf, cmdCmd_t * cmd)
+{
+ char str[8];
+ int f, valid, idx, val;
+ char * ptr;
+ char c, t;
+
+ sscanf(buf, " %7s", str);
+
+ cmd->cmd = -1;
+
+ idx = cmdMatch(str);
+
+ if (idx < 0) return idx;
+
+ valid = 0;
+
+ cmd->cmd = cmdInfo[idx].cmd;
+ cmd->p1 = 0;
+ cmd->p2 = 0;
+
+ switch (cmdInfo[idx].vt)
+ {
+ case 1: /* BR1 BR2 HWVER NO TICK */
+ f = sscanf(buf, " %7s %c", str, &t);
+ if (f == 1) valid = 1;
+ break;
+
+ case 2: /* MODEG READ NC NP PFG PRG PRRG */
+ f = sscanf(buf, " %7s %d %c", str, &cmd->p1, &t);
+ if (f == 2) valid = 1;
+ break;
+
+ case 3: /* WRITE PWM PRS PFS SERVO WDOG */
+ f = sscanf(buf, " %7s %d %d %c", str, &cmd->p1, &cmd->p2, &t);
+ if (f == 3) valid = 1;
+ break;
+
+ case 4: /* NB */
+ f = sscanf(buf, " %7s %d %x %c", str, &cmd->p1, &cmd->p2, &t);
+ if (f == 3) valid = 1;
+ break;
+
+ case 6: /* HELP */
+ valid = 1;
+ break;
+
+ case 7: /* BC1 BC2 BS1 BS2 */
+ f = sscanf(buf, " %7s %x %c", str, &cmd->p1, &t);
+ if (f == 2) valid = 1;
+ break;
+
+ case 8: /* MODES */
+ f = sscanf(buf, " %7s %d %c %c", str, &cmd->p1, &c, &t);
+ if (f == 3)
+ {
+ val = toupper(c);
+ ptr = strchr(fmtMdeStr, val);
+ if (ptr != NULL)
+ {
+ val = ptr - fmtMdeStr;
+ cmd->p2 = val;
+ valid = 1;
+ }
+ }
+ break;
+
+ case 9: /* PUD */
+ f = sscanf(buf, " %7s %d %c %c", str, &cmd->p1, &c, &t);
+ if (f == 3)
+ {
+ val = toupper(c);
+ ptr = strchr(fmtPudStr, val);
+ if (ptr != NULL)
+ {
+ val = ptr - fmtPudStr;
+ cmd->p2 = val;
+ valid = 1;
+ }
+ }
+ break;
+ }
+
+ if (valid) return idx;
+ else return -1;
+}
+
+void cmdFatal(char *fmt, ...)
+{
+ char buf[128];
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ fprintf(stderr, "%s\n", buf);
+
+ fflush(stderr);
+
+ exit(EXIT_FAILURE);
+}
+
+char * cmdErrStr(int error)
+{
+ int i;
+
+ for (i=0; i<(sizeof(errInfo)/sizeof(errInfo_t)); i++)
+ {
+ if (errInfo[i].error == error) return errInfo[i].str;
+ }
+ return "unknown error";
+}
+
--- /dev/null
+/*
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <http://unlicense.org/>
+*/
+
+/*
+This version is for pigpio version 3+
+*/
+
+#ifndef COMMAND_H
+#define COMMAND_H
+
+#include <stdio.h>
+#include <string.h>
+
+#include "pigpio.h"
+
+typedef struct
+{
+ int cmd;
+ char * name;
+ int vt;
+ int rv;
+} cmdInfo_t;
+
+extern cmdInfo_t cmdInfo[];
+
+extern char * cmdUsage;
+
+int cmdParse(char * buf, cmdCmd_t * cmd);
+
+char * cmdErrStr(int error);
+
+void cmdFatal(char *fmt, ...);
+
+#endif
--- /dev/null
+/*
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <http://unlicense.org/>
+*/
+
+/*
+This version is for pigpio version 3+
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <linux/i2c-dev.h>
+#include <sys/ioctl.h>
+
+#include "pigpio.h"
+
+/* ===========================================================================
+THIS PROGRAM NEEDS THE I2C DEVICE AND DEVELOPMENT LIBRARY
+
+TO GET THE NEEDED FILES DO
+
+sudo apt-get install libi2c-dev
+
+BEFORE RUNNING THE PROGRAM ENSURE THAT THE I2C DEVICE IS PRESENT
+
+sudo modprobe i2c-bcm2708
+sudo modprobe i2c-dev
+sudo chmod o+rw /dev/i2c*
+=========================================================================== */
+
+/*
+
+P1 Name gpio used for
+
+ 3 SDA 0/2 i2c
+ 5 SCL 1/3 i2c
+ 7 --- 4 LASER
+ 8 TXD 14 LED1
+10 RXD 15 LED2
+11 --- 17 SERVO 1
+12 --- 18 SERVO 2
+13 --- 21/27 SERVO 3
+15 --- 22 LED3
+16 --- 23 TI Launchpad
+18 --- 24 Sonar trigger
+19 MOSI 10 Sonar echo
+21 MISO 9 Motor B In 1
+22 --- 25 LDR
+23 SCLK 11 Motor B In 2
+24 CE0 8 Motor A In1
+26 CE1 7 Motor A In2
+
+*/
+
+#define LASER 4
+#define MOTOR_A_IN2 7
+#define MOTOR_A_IN1 8
+#define MOTOR_B_IN1 9
+#define SONAR_ECHO 10
+#define MOTOR_B_IN2 11
+#define LED1 14
+#define LED2 15
+#define SERVO1 17
+#define SERVO2 18
+#define SERVO3 21
+#define LED3 22
+#define LAUNCHPAD 23
+#define SONAR_TRIGGER 24
+#define LDR 25
+
+#define LEDS 4
+
+short rawAcc[3];
+short rawGyr[3];
+short rawMag[3];
+
+#define ROLL 0
+#define PITCH 1
+#define YAW 2
+
+#define ACC_ORIENTATION(X, Y, Z) \
+ {rawAcc[ROLL] = -X; rawAcc[PITCH] = -Y; rawAcc[YAW] = Z;}
+
+#define GYRO_ORIENTATION(X, Y, Z) \
+ {rawGyr[ROLL] = Y; rawGyr[PITCH] = -X; rawGyr[YAW] = -Z;}
+
+#define MAG_ORIENTATION(X, Y, Z) \
+ {rawMag[ROLL] = X; rawMag[PITCH] = Y; rawMag[YAW] = -Z;}
+
+
+#define CALIBRATIONS 200
+
+#define ADXL345_I2C_ADDR 0x53
+#define ITG3200_I2C_ADDR 0x68
+
+static int version, micros=5, millis=100;
+
+static volatile unsigned long launchpadPulses;
+static volatile unsigned long launchpad5;
+static volatile unsigned long launchpad10;
+static volatile unsigned long launchpad15;
+static volatile unsigned long launchpadOutRange;
+static volatile int launchpadErr;
+static volatile uint32_t LDRrechargeTick;
+
+
+/* forward prototypes */
+
+void LEDlaserTick (void);
+void motorTick(void);
+void i2cTick (void);
+void servoTick (void);
+void sonarLDRtick(void);
+
+void launchpadAlert(int gpio, int level, uint32_t tick);
+void sonarAlert(int gpio, int level, uint32_t tick);
+void LDRalert(int gpio, int level, uint32_t tick);
+
+void putTTY(char * buf);
+void putTTYstr(int row, int col, char * buf);
+
+
+int main(int argc, char *argv[])
+{
+ char str[256];
+
+ if (argc > 1) micros = atoi(argv[1]);
+
+ if (argc > 2) millis = atoi(argv[2]);
+
+ putTTY("\033c"); /* clear console */
+
+ gpioCfgBufferSize(millis);
+
+ gpioCfgClock(micros, PI_CLOCK_PCM, PI_CLOCK_PLLD);
+
+
+ /* before using the library you must call gpioInitialise */
+
+ version = gpioInitialise();
+
+ if (version >= 0)
+ {
+ /* initialise pins, only gpio numbers are supported */
+
+ gpioSetMode(SERVO1, PI_OUTPUT);
+ gpioSetMode(SERVO2, PI_OUTPUT);
+ gpioSetMode(SERVO3, PI_OUTPUT);
+ gpioSetMode(LASER, PI_OUTPUT);
+ gpioSetMode(LED1, PI_OUTPUT);
+ gpioSetMode(LED2, PI_OUTPUT);
+ gpioSetMode(LED3, PI_OUTPUT);
+ gpioSetMode(MOTOR_A_IN1, PI_OUTPUT);
+ gpioSetMode(MOTOR_A_IN2, PI_OUTPUT);
+ gpioSetMode(MOTOR_B_IN1, PI_OUTPUT);
+ gpioSetMode(MOTOR_B_IN2, PI_OUTPUT);
+
+ gpioSetMode(SONAR_TRIGGER, PI_OUTPUT);
+ gpioWrite (SONAR_TRIGGER, PI_OFF);
+
+ gpioSetMode(SONAR_ECHO, PI_INPUT);
+ gpioSetMode(LAUNCHPAD, PI_INPUT);
+ gpioSetMode(LDR, PI_INPUT);
+
+ /* update i2c fifty times a second, timer #0 */
+
+ gpioSetTimerFunc(0, 20, i2cTick);
+
+ //gpioSetTimerFunc(0, 1000, servoTick);
+
+ /* update LEDs and laser once a second, timer #1 */
+
+ gpioSetTimerFunc(1, 1000, LEDlaserTick);
+
+ /* update motors every three seconds, timer #2 */
+
+ gpioSetTimerFunc(2, 3000, motorTick);
+
+ /* update sonar/LDR 10 times a second, timer #3 */
+
+ gpioSetTimerFunc(3, 100, sonarLDRtick);
+
+ /* an attachecd TI launchpad is transmitting high pulses of
+ 15, 35, 55, 75, ..., 975, 995 microseconds repeating with 50
+ microseconds off between each pulse */
+
+ gpioSetAlertFunc(LAUNCHPAD, launchpadAlert);
+
+ /* monitor sonar echos */
+
+ gpioSetAlertFunc(SONAR_ECHO, sonarAlert);
+
+ /* monitor LDR level changes */
+
+ gpioSetAlertFunc(LDR, LDRalert);
+
+ while (1)
+ {
+ sleep(1);
+
+ sprintf(str, "TI pulses %8ld", launchpadPulses);
+ putTTYstr(9, 1, str);
+
+ sprintf(str, "+/-5 %8ld", launchpad5);
+ putTTYstr(10, 6, str);
+
+ sprintf(str, "+/-10 %8ld", launchpad10);
+ putTTYstr(11, 5, str);
+
+ sprintf(str, "+/-15 %8ld", launchpad15);
+ putTTYstr(12, 5, str);
+
+ sprintf(str, "Others %8ld (last %d) ",
+ launchpadOutRange, launchpadErr);
+ putTTYstr(13, 4, str);
+ }
+ }
+
+ gpioTerminate();
+
+ return 0;
+}
+
+void LEDlaserTick(void)
+{
+ static int gpio[LEDS]={LED1, LED2, LED3, LASER};
+ static int pos [LEDS]={ 0, 3, 6, 9};
+ static int inc [LEDS]={ 1, 1, 1, 1};
+
+ static int vals[] = {0, 1, 2, 4, 8, 16, 32, 64, 128, 249};
+
+ int i;
+
+ for (i=0; i<LEDS; i++)
+ {
+ gpioPWM(gpio[i], vals[pos[i]]);
+
+ pos[i] += inc[i];
+
+ if ( (pos[i]>=(sizeof(vals)/4)) || (pos[i]<0) )
+ {
+ inc[i] = -inc[i];
+ pos[i] += inc[i];
+ }
+ }
+}
+
+void sonarLDRtick(void)
+{
+ /* trigger a sonar reading */
+
+ gpioWrite(SONAR_TRIGGER, PI_ON);
+ usleep(20);
+ gpioWrite(SONAR_TRIGGER, PI_OFF);
+
+ /* trigger a LDR reading */
+
+ gpioSetMode(LDR, PI_OUTPUT); /* drain capacitor */
+
+ gpioWrite(LDR, PI_OFF);
+
+ usleep(200);
+
+ LDRrechargeTick = gpioTick();
+
+ gpioSetMode(LDR, PI_INPUT); /* start capacitor recharge */
+ }
+
+void motorTick(void)
+{
+ static int gpio_in1[2]={MOTOR_A_IN1, MOTOR_B_IN1};
+ static int gpio_in2[2]={MOTOR_A_IN2, MOTOR_B_IN2};
+ static int speed [2]={ 80, 80};
+ static int inc [2]={ -50, 50};
+
+ int i;
+ char str[256];
+
+ for (i=0; i<2; i++)
+ {
+ speed[i]+=inc[i];
+
+ if (speed[i]<0)
+ {
+ gpioPWM(gpio_in1[i], -speed[i]);
+ gpioPWM(gpio_in2[i], 0);
+ if (speed[i] < -205) inc[i] = -inc[i];
+ sprintf(str, "MOT%d IN1=%3d IN2=%3d", i+1, -speed[i], 0);
+ }
+ else
+ {
+ gpioPWM(gpio_in2[i], speed[i]);
+ gpioPWM(gpio_in1[i], 0);
+ if (speed[i] > 205) inc[i] = -inc[i];
+ sprintf(str, "MOT%d IN1=%3d IN2=%3d", i+1, 0, speed[i]);
+ }
+ if (i) putTTYstr(7, 1, str); else putTTYstr(5, 1, str);
+ }
+}
+
+/* loads of code to read/write i2c */
+
+void selectDevice(int i2c, int addr, char * name)
+{
+ if (ioctl(i2c, I2C_SLAVE, addr) < 0)
+ {
+ fprintf(stderr, "%s not present\n", name);
+ }
+}
+
+void writeToDevice(int i2c, char * buf, int len)
+{
+ static int reported = 0;
+ if (write(i2c, buf, len) != len)
+ {
+ if (!reported)
+ {
+ fprintf(stderr, "Can't write to device\n");
+ reported = 1;
+ }
+ }
+ else reported = 0;
+}
+
+void readADXL345(int i2c)
+{
+ char buf[8];
+ static int reported = 0;
+
+ selectDevice(i2c, ADXL345_I2C_ADDR, "ADXL345");
+
+ writeToDevice(i2c, "\x32", 1);
+
+ if (read(i2c, buf, 6) != 6)
+ {
+ if (!reported)
+ {
+ fprintf(stderr, "Unable to read from ADXL345\n");
+ reported = 1;
+ }
+ }
+ else
+ {
+ reported = 0;
+
+ ACC_ORIENTATION (
+ ((buf[1]<<8) | buf[0]),
+ ((buf[3]<<8) | buf[2]),
+ ((buf[5]<<8) | buf[4]) );
+ }
+}
+
+void readITG3200(int i2c)
+{
+ char buf[8];
+ static int reported = 0;
+
+ selectDevice(i2c, ITG3200_I2C_ADDR, "ITG3200");
+
+ writeToDevice(i2c, "\x1D", 1);
+
+ if (read(i2c, buf, 6) != 6)
+ {
+ if (!reported)
+ {
+ fprintf(stderr, "Unable to read from ITG3200\n");
+ reported = 1;
+ }
+ }
+ else
+ {
+ reported = 0;
+
+ GYRO_ORIENTATION (
+ ((buf[0]<<8) | buf[1]),
+ ((buf[2]<<8) | buf[3]),
+ ((buf[4]<<8) | buf[5]) );
+ }
+}
+
+int initI2Cdevices(void)
+{
+ int i2c;
+
+ if ((i2c = open("/dev/i2c-0", O_RDWR)) < 0)
+ {
+ perror("Failed to open i2c bus");
+ exit(1);
+ }
+
+ /* initialise ADXL345 */
+
+ selectDevice(i2c, ADXL345_I2C_ADDR, "ADXL345");
+
+ writeToDevice(i2c, "\x2d\x00", 2);
+ writeToDevice(i2c, "\x2d\x10", 2);
+ writeToDevice(i2c, "\x2d\x08", 2);
+ writeToDevice(i2c, "\x31\x00", 2);
+ writeToDevice(i2c, "\x31\x0b", 2);
+
+ /* initialise ITG3200 */
+
+ selectDevice(i2c, ITG3200_I2C_ADDR, "ITG3200");
+
+ writeToDevice(i2c, "\x16\b00011000", 2);
+
+ return i2c;
+}
+
+/* an attached IMU (GY-85) supplies orientation information which
+ is used to position the servos */
+
+float estimateAngle(int acc, int gyro, float oldAng, int elapsed)
+{
+ float angleAcc, angleInc, estAngle;
+ float secs;
+
+ secs = (float) elapsed / 1e6f;
+
+ angleAcc = (float) acc * 90.0f / 256.0f;
+
+ angleInc = (float) gyro * secs * 2000.0f / 32768.0f;
+
+ estAngle = 0.75 * (oldAng + angleInc) + 0.25 * angleAcc;
+
+ return estAngle;
+}
+
+void servoTick(void)
+{
+ static int wid1=1500, wid2=1500, wid3=1500;
+ static int inc1=50, inc2=75, inc3=100;
+
+ gpioServo(SERVO1, wid1);
+ gpioServo(SERVO2, wid2);
+ gpioServo(SERVO3, wid3);
+
+ wid1+=inc1; if ((wid1<1000) || (wid1>2000)) {inc1 = -inc1; wid1+=inc1;}
+ wid2+=inc2; if ((wid2<1000) || (wid2>2000)) {inc2 = -inc2; wid2+=inc2;}
+ wid3+=inc3; if ((wid3<1000) || (wid3>2000)) {inc3 = -inc3; wid3+=inc3;}
+}
+
+void i2cTick(void)
+{
+ static int inited = 0;
+ static int calibrated = 0;
+ static int calibrations = 0;
+ static int accCalibX = 0, accCalibY = 0, accCalibZ = 0;
+ static int gyroCalibX = 0, gyroCalibY = 0, gyroCalibZ = 0;
+ static int i2c;
+ static float X=0.0, Y=0.0, Z=0.0;
+
+ static uint32_t lastTick;
+
+ uint32_t tick;
+ int elapsed;
+ int pulse;
+ char str[256];
+
+ if (inited)
+ {
+ tick = gpioTick();
+ elapsed = tick - lastTick;
+ lastTick = tick;
+
+ readADXL345(i2c);
+ readITG3200(i2c);
+
+ if (calibrated)
+ {
+ X = estimateAngle(
+ rawAcc[ROLL], rawGyr[ROLL] -gyroCalibX, X, elapsed);
+
+ Y = estimateAngle(
+ rawAcc[PITCH], rawGyr[PITCH] - gyroCalibY, Y, elapsed);
+
+ Z = estimateAngle(
+ rawAcc[YAW], rawGyr[YAW] - gyroCalibZ, Z, elapsed);
+
+ pulse = 1500 + (Y * 1000 / 90);
+ if (pulse < 500) pulse = 500;
+ if (pulse > 2500) pulse = 2500;
+ gpioServo(SERVO1, pulse);
+
+ pulse = 1500 - (X * 500 / 90);
+ if (pulse < 1000) pulse = 1000;
+ if (pulse > 2000) pulse = 2000;
+ gpioServo(SERVO2, pulse);
+
+ /* prefer Z but that doesn't change much */
+ pulse = 1500 - (Y * 500 / 90);
+ if (pulse < 800) pulse = 800;
+ if (pulse > 2200) pulse = 2200;
+ gpioServo(SERVO3, pulse);
+
+ sprintf(str, "X=%4.0f Y=%4.0f Z=%4.0f ", X, Y, Z);
+ putTTYstr(1, 1, str);
+ }
+ else
+ {
+ accCalibX+=rawAcc[ROLL];
+ accCalibY+=rawAcc[PITCH];
+ accCalibZ+=rawAcc[YAW];
+
+ gyroCalibX+=rawGyr[ROLL];
+ gyroCalibY+=rawGyr[PITCH];
+ gyroCalibZ+=rawGyr[YAW];
+
+ if (++calibrations >= CALIBRATIONS)
+ {
+ accCalibX /= CALIBRATIONS;
+ accCalibY /= CALIBRATIONS;
+ accCalibZ /= CALIBRATIONS;
+
+ gyroCalibX /= CALIBRATIONS;
+ gyroCalibY /= CALIBRATIONS;
+ gyroCalibZ /= CALIBRATIONS;
+
+ calibrated = 1;
+ }
+ }
+ }
+ else
+ {
+ i2c = initI2Cdevices();
+
+ gpioServo(SERVO1, 1500);
+ gpioServo(SERVO2, 1500);
+ gpioServo(SERVO3, 1500);
+
+ inited = 1;
+ }
+}
+
+void sonarAlert(int gpio, int level, uint32_t tick)
+{
+ static uint32_t startTick;
+
+ int diffTick;
+ char str[256];
+
+ if (level == PI_ON)
+ {
+ startTick = tick;
+ }
+ else if (level == PI_OFF)
+ {
+ diffTick = tick - startTick;
+
+ if (diffTick < 26100)
+ {
+ sprintf(str, "Sonar %3d cms", (diffTick+29)/58);
+ putTTYstr(15, 1, str);
+ }
+ }
+}
+
+void LDRalert(int pin, int level, uint32_t tick)
+{
+ int diffTick;
+ char str[256];
+
+ if (level == PI_ON)
+ {
+ diffTick = tick - LDRrechargeTick;
+
+ sprintf(str, "LDR %4d micros", diffTick);
+ putTTYstr(17, 1, str);
+ }
+}
+
+void launchpadAlert(int pin, int level, uint32_t tick)
+{
+ static int inited = 0, lastTick, lastPulseLen;
+
+ int pulseLen, pulseDif;
+
+ if (inited)
+ {
+ pulseLen = tick - lastTick;
+ lastTick = tick;
+
+ if (level==0)
+ {
+ if (lastPulseLen)
+ {
+ pulseDif = pulseLen - lastPulseLen;
+
+ /* allow for wrap around */
+ if (pulseDif < 0) pulseDif += 1096;
+
+ /* now centre around expected value */
+ pulseDif -= 33;
+
+ if (pulseDif < 0) pulseDif = -pulseDif;
+
+ launchpadPulses++;
+
+ if (pulseDif <= 5)
+ {
+ launchpad5++;
+ }
+ else if (pulseDif <= 10)
+ {
+ launchpad10++;
+ }
+ else if (pulseDif <= 15)
+ {
+ launchpad15++;
+ }
+ else
+ {
+ launchpadOutRange++;
+ launchpadErr = pulseDif;
+ }
+ }
+ lastPulseLen = pulseLen;
+ }
+ }
+ else
+ {
+ lastTick = tick;
+ lastPulseLen = 0;
+
+ launchpadPulses = 0;
+ launchpad5 = 0;
+ launchpad10 = 0;
+ launchpad15 = 0;
+ launchpadOutRange = 0;
+
+ inited = 1;
+ }
+}
+
+void putTTY(char * buf)
+{
+ write(1, buf, strlen(buf));
+}
+
+void putTTYstr(int row, int col, char * buf)
+{
+ char str[256];
+
+ sprintf(str, "\033[%d;%dH%s", row, col, buf);
+
+ putTTY(str);
+}
+
--- /dev/null
+/*
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <http://unlicense.org/>
+*/
+
+/*
+This version is for pigpio version 3+
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <fcntl.h>
+
+#include "pigpio.h"
+
+/*
+This software converts pigpio notification reports
+into a VCD format understood by GTKWave.
+*/
+
+#define RS (sizeof(gpioReport_t))
+
+static char * timeStamp()
+{
+ static char buf[32];
+
+ struct timeval now;
+ struct tm tmp;
+
+ gettimeofday(&now, NULL);
+
+ localtime_r(&now.tv_sec, &tmp);
+ strftime(buf, sizeof(buf), "%F %T", &tmp);
+
+ return buf;
+}
+
+int symbol(int bit)
+{
+ if (bit < 26) return ('A' + bit);
+ else return ('a' + bit - 26);
+}
+
+int main(int argc, char * argv[])
+{
+ int b, r, v;
+ uint32_t t0;
+ uint32_t lastLevel, changed;
+
+ gpioReport_t report;
+
+ r=read(STDIN_FILENO, &report, RS);
+
+ if (r != RS) exit(-1);
+
+ printf("$date %s $end\n", timeStamp());
+ printf("$version pig2vcd V1 $end\n");
+ printf("$timescale 1 us $end\n");
+ printf("$scope module top $end\n");
+
+ for (b=0; b<32; b++)
+ printf("$var wire 1 %c %d $end\n", symbol(b), b);
+
+ printf("$upscope $end\n");
+ printf("$enddefinitions $end\n");
+
+ t0 = report.tick;
+ lastLevel =0;
+
+ while ((r=read(STDIN_FILENO, &report, RS)) == RS)
+ {
+ if (report.level != lastLevel)
+ {
+ printf("#%u\n", report.tick - t0);
+
+ changed = report.level ^ lastLevel;
+
+ lastLevel = report.level;
+
+ for (b=0; b<32; b++)
+ {
+ if (changed & (1<<b))
+ {
+ if (report.level & (1<<b)) v='1'; else v='0';
+
+ printf("%c%c\n", v, symbol(b));
+ }
+ }
+ }
+ }
+ return 0;
+}
+
--- /dev/null
+/*
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <http://unlicense.org/>
+*/
+
+/* pigpio version 6 */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <syslog.h>
+#include <poll.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <pthread.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <sys/select.h>
+
+#include "pigpio.h"
+#include "command.h"
+
+/* --------------------------------------------------------------- */
+
+/*
+ 0 GPFSEL0 GPIO Function Select 0
+ 1 GPFSEL1 GPIO Function Select 1
+ 2 GPFSEL2 GPIO Function Select 2
+ 3 GPFSEL3 GPIO Function Select 3
+ 4 GPFSEL4 GPIO Function Select 4
+ 5 GPFSEL5 GPIO Function Select 5
+ 6 - Reserved
+ 7 GPSET0 GPIO Pin Output Set 0
+ 8 GPSET1 GPIO Pin Output Set 1
+ 9 - Reserved
+10 GPCLR0 GPIO Pin Output Clear 0
+11 GPCLR1 GPIO Pin Output Clear 1
+12 - Reserved
+13 GPLEV0 GPIO Pin Level 0
+14 GPLEV1 GPIO Pin Level 1
+15 - Reserved
+16 GPEDS0 GPIO Pin Event Detect Status 0
+17 GPEDS1 GPIO Pin Event Detect Status 1
+18 - Reserved
+19 GPREN0 GPIO Pin Rising Edge Detect Enable 0
+20 GPREN1 GPIO Pin Rising Edge Detect Enable 1
+21 - Reserved
+22 GPFEN0 GPIO Pin Falling Edge Detect Enable 0
+23 GPFEN1 GPIO Pin Falling Edge Detect Enable 1
+24 - Reserved
+25 GPHEN0 GPIO Pin High Detect Enable 0
+26 GPHEN1 GPIO Pin High Detect Enable 1
+27 - Reserved
+28 GPLEN0 GPIO Pin Low Detect Enable 0
+29 GPLEN1 GPIO Pin Low Detect Enable 1
+30 - Reserved
+31 GPAREN0 GPIO Pin Async. Rising Edge Detect 0
+32 GPAREN1 GPIO Pin Async. Rising Edge Detect 1
+33 - Reserved
+34 GPAFEN0 GPIO Pin Async. Falling Edge Detect 0
+35 GPAFEN1 GPIO Pin Async. Falling Edge Detect 1
+36 - Reserved
+37 GPPUD GPIO Pin Pull-up/down Enable
+38 GPPUDCLK0 GPIO Pin Pull-up/down Enable Clock 0
+39 GPPUDCLK1 GPIO Pin Pull-up/down Enable Clock 1
+40 - Reserved
+41 - Test
+*/
+
+/*
+0 CS DMA Channel 0 Control and Status
+1 CPI_ONBLK_AD DMA Channel 0 Control Block Address
+2 TI DMA Channel 0 CB Word 0 (Transfer Information)
+3 SOURCE_AD DMA Channel 0 CB Word 1 (Source Address)
+4 DEST_AD DMA Channel 0 CB Word 2 (Destination Address)
+5 TXFR_LEN DMA Channel 0 CB Word 3 (Transfer Length)
+6 STRIDE DMA Channel 0 CB Word 4 (2D Stride)
+7 NEXTCPI_ONBK DMA Channel 0 CB Word 5 (Next CB Address)
+8 DEBUG DMA Channel 0 Debug
+*/
+
+/*
+DEBUG register bits
+
+bit 2 READ_ERROR
+
+ Slave Read Response Error RW 0x0
+
+ Set if the read operation returned an error value on
+ the read response bus. It can be cleared by writing
+ a 1.
+
+bit 1 FIFO_ERROR
+
+ Fifo Error RW 0x0
+
+ Set if the optional read Fifo records an error
+ condition. It can be cleared by writing a 1.
+
+bit 0 READ_LAST_NOT_SET_ERROR
+
+ Read Last Not Set Error RW 0x0
+
+ If the AXI read last signal was not set when
+ expected, then this error bit will be set. It can be
+ cleared by writing a 1.
+*/
+
+/*
+0 CTL PWM Control
+1 STA PWM Status
+2 DMAC PWM DMA Configuration
+4 RNG1 PWM Channel 1 Range
+5 DAT1 PWM Channel 1 Data
+6 FIF1 PWM FIFO Input
+8 RNG2 PWM Channel 2 Range
+9 DAT2 PWM Channel 2 Data
+*/
+
+/*
+0 PCM_CS PCM Control and Status
+1 PCM_FIFO PCM FIFO Data
+2 PCM_MODE PCM Mode
+3 PCM_RXC PCM Receive Configuration
+4 PCM_TXC PCM Transmit Configuration
+5 PCM_DREQ PCM DMA Request Level
+6 PCM_INTEN PCM Interrupt Enables
+7 PCM_INTSTC PCM Interrupt Status & Clear
+8 PCM_GRAY PCM Gray Mode Control
+*/
+
+/*
+0 CS System Timer Control/Status
+1 CLO System Timer Counter Lower 32 bits
+2 CHI System Timer Counter Higher 32 bits
+3 C0 System Timer Compare 0
+4 C1 System Timer Compare 1
+5 C2 System Timer Compare 2
+6 C3 System Timer Compare 3
+*/
+
+/* --------------------------------------------------------------- */
+
+#define THOUSAND 1000
+#define MILLION 1000000
+#define BILLION 1000000000
+
+#define BANK (gpio>>5)
+
+#define BIT (1<<(gpio&0x1F))
+
+
+#define CHECK_INITED \
+ do \
+ { \
+ if (!libInitialised) \
+ { \
+ fprintf(stderr, \
+ "%s %s: pigpio uninitialised, call gpioInitialise()\n",\
+ myTimeStamp(), __FUNCTION__); \
+ return PI_NOT_INITIALISED; \
+ } \
+ } \
+ while (0)
+
+#define CHECK_NOT_INITED \
+ do \
+ { \
+ if (libInitialised) \
+ { \
+ fprintf(stderr, \
+ "%s %s: pigpio initialised, call gpioTerminate()\n", \
+ myTimeStamp(), __FUNCTION__); \
+ return PI_INITIALISED; \
+ } \
+ } \
+ while (0)
+
+#define DBG(level, format, arg...) \
+ do \
+ { \
+ if (gpioCfg.dbgLevel >= level) \
+ fprintf(stderr, "%s %s: " format "\n" , \
+ myTimeStamp(), __FUNCTION__ , ## arg); \
+ } \
+ while (0)
+
+#define SOFT_ERROR(x, format, arg...) \
+ do \
+ { \
+ fprintf(stderr, "%s %s: " format "\n", \
+ myTimeStamp(), __FUNCTION__ , ## arg); \
+ return x; \
+ } \
+ while (0)
+
+#define TIMER_ADD(a, b, result) \
+ do \
+ { \
+ (result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
+ (result)->tv_nsec = (a)->tv_nsec + (b)->tv_nsec; \
+ if ((result)->tv_nsec >= BILLION) \
+ { \
+ ++(result)->tv_sec; \
+ (result)->tv_nsec -= BILLION; \
+ } \
+ } \
+ while (0)
+
+#define TIMER_SUB(a, b, result) \
+ do \
+ { \
+ (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
+ (result)->tv_nsec = (a)->tv_nsec - (b)->tv_nsec; \
+ if ((result)->tv_nsec < 0) \
+ { \
+ --(result)->tv_sec; \
+ (result)->tv_nsec += BILLION; \
+ } \
+ } \
+ while (0)
+
+#define DMA_BUS_ADR 0x40000000
+
+#define CLK_BASE 0x20101000
+#define DMA_BASE 0x20007000
+#define DMA15_BASE 0x20E05000
+#define GPIO_BASE 0x20200000
+#define PCM_BASE 0x20203000
+#define PWM_BASE 0x2020C000
+#define SPI0_BASE 0x20204000
+#define SYST_BASE 0x20003000
+#define UART0_BASE 0x20201000
+#define UART1_BASE 0x20215000
+
+#define DMA_LEN 0x1000 /* allow access to all channels */
+#define CLK_LEN 0xA8
+#define GPIO_LEN 0xB4
+#define SYST_LEN 0x1C
+#define PCM_LEN 0x24
+#define PWM_LEN 0x28
+
+#define DMA_ENABLE (0xFF0/4)
+
+#define GPFSEL0 0
+
+#define GPSET0 7
+#define GPSET1 8
+
+#define GPCLR0 10
+#define GPCLR1 11
+
+#define GPLEV0 13
+#define GPLEV1 14
+
+#define GPEDS0 16
+#define GPEDS1 17
+
+#define GPREN0 19
+#define GPREN1 20
+#define GPFEN0 22
+#define GPFEN1 23
+#define GPHEN0 25
+#define GPHEN1 26
+#define GPLEN0 28
+#define GPLEN1 29
+#define GPAREN0 31
+#define GPAREN1 32
+#define GPAFEN0 34
+#define GPAFEN1 35
+
+#define GPPUD 37
+#define GPPUDCLK0 38
+#define GPPUDCLK1 39
+
+#define DMA_CS 0
+#define DMA_CONBLK_AD 1
+#define DMA_DEBUG 8
+
+/* DMA CS Control and Status bits */
+#define DMA_CHANNEL_RESET (1<<31)
+#define DMA_WAIT_ON_WRITES (1<<28)
+#define DMA_PANIC_PRIORITY(x) ((x)<<20)
+#define DMA_PRIORITY(x) ((x)<<16)
+#define DMA_INTERRUPT_STATUS (1<< 2)
+#define DMA_END_FLAG (1<< 1)
+#define DMA_ACTIVATE (1<< 0)
+
+/* DMA control block "info" field bits */
+#define DMA_NO_WIDE_BURSTS (1<<26)
+#define DMA_PERIPHERAL_MAPPING(x) ((x)<<16)
+#define DMA_BURST_LENGTH(x) ((x)<<12)
+#define DMA_SRC_IGNORE (1<<11)
+#define DMA_SRC_DREQ (1<<10)
+#define DMA_SRC_INC (1<< 8)
+#define DMA_DEST_IGNORE (1<< 7)
+#define DMA_DEST_DREQ (1<< 6)
+#define DMA_DEST_INC (1<< 4)
+#define DMA_WAIT_RESP (1<< 3)
+
+#define DMA_DEBUG_READ_ERR (1<<2)
+#define DMA_DEBUG_FIFO_ERR (1<<1)
+#define DMA_DEBUG_RD_LST_NOT_SET_ERR (1<<0)
+
+#define PWM_CTL 0
+#define PWM_STA 1
+#define PWM_DMAC 2
+#define PWM_RNG1 4
+#define PWM_DAT1 5
+#define PWM_FIFO 6
+#define PWM_RNG2 8
+#define PWM_DAT2 9
+
+#define PWM_CTL_CLRF1 (1<<6)
+#define PWM_CTL_USEF1 (1<<5)
+#define PWM_CTL_MODE1 (1<<1)
+#define PWM_CTL_PWEN1 (1<<0)
+
+#define PWM_DMAC_ENAB (1 <<31)
+#define PWM_DMAC_PANIC(x) ((x)<< 8)
+#define PWM_DMAC_DREQ(x) (x)
+
+#define PCM_CS 0
+#define PCM_FIFO 1
+#define PCM_MODE 2
+#define PCM_RXC 3
+#define PCM_TXC 4
+#define PCM_DREQ 5
+#define PCM_INTEN 6
+#define PCM_INTSTC 7
+#define PCM_GRAY 8
+
+#define PCM_CS_STBY (1 <<25)
+#define PCM_CS_SYNC (1 <<24)
+#define PCM_CS_RXSEX (1 <<23)
+#define PCM_CS_RXERR (1 <<16)
+#define PCM_CS_TXERR (1 <<15)
+#define PCM_CS_DMAEN (1 <<9)
+#define PCM_CS_RXTHR(x) ((x)<<7)
+#define PCM_CS_TXTHR(x) ((x)<<5)
+#define PCM_CS_RXCLR (1 <<4)
+#define PCM_CS_TXCLR (1 <<3)
+#define PCM_CS_TXON (1 <<2)
+#define PCM_CS_RXON (1 <<1)
+#define PCM_CS_EN (1 <<0)
+
+#define PCM_MODE_CLK_DIS (1 <<28)
+#define PCM_MODE_PDMN (1 <<27)
+#define PCM_MODE_PDME (1 <<26)
+#define PCM_MODE_FRXP (1 <<25)
+#define PCM_MODE_FTXP (1 <<24)
+#define PCM_MODE_CLKM (1 <<23)
+#define PCM_MODE_CLKI (1 <<22)
+#define PCM_MODE_FSM (1 <<21)
+#define PCM_MODE_FSI (1 <<20)
+#define PCM_MODE_FLEN(x) ((x)<<10)
+#define PCM_MODE_FSLEN(x) ((x)<< 0)
+
+#define PCM_RXC_CH1WEX (1 <<31)
+#define PCM_RXC_CH1EN (1 <<30)
+#define PCM_RXC_CH1POS(x) ((x)<<20)
+#define PCM_RXC_CH1WID(x) ((x)<<16)
+#define PCM_RXC_CH2WEX (1 <<15)
+#define PCM_RXC_CH2EN (1 <<14)
+#define PCM_RXC_CH2POS(x) ((x)<< 4)
+#define PCM_RXC_CH2WID(x) ((x)<< 0)
+
+#define PCM_TXC_CH1WEX (1 <<31)
+#define PCM_TXC_CH1EN (1 <<30)
+#define PCM_TXC_CH1POS(x) ((x)<<20)
+#define PCM_TXC_CH1WID(x) ((x)<<16)
+#define PCM_TXC_CH2WEX (1 <<15)
+#define PCM_TXC_CH2EN (1 <<14)
+#define PCM_TXC_CH2POS(x) ((x)<< 4)
+#define PCM_TXC_CH2WID(x) ((x)<< 0)
+
+#define PCM_DREQ_TX_PANIC(x) ((x)<<24)
+#define PCM_DREQ_RX_PANIC(x) ((x)<<16)
+#define PCM_DREQ_TX_REQ_L(x) ((x)<< 8)
+#define PCM_DREQ_RX_REQ_L(x) ((x)<< 0)
+
+#define PCM_INTEN_RXERR (1<<3)
+#define PCM_INTEN_TXERR (1<<2)
+#define PCM_INTEN_RXR (1<<1)
+#define PCM_INTEN_TXW (1<<0)
+
+#define PCM_INTSTC_RXERR (1<<3)
+#define PCM_INTSTC_TXERR (1<<2)
+#define PCM_INTSTC_RXR (1<<1)
+#define PCM_INTSTC_TXW (1<<0)
+
+#define PCM_GRAY_FLUSH (1<<2)
+#define PCM_GRAY_CLR (1<<1)
+#define PCM_GRAY_EN (1<<0)
+
+#define CLK_PASSWD (0x5A<<24)
+
+#define CLK_CTL_MASH(x)((x)<<9)
+#define CLK_CTL_BUSY (1 <<7)
+#define CLK_CTL_KILL (1 <<5)
+#define CLK_CTL_ENAB (1 <<4)
+#define CLK_CTL_SRC(x) ((x)<<0)
+
+#define CLK_CTL_SRC_OSC 1 /* 19.2 MHz */
+#define CLK_CTL_SRC_PLLD 6 /* 500.0 MHz */
+
+#define CLK_DIV_DIVI(x) ((x)<<12)
+#define CLK_DIV_DIVF(x) ((x)<< 0)
+
+#define CLK_PCMCTL 38
+#define CLK_PCMDIV 39
+
+#define CLK_PWMCTL 40
+#define CLK_PWMDIV 41
+
+#define SYST_CS 0
+#define SYST_CLO 1
+#define SYST_CHI 2
+
+/* --------------------------------------------------------------- */
+
+#define NORMAL_DMA (DMA_NO_WIDE_BURSTS | DMA_WAIT_RESP)
+
+#define TIMED_DMA(x) (DMA_DEST_DREQ | DMA_PERIPHERAL_MAPPING(x))
+
+#define DBG_MIN_LEVEL 0
+#define DBG_STARTUP 1
+#define DBG_DMACBS 2
+#define DBG_USER 3
+#define DBG_INTERNAL 4
+#define DBG_SLOW_TICK 5
+#define DBG_FAST_TICK 6
+#define DBG_MAX_LEVEL 6
+
+#define GPIO_UNDEFINED 0
+#define GPIO_INPUT 1
+#define GPIO_OUTPUT 2
+#define GPIO_PWM 3
+#define GPIO_SERVO 4
+#define GPIO_ALTERNATE 5
+
+#define STACK_SIZE (256*1024)
+
+#define PAGE_SIZE 4096
+
+#define PWM_FREQS 18
+
+#define CYCLES_PER_BLOCK 80
+#define PULSE_PER_CYCLE 25
+
+#define PAGES_PER_BLOCK 53
+
+#define CBS_PER_IPAGE 117
+#define LVS_PER_IPAGE 38
+#define OFF_PER_IPAGE 38
+#define TCK_PER_IPAGE 2
+#define ON_PER_IPAGE 2
+
+#define CBS_PER_OPAGE 118
+#define ONOFF_PER_OPAGE 79
+
+#define CBS_PER_CYCLE ((PULSE_PER_CYCLE*3)+2)
+
+#define NUM_CBS (CBS_PER_CYCLE * bufferCycles)
+
+#define SUPERCYCLE 800
+#define SUPERLEVEL 20000
+
+#define BLOCK_SIZE (PAGES_PER_BLOCK*PAGE_SIZE)
+
+#define DMA_PAGES (PAGES_PER_BLOCK * bufferBlocks)
+
+#define TICKSLOTS 50
+
+#define PI_NOTIFY_SLOTS 32
+
+#define PI_NOTIFY_CLOSED 0
+#define PI_NOTIFY_CLOSING 1
+#define PI_NOTIFY_OPENED 2
+#define PI_NOTIFY_RUNNING 3
+#define PI_NOTIFY_PAUSED 4
+
+#define PI_WFRX_NONE 0
+#define PI_WFRX_SERIAL 1
+#define PI_WF_MICROS 2
+
+#define PI_WAVE_MAX_PULSES 3000
+
+#define DATUMS 2000
+
+#define DEFAULT_PWM_IDX 5
+
+#define MAX_EMITS (PIPE_BUF / sizeof(gpioReport_t))
+
+/* --------------------------------------------------------------- */
+
+typedef void (*callbk_t) ();
+
+typedef struct { /* linux/arch/arm/mach-bcm2708/include/mach/dma.h */
+ unsigned long info;
+ unsigned long src;
+ unsigned long dst;
+ unsigned long length;
+ unsigned long stride;
+ unsigned long next;
+ unsigned long pad[2];
+} dmaCbs_t;
+
+typedef struct
+{
+ dmaCbs_t cb [128];
+} dmaPage_t;
+
+typedef struct
+{
+ dmaCbs_t cb [CBS_PER_IPAGE];
+ uint32_t level [LVS_PER_IPAGE];
+ uint32_t gpioOff [OFF_PER_IPAGE];
+ uint32_t tick [TCK_PER_IPAGE];
+ uint32_t gpioOn [ON_PER_IPAGE];
+ uint32_t periphData;
+ uint32_t pad[7];
+} dmaIPage_t;
+
+typedef struct
+{
+ dmaCbs_t cb [CBS_PER_OPAGE];
+ uint32_t gpioOnOff [ONOFF_PER_OPAGE];
+ uint32_t periphData;
+} dmaOPage_t;
+
+typedef struct
+{
+ uint8_t is;
+ uint8_t pad;
+ uint16_t width;
+ uint16_t range; /* duty cycles specified by 0 .. range */
+ uint16_t freqIdx;
+} gpioInfo_t;
+
+typedef struct
+{
+ callbk_t func;
+ unsigned ex;
+ void * userdata;
+ unsigned timeout;
+ uint32_t tick;
+} gpioAlert_t;
+
+typedef struct
+{
+ callbk_t func;
+ unsigned ex;
+ void * userdata;
+} gpioSignal_t;
+
+typedef struct
+{
+ callbk_t func;
+ unsigned ex;
+ void * userdata;
+ uint32_t bits;
+} gpioGetSamples_t;
+
+typedef struct
+{
+ callbk_t func;
+ unsigned ex;
+ void * userdata;
+ unsigned id;
+ unsigned running;
+ unsigned millis;
+ struct timespec nextTick;
+ pthread_t pthId;
+} gpioTimer_t;
+
+typedef struct
+{
+ uint16_t valid;
+ uint16_t bits;
+ uint16_t divi;
+ uint16_t divf;
+ uint16_t mash;
+ uint16_t servoIdx;
+ uint16_t pwmIdx;
+} clkCfg_t;
+
+typedef struct
+{
+ uint16_t seqno;
+ uint16_t state;
+ uint32_t bits;
+ int fd;
+ int pipe;
+} gpioNotify_t;
+
+typedef struct
+{
+ uint32_t startTick;
+ uint32_t alertTicks;
+ uint32_t diffTick[TICKSLOTS];
+ uint32_t cbTicks;
+ uint32_t cbCalls;
+ uint32_t maxEmit;
+ uint32_t emitFrags;
+ uint32_t maxSamples;
+ uint32_t numSamples;
+} gpioStats_t;
+
+typedef struct
+{
+ unsigned bufferMilliseconds;
+ unsigned clockMicros;
+ unsigned clockPeriph;
+ unsigned clockSource;
+ unsigned DMAprimaryChannel;
+ unsigned DMAsecondaryChannel;
+ unsigned socketPort;
+ unsigned ifFlags;
+ int dbgLevel;
+ unsigned showStats;
+} gpioCfg_t;
+
+typedef struct
+{
+ uint32_t micros;
+ uint32_t highMicros;
+ uint32_t maxMicros;
+ uint32_t pulses;
+ uint32_t highPulses;
+ uint32_t maxPulses;
+ uint32_t cbs;
+ uint32_t highCbs;
+ uint32_t maxCbs;
+} wfStats_t;
+
+typedef struct
+{
+ gpioRx_t * rxp;
+ uint32_t baud;
+ uint32_t fullBit;
+ uint32_t halfBit;
+ uint32_t startBitTick;
+ uint32_t nextBitDiff;
+ int bit;
+ int byte;
+ int level;
+ int mode;
+} wfRx_t;
+
+
+/* --------------------------------------------------------------- */
+
+/* initialise once then preserve */
+
+static volatile gpioCfg_t gpioCfg =
+{
+ PI_DEFAULT_BUFFER_MILLIS,
+ PI_DEFAULT_CLK_MICROS,
+ PI_DEFAULT_CLK_PERIPHERAL,
+ PI_DEFAULT_CLK_SOURCE,
+ PI_DEFAULT_DMA_PRIMARY_CHANNEL,
+ PI_DEFAULT_DMA_SECONDARY_CHANNEL,
+ PI_DEFAULT_SOCKET_PORT,
+ PI_DEFAULT_IF_FLAGS,
+ 0,
+ 0,
+};
+
+static volatile gpioStats_t gpioStats;
+
+/* initialise every gpioInitialise */
+
+static struct timespec libStarted;
+
+/* initialse if not libInitialised */
+
+static gpioPulse_t wf[3][PI_WAVE_MAX_PULSES];
+
+static int wfc[3]={0, 0, 0};
+
+static int wfcur=0;
+
+static wfStats_t wfStats=
+{
+ 0, 0, -1,
+ 0, 0, PI_WAVE_MAX_PULSES,
+ 0, 0, (PAGES_PER_BLOCK * CBS_PER_OPAGE)
+};
+
+static wfRx_t wfRx[PI_MAX_USER_GPIO+1];
+
+static volatile uint32_t alertBits = 0;
+static volatile uint32_t monitorBits = 0;
+static volatile uint32_t notifyBits = 0;
+
+static volatile int DMAstarted = 0;
+
+static int libInitialised = 0;
+static unsigned hardwareRevision = 0;
+
+static int pthAlertRunning = 0;
+static int pthFifoRunning = 0;
+static int pthSocketRunning = 0;
+
+static gpioAlert_t gpioAlert [PI_MAX_USER_GPIO+1];
+
+static gpioGetSamples_t gpioGetSamples;
+
+static gpioInfo_t gpioInfo [PI_MAX_USER_GPIO+1];
+
+static gpioNotify_t gpioNotify [PI_NOTIFY_SLOTS];
+
+static gpioSignal_t gpioSignal [PI_MAX_SIGNUM+1];
+
+static gpioTimer_t gpioTimer [PI_MAX_TIMER+1];
+
+static int pwmFreq[PWM_FREQS];
+
+/* no initialisation required */
+
+static unsigned bufferBlocks; /* number of blocks in buffer */
+static unsigned bufferCycles; /* number of cycles */
+
+static pthread_attr_t pthAttr;
+
+static pthread_t pthAlert;
+static pthread_t pthFifo;
+static pthread_t pthSocket;
+
+static gpioSample_t gpioSample[DATUMS];
+static gpioReport_t gpioReport[DATUMS];
+
+/* resources which must be released on gpioTerminate */
+
+static FILE * inpFifo = NULL;
+static FILE * outFifo = NULL;
+
+static int fdLock = -1;
+static int fdMem = -1;
+static int fdSock = -1;
+
+static dmaPage_t * * dmaBloc = MAP_FAILED;
+static dmaPage_t * * dmaVirt = MAP_FAILED;
+static dmaPage_t * * dmaPhys = MAP_FAILED;
+
+static dmaIPage_t * * dmaIVirt = MAP_FAILED;
+static dmaIPage_t * * dmaIPhys = MAP_FAILED;
+
+static dmaOPage_t * * dmaOVirt = MAP_FAILED;
+static dmaOPage_t * * dmaOPhys = MAP_FAILED;
+
+static volatile uint32_t * clkReg = MAP_FAILED;
+static volatile uint32_t * dmaReg = MAP_FAILED;
+static volatile uint32_t * gpioReg = MAP_FAILED;
+static volatile uint32_t * pcmReg = MAP_FAILED;
+static volatile uint32_t * pwmReg = MAP_FAILED;
+static volatile uint32_t * systReg = MAP_FAILED;
+
+static volatile uint32_t * dmaIn = MAP_FAILED;
+static volatile uint32_t * dmaOut = MAP_FAILED;
+
+/* constant data */
+
+static const clkCfg_t clkCfg[]=
+{
+ /* valid bits divi divf mash servo pwm */
+ { 0, 0, 0, 0, 0, 0, 0}, /* 0 */
+ { 1, 9, 2, 546, 1, 17, DEFAULT_PWM_IDX}, /* 1 */
+ { 1, 19, 2, 86, 1, 16, DEFAULT_PWM_IDX}, /* 2 */
+ { 0, 19, 3, 129, 1, 0, 0}, /* 3 */
+ { 1, 11, 6, 4021, 1, 15, DEFAULT_PWM_IDX}, /* 4 */
+ { 1, 8, 12, 0, 0, 14, DEFAULT_PWM_IDX}, /* 5 */
+ { 0, 23, 5, 35, 1, 0, 0}, /* 6 */
+ { 0, 27, 4004, 0, 1, 0, 0}, /* 7 */
+ { 1, 51, 3, 48, 1, 13, DEFAULT_PWM_IDX}, /* 8 */
+ { 0, 43, 4, 76, 1, 0, 0}, /* 9 */
+ { 1, 8, 24, 0, 0, 12, DEFAULT_PWM_IDX}, /* 10 */
+};
+
+static const uint16_t pwmCycles[PWM_FREQS]=
+ { 1, 2, 4, 5, 8, 10, 16, 20, 25,
+ 32, 40, 50, 80, 100, 160, 200, 400, 800};
+
+static const uint16_t pwmRealRange[PWM_FREQS]=
+ { 25, 50, 100, 125, 200, 250, 400, 500, 625,
+ 800, 1000, 1250, 2000, 2500, 4000, 5000, 10000, 20000};
+
+/* ======================================================================= */
+
+static void intNotifyBits(void);
+
+static int gpioNotifyOpenInBand(int fd);
+
+static void myGpioSleep(int seconds, int micros)
+{
+ struct timespec ts, rem;
+
+ ts.tv_sec = seconds;
+ ts.tv_nsec = micros * 1000;
+
+ while (clock_nanosleep(CLOCK_REALTIME, 0, &ts, &rem))
+ {
+ /* copy remaining time to ts */
+ ts.tv_sec = rem.tv_sec;
+ ts.tv_nsec = rem.tv_nsec;
+ }
+}
+
+static uint32_t myGpioDelay(uint32_t micros)
+{
+ uint32_t start;
+
+ start = systReg[SYST_CLO];
+
+ if (micros < 100) while ((systReg[SYST_CLO] - start) <= micros) ;
+
+ else myGpioSleep(micros/MILLION, micros%MILLION);
+
+ return (systReg[SYST_CLO] - start);
+}
+
+static char * myTimeStamp()
+{
+ static struct timeval last;
+ static char buf[32];
+ struct timeval now;
+
+ struct tm tmp;
+
+ gettimeofday(&now, NULL);
+
+ if (now.tv_sec != last.tv_sec)
+ {
+ localtime_r(&now.tv_sec, &tmp);
+ strftime(buf, sizeof(buf), "%F %T", &tmp);
+ last.tv_sec = now.tv_sec;
+ }
+
+ return buf;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void myCreatePipe(char * name, int perm)
+{
+ unlink(name);
+
+ mkfifo(name, perm);
+
+ if (chmod(name, perm) < 0)
+ {
+ DBG(DBG_MIN_LEVEL, "Can't set permissions (%d) for %s, %m", perm, name);
+ return;
+ }
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void myOffPageSlot(int pos, int * page, int * slot)
+{
+ *page = pos/OFF_PER_IPAGE;
+ *slot = pos%OFF_PER_IPAGE;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void myLvsPageSlot(int pos, int * page, int * slot)
+{
+ *page = pos/LVS_PER_IPAGE;
+ *slot = pos%LVS_PER_IPAGE;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void myTckPageSlot(int pos, int * page, int * slot)
+{
+ *page = pos/TCK_PER_IPAGE;
+ *slot = pos%TCK_PER_IPAGE;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static uint32_t myGetLevel(int pos)
+{
+ uint32_t level;
+ int page, slot;
+
+ myLvsPageSlot(pos, &page, &slot);
+
+ level = dmaIVirt[page]->level[slot];
+
+ return level;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static uint32_t myGetTick(int pos)
+{
+ uint32_t tick;
+ int page, slot;
+
+ myTckPageSlot(pos, &page, &slot);
+
+ tick = dmaIVirt[page]->tick[slot];
+
+ return tick;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void myDoCommand(cmdCmd_t * cmd)
+{
+ int p1, p2, res;
+
+ p1 = cmd->p1;
+ p2 = cmd->p2;
+
+ res = 0;
+
+ switch (cmd->cmd)
+ {
+ case PI_CMD_MODES:
+ res = gpioSetMode(p1, p2);
+ break;
+
+ case PI_CMD_MODEG:
+ res = gpioGetMode(p1);
+ break;
+
+ case PI_CMD_PUD:
+ res = gpioSetPullUpDown(p1, p2);
+ break;
+
+ case PI_CMD_READ:
+ res = gpioRead(p1);
+ break;
+
+ case PI_CMD_WRITE:
+ res = gpioWrite(p1, p2);
+ break;
+
+ case PI_CMD_PWM:
+ res = gpioPWM(p1, p2);
+ break;
+
+ case PI_CMD_PRS:
+ res = gpioSetPWMrange(p1, p2);
+ break;
+
+ case PI_CMD_PFS:
+ res = gpioSetPWMfrequency(p1, p2);
+ break;
+
+ case PI_CMD_SERVO:
+ res = gpioServo(p1, p2);
+ break;
+
+ case PI_CMD_WDOG:
+ res = gpioSetWatchdog(p1, p2);
+ break;
+
+ case PI_CMD_BR1:
+ res = gpioRead_Bits_0_31();
+ break;
+
+ case PI_CMD_BR2:
+ res = gpioRead_Bits_32_53();
+ break;
+
+ case PI_CMD_BC1:
+ gpioWrite_Bits_0_31_Clear(p1);
+ break;
+
+ case PI_CMD_BC2:
+ gpioWrite_Bits_32_53_Clear(p1);
+ break;
+
+ case PI_CMD_BS1:
+ gpioWrite_Bits_0_31_Set(p1);
+ break;
+
+ case PI_CMD_BS2:
+ gpioWrite_Bits_32_53_Set(p1);
+ break;
+
+ case PI_CMD_TICK:
+ res = gpioTick();
+ break;
+
+ case PI_CMD_HWVER:
+ res = gpioHardwareRevision();
+ break;
+
+ case PI_CMD_PRG:
+ res = gpioGetPWMrange(p1);
+ break;
+
+ case PI_CMD_PFG:
+ res = gpioGetPWMfrequency(p1);
+ break;
+
+ case PI_CMD_PRRG:
+ res = gpioGetPWMrealRange(p1);
+ break;
+
+ case PI_CMD_NO:
+ res = gpioNotifyOpen();
+ break;
+
+ case PI_CMD_NB:
+ res = gpioNotifyBegin(p1, p2);
+ break;
+
+ case PI_CMD_NP:
+ res = gpioNotifyPause(p1);
+ break;
+
+ case PI_CMD_NC:
+ res = gpioNotifyClose(p1);
+ break;
+
+ case PI_CMD_HELP:
+ break;
+
+ }
+
+ cmd->res = res;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void mySetGpioOff(unsigned gpio, int pos)
+{
+ int page, slot;
+
+ myOffPageSlot(pos, &page, &slot);
+
+ dmaIVirt[page]->gpioOff[slot] |= (1<<gpio);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void myClearGpioOff(unsigned gpio, int pos)
+{
+ int page, slot;
+
+ myOffPageSlot(pos, &page, &slot);
+
+ dmaIVirt[page]->gpioOff[slot] &= ~(1<<gpio);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void mySetGpioOn(unsigned gpio, int pos)
+{
+ int page, slot;
+
+ page = pos/ON_PER_IPAGE;
+ slot = pos%ON_PER_IPAGE;
+
+ dmaIVirt[page]->gpioOn[slot] |= (1<<gpio);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void myClearGpioOn(unsigned gpio, int pos)
+{
+ int page, slot;
+
+ page = pos/ON_PER_IPAGE;
+ slot = pos%ON_PER_IPAGE;
+
+ dmaIVirt[page]->gpioOn[slot] &= ~(1<<gpio);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void myGpioSetPwm(unsigned gpio, int oldVal, int newVal)
+{
+ int switchGpioOff;
+ int newOff, oldOff, realRange, cycles, i;
+
+ DBG(DBG_INTERNAL,
+ "myGpioSetPwm %d from %d to %d", gpio, oldVal, newVal);
+
+ switchGpioOff = 0;
+
+ realRange = pwmRealRange[gpioInfo[gpio].freqIdx];
+
+ cycles = pwmCycles [gpioInfo[gpio].freqIdx];
+
+ newOff = (newVal * realRange)/gpioInfo[gpio].range;
+ oldOff = (oldVal * realRange)/gpioInfo[gpio].range;
+
+ if (newOff != oldOff)
+ {
+ if (newOff && oldOff) /* PWM CHANGE */
+ {
+ for (i=0; i<SUPERLEVEL; i+=realRange)
+ mySetGpioOff(gpio, i+newOff);
+
+ for (i=0; i<SUPERLEVEL; i+=realRange)
+ myClearGpioOff(gpio, i+oldOff);
+ }
+ else if (newOff) /* PWM START */
+ {
+ for (i=0; i<SUPERLEVEL; i+=realRange)
+ mySetGpioOff(gpio, i+newOff);
+
+ /* schedule new gpio on */
+
+ for (i=0; i<SUPERCYCLE; i+=cycles) mySetGpioOn(gpio, i);
+ }
+ else /* PWM STOP */
+ {
+ /* deschedule gpio on */
+
+ for (i=0; i<SUPERCYCLE; i+=cycles)
+ myClearGpioOn(gpio, i);
+
+ for (i=0; i<SUPERLEVEL; i+=realRange)
+ myClearGpioOff(gpio, i+oldOff);
+
+ switchGpioOff = 1;
+ }
+
+ if (switchGpioOff)
+ {
+ *(gpioReg + GPCLR0) = (1<<gpio);
+ *(gpioReg + GPCLR0) = (1<<gpio);
+ }
+ }
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void myGpioSetServo(unsigned gpio, int oldVal, int newVal)
+{
+ int switchGpioOff;
+ int newOff, oldOff, realRange, cycles, i;
+
+ DBG(DBG_INTERNAL,
+ "myGpioSetServo %d from %d to %d", gpio, oldVal, newVal);
+
+ switchGpioOff = 0;
+
+ realRange = pwmRealRange[gpioInfo[gpio].freqIdx];
+ cycles = pwmCycles [gpioInfo[gpio].freqIdx];
+
+ newOff = (newVal * realRange)/20000;
+ oldOff = (oldVal * realRange)/20000;
+
+ if (newOff != oldOff)
+ {
+ if (newOff && oldOff) /* SERVO CHANGE */
+ {
+ for (i=0; i<SUPERLEVEL; i+=realRange)
+ mySetGpioOff(gpio, i+newOff);
+
+ for (i=0; i<SUPERLEVEL; i+=realRange)
+ myClearGpioOff(gpio, i+oldOff);
+ }
+ else if (newOff) /* SERVO START */
+ {
+ for (i=0; i<SUPERLEVEL; i+=realRange)
+ mySetGpioOff(gpio, i+newOff);
+
+ /* schedule new gpio on */
+
+ for (i=0; i<SUPERCYCLE; i+=cycles)
+ mySetGpioOn(gpio, i);
+ }
+ else /* SERVO STOP */
+ {
+ /* deschedule gpio on */
+
+ for (i=0; i<SUPERCYCLE; i+=cycles)
+ myClearGpioOn(gpio, i);
+
+ for (i=0; i<SUPERLEVEL; i+=realRange)
+ myClearGpioOff(gpio, i+oldOff);
+
+ switchGpioOff = 1;
+ }
+
+ if (switchGpioOff)
+ {
+ *(gpioReg + GPCLR0) = (1<<gpio);
+ *(gpioReg + GPCLR0) = (1<<gpio);
+ }
+ }
+}
+
+/* ======================================================================= */
+
+static dmaCbs_t * waveCbVOadr(int pos)
+{
+ int page, slot;
+
+ page = pos/CBS_PER_OPAGE;
+ slot = pos%CBS_PER_OPAGE;
+
+ return &dmaOVirt[page]->cb[slot];
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+static uint32_t waveCbPOadr(int pos)
+{
+ int page, slot;
+
+ page = pos/CBS_PER_OPAGE;
+ slot = pos%CBS_PER_OPAGE;
+
+ return (uint32_t) &dmaOPhys[page]->cb[slot];
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+static uint32_t waveOnOffPOadr(int pos)
+{
+ int page, slot;
+
+ page = pos/ONOFF_PER_OPAGE;
+ slot = pos%ONOFF_PER_OPAGE;
+
+ return (uint32_t) &dmaOPhys[page]->gpioOnOff[slot];
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+static void waveCbOPrint(int pos)
+{
+ dmaCbs_t * p;
+
+ p = waveCbVOadr(pos);
+
+ fprintf(stderr, "i=%lx s=%lx d=%lx len=%lx s=%lx nxt=%lx",
+ p->info, p->src, p->dst, p->length, p->stride, p->next);
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+void waveBitDelay(unsigned baud, unsigned * bitDelay)
+{
+ unsigned fullBit, halfBit, s, e, d, m, i, err;
+
+ fullBit = 100000000 / baud;
+ halfBit = 50000000 / baud;
+
+ d = (fullBit/200)*200;
+
+ s = 0;
+
+ e = d;
+
+ bitDelay[0] = d/100;
+
+ err = d / 3;
+
+ for (i=0; i<8; i++)
+ {
+ s = e;
+
+ m = halfBit + (i+1)*fullBit;
+
+ e = s + d;
+
+ if ((e-m) < err) e+=200;
+
+ bitDelay[i+1] = (e-s)/100;
+ }
+
+ s = e;
+
+ e = ((1000000000 / baud)+100)/200*200;
+
+ bitDelay[9] = (e-s)/100;
+}
+
+/* ----------------------------------------------------------------------- */
+
+void gpioWaveDump(void)
+{
+ int i;
+
+ unsigned numPulses, t;
+
+ gpioPulse_t * pulses;
+
+ numPulses = wfc[wfcur];
+ pulses = wf [wfcur];
+
+ t = 0;
+
+ for (i=0; i<numPulses; i++)
+ {
+ printf("%10u %08X %08X %10u\n",
+ t, pulses[i].gpioOn, pulses[i].gpioOff, pulses[i].usDelay);
+ t += pulses[i].usDelay;
+ }
+}
+
+
+
+/* ----------------------------------------------------------------------- */
+
+static int wave2Cbs(unsigned mode)
+{
+ int cb=0, onoff=0;
+
+ dmaCbs_t * p=NULL;
+
+ unsigned i, half, repeatCb;
+
+ unsigned numPulses;
+
+ gpioPulse_t * pulses;
+
+ numPulses = wfc[wfcur];
+ pulses = wf [wfcur];
+
+ half = PI_WF_MICROS/2;
+
+ /* add delay cb at start of DMA */
+
+ p = waveCbVOadr(cb++);
+
+ /* use the secondary clock */
+
+ if (gpioCfg.clockPeriph != PI_CLOCK_PCM)
+ {
+ p->info = NORMAL_DMA |
+ DMA_DEST_DREQ |
+ DMA_PERIPHERAL_MAPPING(2);
+
+ p->dst = ((PCM_BASE + PCM_FIFO*4) & 0x00ffffff) | 0x7e000000;
+ }
+ else
+ {
+ p->info = NORMAL_DMA |
+ DMA_DEST_DREQ |
+ DMA_PERIPHERAL_MAPPING(5);
+
+ p->dst = ((PWM_BASE + PWM_FIFO*4) & 0x00ffffff) | 0x7e000000;
+ }
+
+ p->src = (uint32_t) (&dmaOPhys[0]->periphData) | DMA_BUS_ADR;
+ p->length = 4 * 50 / PI_WF_MICROS; /* 50 micros delay */
+ p->next = waveCbPOadr(cb) | DMA_BUS_ADR;
+
+ repeatCb = cb;
+
+ for (i=0; i<numPulses; i++)
+ {
+ if (pulses[i].gpioOn)
+ {
+ dmaOVirt[onoff/ONOFF_PER_OPAGE]->gpioOnOff[onoff%ONOFF_PER_OPAGE] =
+ pulses[i].gpioOn;
+
+ p = waveCbVOadr(cb++);
+
+ p->info = NORMAL_DMA;
+ p->src = waveOnOffPOadr(onoff++) | DMA_BUS_ADR;
+ p->dst = ((GPIO_BASE + (GPSET0*4)) & 0x00ffffff) | 0x7e000000;
+ p->length = 4;
+ p->next = waveCbPOadr(cb) | DMA_BUS_ADR;
+ }
+
+ if (pulses[i].gpioOff)
+ {
+ dmaOVirt[onoff/ONOFF_PER_OPAGE]->gpioOnOff[onoff%ONOFF_PER_OPAGE] =
+ pulses[i].gpioOff;
+
+ p = waveCbVOadr(cb++);
+
+ p->info = NORMAL_DMA;
+ p->src = waveOnOffPOadr(onoff++) | DMA_BUS_ADR;
+ p->dst = ((GPIO_BASE + (GPCLR0*4)) & 0x00ffffff) | 0x7e000000;
+ p->length = 4;
+ p->next = waveCbPOadr(cb) | DMA_BUS_ADR;
+ }
+
+ if (pulses[i].usDelay)
+ {
+ p = waveCbVOadr(cb++);
+
+ /* use the secondary clock */
+
+ if (gpioCfg.clockPeriph != PI_CLOCK_PCM)
+ {
+ p->info = NORMAL_DMA |
+ DMA_DEST_DREQ |
+ DMA_PERIPHERAL_MAPPING(2);
+
+ p->dst = ((PCM_BASE + PCM_FIFO*4) & 0x00ffffff) | 0x7e000000;
+ }
+ else
+ {
+ p->info = NORMAL_DMA |
+ DMA_DEST_DREQ |
+ DMA_PERIPHERAL_MAPPING(5);
+
+ p->dst = ((PWM_BASE + PWM_FIFO*4) & 0x00ffffff) | 0x7e000000;
+ }
+
+ p->src = (uint32_t) (&dmaOPhys[0]->periphData) | DMA_BUS_ADR;
+ p->length = 4 * ((pulses[i].usDelay+half)/PI_WF_MICROS);
+ p->next = waveCbPOadr(cb) | DMA_BUS_ADR;
+ }
+ }
+
+ if (p != NULL)
+ {
+ if (mode == PI_WAVE_MODE_ONE_SHOT)
+ p->next = 0;
+ else p->next = waveCbPOadr(repeatCb) | DMA_BUS_ADR;
+ }
+
+ return cb;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+static void waveRxSerial(wfRx_t * s, int level, uint32_t tick)
+{
+ int diffTicks;
+
+ if (s->bit >= 0)
+ {
+ diffTicks = tick - s->startBitTick;
+
+ if (level != PI_TIMEOUT) s->level = level;
+
+ while ((s->bit < 9) && (diffTicks > s->nextBitDiff))
+ {
+ if (s->bit)
+ {
+ if (!(s->level)) s->byte |= (1<<(s->bit-1));
+ }
+ else s->byte = 0;
+
+ ++(s->bit);
+
+ s->nextBitDiff += s->fullBit;
+ }
+
+ if (s->bit == 9)
+ {
+ s->rxp->buf[s->rxp->writePos] = s->byte;
+
+ if (++s->rxp->writePos >= s->rxp->bufSize) s->rxp->writePos = 0;
+
+ if (level == 0) /* true transition high->low, not a timeout */
+ {
+ s->bit = 0;
+ s->startBitTick = tick;
+ s->nextBitDiff = s->halfBit;
+ }
+ else s->bit = -1;
+ }
+ }
+ else
+ {
+ /* start bit if high->low */
+
+ if (level == 0)
+ {
+ s->level = 0;
+ s->bit = 0;
+ s->startBitTick = tick;
+ s->nextBitDiff = s->halfBit;
+ }
+ }
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+static void waveRxBit(int gpio, int level, uint32_t tick)
+{
+ switch (wfRx[gpio].mode)
+ {
+ case PI_WFRX_NONE:
+ break;
+
+ case PI_WFRX_SERIAL:
+ waveRxSerial(&wfRx[gpio], level, tick);
+ }
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+static int waveMerge(unsigned numIn1, gpioPulse_t * in1)
+{
+ unsigned inPos1=0, inPos2=0, outPos=0;
+
+ unsigned cbs=0;
+
+ unsigned numIn2, numOut;
+
+ uint32_t tNow, tNext1, tNext2, tDelay;
+
+ gpioPulse_t * in2, * out;
+
+ numIn2 = wfc[wfcur];
+ in2 = wf[wfcur];
+
+ numOut = PI_WAVE_MAX_PULSES;
+ out = wf[1-wfcur];
+
+ tNow = 0;
+
+ if (!numIn1) tNext1 = -1; else tNext1 = 0;
+ if (!numIn2) tNext2 = -1; else tNext2 = 0;
+
+ while (((inPos1<numIn1) || (inPos2<numIn2)) && (outPos<numOut))
+ {
+ if (tNext1 < tNext2)
+ {
+ /* pulse 1 due */
+
+ if (tNow < tNext1)
+ {
+ /* extend previous delay */
+ out[outPos-1].usDelay += (tNext1 - tNow);
+ tNow = tNext1;
+ }
+
+ out[outPos].gpioOn = in1[inPos1].gpioOn;
+ out[outPos].gpioOff = in1[inPos1].gpioOff;
+
+ tNext1 = tNow + in1[inPos1].usDelay; ++inPos1;
+ }
+ else if (tNext2 < tNext1)
+ {
+ /* pulse 2 due */
+
+ if (tNow < tNext2)
+ {
+ /* extend previous delay */
+ out[outPos-1].usDelay += (tNext2 - tNow);
+ tNow = tNext2;
+ }
+
+ out[outPos].gpioOn = in2[inPos2].gpioOn;
+ out[outPos].gpioOff = in2[inPos2].gpioOff;
+
+ tNext2 = tNow + in2[inPos2].usDelay; ++inPos2;
+ }
+ else
+ {
+ /* pulse 1 and 2 both due */
+
+ if (tNow < tNext1)
+ {
+ /* extend previous delay */
+ out[outPos-1].usDelay += (tNext1 - tNow);
+ tNow = tNext1;
+ }
+
+ out[outPos].gpioOn = in1[inPos1].gpioOn | in2[inPos2].gpioOn;
+ out[outPos].gpioOff = in1[inPos1].gpioOff | in2[inPos2].gpioOff;
+
+ tNext1 = tNow + in1[inPos1].usDelay; ++inPos1;
+ tNext2 = tNow + in2[inPos2].usDelay; ++inPos2;
+ }
+
+ if (tNext1 <= tNext2) { tDelay = tNext1 - tNow; tNow = tNext1; }
+ else { tDelay = tNext2 - tNow; tNow = tNext2; }
+
+ out[outPos].usDelay = tDelay;
+
+ cbs++; /* one cb for delay */
+
+ if (out[outPos].gpioOn) cbs++; /* one cb if gpio on */
+
+ if (out[outPos].gpioOff) cbs++; /* one cb if gpio off */
+
+ outPos++;
+
+ if (inPos1 >= numIn1) tNext1 = -1;
+ if (inPos2 >= numIn2) tNext2 = -1;
+
+ }
+
+ if (outPos < numOut)
+ {
+ wfStats.micros = tNow;
+
+ if (tNow > wfStats.highMicros) wfStats.highMicros = tNow;
+
+ wfStats.pulses = outPos;
+
+ if (outPos > wfStats.highPulses) wfStats.highPulses = outPos;
+
+ wfStats.cbs = cbs;
+
+ if (cbs > wfStats.highCbs) wfStats.highCbs = cbs;
+
+ wfc[1-wfcur] = outPos;
+ wfcur = 1 - wfcur;
+
+ return outPos;
+ }
+ else return PI_TOO_MANY_PULSES;
+}
+
+
+/* ======================================================================= */
+
+static dmaCbs_t * dmaCB2adr(int pos)
+{
+ int page, slot;
+
+ page = pos/CBS_PER_IPAGE;
+ slot = pos%CBS_PER_IPAGE;
+
+ return &dmaIVirt[page]->cb[slot];
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void dmaCbPrint(int pos)
+{
+ dmaCbs_t * p;
+
+ p = dmaCB2adr(pos);
+
+ fprintf(stderr, "i=%lx s=%lx d=%lx len=%lx s=%lx nxt=%lx",
+ p->info, p->src, p->dst, p->length, p->stride, p->next);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static unsigned dmaCurrentCb(void)
+{
+ unsigned cb;
+ static unsigned lastPage=0;
+ unsigned page;
+ uint32_t cbAddr;
+ uint32_t startTick, endTick;
+
+ startTick = systReg[SYST_CLO];
+
+ cbAddr = dmaIn[DMA_CONBLK_AD];
+
+ page = lastPage;
+
+ /* which page are we dma'ing? */
+
+ while (1)
+ {
+ cb = (cbAddr - ((int)dmaIPhys[page] | DMA_BUS_ADR)) / 32;
+
+ if (cb < CBS_PER_IPAGE)
+ {
+ endTick = systReg[SYST_CLO];
+
+ if (endTick != startTick)
+ gpioStats.cbTicks += (endTick - startTick);
+ else gpioStats.cbTicks ++;
+
+ gpioStats.cbCalls++;
+
+ lastPage = page;
+
+ return (page*CBS_PER_IPAGE) + cb;
+ }
+
+ if (page++ >= DMA_PAGES) page=0;
+
+ if (page == lastPage) break;
+ }
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static unsigned dmaCurrentSlot(unsigned pos)
+{
+ unsigned cycle=0, slot=0, tmp;
+
+ cycle = (pos/CBS_PER_CYCLE);
+ tmp = (pos%CBS_PER_CYCLE);
+
+ if (tmp > 2) slot = ((tmp-2)/3);
+
+ return (cycle*PULSE_PER_CYCLE)+slot;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static uint32_t dmaPwmDataAdr(int pos)
+{
+ return (uint32_t) &dmaIPhys[pos]->periphData;
+}
+
+static uint32_t dmaGpioOnAdr(int pos)
+{
+ int page, slot;
+
+ page = pos/ON_PER_IPAGE;
+ slot = pos%ON_PER_IPAGE;
+
+ return (uint32_t) &dmaIPhys[page]->gpioOn[slot];
+}
+
+/* ----------------------------------------------------------------------- */
+
+static uint32_t dmaGpioOffAdr(int pos)
+{
+ int page, slot;
+
+ myOffPageSlot(pos, &page, &slot);
+
+ return (uint32_t) &dmaIPhys[page]->gpioOff[slot];
+}
+
+/* ----------------------------------------------------------------------- */
+
+static uint32_t dmaTickAdr(int pos)
+{
+ int page, slot;
+
+ myTckPageSlot(pos, &page, &slot);
+
+ return (uint32_t) &dmaIPhys[page]->tick[slot];
+}
+
+/* ----------------------------------------------------------------------- */
+
+static uint32_t dmaReadLevelsAdr(int pos)
+{
+ int page, slot;
+
+ myLvsPageSlot(pos, &page, &slot);
+
+ return (uint32_t) &dmaIPhys[page]->level[slot];
+}
+
+/* ----------------------------------------------------------------------- */
+
+static uint32_t dmaCbAdr(int pos)
+{
+ int page, slot;
+
+ page = (pos/CBS_PER_IPAGE);
+ slot = (pos%CBS_PER_IPAGE);
+
+ return (uint32_t) &dmaIPhys[page]->cb[slot];
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void dmaGpioOnCb(int b, int pos)
+{
+ dmaCbs_t * p;
+
+ p = dmaCB2adr(b);
+
+ p->info = NORMAL_DMA;
+ p->src = dmaGpioOnAdr(pos) | DMA_BUS_ADR;
+ p->dst = ((GPIO_BASE + (GPSET0*4)) & 0x00ffffff) | 0x7e000000;
+ p->length = 4;
+ p->next = dmaCbAdr(b+1) | DMA_BUS_ADR;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void dmaTickCb(int b, int pos)
+{
+ dmaCbs_t * p;
+
+ p = dmaCB2adr(b);
+
+ p->info = NORMAL_DMA;
+ p->src = ((SYST_BASE + (SYST_CLO*4)) & 0x00ffffff) | 0x7e000000;
+ p->dst = dmaTickAdr(pos) | DMA_BUS_ADR;
+ p->length = 4;
+ p->next = dmaCbAdr(b+1) | DMA_BUS_ADR;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void dmaGpioOffCb(int b, int pos)
+{
+ dmaCbs_t * p;
+
+ p = dmaCB2adr(b);
+
+ p->info = NORMAL_DMA;
+ p->src = dmaGpioOffAdr(pos) | DMA_BUS_ADR;
+ p->dst = ((GPIO_BASE + (GPCLR0*4)) & 0x00ffffff) | 0x7e000000;
+ p->length = 4;
+ p->next = dmaCbAdr(b+1) | DMA_BUS_ADR;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void dmaReadLevelsCb(int b, int pos)
+{
+ dmaCbs_t * p;
+
+ p = dmaCB2adr(b);
+
+ p->info = NORMAL_DMA;
+ p->src = ((GPIO_BASE + (GPLEV0*4)) & 0x00ffffff) | 0x7e000000;
+ p->dst = dmaReadLevelsAdr(pos) | DMA_BUS_ADR;
+ p->length = 4;
+ p->next = dmaCbAdr(b+1) | DMA_BUS_ADR;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void dmaDelayCb(int b)
+{
+ dmaCbs_t * p;
+
+ p = dmaCB2adr(b);
+
+ if (gpioCfg.clockPeriph == PI_CLOCK_PCM)
+ {
+ p->info = NORMAL_DMA | TIMED_DMA(2);
+ p->dst = ((PCM_BASE + PCM_FIFO*4) & 0x00ffffff) | 0x7e000000;
+ }
+ else
+ {
+ p->info = NORMAL_DMA | TIMED_DMA(5);
+ p->dst = ((PWM_BASE + PWM_FIFO*4) & 0x00ffffff) | 0x7e000000;
+ }
+
+ p->src = dmaPwmDataAdr(b%DMA_PAGES) | DMA_BUS_ADR;
+ p->length = 4;
+ p->next = dmaCbAdr(b+1) | DMA_BUS_ADR;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void dmaInitCbs(void)
+{
+ int b, pulse, level, cycle;
+
+ dmaCbs_t * p;
+
+ /* set up the DMA control blocks */
+
+ DBG(DBG_STARTUP, "");
+
+ b = -1;
+ level = 0;
+
+ for (cycle=0; cycle<bufferCycles; cycle++)
+ {
+ b++; dmaGpioOnCb(b, cycle%SUPERCYCLE); /* gpio on slot */
+
+ b++; dmaTickCb(b, cycle); /* tick slot */
+
+ for (pulse=0; pulse<PULSE_PER_CYCLE; pulse++)
+ {
+ b++; dmaReadLevelsCb(b, level); /* read levels slot */
+
+ b++; dmaDelayCb(b); /* delay slot */
+
+ b++; dmaGpioOffCb(b, (level%SUPERLEVEL)+1); /* gpio off slot */
+
+ ++level;
+ }
+ }
+
+ /* point last cb back to first for continuous loop */
+
+ p = dmaCB2adr(b);
+
+ p->next = dmaCbAdr(0) | DMA_BUS_ADR;
+
+ DBG(DBG_STARTUP, "DMA page type size = %d", sizeof(dmaIPage_t));
+
+ DBG(DBG_STARTUP, "%d control blocks (exp=%d)", b+1, NUM_CBS);
+}
+
+/* ======================================================================= */
+
+
+static void sigHandler(int signum)
+{
+ if ((signum >= PI_MIN_SIGNUM) && (signum <= PI_MAX_SIGNUM))
+ {
+ if (gpioSignal[signum].func)
+ {
+ if (gpioSignal[signum].ex)
+ {
+ (gpioSignal[signum].func)(signum, gpioSignal[signum].userdata);
+ }
+ else
+ {
+ (gpioSignal[signum].func)(signum);
+ }
+ }
+ else
+ {
+ if (signum == SIGUSR1)
+ {
+ if (gpioCfg.dbgLevel > DBG_MIN_LEVEL)
+ {
+ --gpioCfg.dbgLevel;
+ }
+ else gpioCfg.dbgLevel = DBG_MIN_LEVEL;
+
+ DBG(DBG_USER, "Debug level %d\n", gpioCfg.dbgLevel);
+ }
+ else if (signum == SIGUSR2)
+ {
+ if (gpioCfg.dbgLevel < DBG_MAX_LEVEL)
+ {
+ ++gpioCfg.dbgLevel;
+ }
+ else gpioCfg.dbgLevel = DBG_MAX_LEVEL;
+
+ DBG(DBG_USER, "Debug level %d\n", gpioCfg.dbgLevel);
+ }
+ else
+ {
+ /* close library safely and exit */
+
+ DBG(DBG_USER, "Unhandled signal %d, terminating\n", signum);
+
+ gpioTerminate();
+
+ exit(-1);
+ }
+ }
+ }
+ else
+ {
+ /* close library safely and exit */
+
+ DBG(DBG_USER, "Unhandled signal %d, terminating\n", signum);
+
+ gpioTerminate();
+
+ exit(-1);
+ }
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void sigSetHandler(void)
+{
+ int i;
+ struct sigaction new;
+
+ for (i=PI_MIN_SIGNUM; i<=PI_MAX_SIGNUM; i++)
+ {
+
+ memset(&new, 0, sizeof(new));
+ new.sa_handler = sigHandler;
+
+ sigaction(i, &new, NULL);
+ }
+}
+
+/* ======================================================================= */
+
+static void * pthAlertThread(void *x)
+{
+ struct timespec req, rem;
+ uint32_t oldLevel, newLevel, level, reportedLevel;
+ uint32_t oldSlot, newSlot;
+ uint32_t tick, expected;
+ int32_t diff;
+ int cycle, pulse;
+ int emit, seqno, emitted;
+ uint32_t changes, bits, changedBits, timeoutBits;
+ int numSamples, d;
+ int b, n, v;
+ int err;
+ char fifo[32];
+
+ req.tv_sec = 0;
+
+ /* don't start until DMA started */
+
+ while (!DMAstarted) myGpioDelay(1000);
+
+ myGpioDelay(20000); /* let DMA run for a while */
+
+ reportedLevel = gpioReg[GPLEV0];
+
+ tick = systReg[SYST_CLO];
+
+ gpioStats.startTick = tick;
+
+ oldSlot = dmaCurrentSlot(dmaCurrentCb());
+
+ cycle = (oldSlot/PULSE_PER_CYCLE);
+ pulse = (oldSlot%PULSE_PER_CYCLE);
+
+ while (1)
+ {
+ gpioStats.alertTicks++;
+
+ req.tv_nsec = 850000;
+
+ while (nanosleep(&req, &rem))
+ {
+ req.tv_sec = rem.tv_sec;
+ req.tv_nsec = rem.tv_nsec;
+ }
+
+ newSlot = dmaCurrentSlot(dmaCurrentCb());
+
+ numSamples = 0;
+
+ changedBits = 0;
+
+ oldLevel = reportedLevel & monitorBits;
+
+ while ((oldSlot != newSlot) && (numSamples < DATUMS))
+ {
+ level = myGetLevel(oldSlot++);
+
+ newLevel = (level & monitorBits);
+
+ if (newLevel != oldLevel)
+ {
+ gpioSample[numSamples].tick = tick;
+ gpioSample[numSamples].level = level;
+
+ changedBits |= (newLevel ^ oldLevel);
+
+ oldLevel = newLevel;
+
+ numSamples++;
+ }
+
+ tick += gpioCfg.clockMicros;
+
+ if (++pulse >= PULSE_PER_CYCLE)
+ {
+ pulse = 0;
+
+ if (++cycle >= bufferCycles)
+ {
+ cycle = 0;
+ oldSlot = 0;
+ }
+
+ expected = tick;
+
+ tick = myGetTick(cycle);
+
+ diff = tick - expected;
+
+ diff += (TICKSLOTS/2);
+
+ if (diff < 0) gpioStats.diffTick[0]++;
+
+ else if (diff >= TICKSLOTS)
+ gpioStats.diffTick[TICKSLOTS-1]++;
+
+ else gpioStats.diffTick[diff]++;
+ }
+ }
+
+ /* should gpioGetSamples be called */
+
+ if (changedBits)
+ {
+ if (gpioGetSamples.func)
+ {
+ if (gpioGetSamples.ex)
+ {
+ (gpioGetSamples.func)
+ (gpioSample, numSamples, gpioGetSamples.userdata);
+ }
+ else
+ {
+ (gpioGetSamples.func)
+ (gpioSample, numSamples);
+ }
+ }
+ }
+
+ /* reset timeouts for any changed bits */
+
+ if (changedBits)
+ {
+ for (b=0; b<=PI_MAX_USER_GPIO; b++)
+ {
+ if (changedBits & (1<<b)) gpioAlert[b].tick = tick;
+ }
+ }
+
+ /* call alert callbacks for each bit transition */
+
+ if (changedBits & alertBits)
+ {
+ oldLevel = reportedLevel & alertBits;
+
+ for (d=0; d<numSamples; d++)
+ {
+ newLevel = gpioSample[d].level & alertBits;
+
+ if (newLevel != oldLevel)
+ {
+ changes = newLevel ^ oldLevel;
+
+ for (b=0; b<=PI_MAX_USER_GPIO; b++)
+ {
+ if (changes & (1<<b))
+ {
+ if (newLevel & (1<<b)) v = 1; else v = 0;
+
+ if (gpioAlert[b].func)
+ {
+ if (gpioAlert[b].ex)
+ {
+ (gpioAlert[b].func)
+ (b, v, gpioSample[d].tick,
+ gpioAlert[b].userdata);
+ }
+ else
+ {
+ (gpioAlert[b].func)
+ (b, v, gpioSample[d].tick);
+ }
+ }
+ }
+ }
+ oldLevel = newLevel;
+ }
+ }
+ }
+
+ /* check for timeout watchdogs */
+
+ timeoutBits = 0;
+
+ for (b=0; b<=PI_MAX_USER_GPIO; b++)
+ {
+ if (gpioAlert[b].timeout)
+ {
+ diff = tick - gpioAlert[b].tick;
+
+ if (diff > (gpioAlert[b].timeout*1000))
+ {
+ timeoutBits |= (1<<b);
+
+ gpioAlert[b].tick += (gpioAlert[b].timeout*1000);
+
+ if (gpioAlert[b].func)
+ {
+ if (gpioAlert[b].ex)
+ {
+ (gpioAlert[b].func)(b, 2, tick, gpioAlert[b].userdata);
+ }
+ else
+ {
+ (gpioAlert[b].func)(b, 2, tick);
+ }
+ }
+ }
+ }
+ }
+
+ for (n=0; n<PI_NOTIFY_SLOTS; n++)
+ {
+ if (gpioNotify[n].state == PI_NOTIFY_CLOSING)
+ {
+ if (gpioNotify[n].pipe)
+ {
+ close(gpioNotify[n].fd);
+
+ sprintf(fifo, "/dev/pigpio%d", n);
+
+ unlink(fifo);
+ }
+
+ gpioNotify[n].state = PI_NOTIFY_CLOSED;
+ }
+ else if (gpioNotify[n].state == PI_NOTIFY_RUNNING)
+ {
+ bits = gpioNotify[n].bits;
+
+ emit = 0;
+
+ seqno = gpioNotify[n].seqno;
+
+ /* check to see if any bits have changed for this
+ notification.
+
+ bits is the set of notification bits
+ changedBits is the set of changed bits
+ */
+
+ if (changedBits & bits)
+ {
+ oldLevel = reportedLevel & bits;
+
+ for (d=0; d<numSamples; d++)
+ {
+ newLevel = gpioSample[d].level & bits;
+
+ if (newLevel != oldLevel)
+ {
+ gpioReport[emit].seqno = seqno;
+ gpioReport[emit].flags = 0;
+ gpioReport[emit].tick = gpioSample[d].tick;
+ gpioReport[emit].level = gpioSample[d].level;
+
+ oldLevel = newLevel;
+
+ emit++;
+ seqno++;
+ }
+ }
+ }
+
+ /* check to see if any watchdogs are due for this
+ notification.
+
+ bits is the set of notification bits
+ timeoutBits is the set of timed out bits
+ */
+
+ if (timeoutBits & bits)
+ {
+ /* at least one watchdog has fired for this
+ notification.
+ */
+
+ for (b=0; b<=PI_MAX_USER_GPIO; b++)
+ {
+ if (timeoutBits & bits & (1<<b))
+ {
+ if (numSamples)
+ newLevel = gpioSample[numSamples-1].level;
+ else
+ newLevel = reportedLevel;
+
+ gpioReport[emit].seqno = seqno;
+ gpioReport[emit].flags = PI_NTFY_FLAGS_WDOG |
+ PI_NTFY_FLAGS_BIT(b);
+ gpioReport[emit].tick = tick;
+ gpioReport[emit].level = newLevel;
+
+ emit++;
+ seqno++;
+ }
+ }
+ }
+
+ if (emit)
+ {
+ if (emit > gpioStats.maxEmit) gpioStats.maxEmit = emit;
+
+ emitted = 0;
+
+ while (emit > 0)
+ {
+ if (emit > MAX_EMITS)
+ {
+ gpioStats.emitFrags++;
+
+ err = write(gpioNotify[n].fd,
+ gpioReport+emitted,
+ MAX_EMITS*sizeof(gpioReport_t));
+
+ if (err != (MAX_EMITS*sizeof(gpioReport_t)))
+ {
+ DBG(0, "fd=%d err=%d errno=%d",
+ gpioNotify[n].fd, err, errno);
+ if (err < 0) DBG(0, "%s", strerror(errno));
+ if ((err != EAGAIN) && (err != EWOULDBLOCK))
+ {
+ /* serious error, no point continuing */
+ gpioNotify[n].bits = 0;
+ gpioNotify[n].state = PI_NOTIFY_CLOSING;
+ intNotifyBits();
+ break;
+ }
+ }
+
+ emitted += MAX_EMITS;
+ emit -= MAX_EMITS;
+ }
+ else
+ {
+ err = write(gpioNotify[n].fd,
+ gpioReport+emitted,
+ emit*sizeof(gpioReport_t));
+
+ if (err != (emit*sizeof(gpioReport_t)))
+ {
+ DBG(0, "fd=%d err=%d errno=%d",
+ gpioNotify[n].fd, err, errno);
+ if (err < 0) DBG(0, "%s", strerror(errno));
+ if ((err != EAGAIN) && (err != EWOULDBLOCK))
+ {
+ /* serious error, no point continuing */
+ gpioNotify[n].bits = 0;
+ gpioNotify[n].state = PI_NOTIFY_CLOSING;
+ intNotifyBits();
+ break;
+ }
+ }
+
+ emitted += emit;
+ emit = 0;
+ }
+ }
+
+ gpioNotify[n].seqno = seqno;
+ }
+ }
+ }
+
+ /* once all outputs have been emitted set reported level */
+
+ if (numSamples) reportedLevel = gpioSample[numSamples-1].level;
+
+ if (numSamples > gpioStats.maxSamples)
+ gpioStats.maxSamples = numSamples;
+
+ gpioStats.numSamples += numSamples;
+ }
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void * pthTimerTick(void *x)
+{
+ gpioTimer_t * tp;
+ struct timespec req, rem, period;
+ char buf[256];
+
+ tp = x;
+
+ clock_gettime(CLOCK_REALTIME, &tp->nextTick);
+
+ while (1)
+ {
+ clock_gettime(CLOCK_REALTIME, &rem);
+
+ period.tv_sec = tp->millis / THOUSAND;
+ period.tv_nsec = (tp->millis % THOUSAND) * THOUSAND * THOUSAND;
+
+ do
+ {
+ TIMER_ADD(&tp->nextTick, &period, &tp->nextTick);
+
+ TIMER_SUB(&tp->nextTick, &rem, &req);
+ }
+ while (req.tv_sec < 0);
+
+ while (nanosleep(&req, &rem))
+ {
+ req.tv_sec = rem.tv_sec;
+ req.tv_nsec = rem.tv_nsec;
+ }
+
+ if (gpioCfg.dbgLevel >= DBG_SLOW_TICK)
+ {
+ if ((tp->millis > 50) || (gpioCfg.dbgLevel >= DBG_FAST_TICK))
+ {
+ sprintf(buf, "pigpio: TIMER=%d @ %u %u\n",
+ tp->id,
+ (unsigned)tp->nextTick.tv_sec,
+ (unsigned)tp->nextTick.tv_nsec);
+ fprintf(stderr, buf);
+ }
+ }
+
+ if (tp->ex) (tp->func)(tp->userdata);
+ else (tp->func)();
+ }
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+
+static void * pthFifoThread(void *x)
+{
+ char inBuf[128];
+ int idx, flags;
+ cmdCmd_t cmd;
+
+ myCreatePipe(PI_INPFIFO, 0662);
+
+ if ((inpFifo = fopen(PI_INPFIFO, "r+")) == NULL)
+ SOFT_ERROR((void*)PI_INIT_FAILED, "fopen %s failed(%m)", PI_INPFIFO);
+
+ myCreatePipe(PI_OUTFIFO, 0664);
+
+ if ((outFifo = fopen(PI_OUTFIFO, "w+")) == NULL)
+ SOFT_ERROR((void*)PI_INIT_FAILED, "fopen %s failed (%m)", PI_OUTFIFO);
+
+ /* set outFifo non-blocking */
+
+ flags = fcntl(fileno(outFifo), F_GETFL, 0);
+ fcntl(fileno(outFifo), F_SETFL, flags | O_NONBLOCK);
+
+ while (1)
+ {
+ if (fgets(inBuf, sizeof(inBuf), inpFifo) == NULL)
+ SOFT_ERROR((void*)PI_INIT_FAILED, "fifo fgets failed (%m)");
+
+ if ((idx=cmdParse(inBuf, &cmd)) >= 0)
+ {
+ myDoCommand(&cmd);
+
+ switch (cmdInfo[idx].rv)
+ {
+ case 0:
+ fprintf(outFifo, "%d\n", cmd.res);
+ break;
+
+ case 1:
+ break;
+
+ case 2:
+ fprintf(outFifo, "%d\n", cmd.res);
+ break;
+
+ case 3:
+ fprintf(outFifo, "%08X\n", cmd.res);
+ break;
+
+ case 4:
+ fprintf(outFifo, "%u\n", cmd.res);
+ break;
+
+ case 5:
+ fprintf(outFifo, cmdUsage);
+ break;
+ }
+
+ }
+ else fprintf(outFifo, "%d\n", PI_BAD_FIFO_COMMAND);
+
+ fflush(outFifo);
+ }
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void * pthSocketThreadHandler(void *fdC)
+{
+ int sock = *(int*)fdC;
+ cmdCmd_t cmd;
+
+ free(fdC);
+
+ while(1)
+ {
+ if (recv(sock, &cmd, sizeof(cmdCmd_t), 0) == sizeof(cmdCmd_t))
+ {
+ if (cmd.cmd != PI_CMD_NOIB)
+ {
+ myDoCommand(&cmd);
+ }
+ else
+ {
+ cmd.res = gpioNotifyOpenInBand(sock);
+ }
+
+ write(sock, &cmd, sizeof(cmdCmd_t));
+
+ }
+ else break;
+ }
+
+ close(sock);
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void * pthSocketThread(void *x)
+{
+ int fdC, c, *sock;
+ struct sockaddr_in server, client;
+ pthread_attr_t attr;
+ char * portStr;
+ unsigned port;
+
+ if (pthread_attr_init(&attr))
+ SOFT_ERROR((void*)PI_INIT_FAILED,
+ "pthread_attr_init failed (%m)");
+
+ if (pthread_attr_setstacksize(&attr, STACK_SIZE))
+ SOFT_ERROR((void*)PI_INIT_FAILED,
+ "pthread_attr_setstacksize failed (%m)");
+
+ if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED))
+ SOFT_ERROR((void*)PI_INIT_FAILED,
+ "pthread_attr_setdetachstate failed (%m)");
+
+ fdSock = socket(AF_INET , SOCK_STREAM , 0);
+
+ if (fdSock == -1)
+ SOFT_ERROR((void*)PI_INIT_FAILED, "socket failed (%m)");
+
+ portStr = getenv(PI_ENVPORT);
+
+ if (portStr) port = atoi(portStr); else port = gpioCfg.socketPort;
+
+ server.sin_family = AF_INET;
+ server.sin_addr.s_addr = INADDR_ANY;
+ server.sin_port = htons(port);
+
+ if (bind(fdSock,(struct sockaddr *)&server , sizeof(server)) < 0)
+ SOFT_ERROR((void*)PI_INIT_FAILED, "bind failed (%m)");
+
+ listen(fdSock, 100);
+
+ c = sizeof(struct sockaddr_in);
+
+ while ((fdC =
+ accept(fdSock, (struct sockaddr *)&client, (socklen_t*)&c)))
+ {
+ pthread_t thr;
+
+ sock = malloc(sizeof(int));
+
+ *sock = fdC;
+
+ if (pthread_create
+ (&thr, &attr, pthSocketThreadHandler, (void*) sock) < 0)
+ SOFT_ERROR((void*)PI_INIT_FAILED,
+ "socket pthread_create failed (%m)");
+ }
+
+ if (fdC < 0)
+ SOFT_ERROR((void*)PI_INIT_FAILED, "accept failed (%m)");
+
+ return 0;
+}
+
+/* ======================================================================= */
+
+static int initGrabLockFile(void)
+{
+ int fd;
+ int lockResult;
+ char pidStr[20];
+
+ /* try to grab the lock file */
+
+ fd = open(PI_LOCKFILE, O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, 0644);
+
+ if (fd != -1)
+ {
+ lockResult = flock(fd, LOCK_EX|LOCK_NB);
+
+ if(lockResult == 0)
+ {
+ sprintf(pidStr, "%d\n", (int)getpid());
+
+ write(fd, pidStr, strlen(pidStr));
+ }
+ else
+ {
+ close(fd);
+ return -1;
+ }
+ }
+
+ return fd;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int initZaps
+(
+ int pmapFd,
+ dmaPage_t *dmaV1,
+ dmaPage_t *dmaV2[],
+ dmaPage_t *dmaP[],
+ int pages)
+{
+ int n;
+ long index;
+ off_t offset;
+ ssize_t t;
+ int status;
+ uint32_t pageAdr2;
+ unsigned long long pa;
+
+ DBG(DBG_STARTUP, "");
+
+ status = 0;
+
+ pageAdr2 = (uint32_t) dmaV2[0];
+
+ index = ((uint32_t)dmaV1 / PAGE_SIZE) * 8;
+
+ offset = lseek(pmapFd, index, SEEK_SET);
+
+ if (offset != index)
+ SOFT_ERROR(PI_INIT_FAILED, "lseek pagemap failed (%m)");
+
+ for (n=0; n<pages; n++)
+ {
+ t = read(pmapFd, &pa, sizeof(pa));
+
+ if (t != sizeof(pa))
+ SOFT_ERROR(PI_INIT_FAILED, "read pagemap failed (%m)");
+
+ DBG(DBG_STARTUP, "pf%d=%016llX", n, pa);
+
+ dmaP[n] = (dmaPage_t *) (uint32_t) (PAGE_SIZE * (pa & 0xFFFFFFFF));
+
+ dmaV2[n] = mmap
+ (
+ (void *)pageAdr2,
+ PAGE_SIZE,
+ PROT_READ|PROT_WRITE,
+ MAP_SHARED|MAP_FIXED|MAP_LOCKED|MAP_NORESERVE,
+ fdMem,
+ (uint32_t)dmaP[n] | 0x40000000
+ );
+
+ pageAdr2 += PAGE_SIZE;
+
+ if (dmaP[n] == 0) status = 1;
+ }
+
+ return status;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static uint32_t * initMapMem(int fd, uint32_t addr, uint32_t len)
+{
+ return (uint32_t *) mmap(0, len,
+ PROT_READ|PROT_WRITE|PROT_EXEC,
+ MAP_SHARED|MAP_LOCKED,
+ fd, addr);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int initCheckPermitted(void)
+{
+ DBG(DBG_STARTUP, "");
+
+ if ((fdMem = open("/dev/mem", O_RDWR | O_SYNC) ) < 0)
+ {
+ fprintf(stderr,
+ "\n" \
+ "+---------------------------------------------------------+\n" \
+ "|Sorry, you don't have permission to run this program. |\n" \
+ "|Try running as root, e.g. precede the command with sudo. |\n" \
+ "+---------------------------------------------------------+\n\n");
+ exit(-1);
+ }
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int initPeripherals(void)
+{
+ uint32_t dmaBase;
+
+ DBG(DBG_STARTUP, "");
+
+ gpioReg = initMapMem(fdMem, GPIO_BASE, GPIO_LEN);
+
+ if (gpioReg == MAP_FAILED)
+ SOFT_ERROR(PI_INIT_FAILED, "mmap gpio failed (%m)");
+
+ /* dma channels 0-14 share one page, 15 has another */
+
+ if (gpioCfg.DMAprimaryChannel < 15)
+ {
+ dmaBase = DMA_BASE;
+ }
+ else dmaBase = DMA15_BASE;
+
+ dmaReg = initMapMem(fdMem, dmaBase, DMA_LEN);
+
+ if (dmaReg == MAP_FAILED)
+ SOFT_ERROR(PI_INIT_FAILED, "mmap dma failed (%m)");
+
+ if (gpioCfg.DMAprimaryChannel < 15)
+ {
+ dmaIn = dmaReg + (gpioCfg.DMAprimaryChannel * 0x40);
+ dmaOut = dmaReg + (gpioCfg.DMAsecondaryChannel * 0x40);
+ }
+
+ DBG(DBG_STARTUP, "DMA #%d @ %08X @ %08X",
+ gpioCfg.DMAprimaryChannel, dmaBase, (uint32_t)dmaIn);
+
+ DBG(DBG_STARTUP, "debug reg is %08X", dmaIn[DMA_DEBUG]);
+
+ clkReg = initMapMem(fdMem, CLK_BASE, CLK_LEN);
+
+ if (clkReg == MAP_FAILED)
+ SOFT_ERROR(PI_INIT_FAILED, "mmap clk failed (%m)");
+
+ systReg = initMapMem(fdMem, SYST_BASE, SYST_LEN);
+
+ if (systReg == MAP_FAILED)
+ SOFT_ERROR(PI_INIT_FAILED, "mmap syst failed (%m)");
+
+ pwmReg = initMapMem(fdMem, PWM_BASE, PWM_LEN);
+
+ if (pwmReg == MAP_FAILED)
+ SOFT_ERROR(PI_INIT_FAILED, "mmap pwm failed (%m)");
+
+ pcmReg = initMapMem(fdMem, PCM_BASE, PCM_LEN);
+
+ if (pcmReg == MAP_FAILED)
+ SOFT_ERROR(PI_INIT_FAILED, "mmap pcm failed (%m)");
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int initDMAblock(int pagemapFd, int block)
+{
+ int trys, ok;
+ unsigned pageNum;
+
+ DBG(DBG_STARTUP, "");
+
+ dmaBloc[block] = mmap(
+ 0, (PAGES_PER_BLOCK*PAGE_SIZE),
+ PROT_READ|PROT_WRITE,
+ MAP_SHARED|MAP_ANONYMOUS|MAP_NORESERVE|MAP_LOCKED,
+ -1, 0);
+
+ if (dmaBloc[block] == MAP_FAILED)
+ SOFT_ERROR(PI_INIT_FAILED, "mmap dma block %d failed (%m)", block);
+
+ /* force allocation of physical memory */
+
+ memset((void *)dmaBloc[block], 0, (PAGES_PER_BLOCK*PAGE_SIZE));
+
+ pageNum = block * PAGES_PER_BLOCK;
+
+ dmaVirt[pageNum] = mmap(
+ 0, (PAGES_PER_BLOCK*PAGE_SIZE),
+ PROT_READ|PROT_WRITE,
+ MAP_SHARED|MAP_ANONYMOUS|MAP_NORESERVE|MAP_LOCKED,
+ -1, 0);
+
+ if (dmaVirt[pageNum] == MAP_FAILED)
+ SOFT_ERROR(PI_INIT_FAILED, "mmap dma block %d failed (%m)", block);
+
+ munmap(dmaVirt[pageNum], PAGES_PER_BLOCK*PAGE_SIZE);
+
+ trys = 0;
+ ok = 0;
+
+ while ((trys < 10) && !ok)
+ {
+ if (initZaps(pagemapFd,
+ dmaBloc[block],
+ &dmaVirt[pageNum],
+ &dmaPhys[pageNum],
+ PAGES_PER_BLOCK) == 0) ok = 1;
+ else myGpioDelay(50000);
+
+ ++trys;
+ }
+
+ if (!ok) SOFT_ERROR(PI_INIT_FAILED, "initZaps failed");
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+static int initDMAcbs(void)
+{
+ int pid;
+ char str[64];
+ int pagemapFd;
+ int i, servoCycles, superCycles;
+
+ DBG(DBG_STARTUP, "");
+
+ /* Calculate the number of blocks needed for buffers. The number
+ of blocks must be a multiple of the 20ms servo cycle.
+ */
+
+ servoCycles = gpioCfg.bufferMilliseconds / 20;
+ if (gpioCfg.bufferMilliseconds % 20) servoCycles++;
+
+ bufferCycles = (SUPERCYCLE * servoCycles) / gpioCfg.clockMicros;
+
+ superCycles = bufferCycles / SUPERCYCLE;
+ if (bufferCycles % SUPERCYCLE) superCycles++;
+
+ bufferCycles = SUPERCYCLE * superCycles;
+
+ bufferBlocks = bufferCycles / CYCLES_PER_BLOCK;
+
+ DBG(DBG_STARTUP, "bmillis=%d mics=%d bblk=%d bcyc=%d",
+ gpioCfg.bufferMilliseconds, gpioCfg.clockMicros,
+ bufferBlocks, bufferCycles);
+
+ /* allocate memory for pointers to virtual and physical pages */
+
+ dmaBloc = mmap(
+ 0, (bufferBlocks+1)*sizeof(dmaPage_t *),
+ PROT_READ|PROT_WRITE,
+ MAP_PRIVATE|MAP_ANONYMOUS|MAP_LOCKED,
+ -1, 0);
+
+ if (dmaBloc == MAP_FAILED)
+ SOFT_ERROR(PI_INIT_FAILED, "mmap dma virtual failed (%m)");
+
+ dmaVirt = mmap(
+ 0, PAGES_PER_BLOCK*(bufferBlocks+1)*sizeof(dmaPage_t *),
+ PROT_READ|PROT_WRITE,
+ MAP_PRIVATE|MAP_ANONYMOUS|MAP_LOCKED,
+ -1, 0);
+
+ if (dmaVirt == MAP_FAILED)
+ SOFT_ERROR(PI_INIT_FAILED, "mmap dma virtual failed (%m)");
+
+ dmaPhys = mmap(
+ 0, PAGES_PER_BLOCK*(bufferBlocks+1)*sizeof(dmaPage_t *),
+ PROT_READ|PROT_WRITE,
+ MAP_PRIVATE|MAP_ANONYMOUS|MAP_LOCKED,
+ -1, 0);
+
+ if (dmaPhys == MAP_FAILED)
+ SOFT_ERROR(PI_INIT_FAILED, "mmap dma physical failed (%m)");
+
+ dmaIPhys = (dmaIPage_t **) dmaPhys;
+ dmaIVirt = (dmaIPage_t **) dmaVirt;
+
+ dmaOPhys = (dmaOPage_t **)(dmaPhys + (PAGES_PER_BLOCK*bufferBlocks));
+ dmaOVirt = (dmaOPage_t **)(dmaVirt + (PAGES_PER_BLOCK*bufferBlocks));
+
+ pid = getpid();
+
+ sprintf(str, "/proc/%d/pagemap", pid);
+
+ pagemapFd = open(str, O_RDONLY);
+
+ if (pagemapFd < 0)
+ SOFT_ERROR(PI_INIT_FAILED, "open pagemap failed(%m)");
+
+ for (i=0; i<(bufferBlocks+1); i++) initDMAblock(pagemapFd, i);
+
+ close(pagemapFd);
+
+ DBG(DBG_STARTUP, "dmaBloc=%08X dmaIn=%08X",
+ (uint32_t)dmaBloc, (uint32_t)dmaIn);
+
+ DBG(DBG_STARTUP, "gpioReg=%08X pwmReg=%08X pcmReg=%08X clkReg=%08X",
+ (uint32_t)gpioReg, (uint32_t)pwmReg,
+ (uint32_t)pcmReg, (uint32_t)clkReg);
+
+ for (i=0; i<DMA_PAGES; i++)
+ DBG(DBG_STARTUP, "dmaIPhys[%d]=%08X", i, (uint32_t)dmaIPhys[i]);
+
+ dmaInitCbs();
+
+ if (gpioCfg.dbgLevel >= DBG_DMACBS)
+ for (i=0; i<NUM_CBS; i++) dmaCbPrint(i);
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void initPWM(unsigned bits)
+{
+ DBG(DBG_STARTUP, "");
+
+ /* reset PWM */
+
+ pwmReg[PWM_CTL] = 0;
+
+ myGpioDelay(10);
+
+ pwmReg[PWM_STA] = -1;
+
+ myGpioDelay(10);
+
+ /* set number of bits to transmit */
+
+ pwmReg[PWM_RNG1] = bits;
+
+ myGpioDelay(10);
+
+ dmaIVirt[0]->periphData = 1;
+
+ /* enable PWM DMA, raise panic and dreq thresholds to 15 */
+
+ pwmReg[PWM_DMAC] = PWM_DMAC_ENAB |
+ PWM_DMAC_PANIC(15) |
+ PWM_DMAC_DREQ(15);
+
+ myGpioDelay(10);
+
+ /* clear PWM fifo */
+
+ pwmReg[PWM_CTL] = PWM_CTL_CLRF1;
+
+ myGpioDelay(10);
+
+ /* enable PWM channel 1 and use fifo */
+
+ pwmReg[PWM_CTL] = PWM_CTL_USEF1 | PWM_CTL_MODE1 | PWM_CTL_PWEN1;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void initPCM(unsigned bits)
+{
+ DBG(DBG_STARTUP, "");
+
+ /* disable PCM so we can modify the regs */
+
+ pcmReg[PCM_CS] = 0;
+
+ myGpioDelay(1000);
+
+ pcmReg[PCM_FIFO] = 0;
+ pcmReg[PCM_MODE] = 0;
+ pcmReg[PCM_RXC] = 0;
+ pcmReg[PCM_TXC] = 0;
+ pcmReg[PCM_DREQ] = 0;
+ pcmReg[PCM_INTEN] = 0;
+ pcmReg[PCM_INTSTC] = 0;
+ pcmReg[PCM_GRAY] = 0;
+
+ myGpioDelay(1000);
+
+ pcmReg[PCM_MODE] = PCM_MODE_FLEN(bits-1); /* # bits in frame */
+
+ /* enable channel 1 with # bits width */
+
+ pcmReg[PCM_TXC] = PCM_TXC_CH1EN | PCM_TXC_CH1WID(bits-8);
+
+ pcmReg[PCM_CS] |= PCM_CS_STBY; /* clear standby */
+
+ myGpioDelay(1000);
+
+ pcmReg[PCM_CS] |= PCM_CS_TXCLR; /* clear TX FIFO */
+
+ pcmReg[PCM_CS] |= PCM_CS_DMAEN; /* enable DREQ */
+
+ pcmReg[PCM_DREQ] = PCM_DREQ_TX_PANIC(16) | PCM_DREQ_TX_REQ_L(30);
+
+ pcmReg[PCM_INTSTC] = 0b1111; /* clear status bits */
+
+ /* enable PCM */
+
+ pcmReg[PCM_CS] |= PCM_CS_EN ;
+
+ /* enable tx */
+
+ pcmReg[PCM_CS] |= PCM_CS_TXON;
+
+ dmaIVirt[0]->periphData = 0x0F;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void initClock(int mainClock)
+{
+ unsigned clkCtl, clkDiv, clkSrc, clkDivI, clkDivF, clkMash, clkBits;
+ char * per, * src;
+ unsigned micros;
+
+ DBG(DBG_STARTUP, "");
+
+ if (mainClock) micros = gpioCfg.clockMicros;
+ else micros = PI_WF_MICROS;
+
+ if ((gpioCfg.clockPeriph == PI_CLOCK_PCM) && mainClock)
+ {
+ clkCtl = CLK_PCMCTL;
+ clkDiv = CLK_PCMDIV;
+ per = "PCM";
+ }
+ else
+ {
+ clkCtl = CLK_PWMCTL;
+ clkDiv = CLK_PWMDIV;
+ per = "PWM";
+ }
+
+ if (gpioCfg.clockSource == PI_CLOCK_PLLD)
+ {
+ clkSrc = CLK_CTL_SRC_PLLD;
+ clkDivI = 50 * micros;
+ clkDivF = 0;
+ clkMash = 0;
+ clkBits = 10;
+ src = "PLLD";
+ }
+ else
+ {
+ clkSrc = CLK_CTL_SRC_OSC;
+ clkDivI = clkCfg[micros].divi;
+ clkDivF = clkCfg[micros].divf;
+ clkMash = clkCfg[micros].mash;
+ clkBits = clkCfg[micros].bits;
+ src = "OSC";
+ }
+
+ DBG(DBG_STARTUP, "%s %s divi=%d divf=%d mash=%d bits=%d",
+ per, src, clkDivI, clkDivF, clkMash, clkBits);
+
+ clkReg[clkCtl] = CLK_PASSWD | CLK_CTL_KILL;
+
+ myGpioDelay(10);
+
+ clkReg[clkDiv] =
+ (CLK_PASSWD | CLK_DIV_DIVI(clkDivI) | CLK_DIV_DIVF(clkDivF));
+
+ myGpioDelay(10);
+
+ clkReg[clkCtl] =
+ (CLK_PASSWD | CLK_CTL_MASH(clkMash) | CLK_CTL_SRC(clkSrc));
+
+ myGpioDelay(10);
+
+ clkReg[clkCtl] |= (CLK_PASSWD | CLK_CTL_ENAB);
+
+ myGpioDelay(10);
+
+ if ((gpioCfg.clockPeriph == PI_CLOCK_PCM) && mainClock)
+ initPCM(clkBits);
+ else initPWM(clkBits);
+
+ myGpioDelay(2000);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void initDMAgo(uint32_t * dmaAddr, uint32_t cbAddr)
+{
+ DBG(DBG_STARTUP, "");
+
+ dmaAddr[DMA_CS] = DMA_CHANNEL_RESET;
+
+ dmaAddr[DMA_CS] = DMA_INTERRUPT_STATUS | DMA_END_FLAG;
+
+ dmaAddr[DMA_CONBLK_AD] = cbAddr | DMA_BUS_ADR;
+
+ /* clear READ/FIFO/READ_LAST_NOT_SET error bits */
+
+ dmaAddr[DMA_DEBUG] = DMA_DEBUG_READ_ERR |
+ DMA_DEBUG_FIFO_ERR |
+ DMA_DEBUG_RD_LST_NOT_SET_ERR;
+
+
+ dmaAddr[DMA_CS] = DMA_WAIT_ON_WRITES |
+ DMA_PANIC_PRIORITY(8) |
+ DMA_PRIORITY(8) |
+ DMA_ACTIVATE;
+
+ DMAstarted = 1;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void initClearGlobals(void)
+{
+ int i;
+
+ DBG(DBG_STARTUP, "");
+
+ alertBits = 0;
+ monitorBits = 0;
+ notifyBits = 0;
+
+ libInitialised = 0;
+ DMAstarted = 0;
+ hardwareRevision = 0;
+
+ pthAlertRunning = 0;
+ pthFifoRunning = 0;
+ pthSocketRunning = 0;
+
+ wfc[0] = 0;
+ wfc[1] = 0;
+ wfc[2] = 0;
+
+ wfcur=0;
+
+ wfStats.micros = 0;
+ wfStats.highMicros = 0;
+ wfStats.maxMicros = -1;
+
+ wfStats.pulses = 0;
+ wfStats.highPulses = 0;
+ wfStats.maxPulses = PI_WAVE_MAX_PULSES;
+
+ wfStats.cbs = 0;
+ wfStats.highCbs = 0;
+ wfStats.maxCbs = (PAGES_PER_BLOCK * CBS_PER_OPAGE);
+
+ gpioGetSamples.func = NULL;
+ gpioGetSamples.ex = 0;
+ gpioGetSamples.userdata = NULL;
+ gpioGetSamples.bits = 0;
+
+ for (i=0; i<=PI_MAX_USER_GPIO; i++)
+ {
+ wfRx[i].mode = PI_WFRX_NONE;
+
+ gpioAlert[i].func = NULL;
+
+ gpioInfo [i].is = GPIO_UNDEFINED;
+ gpioInfo [i].width = 0;
+ gpioInfo [i].range = PI_DEFAULT_DUTYCYCLE_RANGE;
+ gpioInfo [i].freqIdx = DEFAULT_PWM_IDX;
+ }
+
+ for (i=0; i<PI_NOTIFY_SLOTS; i++)
+ {
+ gpioNotify[i].seqno = 0;
+ gpioNotify[i].state = PI_NOTIFY_CLOSED;
+ }
+
+ for (i=0; i<=PI_MAX_SIGNUM; i++)
+ {
+ gpioSignal[i].func = NULL;
+ gpioSignal[i].ex = 0;
+ gpioSignal[i].userdata = NULL;
+ }
+
+ for (i=0; i<=PI_MAX_TIMER; i++)
+ {
+ gpioTimer[i].running = 0;
+ gpioTimer[i].func = NULL;
+ }
+
+ /* calculate the usable PWM frequencies */
+
+ for (i=0; i<PWM_FREQS; i++)
+ {
+ pwmFreq[i]=
+ (1000000.0/
+ ((float)PULSE_PER_CYCLE*gpioCfg.clockMicros*pwmCycles[i]))+0.5;
+
+ DBG(DBG_STARTUP, "f%d is %d", i, pwmFreq[i]);
+ }
+
+ inpFifo = NULL;
+ outFifo = NULL;
+
+ fdLock = -1;
+ fdMem = -1;
+ fdSock = -1;
+
+ dmaBloc = MAP_FAILED;
+ dmaVirt = MAP_FAILED;
+ dmaPhys = MAP_FAILED;
+
+ clkReg = MAP_FAILED;
+ dmaReg = MAP_FAILED;
+ gpioReg = MAP_FAILED;
+ pcmReg = MAP_FAILED;
+ pwmReg = MAP_FAILED;
+ systReg = MAP_FAILED;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static unsigned initHardwareRevision(void)
+{
+ FILE * filp;
+ unsigned rev;
+ char buf[512];
+ char term;
+
+ rev = 0;
+
+ filp = fopen ("/proc/cpuinfo", "r");
+
+ if (filp != NULL)
+ {
+ while (fgets(buf, sizeof(buf), filp) != NULL)
+ {
+ if (!strncasecmp("revision\t", buf, 9))
+ {
+ if (sscanf(buf+strlen(buf)-5, "%x%c", &rev, &term) == 2)
+ {
+ if (term == '\n') break;
+ rev = 0;
+ }
+ }
+ }
+ fclose(filp);
+ }
+ return rev;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void initReleaseResources(void)
+{
+ int i;
+
+ DBG(DBG_STARTUP, "");
+
+ /* shut down running threads */
+
+ for (i=0; i<=PI_MAX_TIMER; i++)
+ {
+ if (gpioTimer[i].running)
+ {
+ /* destroy thread */
+
+ pthread_cancel(gpioTimer[i].pthId);
+ pthread_join(gpioTimer[i].pthId, NULL);
+ gpioTimer[i].running = 0;
+ }
+ }
+
+ if (pthAlertRunning)
+ {
+ pthread_cancel(pthAlert);
+ pthread_join(pthAlert, NULL);
+ pthAlertRunning = 0;
+ }
+
+ if (pthFifoRunning)
+ {
+ pthread_cancel(pthFifo);
+ pthread_join(pthFifo, NULL);
+ pthFifoRunning = 0;
+ }
+
+ if (pthSocketRunning)
+ {
+ pthread_cancel(pthSocket);
+ pthread_join(pthSocket, NULL);
+ pthSocketRunning = 0;
+ }
+
+ /* release mmap'd memory */
+
+ if (clkReg != MAP_FAILED) munmap((void *)clkReg, CLK_LEN);
+ if (dmaReg != MAP_FAILED) munmap((void *)dmaReg, DMA_LEN);
+ if (gpioReg != MAP_FAILED) munmap((void *)gpioReg, GPIO_LEN);
+ if (pcmReg != MAP_FAILED) munmap((void *)pcmReg, PCM_LEN);
+ if (pwmReg != MAP_FAILED) munmap((void *)pwmReg, PWM_LEN);
+ if (systReg != MAP_FAILED) munmap((void *)systReg, SYST_LEN);
+
+ clkReg = MAP_FAILED;
+ dmaReg = MAP_FAILED;
+ gpioReg = MAP_FAILED;
+ pcmReg = MAP_FAILED;
+ pwmReg = MAP_FAILED;
+ systReg = MAP_FAILED;
+
+ if (dmaVirt != MAP_FAILED)
+ {
+ for (i=0; i<PAGES_PER_BLOCK*(bufferBlocks+1); i++)
+ {
+ munmap(dmaVirt[i], PAGE_SIZE);
+ }
+
+ munmap(dmaVirt, PAGES_PER_BLOCK*(bufferBlocks+1)*sizeof(dmaPage_t *));
+ }
+
+ dmaVirt = MAP_FAILED;
+
+ if (dmaPhys != MAP_FAILED)
+ {
+ for (i=0; i<PAGES_PER_BLOCK*(bufferBlocks+1); i++)
+ {
+ munmap(dmaPhys[i], PAGE_SIZE);
+ }
+
+ munmap(dmaPhys, PAGES_PER_BLOCK*(bufferBlocks+1)*sizeof(dmaPage_t *));
+ }
+
+ dmaPhys = MAP_FAILED;
+
+ if (dmaBloc != MAP_FAILED)
+ {
+ for (i=0; i<(bufferBlocks+1); i++)
+ {
+ munmap(dmaBloc[i], PAGES_PER_BLOCK*PAGE_SIZE);
+ }
+
+ munmap(dmaBloc, (bufferBlocks+1)*sizeof(dmaPage_t *));
+ }
+
+ dmaBloc = MAP_FAILED;
+
+ if (inpFifo != NULL)
+ {
+ fclose(inpFifo);
+ unlink(PI_INPFIFO);
+ inpFifo = NULL;
+ }
+
+ if (outFifo != NULL)
+ {
+ fclose(outFifo);
+ unlink(PI_OUTFIFO);
+ outFifo = NULL;
+ }
+
+ if (fdMem != -1)
+ {
+ close(fdMem);
+ fdMem = -1;
+ }
+
+ if (fdLock != -1)
+ {
+ close(fdLock);
+ unlink(PI_LOCKFILE);
+ fdLock = -1;
+ }
+
+ if (fdSock != -1)
+ {
+ close(fdSock);
+ fdSock = -1;
+ }
+}
+
+/* ======================================================================= */
+
+int gpioInitialise(void)
+{
+ int i;
+
+ clock_gettime(CLOCK_REALTIME, &libStarted);
+
+ DBG(DBG_STARTUP, "");
+
+ if (libInitialised) return PIGPIO_VERSION;
+
+ initClearGlobals();
+
+ if (initCheckPermitted() < 0) return PI_INIT_FAILED;
+
+ fdLock = initGrabLockFile();
+
+ if (fdLock < 0)
+ SOFT_ERROR(PI_INIT_FAILED, "Can't lock %s", PI_LOCKFILE);
+
+ sigSetHandler();
+
+ if (initPeripherals() < 0) return PI_INIT_FAILED;
+
+ if (initDMAcbs() < 0) return PI_INIT_FAILED;
+
+ /* done with /dev/mem */
+
+ if (fdMem != -1)
+ {
+ close(fdMem);
+ fdMem = -1;
+ }
+
+ initClock(1); /* initialise main clock */
+
+ libInitialised = 1;
+
+ atexit(gpioTerminate);
+
+ if (pthread_attr_init(&pthAttr))
+ SOFT_ERROR(PI_INIT_FAILED, "pthread_attr_init failed (%m)");
+
+ if (pthread_attr_setstacksize(&pthAttr, STACK_SIZE))
+ SOFT_ERROR(PI_INIT_FAILED, "pthread_attr_setstacksize failed (%m)");
+
+ if (pthread_create(&pthAlert, &pthAttr, pthAlertThread, &i))
+ SOFT_ERROR(PI_INIT_FAILED, "pthread_create alert failed (%m)");
+
+ pthAlertRunning = 1;
+
+ if (!(gpioCfg.ifFlags & PI_DISABLE_FIFO_IF))
+ {
+ if (pthread_create(&pthFifo, &pthAttr, pthFifoThread, &i))
+ SOFT_ERROR(PI_INIT_FAILED, "pthread_create fifo failed (%m)");
+
+ pthFifoRunning = 1;
+ }
+
+ if (!(gpioCfg.ifFlags & PI_DISABLE_SOCK_IF))
+ {
+ if (pthread_create(&pthSocket, &pthAttr, pthSocketThread, &i))
+ SOFT_ERROR(PI_INIT_FAILED, "pthread_create socket failed (%m)");
+
+ pthSocketRunning = 1;
+ }
+
+ hardwareRevision = initHardwareRevision();
+
+ initDMAgo((uint32_t *)dmaIn, (uint32_t)dmaIPhys[0]);
+
+ return PIGPIO_VERSION;
+ }
+
+/* ----------------------------------------------------------------------- */
+
+void gpioTerminate(void)
+{
+ int i;
+
+ DBG(DBG_USER, "");
+
+
+ if (libInitialised)
+ {
+ /* reset DMA */
+
+ dmaIn[DMA_CS] = DMA_CHANNEL_RESET;
+ dmaOut[DMA_CS] = DMA_CHANNEL_RESET;
+
+ /* reset PWM */
+
+ pwmReg[PWM_CTL] = 0;
+
+ libInitialised = 0;
+
+ DMAstarted = 0;
+
+ if (gpioCfg.showStats)
+ {
+ fprintf(stderr, "micros=%d\n", gpioCfg.clockMicros);
+
+ fprintf(stderr, "samples %u maxSamples %u maxEmit %u emitFrags %u\n",
+ gpioStats.numSamples, gpioStats.maxSamples,
+ gpioStats.maxEmit, gpioStats.emitFrags);
+
+ fprintf(stderr, "cb time %d, calls %u alert ticks %u\n",
+ gpioStats.cbTicks, gpioStats.cbCalls, gpioStats.alertTicks);
+
+ for (i=0; i< TICKSLOTS; i++)
+ fprintf(stderr, "%9u ", gpioStats.diffTick[i]);
+
+ fprintf(stderr, "\n");
+ }
+ }
+
+ initReleaseResources();
+
+ fflush(NULL);
+}
+
+/* ----------------------------------------------------------------------- */
+
+int gpioSetMode(unsigned gpio, unsigned mode)
+{
+ int reg, shift;
+
+ DBG(DBG_USER, "gpio=%d mode=%d", gpio, mode);
+
+ CHECK_INITED;
+
+ if (gpio > PI_MAX_GPIO)
+ SOFT_ERROR(PI_BAD_GPIO, "bad gpio (%d)", gpio);
+
+ if (mode > PI_ALT3)
+ SOFT_ERROR(PI_BAD_MODE, "gpio %d, bad mode (%d)", gpio, mode);
+
+ reg = gpio/10;
+ shift = (gpio%10) * 3;
+
+ if (gpio <= PI_MAX_USER_GPIO)
+ {
+ if (mode != PI_OUTPUT)
+ {
+ switch (gpioInfo[gpio].is)
+ {
+ case GPIO_SERVO:
+ /* switch servo off */
+ myGpioSetServo(gpio,
+ gpioInfo[gpio].width/gpioCfg.clockMicros, 0);
+ break;
+
+ case GPIO_PWM:
+ /* switch pwm off */
+ myGpioSetPwm(gpio, gpioInfo[gpio].width, 0);
+ break;
+
+ }
+
+ gpioInfo[gpio].is = GPIO_UNDEFINED;
+ }
+ }
+
+ gpioReg[reg] = (gpioReg[reg] & ~(7<<shift)) | (mode<<shift);
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioGetMode(unsigned gpio)
+{
+ int reg, shift;
+
+ DBG(DBG_USER, "gpio=%d", gpio);
+
+ CHECK_INITED;
+
+ if (gpio > PI_MAX_GPIO)
+ SOFT_ERROR(PI_BAD_GPIO, "bad gpio (%d)", gpio);
+
+ reg = gpio/10;
+ shift = (gpio%10) * 3;
+
+ return (*(gpioReg + reg) >> shift) & 7;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioSetPullUpDown(unsigned gpio, unsigned pud)
+{
+ DBG(DBG_USER, "gpio=%d pud=%d", gpio, pud);
+
+ CHECK_INITED;
+
+ if (gpio > PI_MAX_GPIO)
+ SOFT_ERROR(PI_BAD_GPIO, "bad gpio (%d)", gpio);
+
+ if (pud > PI_PUD_UP)
+ SOFT_ERROR(PI_BAD_PUD, "gpio %d, bad pud (%d)", gpio, pud);
+
+ *(gpioReg + GPPUD) = pud;
+
+ myGpioDelay(20);
+
+ *(gpioReg + GPPUDCLK0 + BANK) = BIT;
+
+ myGpioDelay(20);
+
+ *(gpioReg + GPPUD) = 0;
+
+ *(gpioReg + GPPUDCLK0 + BANK) = 0;
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioRead(unsigned gpio)
+{
+ DBG(DBG_USER, "gpio=%d", gpio);
+
+ CHECK_INITED;
+
+ if (gpio > PI_MAX_GPIO)
+ SOFT_ERROR(PI_BAD_GPIO, "bad gpio (%d)", gpio);
+
+ if ((*(gpioReg + GPLEV0 + BANK) & BIT) != 0) return PI_ON;
+ else return PI_OFF;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioWrite(unsigned gpio, unsigned level)
+{
+ DBG(DBG_USER, "gpio=%d level=%d", gpio, level);
+
+ CHECK_INITED;
+
+ if (gpio > PI_MAX_GPIO)
+ SOFT_ERROR(PI_BAD_GPIO, "bad gpio (%d)", gpio);
+
+ if (level > PI_ON)
+ SOFT_ERROR(PI_BAD_LEVEL, "gpio %d, bad level (%d)", gpio, level);
+
+ if (gpio <= PI_MAX_USER_GPIO)
+ {
+ if (gpioInfo[gpio].is != GPIO_OUTPUT)
+ {
+ if (gpioInfo[gpio].is == GPIO_UNDEFINED)
+ {
+ gpioSetMode(gpio, PI_OUTPUT);
+ }
+ else if (gpioInfo[gpio].is == GPIO_PWM)
+ {
+ /* switch pwm off */
+ myGpioSetPwm(gpio, gpioInfo[gpio].width, 0);
+ }
+ else if (gpioInfo[gpio].is == GPIO_SERVO)
+ {
+ /* switch servo off */
+ myGpioSetServo(
+ gpio, gpioInfo[gpio].width/gpioCfg.clockMicros, 0);
+ }
+
+ gpioInfo[gpio].is=GPIO_OUTPUT;
+ gpioInfo[gpio].width=0;
+ }
+ }
+
+ if (level == PI_OFF) *(gpioReg + GPCLR0 + BANK) = BIT;
+ else *(gpioReg + GPSET0 + BANK) = BIT;
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioPWM(unsigned gpio, unsigned val)
+{
+ DBG(DBG_USER, "gpio=%d dutycycle=%d", gpio, val);
+
+ CHECK_INITED;
+
+ if (gpio > PI_MAX_USER_GPIO)
+ SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", gpio);
+
+ if (val > gpioInfo[gpio].range)
+ SOFT_ERROR(PI_BAD_DUTYCYCLE, "gpio %d, bad dutycycle (%d)", gpio, val);
+
+ if (gpioInfo[gpio].is != GPIO_PWM)
+ {
+ if (gpioInfo[gpio].is == GPIO_UNDEFINED)
+ {
+ gpioSetMode(gpio, PI_OUTPUT);
+ }
+ else if (gpioInfo[gpio].is == GPIO_SERVO)
+ {
+ /* switch servo off */
+ myGpioSetServo(gpio, gpioInfo[gpio].width, 0);
+ gpioInfo[gpio].width = 0;
+ gpioInfo[gpio].freqIdx = DEFAULT_PWM_IDX; /* default frequency */
+ }
+ gpioInfo[gpio].is = GPIO_PWM;
+ }
+
+ myGpioSetPwm(gpio, gpioInfo[gpio].width, val);
+
+ gpioInfo[gpio].width=val;
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioSetPWMrange(unsigned gpio, unsigned range)
+{
+ int oldWidth, newWidth;
+
+ DBG(DBG_USER, "gpio=%d range=%d", gpio, range);
+
+ CHECK_INITED;
+
+ if (gpio > PI_MAX_USER_GPIO)
+ SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", gpio);
+
+ if ((range < PI_MIN_DUTYCYCLE_RANGE) || (range > PI_MAX_DUTYCYCLE_RANGE))
+ SOFT_ERROR(PI_BAD_DUTY_RANGE, "gpio %d, bad range (%d)", gpio, range);
+
+ oldWidth = gpioInfo[gpio].width;
+
+ if (oldWidth)
+ {
+ if (gpioInfo[gpio].is == GPIO_PWM)
+ {
+ newWidth = (range * oldWidth) / gpioInfo[gpio].range;
+
+ myGpioSetPwm(gpio, oldWidth, 0);
+ gpioInfo[gpio].range = range;
+ gpioInfo[gpio].width = newWidth;
+ myGpioSetPwm(gpio, 0, newWidth);
+ }
+ }
+
+ gpioInfo[gpio].range = range;
+
+ /* return the actual range for the current gpio frequency */
+
+ return pwmRealRange[gpioInfo[gpio].freqIdx];
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioGetPWMrange(unsigned gpio)
+{
+ DBG(DBG_USER, "gpio=%d", gpio);
+
+ CHECK_INITED;
+
+ if (gpio > PI_MAX_USER_GPIO)
+ SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", gpio);
+
+ return (gpioInfo[gpio].range);
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioGetPWMrealRange(unsigned gpio)
+{
+ DBG(DBG_USER, "gpio=%d", gpio);
+
+ CHECK_INITED;
+
+ if (gpio > PI_MAX_USER_GPIO)
+ SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", gpio);
+
+ return pwmRealRange[gpioInfo[gpio].freqIdx];
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioSetPWMfrequency(unsigned gpio, unsigned frequency)
+{
+ int i, width;
+ unsigned diff, best, idx;
+
+ DBG(DBG_USER, "gpio=%d frequency=%d", gpio, frequency);
+
+ CHECK_INITED;
+
+ if (gpio > PI_MAX_USER_GPIO)
+ SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", gpio);
+
+ if (frequency > pwmFreq[0]) idx = 0;
+ else if (frequency < pwmFreq[PWM_FREQS-1]) idx = PWM_FREQS-1;
+ else
+ {
+ best = 100000; /* impossibly high frequency difference */
+ idx = 0;
+
+ for (i=0; i<PWM_FREQS; i++)
+ {
+ if (frequency > pwmFreq[i]) diff = frequency - pwmFreq[i];
+ else diff = pwmFreq[i] - frequency;
+
+ if (diff < best)
+ {
+ best = diff;
+ idx = i;
+ }
+ }
+ }
+
+ width = gpioInfo[gpio].width;
+
+ if (width)
+ {
+ if (gpioInfo[gpio].is == GPIO_PWM)
+ {
+ myGpioSetPwm(gpio, width, 0);
+ gpioInfo[gpio].freqIdx = idx;
+ myGpioSetPwm(gpio, 0, width);
+ }
+ }
+
+ gpioInfo[gpio].freqIdx = idx;
+
+ return pwmFreq[idx];
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioGetPWMfrequency(unsigned gpio)
+{
+ DBG(DBG_USER, "gpio=%d", gpio);
+
+ CHECK_INITED;
+
+ if (gpio > PI_MAX_USER_GPIO)
+ SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", gpio);
+
+ return (pwmFreq[gpioInfo[gpio].freqIdx]);
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioServo(unsigned gpio, unsigned val)
+{
+ DBG(DBG_USER, "gpio=%d pulsewidth=%d", gpio, val);
+
+ CHECK_INITED;
+
+ if (gpio > PI_MAX_USER_GPIO)
+ SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", gpio);
+
+ if ((val!=PI_SERVO_OFF) && (val<PI_MIN_SERVO_PULSEWIDTH))
+ SOFT_ERROR(PI_BAD_PULSEWIDTH,
+ "gpio %d, bad pulsewidth (%d)", gpio, val);
+
+ if (val>PI_MAX_SERVO_PULSEWIDTH)
+ SOFT_ERROR(PI_BAD_PULSEWIDTH,
+ "gpio %d, bad pulsewidth (%d)", gpio, val);
+
+ if (gpioInfo[gpio].is != GPIO_SERVO)
+ {
+ if (gpioInfo[gpio].is == GPIO_UNDEFINED)
+ {
+ gpioSetMode(gpio, PI_OUTPUT);
+ }
+ else if (gpioInfo[gpio].is == GPIO_PWM)
+ {
+ /* switch pwm off */
+ myGpioSetPwm(gpio, gpioInfo[gpio].width, 0);
+ gpioInfo[gpio].width=0;
+ }
+ gpioInfo[gpio].is = GPIO_SERVO;
+ gpioInfo[gpio].freqIdx = clkCfg[gpioCfg.clockMicros].servoIdx;
+ }
+
+ myGpioSetServo(gpio, gpioInfo[gpio].width, val);
+
+ gpioInfo[gpio].width=val;
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioWaveGetMicros(void)
+{
+ DBG(DBG_USER, "");
+
+ CHECK_INITED;
+
+ return wfStats.micros;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioWaveGetHighMicros(void)
+{
+ DBG(DBG_USER, "");
+
+ CHECK_INITED;
+
+ return wfStats.highMicros;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioWaveGetMaxMicros(void)
+{
+ DBG(DBG_USER, "");
+
+ CHECK_INITED;
+
+ return wfStats.maxMicros;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioWaveGetPulses(void)
+{
+ DBG(DBG_USER, "");
+
+ CHECK_INITED;
+
+ return wfStats.pulses;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioWaveGetHighPulses(void)
+{
+ DBG(DBG_USER, "");
+
+ CHECK_INITED;
+
+ return wfStats.highPulses;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioWaveGetMaxPulses(void)
+{
+ DBG(DBG_USER, "");
+
+ CHECK_INITED;
+
+ return wfStats.maxPulses;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioWaveGetCbs(void)
+{
+ DBG(DBG_USER, "");
+
+ CHECK_INITED;
+
+ return wfStats.cbs;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioWaveGetHighCbs(void)
+{
+ DBG(DBG_USER, "");
+
+ CHECK_INITED;
+
+ return wfStats.highCbs;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioWaveGetMaxCbs(void)
+{
+ DBG(DBG_USER, "");
+
+ CHECK_INITED;
+
+ return wfStats.maxCbs;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioWaveClear(void)
+{
+ DBG(DBG_USER, "");
+
+ CHECK_INITED;
+
+ wfc[0] = 0;
+ wfc[1] = 0;
+ wfc[2] = 0;
+
+ wfcur = 0;
+
+ wfStats.micros = 0;
+ wfStats.pulses = 0;
+ wfStats.cbs = 0;
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioWaveAddGeneric(unsigned numPulses, gpioPulse_t * pulses)
+{
+ DBG(DBG_USER, "numPulses=%u pulses=%08X", numPulses, (uint32_t)pulses);
+
+ CHECK_INITED;
+
+ if (numPulses > PI_WAVE_MAX_PULSES)
+ SOFT_ERROR(PI_TOO_MANY_PULSES, "bad number of pulses (%d)", numPulses);
+
+ return waveMerge(numPulses, pulses);
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioWaveAddSerial(unsigned gpio,
+ unsigned baud,
+ unsigned numChar,
+ char * str)
+{
+ int i, b, p, lev, c, v;
+
+ unsigned bitDelay[10];
+
+ DBG(DBG_USER, "gpio=%d baud=%d numChar=%d str*=%08X",
+ gpio, baud, numChar, (uint32_t)str);
+
+ CHECK_INITED;
+
+ if (gpio > PI_MAX_USER_GPIO)
+ SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", gpio);
+
+ if ((baud < PI_WAVE_MIN_BAUD) || (baud > PI_WAVE_MAX_BAUD))
+ SOFT_ERROR(PI_BAD_WAVE_BAUD,
+ "gpio %d, bad baud rate (%d)", gpio, baud);
+
+ if (numChar > PI_WAVE_MAX_CHARS)
+ SOFT_ERROR(PI_TOO_MANY_CHARS, "too many chars (%d)", numChar);
+
+ if (!numChar) return 0;
+
+ waveBitDelay(baud, bitDelay);
+
+ p = 0;
+
+ wf[2][p].gpioOn = (1<<gpio);
+ wf[2][p].gpioOff = 0;
+ wf[2][p].usDelay = bitDelay[0];
+
+ for (i=0; i<numChar; i++)
+ {
+ p++;
+
+ /* start bit */
+
+ wf[2][p].gpioOn = 0;
+ wf[2][p].gpioOff = (1<<gpio);
+ wf[2][p].usDelay = bitDelay[0];
+
+ lev = 0;
+
+ c = str[i];
+
+ for (b=0; b<8; b++)
+ {
+ if (c & (1<<b)) v=1; else v=0;
+
+ if (v == lev) wf[2][p].usDelay += bitDelay[b+1];
+ else
+ {
+ p++;
+
+ lev = v;
+
+ if (lev)
+ {
+ wf[2][p].gpioOn = (1<<gpio);
+ wf[2][p].gpioOff = 0;
+ }
+ else
+ {
+ wf[2][p].gpioOn = 0;
+ wf[2][p].gpioOff = (1<<gpio);
+ }
+
+ wf[2][p].usDelay = bitDelay[b+1];
+ }
+ }
+
+ /* stop bit */
+
+ if (lev) wf[2][p].usDelay += bitDelay[9];
+ else
+ {
+ p++;
+
+ wf[2][p].gpioOn = (1<<gpio);
+ wf[2][p].gpioOff = 0;
+ wf[2][p].usDelay = bitDelay[9];
+ }
+ }
+
+ p++;
+
+ wf[2][p].gpioOn = (1<<gpio);
+ wf[2][p].gpioOff = 0;
+ wf[2][p].usDelay = bitDelay[0];
+
+ return waveMerge(p, wf[2]);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+int gpioWaveSerialReadStart(unsigned gpio,
+ unsigned baud,
+ gpioRx_t *rxp)
+{
+ int bitTime, timeoutMs;
+
+ DBG(DBG_USER, "gpio=%d baud=%d rxp*=%08X", gpio, baud, (uint32_t)rxp);
+
+ CHECK_INITED;
+
+ if (gpio > PI_MAX_USER_GPIO)
+ SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", gpio);
+
+ if ((baud < PI_WAVE_MIN_BAUD) || (baud > PI_WAVE_MAX_BAUD))
+ SOFT_ERROR(PI_BAD_WAVE_BAUD,
+ "gpio %d, bad baud rate (%d)", gpio, baud);
+
+ if (rxp == NULL)
+ SOFT_ERROR(PI_BAD_SERIAL_STRUC, "Null structure pointer");
+
+ if (rxp->buf == NULL)
+ SOFT_ERROR(PI_BAD_SERIAL_BUF, "Null buffer pointer");
+
+ bitTime = MILLION / baud;
+
+ timeoutMs = ((12 * bitTime)+1000)/1000;
+
+ wfRx[gpio].mode = PI_WFRX_SERIAL;
+ wfRx[gpio].baud = baud;
+ wfRx[gpio].rxp = rxp;
+ wfRx[gpio].baud = baud;
+ wfRx[gpio].fullBit = bitTime;
+ wfRx[gpio].halfBit = bitTime/2;
+ wfRx[gpio].rxp->readPos = 0;
+ wfRx[gpio].rxp->writePos = 0;
+ wfRx[gpio].bit = -1;
+
+ gpioSetWatchdog(gpio, timeoutMs); /* get a nudge if no change */
+
+ gpioSetAlertFunc(gpio, waveRxBit);
+
+ return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+int gpioWaveSerialReadStop(unsigned gpio)
+{
+ DBG(DBG_USER, "gpio=%d", gpio);
+
+ CHECK_INITED;
+
+ if (gpio > PI_MAX_USER_GPIO)
+ SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", gpio);
+
+ switch(wfRx[gpio].mode)
+ {
+ case PI_WFRX_NONE:
+
+ SOFT_ERROR(PI_NOT_SERIAL_GPIO, "no serial read on gpio (%d)", gpio);
+
+ break;
+
+ case PI_WFRX_SERIAL:
+
+ gpioSetWatchdog(gpio, 0); /* switch off timeouts */
+
+ gpioSetAlertFunc(gpio, NULL); /* cancel alert */
+
+ wfRx[gpio].mode = PI_WFRX_NONE;
+
+ break;
+ }
+
+ return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+int gpioWaveTxBusy(void)
+{
+ DBG(DBG_USER, "");
+
+ CHECK_INITED;
+
+ if (dmaOut[DMA_CONBLK_AD])
+ return 1;
+ else
+ return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioWaveTxStart(unsigned mode)
+{
+ static int secondaryClockInited = 0;
+
+ int cb, i;
+
+ DBG(DBG_USER, "mode=%d", mode);
+
+ CHECK_INITED;
+
+ if (mode > PI_WAVE_MODE_REPEAT)
+ SOFT_ERROR(PI_BAD_WAVE_MODE, "bad wave mode (%d)", mode);
+
+ if (wfc[wfcur] == 0) return 0;
+
+ if (!secondaryClockInited)
+ {
+ initClock(0); /* initialise secondary clock */
+ secondaryClockInited = 1;
+ }
+
+ cb = wave2Cbs(mode);
+
+ if (gpioCfg.dbgLevel >= DBG_SLOW_TICK)
+ for (i=0; i<cb; i++) waveCbOPrint(i);
+
+ initDMAgo((uint32_t *)dmaOut, (uint32_t)dmaOPhys[0]);
+
+ return cb;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioWaveTxStop(void)
+{
+ DBG(DBG_USER, "");
+
+ CHECK_INITED;
+
+ dmaOut[DMA_CS] = DMA_CHANNEL_RESET;
+
+ dmaOut[DMA_CONBLK_AD] = 0;
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+static int intGpioSetAlertFunc(
+ unsigned gpio,
+ void * f,
+ int user,
+ void * userdata)
+{
+ DBG(DBG_INTERNAL, "gpio=%d function=%08X, user=%d, userdata=%08X",
+ gpio, (uint32_t)f, user, (uint32_t)userdata);
+
+ gpioAlert[gpio].ex = user;
+ gpioAlert[gpio].userdata = userdata;
+
+ gpioAlert[gpio].func = f;
+
+ if (f)
+ {
+ alertBits |= BIT;
+ }
+ else
+ {
+ alertBits &= ~BIT;
+ }
+
+ monitorBits = alertBits | notifyBits | gpioGetSamples.bits;
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+int gpioSetAlertFunc(unsigned gpio, gpioAlertFunc_t f)
+{
+ DBG(DBG_USER, "gpio=%d function=%08X", gpio, (uint32_t)f);
+
+ CHECK_INITED;
+
+ if (gpio > PI_MAX_USER_GPIO)
+ SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", gpio);
+
+ intGpioSetAlertFunc(gpio, f, 0, NULL);
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioSetAlertFuncEx(unsigned gpio, gpioAlertFuncEx_t f, void * userdata)
+{
+ DBG(DBG_USER, "gpio=%d function=%08X userdata=%08X",
+ gpio, (uint32_t)f, (uint32_t)userdata);
+
+ CHECK_INITED;
+
+ if (gpio > PI_MAX_USER_GPIO)
+ SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", gpio);
+
+ intGpioSetAlertFunc(gpio, f, 1, userdata);
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioNotifyOpen(void)
+{
+ int i, slot, fd;
+ char name[32];
+
+ DBG(DBG_USER, "");
+
+ CHECK_INITED;
+
+ slot = -1;
+
+ for (i=0; i<PI_NOTIFY_SLOTS; i++)
+ {
+ if (gpioNotify[i].state == PI_NOTIFY_CLOSED)
+ {
+ slot = i;
+ break;
+ }
+ }
+
+ if (slot < 0)
+ SOFT_ERROR(PI_NO_HANDLE, "no handle");
+
+ sprintf(name, "/dev/pigpio%d", slot);
+
+ myCreatePipe(name, 0664);
+
+ fd = open(name, O_RDWR|O_NONBLOCK);
+
+ if (fd < 0)
+ SOFT_ERROR(PI_BAD_PATHNAME, "open %s failed (%m)", name);
+
+ gpioNotify[slot].state = PI_NOTIFY_OPENED;
+ gpioNotify[slot].seqno = 0;
+ gpioNotify[slot].bits = 0;
+ gpioNotify[slot].fd = fd;
+ gpioNotify[slot].pipe = 1;
+
+ return slot;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+static int gpioNotifyOpenInBand(int fd)
+{
+ int i, slot;
+
+ DBG(DBG_USER, "");
+
+ CHECK_INITED;
+
+ slot = -1;
+
+ for (i=0; i<PI_NOTIFY_SLOTS; i++)
+ {
+ if (gpioNotify[i].state == PI_NOTIFY_CLOSED)
+ {
+ slot = i;
+ break;
+ }
+ }
+
+ if (slot < 0) SOFT_ERROR(PI_NO_HANDLE, "no handle");
+
+ gpioNotify[slot].state = PI_NOTIFY_OPENED;
+ gpioNotify[slot].seqno = 0;
+ gpioNotify[slot].bits = 0;
+ gpioNotify[slot].fd = fd;
+ gpioNotify[slot].pipe = 0;
+
+ return slot;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+static void intNotifyBits(void)
+{
+ int i;
+ uint32_t bits;
+
+ bits = 0;
+
+ for (i=0; i<PI_NOTIFY_SLOTS; i++)
+ {
+ if (gpioNotify[i].state == PI_NOTIFY_RUNNING)
+ {
+ bits |= gpioNotify[i].bits;
+ }
+ }
+
+ notifyBits = bits;
+
+ monitorBits = alertBits | notifyBits | gpioGetSamples.bits;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioNotifyBegin(unsigned handle, uint32_t bits)
+{
+ DBG(DBG_USER, "handle=%d bits=%08X", handle, bits);
+
+ CHECK_INITED;
+
+ if (handle > PI_NOTIFY_SLOTS)
+ SOFT_ERROR(PI_BAD_HANDLE, "bad handle (%d)", handle);
+
+ if (gpioNotify[handle].state <= PI_NOTIFY_CLOSING)
+ SOFT_ERROR(PI_BAD_HANDLE, "bad handle (%d)", handle);
+
+ gpioNotify[handle].bits = bits;
+
+ gpioNotify[handle].state = PI_NOTIFY_RUNNING;
+
+ intNotifyBits();
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioNotifyPause (unsigned handle)
+{
+ DBG(DBG_USER, "handle=%d", handle);
+
+ CHECK_INITED;
+
+ if (handle > PI_NOTIFY_SLOTS)
+ SOFT_ERROR(PI_BAD_HANDLE, "bad handle (%d)", handle);
+
+ if (gpioNotify[handle].state <= PI_NOTIFY_CLOSING)
+ SOFT_ERROR(PI_BAD_HANDLE, "bad handle (%d)", handle);
+
+ gpioNotify[handle].bits = 0;
+
+ gpioNotify[handle].state = PI_NOTIFY_PAUSED;
+
+ intNotifyBits();
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioNotifyClose(unsigned handle)
+{
+ DBG(DBG_USER, "handle=%d", handle);
+
+ CHECK_INITED;
+
+ if (handle > PI_NOTIFY_SLOTS)
+ SOFT_ERROR(PI_BAD_HANDLE, "bad handle (%d)", handle);
+
+ if (gpioNotify[handle].state <= PI_NOTIFY_CLOSING)
+ SOFT_ERROR(PI_BAD_HANDLE, "bad handle (%d)", handle);
+
+ gpioNotify[handle].bits = 0;
+
+ gpioNotify[handle].state = PI_NOTIFY_CLOSING;
+
+ intNotifyBits();
+
+ /* actual close done in alert thread */
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioSetWatchdog(unsigned gpio, unsigned timeout)
+{
+ DBG(DBG_USER, "gpio=%d timeout=%d", gpio, timeout);
+
+ CHECK_INITED;
+
+ if (gpio > PI_MAX_USER_GPIO)
+ SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", gpio);
+
+ if (timeout > PI_MAX_WDOG_TIMEOUT)
+ SOFT_ERROR(PI_BAD_WDOG_TIMEOUT,
+ "gpio %d, bad timeout (%d)", gpio, timeout);
+
+ gpioAlert[gpio].timeout = timeout;
+ gpioAlert[gpio].tick = systReg[SYST_CLO];
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+int gpioSetGetSamplesFunc(gpioGetSamplesFunc_t f, uint32_t bits)
+{
+ DBG(DBG_USER, "function=%08X bits=%08X", (uint32_t)f, bits);
+
+ CHECK_INITED;
+
+ gpioGetSamples.ex = 0;
+ gpioGetSamples.userdata = NULL;
+ gpioGetSamples.func = f;
+
+ if (f) gpioGetSamples.bits = bits;
+ else gpioGetSamples.bits = 0;
+
+ monitorBits = alertBits | notifyBits | gpioGetSamples.bits;
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioSetGetSamplesFuncEx(gpioGetSamplesFuncEx_t f,
+ uint32_t bits,
+ void * userdata)
+{
+ DBG(DBG_USER, "function=%08X bits=%08X", (uint32_t)f, bits);
+
+ CHECK_INITED;
+
+ gpioGetSamples.ex = 1;
+ gpioGetSamples.userdata = userdata;
+ gpioGetSamples.func = f;
+
+ if (f) gpioGetSamples.bits = bits;
+ else gpioGetSamples.bits = 0;
+
+ monitorBits = alertBits | notifyBits | gpioGetSamples.bits;
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+static int intGpioSetTimerFunc(unsigned id,
+ unsigned ms,
+ void * f,
+ int user,
+ void * userdata)
+{
+ DBG(DBG_INTERNAL, "id=%d ms=%d function=%08X user=%d userdata=%08X",
+ id, ms, (uint32_t)f, user, (uint32_t)userdata);
+
+ gpioTimer[id].id = id;
+
+ if (f)
+ {
+ gpioTimer[id].func = f;
+ gpioTimer[id].ex = user;
+ gpioTimer[id].userdata = userdata;
+ gpioTimer[id].millis = ms;
+
+ if (!gpioTimer[id].running)
+ {
+ if (pthread_create(
+ &gpioTimer[id].pthId, &pthAttr, pthTimerTick, &gpioTimer[id]))
+ SOFT_ERROR(PI_TIMER_FAILED,
+ "timer %d, create failed (%m)", id);
+
+ gpioTimer[id].running = 1;
+ }
+ }
+ else
+ {
+ if (gpioTimer[id].running)
+ {
+ /* destroy thread */
+
+ if (pthread_cancel(gpioTimer[id].pthId))
+ SOFT_ERROR(PI_TIMER_FAILED, "timer %d, cancel failed (%m)", id);
+
+ if (pthread_join(gpioTimer[id].pthId, NULL))
+ SOFT_ERROR(PI_TIMER_FAILED, "timer %d, join failed (%m)", id);
+
+ gpioTimer[id].running = 0;
+ gpioTimer[id].func = f;
+ }
+ }
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioSetTimerFunc(unsigned id, unsigned ms, gpioTimerFunc_t f)
+{
+ DBG(DBG_USER, "id=%d ms=%d function=%08X", id, ms, (uint32_t)f);
+
+ CHECK_INITED;
+
+ if (id > PI_MAX_TIMER)
+ SOFT_ERROR(PI_BAD_TIMER, "bad timer id (%d)", id);
+
+ if ((ms < PI_MIN_MS) || (ms > PI_MAX_MS))
+ SOFT_ERROR(PI_BAD_MS, "timer %d, bad ms (%d)", id, ms);
+
+ intGpioSetTimerFunc(id, ms, f, 0, NULL);
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioSetTimerFuncEx(unsigned id, unsigned ms, gpioTimerFuncEx_t f,
+ void * userdata)
+{
+ DBG(DBG_USER, "id=%d ms=%d function=%08X, userdata=%08X",
+ id, ms, (uint32_t)f, (uint32_t)userdata);
+
+ CHECK_INITED;
+
+ if (id > PI_MAX_TIMER)
+ SOFT_ERROR(PI_BAD_TIMER, "bad timer id (%d)", id);
+
+ if ((ms < PI_MIN_MS) || (ms > PI_MAX_MS))
+ SOFT_ERROR(PI_BAD_MS, "timer %d, bad ms (%d)", id, ms);
+
+ intGpioSetTimerFunc(id, ms, f, 1, userdata);
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioSetSignalFunc(unsigned signum, gpioSignalFunc_t f)
+{
+ DBG(DBG_USER, "signum=%d function=%08X", signum, (uint32_t)f);
+
+ CHECK_INITED;
+
+ if (signum > PI_MAX_SIGNUM)
+ SOFT_ERROR(PI_BAD_SIGNUM, "bad signum (%d)", signum);
+
+ gpioSignal[signum].ex = 0;
+ gpioSignal[signum].userdata = NULL;
+
+ gpioSignal[signum].func = f;
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioSetSignalFuncEx(unsigned signum, gpioSignalFuncEx_t f,
+ void * userdata)
+{
+ DBG(DBG_USER, "signum=%d function=%08X userdata=%08X",
+ signum, (uint32_t)f, (uint32_t)userdata);
+
+ CHECK_INITED;
+
+ if (signum > PI_MAX_SIGNUM)
+ SOFT_ERROR(PI_BAD_SIGNUM, "bad signum (%d)", signum);
+
+ gpioSignal[signum].ex = 1;
+ gpioSignal[signum].userdata = userdata;
+
+ gpioSignal[signum].func = f;
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+uint32_t gpioRead_Bits_0_31(void)
+{
+ DBG(DBG_USER, "");
+
+ CHECK_INITED;
+
+ return (*(gpioReg + GPLEV0));
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+uint32_t gpioRead_Bits_32_53(void)
+{
+ DBG(DBG_USER, "");
+
+ CHECK_INITED;
+
+ return (*(gpioReg + GPLEV1));
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioWrite_Bits_0_31_Clear(uint32_t levels)
+{
+ DBG(DBG_USER, "levels=%08X", levels);
+
+ CHECK_INITED;
+
+ *(gpioReg + GPCLR0) = levels;
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioWrite_Bits_32_53_Clear(uint32_t levels)
+{
+ DBG(DBG_USER, "levels=%08X", levels);
+
+ CHECK_INITED;
+
+ *(gpioReg + GPCLR1) = levels;
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioWrite_Bits_0_31_Set(uint32_t levels)
+{
+ DBG(DBG_USER, "levels=%08X", levels);
+
+ CHECK_INITED;
+
+ *(gpioReg + GPSET0) = levels;
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioWrite_Bits_32_53_Set(uint32_t levels)
+{
+ DBG(DBG_USER, "levels=%08X", levels);
+
+ CHECK_INITED;
+
+ *(gpioReg + GPSET1) = levels;
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioTime(unsigned timetype, int * seconds, int * micros)
+{
+ struct timespec ts;
+
+ DBG(DBG_USER, "timetype=%d &seconds=%08X µs=%08X",
+ timetype, (uint32_t)seconds, (uint32_t)micros);
+
+ CHECK_INITED;
+
+ if (timetype > PI_TIME_ABSOLUTE)
+ SOFT_ERROR(PI_BAD_TIMETYPE, "bad timetype (%d)", timetype);
+
+ if (timetype == PI_TIME_ABSOLUTE)
+ {
+ clock_gettime(CLOCK_REALTIME, &ts);
+ *seconds = ts.tv_sec;
+ *micros = ts.tv_nsec/1000;
+ }
+ else
+ {
+ clock_gettime(CLOCK_REALTIME, &ts);
+
+ TIMER_SUB(&ts, &libStarted, &ts);
+
+ *seconds = ts.tv_sec;
+ *micros = ts.tv_nsec/1000;
+ }
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioSleep(unsigned timetype, int seconds, int micros)
+{
+ struct timespec ts, rem;
+
+ DBG(DBG_USER, "timetype=%d seconds=%d micros=%d",
+ timetype, seconds, micros);
+
+ CHECK_INITED;
+
+ if (timetype > PI_TIME_ABSOLUTE)
+ SOFT_ERROR(PI_BAD_TIMETYPE, "bad timetype (%d)", timetype);
+
+ if (seconds < 0)
+ SOFT_ERROR(PI_BAD_SECONDS, "bad seconds (%d)", seconds);
+
+ if ((micros < 0) || (micros > 999999))
+ SOFT_ERROR(PI_BAD_MICROS, "bad micros (%d)", micros);
+
+ ts.tv_sec = seconds;
+ ts.tv_nsec = micros * 1000;
+
+ if (timetype == PI_TIME_ABSOLUTE)
+ {
+ while (clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &ts, &rem));
+ }
+ else
+ {
+ while (clock_nanosleep(CLOCK_REALTIME, 0, &ts, &rem))
+ {
+ /* copy remaining time to ts */
+ ts.tv_sec = rem.tv_sec;
+ ts.tv_nsec = rem.tv_nsec;
+ }
+ }
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+uint32_t gpioDelay(uint32_t micros)
+{
+ uint32_t start;
+
+ DBG(DBG_USER, "microseconds=%u", micros);
+
+ CHECK_INITED;
+
+ start = systReg[SYST_CLO];
+
+ if (micros < 100) while ((systReg[SYST_CLO] - start) <= micros) ;
+
+ else gpioSleep(PI_TIME_RELATIVE, (micros/MILLION), (micros%MILLION));
+
+ return (systReg[SYST_CLO] - start);
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+uint32_t gpioTick(void)
+{
+ CHECK_INITED;
+
+ return systReg[SYST_CLO];
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+unsigned gpioHardwareRevision(void)
+{
+ DBG(DBG_USER, "");
+
+ CHECK_INITED;
+
+ return hardwareRevision;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioCfgBufferSize(unsigned millis)
+{
+ DBG(DBG_USER, "millis=%d", millis);
+
+ CHECK_NOT_INITED;
+
+ if ((millis < PI_BUF_MILLIS_MIN) || (millis > PI_BUF_MILLIS_MAX))
+ SOFT_ERROR(PI_BAD_BUF_MILLIS, "bad millis (%d)", millis);
+
+ gpioCfg.bufferMilliseconds = millis;
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioCfgClock(unsigned micros, unsigned peripheral, unsigned source)
+{
+ DBG(DBG_USER, "micros=%d peripheral=%d source=%d",
+ micros, peripheral, source);
+
+ CHECK_NOT_INITED;
+
+ if ((micros < 1) || (micros > 10))
+ SOFT_ERROR(PI_BAD_CLK_MICROS, "bad micros (%d)", micros);
+
+ if (!clkCfg[micros].valid)
+ SOFT_ERROR(PI_BAD_CLK_MICROS, "bad micros (%d)", micros);
+
+ if (peripheral > PI_CLOCK_PCM)
+ SOFT_ERROR(PI_BAD_CLK_PERIPH, "bad peripheral (%d)", peripheral);
+
+ if (source > PI_CLOCK_PLLD)
+ SOFT_ERROR(PI_BAD_CLK_SOURCE, "bad clock (%d)", source);
+
+ gpioCfg.clockMicros = micros;
+ gpioCfg.clockPeriph = peripheral;
+ gpioCfg.clockSource = source;
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioCfgDMAchannel(unsigned channel)
+{
+ DBG(DBG_USER, "channel=%d", channel);
+
+ CHECK_NOT_INITED;
+
+ if ((channel < PI_MIN_DMA_CHANNEL) || (channel > PI_MAX_DMA_CHANNEL))
+ SOFT_ERROR(PI_BAD_CHANNEL, "bad channel (%d)", channel);
+
+ gpioCfg.DMAprimaryChannel = channel;
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioCfgDMAchannels(unsigned primaryChannel, unsigned secondaryChannel)
+{
+ DBG(DBG_USER, "primary channel=%d, secondary channel=%d",
+ primaryChannel, secondaryChannel);
+
+ CHECK_NOT_INITED;
+
+ if (primaryChannel > PI_MAX_PRIMARY_CHANNEL)
+ SOFT_ERROR(PI_BAD_PRIM_CHANNEL, "bad primary channel (%d)",
+ primaryChannel);
+
+ if (secondaryChannel > PI_MAX_SECONDARY_CHANNEL)
+ SOFT_ERROR(PI_BAD_SECO_CHANNEL, "bad secondary channel (%d)",
+ secondaryChannel);
+
+ gpioCfg.DMAprimaryChannel = primaryChannel;
+ gpioCfg.DMAsecondaryChannel = secondaryChannel;
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioCfgInterfaces(unsigned ifFlags)
+{
+ DBG(DBG_USER, "ifFlags=%X", ifFlags);
+
+ CHECK_NOT_INITED;
+
+ if (ifFlags > 3)
+ SOFT_ERROR(PI_BAD_IF_FLAGS, "bad ifFlags (%X)", ifFlags);
+
+ gpioCfg.ifFlags = ifFlags;
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioCfgSocketPort(unsigned port)
+{
+ DBG(DBG_USER, "port=%d", port);
+
+ CHECK_NOT_INITED;
+
+ if ((port < PI_MIN_SOCKET_PORT) || (port > PI_MAX_SOCKET_PORT))
+ SOFT_ERROR(PI_BAD_SOCKET_PORT, "bad port (%d)", port);
+
+ gpioCfg.socketPort = port;
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioCfgInternals(unsigned what, int value)
+{
+ int retVal = PI_BAD_CFG_INTERNAL;
+
+ DBG(DBG_USER, "what=%u, value=%d", what, value);
+
+ CHECK_NOT_INITED;
+
+ /*
+ 133084774
+ 207081315
+ 293640712
+ 394342930
+ 472769257
+ 430873902
+ 635370313
+ 684442696
+ 786301093
+ 816051706
+ 858202631
+ 997413601
+ */
+
+ switch(what)
+ {
+ case 562484977:
+
+ gpioCfg.showStats = value;
+
+ DBG(DBG_MIN_LEVEL, "showStats is %u", value);
+
+ retVal = 0;
+
+ break;
+
+ case 984762879:
+
+ if (value < DBG_MIN_LEVEL) value = DBG_MIN_LEVEL;
+
+ if (value > DBG_MAX_LEVEL) value = DBG_MAX_LEVEL;
+
+ gpioCfg.dbgLevel = value;
+
+ DBG(DBG_MIN_LEVEL, "Debug level is %u", value);
+
+ retVal = 0;
+
+ break;
+ }
+
+ return retVal;
+}
+
--- /dev/null
+/*
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <http://unlicense.org/>
+*/
+
+/*
+This version is for pigpio version 6
+*/
+
+#ifndef PIGPIO_H
+#define PIGPIO_H
+
+/************************************************************************** /
+/ /
+/ pigpio is a C library for the Raspberry Pi which allows /
+/ control of the gpios. /
+/ /
+/ Its main features are: /
+/ /
+/ 1) provision of PWM on any number of gpios 0-31 simultaneously. /
+/ 2) provision of servo pulses on any number of gpios 0-31 simultaneously. /
+/ 3) callbacks when any of gpios 0-31 change state. /
+/ 4) callbacks at timed intervals. /
+/ 5) reading/writing all of the gpios in a bank (0-31, 32-53) as a /
+/ single operation. /
+/ 6) individually setting gpio modes, reading and writing. /
+/ 7) notifications when any of gpios 0-31 change state. /
+/ 8) the construction of arbitrary waveforms to give precise timing of /
+/ output gpio level changes. /
+/ /
+/ NOTE: /
+/ /
+/ ALL gpios are identified by their Broadcom number. /
+/ /
+*************************************************************************** /
+/ /
+/ The PWM and servo pulses are timed using the DMA and PWM peripherals. /
+/ /
+/ This use was inspired by Richard Hirst's servoblaster kernel module. /
+/ See https://github.com/richardghirst/PiBits /
+/ Tag rgh on the Raspberry Pi forums http://www.raspberrypi.org/phpBB3/ /
+/ /
+*************************************************************************** /
+/ /
+/ Usage: /
+/ /
+/ copy libpigpio.a to /usr/local/lib /
+/ copy pigpio.h to /usr/local/include /
+/ /
+/ #include <pigpio.h> in your source files /
+/ /
+/ Assuming your source is in example.c use the following command to build /
+/ /
+/ gcc -o example example.c -lpigpio -lpthread -lrt /
+/ /
+/ For examples see checklib.c, demolib.c, pigpio.c, pigpiod.c, pig2vcd.c, /
+/ and pigs.c /
+/ /
+****************************************************************************/
+
+#include <stdint.h>
+
+#define PIGPIO_VERSION 6
+
+/*-------------------------------------------------------------------------*/
+
+/*
+
+Function Usage
+-------- -----
+
+gpioInitialise Initialise library.
+gpioTerminate Terminate library.
+
+gpioSetMode Set a gpio mode.
+gpioGetMode Get a gpio mode.
+
+gpioSetPullUpDown Set/clear gpio pull up/down resistor.
+
+gpioRead Read a gpio.
+gpioWrite Write a gpio.
+
+gpioPWM Start/stop PWM pulses on a gpio.
+
+gpioSetPWMrange Configure PWM range for a gpio.
+gpioGetPWMrange Get configured PWM range for a gpio.
+gpioGetPWMrealRange Get underlying PWM range for a gpio.
+
+gpioSetPWMfrequency Configure PWM frequency for a gpio.
+gpioGetPWMfrequency Get configured PWM frequency for a gpio.
+
+gpioServo Start/stop servo pulses on a gpio.
+
+gpioSetAlertFunc Request a gpio change callback.
+gpioSetAlertFuncEx Request a gpio change callback, extended.
+
+gpioNotifyOpen Open a gpio(s) changed notification.
+gpioNotifyBegin Begin a gpio(s) changed notification.
+gpioNotifyPause Pause a gpio(s) changed notification.
+gpioNotifyClose Close a gpio(s) changed notification.
+
+gpioWaveClear Initialises a new waveform.
+gpioWaveAddGeneric Adds a series of pulses to the waveform.
+gpioWaveAddSerial Adds serial data to the waveform.
+
+gpioWaveTxStart Transmits the waveform.
+gpioWaveTxBusy Checks to see if the waveform has ended.
+gpioWaveTxStop Aborts the current waveform.
+
+gpioWaveSerialReadStart Reads serial data from a user gpio.
+gpioWaveSerialReadStop Stops reading serial data from a user gpio.
+
+gpioWaveGetMicros Length in microseconds of the current waveform.
+gpioWaveGetHighMicros Length of longest waveform so far.
+gpioWaveGetMaxMicros Absolute maximum allowed micros.
+
+gpioWaveGetPulses Length in pulses of the current waveform.
+gpioWaveGetHighPulses Length of longest waveform so far.
+gpioWaveGetMaxPulses Absolute maximum allowed pulses.
+
+gpioWaveGetCbs Length in cbs of the current waveform.
+gpioWaveGetHighCbs Length of longest waveform so far.
+gpioWaveGetMaxCbs Absolute maximum allowed cbs.
+
+gpioSetWatchdog Set a watchdog on a gpio.
+
+gpioSetGetSamplesFunc Requests a gpio samples callback.
+gpioSetGetSamplesFuncEx Requests a gpio samples callback, extended.
+
+gpioSetTimerFunc Request a regular timed callback.
+gpioSetTimerFuncEx Request a regular timed callback, extended.
+
+gpioSetSignalFunc Request a signal callback.
+gpioSetSignalFuncEx Request a signal callback, extended.
+
+gpioRead_Bits_0_31 Read gpios in bank 1.
+gpioRead_Bits_32_53 Read gpios in bank 2.
+
+gpioWrite_Bits_0_31_Clear Clear gpios in bank 1.
+gpioWrite_Bits_32_53_Clear Clear gpios in bank 2.
+
+gpioWrite_Bits_0_31_Set Set gpios in bank 1.
+gpioWrite_Bits_32_53_Set Set gpios in bank 2.
+
+gpioTime Get current time.
+
+gpioSleep Sleep for specified time.
+gpioDelay Delay for microseconds.
+
+gpioTick Get current tick (microseconds).
+
+gpioHardwareRevision Get hardware version.
+
+gpioCfgBufferSize Configure the gpio sample buffer size.
+gpioCfgClock Configure the gpio sample rate.
+gpioCfgDMAchannel Configure the DMA channel.
+gpioCfgInterfaces Configure user interfaces.
+gpioCfgSocketPort Configure socket port.
+
+*/
+
+/*-------------------------------------------------------------------------*/
+
+
+#define PI_INPFIFO "/dev/pigpio"
+#define PI_OUTFIFO "/dev/pigout"
+#define PI_ERRFIFO "/dev/pigerr"
+
+#define PI_ENVPORT "PIGPIO_PORT"
+#define PI_ENVADDR "PIGPIO_ADDR"
+
+#define PI_LOCKFILE "/var/run/pigpio.pid"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct
+{
+ uint32_t cmd;
+ uint32_t p1;
+ uint32_t p2;
+ uint32_t res;
+} cmdCmd_t;
+
+typedef struct
+{
+ uint32_t tick;
+ uint32_t level;
+} gpioSample_t;
+
+typedef struct
+{
+ uint16_t seqno;
+ uint16_t flags;
+ uint32_t tick;
+ uint32_t level;
+} gpioReport_t;
+
+typedef struct
+{
+ uint32_t gpioOn;
+ uint32_t gpioOff;
+ uint32_t usDelay;
+} gpioPulse_t;
+
+typedef struct
+{
+ char * buf;
+ uint32_t bufSize;
+ int readPos;
+ int writePos;
+} gpioRx_t;
+
+typedef void (*gpioAlertFunc_t) (int gpio,
+ int level,
+ uint32_t tick);
+
+typedef void (*gpioAlertFuncEx_t) (int gpio,
+ int level,
+ uint32_t tick,
+ void * userdata);
+
+typedef void (*gpioTimerFunc_t) (void);
+
+typedef void (*gpioTimerFuncEx_t) (void * userdata);
+
+typedef void (*gpioSignalFunc_t) (int signum);
+
+typedef void (*gpioSignalFuncEx_t) (int signum,
+ void * userdata);
+
+typedef void (*gpioGetSamplesFunc_t) (const gpioSample_t * samples,
+ int numSamples);
+
+typedef void (*gpioGetSamplesFuncEx_t) (const gpioSample_t * samples,
+ int numSamples,
+ void * userdata);
+
+/*
+ All the functions which return an int return < 0 on error.
+
+ If the library isn't initialised all but the gpioCfg* functions
+ will return error PI_NOT_INITIALISED.
+
+ If the library is initialised the gpioCfg* functions will
+ return error PI_INITIALISED.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioInitialise(void);
+/*-------------------------------------------------------------------------*/
+/* Initialises the library.
+
+ Call before using the other library functions.
+
+ Returns the pigpio version number if OK, otherwise PI_INIT_FAILED.
+
+ NOTES:
+
+ The only exception is the optional gpioCfg* functions, see later.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+void gpioTerminate(void);
+/*-------------------------------------------------------------------------*/
+/* Terminates the library.
+
+ Returns nothing.
+
+ Call before program exit.
+
+ NOTES:
+
+ This function resets the DMA and PWM peripherals, releases memory, and
+ terminates any running threads.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioSetMode(unsigned gpio,
+ unsigned mode);
+/*-------------------------------------------------------------------------*/
+/* Sets the gpio mode, typically input or output.
+
+ Returns 0 if OK, otherwise PI_BAD_GPIO or PI_BAD_MODE.
+
+ Arduino style: pinMode.
+
+ EXAMPLE:
+ ...
+ gpioSetMode(17, PI_INPUT); // set gpio17 as input
+ gpioSetMode(18, PI_OUTPUT); // set gpio18 as output
+ gpioSetMode(22,PI_ALT0); // set gpio22 to alternative mode 0
+ ...
+*/
+
+/* gpio: 0-53 */
+
+#define PI_MIN_GPIO 0
+#define PI_MAX_GPIO 53
+
+/* mode: 0-7 */
+
+#define PI_INPUT 0
+#define PI_OUTPUT 1
+#define PI_ALT0 4
+#define PI_ALT1 5
+#define PI_ALT2 6
+#define PI_ALT3 7
+#define PI_ALT4 3
+#define PI_ALT5 2
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioGetMode(unsigned gpio);
+/*-------------------------------------------------------------------------*/
+/* Gets the gpio mode.
+
+ Returns the gpio mode if OK, otherwise PI_BAD_GPIO.
+
+ EXAMPLE:
+ ...
+ if (gpioGetMode(17) != PI_ALT0)
+ {
+ gpioSetMode(17, PI_ALT0); // set gpio17 to ALT0
+ }
+ ...
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioSetPullUpDown(unsigned gpio,
+ unsigned pud);
+/*-------------------------------------------------------------------------*/
+/* Sets or clears resistor pull ups or downs on the gpio.
+
+ Returns 0 if OK, otherwise PI_BAD_GPIO or PI_BAD_PUD.
+
+ EXAMPLE:
+ ...
+ gpioSetPullUpDown(17, PI_PUD_UP); // sets a pull-up on gpio17
+ gpioSetPullUpDown(18, PI_PUD_DOWN); // sets a pull-down on gpio18
+ gpioSetPullUpDown(23, PI_PUD_OFF); // clear pull-ups/downs on gpio23
+ ...
+*/
+
+/* pud: 0-2 */
+
+#define PI_PUD_OFF 0
+#define PI_PUD_DOWN 1
+#define PI_PUD_UP 2
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioRead (unsigned gpio);
+/*-------------------------------------------------------------------------*/
+/* Reads the gpio level, on or off.
+
+ Returns the gpio level if OK, otherwise PI_BAD_GPIO.
+
+ EXAMPLE:
+ ...
+ printf("gpio24 is level %d\n", gpioRead(24));
+ ...
+
+ NOTES:
+
+ Arduino style: digitalRead.
+*/
+
+/* level: 0-1 */
+
+#define PI_OFF 0
+#define PI_ON 1
+
+#define PI_CLEAR 0
+#define PI_SET 1
+
+#define PI_LOW 0
+#define PI_HIGH 1
+
+/* level: only reported for gpio timeout, see gpioSetWatchdogTimeout */
+
+#define PI_TIMEOUT 2
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioWrite(unsigned gpio,
+ unsigned level);
+/*-------------------------------------------------------------------------*/
+/* Sets the gpio level, on or off.
+
+ Returns 0 if OK, otherwise PI_BAD_GPIO or PI_BAD_LEVEL.
+
+ EXAMPLE:
+ ...
+ gpioWrite(24, 1); // sets gpio24 high
+ ...
+
+ NOTES:
+
+ If PWM or servo pulses are active on the gpio they are switched off.
+
+ Arduino style: digitalWrite
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioPWM(unsigned user_gpio,
+ unsigned dutycycle);
+/*-------------------------------------------------------------------------*/
+/* Starts PWM on the gpio, dutycycle between 0 (off) and range (fully on).
+ Range defaults to 255.
+
+ Returns 0 if OK, otherwise PI_BAD_USER_GPIO or PI_BAD_DUTYCYCLE.
+
+ EXAMPLE:
+ ...
+ gpioPWM(17, 255); // sets gpio17 full on
+ gpioPWM(18, 128); // sets gpio18 half on
+ gpioPWM(23, 0); // sets gpio23 full off
+ ...
+
+ NOTES:
+
+ Arduino style: analogWrite
+
+ This and the servo functionality use the DMA and PWM or PCM peripherals
+ to control and schedule the pulse lengths and duty cycles.
+
+ The gpioSetPWMrange funtion can change the default range of 255.
+*/
+
+/* user_gpio: 0-31 */
+
+#define PI_MAX_USER_GPIO 31
+
+/* dutycycle: 0-range */
+
+#define PI_DEFAULT_DUTYCYCLE_RANGE 255
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioSetPWMrange(unsigned user_gpio,
+ unsigned range);
+/*-------------------------------------------------------------------------*/
+/* Selects the dutycycle range to be used for the gpio. Subsequent calls
+ to gpioPWM will use a dutycycle between 0 (off) and range (fully on).
+
+ Returns the real range for the given gpio's frequency if OK,
+ otherwise PI_BAD_USER_GPIO or PI_BAD_DUTY_RANGE.
+
+ EXAMPLE:
+ ...
+ gpioSetPWMrange(24, 2000); // now 2000 is fully on, 1000 is half on etc.
+ ...
+
+ NOTES:
+
+ If PWM is currently active on the gpio its dutycycle will be scaled
+ to reflect the new range.
+
+ The real range, the number of steps between fully off and fully
+ on for each frequency, is given in the following table.
+
+ 25, 50, 100, 125, 200, 250, 400, 500, 625,
+ 800, 1000, 1250, 2000, 2500, 4000, 5000, 10000, 20000
+
+ The real value set by gpioPWM is
+
+ (dutycycle * real range) / range.
+*/
+
+/* range: 25-40000 */
+
+#define PI_MIN_DUTYCYCLE_RANGE 25
+#define PI_MAX_DUTYCYCLE_RANGE 40000
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioGetPWMrange(unsigned user_gpio);
+/*-------------------------------------------------------------------------*/
+/* Returns the dutycycle range used for the gpio if OK, otherwise
+ PI_BAD_USER_GPIO.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioGetPWMrealRange(unsigned user_gpio);
+/*-------------------------------------------------------------------------*/
+/* Returns the real range used for the gpio if OK, otherwise
+ PI_BAD_USER_GPIO.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioSetPWMfrequency(unsigned user_gpio,
+ unsigned frequency);
+/*-------------------------------------------------------------------------*/
+/* Sets the frequency in hertz to be used for the gpio.
+
+ Returns the numerically closest frequency if OK, otherwise
+ PI_BAD_USER_GPIO.
+
+ The selectable frequencies depend upon the sample rate which
+ may be 1, 2, 4, 5, 8, or 10 microseconds (default 5).
+
+ Each gpio can be independently set to one of 18 different PWM
+ frequencies.
+
+ If PWM is currently active on the gpio it will be
+ switched off and then back on at the new frequency.
+
+ NOTES:
+
+ The frequencies for each sample rate are:
+
+ Hertz
+
+ 1: 40000 20000 10000 8000 5000 4000 2500 2000 1600
+ 1250 1000 800 500 400 250 200 100 50
+
+ 2: 20000 10000 5000 4000 2500 2000 1250 1000 800
+ 625 500 400 250 200 125 100 50 25
+
+ 4: 10000 5000 2500 2000 1250 1000 625 500 400
+ 313 250 200 125 100 63 50 25 13
+sample
+ rate
+ (us) 5: 8000 4000 2000 1600 1000 800 500 400 320
+ 250 200 160 100 80 50 40 20 10
+
+ 8: 5000 2500 1250 1000 625 500 313 250 200
+ 156 125 100 63 50 31 25 13 6
+
+ 10: 4000 2000 1000 800 500 400 250 200 160
+ 125 100 80 50 40 25 20 10 5
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioGetPWMfrequency(unsigned user_gpio);
+/*-------------------------------------------------------------------------*/
+/* Returns the frequency (in hertz) used for the gpio if OK, otherwise
+ PI_BAD_USER_GPIO.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioServo(unsigned user_gpio,
+ unsigned pulsewidth);
+/*-------------------------------------------------------------------------*/
+/* Starts servo pulses on the gpio, 0 (off), 500 (most anti-clockwise) to
+ 2500 (most clockwise).
+
+ Returns 0 if OK, otherwise PI_BAD_USER_GPIO or PI_BAD_PULSEWIDTH.
+
+ NOTES:
+
+ The range supported by servos varies and should probably be determined
+ by experiment. A value of 1500 should always be safe and represents
+ the mid-point of rotation. You can DAMAGE a servo if you command it
+ to move beyond its limits.
+
+ EXAMPLE:
+
+ ...
+ gpioServo(17, 1500);
+ ...
+
+ This example causes an on pulse of 1500 microseconds duration to be
+ transmitted on gpio 17 at a rate of 50 times per second.
+
+ This will command a servo connected to gpio 17 to rotate to
+ its mid-point.
+
+ OTHER UPDATE RATES:
+
+ This function updates servos at 50Hz. If you wish to use a different
+ update frequency you will have to use the PWM functions.
+
+ PWM Hz 50 100 200 400 500
+ 1E6/Hz 20000 10000 5000 2500 2000
+
+ Firstly set the desired PWM frequency using gpioSetPWMfrequency.
+
+ Then set the PWM range using gpioSetPWMrange to 1E6/frequency.
+ Doing this allows you to use units of microseconds when setting
+ the servo pulse width.
+
+ E.g. If you want to update a servo connected to gpio 25 at 400Hz
+
+ gpioSetPWMfrequency(25, 400);
+ gpioSetPWMrange(25, 2500);
+
+ Thereafter use the PWM command to move the servo,
+ e.g. gpioPWM(25, 1500) will set a 1500 us pulse.
+
+*/
+
+/* pulsewidth: 0, 500-2500 */
+
+#define PI_SERVO_OFF 0
+#define PI_MIN_SERVO_PULSEWIDTH 500
+#define PI_MAX_SERVO_PULSEWIDTH 2500
+
+
+/*-------------------------------------------------------------------------*/
+int gpioSetAlertFunc(unsigned user_gpio,
+ gpioAlertFunc_t f);
+/*-------------------------------------------------------------------------*/
+/* Registers a function to be called (a callback) when the specified
+ gpio changes state.
+
+ Returns 0 if OK, otherwise PI_BAD_USER_GPIO.
+
+ One function may be registered per gpio.
+
+ The function is passed the gpio, the new level, and the tick.
+
+ The alert may be cancelled by passing NULL as the function.
+
+ EXAMPLE:
+ ...
+ void aFunction(int gpio, int level, uint32_t tick)
+ {
+ printf("gpio %d became %d at %d\n", gpio, level, tick);
+ }
+ ...
+ gpioSetAlertFunc(4, aFunction);
+ ...
+
+ This example causes aFunction to be called whenever
+ gpio 4 changes state.
+
+ NOTES:
+
+ The gpios are sampled at a rate set when the library is started.
+
+ If a value isn't specifically set the default of 5 us is used.
+
+ The number of samples per second is given in the following table.
+
+ samples
+ per sec
+
+ 1 1,000,000
+ 2 500,000
+ sample 4 250,000
+ rate 5 200,000
+ (us) 8 125,000
+ 10 100,000
+
+ Level changes of length less than the sample rate may be missed.
+
+ The thread which calls the alert functions is triggered nominally
+ 1000 times per second. The active alert functions will be called
+ once per level change since the last time the thread was activated.
+ i.e. The active alert functions will get all level changes but there
+ will be a latency.
+
+ The tick value is the time stamp of the sample in microseconds, see
+ gpioTick for more details.
+*/
+
+
+/*-------------------------------------------------------------------------*/
+int gpioSetAlertFuncEx(unsigned user_gpio,
+ gpioAlertFuncEx_t f,
+ void * userdata);
+/*-------------------------------------------------------------------------*/
+/* Registers a function to be called (a callback) when the specified
+ gpio changes state.
+
+ Returns 0 if OK, otherwise PI_BAD_USER_GPIO.
+
+ One function may be registered per gpio.
+
+ The function is passed the gpio, the new level, the tick, and
+ the userdata pointer.
+
+ Only one of gpioSetAlertFunc or gpioSetAlertFuncEx can be
+ registered per gpio.
+
+ See gpioSetAlertFunc for further details.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioNotifyOpen(void);
+/*-------------------------------------------------------------------------*/
+/* This function requests a free notification handle.
+
+ Returns a handle greater than or equal to zero if OK,
+ otherwise PI_NO_HANDLE.
+
+ A notification is a method for being notified of gpio state changes
+ via a pipe or socket.
+
+ Pipe notifications for handle x will be available at the pipe
+ named /dev/pigpiox (where x is the handle number). E.g. if the
+ function returns 15 then the notifications must be read
+ from /dev/pigpio15.
+
+ Socket notifications are returned to the socket which requested the
+ handle.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioNotifyBegin(unsigned handle,
+ uint32_t bits);
+/*-------------------------------------------------------------------------*/
+/* This function starts notifications on a previously opened handle.
+
+ Returns 0 if OK, otherwise PI_BAD_HANDLE.
+
+ The notification sends state changes for each gpio whose corresponding
+ bit in bits is set.
+
+ EXAMPLE:
+
+ gpioNotifyBegin(0, 1234) will start notifications for gpios 1, 4, 6,
+ 7, 10 (1234 = 0x04D2 = 0b0000010011010010).
+
+ NOTES:
+
+ Each notification occupies 12 bytes in the fifo and has the
+ following structure.
+
+ typedef struct
+ {
+ uint16_t seqno;
+ uint16_t flags;
+ uint32_t tick;
+ uint32_t level;
+ } gpioReport_t;
+
+ seqno starts at 0 each time the handle is opened and then increments
+ by one for each report.
+
+ flags, if bit 5 is set then bits 0-4 of the flags indicate a gpio
+ which has had a watchdog timeout.
+
+ tick is the number of microseconds since system boot.
+
+ level indicates the level of each gpio.
+*/
+
+#define PI_NTFY_FLAGS_WDOG (1 <<5)
+#define PI_NTFY_FLAGS_BIT(x) (((x)<<0)&31)
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioNotifyPause(unsigned handle);
+/*-------------------------------------------------------------------------*/
+/* This function pauses notifications on a previously opened handle.
+
+ Returns 0 if OK, otherwise PI_BAD_HANDLE.
+
+ Notifications for the handle are suspended until gpioNotifyBegin
+ is called again.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioNotifyClose(unsigned handle);
+/*-------------------------------------------------------------------------*/
+/* This function stops notifications on a previously opened handle
+ and releases the handle for reuse.
+
+ Returns 0 if OK, otherwise PI_BAD_HANDLE.
+*/
+
+
+/*-------------------------------------------------------------------------*/
+int gpioWaveClear(void);
+/*-------------------------------------------------------------------------*/
+/* This function initialises a new waveform.
+
+ Returns 0 if OK.
+
+ A waveform comprises one of more pulses. Each pulse consists of a
+ gpioPulse_t structure.
+
+ typedef struct
+ {
+ uint32_t gpioOn;
+ uint32_t gpioOff;
+ uint32_t usDelay;
+ } gpioPulse_t;
+
+ The fields specify
+
+ 1) the gpios to be switched on at the start of the pulse.
+ 2) the gpios to be switched off at the end of the pulse.
+ 3) the delay in microseconds before the next pulse.
+
+ Any or all the fields can be zero. It doesn't make any sense to
+ set all the fields to zero (the pulse will be ignored).
+
+ When a waveform is started each pulse is executed in order with the
+ specified delay between the pulse and the next.
+*/
+
+
+/*-------------------------------------------------------------------------*/
+int gpioWaveAddGeneric(unsigned numPulses, gpioPulse_t * pulses);
+/*-------------------------------------------------------------------------*/
+/* This function adds a number of pulses to the current waveform.
+
+ Returns the new total number of pulses in the current waveform if OK,
+ otherwise PI_TOO_MANY_PULSES.
+
+ NOTES:
+
+ The pulses are interleaved in time order within the existing waveform
+ (if any).
+
+ Merging allows the waveform to be built in parts, that is the settings
+ for gpio#1 can be added, and then gpio#2 etc.
+
+ If the added waveform is intended to start after or within the existing
+ waveform then the first pulse should consist of a delay.
+
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioWaveAddSerial(unsigned user_gpio,
+ unsigned baud,
+ unsigned numChar,
+ char * str);
+/*-------------------------------------------------------------------------*/
+/* This function adds a waveform representing serial data to the
+ existing waveform (if any).
+
+ Returns the new total number of pulses in the current waveform if OK,
+ otherwise PI_BAD_USER_GPIO, PI_BAD_WAVE_BAUD, PI_TOO_MANY_CHARS, or
+ PI_TOO_MANY_PULSES.
+
+ NOTES:
+
+ The serial data is formatted as one start bit, eight data bits, and one
+ stop bit.
+
+ It is legal to add serial data streams with different baud rates to
+ the same waveform.
+*/
+
+#define PI_WAVE_MIN_BAUD 100
+#define PI_WAVE_MAX_BAUD 250000
+#define PI_WAVE_MAX_CHARS 256
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioWaveTxStart(unsigned mode);
+/*-------------------------------------------------------------------------*/
+/* This function transmits the current waveform. The mode determines
+ whether the waveform is sent once or cycles endlessly.
+
+ Returns 0 if OK, otherwise PI_BAD_WAVE_MODE.
+*/
+
+#define PI_WAVE_MODE_ONE_SHOT 0
+#define PI_WAVE_MODE_REPEAT 1
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioWaveTxBusy(void);
+/*-------------------------------------------------------------------------*/
+/* This function checks to see if a waveform is currently being
+ transmitted.
+
+ Returns 1 if a waveform is currently being transmitted, otherwise 0.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioWaveTxStop(void);
+/*-------------------------------------------------------------------------*/
+/* This function aborts the transmission of the current waveform.
+
+ Returns 0 if OK.
+
+ NOTES:
+
+ This function is intended to stop a waveform started with the repeat mode.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioWaveSerialReadStart(unsigned user_gpio,
+ unsigned baud,
+ gpioRx_t * rxp);
+/*-------------------------------------------------------------------------*/
+/* This function starts the reception of serial data with the
+ specified baud rate on a gpio.
+
+ Returns 0 if OK, otherwise PI_BAD_USER_GPIO, PI_BAD_WAVE_BAUD,
+ PI_BAD_SERIAL_STRUC, or PI_BAD_SERIAL_BUF.
+
+ NOTES:
+
+ typedef struct
+ {
+ char * buf;
+ uint32_t bufSize;
+ int readPos;
+ int writePos;
+ } gpioRx_t;
+
+ The serial data is returned in a cyclic buffer which MUST be allocated
+ by the caller. The caller specifies the location and size of the
+ buffer in buf and bufSize.
+
+ It is the caller's responsibility to read data from the cyclic buffer
+ in a timely fashion. Data is available when readPos is not equal to
+ writePos.
+
+ EXAMPLE:
+
+ #define BUFSIZE 1000
+
+ char buf[BUFSIZE];
+ int bytes, wpos;
+ FILE * outFile;
+
+ gpioRx_t rx;
+
+ ...
+
+ rx.buf = buf;
+ rx.bufSize = sizeof(buf);
+
+ if (gpioWaveSerialReadStart(4, 38400, &rx) == 0)
+ {
+ ...
+
+ while (rx.readPos != rx.writePos)
+ {
+ wpos = rx.writePos;
+
+ if (wpos > rx.readPos) bytes = wpos - rx.readPos;
+ else bytes = rx.bufSize - rx.readPos;
+
+ fwrite(rx.buf+rx.readPos, 1, bytes, outFile);
+
+ rx.readPos += bytes;
+
+ if (rx.readPos >= rx.bufSize) rx.readPos = 0;
+ }
+ ...
+ }
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioWaveSerialReadStop(unsigned user_gpio);
+/*-------------------------------------------------------------------------*/
+/* This function stops reading serial data from a gpio.
+
+ Returns 0 if OK, otherwise PI_BAD_USER_GPIO, or PI_NOT_SERIAL_GPIO.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioWaveGetMicros(void);
+/*-------------------------------------------------------------------------*/
+/* This function returns the length in microseconds of the current
+ waveform.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioWaveGetHighMicros(void);
+/*-------------------------------------------------------------------------*/
+/* This function returns the length in microseconds of the longest waveform
+ created since gpioInitialise was called.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioWaveGetMaxMicros(void);
+/*-------------------------------------------------------------------------*/
+/* This function returns the maximum possible size of a waveform in
+ microseconds.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioWaveGetPulses(void);
+/*-------------------------------------------------------------------------*/
+/* This function returns the length in pulses of the current waveform.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioWaveGetHighPulses(void);
+/*-------------------------------------------------------------------------*/
+/* This function returns the length in pulses of the longest waveform
+ created since gpioInitialise was called.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioWaveGetMaxPulses(void);
+/*-------------------------------------------------------------------------*/
+/* This function returns the maximum possible size of a waveform in pulses.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioWaveGetCbs(void);
+/*-------------------------------------------------------------------------*/
+/* This function returns the length in DMA control blocks of the current
+ waveform.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioWaveGetHighCbs(void);
+/*-------------------------------------------------------------------------*/
+/* This function returns the length in DMA control blocks of the longest
+ waveform created since gpioInitialise was called.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioWaveGetMaxCbs(void);
+/*-------------------------------------------------------------------------*/
+/* This function returns the maximum possible size of a waveform in DMA
+ control blocks.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioSetWatchdog(unsigned user_gpio,
+ unsigned timeout);
+/*-------------------------------------------------------------------------*/
+/* Sets a watchdog for a gpio.
+
+ Returns 0 if OK, otherwise PI_BAD_USER_GPIO or PI_BAD_WDOG_TIMEOUT.
+
+ The watchdog is nominally in milliseconds.
+
+ One watchdog may be registered per gpio.
+
+ The watchdog may be cancelled by setting timeout to 0.
+
+ If no level change has been detected for the gpio for timeout
+ milliseconds:-
+
+ 1) any registered alert function for the gpio is called with
+ the level set to PI_TIMEOUT.
+ 2) any notification for the gpio has a report written to the
+ fifo with the flags set to indicate a watchdog timeout.
+
+ EXAMPLE:
+
+ void aFunction(int gpio, int level, uint32_t tick)
+ {
+ printf("gpio %d became %d at %d\n", gpio, level, tick);
+ }
+ ...
+ gpioSetAlertFunc(4, aFunction);
+ gpioSetWatchdogTimeout(4, 5);
+ ...
+
+ This example causes aFunction to be called whenever
+ gpio 4 changes state or approximately every 5 ms.
+*/
+
+/* timeout: 0-60000 */
+
+#define PI_MIN_WDOG_TIMEOUT 0
+#define PI_MAX_WDOG_TIMEOUT 60000
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioSetGetSamplesFunc(gpioGetSamplesFunc_t f,
+ uint32_t bits);
+/*-------------------------------------------------------------------------*/
+/* Registers a function to be called (a callback) every millisecond
+ with the latest gpio samples.
+
+ Returns 0 if OK.
+
+ The function is passed a pointer to the samples and the number
+ of samples.
+
+ Only one function can be registered.
+
+ The callback may be cancelled by passing NULL as the function.
+
+ NOTES:
+
+ The samples returned will be the union of bits, plus any active alerts,
+ plus any active notifications.
+
+ e.g. if there are alerts for gpios 7, 8, and 9, notifications for gpios
+ 8, 10, 23, 24, and bits is (1<<23)|(1<<17) then samples for gpios
+ 7, 8, 9, 10, 17, 23, and 24 will be reported.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioSetGetSamplesFuncEx(gpioGetSamplesFuncEx_t f,
+ uint32_t bits,
+ void * userdata);
+/*-------------------------------------------------------------------------*/
+/* Registers a function to be called (a callback) every millisecond
+ with the latest gpio samples.
+
+ Returns 0 if OK.
+
+ The function is passed a pointer to the samples, the number
+ of samples, and the userdata pointer.
+
+ Only one of gpioGetSamplesFunc or gpioGetSamplesFuncEx can be
+ registered.
+
+ See gpioSetGetSamplesFunc for further details.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioSetTimerFunc(unsigned timer,
+ unsigned ms,
+ gpioTimerFunc_t f);
+/*-------------------------------------------------------------------------*/
+/* Registers a function to be called (a callback) every ms milliseconds.
+
+ Returns 0 if OK, otherwise PI_BAD_TIMER, PI_BAD_MS, or PI_TIMER_FAILED.
+
+ 10 timers are supported numbered 0 to 9.
+
+ One function may be registered per timer.
+
+ The timer may be cancelled by passing NULL as the function.
+
+ EXAMPLE:
+
+ ...
+ void bFunction(void)
+ {
+ printf("two seconds have elapsed\n");
+ }
+ ...
+ gpioSetTimerFunc(0, 2000, bFunction);
+ ...
+
+ This example causes bFunction to be called every 2000 milliseconds.
+*/
+
+/* timer: 0-9 */
+
+#define PI_MIN_TIMER 0
+#define PI_MAX_TIMER 9
+
+/* ms: 10-60000 */
+
+#define PI_MIN_MS 10
+#define PI_MAX_MS 60000
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioSetTimerFuncEx(unsigned timer,
+ unsigned ms,
+ gpioTimerFuncEx_t f,
+ void * userdata);
+/*-------------------------------------------------------------------------*/
+/* Registers a function to be called (a callback) every ms milliseconds.
+
+ Returns 0 if OK, otherwise PI_BAD_TIMER, PI_BAD_MS, or PI_TIMER_FAILED.
+
+ The function is passed the userdata pointer.
+
+ Only one of gpioSetTimerFunc or gpioSetTimerFuncEx can be
+ registered per timer.
+
+ See gpioSetTimerFunc for further details.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioSetSignalFunc(unsigned signum,
+ gpioSignalFunc_t f);
+/*-------------------------------------------------------------------------*/
+/* Registers a function to be called (a callback) when a signal occurs.
+
+ Returns 0 if OK, otherwise PI_BAD_SIGNUM.
+
+ The function is passed the signal number.
+
+ One function may be registered per signal.
+
+ The callback may be cancelled by passing NULL.
+
+ NOTES:
+
+ By default all signals are treated as fatal and cause the library
+ to call gpioTerminate and then exit.
+*/
+
+/* signum: 0-63 */
+
+#define PI_MIN_SIGNUM 0
+#define PI_MAX_SIGNUM 63
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioSetSignalFuncEx(unsigned signum,
+ gpioSignalFuncEx_t f,
+ void * userdata);
+/*-------------------------------------------------------------------------*/
+/* Registers a function to be called (a callback) when a signal occurs.
+
+ Returns 0 if OK, otherwise PI_BAD_SIGNUM.
+
+ The function is passed the signal number and the userdata pointer.
+
+ Only one of gpioSetSignalFunc or gpioSetSignalFuncEx can be
+ registered per signal.
+
+ See gpioSetSignalFunc for further details.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+uint32_t gpioRead_Bits_0_31(void);
+/*-------------------------------------------------------------------------*/
+/* Returns the current level of gpios 0-31.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+uint32_t gpioRead_Bits_32_53(void);
+/*-------------------------------------------------------------------------*/
+/* Returns the current level of gpios 32-53.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioWrite_Bits_0_31_Clear(uint32_t levels);
+/*-------------------------------------------------------------------------*/
+/* Clears gpios 0-31 if the corresponding bit in levels is set.
+
+ Returns 0 if OK.
+
+ EXAMPLE:
+
+ To clear (set to 0) gpios 4, 7, and 15.
+
+ ...
+ gpioWrite_Bits_0_31_Clear( (1<<4) | (1<<7) | (1<<15) );
+ ...
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioWrite_Bits_32_53_Clear(uint32_t levels);
+/*-------------------------------------------------------------------------*/
+/* Clears gpios 32-53 if the corresponding bit (0-21) in levels is set.
+
+ Returns 0 if OK.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioWrite_Bits_0_31_Set(uint32_t levels);
+/*-------------------------------------------------------------------------*/
+/* Sets gpios 0-31 if the corresponding bit in levels is set.
+
+ Returns 0 if OK.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioWrite_Bits_32_53_Set(uint32_t levels);
+/*-------------------------------------------------------------------------*/
+/* Sets gpios 32-53 if the corresponding bit (0-21) in levels is set.
+
+ Returns 0 if OK.
+
+ EXAMPLE:
+
+ To set (set to 1) gpios 32, 40, and 53.
+
+ ...
+ gpioWrite_Bits_32_53_Set( (1<<(32-32)) | (1<<(40-32)) | (1<<(53-32)) );
+ ...
+*/
+
+/*-------------------------------------------------------------------------*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioTime(unsigned timetype,
+ int * seconds,
+ int * micros);
+/*-------------------------------------------------------------------------*/
+/* Updates the seconds and micros variables with the current time.
+
+ Returns 0 if OK, otherwise PI_BAD_TIMETYPE.
+
+ If timetype is PI_TIME_ABSOLUTE updates seconds and micros with the
+ number of seconds and microseconds since the epoch (1st January 1970).
+
+ If timetype is PI_TIME_RELATIVE updates seconds and micros with the
+ number of seconds and microseconds since the library was initialised.
+
+ EXAMPLE:
+
+ ...
+ int secs, mics;
+ ...
+ gpioTime(PI_TIME_RELATIVE, &secs, &mics);
+ printf("library started %d.%03d seconds ago\n", secs, mics/1000);
+ ...
+ prints the number of seconds since the library was started.
+*/
+
+/* timetype: 0-1 */
+
+#define PI_TIME_RELATIVE 0
+#define PI_TIME_ABSOLUTE 1
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioSleep(unsigned timetype,
+ int seconds,
+ int micros);
+/*-------------------------------------------------------------------------*/
+/* Sleeps for the number of seconds and microseconds specified by seconds
+ and micros.
+
+ Returns 0 if OK, otherwise PI_BAD_TIMETYPE, PI_BAD_SECONDS,
+ or PI_BAD_MICROS.
+
+ If timetype is PI_TIME_ABSOLUTE the sleep ends when the number of seconds
+ and microseconds since the epoch (1st January 1970) has elapsed. System
+ clock changes are taken into account.
+
+ If timetype is PI_TIME_RELATIVE the sleep is for the specified number
+ of seconds and microseconds. System clock changes do not effect the
+ sleep length.
+
+ NOTES:
+
+ For short delays (say, 250 microseonds or less) use gpioDelayMicroseconds.
+
+ EXAMPLE:
+
+ ...
+ gpioSleep(PI_TIME_RELATIVE, 2, 500000); // sleep for 2.5 seconds
+ ...
+ gpioSleep(PI_TIME_RELATIVE, 0, 100000); // sleep for 1/10th of a second
+ ...
+ gpioSleep(PI_TIME_RELATIVE, 60, 0); // sleep for one minute
+ ...
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+uint32_t gpioDelay(uint32_t micros);
+/*-------------------------------------------------------------------------*/
+/* Delays for at least the number of microseconds specified by micros.
+
+ Returns the actual length of the delay in microseconds.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+uint32_t gpioTick(void);
+/*-------------------------------------------------------------------------*/
+/* Returns the current system tick.
+
+ Tick is the number of microseconds since system boot.
+
+ NOTES:
+
+ As tick is an unsigned 32 bit quantity it wraps around after
+ 2^32 microseconds, which is approximately 1 hour 12 minutes.
+
+ You don't need to worry about the wrap around as long as you
+ take a tick (uint32_t) from another tick, i.e. the following
+ code will always provide the correct difference.
+
+ EXAMPLE:
+
+ uint32_t startTick, endTick;
+ int diffTick;
+ ...
+ startTick = gpioTick();
+ ...
+ // do some processing
+ ...
+ endTick = gpioTick();
+
+ diffTick = endTick - startTick;
+
+ printf("some processing took %d microseconds\n", diffTick);
+ ...
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+unsigned gpioHardwareRevision(void);
+/*-------------------------------------------------------------------------*/
+/* Returns the hardware revision.
+
+ If the hardware revision can not be found or is not a valid hexadecimal
+ number the function returns 0.
+
+ NOTES:
+
+ The hardware revision is the last 4 characters on the Revision line of
+ /proc/cpuinfo.
+
+ The revision number can be used to determine the assignment of gpios
+ to pins.
+
+ There are at least two types of board.
+
+ Type 1 has gpio 0 on P1-3, gpio 1 on P1-5, and gpio 21 on P1-13.
+
+ Type 2 has gpio 2 on P1-3, gpio 3 on P1-5, gpio 27 on P1-13, and
+ gpios 28-31 on P5.
+
+ Type 1 boards have hardware revision numbers of 2 and 3.
+
+ Type 2 boards have hardware revision numbers of 4, 5, 6, and 15.
+
+ EXAMPLES:
+
+ for "Revision : 0002" the function returns 2.
+ for "Revision : 000f" the function returns 15.
+ for "Revision : 000g" the function returns 0.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioCfgBufferSize(unsigned millis);
+/*-------------------------------------------------------------------------*/
+/* Configures pigpio to buffer millis milliseconds of gpio samples.
+
+ The default setting is 120 milliseconds.
+
+ NOTES:
+
+ The intention is to allow for bursts of data and protection against
+ other processes hogging cpu time.
+
+ I haven't seen a process locked out for more than 100 milliseconds.
+
+ Making the buffer bigger uses a LOT of memory at the more frequent
+ sampling rates as shown in the following table in MBs.
+
+ buffer milliseconds
+ 120 250 500 1sec 2sec 4sec 8sec
+
+ 1 16 31 55 107 --- --- ---
+ 2 10 18 31 55 107 --- ---
+sample 4 8 12 18 31 55 107 ---
+ rate 5 8 10 14 24 45 87 ---
+ (us) 8 6 8 12 18 31 55 107
+ 10 6 8 10 14 24 45 87
+*/
+
+/* millis */
+
+#define PI_BUF_MILLIS_MIN 100
+#define PI_BUF_MILLIS_MAX 10000
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioCfgClock(unsigned micros,
+ unsigned peripheral,
+ unsigned source);
+/*-------------------------------------------------------------------------*/
+/* Configures pigpio to use a sample rate of micros microseconds,
+ permitted values are 1, 2, 4, 5, 8 and 10.
+
+ The timings are provided by the specified peripheral (PWM or PCM)
+ using the frequency source (OSC or PLLD).
+
+ The default setting is 5 microseconds using the PCM peripheral
+ with the PLLD source.
+
+ NOTES:
+
+ The approximate CPU percentage used for each sample rate is:
+
+ sample cpu
+ rate %
+
+ 1 25
+ 2 16
+ 4 11
+ 5 10
+ 8 15
+ 10 14
+
+ A sample rate of 5 microseconds seeems to be the sweet spot.
+
+ These readings were done by checking the resources used by
+ the demolib program (which is reasonably busy).
+*/
+
+/* micros: 1, 2, 4, 5, 8, or 10 */
+
+/* peripheral: 0-1 */
+
+#define PI_CLOCK_PWM 0
+#define PI_CLOCK_PCM 1
+
+/* source: 0-1 */
+
+#define PI_CLOCK_OSC 0
+#define PI_CLOCK_PLLD 1
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioCfgDMAchannel(unsigned channel);
+/*-------------------------------------------------------------------------*/
+/* Configures pigpio to use the specified DMA channel.
+
+ The default setting is to use channel 14.
+*/
+
+/* channel: 0-14 */
+
+#define PI_MIN_DMA_CHANNEL 0
+#define PI_MAX_DMA_CHANNEL 14
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioCfgDMAchannels(unsigned primaryChannel,
+ unsigned secondaryChannel);
+/*-------------------------------------------------------------------------*/
+/* Configures pigpio to use the specified DMA channels.
+
+ The default setting is to use channel 14 for the primary channel and
+ channel 6 for the secondary channel.
+*/
+
+#define PI_MAX_PRIMARY_CHANNEL 14
+#define PI_MAX_SECONDARY_CHANNEL 6
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioCfgSocketPort(unsigned port);
+/*-------------------------------------------------------------------------*/
+/* Configures pigpio to use the specified socket port.
+
+ The default setting is to use port 8888.
+*/
+
+/* port: 1024-9999 */
+
+#define PI_MIN_SOCKET_PORT 1024
+#define PI_MAX_SOCKET_PORT 32000
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioCfgInterfaces(unsigned ifFlags);
+/*-------------------------------------------------------------------------*/
+/* Configures pigpio support of the fifo and socket interfaces.
+
+ The default setting is that both interfaces are enabled.
+*/
+
+/* ifFlags: */
+
+#define PI_DISABLE_FIFO_IF 1
+#define PI_DISABLE_SOCK_IF 2
+
+/*-------------------------------------------------------------------------*/
+int gpioCfgInternals(unsigned what,
+ int value);
+/*-------------------------------------------------------------------------*/
+/* Used to tune internal settings.
+ Not intended for general use.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+void gpioWaveDump(void);
+/*-------------------------------------------------------------------------*/
+/* Used to print a readable version of the current waveform to stdout.
+ Not intended for general use.
+*/
+
+
+#ifdef __cplusplus
+}
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+#define PI_CMD_MODES 0
+#define PI_CMD_MODEG 1
+#define PI_CMD_PUD 2
+#define PI_CMD_READ 3
+#define PI_CMD_WRITE 4
+#define PI_CMD_PWM 5
+#define PI_CMD_PRS 6
+#define PI_CMD_PFS 7
+#define PI_CMD_SERVO 8
+#define PI_CMD_WDOG 9
+#define PI_CMD_BR1 10
+#define PI_CMD_BR2 11
+#define PI_CMD_BC1 12
+#define PI_CMD_BC2 13
+#define PI_CMD_BS1 14
+#define PI_CMD_BS2 15
+#define PI_CMD_TICK 16
+#define PI_CMD_HWVER 17
+#define PI_CMD_NO 18
+#define PI_CMD_NB 19
+#define PI_CMD_NP 20
+#define PI_CMD_NC 21
+#define PI_CMD_PRG 22
+#define PI_CMD_PFG 23
+#define PI_CMD_PRRG 24
+#define PI_CMD_HELP 25
+
+/*
+The following command only works on the socket interface.
+It returns a spare notification handle. Notifications for
+that handle will be sent to the socket (rather than a
+/dev/pigpiox pipe).
+
+The socket should be dedicated to receiving notifications
+after this command is issued.
+*/
+
+#define PI_CMD_NOIB 99
+
+
+/*-------------------------------------------------------------------------*/
+
+/* error numbers reported by functions */
+
+#define PI_INIT_FAILED -1 /* gpioInitialise failed */
+#define PI_BAD_USER_GPIO -2 /* gpio not 0-31 */
+#define PI_BAD_GPIO -3 /* gpio not 0-53 */
+#define PI_BAD_MODE -4 /* mode not 0-7 */
+#define PI_BAD_LEVEL -5 /* level not 0-1 */
+#define PI_BAD_PUD -6 /* pud not 0-2 */
+#define PI_BAD_PULSEWIDTH -7 /* pulsewidth not 0 or 500-2500 */
+#define PI_BAD_DUTYCYCLE -8 /* dutycycle not 0-255 */
+#define PI_BAD_TIMER -9 /* timer not 0-9 */
+#define PI_BAD_MS -10 /* ms not 10-60000 */
+#define PI_BAD_TIMETYPE -11 /* timetype not 0-1 */
+#define PI_BAD_SECONDS -12 /* seconds < 0 */
+#define PI_BAD_MICROS -13 /* micros not 0-999999 */
+#define PI_TIMER_FAILED -14 /* gpioSetTimerFunc failed */
+#define PI_BAD_WDOG_TIMEOUT -15 /* timeout not 0-60000 */
+#define PI_NO_ALERT_FUNC -16 /* DEPRECATED */
+#define PI_BAD_CLK_PERIPH -17 /* clock peripheral not 0-1 */
+#define PI_BAD_CLK_SOURCE -18 /* clock source not 0-1 */
+#define PI_BAD_CLK_MICROS -19 /* clock micros not 1, 2, 4, 5, 8, or 10 */
+#define PI_BAD_BUF_MILLIS -20 /* buf millis not 100-10000 */
+#define PI_BAD_DUTY_RANGE -21 /* dutycycle range not 25-40000 */
+#define PI_BAD_SIGNUM -22 /* signum not 0-63 */
+#define PI_BAD_PATHNAME -23 /* can't open pathname */
+#define PI_NO_HANDLE -24 /* no handle available */
+#define PI_BAD_HANDLE -25 /* unknown notify handle */
+#define PI_BAD_IF_FLAGS -26 /* ifFlags > 3 */
+#define PI_BAD_CHANNEL -27 /* DMA channel not 0-14 */
+#define PI_BAD_PRIM_CHANNEL -27 /* DMA primary channel not 0-14 */
+#define PI_BAD_SOCKET_PORT -28 /* socket port not 1024-32000 */
+#define PI_BAD_FIFO_COMMAND -29 /* unrecognized fifo command */
+#define PI_BAD_SECO_CHANNEL -30 /* DMA secondary channel not 0-6 */
+#define PI_NOT_INITIALISED -31 /* function called before gpioInitialise */
+#define PI_INITIALISED -32 /* function called after gpioInitialise */
+#define PI_BAD_WAVE_MODE -33 /* waveform mode not 0-1 */
+#define PI_BAD_CFG_INTERNAL -34 /* bad parameter in gpioCfgInternals call */
+#define PI_BAD_WAVE_BAUD -35 /* baud rate not 100-250000 */
+#define PI_TOO_MANY_PULSES -36 /* waveform has too many pulses */
+#define PI_TOO_MANY_CHARS -37 /* waveform has too many chars */
+#define PI_NOT_SERIAL_GPIO -38 /* no serial read in progress on gpio */
+#define PI_BAD_SERIAL_STRUC -39 /* bad null serial structure parameter */
+#define PI_BAD_SERIAL_BUF -40 /* bad null serial buf parameter */
+
+
+/*-------------------------------------------------------------------------*/
+
+#define PI_DEFAULT_BUFFER_MILLIS 120
+#define PI_DEFAULT_CLK_MICROS 5
+#define PI_DEFAULT_CLK_PERIPHERAL PI_CLOCK_PCM
+#define PI_DEFAULT_CLK_SOURCE PI_CLOCK_PLLD
+#define PI_DEFAULT_IF_FLAGS 0
+#define PI_DEFAULT_DMA_CHANNEL 14
+#define PI_DEFAULT_DMA_PRIMARY_CHANNEL 14
+#define PI_DEFAULT_DMA_SECONDARY_CHANNEL 6
+#define PI_DEFAULT_SOCKET_PORT 8888
+
+#endif
+
--- /dev/null
+/*
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <http://unlicense.org/>
+*/
+
+/*
+This version is for pigpio version 4+
+*/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <string.h>
+#include <signal.h>
+#include <ctype.h>
+
+#include "pigpio.h"
+#include "command.h"
+
+/*
+This program starts the pigpio library as a daemon.
+*/
+
+static unsigned bufferSizeMilliseconds = PI_DEFAULT_BUFFER_MILLIS;
+static unsigned clockMicros = PI_DEFAULT_CLK_MICROS;
+static unsigned clockPeripheral = PI_DEFAULT_CLK_PERIPHERAL;
+static unsigned clockSource = PI_DEFAULT_CLK_SOURCE;
+static unsigned ifFlags = PI_DEFAULT_IF_FLAGS;
+static unsigned DMAchannelChannel = PI_DEFAULT_DMA_CHANNEL;
+static unsigned socketPort = PI_DEFAULT_SOCKET_PORT;
+
+static FILE * errFifo;
+
+void usage()
+{
+ fprintf(stderr, "\n" \
+ "Usage: sudo pigpiod [OPTION] ...\n" \
+ " -b value, gpio sample buffer in milliseconds, default 120\n" \
+ " -d value, DMA channel, 0-14, default 14\n" \
+ " -f, disable fifo interface, default enabled\n" \
+ " -k, disable socket interface, default enabled\n" \
+ " -p value, socket port, 1024-32000, default 8888\n" \
+ " -s value, sample rate, 1, 2, 4, 5, 8, or 10, default 5\n" \
+ " -t value, clock peripheral, 0=PWM 1=PCM, default PCM\n" \
+ " -u value, clock source, 0=OSC 1=PLLD, default PLLD\n" \
+ "EXAMPLE\n" \
+ "sudo pigpiod -s 2 -b 200 -f\n" \
+ " Set a sample rate of 2 microseconds with a 200 millisecond\n" \
+ " buffer. Disable the fifo interface.\n" \
+ "\n");
+}
+
+static void initOpts(int argc, char *argv[])
+{
+ int i, opt;
+
+ while ((opt = getopt(argc, argv, "b:d:fkp:s:t:u:")) != -1)
+ {
+ i = -1;
+
+ switch (opt)
+ {
+ case 'b':
+ i = atoi(optarg);
+ if ((i >= PI_BUF_MILLIS_MIN) && (i <= PI_BUF_MILLIS_MAX))
+ bufferSizeMilliseconds = i;
+ else cmdFatal("invalid -b option (%d)", i);
+ break;
+
+ case 'd':
+ i = atoi(optarg);
+ if ((i >= PI_MIN_DMA_CHANNEL) && (i <= PI_MAX_DMA_CHANNEL))
+ DMAchannelChannel = i;
+ else cmdFatal("invalid -d option (%d)", i);
+ break;
+
+ case 'f':
+ ifFlags |= PI_DISABLE_FIFO_IF;
+ break;
+
+ case 'k':
+ ifFlags |= PI_DISABLE_SOCK_IF;
+ break;
+
+ case 'p':
+ i = atoi(optarg);
+ if ((i >= PI_MIN_SOCKET_PORT) && (i <= PI_MAX_SOCKET_PORT))
+ socketPort = i;
+ else cmdFatal("invalid -p option (%d)", i);
+ break;
+
+ case 's':
+ i = atoi(optarg);
+
+ switch(i)
+ {
+ case 1:
+ case 2:
+ case 4:
+ case 5:
+ case 8:
+ case 10:
+ clockMicros = i;
+ break;
+
+ default:
+ cmdFatal("invalid -s option (%d)", i);
+ break;
+ }
+ break;
+
+ case 't':
+ i = atoi(optarg);
+ if ((i >= PI_CLOCK_PWM) && (i <= PI_CLOCK_PCM))
+ clockPeripheral = i;
+ else cmdFatal("invalid -t option (%d)", i);
+ break;
+
+ case 'u':
+ i = atoi(optarg);
+ if ((i >= PI_CLOCK_OSC) && (i <= PI_CLOCK_PLLD))
+ clockSource = i;
+ else cmdFatal("invalid -u option (%d)", i);
+ break;
+
+ default: /* '?' */
+ usage();
+ exit(-1);
+ }
+ }
+}
+
+void terminate(int signum)
+{
+ /* only registered for SIGHUP/SIGTERM */
+
+ gpioTerminate();
+
+ fprintf(errFifo, "SIGHUP/SIGTERM received\n");
+
+ fflush(NULL);
+
+ fclose(errFifo);
+
+ unlink(PI_ERRFIFO);
+
+ exit(0);
+}
+
+
+int main(int argc, char **argv)
+{
+ pid_t pid;
+ int flags;
+
+ /* Fork off the parent process */
+
+ pid = fork();
+
+ if (pid < 0) { exit(EXIT_FAILURE); }
+
+ /* If we got a good PID, then we can exit the parent process. */
+
+ if (pid > 0) { exit(EXIT_SUCCESS); }
+
+ /* Change the file mode mask */
+
+ umask(0);
+
+ /* Open any logs here */
+
+ /* NONE */
+
+ /* Create a new SID for the child process */
+
+ if (setsid() < 0) cmdFatal("setsid failed (%m)");
+
+ /* Change the current working directory */
+
+ if ((chdir("/")) < 0) cmdFatal("chdir failed (%m)");
+
+ /* check command line parameters */
+
+ initOpts(argc, argv);
+
+ /* Close out the standard file descriptors */
+
+ fclose(stdin);
+ fclose(stdout);
+
+ /* configure library */
+
+ gpioCfgBufferSize(bufferSizeMilliseconds);
+
+ gpioCfgClock(clockMicros, clockPeripheral, clockSource);
+
+ gpioCfgInterfaces(ifFlags);
+
+ gpioCfgDMAchannel(DMAchannelChannel);
+
+ gpioCfgSocketPort(socketPort);
+
+ /* start library */
+
+ if (gpioInitialise()< 0) cmdFatal("Can't initialise pigpio library");
+
+ /* create pipe for error reporting */
+
+ unlink(PI_ERRFIFO);
+
+ mkfifo(PI_ERRFIFO, 0664);
+
+ if (chmod(PI_ERRFIFO, 0664) < 0)
+ cmdFatal("chmod %s failed (%m)", PI_ERRFIFO);
+
+ errFifo = freopen(PI_ERRFIFO, "w+", stderr);
+
+ if (errFifo)
+ {
+ /* set stderr non-blocking */
+
+ flags = fcntl(fileno(errFifo), F_GETFL, 0);
+ fcntl(fileno(errFifo), F_SETFL, flags | O_NONBLOCK);
+
+ /* request SIGHUP/SIGTERM from libarary for termination */
+
+ gpioSetSignalFunc(SIGHUP, terminate);
+ gpioSetSignalFunc(SIGTERM, terminate);
+
+ /* sleep forever */
+
+ while (1)
+ {
+ /* cat /dev/pigerr to view daemon errors */
+
+ sleep(5);
+
+ fflush(errFifo);
+ }
+ }
+ else
+ {
+ fprintf(stderr, "freopen failed (%m)");
+
+ gpioTerminate();
+ }
+
+ return 0;
+}
+
--- /dev/null
+/*
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <http://unlicense.org/>
+*/
+
+/*
+This version is for pigpio version 3+
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include "pigpio.h"
+#include "command.h"
+
+/*
+This program provides a socket interface
+to the commands available from pigpio.
+*/
+
+int main(int argc , char *argv[])
+{
+ int sock, r, idx, port;
+ struct sockaddr_in server;
+ cmdCmd_t cmd;
+ char * portStr, * addrStr;
+ char buf[128];
+
+ sock = socket(AF_INET, SOCK_STREAM, 0);
+
+ if (sock != -1)
+ {
+ portStr = getenv(PI_ENVPORT);
+
+ if (portStr) port = atoi(portStr);
+ else port = PI_DEFAULT_SOCKET_PORT;
+
+ addrStr = getenv(PI_ENVADDR);
+
+ if (!addrStr) addrStr="127.0.0.1";
+
+ server.sin_addr.s_addr = inet_addr(addrStr);
+ server.sin_family = AF_INET;
+ server.sin_port = htons(port);
+
+ if (connect(sock, (struct sockaddr *)&server, sizeof(server)) == 0)
+ {
+ switch(argc)
+ {
+ case 1:
+ exit(0);
+
+ case 2:
+ sprintf(buf, "%10s", argv[1]);
+ break;
+
+ case 3:
+ sprintf(buf, "%10s %10s", argv[1], argv[2]);
+ break;
+
+ case 4:
+ sprintf(buf, "%10s %10s %10s", argv[1], argv[2], argv[3]);
+ break;
+
+ default:
+ cmdFatal("what?");
+ }
+
+ if ((idx=cmdParse(buf, &cmd)) >= 0)
+ {
+ if (send(sock, &cmd, sizeof(cmdCmd_t), 0) == sizeof(cmdCmd_t))
+ {
+ if (recv(sock, &cmd, sizeof(cmdCmd_t), 0) == sizeof(cmdCmd_t))
+ {
+ switch (cmdInfo[idx].rv)
+ {
+ case 0:
+ r = cmd.res;
+ if (r < 0) cmdFatal("ERROR: %s", cmdErrStr(r));
+ break;
+
+ case 1:
+ break;
+
+ case 2:
+ r = cmd.res;
+ if (r < 0) cmdFatal("ERROR: %s", cmdErrStr(r));
+ else printf("%d\n", r);
+ break;
+
+ case 3:
+ printf("%08X\n", cmd.res);
+ break;
+
+ case 4:
+ printf("%u\n", cmd.res);
+ break;
+
+ case 5:
+ printf(cmdUsage);
+ break;
+ }
+ }
+ else cmdFatal("recv failed, %m");
+ }
+ else cmdFatal("send failed, %m");
+ }
+ else cmdFatal("what?");
+ }
+ else cmdFatal("connect failed, %m");
+
+ close(sock);
+ }
+ else cmdFatal("socket failed, %m");
+
+ return 0;
+}
+