X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=wiringPi%2FwiringPi.c;h=7c899ea5c276316e4f4c0d2b78bb31815557369b;hb=170dce5f1939a3629bbc2329c5557cbaa0bc278f;hp=39c34da75b76519e9ebb88bc68995d1fbaae75e8;hpb=e636f6213bedb35dad2ab172f5e3f1414e50aaf2;p=wiringPi.git diff --git a/wiringPi/wiringPi.c b/wiringPi/wiringPi.c index 39c34da..7c899ea 100644 --- a/wiringPi/wiringPi.c +++ b/wiringPi/wiringPi.c @@ -1,7 +1,8 @@ /* * wiringPi: - * Arduino compatable (ish) Wiring library for the Raspberry Pi - * Copyright (c) 2012 Gordon Henderson + * Arduino look-a-like Wiring library for the Raspberry Pi + * Copyright (c) 2012-2015 Gordon Henderson + * Additional code for pwmSetClock by Chris Hall * * Thanks to code samples from Gert Jan van Loo and the * BCM2835 ARM Peripherals manual, however it's missing @@ -50,55 +51,76 @@ // Added in the 2 UART pins // Change maxPins to numPins to more accurately reflect purpose -// Pad drive current fiddling - -#undef DEBUG_PADS #include +#include #include +#include +#include #include #include #include #include #include #include +#include #include #include -#include #include +#include +#include -#include "wiringPi.h" - -// Function stubs - -void (*pinMode) (int pin, int mode) ; -void (*pullUpDnControl) (int pin, int pud) ; -void (*digitalWrite) (int pin, int value) ; -void (*pwmWrite) (int pin, int value) ; -void (*setPadDrive) (int group, int value) ; -int (*digitalRead) (int pin) ; -int (*waitForInterrupt) (int pin, int mS) ; -void (*delayMicroseconds) (unsigned int howLong) ; -void (*pwmSetMode) (int mode) ; -void (*pwmSetRange) (unsigned int range) ; +#include "softPwm.h" +#include "softTone.h" +#include "wiringPi.h" #ifndef TRUE #define TRUE (1==1) #define FALSE (1==2) #endif +// Environment Variables + +#define ENV_DEBUG "WIRINGPI_DEBUG" +#define ENV_CODES "WIRINGPI_CODES" + + +// Mask for the bottom 64 pins which belong to the Raspberry Pi +// The others are available for the other devices + +#define PI_GPIO_MASK (0xFFFFFFC0) + +struct wiringPiNodeStruct *wiringPiNodes = NULL ; + // BCM Magic #define BCM_PASSWORD 0x5A000000 +// The BCM2835 has 54 GPIO pins. +// BCM2835 data sheet, Page 90 onwards. +// There are 6 control registers, each control the functions of a block +// of 10 pins. +// Each control register has 10 sets of 3 bits per GPIO pin - the ALT values +// +// 000 = GPIO Pin X is an input +// 001 = GPIO Pin X is an output +// 100 = GPIO Pin X takes alternate function 0 +// 101 = GPIO Pin X takes alternate function 1 +// 110 = GPIO Pin X takes alternate function 2 +// 111 = GPIO Pin X takes alternate function 3 +// 011 = GPIO Pin X takes alternate function 4 +// 010 = GPIO Pin X takes alternate function 5 +// +// So the 3 bits for port X are: +// X / 10 + ((X % 10) * 3) + // Port function select bits #define FSEL_INPT 0b000 #define FSEL_OUTP 0b001 #define FSEL_ALT0 0b100 -#define FSEL_ALT0 0b100 #define FSEL_ALT1 0b101 #define FSEL_ALT2 0b110 #define FSEL_ALT3 0b111 @@ -106,20 +128,24 @@ void (*pwmSetRange) (unsigned int range) ; #define FSEL_ALT5 0b010 // Access from ARM Running Linux -// Take from Gert/Doms code. Some of this is not in the manual +// Taken from Gert/Doms code. Some of this is not in the manual // that I can find )-: +// +// Updates in September 2015 - all now static variables (and apologies for the caps) +// due to the Pi v2 and the new /dev/gpiomem interface -#define BCM2708_PERI_BASE 0x20000000 -#define GPIO_PADS (BCM2708_PERI_BASE + 0x100000) -#define CLOCK_BASE (BCM2708_PERI_BASE + 0x101000) -#define GPIO_BASE (BCM2708_PERI_BASE + 0x200000) -#define GPIO_TIMER (BCM2708_PERI_BASE + 0x00B000) -#define GPIO_PWM (BCM2708_PERI_BASE + 0x20C000) +static volatile unsigned int RASPBERRY_PI_PERI_BASE ; +static volatile unsigned int GPIO_PADS ; +static volatile unsigned int GPIO_CLOCK_BASE ; +static volatile unsigned int GPIO_BASE ; +static volatile unsigned int GPIO_TIMER ; +static volatile unsigned int GPIO_PWM ; #define PAGE_SIZE (4*1024) #define BLOCK_SIZE (4*1024) // PWM +// Word offsets into the PWM control region #define PWM_CONTROL 0 #define PWM_STATUS 1 @@ -128,17 +154,11 @@ void (*pwmSetRange) (unsigned int range) ; #define PWM1_RANGE 8 #define PWM1_DATA 9 +// Clock regsiter offsets + #define PWMCLK_CNTL 40 #define PWMCLK_DIV 41 -#define PWM1_MS_MODE 0x8000 // Run in MS mode -#define PWM1_USEFIFO 0x2000 // Data from FIFO -#define PWM1_REVPOLAR 0x1000 // Reverse polarity -#define PWM1_OFFSTATE 0x0800 // Ouput Off state -#define PWM1_REPEATFF 0x0400 // Repeat last value if FIFO empty -#define PWM1_SERIAL 0x0200 // Run in serial mode -#define PWM1_ENABLE 0x0100 // Channel Enable - #define PWM0_MS_MODE 0x0080 // Run in MS mode #define PWM0_USEFIFO 0x0020 // Data from FIFO #define PWM0_REVPOLAR 0x0010 // Reverse polarity @@ -147,7 +167,16 @@ void (*pwmSetRange) (unsigned int range) ; #define PWM0_SERIAL 0x0002 // Run in serial mode #define PWM0_ENABLE 0x0001 // Channel Enable +#define PWM1_MS_MODE 0x8000 // Run in MS mode +#define PWM1_USEFIFO 0x2000 // Data from FIFO +#define PWM1_REVPOLAR 0x1000 // Reverse polarity +#define PWM1_OFFSTATE 0x0800 // Ouput Off state +#define PWM1_REPEATFF 0x0400 // Repeat last value if FIFO empty +#define PWM1_SERIAL 0x0200 // Run in serial mode +#define PWM1_ENABLE 0x0100 // Channel Enable + // Timer +// Word offsets #define TIMER_LOAD (0x400 >> 2) #define TIMER_VALUE (0x404 >> 2) @@ -165,32 +194,80 @@ static volatile uint32_t *gpio ; static volatile uint32_t *pwm ; static volatile uint32_t *clk ; static volatile uint32_t *pads ; -static volatile uint32_t *timer ; +#ifdef USE_TIMER +static volatile uint32_t *timer ; static volatile uint32_t *timerIrqRaw ; +#endif -// The BCM2835 has 54 GPIO pins. -// BCM2835 data sheet, Page 90 onwards. -// There are 6 control registers, each control the functions of a block -// of 10 pins. -// Each control register has 10 sets of 3 bits per GPIO pin: -// -// 000 = GPIO Pin X is an input -// 001 = GPIO Pin X is an output -// 100 = GPIO Pin X takes alternate function 0 -// 101 = GPIO Pin X takes alternate function 1 -// 110 = GPIO Pin X takes alternate function 2 -// 111 = GPIO Pin X takes alternate function 3 -// 011 = GPIO Pin X takes alternate function 4 -// 010 = GPIO Pin X takes alternate function 5 -// -// So the 3 bits for port X are: -// X / 10 + ((X % 10) * 3) + +// Data for use with the boardId functions. +// The order of entries here to correspond with the PI_MODEL_X +// and PI_VERSION_X defines in wiringPi.h +// Only intended for the gpio command - use at your own risk! + +static int piModel2 = FALSE ; + +const char *piModelNames [7] = +{ + "Unknown", + "Model A", + "Model B", + "Model B+", + "Compute Module", + "Model A+", + "Model 2", // Quad Core +} ; + +const char *piRevisionNames [5] = +{ + "Unknown", + "1", + "1.1", + "1.2", + "2", +} ; + +const char *piMakerNames [5] = +{ + "Unknown", + "Egoman", + "Sony", + "Qusda", + "MBest", +} ; + + +// Time for easy calculations + +static uint64_t epochMilli, epochMicro ; + +// Misc + +static int wiringPiMode = WPI_MODE_UNINITIALISED ; +static volatile int pinPass = -1 ; +static pthread_mutex_t pinMutex ; + +// Debugging & Return codes + +int wiringPiDebug = FALSE ; +int wiringPiReturnCodes = FALSE ; // sysFds: // Map a file descriptor from the /sys/class/gpio/gpioX/value -static int sysFds [64] ; +static int sysFds [64] = +{ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +} ; + +// ISR Data + +static void (*isrFunctions [64])(void) ; + // Doing it the Arduino way with lookup tables... // Yes, it's probably more innefficient than all the bit-twidling, but it @@ -198,24 +275,123 @@ static int sysFds [64] ; // pinToGpio: // Take a Wiring pin (0 through X) and re-map it to the BCM_GPIO pin +// Cope for 3 different board revisions here. + +static int *pinToGpio ; + +// Revision 1, 1.1: + +static int pinToGpioR1 [64] = +{ + 17, 18, 21, 22, 23, 24, 25, 4, // From the Original Wiki - GPIO 0 through 7: wpi 0 - 7 + 0, 1, // I2C - SDA1, SCL1 wpi 8 - 9 + 8, 7, // SPI - CE1, CE0 wpi 10 - 11 + 10, 9, 11, // SPI - MOSI, MISO, SCLK wpi 12 - 14 + 14, 15, // UART - Tx, Rx wpi 15 - 16 + +// Padding: -static int pinToGpio [64] = + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // ... 31 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // ... 47 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // ... 63 +} ; + +// Revision 2: + +static int pinToGpioR2 [64] = { - 17, 18, 21, 22, 23, 24, 25, 4, // From the Original Wiki - GPIO 0 through 7 - 0, 1, // I2C - SDA0, SCL0 - 8, 7, // SPI - CE1, CE0 - 10, 9, 11, // SPI - MOSI, MISO, SCLK - 14, 15, // UART - Tx, Rx + 17, 18, 27, 22, 23, 24, 25, 4, // From the Original Wiki - GPIO 0 through 7: wpi 0 - 7 + 2, 3, // I2C - SDA0, SCL0 wpi 8 - 9 + 8, 7, // SPI - CE1, CE0 wpi 10 - 11 + 10, 9, 11, // SPI - MOSI, MISO, SCLK wpi 12 - 14 + 14, 15, // UART - Tx, Rx wpi 15 - 16 + 28, 29, 30, 31, // Rev 2: New GPIOs 8 though 11 wpi 17 - 20 + 5, 6, 13, 19, 26, // B+ wpi 21, 22, 23, 24, 25 + 12, 16, 20, 21, // B+ wpi 26, 27, 28, 29 + 0, 1, // B+ wpi 30, 31 // Padding: - -1, -1, -1,-1,-1,-1,-1, -1, -1, -1, -1, -1, -1, -1, -1, // ... 31 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // ... 47 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // ... 63 } ; + +// physToGpio: +// Take a physical pin (1 through 26) and re-map it to the BCM_GPIO pin +// Cope for 2 different board revisions here. +// Also add in the P5 connector, so the P5 pins are 3,4,5,6, so 53,54,55,56 + +static int *physToGpio ; + +static int physToGpioR1 [64] = +{ + -1, // 0 + -1, -1, // 1, 2 + 0, -1, + 1, -1, + 4, 14, + -1, 15, + 17, 18, + 21, -1, + 22, 23, + -1, 24, + 10, -1, + 9, 25, + 11, 8, + -1, 7, // 25, 26 + + -1, -1, -1, -1, -1, // ... 31 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // ... 47 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // ... 63 +} ; + +static int physToGpioR2 [64] = +{ + -1, // 0 + -1, -1, // 1, 2 + 2, -1, + 3, -1, + 4, 14, + -1, 15, + 17, 18, + 27, -1, + 22, 23, + -1, 24, + 10, -1, + 9, 25, + 11, 8, + -1, 7, // 25, 26 + +// B+ + + 0, 1, + 5, -1, + 6, 12, + 13, -1, + 19, 16, + 26, 20, + -1, 21, + +// the P5 connector on the Rev 2 boards: + + -1, -1, + -1, -1, + -1, -1, + -1, -1, + -1, -1, + 28, 29, + 30, 31, + -1, -1, + -1, -1, + -1, -1, + -1, -1, +} ; + // gpioToGPFSEL: -// Map a BCM_GPIO pin to it's control port. (GPFSEL 0-5) +// Map a BCM_GPIO pin to it's Function Selection +// control port. (GPFSEL 0-5) +// Groups of 10 - 3 bits per Function - 30 bits per port static uint8_t gpioToGPFSEL [] = { @@ -227,6 +403,7 @@ static uint8_t gpioToGPFSEL [] = 5,5,5,5,5,5,5,5,5,5, } ; + // gpioToShift // Define the shift up for the 3 bits per pin in each GPFSEL port @@ -239,6 +416,7 @@ static uint8_t gpioToShift [] = 0,3,6,9,12,15,18,21,24,27, } ; + // gpioToGPSET: // (Word) offset to the GPIO Set registers for each GPIO pin @@ -257,6 +435,7 @@ static uint8_t gpioToGPCLR [] = 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, } ; + // gpioToGPLEV: // (Word) offset to the GPIO Input level registers for each GPIO pin @@ -266,6 +445,7 @@ static uint8_t gpioToGPLEV [] = 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, } ; + #ifdef notYetReady // gpioToEDS // (Word) offset to the Event Detect Status @@ -277,7 +457,7 @@ static uint8_t gpioToEDS [] = } ; // gpioToREN -// (Word) offset to the Rising edgde ENable register +// (Word) offset to the Rising edge ENable register static uint8_t gpioToREN [] = { @@ -295,17 +475,22 @@ static uint8_t gpioToFEN [] = } ; #endif -// gpioToPUDCLK -// (Word) offset to the Pull Up Down Clock regsiter + +// GPPUD: +// GPIO Pin pull up/down register #define GPPUD 37 +// gpioToPUDCLK +// (Word) offset to the Pull Up Down Clock regsiter + static uint8_t gpioToPUDCLK [] = { 38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38, 39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39, } ; + // gpioToPwmALT // the ALT value to put a GPIO pin into PWM mode @@ -321,6 +506,10 @@ static uint8_t gpioToPwmALT [] = 0, 0, 0, 0, 0, 0, 0, 0, // 56 -> 63 } ; + +// gpioToPwmPort +// The port value to put a GPIO pin into PWM mode + static uint8_t gpioToPwmPort [] = { 0, 0, 0, 0, 0, 0, 0, 0, // 0 -> 7 @@ -334,213 +523,381 @@ static uint8_t gpioToPwmPort [] = } ; +// gpioToGpClkALT: +// ALT value to put a GPIO pin into GP Clock mode. +// On the Pi we can really only use BCM_GPIO_4 and BCM_GPIO_21 +// for clocks 0 and 1 respectively, however I'll include the full +// list for completeness - maybe one day... -// Time for easy calculations +#define GPIO_CLOCK_SOURCE 1 + +// gpioToGpClkALT0: + +static uint8_t gpioToGpClkALT0 [] = +{ + 0, 0, 0, 0, FSEL_ALT0, FSEL_ALT0, FSEL_ALT0, 0, // 0 -> 7 + 0, 0, 0, 0, 0, 0, 0, 0, // 8 -> 15 + 0, 0, 0, 0, FSEL_ALT5, FSEL_ALT5, 0, 0, // 16 -> 23 + 0, 0, 0, 0, 0, 0, 0, 0, // 24 -> 31 + FSEL_ALT0, 0, FSEL_ALT0, 0, 0, 0, 0, 0, // 32 -> 39 + 0, 0, FSEL_ALT0, FSEL_ALT0, FSEL_ALT0, 0, 0, 0, // 40 -> 47 + 0, 0, 0, 0, 0, 0, 0, 0, // 48 -> 55 + 0, 0, 0, 0, 0, 0, 0, 0, // 56 -> 63 +} ; + +// gpioToClk: +// (word) Offsets to the clock Control and Divisor register -static unsigned long long epoch ; +static uint8_t gpioToClkCon [] = +{ + -1, -1, -1, -1, 28, 30, 32, -1, // 0 -> 7 + -1, -1, -1, -1, -1, -1, -1, -1, // 8 -> 15 + -1, -1, -1, -1, 28, 30, -1, -1, // 16 -> 23 + -1, -1, -1, -1, -1, -1, -1, -1, // 24 -> 31 + 28, -1, 28, -1, -1, -1, -1, -1, // 32 -> 39 + -1, -1, 28, 30, 28, -1, -1, -1, // 40 -> 47 + -1, -1, -1, -1, -1, -1, -1, -1, // 48 -> 55 + -1, -1, -1, -1, -1, -1, -1, -1, // 56 -> 63 +} ; -////////////////////////////////////////////////////////////////////////////////// +static uint8_t gpioToClkDiv [] = +{ + -1, -1, -1, -1, 29, 31, 33, -1, // 0 -> 7 + -1, -1, -1, -1, -1, -1, -1, -1, // 8 -> 15 + -1, -1, -1, -1, 29, 31, -1, -1, // 16 -> 23 + -1, -1, -1, -1, -1, -1, -1, -1, // 24 -> 31 + 29, -1, 29, -1, -1, -1, -1, -1, // 32 -> 39 + -1, -1, 29, 31, 29, -1, -1, -1, // 40 -> 47 + -1, -1, -1, -1, -1, -1, -1, -1, // 48 -> 55 + -1, -1, -1, -1, -1, -1, -1, -1, // 56 -> 63 +} ; /* - * pinMode: - * Sets the mode of a pin to be input, output or PWM output + * Functions + ********************************************************************************* + */ + + +/* + * wiringPiFailure: + * Fail. Or not. ********************************************************************************* */ -void pinModeGpio (int pin, int mode) +int wiringPiFailure (int fatal, const char *message, ...) { - static int pwmRunning = FALSE ; - int fSel, shift, alt ; + va_list argp ; + char buffer [1024] ; - pin &= 63 ; + if (!fatal && wiringPiReturnCodes) + return -1 ; - fSel = gpioToGPFSEL [pin] ; - shift = gpioToShift [pin] ; + va_start (argp, message) ; + vsnprintf (buffer, 1023, message, argp) ; + va_end (argp) ; - /**/ if (mode == INPUT) - *(gpio + fSel) = (*(gpio + fSel) & ~(7 << shift)) ; // Sets bits to zero = input - else if (mode == OUTPUT) - *(gpio + fSel) = (*(gpio + fSel) & ~(7 << shift)) | (1 << shift) ; - else if (mode == PWM_OUTPUT) - { - if ((alt = gpioToPwmALT [pin]) == 0) // Not a PWM pin - return ; + fprintf (stderr, "%s", buffer) ; + exit (EXIT_FAILURE) ; -// Set pin to PWM mode + return 0 ; +} - *(gpio + fSel) = (*(gpio + fSel) & ~(7 << shift)) | (alt << shift) ; -// We didn't initialise the PWM hardware at setup time - because it's possible that -// something else is using the PWM - e.g. the Audio systems! So if we use PWM -// here, then we're assuming that nothing else is, otherwise things are going -// to sound a bit funny... +/* + * piBoardRev: + * Return a number representing the hardware revision of the board. + * + * Revision 1 really means the early Model B's. + * Revision 2 is everything else - it covers the B, B+ and CM. + * ... and the Pi 2 - which is a B+ ++ ... + * + * Seems there are some boards with 0000 in them (mistake in manufacture) + * So the distinction between boards that I can see is: + * 0000 - Error + * 0001 - Not used + * 0002 - Model B, Rev 1, 256MB, Egoman + * 0003 - Model B, Rev 1.1, 256MB, Egoman, Fuses/D14 removed. + * 0004 - Model B, Rev 2, 256MB, Sony + * 0005 - Model B, Rev 2, 256MB, Qisda + * 0006 - Model B, Rev 2, 256MB, Egoman + * 0007 - Model A, Rev 2, 256MB, Egoman + * 0008 - Model A, Rev 2, 256MB, Sony + * 0009 - Model A, Rev 2, 256MB, Qisda + * 000d - Model B, Rev 2, 512MB, Egoman + * 000e - Model B, Rev 2, 512MB, Sony + * 000f - Model B, Rev 2, 512MB, Qisda + * 0010 - Model B+, Rev 1.2, 512MB, Sony + * 0011 - Pi CM, Rev 1.2, 512MB, Sony + * 0012 - Model A+ Rev 1.2, 256MB, Sony + * 0014 - Pi CM, Rev 1.1, 512MB, Sony (Actual Revision might be different) + * 0015 - Model A+ Rev 1.1, 256MB, Sony + * + * For the Pi 2: + * 0010 - Model 2, Rev 1.1, Quad Core, 1GB, Sony + * + * A small thorn is the olde style overvolting - that will add in + * 1000000 + * + * The Pi compute module has an revision of 0011 - since we only check the + * last digit, then it's 1, therefore it'll default to not 2 or 3 for a + * Rev 1, so will appear as a Rev 2. This is fine for the most part, but + * we'll properly detect the Compute Module later and adjust accordingly. + * And the next rev of the CN is 0014 ... + * + ********************************************************************************* + */ - if (!pwmRunning) - { +static void piBoardRevOops (const char *why) +{ + fprintf (stderr, "piBoardRev: Unable to determine board revision from /proc/cpuinfo\n") ; + fprintf (stderr, " -> %s\n", why) ; + fprintf (stderr, " -> You may want to check:\n") ; + fprintf (stderr, " -> http://www.raspberrypi.org/phpBB3/viewtopic.php?p=184410#p184410\n") ; + exit (EXIT_FAILURE) ; +} - *(pwm + PWM_CONTROL) = 0 ; // Stop PWM - delayMicroseconds (10) ; - -// Gert/Doms Values - *(clk + PWMCLK_DIV) = BCM_PASSWORD | (32<<12) ; // set pwm div to 32 (19.2/32 = 600KHz) - *(clk + PWMCLK_CNTL) = BCM_PASSWORD | 0x11 ; // Source=osc and enable +int piBoardRev (void) +{ + FILE *cpuFd ; + char line [120] ; + char *c ; + static int boardRev = -1 ; - delayMicroseconds (10) ; + if (boardRev != -1) // No point checking twice + return boardRev ; - *(pwm + PWM0_RANGE) = 0x400 ; delayMicroseconds (10) ; - *(pwm + PWM1_RANGE) = 0x400 ; delayMicroseconds (10) ; + if ((cpuFd = fopen ("/proc/cpuinfo", "r")) == NULL) + piBoardRevOops ("Unable to open /proc/cpuinfo") ; -// Enable PWMs +// Start by looking for the Architecture, then we can look for a B2 revision.... - *(pwm + PWM0_DATA) = 512 ; - *(pwm + PWM1_DATA) = 512 ; + while (fgets (line, 120, cpuFd) != NULL) + if (strncmp (line, "Hardware", 8) == 0) + break ; -// Balanced mode (default) + if (strncmp (line, "Hardware", 8) != 0) + piBoardRevOops ("No \"Hardware\" line") ; - *(pwm + PWM_CONTROL) = PWM0_ENABLE | PWM1_ENABLE ; + if (wiringPiDebug) + printf ("piboardRev: Hardware: %s\n", line) ; - pwmRunning = TRUE ; - } +// See if it's BCM2708 or BCM2709 + if (strstr (line, "BCM2709") != NULL) + piModel2 = TRUE ; + else if (strstr (line, "BCM2708") == NULL) + { + fprintf (stderr, "Unable to determine hardware version. I see: %s,\n", line) ; + fprintf (stderr, " - expecting BCM2708 or BCM2709.\n") ; + fprintf (stderr, "If this is a genuine Raspberry Pi then please report this\n") ; + fprintf (stderr, "to projects@drogon.net. If this is not a Raspberry Pi then you\n") ; + fprintf (stderr, "are on your own as wiringPi is designed to support the\n") ; + fprintf (stderr, "Raspberry Pi ONLY.\n") ; + exit (EXIT_FAILURE) ; } -// When we change mode of any pin, we remove the pull up/downs -// Or we used to... Hm. Commented out now because for some wieird reason, -// it seems to block subsequent attempts to set the pull up/downs and I've -// not quite gotten to the bottom of why this happens -// The down-side is that the pull up/downs are rememberd in the SoC between -// power cycles, so it's going to be a good idea to explicitly set them in -// any new code. -// -// pullUpDnControl (pin, PUD_OFF) ; +// Now do the rest of it as before -} + rewind (cpuFd) ; -void pinModeWPi (int pin, int mode) -{ - pinModeGpio (pinToGpio [pin & 63], mode) ; -} + while (fgets (line, 120, cpuFd) != NULL) + if (strncmp (line, "Revision", 8) == 0) + break ; -void pinModeSys (int pin, int mode) -{ - return ; -} + fclose (cpuFd) ; + if (strncmp (line, "Revision", 8) != 0) + piBoardRevOops ("No \"Revision\" line") ; -/* - * pwmControl: - * Allow the user to control some of the PWM functions - ********************************************************************************* - */ +// Chomp trailing CR/NL -void pwmSetModeWPi (int mode) -{ - if (mode == PWM_MODE_MS) - *(pwm + PWM_CONTROL) = PWM0_ENABLE | PWM1_ENABLE | PWM0_MS_MODE | PWM1_MS_MODE ; - else - *(pwm + PWM_CONTROL) = PWM0_ENABLE | PWM1_ENABLE ; -} + for (c = &line [strlen (line) - 1] ; (*c == '\n') || (*c == '\r') ; --c) + *c = 0 ; + + if (wiringPiDebug) + printf ("piboardRev: Revision string: %s\n", line) ; -void pwmSetModeSys (int mode) -{ - return ; -} +// Scan to first digit + for (c = line ; *c ; ++c) + if (isdigit (*c)) + break ; -void pwmSetRangeWPi (unsigned int range) -{ - *(pwm + PWM0_RANGE) = range ; delayMicroseconds (10) ; - *(pwm + PWM1_RANGE) = range ; delayMicroseconds (10) ; -} + if (!isdigit (*c)) + piBoardRevOops ("No numeric revision string") ; -void pwmSetRangeSys (unsigned int range) -{ - return ; +// Make sure its long enough + + if (strlen (c) < 4) + piBoardRevOops ("Bogus \"Revision\" line (too small)") ; + +// If you have overvolted the Pi, then it appears that the revision +// has 100000 added to it! +// The actual condition for it being set is: +// (force_turbo || current_limit_override || temp_limit>85) && over_voltage>0 + + if (wiringPiDebug) + if (strlen (c) != 4) + printf ("piboardRev: This Pi has/is (force_turbo || current_limit_override || temp_limit>85) && over_voltage>0\n") ; + +// Isolate last 4 characters: + + c = c + strlen (c) - 4 ; + + if (wiringPiDebug) + printf ("piboardRev: last4Chars are: \"%s\"\n", c) ; + + if ( (strcmp (c, "0002") == 0) || (strcmp (c, "0003") == 0)) + boardRev = 1 ; + else + boardRev = 2 ; // Covers everything else from the B revision 2 to the B+, the Pi v2 and CM's. + + if (wiringPiDebug) + printf ("piBoardRev: Returning revision: %d\n", boardRev) ; + + return boardRev ; } -#ifdef notYetReady /* - * pinED01: - * pinED10: - * Enables edge-detect mode on a pin - from a 0 to a 1 or 1 to 0 - * Pin must already be in input mode with appropriate pull up/downs set. + * piBoardId: + * Do more digging into the board revision string as above, but return + * as much details as we can. + * This is undocumented and really only intended for the GPIO command. + * Use at your own risk! + * + * for Pi v2: + * [USER:8] [NEW:1] [MEMSIZE:3] [MANUFACTURER:4] [PROCESSOR:4] [TYPE:8] [REV:4] + * NEW 23: will be 1 for the new scheme, 0 for the old scheme + * MEMSIZE 20: 0=256M 1=512M 2=1G + * MANUFACTURER 16: 0=SONY 1=EGOMAN 2=EMBEST + * PROCESSOR 12: 0=2835 1=2836 + * TYPE 04: 0=MODELA 1=MODELB 2=MODELA+ 3=MODELB+ 4=Pi2 MODEL B 5=ALPHA 6=CM + * REV 00: 0=REV0 1=REV1 2=REV2 ********************************************************************************* */ -void pinEnableED01Pi (int pin) +void piBoardId (int *model, int *rev, int *mem, int *maker, int *overVolted) { - pin = pinToGpio [pin & 63] ; -} -#endif + FILE *cpuFd ; + char line [120] ; + char *c ; +// Will deal with the properly later on - for now, lets just get it going... +// unsigned int modelNum ; + (void)piBoardRev () ; // Call this first to make sure all's OK. Don't care about the result. -/* - * digitalWrite: - * Set an output bit - ********************************************************************************* - */ + if ((cpuFd = fopen ("/proc/cpuinfo", "r")) == NULL) + piBoardRevOops ("Unable to open /proc/cpuinfo") ; -void digitalWriteWPi (int pin, int value) -{ - pin = pinToGpio [pin & 63] ; + while (fgets (line, 120, cpuFd) != NULL) + if (strncmp (line, "Revision", 8) == 0) + break ; - if (value == LOW) - *(gpio + gpioToGPCLR [pin]) = 1 << (pin & 31) ; - else - *(gpio + gpioToGPSET [pin]) = 1 << (pin & 31) ; -} + fclose (cpuFd) ; -void digitalWriteGpio (int pin, int value) -{ - pin &= 63 ; + if (strncmp (line, "Revision", 8) != 0) + piBoardRevOops ("No \"Revision\" line") ; - if (value == LOW) - *(gpio + gpioToGPCLR [pin]) = 1 << (pin & 31) ; - else - *(gpio + gpioToGPSET [pin]) = 1 << (pin & 31) ; -} +// Chomp trailing CR/NL -void digitalWriteSys (int pin, int value) -{ - pin &= 63 ; + for (c = &line [strlen (line) - 1] ; (*c == '\n') || (*c == '\r') ; --c) + *c = 0 ; + + if (wiringPiDebug) + printf ("piboardId: Revision string: %s\n", line) ; - if (sysFds [pin] != -1) + if (piModel2) { - if (value == LOW) - write (sysFds [pin], "0\n", 2) ; - else - write (sysFds [pin], "1\n", 2) ; + +// Scan to the colon + + for (c = line ; *c ; ++c) + if (*c == ':') + break ; + + if (*c != ':') + piBoardRevOops ("Bogus \"Revision\" line (no colon)") ; + +// modelNum = (unsigned int)strtol (++c, NULL, 16) ; // Hex number with no leading 0x + + *model = PI_MODEL_2 ; + *rev = PI_VERSION_1_1 ; + *mem = 1024 ; + *maker = PI_MAKER_SONY ; + } + else + { + +// Scan to first digit + + for (c = line ; *c ; ++c) + if (isdigit (*c)) + break ; + +// Make sure its long enough + + if (strlen (c) < 4) + piBoardRevOops ("Bogus \"Revision\" line (not long enough)") ; + +// If longer than 4, we'll assume it's been overvolted + + *overVolted = strlen (c) > 4 ; + +// Extract last 4 characters: + + c = c + strlen (c) - 4 ; + +// Fill out the replys as appropriate + + /**/ if (strcmp (c, "0002") == 0) { *model = PI_MODEL_B ; *rev = PI_VERSION_1 ; *mem = 256 ; *maker = PI_MAKER_EGOMAN ; } + else if (strcmp (c, "0003") == 0) { *model = PI_MODEL_B ; *rev = PI_VERSION_1_1 ; *mem = 256 ; *maker = PI_MAKER_EGOMAN ; } + else if (strcmp (c, "0004") == 0) { *model = PI_MODEL_B ; *rev = PI_VERSION_2 ; *mem = 256 ; *maker = PI_MAKER_SONY ; } + else if (strcmp (c, "0005") == 0) { *model = PI_MODEL_B ; *rev = PI_VERSION_2 ; *mem = 256 ; *maker = PI_MAKER_QISDA ; } + else if (strcmp (c, "0006") == 0) { *model = PI_MODEL_B ; *rev = PI_VERSION_2 ; *mem = 256 ; *maker = PI_MAKER_EGOMAN ; } + else if (strcmp (c, "0007") == 0) { *model = PI_MODEL_A ; *rev = PI_VERSION_2 ; *mem = 256 ; *maker = PI_MAKER_EGOMAN ; } + else if (strcmp (c, "0008") == 0) { *model = PI_MODEL_A ; *rev = PI_VERSION_2 ; *mem = 256 ; *maker = PI_MAKER_SONY ; ; } + else if (strcmp (c, "0009") == 0) { *model = PI_MODEL_B ; *rev = PI_VERSION_2 ; *mem = 256 ; *maker = PI_MAKER_QISDA ; } + else if (strcmp (c, "000d") == 0) { *model = PI_MODEL_B ; *rev = PI_VERSION_2 ; *mem = 512 ; *maker = PI_MAKER_EGOMAN ; } + else if (strcmp (c, "000e") == 0) { *model = PI_MODEL_B ; *rev = PI_VERSION_2 ; *mem = 512 ; *maker = PI_MAKER_SONY ; } + else if (strcmp (c, "000f") == 0) { *model = PI_MODEL_B ; *rev = PI_VERSION_2 ; *mem = 512 ; *maker = PI_MAKER_EGOMAN ; } + else if (strcmp (c, "0010") == 0) { *model = PI_MODEL_BP ; *rev = PI_VERSION_1_2 ; *mem = 512 ; *maker = PI_MAKER_SONY ; } + else if (strcmp (c, "0011") == 0) { *model = PI_MODEL_CM ; *rev = PI_VERSION_1_2 ; *mem = 512 ; *maker = PI_MAKER_SONY ; } + else if (strcmp (c, "0012") == 0) { *model = PI_MODEL_AP ; *rev = PI_VERSION_1_2 ; *mem = 256 ; *maker = PI_MAKER_SONY ; } + else if (strcmp (c, "0013") == 0) { *model = PI_MODEL_BP ; *rev = PI_VERSION_1_2 ; *mem = 512 ; *maker = PI_MAKER_MBEST ; } + else if (strcmp (c, "0014") == 0) { *model = PI_MODEL_CM ; *rev = PI_VERSION_1_2 ; *mem = 512 ; *maker = PI_MAKER_SONY ; } + else if (strcmp (c, "0015") == 0) { *model = PI_MODEL_AP ; *rev = PI_VERSION_1_1 ; *mem = 256 ; *maker = PI_MAKER_SONY ; } + else { *model = 0 ; *rev = 0 ; *mem = 0 ; *maker = 0 ; } } } + /* - * pwnWrite: - * Set an output PWM value + * wpiPinToGpio: + * Translate a wiringPi Pin number to native GPIO pin number. + * Provided for external support. ********************************************************************************* */ -void pwmWriteGpio (int pin, int value) +int wpiPinToGpio (int wpiPin) { - int port ; - - pin = pin & 63 ; - port = gpioToPwmPort [pin] ; - - *(pwm + port) = value ; + return pinToGpio [wpiPin & 63] ; } -void pwmWriteWPi (int pin, int value) -{ - pwmWriteGpio (pinToGpio [pin & 63], value) ; -} -void pwmWriteSys (int pin, int value) +/* + * physPinToGpio: + * Translate a physical Pin number to native GPIO pin number. + * Provided for external support. + ********************************************************************************* + */ + +int physPinToGpio (int physPin) { - return ; + return physToGpio [physPin & 63] ; } @@ -550,71 +907,365 @@ void pwmWriteSys (int pin, int value) ********************************************************************************* */ -void setPadDriveWPi (int group, int value) +void setPadDrive (int group, int value) { uint32_t wrVal ; - if ((group < 0) || (group > 2)) - return ; + if ((wiringPiMode == WPI_MODE_PINS) || (wiringPiMode == WPI_MODE_PHYS) || (wiringPiMode == WPI_MODE_GPIO)) + { + if ((group < 0) || (group > 2)) + return ; - wrVal = BCM_PASSWORD | 0x18 | (value & 7) ; - *(pads + group + 11) = wrVal ; + wrVal = BCM_PASSWORD | 0x18 | (value & 7) ; + *(pads + group + 11) = wrVal ; -#ifdef DEBUG_PADS - printf ("setPadDrive: Group: %d, value: %d (%08X)\n", group, value, wrVal) ; - printf ("Read : %08X\n", *(pads + group + 11)) ; -#endif -} + if (wiringPiDebug) + { + printf ("setPadDrive: Group: %d, value: %d (%08X)\n", group, value, wrVal) ; + printf ("Read : %08X\n", *(pads + group + 11)) ; + } + } +} + + +/* + * getAlt: + * Returns the ALT bits for a given port. Only really of-use + * for the gpio readall command (I think) + ********************************************************************************* + */ + +int getAlt (int pin) +{ + int fSel, shift, alt ; + + pin &= 63 ; + + /**/ if (wiringPiMode == WPI_MODE_PINS) + pin = pinToGpio [pin] ; + else if (wiringPiMode == WPI_MODE_PHYS) + pin = physToGpio [pin] ; + else if (wiringPiMode != WPI_MODE_GPIO) + return 0 ; + + fSel = gpioToGPFSEL [pin] ; + shift = gpioToShift [pin] ; + + alt = (*(gpio + fSel) >> shift) & 7 ; + + return alt ; +} + + +/* + * pwmSetMode: + * Select the native "balanced" mode, or standard mark:space mode + ********************************************************************************* + */ -void setPadDriveGpio (int group, int value) +void pwmSetMode (int mode) { - setPadDriveWPi (group, value) ; + if ((wiringPiMode == WPI_MODE_PINS) || (wiringPiMode == WPI_MODE_PHYS) || (wiringPiMode == WPI_MODE_GPIO)) + { + if (mode == PWM_MODE_MS) + *(pwm + PWM_CONTROL) = PWM0_ENABLE | PWM1_ENABLE | PWM0_MS_MODE | PWM1_MS_MODE ; + else + *(pwm + PWM_CONTROL) = PWM0_ENABLE | PWM1_ENABLE ; + } } -void setPadDriveSys (int group, int value) + +/* + * pwmSetRange: + * Set the PWM range register. We set both range registers to the same + * value. If you want different in your own code, then write your own. + ********************************************************************************* + */ + +void pwmSetRange (unsigned int range) { - return ; + if ((wiringPiMode == WPI_MODE_PINS) || (wiringPiMode == WPI_MODE_PHYS) || (wiringPiMode == WPI_MODE_GPIO)) + { + *(pwm + PWM0_RANGE) = range ; delayMicroseconds (10) ; + *(pwm + PWM1_RANGE) = range ; delayMicroseconds (10) ; + } } /* - * digitalRead: - * Read the value of a given Pin, returning HIGH or LOW + * pwmSetClock: + * Set/Change the PWM clock. Originally my code, but changed + * (for the better!) by Chris Hall, + * after further study of the manual and testing with a 'scope ********************************************************************************* */ -int digitalReadWPi (int pin) +void pwmSetClock (int divisor) { - pin = pinToGpio [pin & 63] ; + uint32_t pwm_control ; + divisor &= 4095 ; - if ((*(gpio + gpioToGPLEV [pin]) & (1 << (pin & 31))) != 0) - return HIGH ; - else - return LOW ; + if ((wiringPiMode == WPI_MODE_PINS) || (wiringPiMode == WPI_MODE_PHYS) || (wiringPiMode == WPI_MODE_GPIO)) + { + if (wiringPiDebug) + printf ("Setting to: %d. Current: 0x%08X\n", divisor, *(clk + PWMCLK_DIV)) ; + + pwm_control = *(pwm + PWM_CONTROL) ; // preserve PWM_CONTROL + +// We need to stop PWM prior to stopping PWM clock in MS mode otherwise BUSY +// stays high. + + *(pwm + PWM_CONTROL) = 0 ; // Stop PWM + +// Stop PWM clock before changing divisor. The delay after this does need to +// this big (95uS occasionally fails, 100uS OK), it's almost as though the BUSY +// flag is not working properly in balanced mode. Without the delay when DIV is +// adjusted the clock sometimes switches to very slow, once slow further DIV +// adjustments do nothing and it's difficult to get out of this mode. + + *(clk + PWMCLK_CNTL) = BCM_PASSWORD | 0x01 ; // Stop PWM Clock + delayMicroseconds (110) ; // prevents clock going sloooow + + while ((*(clk + PWMCLK_CNTL) & 0x80) != 0) // Wait for clock to be !BUSY + delayMicroseconds (1) ; + + *(clk + PWMCLK_DIV) = BCM_PASSWORD | (divisor << 12) ; + + *(clk + PWMCLK_CNTL) = BCM_PASSWORD | 0x11 ; // Start PWM clock + *(pwm + PWM_CONTROL) = pwm_control ; // restore PWM_CONTROL + + if (wiringPiDebug) + printf ("Set to: %d. Now : 0x%08X\n", divisor, *(clk + PWMCLK_DIV)) ; + } } -int digitalReadGpio (int pin) + +/* + * gpioClockSet: + * Set the freuency on a GPIO clock pin + ********************************************************************************* + */ + +void gpioClockSet (int pin, int freq) { + int divi, divr, divf ; + pin &= 63 ; - if ((*(gpio + gpioToGPLEV [pin]) & (1 << (pin & 31))) != 0) - return HIGH ; - else - return LOW ; + /**/ if (wiringPiMode == WPI_MODE_PINS) + pin = pinToGpio [pin] ; + else if (wiringPiMode == WPI_MODE_PHYS) + pin = physToGpio [pin] ; + else if (wiringPiMode != WPI_MODE_GPIO) + return ; + + divi = 19200000 / freq ; + divr = 19200000 % freq ; + divf = (int)((double)divr * 4096.0 / 19200000.0) ; + + if (divi > 4095) + divi = 4095 ; + + *(clk + gpioToClkCon [pin]) = BCM_PASSWORD | GPIO_CLOCK_SOURCE ; // Stop GPIO Clock + while ((*(clk + gpioToClkCon [pin]) & 0x80) != 0) // ... and wait + ; + + *(clk + gpioToClkDiv [pin]) = BCM_PASSWORD | (divi << 12) | divf ; // Set dividers + *(clk + gpioToClkCon [pin]) = BCM_PASSWORD | 0x10 | GPIO_CLOCK_SOURCE ; // Start Clock } -int digitalReadSys (int pin) + +/* + * wiringPiFindNode: + * Locate our device node + ********************************************************************************* + */ + +struct wiringPiNodeStruct *wiringPiFindNode (int pin) { - char c ; + struct wiringPiNodeStruct *node = wiringPiNodes ; - pin &= 63 ; + while (node != NULL) + if ((pin >= node->pinBase) && (pin <= node->pinMax)) + return node ; + else + node = node->next ; - if (sysFds [pin] == -1) - return 0 ; + return NULL ; +} + + +/* + * wiringPiNewNode: + * Create a new GPIO node into the wiringPi handling system + ********************************************************************************* + */ - lseek (sysFds [pin], 0L, SEEK_SET) ; - read (sysFds [pin], &c, 1) ; - return (c == '0') ? 0 : 1 ; +static void pinModeDummy (struct wiringPiNodeStruct *node, int pin, int mode) { return ; } +static void pullUpDnControlDummy (struct wiringPiNodeStruct *node, int pin, int pud) { return ; } +static int digitalReadDummy (struct wiringPiNodeStruct *node, int pin) { return LOW ; } +static void digitalWriteDummy (struct wiringPiNodeStruct *node, int pin, int value) { return ; } +static void pwmWriteDummy (struct wiringPiNodeStruct *node, int pin, int value) { return ; } +static int analogReadDummy (struct wiringPiNodeStruct *node, int pin) { return 0 ; } +static void analogWriteDummy (struct wiringPiNodeStruct *node, int pin, int value) { return ; } + +struct wiringPiNodeStruct *wiringPiNewNode (int pinBase, int numPins) +{ + int pin ; + struct wiringPiNodeStruct *node ; + +// Minimum pin base is 64 + + if (pinBase < 64) + (void)wiringPiFailure (WPI_FATAL, "wiringPiNewNode: pinBase of %d is < 64\n", pinBase) ; + +// Check all pins in-case there is overlap: + + for (pin = pinBase ; pin < (pinBase + numPins) ; ++pin) + if (wiringPiFindNode (pin) != NULL) + (void)wiringPiFailure (WPI_FATAL, "wiringPiNewNode: Pin %d overlaps with existing definition\n", pin) ; + + node = (struct wiringPiNodeStruct *)calloc (sizeof (struct wiringPiNodeStruct), 1) ; // calloc zeros + if (node == NULL) + (void)wiringPiFailure (WPI_FATAL, "wiringPiNewNode: Unable to allocate memory: %s\n", strerror (errno)) ; + + node->pinBase = pinBase ; + node->pinMax = pinBase + numPins - 1 ; + node->pinMode = pinModeDummy ; + node->pullUpDnControl = pullUpDnControlDummy ; + node->digitalRead = digitalReadDummy ; + node->digitalWrite = digitalWriteDummy ; + node->pwmWrite = pwmWriteDummy ; + node->analogRead = analogReadDummy ; + node->analogWrite = analogWriteDummy ; + node->next = wiringPiNodes ; + wiringPiNodes = node ; + + return node ; +} + + +#ifdef notYetReady +/* + * pinED01: + * pinED10: + * Enables edge-detect mode on a pin - from a 0 to a 1 or 1 to 0 + * Pin must already be in input mode with appropriate pull up/downs set. + ********************************************************************************* + */ + +void pinEnableED01Pi (int pin) +{ + pin = pinToGpio [pin & 63] ; +} +#endif + + +/* + ********************************************************************************* + * Core Functions + ********************************************************************************* + */ + +/* + * pinModeAlt: + * This is an un-documented special to let you set any pin to any mode + ********************************************************************************* + */ + +void pinModeAlt (int pin, int mode) +{ + int fSel, shift ; + + if ((pin & PI_GPIO_MASK) == 0) // On-board pin + { + /**/ if (wiringPiMode == WPI_MODE_PINS) + pin = pinToGpio [pin] ; + else if (wiringPiMode == WPI_MODE_PHYS) + pin = physToGpio [pin] ; + else if (wiringPiMode != WPI_MODE_GPIO) + return ; + + fSel = gpioToGPFSEL [pin] ; + shift = gpioToShift [pin] ; + + *(gpio + fSel) = (*(gpio + fSel) & ~(7 << shift)) | ((mode & 0x7) << shift) ; + } +} + + +/* + * pinMode: + * Sets the mode of a pin to be input, output or PWM output + ********************************************************************************* + */ + +void pinMode (int pin, int mode) +{ + int fSel, shift, alt ; + struct wiringPiNodeStruct *node = wiringPiNodes ; + int origPin = pin ; + + if ((pin & PI_GPIO_MASK) == 0) // On-board pin + { + /**/ if (wiringPiMode == WPI_MODE_PINS) + pin = pinToGpio [pin] ; + else if (wiringPiMode == WPI_MODE_PHYS) + pin = physToGpio [pin] ; + else if (wiringPiMode != WPI_MODE_GPIO) + return ; + + softPwmStop (origPin) ; + softToneStop (origPin) ; + + fSel = gpioToGPFSEL [pin] ; + shift = gpioToShift [pin] ; + + /**/ if (mode == INPUT) + *(gpio + fSel) = (*(gpio + fSel) & ~(7 << shift)) ; // Sets bits to zero = input + else if (mode == OUTPUT) + *(gpio + fSel) = (*(gpio + fSel) & ~(7 << shift)) | (1 << shift) ; + else if (mode == SOFT_PWM_OUTPUT) + softPwmCreate (origPin, 0, 100) ; + else if (mode == SOFT_TONE_OUTPUT) + softToneCreate (origPin) ; + else if (mode == PWM_TONE_OUTPUT) + { + pinMode (origPin, PWM_OUTPUT) ; // Call myself to enable PWM mode + pwmSetMode (PWM_MODE_MS) ; + } + else if (mode == PWM_OUTPUT) + { + if ((alt = gpioToPwmALT [pin]) == 0) // Not a hardware capable PWM pin + return ; + +// Set pin to PWM mode + + *(gpio + fSel) = (*(gpio + fSel) & ~(7 << shift)) | (alt << shift) ; + delayMicroseconds (110) ; // See comments in pwmSetClockWPi + + pwmSetMode (PWM_MODE_BAL) ; // Pi default mode + pwmSetRange (1024) ; // Default range of 1024 + pwmSetClock (32) ; // 19.2 / 32 = 600KHz - Also starts the PWM + } + else if (mode == GPIO_CLOCK) + { + if ((alt = gpioToGpClkALT0 [pin]) == 0) // Not a GPIO_CLOCK pin + return ; + +// Set pin to GPIO_CLOCK mode and set the clock frequency to 100KHz + + *(gpio + fSel) = (*(gpio + fSel) & ~(7 << shift)) | (alt << shift) ; + delayMicroseconds (110) ; + gpioClockSet (pin, 100000) ; + } + } + else + { + if ((node = wiringPiFindNode (pin)) != NULL) + node->pinMode (node, pin, mode) ; + return ; + } } @@ -627,31 +1278,258 @@ int digitalReadSys (int pin) ********************************************************************************* */ -void pullUpDnControlGpio (int pin, int pud) +void pullUpDnControl (int pin, int pud) { - pin &= 63 ; - pud &= 3 ; + struct wiringPiNodeStruct *node = wiringPiNodes ; - *(gpio + GPPUD) = pud ; delayMicroseconds (5) ; - *(gpio + gpioToPUDCLK [pin]) = 1 << (pin & 31) ; delayMicroseconds (5) ; - - *(gpio + GPPUD) = 0 ; delayMicroseconds (5) ; - *(gpio + gpioToPUDCLK [pin]) = 0 ; delayMicroseconds (5) ; + if ((pin & PI_GPIO_MASK) == 0) // On-Board Pin + { + /**/ if (wiringPiMode == WPI_MODE_PINS) + pin = pinToGpio [pin] ; + else if (wiringPiMode == WPI_MODE_PHYS) + pin = physToGpio [pin] ; + else if (wiringPiMode != WPI_MODE_GPIO) + return ; + + *(gpio + GPPUD) = pud & 3 ; delayMicroseconds (5) ; + *(gpio + gpioToPUDCLK [pin]) = 1 << (pin & 31) ; delayMicroseconds (5) ; + + *(gpio + GPPUD) = 0 ; delayMicroseconds (5) ; + *(gpio + gpioToPUDCLK [pin]) = 0 ; delayMicroseconds (5) ; + } + else // Extension module + { + if ((node = wiringPiFindNode (pin)) != NULL) + node->pullUpDnControl (node, pin, pud) ; + return ; + } } -void pullUpDnControlWPi (int pin, int pud) + +/* + * digitalRead: + * Read the value of a given Pin, returning HIGH or LOW + ********************************************************************************* + */ + +int digitalRead (int pin) { - pullUpDnControlGpio (pinToGpio [pin & 63], pud) ; + char c ; + struct wiringPiNodeStruct *node = wiringPiNodes ; + + if ((pin & PI_GPIO_MASK) == 0) // On-Board Pin + { + /**/ if (wiringPiMode == WPI_MODE_GPIO_SYS) // Sys mode + { + if (sysFds [pin] == -1) + return LOW ; + + lseek (sysFds [pin], 0L, SEEK_SET) ; + read (sysFds [pin], &c, 1) ; + return (c == '0') ? LOW : HIGH ; + } + else if (wiringPiMode == WPI_MODE_PINS) + pin = pinToGpio [pin] ; + else if (wiringPiMode == WPI_MODE_PHYS) + pin = physToGpio [pin] ; + else if (wiringPiMode != WPI_MODE_GPIO) + return LOW ; + + if ((*(gpio + gpioToGPLEV [pin]) & (1 << (pin & 31))) != 0) + return HIGH ; + else + return LOW ; + } + else + { + if ((node = wiringPiFindNode (pin)) == NULL) + return LOW ; + return node->digitalRead (node, pin) ; + } } -void pullUpDnControlSys (int pin, int pud) + +/* + * digitalWrite: + * Set an output bit + ********************************************************************************* + */ + +void digitalWrite (int pin, int value) { - return ; + struct wiringPiNodeStruct *node = wiringPiNodes ; + + if ((pin & PI_GPIO_MASK) == 0) // On-Board Pin + { + /**/ if (wiringPiMode == WPI_MODE_GPIO_SYS) // Sys mode + { + if (sysFds [pin] != -1) + { + if (value == LOW) + write (sysFds [pin], "0\n", 2) ; + else + write (sysFds [pin], "1\n", 2) ; + } + return ; + } + else if (wiringPiMode == WPI_MODE_PINS) + pin = pinToGpio [pin] ; + else if (wiringPiMode == WPI_MODE_PHYS) + pin = physToGpio [pin] ; + else if (wiringPiMode != WPI_MODE_GPIO) + return ; + + if (value == LOW) + *(gpio + gpioToGPCLR [pin]) = 1 << (pin & 31) ; + else + *(gpio + gpioToGPSET [pin]) = 1 << (pin & 31) ; + } + else + { + if ((node = wiringPiFindNode (pin)) != NULL) + node->digitalWrite (node, pin, value) ; + } +} + + +/* + * pwmWrite: + * Set an output PWM value + ********************************************************************************* + */ + +void pwmWrite (int pin, int value) +{ + struct wiringPiNodeStruct *node = wiringPiNodes ; + + if ((pin & PI_GPIO_MASK) == 0) // On-Board Pin + { + /**/ if (wiringPiMode == WPI_MODE_PINS) + pin = pinToGpio [pin] ; + else if (wiringPiMode == WPI_MODE_PHYS) + pin = physToGpio [pin] ; + else if (wiringPiMode != WPI_MODE_GPIO) + return ; + + *(pwm + gpioToPwmPort [pin]) = value ; + } + else + { + if ((node = wiringPiFindNode (pin)) != NULL) + node->pwmWrite (node, pin, value) ; + } +} + + +/* + * analogRead: + * Read the analog value of a given Pin. + * There is no on-board Pi analog hardware, + * so this needs to go to a new node. + ********************************************************************************* + */ + +int analogRead (int pin) +{ + struct wiringPiNodeStruct *node = wiringPiNodes ; + + if ((node = wiringPiFindNode (pin)) == NULL) + return 0 ; + else + return node->analogRead (node, pin) ; +} + + +/* + * analogWrite: + * Write the analog value to the given Pin. + * There is no on-board Pi analog hardware, + * so this needs to go to a new node. + ********************************************************************************* + */ + +void analogWrite (int pin, int value) +{ + struct wiringPiNodeStruct *node = wiringPiNodes ; + + if ((node = wiringPiFindNode (pin)) == NULL) + return ; + + node->analogWrite (node, pin, value) ; +} + + +/* + * pwmToneWrite: + * Pi Specific. + * Output the given frequency on the Pi's PWM pin + ********************************************************************************* + */ + +void pwmToneWrite (int pin, int freq) +{ + int range ; + + if (freq == 0) + pwmWrite (pin, 0) ; // Off + else + { + range = 600000 / freq ; + pwmSetRange (range) ; + pwmWrite (pin, freq / 2) ; + } +} + + + +/* + * digitalWriteByte: + * Pi Specific + * Write an 8-bit byte to the first 8 GPIO pins - try to do it as + * fast as possible. + * However it still needs 2 operations to set the bits, so any external + * hardware must not rely on seeing a change as there will be a change + * to set the outputs bits to zero, then another change to set the 1's + ********************************************************************************* + */ + +void digitalWriteByte (int value) +{ + uint32_t pinSet = 0 ; + uint32_t pinClr = 0 ; + int mask = 1 ; + int pin ; + + /**/ if (wiringPiMode == WPI_MODE_GPIO_SYS) + { + for (pin = 0 ; pin < 8 ; ++pin) + { + digitalWrite (pin, value & mask) ; + mask <<= 1 ; + } + return ; + } + else + { + for (pin = 0 ; pin < 8 ; ++pin) + { + if ((value & mask) == 0) + pinClr |= (1 << pinToGpio [pin]) ; + else + pinSet |= (1 << pinToGpio [pin]) ; + + mask <<= 1 ; + } + + *(gpio + gpioToGPCLR [0]) = pinClr ; + *(gpio + gpioToGPSET [0]) = pinSet ; + } } /* * waitForInterrupt: + * Pi Specific. * Wait for Interrupt on a GPIO pin. * This is actually done via the /sys/class/gpio interface regardless of * the wiringPi access mode in-use. Maybe sometime it might get a better @@ -659,24 +1537,19 @@ void pullUpDnControlSys (int pin, int pud) ********************************************************************************* */ -int waitForInterruptSys (int pin, int mS) +int waitForInterrupt (int pin, int mS) { int fd, x ; - char buf [8] ; + uint8_t c ; struct pollfd polls ; - if ((fd = sysFds [pin & 63]) == -1) - return -2 ; - -// Do a dummy read - - x = read (fd, buf, 6) ; - if (x < 0) - return x ; + /**/ if (wiringPiMode == WPI_MODE_PINS) + pin = pinToGpio [pin] ; + else if (wiringPiMode == WPI_MODE_PHYS) + pin = physToGpio [pin] ; -// And seek - - lseek (fd, 0, SEEK_SET) ; + if ((fd = sysFds [pin]) == -1) + return -2 ; // Setup poll structure @@ -685,25 +1558,163 @@ int waitForInterruptSys (int pin, int mS) // Wait for it ... - return poll (&polls, 1, mS) ; + x = poll (&polls, 1, mS) ; + +// Do a dummy read to clear the interrupt +// A one character read appars to be enough. +// Followed by a seek to reset it. + + (void)read (fd, &c, 1) ; + lseek (fd, 0, SEEK_SET) ; + + return x ; } -int waitForInterruptWPi (int pin, int mS) + +/* + * interruptHandler: + * This is a thread and gets started to wait for the interrupt we're + * hoping to catch. It will call the user-function when the interrupt + * fires. + ********************************************************************************* + */ + +static void *interruptHandler (void *arg) { - return waitForInterruptSys (pinToGpio [pin & 63], mS) ; + int myPin ; + + (void)piHiPri (55) ; // Only effective if we run as root + + myPin = pinPass ; + pinPass = -1 ; + + for (;;) + if (waitForInterrupt (myPin, -1) > 0) + isrFunctions [myPin] () ; + + return NULL ; } -int waitForInterruptGpio (int pin, int mS) + +/* + * wiringPiISR: + * Pi Specific. + * Take the details and create an interrupt handler that will do a call- + * back to the user supplied function. + ********************************************************************************* + */ + +int wiringPiISR (int pin, int mode, void (*function)(void)) { - return waitForInterruptSys (pin, mS) ; + pthread_t threadId ; + const char *modeS ; + char fName [64] ; + char pinS [8] ; + pid_t pid ; + int count, i ; + char c ; + int bcmGpioPin ; + + if ((pin < 0) || (pin > 63)) + return wiringPiFailure (WPI_FATAL, "wiringPiISR: pin must be 0-63 (%d)\n", pin) ; + + /**/ if (wiringPiMode == WPI_MODE_UNINITIALISED) + return wiringPiFailure (WPI_FATAL, "wiringPiISR: wiringPi has not been initialised. Unable to continue.\n") ; + else if (wiringPiMode == WPI_MODE_PINS) + bcmGpioPin = pinToGpio [pin] ; + else if (wiringPiMode == WPI_MODE_PHYS) + bcmGpioPin = physToGpio [pin] ; + else + bcmGpioPin = pin ; + +// Now export the pin and set the right edge +// We're going to use the gpio program to do this, so it assumes +// a full installation of wiringPi. It's a bit 'clunky', but it +// is a way that will work when we're running in "Sys" mode, as +// a non-root user. (without sudo) + + if (mode != INT_EDGE_SETUP) + { + /**/ if (mode == INT_EDGE_FALLING) + modeS = "falling" ; + else if (mode == INT_EDGE_RISING) + modeS = "rising" ; + else + modeS = "both" ; + + sprintf (pinS, "%d", bcmGpioPin) ; + + if ((pid = fork ()) < 0) // Fail + return wiringPiFailure (WPI_FATAL, "wiringPiISR: fork failed: %s\n", strerror (errno)) ; + + if (pid == 0) // Child, exec + { + /**/ if (access ("/usr/local/bin/gpio", X_OK) == 0) + { + execl ("/usr/local/bin/gpio", "gpio", "edge", pinS, modeS, (char *)NULL) ; + return wiringPiFailure (WPI_FATAL, "wiringPiISR: execl failed: %s\n", strerror (errno)) ; + } + else if (access ("/usr/bin/gpio", X_OK) == 0) + { + execl ("/usr/bin/gpio", "gpio", "edge", pinS, modeS, (char *)NULL) ; + return wiringPiFailure (WPI_FATAL, "wiringPiISR: execl failed: %s\n", strerror (errno)) ; + } + else + return wiringPiFailure (WPI_FATAL, "wiringPiISR: Can't find gpio program\n") ; + } + else // Parent, wait + wait (NULL) ; + } + +// Now pre-open the /sys/class node - but it may already be open if +// we are in Sys mode... + + if (sysFds [bcmGpioPin] == -1) + { + sprintf (fName, "/sys/class/gpio/gpio%d/value", bcmGpioPin) ; + if ((sysFds [bcmGpioPin] = open (fName, O_RDWR)) < 0) + return wiringPiFailure (WPI_FATAL, "wiringPiISR: unable to open %s: %s\n", fName, strerror (errno)) ; + } + +// Clear any initial pending interrupt + + ioctl (sysFds [bcmGpioPin], FIONREAD, &count) ; + for (i = 0 ; i < count ; ++i) + read (sysFds [bcmGpioPin], &c, 1) ; + + isrFunctions [pin] = function ; + + pthread_mutex_lock (&pinMutex) ; + pinPass = pin ; + pthread_create (&threadId, NULL, interruptHandler, NULL) ; + while (pinPass != -1) + delay (1) ; + pthread_mutex_unlock (&pinMutex) ; + + return 0 ; } +/* + * initialiseEpoch: + * Initialise our start-of-time variable to be the current unix + * time in milliseconds and microseconds. + ********************************************************************************* + */ + +static void initialiseEpoch (void) +{ + struct timeval tv ; + + gettimeofday (&tv, NULL) ; + epochMilli = (uint64_t)tv.tv_sec * (uint64_t)1000 + (uint64_t)(tv.tv_usec / 1000) ; + epochMicro = (uint64_t)tv.tv_sec * (uint64_t)1000000 + (uint64_t)(tv.tv_usec) ; +} /* * delay: - * Wait for some number of milli seconds + * Wait for some number of milliseconds ********************************************************************************* */ @@ -730,31 +1741,30 @@ void delay (unsigned int howLong) * somewhat sub-optimal in that it uses 100% CPU, something not an issue * in a microcontroller, but under a multi-tasking, multi-user OS, it's * wastefull, however we've no real choice )-: + * + * Plan B: It seems all might not be well with that plan, so changing it + * to use gettimeofday () and poll on that instead... ********************************************************************************* */ -void delayMicrosecondsSys (unsigned int howLong) -{ - struct timespec sleeper, dummy ; - - sleeper.tv_sec = 0 ; - sleeper.tv_nsec = (long)(howLong * 1000) ; - - nanosleep (&sleeper, &dummy) ; -} - void delayMicrosecondsHard (unsigned int howLong) { - *(timer + TIMER_LOAD) = howLong ; - *(timer + TIMER_IRQ_CLR) = 0 ; + struct timeval tNow, tLong, tEnd ; - while (*timerIrqRaw == 0) - ; + gettimeofday (&tNow, NULL) ; + tLong.tv_sec = howLong / 1000000 ; + tLong.tv_usec = howLong % 1000000 ; + timeradd (&tNow, &tLong, &tEnd) ; + + while (timercmp (&tNow, &tEnd, <)) + gettimeofday (&tNow, NULL) ; } -void delayMicrosecondsWPi (unsigned int howLong) +void delayMicroseconds (unsigned int howLong) { - struct timespec sleeper, dummy ; + struct timespec sleeper ; + unsigned int uSecs = howLong % 1000000 ; + unsigned int wSecs = howLong / 1000000 ; /**/ if (howLong == 0) return ; @@ -762,9 +1772,9 @@ void delayMicrosecondsWPi (unsigned int howLong) delayMicrosecondsHard (howLong) ; else { - sleeper.tv_sec = 0 ; - sleeper.tv_nsec = (long)(howLong * 1000) ; - nanosleep (&sleeper, &dummy) ; + sleeper.tv_sec = wSecs ; + sleeper.tv_nsec = (long)(uSecs * 1000L) ; + nanosleep (&sleeper, NULL) ; } } @@ -778,13 +1788,30 @@ void delayMicrosecondsWPi (unsigned int howLong) unsigned int millis (void) { struct timeval tv ; - unsigned long long t1 ; + uint64_t now ; gettimeofday (&tv, NULL) ; + now = (uint64_t)tv.tv_sec * (uint64_t)1000 + (uint64_t)(tv.tv_usec / 1000) ; + + return (uint32_t)(now - epochMilli) ; +} - t1 = (tv.tv_sec * 1000000 + tv.tv_usec) / 1000 ; - return (uint32_t)(t1 - epoch) ; +/* + * micros: + * Return a number of microseconds as an unsigned int. + ********************************************************************************* + */ + +unsigned int micros (void) +{ + struct timeval tv ; + uint64_t now ; + + gettimeofday (&tv, NULL) ; + now = (uint64_t)tv.tv_sec * (uint64_t)1000000 + (uint64_t)tv.tv_usec ; + + return (uint32_t)(now - epochMicro) ; } @@ -794,137 +1821,106 @@ unsigned int millis (void) * * Default setup: Initialises the system into wiringPi Pin mode and uses the * memory mapped hardware directly. + * + * Changed now to revert to "gpio" mode if we're running on a Compute Module. ********************************************************************************* */ int wiringPiSetup (void) { - int fd ; - uint8_t *gpioMem, *pwmMem, *clkMem, *padsMem, *timerMem ; - struct timeval tv ; + int fd ; + int boardRev ; + int model, rev, mem, maker, overVolted ; - pinMode = pinModeWPi ; - pullUpDnControl = pullUpDnControlWPi ; - digitalWrite = digitalWriteWPi ; - pwmWrite = pwmWriteWPi ; - setPadDrive = setPadDriveWPi ; - digitalRead = digitalReadWPi ; - waitForInterrupt = waitForInterruptWPi ; - delayMicroseconds = delayMicrosecondsWPi ; - pwmSetMode = pwmSetModeWPi ; - pwmSetRange = pwmSetRangeWPi ; - -// Open the master /dev/memory device + if (getenv (ENV_DEBUG) != NULL) + wiringPiDebug = TRUE ; - if ((fd = open ("/dev/mem", O_RDWR | O_SYNC) ) < 0) - { - fprintf (stderr, "wiringPiSetup: Unable to open /dev/mem: %s\n", strerror (errno)) ; - return -1 ; - } + if (getenv (ENV_CODES) != NULL) + wiringPiReturnCodes = TRUE ; -// GPIO: + if (wiringPiDebug) + printf ("wiringPi: wiringPiSetup called\n") ; -// Allocate 2 pages - 1 ... + boardRev = piBoardRev () ; - if ((gpioMem = malloc (BLOCK_SIZE + (PAGE_SIZE-1))) == NULL) + /**/ if (boardRev == 1) // A, B, Rev 1, 1.1 { - fprintf (stderr, "wiringPiSetup: malloc failed: %s\n", strerror (errno)) ; - return -1 ; + pinToGpio = pinToGpioR1 ; + physToGpio = physToGpioR1 ; } - -// ... presumably to make sure we can round it up to a whole page size - - if (((uint32_t)gpioMem % PAGE_SIZE) != 0) - gpioMem += PAGE_SIZE - ((uint32_t)gpioMem % PAGE_SIZE) ; - - gpio = (uint32_t *)mmap((caddr_t)gpioMem, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED, fd, GPIO_BASE) ; - - if ((int32_t)gpio < 0) + else // A, B, Rev 2, B+, CM, Pi2 { - fprintf (stderr, "wiringPiSetup: mmap failed: %s\n", strerror (errno)) ; - return -1 ; + pinToGpio = pinToGpioR2 ; + physToGpio = physToGpioR2 ; } -// PWM + if (piModel2) + RASPBERRY_PI_PERI_BASE = 0x3F000000 ; + else + RASPBERRY_PI_PERI_BASE = 0x20000000 ; - if ((pwmMem = malloc (BLOCK_SIZE + (PAGE_SIZE-1))) == NULL) - { - fprintf (stderr, "wiringPiSetup: pwmMem malloc failed: %s\n", strerror (errno)) ; - return -1 ; - } +// Open the master /dev/ memory control device - if (((uint32_t)pwmMem % PAGE_SIZE) != 0) - pwmMem += PAGE_SIZE - ((uint32_t)pwmMem % PAGE_SIZE) ; +// See if /dev/gpiomem exists and we can open it... - pwm = (uint32_t *)mmap(pwmMem, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED, fd, GPIO_PWM) ; + if ((fd = open ("/dev/gpiomem", O_RDWR | O_SYNC | O_CLOEXEC) ) >= 0) + RASPBERRY_PI_PERI_BASE = 0 ; - if ((int32_t)pwm < 0) - { - fprintf (stderr, "wiringPiSetup: mmap failed (pwm): %s\n", strerror (errno)) ; - return -1 ; - } - -// Clock control (needed for PWM) +// ... otherwise fall back to the original /dev/mem which requires root level access - if ((clkMem = malloc (BLOCK_SIZE + (PAGE_SIZE-1))) == NULL) + else { - fprintf (stderr, "wiringPiSetup: clkMem malloc failed: %s\n", strerror (errno)) ; - return -1 ; - } - if (((uint32_t)clkMem % PAGE_SIZE) != 0) - clkMem += PAGE_SIZE - ((uint32_t)clkMem % PAGE_SIZE) ; +// This check is here because people are too stupid to check for themselves or read +// error messages. - clk = (uint32_t *)mmap(clkMem, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED, fd, CLOCK_BASE) ; + if (geteuid () != 0) + (void)wiringPiFailure (WPI_FATAL, "wiringPiSetup: Must be root. (Did you forget sudo?)\n") ; - if ((int32_t)clk < 0) - { - fprintf (stderr, "wiringPiSetup: mmap failed (clk): %s\n", strerror (errno)) ; - return -1 ; + if ((fd = open ("/dev/mem", O_RDWR | O_SYNC | O_CLOEXEC) ) < 0) + return wiringPiFailure (WPI_ALMOST, "wiringPiSetup: Unable to open /dev/mem: %s\n", strerror (errno)) ; } - -// The drive pads - if ((padsMem = malloc (BLOCK_SIZE + (PAGE_SIZE-1))) == NULL) - { - fprintf (stderr, "wiringPiSetup: padsMem malloc failed: %s\n", strerror (errno)) ; - return -1 ; - } +// Set the offsets into the memory interface. - if (((uint32_t)padsMem % PAGE_SIZE) != 0) - padsMem += PAGE_SIZE - ((uint32_t)padsMem % PAGE_SIZE) ; + GPIO_PADS = RASPBERRY_PI_PERI_BASE + 0x00100000 ; + GPIO_CLOCK_BASE = RASPBERRY_PI_PERI_BASE + 0x00101000 ; + GPIO_BASE = RASPBERRY_PI_PERI_BASE + 0x00200000 ; + GPIO_TIMER = RASPBERRY_PI_PERI_BASE + 0x0000B000 ; + GPIO_PWM = RASPBERRY_PI_PERI_BASE + 0x0020C000 ; - pads = (uint32_t *)mmap(padsMem, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED, fd, GPIO_PADS) ; +// Map the individual hardware components - if ((int32_t)pads < 0) - { - fprintf (stderr, "wiringPiSetup: mmap failed (pads): %s\n", strerror (errno)) ; - return -1 ; - } +// GPIO: -#ifdef DEBUG_PADS - printf ("Checking pads @ 0x%08X\n", (unsigned int)pads) ; - printf (" -> %08X %08X %08X\n", *(pads + 11), *(pads + 12), *(pads + 13)) ; -#endif + gpio = (uint32_t *)mmap(0, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, GPIO_BASE) ; + if ((int32_t)gpio == -1) + return wiringPiFailure (WPI_ALMOST, "wiringPiSetup: mmap (GPIO) failed: %s\n", strerror (errno)) ; -// The system timer +// PWM - if ((timerMem = malloc (BLOCK_SIZE + (PAGE_SIZE-1))) == NULL) - { - fprintf (stderr, "wiringPiSetup: timerMem malloc failed: %s\n", strerror (errno)) ; - return -1 ; - } + pwm = (uint32_t *)mmap(0, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, GPIO_PWM) ; + if ((int32_t)pwm == -1) + return wiringPiFailure (WPI_ALMOST, "wiringPiSetup: mmap (PWM) failed: %s\n", strerror (errno)) ; + +// Clock control (needed for PWM) - if (((uint32_t)timerMem % PAGE_SIZE) != 0) - timerMem += PAGE_SIZE - ((uint32_t)timerMem % PAGE_SIZE) ; + clk = (uint32_t *)mmap(0, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, GPIO_CLOCK_BASE) ; + if ((int32_t)clk == -1) + return wiringPiFailure (WPI_ALMOST, "wiringPiSetup: mmap (CLOCK) failed: %s\n", strerror (errno)) ; + +// The drive pads - timer = (uint32_t *)mmap(timerMem, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED, fd, GPIO_TIMER) ; + pads = (uint32_t *)mmap(0, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, GPIO_PADS) ; + if ((int32_t)pads == -1) + return wiringPiFailure (WPI_ALMOST, "wiringPiSetup: mmap (PADS) failed: %s\n", strerror (errno)) ; - if ((int32_t)timer < 0) - { - fprintf (stderr, "wiringPiSetup: mmap failed (timer): %s\n", strerror (errno)) ; - return -1 ; - } +#ifdef USE_TIMER +// The system timer + + timer = (uint32_t *)mmap(0, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, GPIO_TIMER) ; + if ((int32_t)timer == -1) + return wiringPiFailure (WPI_ALMOST, "wiringPiSetup: mmap (TIMER) failed: %s\n", strerror (errno)) ; // Set the timer to free-running, 1MHz. // 0xF9 is 249, the timer divide is base clock / (divide+1) @@ -933,11 +1929,17 @@ int wiringPiSetup (void) *(timer + TIMER_CONTROL) = 0x0000280 ; *(timer + TIMER_PRE_DIV) = 0x00000F9 ; timerIrqRaw = timer + TIMER_IRQ_RAW ; +#endif -// Initialise our epoch for millis() + initialiseEpoch () ; - gettimeofday (&tv, NULL) ; - epoch = (tv.tv_sec * 1000000 + tv.tv_usec) / 1000 ; +// If we're running on a compute module, then wiringPi pin numbers don't really many anything... + + piBoardId (&model, &rev, &mem, &maker, &overVolted) ; + if (model == PI_MODEL_CM) + wiringPiMode = WPI_MODE_GPIO ; + else + wiringPiMode = WPI_MODE_PINS ; return 0 ; } @@ -954,21 +1956,34 @@ int wiringPiSetup (void) int wiringPiSetupGpio (void) { - int x = wiringPiSetup () ; + (void)wiringPiSetup () ; + + if (wiringPiDebug) + printf ("wiringPi: wiringPiSetupGpio called\n") ; + + wiringPiMode = WPI_MODE_GPIO ; + + return 0 ; +} + + +/* + * wiringPiSetupPhys: + * Must be called once at the start of your program execution. + * + * Phys setup: Initialises the system into Physical Pin mode and uses the + * memory mapped hardware directly. + ********************************************************************************* + */ + +int wiringPiSetupPhys (void) +{ + (void)wiringPiSetup () ; - if (x != 0) - return x ; + if (wiringPiDebug) + printf ("wiringPi: wiringPiSetupPhys called\n") ; - pinMode = pinModeGpio ; - pullUpDnControl = pullUpDnControlGpio ; - digitalWrite = digitalWriteGpio ; - pwmWrite = pwmWriteGpio ; - setPadDrive = setPadDriveGpio ; - digitalRead = digitalReadGpio ; - waitForInterrupt = waitForInterruptGpio ; - delayMicroseconds = delayMicrosecondsWPi ; // Same - pwmSetMode = pwmSetModeWPi ; - pwmSetRange = pwmSetRangeWPi ; + wiringPiMode = WPI_MODE_PHYS ; return 0 ; } @@ -985,21 +2000,31 @@ int wiringPiSetupGpio (void) int wiringPiSetupSys (void) { + int boardRev ; int pin ; - struct timeval tv ; char fName [128] ; - pinMode = pinModeSys ; - pullUpDnControl = pullUpDnControlSys ; - digitalWrite = digitalWriteSys ; - pwmWrite = pwmWriteSys ; - setPadDrive = setPadDriveSys ; - digitalRead = digitalReadSys ; - waitForInterrupt = waitForInterruptSys ; - delayMicroseconds = delayMicrosecondsSys ; - pwmSetMode = pwmSetModeSys ; - pwmSetRange = pwmSetRangeSys ; + if (getenv (ENV_DEBUG) != NULL) + wiringPiDebug = TRUE ; + if (getenv (ENV_CODES) != NULL) + wiringPiReturnCodes = TRUE ; + + if (wiringPiDebug) + printf ("wiringPi: wiringPiSetupSys called\n") ; + + boardRev = piBoardRev () ; + + if (boardRev == 1) + { + pinToGpio = pinToGpioR1 ; + physToGpio = physToGpioR1 ; + } + else + { + pinToGpio = pinToGpioR2 ; + physToGpio = physToGpioR2 ; + } // Open and scan the directory, looking for exported GPIOs, and pre-open // the 'value' interface to speed things up for later @@ -1010,10 +2035,9 @@ int wiringPiSetupSys (void) sysFds [pin] = open (fName, O_RDWR) ; } -// Initialise the epoch for mills() ... + initialiseEpoch () ; - gettimeofday (&tv, NULL) ; - epoch = (tv.tv_sec * 1000000 + tv.tv_usec) / 1000 ; + wiringPiMode = WPI_MODE_GPIO_SYS ; return 0 ; }