commit 56b5f6288528358556fb8e929b834e14511c39be Author: joan Date: Thu Dec 12 10:27:22 2013 +0000 V6 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2b69494 --- /dev/null +++ b/Makefile @@ -0,0 +1,63 @@ +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 diff --git a/README b/README new file mode 100644 index 0000000..41c00cb --- /dev/null +++ b/README @@ -0,0 +1,67 @@ +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 + diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/UNLICENCE b/UNLICENCE new file mode 100644 index 0000000..471f09f --- /dev/null +++ b/UNLICENCE @@ -0,0 +1,25 @@ +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 + diff --git a/checklib.c b/checklib.c new file mode 100644 index 0000000..bc2316d --- /dev/null +++ b/checklib.c @@ -0,0 +1,709 @@ +/* +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 +*/ + +/* +This version is for pigpio version 3+ +*/ + +#include +#include +#include +#include +#include + +#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) && (tickser1) && (ratioer1) && (ratio0; 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;i200000 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< 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)); +} + diff --git a/command.c b/command.c new file mode 100644 index 0000000..ab46aa8 --- /dev/null +++ b/command.c @@ -0,0 +1,292 @@ +/* +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 +*/ + +/* +This version is for pigpio version 4+ +*/ + +#include +#include +#include +#include +#include + +#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"; +} + diff --git a/command.h b/command.h new file mode 100644 index 0000000..9d2da7d --- /dev/null +++ b/command.h @@ -0,0 +1,58 @@ +/* +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 +*/ + +/* +This version is for pigpio version 3+ +*/ + +#ifndef COMMAND_H +#define COMMAND_H + +#include +#include + +#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 diff --git a/demolib.c b/demolib.c new file mode 100644 index 0000000..d2e9bd4 --- /dev/null +++ b/demolib.c @@ -0,0 +1,673 @@ +/* +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 +*/ + +/* +This version is for pigpio version 3+ +*/ + +#include +#include +#include +#include +#include +#include +#include + +#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=(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); +} + diff --git a/pig2vcd.c b/pig2vcd.c new file mode 100644 index 0000000..c0afb0c --- /dev/null +++ b/pig2vcd.c @@ -0,0 +1,121 @@ +/* +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 +*/ + +/* +This version is for pigpio version 3+ +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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< +*/ + +/* pigpio version 6 */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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<gpioOff[slot] &= ~(1<gpioOn[slot] |= (1<gpioOn[slot] &= ~(1<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; iinfo = 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; igpioOnOff[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) 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; cyclenext = 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< (gpioAlert[b].timeout*1000)) + { + timeoutBits |= (1< 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= DBG_DMACBS) + for (i=0; iperiphData = 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_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< 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 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) && (valPI_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< 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 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) + 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; +} + diff --git a/pigpio.h b/pigpio.h new file mode 100644 index 0000000..aa95892 --- /dev/null +++ b/pigpio.h @@ -0,0 +1,1785 @@ +/* +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 +*/ + +/* +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 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 + +#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 + diff --git a/pigpiod.c b/pigpiod.c new file mode 100644 index 0000000..106354b --- /dev/null +++ b/pigpiod.c @@ -0,0 +1,278 @@ +/* +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 +*/ + +/* +This version is for pigpio version 4+ +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} + diff --git a/pigs.c b/pigs.c new file mode 100644 index 0000000..e794632 --- /dev/null +++ b/pigs.c @@ -0,0 +1,144 @@ +/* +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 +*/ + +/* +This version is for pigpio version 3+ +*/ + +#include +#include +#include +#include +#include +#include + +#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; +} +