From 8d188fa0e00bb8c6ff6eddd07bf92857e9bd533a Mon Sep 17 00:00:00 2001 From: Gordon Henderson Date: Wed, 14 Mar 2018 07:17:04 +0000 Subject: [PATCH] update for the v3+ --- INSTALL | 7 -- VERSION | 2 +- build | 3 + debian-template/wiringPi/DEBIAN/control | 2 +- gpio/pins.c => examples/blink-thread.c | 44 +++++-- examples/delayTest.c | 47 ++++---- gpio/Makefile | 12 +- gpio/gpio.1 | 35 ++---- gpio/gpio.c | 43 +++---- gpio/readall.c | 37 +++++- version.h | 4 +- wiringPi/Makefile | 20 +--- wiringPi/noMoreStatic | 20 ++++ wiringPi/wiringPi.c | 147 ++++++++++++++++++------ wiringPi/wiringPi.h | 10 ++ wiringPi/wpiExtensions.c | 2 +- wiringPiD/wiringpid | Bin 18654 -> 0 bytes 17 files changed, 281 insertions(+), 154 deletions(-) rename gpio/pins.c => examples/blink-thread.c (60%) create mode 100644 wiringPi/noMoreStatic delete mode 100755 wiringPiD/wiringpid diff --git a/INSTALL b/INSTALL index 8e0c43c..4e1df2e 100644 --- a/INSTALL +++ b/INSTALL @@ -19,13 +19,6 @@ then run the ldconfig command. sudo ldconfig -If you want to install a static library, you may need to do this manually: - - cd wiringPi - make static - sudo make install-static - - To un-install wiringPi: ./build uninstall diff --git a/VERSION b/VERSION index f454b81..e72716a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.44 +2.46 diff --git a/build b/build index 7dc074c..6844946 100755 --- a/build +++ b/build @@ -64,6 +64,9 @@ if [ x$1 = "xclean" ]; then echo -n "PiGlow: " ; make clean cd ../scrollPhat echo -n "scrollPhat: " ; make clean + cd ../.. + echo -n "Deb: " ; rm -f debian-template/wiringpi*.deb + echo exit fi diff --git a/debian-template/wiringPi/DEBIAN/control b/debian-template/wiringPi/DEBIAN/control index ad7d0e1..72b3bc8 100644 --- a/debian-template/wiringPi/DEBIAN/control +++ b/debian-template/wiringPi/DEBIAN/control @@ -1,5 +1,5 @@ Package: wiringpi -Version: 2.44 +Version: 2.46 Section: libraries Priority: optional Architecture: armhf diff --git a/gpio/pins.c b/examples/blink-thread.c similarity index 60% rename from gpio/pins.c rename to examples/blink-thread.c index 3a4dc15..a53fbf3 100644 --- a/gpio/pins.c +++ b/examples/blink-thread.c @@ -1,7 +1,9 @@ /* - * pins.c: - * Just display a handy Pi pinnout diagram. - * Copyright (c) 2012-2017 Gordon Henderson + * blink-thread.c: + * Standard "blink" program in wiringPi. Blinks an LED connected + * to the first GPIO pin. + * + * Copyright (c) 2012-2013 Gordon Henderson. *********************************************************************** * This file is part of wiringPi: * https://projects.drogon.net/raspberry-pi/wiringpi/ @@ -21,13 +23,39 @@ *********************************************************************** */ - #include +#include -void doPins (void) +// LED Pin - wiringPi pin 0 is BCM_GPIO 17. + +#define LED 0 + +PI_THREAD (blinky) { - printf ("The pins command has been deprecated - sorry. Please use the\n") ; - printf (" gpio readall\n") ; - printf ("command to get a list of the pinnouts for your Pi.\n") ; + for (;;) + { + digitalWrite (LED, HIGH) ; // On + delay (500) ; // mS + digitalWrite (LED, LOW) ; // Off + delay (500) ; + } } + +int main (void) +{ + printf ("Raspberry Pi blink\n") ; + + wiringPiSetup () ; + pinMode (LED, OUTPUT) ; + + piThreadCreate (blinky) ; + + for (;;) + { + printf ("Hello, world\n") ; + delay (600) ; + } + + return 0 ; +} diff --git a/examples/delayTest.c b/examples/delayTest.c index 4c8b6ca..d772cf9 100644 --- a/examples/delayTest.c +++ b/examples/delayTest.c @@ -25,7 +25,6 @@ #include #include -#include #include @@ -34,17 +33,13 @@ int main() { int x ; - struct timeval t1, t2 ; + struct timeval t1, t2, t3 ; int t ; int max, min ; int del ; - int underRuns, overRuns, exactRuns, total ; + int underRuns, overRuns, exactRuns, bogusRuns, total ; int descheds ; - if (wiringPiSetup () == -1) - return 1 ; - - piHiPri (10) ; sleep (1) ; // Baseline test @@ -58,21 +53,22 @@ int main() { underRuns = overRuns = exactRuns = total = 0 ; descheds = 0 ; - max = del ; - min = del ; + max = 0 ; + min = 999 ; for (x = 0 ; x < CYCLES ; ++x) { for (;;) // Repeat this if we get a delay over 999uS { // -> High probability Linux has deschedulled us gettimeofday (&t1, NULL) ; - delayMicroseconds (del) ; + usleep (del) ; +// delayMicroseconds (del) ; gettimeofday (&t2, NULL) ; - if (t2.tv_usec < t1.tv_usec) // Counter wrapped - t = (1000000 + t2.tv_usec) - t1.tv_usec; - else - t = t2.tv_usec - t1.tv_usec ; + timersub (&t2, &t1, &t3) ; + + t = t3.tv_usec ; + if (t > 999) { ++descheds ; @@ -82,25 +78,24 @@ int main() break ; } - if (t > max) - { - max = t ; - ++overRuns ; - } - else if (t < min) - { - min = t ; - ++underRuns ; - } - else + if (t == del) ++exactRuns ; + else if (t < del) + ++underRuns ; + else if (t > del) + ++overRuns ; + + if (t > max) + max = t ; + else if (t < min) + min = t ; total += t ; } printf ("Delay: %3d. Min: %3d, Max: %3d, Unders: %3d, Overs: %3d, Exacts: %3d, Average: %3d, Descheds: %2d\n", del, min, max, underRuns, overRuns, exactRuns, total / CYCLES, descheds) ; fflush (stdout) ; - delay (1) ; + usleep (1000) ; } return 0 ; diff --git a/gpio/Makefile b/gpio/Makefile index f41a005..9ec160d 100644 --- a/gpio/Makefile +++ b/gpio/Makefile @@ -42,7 +42,7 @@ LIBS = -lwiringPi -lwiringPiDev -lpthread -lrt -lm -lcrypt # May not need to alter anything below this line ############################################################################### -SRC = gpio.c readall.c pins.c +SRC = gpio.c readall.c OBJ = $(SRC:.c=.o) @@ -77,22 +77,22 @@ ifneq ($(WIRINGPI_SUID),0) $Q chown root.root $(DESTDIR)$(PREFIX)/bin/gpio $Q chmod 4755 $(DESTDIR)$(PREFIX)/bin/gpio endif - $Q mkdir -p $(DESTDIR)$(PREFIX)/man/man1 - $Q cp gpio.1 $(DESTDIR)$(PREFIX)/man/man1 + $Q mkdir -p $(DESTDIR)$(PREFIX)/share/man/man1 + $Q cp gpio.1 $(DESTDIR)$(PREFIX)/share/man/man1 .PHONY: install-deb install-deb: gpio $Q echo "[Install: deb]" $Q install -m 0755 -d ~/wiringPi/debian-template/wiringPi/usr/bin $Q install -m 0755 gpio ~/wiringPi/debian-template/wiringPi/usr/bin - $Q install -m 0755 -d ~/wiringPi/debian-template/wiringPi/man/man1 - $Q install -m 0644 gpio.1 ~/wiringPi/debian-template/wiringPi/man/man1 + $Q install -m 0755 -d ~/wiringPi/debian-template/wiringPi/usr/share/man/man1 + $Q install -m 0644 gpio.1 ~/wiringPi/debian-template/wiringPi/usr/share/man/man1 .PHONY: uninstall uninstall: $Q echo "[UnInstall]" $Q rm -f $(DESTDIR)$(PREFIX)/bin/gpio - $Q rm -f $(DESTDIR)$(PREFIX)/man/man1/gpio.1 + $Q rm -f $(DESTDIR)$(PREFIX)/share/man/man1/gpio.1 .PHONY: depend depend: diff --git a/gpio/gpio.1 b/gpio/gpio.1 index e5490d6..a71aaae 100644 --- a/gpio/gpio.1 +++ b/gpio/gpio.1 @@ -1,4 +1,4 @@ -.TH GPIO 1 "September 2015" wiringPi "Command-Line access to Raspberry Pi's GPIO" +.TH GPIO 1 "March 2018" wiringPi "Command-Line access to Raspberry Pi's GPIO" .SH NAME gpio \- Command-line access to Raspberry Pi's GPIO @@ -9,7 +9,7 @@ gpio \- Command-line access to Raspberry Pi's GPIO .PP .B gpio .B [ \-g | \-1 ] -.B mode/read/write/aread/awrite/wb/pwm/clock/toggle/blink ... +.B mode/read/write/aread/awrite/wb/pwm/pwnTone/clock/toggle/blink ... .PP .B gpio .B [ \-x extension:params ] @@ -21,6 +21,11 @@ gpio \- Command-line access to Raspberry Pi's GPIO .B ... .PP .B gpio +.B [ \-p ] +.B pwnTone pin frequency +.B ... +.PP +.B gpio .B readall .PP .B gpio @@ -256,30 +261,6 @@ Change the PWM mode to balanced (the default) or mark:space ratio (traditional) .B pwmr Change the PWM range register. The default is 1024. -.TP -.B load i2c [baudrate] -This loads the i2c or drivers into the kernel and changes the permissions -on the associated /dev/ entries so that the current user has access to -them. Optionally it will set the I2C baudrate to that supplied in Kb/sec -(or as close as the Pi can manage) The default speed is 100Kb/sec. - -Note: On recent kernels with the device tree enabled you should use the -raspi-config program to load/unload the I2C device at boot time. - -.TP -.B load spi -This loads the spi drivers into the kernel and changes the permissions -on the associated /dev/ entries so that the current user has access to -them. It used to have the ability to change the buffer size from the -default of 4096 bytes to an arbitrary value, however for some time the -Pi Foundation have compiled the SPI device driver into the kernel and -this has fixed the buffer size. The way to change it now is to edit -the /boot/cmdline.txt file and add on spdev.bufsiz=8192 to set it to -e.g. 8192 bytes then reboot. - -Note: On recent kernels with the device tree enabled you should use the -raspi-config program to load/unload the SPI device at boot time. - .TP .B gbr channel @@ -360,7 +341,7 @@ Please report bugs to .SH COPYRIGHT -Copyright (c) 2012-2015 Gordon Henderson +Copyright (c) 2012-2018 Gordon Henderson This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. diff --git a/gpio/gpio.c b/gpio/gpio.c index 6fd71f8..714e790 100644 --- a/gpio/gpio.c +++ b/gpio/gpio.c @@ -2,7 +2,7 @@ * gpio.c: * Swiss-Army-Knife, Set-UID command-line interface to the Raspberry * Pi's GPIO. - * Copyright (c) 2012-2017 Gordon Henderson + * Copyright (c) 2012-2018 Gordon Henderson *********************************************************************** * This file is part of wiringPi: * https://projects.drogon.net/raspberry-pi/wiringpi/ @@ -48,7 +48,7 @@ extern int wiringPiDebug ; extern void doReadall (void) ; extern void doAllReadall (void) ; -extern void doPins (void) ; +extern void doQmode (int argc, char *argv []) ; #ifndef TRUE # define TRUE (1==1) @@ -56,9 +56,9 @@ extern void doPins (void) ; #endif #define PI_USB_POWER_CONTROL 38 -#define I2CDETECT "/usr/sbin/i2cdetect" -#define MODPROBE "/sbin/modprobe" -#define RMMOD "/sbin/rmmod" +#define I2CDETECT "i2cdetect" +#define MODPROBE "modprobe" +#define RMMOD "rmmod" int wpMode ; @@ -68,9 +68,9 @@ char *usage = "Usage: gpio -v\n" " gpio [-d] ...\n" " [-x extension:params] [[ -x ...]] ...\n" " gpio [-p] ...\n" - " gpio ...\n" + " gpio ...\n" " gpio \n" - " gpio readall/reset\n" + " gpio readall\n" " gpio unexportall/exports\n" " gpio export/edge/unexport ...\n" " gpio wfi \n" @@ -221,9 +221,7 @@ static void checkDevTree (char *argv []) fprintf (stderr, "%s: Unable to load/unload modules as this Pi has the device tree enabled.\n" " You need to run the raspi-config program (as root) and select the\n" -" modules (SPI or I2C) that you wish to load/unload there and reboot.\n" -" There is more information here:\n" -" https://www.raspberrypi.org/forums/viewtopic.php?f=28&t=97314\n", argv [0]) ; +" modules (SPI or I2C) that you wish to load/unload there and reboot.\n", argv [0]) ; exit (1) ; } } @@ -1270,7 +1268,7 @@ static void doVersion (char *argv []) wiringPiVersion (&vMaj, &vMin) ; printf ("gpio version: %d.%d\n", vMaj, vMin) ; - printf ("Copyright (c) 2012-2017 Gordon Henderson\n") ; + printf ("Copyright (c) 2012-2018 Gordon Henderson\n") ; printf ("This is free software with ABSOLUTELY NO WARRANTY.\n") ; printf ("For details type: %s -warranty\n", argv [0]) ; printf ("\n") ; @@ -1320,8 +1318,11 @@ int main (int argc, char *argv []) if (argc == 1) { - fprintf (stderr, "%s\n", usage) ; - return 1 ; + fprintf (stderr, +"%s: At your service!\n" +" Type: gpio -h for full details and\n" +" gpio readall for a quick printout of your connector details\n", argv [0]) ; + exit (EXIT_FAILURE) ; } // Help @@ -1329,7 +1330,7 @@ int main (int argc, char *argv []) if (strcasecmp (argv [1], "-h") == 0) { printf ("%s: %s\n", argv [0], usage) ; - return 0 ; + exit (EXIT_SUCCESS) ; } // Version & Warranty @@ -1338,7 +1339,7 @@ int main (int argc, char *argv []) if ((strcmp (argv [1], "-R") == 0) || (strcmp (argv [1], "-V") == 0)) { printf ("%d\n", piGpioLayout ()) ; - return 0 ; + exit (EXIT_SUCCESS) ; } // Version and information @@ -1346,13 +1347,13 @@ int main (int argc, char *argv []) if (strcmp (argv [1], "-v") == 0) { doVersion (argv) ; - return 0 ; + exit (EXIT_SUCCESS) ; } if (strcasecmp (argv [1], "-warranty") == 0) { printf ("gpio version: %s\n", VERSION) ; - printf ("Copyright (c) 2012-2017 Gordon Henderson\n") ; + printf ("Copyright (c) 2012-2018 Gordon Henderson\n") ; printf ("\n") ; printf (" This program is free software; you can redistribute it and/or modify\n") ; printf (" it under the terms of the GNU Leser General Public License as published\n") ; @@ -1367,13 +1368,13 @@ int main (int argc, char *argv []) printf (" You should have received a copy of the GNU Lesser General Public License\n") ; printf (" along with this program. If not, see .\n") ; printf ("\n") ; - return 0 ; + exit (EXIT_SUCCESS) ; } if (geteuid () != 0) { fprintf (stderr, "%s: Must be root to run. Program should be suid root. This is an error.\n", argv [0]) ; - return 1 ; + exit (EXIT_FAILURE) ; } // Initial test for /sys/class/gpio operations: @@ -1517,7 +1518,8 @@ int main (int argc, char *argv []) else if (strcasecmp (argv [1], "drive" ) == 0) doPadDrive (argc, argv) ; else if (strcasecmp (argv [1], "readall" ) == 0) doReadall () ; else if (strcasecmp (argv [1], "nreadall" ) == 0) doReadall () ; - else if (strcasecmp (argv [1], "pins" ) == 0) doPins () ; + else if (strcasecmp (argv [1], "pins" ) == 0) doReadall () ; + else if (strcasecmp (argv [1], "qmode" ) == 0) doQmode (argc, argv) ; else if (strcasecmp (argv [1], "i2cdetect") == 0) doI2Cdetect (argc, argv) ; else if (strcasecmp (argv [1], "i2cd" ) == 0) doI2Cdetect (argc, argv) ; else if (strcasecmp (argv [1], "reset" ) == 0) doReset (argv [0]) ; @@ -1531,5 +1533,6 @@ int main (int argc, char *argv []) fprintf (stderr, "%s: Unknown command: %s.\n", argv [0], argv [1]) ; exit (EXIT_FAILURE) ; } + return 0 ; } diff --git a/gpio/readall.c b/gpio/readall.c index 18f836f..9396c6d 100644 --- a/gpio/readall.c +++ b/gpio/readall.c @@ -1,7 +1,7 @@ /* * readall.c: * The readall functions - getting a bit big, so split them out. - * Copyright (c) 2012-2017 Gordon Henderson + * Copyright (c) 2012-2018 Gordon Henderson *********************************************************************** * This file is part of wiringPi: * https://projects.drogon.net/raspberry-pi/wiringpi/ @@ -287,16 +287,16 @@ void abReadall (int model, int rev) /* * piPlusReadall: - * Read all the pins on the model A+ or the B+ + * Read all the pins on the model A+ or the B+ or actually, all 40-pin Pi's ********************************************************************************* */ static void plus2header (int model) { /**/ if (model == PI_MODEL_AP) - printf (" +-----+-----+---------+------+---+--A Plus--+---+------+---------+-----+-----+\n") ; + printf (" +-----+-----+---------+------+---+---Pi A+--+---+------+---------+-----+-----+\n") ; else if (model == PI_MODEL_BP) - printf (" +-----+-----+---------+------+---+--B Plus--+---+------+---------+-----+-----+\n") ; + printf (" +-----+-----+---------+------+---+---Pi B+--+---+------+---------+-----+-----+\n") ; else if (model == PI_MODEL_ZERO) printf (" +-----+-----+---------+------+---+-Pi Zero--+---+------+---------+-----+-----+\n") ; else if (model == PI_MODEL_ZERO_W) @@ -305,6 +305,8 @@ static void plus2header (int model) printf (" +-----+-----+---------+------+---+---Pi 2---+---+------+---------+-----+-----+\n") ; else if (model == PI_MODEL_3) printf (" +-----+-----+---------+------+---+---Pi 3---+---+------+---------+-----+-----+\n") ; + else if (model == PI_MODEL_3P) + printf (" +-----+-----+---------+------+---+---Pi 3+--+---+------+---------+-----+-----+\n") ; else printf (" +-----+-----+---------+------+---+---Pi ?---+---+------+---------+-----+-----+\n") ; } @@ -348,7 +350,10 @@ void doReadall (void) /**/ if ((model == PI_MODEL_A) || (model == PI_MODEL_B)) abReadall (model, rev) ; - else if ((model == PI_MODEL_BP) || (model == PI_MODEL_AP) || (model == PI_MODEL_2) || (model == PI_MODEL_3) || (model == PI_MODEL_ZERO) || (model == PI_MODEL_ZERO_W)) + else if ((model == PI_MODEL_BP) || (model == PI_MODEL_AP) || + (model == PI_MODEL_2) || + (model == PI_MODEL_3) || (model == PI_MODEL_3P) || + (model == PI_MODEL_ZERO) || (model == PI_MODEL_ZERO_W)) piPlusReadall (model) ; else if ((model == PI_MODEL_CM) || (model == PI_MODEL_CM3)) allReadall () ; @@ -356,6 +361,7 @@ void doReadall (void) printf ("Oops - unable to determine board type... model: %d\n", model) ; } + /* * doAllReadall: * Force reading of all pins regardless of Pi model @@ -366,3 +372,24 @@ void doAllReadall (void) { allReadall () ; } + + +/* + * doQmode: + * Query mode on a pin + ********************************************************************************* + */ + +void doQmode (int argc, char *argv []) +{ + int pin ; + + if (argc != 3) + { + fprintf (stderr, "Usage: %s qmode pin\n", argv [0]) ; + exit (EXIT_FAILURE) ; + } + + pin = atoi (argv [2]) ; + printf ("%s\n", alts [getAlt (pin)]) ; +} diff --git a/version.h b/version.h index 45b2c50..242c62b 100644 --- a/version.h +++ b/version.h @@ -1,3 +1,3 @@ -#define VERSION "2.44" +#define VERSION "2.46" #define VERSION_MAJOR 2 -#define VERSION_MINOR 44 +#define VERSION_MINOR 46 diff --git a/wiringPi/Makefile b/wiringPi/Makefile index e1868b9..287fa58 100644 --- a/wiringPi/Makefile +++ b/wiringPi/Makefile @@ -40,6 +40,7 @@ CC = gcc INCLUDE = -I. DEFS = -D_GNU_SOURCE CFLAGS = $(DEBUG) $(DEFS) -Wformat=2 -Wall -Wextra -Winline $(INCLUDE) -pipe -fPIC +#CFLAGS = $(DEBUG) $(DEFS) -Wformat=2 -Wall -Wextra -Wconversion -Winline $(INCLUDE) -pipe -fPIC LIBS = -lm -lpthread -lrt -lcrypt @@ -68,13 +69,9 @@ OBJ = $(SRC:.c=.o) all: $(DYNAMIC) -static: $(STATIC) - -$(STATIC): $(OBJ) - $Q echo "[Link (Static)]" - $Q ar rcs $(STATIC) $(OBJ) - $Q ranlib $(STATIC) -# @size $(STATIC) +.PHONY: static +static: + $Q cat noMoreStatic $(DYNAMIC): $(OBJ) $Q echo "[Link (Dynamic)]" @@ -107,15 +104,6 @@ install: $(DYNAMIC) $Q ln -sf $(DESTDIR)$(PREFIX)/lib/libwiringPi.so.$(VERSION) $(DESTDIR)/lib/libwiringPi.so $Q $(LDCONFIG) -.PHONY: install-static -install-static: $(STATIC) - $Q echo "[Install Headers]" - $Q install -m 0755 -d $(DESTDIR)$(PREFIX)/include - $Q install -m 0644 $(HEADERS) $(DESTDIR)$(PREFIX)/include - $Q echo "[Install Static Lib]" - $Q install -m 0755 -d $(DESTDIR)$(PREFIX)/lib - $Q install -m 0755 libwiringPi.a $(DESTDIR)$(PREFIX)/lib - .PHONY: install-deb install-deb: $(DYNAMIC) $Q echo "[Install Headers: deb]" diff --git a/wiringPi/noMoreStatic b/wiringPi/noMoreStatic new file mode 100644 index 0000000..1d25942 --- /dev/null +++ b/wiringPi/noMoreStatic @@ -0,0 +1,20 @@ + +wiringPi is no-longer shipped with the ability to statically link it. + +Many reasons but the biggest issue is people who have statically linked +wiringPi into their product - for example a Pi UPS device or a Tetris-like +game and not subsequently shipped their modified sources. These people are +no better than common thieves with complete disregard to the conditions +of the LGPL that wiringPi ships with. + +Additionally, many think it's a good idea to statically link wiringPi +into their favourite language - like Node, and Java and other itsy bitsy +little things. These people have a complete and utter disregard to what +happens underneath when e.g. the Linux kernel changes on the Pi then +wiringPi stops as it depends on some Pi kernel features, then the poor +user get in-touch with me and I've had over 10,000 emails so-far and +it's now beyond a joke. + +DO NOT STATICALLY LINK WIRINGPI. + +Gordon Henderson, March 2018. diff --git a/wiringPi/wiringPi.c b/wiringPi/wiringPi.c index bd1a369..586b148 100644 --- a/wiringPi/wiringPi.c +++ b/wiringPi/wiringPi.c @@ -139,6 +139,9 @@ static volatile unsigned int GPIO_PWM ; #define PAGE_SIZE (4*1024) #define BLOCK_SIZE (4*1024) +static unsigned int usingGpioMem = FALSE ; +static int wiringPiSetuped = FALSE ; + // PWM // Word offsets into the PWM control region @@ -185,15 +188,22 @@ static volatile unsigned int GPIO_PWM ; // Locals to hold pointers to the hardware -static volatile uint32_t *gpio ; -static volatile uint32_t *pwm ; -static volatile uint32_t *clk ; -static volatile uint32_t *pads ; +static volatile unsigned int *gpio ; +static volatile unsigned int *pwm ; +static volatile unsigned int *clk ; +static volatile unsigned int *pads ; +static volatile unsigned int *timer ; +static volatile unsigned int *timerIrqRaw ; + +// Export variables for the hardware pointers + +volatile unsigned int *_wiringPiGpio ; +volatile unsigned int *_wiringPiPwm ; +volatile unsigned int *_wiringPiClk ; +volatile unsigned int *_wiringPiPads ; +volatile unsigned int *_wiringPiTimer ; +volatile unsigned int *_wiringPiTimerIrqRaw ; -#ifdef USE_TIMER -static volatile uint32_t *timer ; -static volatile uint32_t *timerIrqRaw ; -#endif // Data for use with the boardId functions. // The order of entries here to correspond with the PI_MODEL_X @@ -223,7 +233,7 @@ const char *piModelNames [16] = "CM3", // 10 "Unknown11", // 11 "Pi Zero-W", // 12 - "Unknown13", // 13 + "Pi 3+", // 13 "Unknown14", // 14 "Unknown15", // 15 } ; @@ -651,6 +661,41 @@ int wiringPiFailure (int fatal, const char *message, ...) } +/* + * setupCheck + * Another sanity check because some users forget to call the setup + * function. Mosty because they need feeding C drip by drip )-: + ********************************************************************************* + */ + +static void setupCheck (const char *fName) +{ + if (!wiringPiSetuped) + { + fprintf (stderr, "%s: You have not called one of the wiringPiSetup\n" + " functions, so I'm aborting your program before it crashes anyway.\n", fName) ; + exit (EXIT_FAILURE) ; + } +} + +/* + * gpioMemCheck: + * See if we're using the /dev/gpiomem interface, if-so then some operations + * can't be done and will crash the Pi. + ********************************************************************************* + */ + +static void usingGpioMemCheck (const char *what) +{ + if (usingGpioMem) + { + fprintf (stderr, "%s: Unable to do this when using /dev/gpiomem. Try sudo?\n", what) ; + exit (EXIT_FAILURE) ; + } +} + + + /* * piGpioLayout: * Return a number representing the hardware revision of the board. @@ -720,6 +765,7 @@ int piGpioLayout (void) // I do not support so don't email me your bleating whinges about anything // other than a genuine Raspberry Pi. +#ifdef DONT_CARE_ANYMORE if (! (strstr (line, "BCM2708") || strstr (line, "BCM2709") || strstr (line, "BCM2835"))) { fprintf (stderr, "Unable to determine hardware version. I see: %s,\n", line) ; @@ -730,12 +776,27 @@ int piGpioLayout (void) fprintf (stderr, "Raspberry Pi ONLY.\n") ; exit (EXIT_FAILURE) ; } +#endif -// Right - we're Probably on a Raspberry Pi. Check the revision field for the real +// Actually... That has caused me more than 10,000 emails so-far. Mosty by +// people who think they know better by creating a statically linked +// version that will not run with a new 4.9 kernel. I utterly hate and +// despise those people. +// +// I also get bleats from people running other than Raspbian with another +// distros compiled kernel rather than a foundation compiled kernel, so +// this might actually help them. It might not - I only have the capacity +// to support Raspbian. +// +// However, I've decided to leave this check out and rely purely on the +// Revision: line for now. It will not work on a non-pi hardware or weird +// kernels that don't give you a suitable revision line. + +// So - we're Probably on a Raspberry Pi. Check the revision field for the real // hardware type // In-future, I ought to use the device tree as there are now Pi entries in // /proc/device-tree/ ... -// but I'll leave that for the next revision. +// but I'll leave that for the next revision. Or the next. // Isolate the Revision line @@ -938,7 +999,7 @@ void piBoardId (int *model, int *rev, int *mem, int *maker, int *warranty) if ((revision & (1 << 23)) != 0) // New way { if (wiringPiDebug) - printf ("piBoardId: New Way: revision is: 0x%08X\n", revision) ; + printf ("piBoardId: New Way: revision is: %08X\n", revision) ; bRev = (revision & (0x0F << 0)) >> 0 ; bType = (revision & (0xFF << 4)) >> 4 ; @@ -1183,7 +1244,7 @@ void pwmSetClock (int divisor) /* * gpioClockSet: - * Set the freuency on a GPIO clock pin + * Set the frequency on a GPIO clock pin ********************************************************************************* */ @@ -1322,6 +1383,8 @@ void pinModeAlt (int pin, int mode) { int fSel, shift ; + setupCheck ("pinModeAlt") ; + if ((pin & PI_GPIO_MASK) == 0) // On-board pin { /**/ if (wiringPiMode == WPI_MODE_PINS) @@ -1351,6 +1414,8 @@ void pinMode (int pin, int mode) struct wiringPiNodeStruct *node = wiringPiNodes ; int origPin = pin ; + setupCheck ("pinMode") ; + if ((pin & PI_GPIO_MASK) == 0) // On-board pin { /**/ if (wiringPiMode == WPI_MODE_PINS) @@ -1384,6 +1449,8 @@ void pinMode (int pin, int mode) if ((alt = gpioToPwmALT [pin]) == 0) // Not a hardware capable PWM pin return ; + usingGpioMemCheck ("pinMode PWM") ; + // Set pin to PWM mode *(gpio + fSel) = (*(gpio + fSel) & ~(7 << shift)) | (alt << shift) ; @@ -1398,6 +1465,8 @@ void pinMode (int pin, int mode) if ((alt = gpioToGpClkALT0 [pin]) == 0) // Not a GPIO_CLOCK pin return ; + usingGpioMemCheck ("pinMode CLOCK") ; + // Set pin to GPIO_CLOCK mode and set the clock frequency to 100KHz *(gpio + fSel) = (*(gpio + fSel) & ~(7 << shift)) | (alt << shift) ; @@ -1416,10 +1485,7 @@ void pinMode (int pin, int mode) /* * pullUpDownCtrl: - * Control the internal pull-up/down resistors on a GPIO pin - * The Arduino only has pull-ups and these are enabled by writing 1 - * to a port when in input mode - this paradigm doesn't quite apply - * here though. + * Control the internal pull-up/down resistors on a GPIO pin. ********************************************************************************* */ @@ -1427,6 +1493,8 @@ void pullUpDnControl (int pin, int pud) { struct wiringPiNodeStruct *node = wiringPiNodes ; + setupCheck ("pullUpDnControl") ; + if ((pin & PI_GPIO_MASK) == 0) // On-Board Pin { /**/ if (wiringPiMode == WPI_MODE_PINS) @@ -1588,6 +1656,8 @@ void pwmWrite (int pin, int value) { struct wiringPiNodeStruct *node = wiringPiNodes ; + setupCheck ("pwmWrite") ; + if ((pin & PI_GPIO_MASK) == 0) // On-Board Pin { /**/ if (wiringPiMode == WPI_MODE_PINS) @@ -1597,6 +1667,7 @@ void pwmWrite (int pin, int value) else if (wiringPiMode != WPI_MODE_GPIO) return ; + usingGpioMemCheck ("pwmWrite") ; *(pwm + gpioToPwmPort [pin]) = value ; } else @@ -1656,6 +1727,8 @@ void pwmToneWrite (int pin, int freq) { int range ; + setupCheck ("pwmToneWrite") ; + if (freq == 0) pwmWrite (pin, 0) ; // Off else @@ -2139,16 +2212,15 @@ int wiringPiSetup (void) { int fd ; int model, rev, mem, maker, overVolted ; - static int alreadyDoneThis = FALSE ; // It's actually a fatal error to call any of the wiringPiSetup routines more than once, // (you run out of file handles!) but I'm fed-up with the useless twats who email // me bleating that there is a bug in my code, so screw-em. - if (alreadyDoneThis) + if (wiringPiSetuped) return 0 ; - alreadyDoneThis = TRUE ; + wiringPiSetuped = TRUE ; if (getenv (ENV_DEBUG) != NULL) wiringPiDebug = TRUE ; @@ -2204,11 +2276,18 @@ int wiringPiSetup (void) // Try /dev/mem. If that fails, then // try /dev/gpiomem. If that fails then game over. - if ((fd = open ("/dev/mem", O_RDWR | O_SYNC | O_CLOEXEC) ) < 0) + if ((fd = open ("/dev/mem", O_RDWR | O_SYNC | O_CLOEXEC)) < 0) { - if ((fd = open ("/dev/gpiomem", O_RDWR | O_SYNC | O_CLOEXEC) ) < 0) - return wiringPiFailure (WPI_ALMOST, "wiringPiSetup: Unable to open /dev/mem or /dev/gpiomem: %s.\n Try running with sudo?\n", strerror (errno)) ; - piGpioBase = 0 ; + if ((fd = open ("/dev/gpiomem", O_RDWR | O_SYNC | O_CLOEXEC) ) >= 0) // We're using gpiomem + { + piGpioBase = 0 ; + usingGpioMem = TRUE ; + } + else + return wiringPiFailure (WPI_ALMOST, "wiringPiSetup: Unable to open /dev/mem or /dev/gpiomem: %s.\n" + " Aborting your program because if it can not access the GPIO\n" + " hardware then it most certianly won't work\n" + " Try running with sudo?\n", strerror (errno)) ; } // Set the offsets into the memory interface. @@ -2245,7 +2324,6 @@ int wiringPiSetup (void) if (pads == MAP_FAILED) return wiringPiFailure (WPI_ALMOST, "wiringPiSetup: mmap (PADS) failed: %s\n", strerror (errno)) ; -#ifdef USE_TIMER // The system timer timer = (uint32_t *)mmap(0, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, GPIO_TIMER) ; @@ -2259,7 +2337,14 @@ int wiringPiSetup (void) *(timer + TIMER_CONTROL) = 0x0000280 ; *(timer + TIMER_PRE_DIV) = 0x00000F9 ; timerIrqRaw = timer + TIMER_IRQ_RAW ; -#endif + +// Export the base addresses for any external software that might need them + + _wiringPiGpio = gpio ; + _wiringPiPwm = pwm ; + _wiringPiClk = clk ; + _wiringPiPads = pads ; + _wiringPiTimer = timer ; initialiseEpoch () ; @@ -2325,16 +2410,10 @@ int wiringPiSetupSys (void) int pin ; char fName [128] ; - static int alreadyDoneThis = FALSE ; - -// It's actually a fatal error to call any of the wiringPiSetup routines more than once, -// (you run out of file handles!) but I'm fed-up with the useless twats who email -// me bleating that there is a bug in my code, so screw-em. - - if (alreadyDoneThis) + if (wiringPiSetuped) return 0 ; - alreadyDoneThis = TRUE ; + wiringPiSetuped = TRUE ; if (getenv (ENV_DEBUG) != NULL) wiringPiDebug = TRUE ; diff --git a/wiringPi/wiringPi.h b/wiringPi/wiringPi.h index f601f13..ae5d647 100644 --- a/wiringPi/wiringPi.h +++ b/wiringPi/wiringPi.h @@ -99,6 +99,7 @@ #define PI_MODEL_ZERO 9 #define PI_MODEL_CM3 10 #define PI_MODEL_ZERO_W 12 +#define PI_MODEL_3P 13 #define PI_VERSION_1 0 #define PI_VERSION_1_1 1 @@ -162,6 +163,15 @@ struct wiringPiNodeStruct extern struct wiringPiNodeStruct *wiringPiNodes ; +// Export variables for the hardware pointers + +extern volatile unsigned int *_wiringPiGpio ; +extern volatile unsigned int *_wiringPiPwm ; +extern volatile unsigned int *_wiringPiClk ; +extern volatile unsigned int *_wiringPiPads ; +extern volatile unsigned int *_wiringPiTimer ; +extern volatile unsigned int *_wiringPiTimerIrqRaw ; + // Function prototypes // c++ wrappers thanks to a comment by Nick Lott diff --git a/wiringPi/wpiExtensions.c b/wiringPi/wpiExtensions.c index 53fafc0..bef126f 100644 --- a/wiringPi/wpiExtensions.c +++ b/wiringPi/wpiExtensions.c @@ -871,7 +871,7 @@ int loadWPiExtension (char *progName, char *extensionData, int printErrors) char *p ; char *extension = extensionData ; struct extensionFunctionStruct *extensionFn ; - unsigned pinBase = 0 ; + int pinBase = 0 ; verbose = printErrors ; diff --git a/wiringPiD/wiringpid b/wiringPiD/wiringpid deleted file mode 100755 index b21232e76adc76f7666d6af737b36fadbc79a170..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18654 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