diff --git a/README.md b/README.md index de2dc3a..7125be7 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,16 @@ -# WiringPi +# WiringPi Library +Welcome to the WiringPi Library, the highly performant GPIO access library for Raspberry Pi boards. This library is written in C and is designed to provide fast and efficient control of the GPIO pins by directly accessing the hardware registers using DMA. -WiringPi is a _performant_ GPIO access library written in C for Raspberry Pi boards. +**Key Features:** +- **Support:** WiringPi supports all Raspberry Pi Boards including Pi 5 ( :construction: On the Pi 5, only the GCLK functionality is currently not supported due to missing documentation of the RP1 chip). +- **High Performance:** By directly accessing the hardware registers, WiringPi ensures minimal latency and maximum performance for your GPIO operations. +- **Wide Adoption:** WiringPi is widely used in numerous projects, making it a reliable choice for your Raspberry Pi GPIO needs. -:warning:️ :construction: on Pi5, PWM support is currently under development and _will not work at this point_. If you're interested in the progress, please check the [corresponding issue](https://github.com/GrazerComputerClub/WiringPi/issues/21). +Whether you’re working on a simple LED blink project or a complex automation system, WiringPi provides the tools you need to get the job done efficiently. +## How to use -To compile programs with wiringPi, you need to include `wiringPi.h` as well as link against `wiringPi`: +To compile programs with wiringPi Library, you need to include `wiringPi.h` as well as link against `wiringPi`: ```c #include // Include WiringPi library! @@ -143,7 +148,7 @@ Please don't email GC2 for reporting issues, you might [contact us](mailto:wirin ## History -This repository is the continuation of 'Gordon's wiringPi' which has been [deprecated](https://web.archive.org/web/20220405225008/http://wiringpi.com/wiringpi-deprecated/), a while ago. +This repository is the continuation of 'Gordon's wiringPi 2.5' which has been [deprecated](https://web.archive.org/web/20220405225008/http://wiringpi.com/wiringpi-deprecated/), a while ago. * The last "old wiringPi" source of Gordon's release can be found at the [`final_source_2.50`](https://github.com/WiringPi/WiringPi/tree/final_official_2.50) tag. diff --git a/VERSION b/VERSION index 3faf9b8..cc1923a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.6 \ No newline at end of file +3.8 diff --git a/gpio/gpio.c b/gpio/gpio.c index d468ed9..a563b24 100644 --- a/gpio/gpio.c +++ b/gpio/gpio.c @@ -816,7 +816,7 @@ static void doPwmClock (int argc, char *argv []) if ((clock < 1) || (clock > 4095)) { - fprintf (stderr, "%s: clock must be between 0 and 4096\n", argv [0]) ; + fprintf (stderr, "%s: pwm clock must be between 1 and 4095\n", argv [0]) ; exit (1) ; } diff --git a/version.h b/version.h index cfc643f..b79a6e9 100644 --- a/version.h +++ b/version.h @@ -1,3 +1,3 @@ -#define VERSION "3.6" +#define VERSION "3.8" #define VERSION_MAJOR 3 -#define VERSION_MINOR 6 \ No newline at end of file +#define VERSION_MINOR 8 diff --git a/wiringPi/test/Makefile b/wiringPi/test/Makefile index cbcd3d8..7c379a7 100644 --- a/wiringPi/test/Makefile +++ b/wiringPi/test/Makefile @@ -1,47 +1,64 @@ CC = gcc CFLAGS = -Wall -LDFLAGS = +LDFLAGS = -tests = wiringpi_test1_sysfs wiringpi_test2_sysfs wiringpi_test3_device_wpi wiringpi_test4_device_phys wiringpi_test5_default wiringpi_test6_isr wiringpi_test7_version +# Need BCM19 <-> BCM26, +PWM: BCM12 <-> BCM13, BCM18 <-> BCM17 connected (1kOhm) +tests = wiringpi_test1_sysfs wiringpi_test2_sysfs wiringpi_test3_device_wpi wiringpi_test4_device_phys wiringpi_test5_default wiringpi_test6_isr wiringpi_test7_version wiringpi_test8_pwm wiringpi_test9_pwm -xotests = wiringpi_xotest_test1_spi +# Need XO hardware +xotests = wiringpi_xotest_test1_spi wiringpi_i2c_test1_pcf8574 wiringpi_test8_pwm wiringpi_test9_pwm -i2ctests = wiringpi_i2c_test1_pcf8574 +# Need PiFace hardware and BCM23 <-> BCM24 , BCM18 <-> BCM17 connected (1kOhm), and PiFace Out7<->In4, Out6<->In5, R0_NO<->In6, R0_NO<->In7, R_C<-100Ohm->GND +pifacetests = wiringpi_piface_test1 wiringpi_test8_pwm wiringpi_test9_pwm -all: $(tests) $(xotests) $(i2ctests) +all: $(tests) $(xotests) $(pifacetests) -wiringpi_test1_sysfs: +wiringpi_test1_sysfs: ${CC} ${CFLAGS} wiringpi_test1_sysfs.c -o wiringpi_test1_sysfs -lwiringPi -wiringpi_test2_sysfs: +wiringpi_test2_sysfs: ${CC} ${CFLAGS} wiringpi_test2_sysfs.c -o wiringpi_test2_sysfs -lwiringPi -wiringpi_test3_device_wpi: +wiringpi_test3_device_wpi: ${CC} ${CFLAGS} wiringpi_test3_device_wpi.c -o wiringpi_test3_device_wpi -lwiringPi -wiringpi_test4_device_phys: +wiringpi_test4_device_phys: ${CC} ${CFLAGS} wiringpi_test4_device_phys.c -o wiringpi_test4_device_phys -lwiringPi -wiringpi_test5_default: +wiringpi_test5_default: ${CC} ${CFLAGS} wiringpi_test5_default.c -o wiringpi_test5_default -lwiringPi -wiringpi_test6_isr: +wiringpi_test6_isr: ${CC} ${CFLAGS} wiringpi_test6_isr.c -o wiringpi_test6_isr -lwiringPi -wiringpi_test7_version: +wiringpi_test7_version: ${CC} ${CFLAGS} wiringpi_test7_version.c -o wiringpi_test7_version -lwiringPi +wiringpi_test8_pwm: + ${CC} ${CFLAGS} wiringpi_test8_pwm.c -o wiringpi_test8_pwm -lwiringPi + +wiringpi_test9_pwm: + ${CC} ${CFLAGS} wiringpi_test9_pwm.c -o wiringpi_test9_pwm -lwiringPi + +wiringpi_piface_test1: + ${CC} ${CFLAGS} wiringpi_piface_test1.c -o wiringpi_piface_test1 -lwiringPi -lwiringPiDev + wiringpi_xotest_test1_spi: ${CC} ${CFLAGS} wiringpi_xotest_test1_spi.c -o wiringpi_xotest_test1_spi -lwiringPi wiringpi_i2c_test1_pcf8574: ${CC} ${CFLAGS} wiringpi_i2c_test1_pcf8574.c -o wiringpi_i2c_test1_pcf8574 -lwiringPi + test: @error_state=false ; \ for t in $(tests) ; do \ echo === unit test: $${t} === ; \ - time ./$${t}; \ + if [ $${t} = *"8_pwm" ] || [ $${t} = *"9_pwm" ]; then \ + time sudo ./$${t} ; \ + else \ + time ./$${t} ; \ + fi ; \ if [ $$? -ne 0 ]; then \ error_state=true ; \ fi ; \ @@ -57,7 +74,11 @@ xotest: @error_state=false ; \ for t in $(tests) $(xotests) ; do \ echo === XO unit test: $${t} === ; \ - time ./$${t} ; \ + if [ $${t} = *"8_pwm" ] || [ $${t} = *"9_pwm" ]; then \ + time sudo ./$${t} ; \ + else \ + time ./$${t} ; \ + fi ; \ if [ $$? -ne 0 ]; then \ error_state=true ; \ fi ; \ @@ -69,23 +90,27 @@ xotest: echo "\n\e[5mSTD/XO TEST SUCCESS\e[0m\n"; \ fi -i2ctest: +pifacetest: @error_state=false ; \ - for t in $(tests) $(i2ctests) ; do \ - echo === I2C unit test: $${t} === ; \ - time ./$${t} ; \ + for t in $(tests) $(pifacetests) ; do \ + echo === PiFace unit test: $${t} === ; \ + if [ $${t} = *"8_pwm" ] || [ $${t} = *"9_pwm" ]; then \ + time sudo ./$${t} ; \ + else \ + time ./$${t} ; \ + fi ; \ if [ $$? -ne 0 ]; then \ error_state=true ; \ fi ; \ echo ; echo ; \ done if [ "$$error_state" = true ]; then \ - echo "\n\e[5mSTD/I2C TEST FAILED\e[0m\n"; \ + echo "\n\e[5mPIFACE TEST FAILED\e[0m\n"; \ else \ - echo "\n\e[5mSTD/I2C TEST SUCCESS\e[0m\n"; \ + echo "\n\e[5mPIFACE TEST SUCCESS\e[0m\n"; \ fi -clean: - for t in $(tests) $(xotests) $(i2ctests) ; do \ +clean: + for t in $(tests) $(xotests) $(pifacetests) ; do \ rm -fv $${t} ; \ done diff --git a/wiringPi/test/wiringpi_piface_test1.c b/wiringPi/test/wiringpi_piface_test1.c new file mode 100644 index 0000000..78619f3 --- /dev/null +++ b/wiringPi/test/wiringpi_piface_test1.c @@ -0,0 +1,133 @@ +// WiringPi piface program IN, OUT, PULL +// Compile: gcc -Wall wiringpi_piface_test1.c -o wiringpi_piface_test1 -lwiringPi -lwiringPiDev + +#include "wpi_test.h" +#include + +// Use 200 as the pin-base for the PiFace board, and change all pins +// for the LED and relays + +const int PIFACE = 200; //Mapped wiringpi IO address +const int defaultsleep = 200000; // 200 ms + +void ReadUntilTimeout(int GPIO, int expect, int timeoutSec) { + const int intervaluS = 250000; //250ms + int in; + const char* strexpect = expect ? "HIGH" : "LOW"; + for(int loop=0, end=(timeoutSec*1000000/intervaluS); loop test not possible + } + //6 & 7 connected from relais NO (normaly open) to in -> test possible + delayMicroseconds(defaultsleep); + pullUpDnControl (PIFACE + IN, PUD_UP) ; + ReadUntilTimeout(PIFACE + IN, HIGH, 2) ; + pullUpDnControl (PIFACE + IN, PUD_DOWN) ; + // cool down very slowly, connect 680 kOhm pull down resistor to make ist faster + ReadUntilTimeout(PIFACE + IN, LOW, 60) ; + pullUpDnControl (PIFACE + IN, PUD_UP) ; // finally up + ReadUntilTimeout(PIFACE + IN, HIGH, 2) ; + } + + return UnitTestState(); +} diff --git a/wiringPi/test/wiringpi_test1_sysfs.c b/wiringPi/test/wiringpi_test1_sysfs.c index 45a62fe..a97f85e 100644 --- a/wiringPi/test/wiringpi_test1_sysfs.c +++ b/wiringPi/test/wiringpi_test1_sysfs.c @@ -7,8 +7,8 @@ #include -const int GPIO = 19; -const int GPIOIN = 26; +int GPIO = 19; +int GPIOIN = 26; const int ToggleValue = 4; @@ -21,6 +21,11 @@ int main (void) { printf("wiringPiSetupSys failed\n\n"); exit(EXIT_FAILURE); } + if (!piBoard40Pin()) { + GPIO = 23; + GPIOIN = 24; + } + pinMode(GPIOIN, INPUT); pinMode(GPIO, OUTPUT); diff --git a/wiringPi/test/wiringpi_test2_sysfs.c b/wiringPi/test/wiringpi_test2_sysfs.c index 0e5a0d2..76c48d3 100644 --- a/wiringPi/test/wiringpi_test2_sysfs.c +++ b/wiringPi/test/wiringpi_test2_sysfs.c @@ -7,8 +7,8 @@ #include -const int GPIO = 19; -const int GPIOIN = 26; +int GPIO = 19; +int GPIOIN = 26; const int ToggleValue = 4; @@ -21,6 +21,11 @@ int main (void) { printf("wiringPiSetupSys failed\n\n"); exit(EXIT_FAILURE); } + if (!piBoard40Pin()) { + GPIO = 23; + GPIOIN = 24; + } + pinMode(GPIOIN, INPUT); pinMode(GPIO, OUTPUT); diff --git a/wiringPi/test/wiringpi_test3_device_wpi.c b/wiringPi/test/wiringpi_test3_device_wpi.c index 24af1bc..4176459 100644 --- a/wiringPi/test/wiringpi_test3_device_wpi.c +++ b/wiringPi/test/wiringpi_test3_device_wpi.c @@ -7,8 +7,8 @@ #include -const int GPIO = 24; //BCM 19 -const int GPIOIN = 25; //BCM 26; +int GPIO = 24; //BCM 19 +int GPIOIN = 25; //BCM 26; const int ToggleValue = 4; @@ -21,6 +21,11 @@ int main (void) { printf("wiringPiSetupGpioDevice failed\n\n"); exit(EXIT_FAILURE); } + if (!piBoard40Pin()) { + GPIO = 4; //BCM 23 + GPIOIN = 5; //BCM 24 + } + pinMode(GPIOIN, INPUT); pinMode(GPIO, OUTPUT); diff --git a/wiringPi/test/wiringpi_test4_device_phys.c b/wiringPi/test/wiringpi_test4_device_phys.c index ecd07a5..31157b8 100644 --- a/wiringPi/test/wiringpi_test4_device_phys.c +++ b/wiringPi/test/wiringpi_test4_device_phys.c @@ -7,8 +7,8 @@ #include -const int GPIO = 35; //BCM 19 -const int GPIOIN = 37; //BCM 26; +int GPIO = 35; //BCM 19 +int GPIOIN = 37; //BCM 26; const int ToggleValue = 4; @@ -21,6 +21,11 @@ int main (void) { printf("wiringPiSetupGpioDevice failed\n\n"); exit(EXIT_FAILURE); } + if (!piBoard40Pin()) { + GPIO = 16; //BCM 23 + GPIOIN = 18; //BCM 24 + } + pinMode(GPIOIN, INPUT); pinMode(GPIO, OUTPUT); diff --git a/wiringPi/test/wiringpi_test5_default.c b/wiringPi/test/wiringpi_test5_default.c index 8223090..ecf80bf 100644 --- a/wiringPi/test/wiringpi_test5_default.c +++ b/wiringPi/test/wiringpi_test5_default.c @@ -7,8 +7,8 @@ #include -const int GPIO = 19; -const int GPIOIN = 26; +int GPIO = 19; +int GPIOIN = 26; const int ToggleValue = 4; int RaspberryPiModel = -1; @@ -58,6 +58,10 @@ int main (void) { } else { printf("Raspberry Pi with BCM GPIO found (not Pi 5)\n"); } + if (!piBoard40Pin()) { + GPIO = 23; + GPIOIN = 24; + } enum WPIPinAlt AltGpio = WPI_ALT_UNKNOWN; diff --git a/wiringPi/test/wiringpi_test6_isr.c b/wiringPi/test/wiringpi_test6_isr.c index 24f1496..644fa50 100644 --- a/wiringPi/test/wiringpi_test6_isr.c +++ b/wiringPi/test/wiringpi_test6_isr.c @@ -8,8 +8,8 @@ #include -const int GPIO = 19; -const int GPIOIN = 26; +int GPIO = 19; +int GPIOIN = 26; const int ToggleValue = 4; @@ -115,55 +115,61 @@ double DurationTime(int Enge, int OUTpin, int IRQpin) { return fTime; } -int main (void) { - const int IRQpin = GPIOIN ; - const int OUTpin = GPIO ; - int major, minor; - wiringPiVersion(&major, &minor); +int main (void) { + + int major, minor; + + wiringPiVersion(&major, &minor); printf("WiringPi GPIO test program 1 (using GPIO%d (output) and GPIO%d (input))\n", GPIO, GPIOIN); printf(" testing irq\n"); - printf("\nISR test (WiringPi %d.%d)\n", major, minor); + printf("\nISR test (WiringPi %d.%d)\n", major, minor); - wiringPiSetupGpio() ; - - pinMode(IRQpin, INPUT); - pinMode(OUTpin, OUTPUT); - digitalWrite (OUTpin, LOW) ; + wiringPiSetupGpio() ; + if (!piBoard40Pin()) { + GPIO = 23; + GPIOIN = 24; + } + int IRQpin = GPIOIN ; + int OUTpin = GPIO ; + + pinMode(IRQpin, INPUT); + pinMode(OUTpin, OUTPUT); + digitalWrite (OUTpin, LOW) ; - printf("Testing IRQ @ GPIO%d with trigger @ GPIO%d rising\n", IRQpin, OUTpin); - wiringPiISR (IRQpin, INT_EDGE_RISING, &wfi) ; - sleep(1); - StartSequence (INT_EDGE_RISING, OUTpin); - printf("Testing close\n"); - - wiringPiISRStop (IRQpin) ; + printf("Testing IRQ @ GPIO%d with trigger @ GPIO%d rising\n", IRQpin, OUTpin); + wiringPiISR (IRQpin, INT_EDGE_RISING, &wfi) ; + sleep(1); + StartSequence (INT_EDGE_RISING, OUTpin); + printf("Testing close\n"); - printf("Testing IRQ @ GPIO%d with trigger @ GPIO%d falling\n", IRQpin, OUTpin); - wiringPiISR (IRQpin, INT_EDGE_FALLING, &wfi) ; - sleep(1); - StartSequence (INT_EDGE_FALLING, OUTpin); - printf("Testing close\n"); - wiringPiISRStop (IRQpin) ; + wiringPiISRStop (IRQpin) ; - printf("Testing IRQ @ GPIO%d with trigger @ GPIO%d both\n", IRQpin, OUTpin); - wiringPiISR (IRQpin, INT_EDGE_BOTH, &wfi) ; - sleep(1); - StartSequence (INT_EDGE_BOTH, OUTpin); - printf("Testing close\n"); - wiringPiISRStop (IRQpin) ; + printf("Testing IRQ @ GPIO%d with trigger @ GPIO%d falling\n", IRQpin, OUTpin); + wiringPiISR (IRQpin, INT_EDGE_FALLING, &wfi) ; + sleep(1); + StartSequence (INT_EDGE_FALLING, OUTpin); + printf("Testing close\n"); + wiringPiISRStop (IRQpin) ; - for (int count=0; count<2; count++) { - printf("Measuring duration IRQ @ GPIO%d with trigger @ GPIO%d rising\n", IRQpin, OUTpin); - DurationTime(INT_EDGE_RISING, OUTpin, IRQpin); + printf("Testing IRQ @ GPIO%d with trigger @ GPIO%d both\n", IRQpin, OUTpin); + wiringPiISR (IRQpin, INT_EDGE_BOTH, &wfi) ; + sleep(1); + StartSequence (INT_EDGE_BOTH, OUTpin); + printf("Testing close\n"); + wiringPiISRStop (IRQpin) ; - printf("Measuring duration IRQ @ GPIO%d with trigger @ GPIO%d falling\n", IRQpin, OUTpin); - DurationTime(INT_EDGE_FALLING, OUTpin, IRQpin); - } - pinMode(OUTpin, INPUT); + for (int count=0; count<2; count++) { + printf("Measuring duration IRQ @ GPIO%d with trigger @ GPIO%d rising\n", IRQpin, OUTpin); + DurationTime(INT_EDGE_RISING, OUTpin, IRQpin); - return UnitTestState(); + printf("Measuring duration IRQ @ GPIO%d with trigger @ GPIO%d falling\n", IRQpin, OUTpin); + DurationTime(INT_EDGE_FALLING, OUTpin, IRQpin); + } + pinMode(OUTpin, INPUT); + + return UnitTestState(); } diff --git a/wiringPi/test/wiringpi_test8_pwm.c b/wiringPi/test/wiringpi_test8_pwm.c new file mode 100644 index 0000000..b690fc5 --- /dev/null +++ b/wiringPi/test/wiringpi_test8_pwm.c @@ -0,0 +1,187 @@ +// WiringPi test program: PWM test +// Compile: gcc -Wall wiringpi_test8_pwm.c -o wiringpi_test8_pwm -lwiringPi + +#include "wpi_test.h" +#include +#include +#include +#include +#include +#include + +int PWM_OUT[4] = { 18, 12, 13, 19 }; +int PWM_IN[4] = { 17, 13, 12, 26 }; + +volatile int gCounter = 0; + +//Interrupt Service Routine for FREQIN +void ISR_FREQIN(void) { + gCounter++; +} + +double MeasureAndCheckFreqTolerance(const char* msg, double expect_freq, int tolerance) { + double fFrequency; + clock_t CPUClockBegin, CPUClockEnd; + int CountBegin, CountEnd; + double CPUClockInterval, CountInterval; + double elapsed_time, CPULoad; + uint64_t tbegin, tend; + int SleepMs = 1200; + + CPUClockBegin = clock(); + tbegin = piMicros64(); + CountBegin = gCounter; + delay(SleepMs); + CountEnd = gCounter; + CPUClockEnd = clock(); + tend = piMicros64(); + + elapsed_time = (double)(tend-tbegin)/1.0e6; + CountInterval = CountEnd - CountBegin; + CPUClockInterval = CPUClockEnd - CPUClockBegin; + CPULoad = CPUClockInterval*100.0 / CLOCKS_PER_SEC / elapsed_time; + fFrequency = CountInterval / elapsed_time / 1000; + + printf("\nInterval: time: %.6f sec (CPU: %3.1f %%), count: %g -> frequency: %.3f kHz\n", + elapsed_time, CPULoad, CountInterval, fFrequency); + + CheckSameDouble("Wait for freq. meas.", elapsed_time, SleepMs/1000.0, 0.1); //100ms tolerance. maybe problematic on high freq/cpu load + CheckSameDouble(msg, fFrequency, expect_freq, (expect_freq!=0.0) ? expect_freq*tolerance/100 : 0.1); //x% tolerance + return fFrequency; +} + +double MeasureAndCheckFreq(const char* msg, double expect_freq) { + return MeasureAndCheckFreqTolerance(msg, expect_freq, 2); +} + + +int main (void) { + + int major, minor; + int PWM, FREQIN; + + wiringPiVersion(&major, &minor); + + printf("WiringPi PWM GPIO test program 8\n"); + printf("PWM/ISR test (WiringPi %d.%d)\n", major, minor); + + wiringPiSetupGpio() ; + + int rev, mem, maker, overVolted, RaspberryPiModel; + piBoardId(&RaspberryPiModel, &rev, &mem, &maker, &overVolted); + CheckNotSame("Model: ", RaspberryPiModel, -1); + + PWM = 18; + FREQIN = 17; + + printf("Register ISR@%d\n", PWM); + // INT_EDGE_BOTH, INT_EDGE_FALLING, INT_EDGE_RISING only one ISR per input + int result = wiringPiISR(FREQIN, INT_EDGE_RISING, &ISR_FREQIN); + CheckSame("Register ISR", result, 0); + if (result < 0) { + printf("Unable to setup ISR for GPIO %d (%s)\n\n", FREQIN, strerror(errno)); + return UnitTestState(); + } + + printf("\n==> Set pwm 0%% and enable PWM output with PWM_OUTPUT (default mode)\n"); + pwmWrite(PWM, 0); // <-- Allways start with 0 Hz + pinMode(PWM, PWM_OUTPUT); //Mode BAL, pwmr=1024, pwmc=32 + delay(250); + double duty_fact = 0.0; + double freq = 0.0; + MeasureAndCheckFreq("PMW Pi0-4:BAL/Pi5:MS without change", freq); + + printf("Keep pwm 0%% and set mode MS\n"); + pwmSetMode(PWM_MODE_MS); + delay(250); + MeasureAndCheckFreq("PWM MS without change", freq); + + int pwmc; + int pwmr; + int pwm; + + if (RaspberryPiModel!=PI_MODEL_5) { + pwmSetMode(PWM_MODE_BAL); + pwmc = 1000; + pwmr = 1024; + pwm = 512; + duty_fact = (double)pwm/(double)pwmr; + printf("\n==> set mode BAL, pwmc=%d, pwmr=%d, pwm=%d, duty=%g%%\n", pwmc, pwmr, pwm, duty_fact*100); + pwmSetClock(pwmc); + pwmSetRange(pwmr); + pwmWrite(PWM, pwm); + delay(250); + freq = 19200.0/pwmc*duty_fact; + MeasureAndCheckFreq("PWM BAL with settings", freq); + } + + pwmSetMode(PWM_MODE_MS); + pwmc = 10; + pwmr = 256; + pwm = 171; + duty_fact = (double)pwm/(double)pwmr; + printf("\n==> set mode MS, pwmc=%d, pwmr=%d, pwm=%d, duty=%g%%\n", pwmc, pwmr, pwm, duty_fact*100); + pwmSetClock(pwmc); + pwmSetRange(pwmr); + pwmWrite(PWM, pwm); + delay(250); + freq = 19200.0/(double)pwmc/(double)pwmr; + MeasureAndCheckFreq("PWM BAL with settings", freq); + + printf("set PWM@GPIO%d (output) off\n", PWM); + pinMode(PWM, PM_OFF); + delay(1000); + MeasureAndCheckFreq("PMW off", 0.0); + + if (RaspberryPiModel!=PI_MODEL_5) { + pwmc = 800; + pwmr = 2048; + pwm = 768; + duty_fact = (double)pwm/(double)pwmr; + printf("\n==> set mode PWM_BAL_OUTPUT, pwmc=%d, pwmr=%d, pwm=%d, duty=%g%%\n", pwmc, pwmr, pwm, duty_fact*100); + pwmSetRange(pwmr); + pwmSetClock(pwmc); + pwmWrite(PWM, pwm); + pinMode(PWM, PWM_BAL_OUTPUT); + delay(250); + freq = 19200.0/pwmc*duty_fact; + MeasureAndCheckFreq("PMW BAL start values", freq); + } + + printf("set PWM@GPIO%d (output) off\n", PWM); + pinMode(PWM, PM_OFF); + delay(1000); + MeasureAndCheckFreq("PMW off", 0.0); + + printf("Set pwm settings and enable PWM\n"); + //pwmc = 5; //Problem with Pi0/1 after setting, PWM stops working, maybe IRQ problem or PWM BAL with that high freq (>2 MHz) + pwmc = 35; //PWM BAL would be >400 kHz + pwmr = 1024; + pwm = 768; + duty_fact = (double)pwm/(double)pwmr; + printf("\n==> set mode PWM_MS_OUTPUT, pwmc=%d, pwmr=%d, pwm=%d, duty=%g%%\n", pwmc, pwmr, pwm, duty_fact*100); + pwmSetRange(pwmr); + pwmSetClock(pwmc); + pwmWrite(PWM, pwm); + pinMode(PWM, PWM_MS_OUTPUT); + delay(250); + freq = 19200.0/(double)pwmc/(double)pwmr; + MeasureAndCheckFreq("PMW MS start values", freq); + + printf("set PWM@GPIO%d (output) off\n", PWM); + pinMode(PWM, PM_OFF); + delay(1000); + MeasureAndCheckFreq("PMW off", 0.0); + + printf("set PWM0 CLK off @ Pi5\n"); + pwmSetClock(0); + + result = wiringPiISRStop(FREQIN); + CheckSame("\n\nRelease ISR", result, 0); + if (result < 0) { + printf("Unable to release ISR for GPIO %d (%s)\n\n", FREQIN, strerror(errno)); + return UnitTestState(); + } + + return UnitTestState(); +} diff --git a/wiringPi/test/wiringpi_test9_pwm.c b/wiringPi/test/wiringpi_test9_pwm.c new file mode 100644 index 0000000..19baa12 --- /dev/null +++ b/wiringPi/test/wiringpi_test9_pwm.c @@ -0,0 +1,225 @@ +// WiringPi test program: PWM test +// Compile: gcc -Wall wiringpi_test9_pwm.c -o wiringpi_test9_pwm -lwiringPi + +#include "wpi_test.h" +#include +#include +#include +#include +#include +#include + +int PWM_OUT[4] = { 18, 12, 13, 19 }; +int PWM_IN[4] = { 17, 13, 12, 26 }; + +volatile int gCounter = 0; + +//Interrupt Service Routine for FREQIN +void ISR_FREQIN(void) { + gCounter++; +} + +double MeasureAndCheckFreq(const char* msg, double expect_freq) { + double fFrequency; + clock_t CPUClockBegin, CPUClockEnd; + int CountBegin, CountEnd; + double CPUClockInterval, CountInterval; + double elapsed_time, CPULoad; + uint64_t tbegin, tend; + int SleepMs = 1200; + + CPUClockBegin = clock(); + tbegin = piMicros64(); + CountBegin = gCounter; + delay(SleepMs); + CountEnd = gCounter; + CPUClockEnd = clock(); + tend = piMicros64(); + + elapsed_time = (double)(tend-tbegin)/1.0e6; + CountInterval = CountEnd - CountBegin; + CPUClockInterval = CPUClockEnd - CPUClockBegin; + CPULoad = CPUClockInterval*100.0 / CLOCKS_PER_SEC / elapsed_time; + fFrequency = CountInterval / elapsed_time / 1000; + + printf("\nInterval: time: %.6f sec (CPU: %3.1f %%), count: %g -> frequency: %.3f kHz\n", + elapsed_time, CPULoad, CountInterval, fFrequency); + + CheckSameDouble("Wait for freq. meas.", elapsed_time, SleepMs/1000.0, 0.1); //100ms tolerance. maybe problematic on high freq/cpu load + CheckSameDouble(msg, fFrequency, expect_freq, expect_freq*2/100); //2% toleranc + return fFrequency; +} + + +int tests_pwmc[7] = {1456, 1000, 512, 200, 2000, 3000, 4000}; +int tests_duty[7] = { 512, 768, 682, 922, 256, 341, 102}; +int tests_pwmr[12]= { 50, 100, 200, 512, 1024, 1456, 2000, 3000, 5000, 10000, 15000, 20000}; +int tests_pwm[3] = { 50, 25, 75}; + +int main (void) { + + int major, minor; + char msg[255]; + int testruns = 4; + int PWM, FREQIN; + + wiringPiVersion(&major, &minor); + + printf("WiringPi GPIO test program 9\n"); + printf("PWM/ISR test (WiringPi %d.%d)\n", major, minor); + + wiringPiSetupGpio() ; + + int rev, mem, maker, overVolted, RaspberryPiModel; + piBoardId(&RaspberryPiModel, &rev, &mem, &maker, &overVolted); + CheckNotSame("Model: ", RaspberryPiModel, -1); + int Pi4 = 0; + int Pi5 = 0; + double MaxFreq = 100.0; + switch(RaspberryPiModel) { + case PI_MODEL_A: + case PI_MODEL_B: + case PI_MODEL_BP: + case PI_MODEL_AP: + case PI_MODEL_ZERO: + case PI_MODEL_ZERO_W: + case PI_MODEL_CM: + MaxFreq = 13.0; // 12.5 kHz -> ~40% CPU@800 MHz + printf(" - Pi1/BCM2835 detected, will skip tests with frequency above %g kHz\n", MaxFreq); + break; + case PI_MODEL_2: + MaxFreq = 20.0; + printf(" - Pi2/BCM2836 detected, will skip tests with frequency above %g kHz\n", MaxFreq); + break; + case PI_MODEL_3B: + case PI_MODEL_CM3: + case PI_MODEL_3BP: + case PI_MODEL_3AP: + case PI_MODEL_CM3P: + case PI_MODEL_ZERO_2W: + MaxFreq = 50.0; + printf(" - Pi3/BCM2837 detected, will skip tests with frequency above %g kHz\n", MaxFreq); + break; + case PI_MODEL_4B: + case PI_MODEL_400: + case PI_MODEL_CM4: + case PI_MODEL_CM4S: + Pi4 = 1; + break; + case PI_MODEL_5: + Pi5 = 1; + break; + } + + if (!piBoard40Pin()) { + testruns = 1; // only fist PWM0, supported + } + + for (int testrun=0; testrun(pmwr/2)) { + tests_duty_corr = pmwr-tests_duty[c_duty]; + } else { + tests_duty_corr = tests_duty[c_duty]; + } + + double duty_fact = tests_duty_corr/(double)pmwr; + printf("\n%d/%d set duty %d/%d\n",c_duty+1, c_duty_end, tests_duty[c_duty], pmwr); + pwmWrite(PWM, tests_duty[c_duty]); + + for (int c_pwmc=0, end = sizeof(tests_pwmc)/sizeof(tests_pwmc[0]); c_pwmc1456) { + printf("* Set clock (pwmc) %d not possible on BCM2711 system (OSC 54 MHz), ignore\n", pwmc); + continue; + } + double freq = 19200.0/pwmc*duty_fact; + if (freq>MaxFreq) { + printf("* Set clock (pwmc) %d not possible on system (to slow to measure %g kHz with ISR), ignore\n", pwmc, freq); + continue; + } + pwmSetClock(pwmc); + delay(250); + sprintf(msg, "Set Clock (pwmc) %d, %d%% duty", pwmc, tests_duty[c_duty]*100/pmwr); + MeasureAndCheckFreq(msg, freq); + } + } + } + + delay(250); + printf("\n"); + printf("*********************************\n"); + printf("* PWM MS mode *\n"); + printf("*********************************\n"); + int pwmc = 10; + printf("SetClock pwmc=%d and enable MS mode\n", pwmc); + pwmSetClock(pwmc); + pwmSetMode(PWM_MODE_MS); + printf("Wait for start ...\n"); + delay(250); + printf("Start:\n"); + for (int c_pmwr=0, c_pmwr_end = sizeof(tests_pwmr)/sizeof(tests_pwmr[0]); c_pmwrMaxFreq) { + printf("* Set Clock (pwmc, pwmr) %d, %d not possible on system (to slow to measure %g kHz with ISR), ignore\n", pwmc, pwmr, freq); + continue; + } + sprintf(msg, "Set range (pwmr) %d", pwmr); + pwmSetRange(pwmr); + + for (int c_pmw=0, c_pmw_end = sizeof(tests_pwm)/sizeof(tests_pwm[0]); c_pmw=0) { - in = digitalRead(GPIOIN); + in = digitalRead(GPIOIN); } int readback = digitalRead(GPIO); int pass = 0; - if (out==readback && in==out) { - pass = 1; - } + if (out==readback && in==out) { + pass = 1; + } if (GPIOIN>=0) { printf("set GPIO%02d = %d (readback %d), in GPIO%02d = %d ", GPIO, out, readback, GPIOIN, in); @@ -44,6 +44,11 @@ void CheckGPIO(int GPIO, int GPIOIN, int out) { } +void CheckInversGPIO(int GPIO, int GPIOIN, int out) { + CheckGPIO(GPIO, GPIOIN, out==HIGH ? LOW : HIGH); +} + + void digitalWriteEx(int GPIO, int GPIOIN, int mode) { digitalWrite(GPIO, mode); delayMicroseconds(5000); @@ -90,8 +95,21 @@ void CheckNotSame(const char* msg, int value, int expect) { } -void CheckSameFloat(const char* msg, float value, float expect) { - if (fabs(value-expect)<0.08) { +void CheckSameFloat(const char* msg, float value, float expect, float epsilon) { + if (fabs(value-expect) %spassed%s \n", msg, value, expect, COLORGRN, COLORDEF); + } else { + printf("%35s (%.3f<>%.3f) -> %sfailed%s \n" , msg, value, expect, COLORRED, COLORDEF); + globalError=1; + } +} + +void CheckSameFloatX(const char* msg, float value, float expect) { + return CheckSameFloat(msg, value, expect, 0.08f); +} + +void CheckSameDouble(const char* msg, double value, double expect, double epsilon) { + if (fabs(value-expect) %spassed%s \n", msg, value, expect, COLORGRN, COLORDEF); } else { printf("%35s (%.3f<>%.3f) -> %sfailed%s \n" , msg, value, expect, COLORRED, COLORDEF); diff --git a/wiringPi/wiringPi.c b/wiringPi/wiringPi.c index 22d2010..eb2090e 100644 --- a/wiringPi/wiringPi.c +++ b/wiringPi/wiringPi.c @@ -118,14 +118,20 @@ struct wiringPiNodeStruct *wiringPiNodes = NULL ; // Port function select bits -#define FSEL_INPT 0b000 -#define FSEL_OUTP 0b001 -#define FSEL_ALT0 0b100 -#define FSEL_ALT1 0b101 -#define FSEL_ALT2 0b110 -#define FSEL_ALT3 0b111 -#define FSEL_ALT4 0b011 -#define FSEL_ALT5 0b010 +#define FSEL_INPT 0b000 //0 +#define FSEL_OUTP 0b001 //1 +#define FSEL_ALT0 0b100 //4 +#define FSEL_ALT1 0b101 //5 +#define FSEL_ALT2 0b110 //6 +#define FSEL_ALT3 0b111 //7 +#define FSEL_ALT4 0b011 //3 +#define FSEL_ALT5 0b010 //2 +//RP1 defines +#define FSEL_ALT6 8 +#define FSEL_ALT7 9 +#define FSEL_ALT8 10 +#define FSEL_ALT9 11 + //RP1 chip (@Pi5) - 3.1.1. Function select #define RP1_FSEL_ALT0 0x00 @@ -166,10 +172,40 @@ const unsigned int RP1_PAD_IC_DEFAULT_FROM9 = 0x96; //pull-down, Schmitt const unsigned int RP1_PAD_DRIVE_MASK = 0x00000030; const unsigned int RP1_INV_PAD_DRIVE_MASK = ~(RP1_PAD_DRIVE_MASK); +const unsigned int RP1_PWM0_GLOBAL_CTRL = 0; +const unsigned int RP1_PWM0_FIFO_CTRL = 1; +const unsigned int RP1_PWM0_COMMON_RANGE= 2; +const unsigned int RP1_PWM0_COMMON_DUTY = 3; +const unsigned int RP1_PWM0_DUTY_FIFO = 4; +const unsigned int RP1_PWM0_CHAN_START = 5; +//offset channel +const unsigned int RP1_PWM0_CHAN_CTRL = 0; +const unsigned int RP1_PWM0_CHAN_RANGE = 1; +const unsigned int RP1_PWM0_CHAN_PHASE = 2; +const unsigned int RP1_PWM0_CHAN_DUTY = 3; +const unsigned int RP1_PWM0_CHAN_OFFSET= 4; + +const unsigned int RP1_PWM0_CHAN0_RANGE = RP1_PWM0_CHAN_START+RP1_PWM0_CHAN_OFFSET*0+RP1_PWM0_CHAN_RANGE; +const unsigned int RP1_PWM0_CHAN1_RANGE = RP1_PWM0_CHAN_START+RP1_PWM0_CHAN_OFFSET*1+RP1_PWM0_CHAN_RANGE; +const unsigned int RP1_PWM0_CHAN2_RANGE = RP1_PWM0_CHAN_START+RP1_PWM0_CHAN_OFFSET*2+RP1_PWM0_CHAN_RANGE; +const unsigned int RP1_PWM0_CHAN3_RANGE = RP1_PWM0_CHAN_START+RP1_PWM0_CHAN_OFFSET*3+RP1_PWM0_CHAN_RANGE; + +const unsigned int RP1_PWM_CTRL_SETUPDATE = 0x80000000; // Bit 32 +const unsigned int RP1_PWM_TRAIL_EDGE_MS = 0x1; +const unsigned int RP1_PWM_FIFO_POP_MASK = 0x100; // Bit 8 +const unsigned int RP1_CLK_PWM0_CTRL_DISABLE_MAGIC = 0x10000000; // Default after boot +const unsigned int RP1_CLK_PWM0_CTRL_ENABLE_MAGIC = 0x11000840; // Reverse engineered, because of missing documentation, don't known meaning of of bits + +const unsigned int CLK_PWM0_CTRL = (0x00074/4); +const unsigned int CLK_PWM0_DIV_INT = (0x00078/4); +const unsigned int CLK_PWM0_DIV_FRAC = (0x0007C/4); +const unsigned int CLK_PWM0_SEL = (0x00080/4); + //RP1 chip (@Pi5) address const unsigned long long RP1_64_BASE_Addr = 0x1f000d0000; const unsigned int RP1_BASE_Addr = 0x40000000; -const unsigned int RP1_PWM0_Addr = 0x40098000; // Adress is not mapped to gpiomem device! +const unsigned int RP1_CLOCK_Addr = 0x40018000; // Adress is not mapped to gpiomem device, lower than RP1_IO0_Addr +const unsigned int RP1_PWM0_Addr = 0x40098000; // Adress is not mapped to gpiomem device, lower than RP1_IO0_Addr const unsigned int RP1_IO0_Addr = 0x400d0000; const unsigned int RP1_SYS_RIO0_Addr = 0x400e0000; const unsigned int RP1_PADS0_Addr = 0x400f0000; @@ -195,7 +231,7 @@ const unsigned short pciemem_RP1_Ventor= 0x1de4; const unsigned short pciemem_RP1_Device= 0x0001; static volatile unsigned int GPIO_PADS ; -static volatile unsigned int GPIO_CLOCK_BASE ; +static volatile unsigned int GPIO_CLOCK_ADR ; static volatile unsigned int GPIO_BASE ; static volatile unsigned int GPIO_TIMER ; static volatile unsigned int GPIO_PWM ; @@ -238,6 +274,11 @@ static int wiringPiSetuped = FALSE ; #define PWM1_SERIAL 0x0200 // Run in serial mode #define PWM1_ENABLE 0x0100 // Channel Enable +const int PWMCLK_DIVI_MAX = 0xFFF; // 3 Byte max size for Clock devider +const int OSC_FREQ_DEFAULT = 192; // x100kHz OSC +const int OSC_FREQ_BCM2711 = 540; // x100kHz OSC +const int OSC_FREQ_BCM2712 = 500; // x100kHz OSC - cat /sys/kernel/debug/clk/clk_summary | grep pwm0 + // Timer // Word offsets @@ -557,6 +598,27 @@ int piBoard() { return RaspberryPiModel<0 ? 0 : 1; } + +int piBoard40Pin() { + if (!piBoard()){ + // Board not detected + return -1; + } + switch(RaspberryPiModel){ + case PI_MODEL_A: + case PI_MODEL_B: + return 0; +// PI_MODEL_CM +// PI_MODEL_CM3 +// PI_MODEL_CM4 +// PI_MODEL_CM4S +// ? guess yes + default: + return 1; + } +} + + int GetMaxPin() { return PI_MODEL_5 == RaspberryPiModel ? 27 : 63; } @@ -564,10 +626,10 @@ int GetMaxPin() { #define RETURN_ON_MODEL5 if (PI_MODEL_5 == RaspberryPiModel) { if (wiringPiDebug) printf("Function not supported on Pi5\n"); return; } -int FailOnModel5() { +int FailOnModel5(const char *function) { if (PI_MODEL_5 == RaspberryPiModel) { - return wiringPiFailure (WPI_ALMOST, "Function not supported on Raspberry Pi 5.\n" - " Unable to continue. Keep an eye of new versions at https://github.com/wiringpi/wiringpi\n") ; + return wiringPiFailure (WPI_ALMOST, "Function '%s' not supported on Raspberry Pi 5.\n" + " Unable to continue. Keep an eye of new versions at https://github.com/wiringpi/wiringpi\n", function) ; } return 0; } @@ -690,8 +752,8 @@ static uint8_t gpioToPUDCLK [] = static uint8_t gpioToPwmALT [] = { 0, 0, 0, 0, 0, 0, 0, 0, // 0 -> 7 - 0, 0, 0, 0, FSEL_ALT0, FSEL_ALT0, 0, 0, // 8 -> 15 - 0, 0, FSEL_ALT5, FSEL_ALT5, 0, 0, 0, 0, // 16 -> 23 + 0, 0, 0, 0, FSEL_ALT0, FSEL_ALT0, 0, 0, // 8 -> 15 + 0, 0, FSEL_ALT5, FSEL_ALT5, 0, 0, 0, 0, // 16 -> 23 0, 0, 0, 0, 0, 0, 0, 0, // 24 -> 31 0, 0, 0, 0, 0, 0, 0, 0, // 32 -> 39 FSEL_ALT0, FSEL_ALT0, 0, 0, 0, FSEL_ALT0, 0, 0, // 40 -> 47 @@ -706,8 +768,8 @@ static uint8_t gpioToPwmALT [] = static uint8_t gpioToPwmPort [] = { 0, 0, 0, 0, 0, 0, 0, 0, // 0 -> 7 - 0, 0, 0, 0, PWM0_DATA, PWM1_DATA, 0, 0, // 8 -> 15 - 0, 0, PWM0_DATA, PWM1_DATA, 0, 0, 0, 0, // 16 -> 23 + 0, 0, 0, 0, PWM0_DATA, PWM1_DATA, 0, 0, // 8 -> 15 + 0, 0, PWM0_DATA, PWM1_DATA, 0, 0, 0, 0, // 16 -> 23 0, 0, 0, 0, 0, 0, 0, 0, // 24 -> 31 0, 0, 0, 0, 0, 0, 0, 0, // 32 -> 39 PWM0_DATA, PWM1_DATA, 0, 0, 0, PWM1_DATA, 0, 0, // 40 -> 47 @@ -1131,6 +1193,7 @@ void piBoardId (int *model, int *rev, int *mem, int *maker, int *warranty) case PI_MODEL_4B: case PI_MODEL_400: case PI_MODEL_CM4: + case PI_MODEL_CM4S: piGpioBase = GPIO_PERI_BASE_2711 ; piGpioPupOffset = GPPUPPDN0 ; break ; @@ -1261,7 +1324,7 @@ void setPadDrive (int group, int value) int getAlt (int pin) { - int fSel, shift, alt ; + int alt; pin &= 63 ; @@ -1292,29 +1355,28 @@ int getAlt (int pin) 11 = alternate function 9 */ switch(alt) { - case 0: return 4; - case 1: return 5; - case 2: return 6; - case 3: return 7; - case 4: return 3; + case 0: return FSEL_ALT0; + case 1: return FSEL_ALT1; + case 2: return FSEL_ALT2; + case 3: return FSEL_ALT3; + case 4: return FSEL_ALT4; case RP1_FSEL_GPIO: { unsigned int outputmask = gpio[2*pin] & 0x3000; //Bit13-OETOPAD + Bit12-OEFROMPERI - return (outputmask==0x3000) ? 1 : 0; //1=OUT 0=IN + return (outputmask==0x3000) ? FSEL_OUTP : FSEL_INPT; } - case 6: return 8; - case 7: return 9; - case 8: return 10; - case 9: return 11; + case 6: return FSEL_ALT6; + case 7: return FSEL_ALT7; + case 8: return FSEL_ALT8; + case RP1_FSEL_NONE: return FSEL_ALT9; default:return alt; } - } else { - fSel = gpioToGPFSEL [pin] ; - shift = gpioToShift [pin] ; + int fSel = gpioToGPFSEL [pin] ; + int shift = gpioToShift [pin] ; alt = (*(gpio + fSel) >> shift) & 7 ; } - return alt ; + return alt; } @@ -1333,11 +1395,20 @@ void pwmSetMode (int mode) { if ((wiringPiMode == WPI_MODE_PINS) || (wiringPiMode == WPI_MODE_PHYS) || (wiringPiMode == WPI_MODE_GPIO)) { - FailOnModel5(); - if (mode == PWM_MODE_MS) + if (PI_MODEL_5 == RaspberryPiModel) { + if(mode != PWM_MODE_MS) { + fprintf(stderr, "pwmSetMode: Raspberry Pi 5 missing feature PWM BAL mode\n"); + } + return; + } + if (mode == PWM_MODE_MS) { *(pwm + PWM_CONTROL) = PWM0_ENABLE | PWM1_ENABLE | PWM0_MS_MODE | PWM1_MS_MODE ; - else + } else { *(pwm + PWM_CONTROL) = PWM0_ENABLE | PWM1_ENABLE ; + } + if (wiringPiDebug) { + printf ("Enable PWM mode: %s. Current register: 0x%08X\n", mode == PWM_MODE_MS ? "mark:space (freq. stable)" : "balanced (freq. change)", *(pwm + PWM_CONTROL)); + } } } @@ -1351,11 +1422,32 @@ void pwmSetMode (int mode) void pwmSetRange (unsigned int range) { - FailOnModel5(); 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) ; + /* would be possible on ms mode but not on bal, deactivated, use pwmc modify instead + if (piGpioBase == GPIO_PERI_BASE_2711) { + range = (OSC_FREQ_BCM2711*range)/OSC_FREQ_DEFAULT; + } + */ + if (!pwm) { + fprintf(stderr, "wiringPi: pwmSetRange but no pwm memory available, ignoring\n"); + return; + } + int readback = 0x00; + if (PI_MODEL_5 == RaspberryPiModel) { + pwm[RP1_PWM0_CHAN0_RANGE] = range; + pwm[RP1_PWM0_CHAN1_RANGE] = range; + pwm[RP1_PWM0_CHAN2_RANGE] = range; + pwm[RP1_PWM0_CHAN3_RANGE] = range; + readback = pwm[RP1_PWM0_CHAN0_RANGE]; + } else { + *(pwm + PWM0_RANGE) = range ; delayMicroseconds (10) ; + *(pwm + PWM1_RANGE) = range ; delayMicroseconds (10) ; + readback = *(pwm + PWM0_RANGE); + } + if (wiringPiDebug) { + printf ("PWM range: %u. Current register: 0x%08X\n", range, readback); + } } } @@ -1371,19 +1463,45 @@ void pwmSetRange (unsigned int range) void pwmSetClock (int divisor) { uint32_t pwm_control ; - - FailOnModel5(); - if (piGpioBase == GPIO_PERI_BASE_2711) - { - divisor = 540*divisor/192; + if (!clk) { + fprintf(stderr, "wiringPi: pwmSetClock but no clk memory available, ignoring\n"); + return; } - divisor &= 4095 ; + if (divisor > PWMCLK_DIVI_MAX) { + divisor = PWMCLK_DIVI_MAX; // even on Pi5 4095 is OK + } + if (PI_MODEL_5 == RaspberryPiModel) { + if (divisor < 1) { + if (wiringPiDebug) { printf("Disable PWM0 clock"); } + clk[CLK_PWM0_CTRL] = RP1_CLK_PWM0_CTRL_DISABLE_MAGIC; // 0 = disable on Pi5 + } else { + divisor = (OSC_FREQ_BCM2712*divisor)/OSC_FREQ_DEFAULT; + if (wiringPiDebug) { + printf ("PWM clock divisor: %d\n", divisor) ; + } + //clk[CLK_PWM0_CTRL] = RP1_CLK_PWM0_CTRL_DISABLE_MAGIC; + //delayMicroseconds(100); + clk[CLK_PWM0_DIV_INT] = divisor; + clk[CLK_PWM0_DIV_FRAC] = 0; + clk[CLK_PWM0_SEL] = 1; + clk[CLK_PWM0_CTRL] = RP1_CLK_PWM0_CTRL_ENABLE_MAGIC; + } + return; + } + if (piGpioBase == GPIO_PERI_BASE_2711) { + //calculate value for OSC 54MHz -> 19.2MHz + // Pi 4 max divisor is 1456, Pi0-3 is 4095 (0xFFF) + divisor = (OSC_FREQ_BCM2711*divisor)/OSC_FREQ_DEFAULT; + } + if (divisor < 1) { + divisor = 1; + } 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)) ; - + if (wiringPiDebug) { + printf ("PWM clock divisor: Old register: 0x%08X\n", *(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 @@ -1408,8 +1526,9 @@ void pwmSetClock (int divisor) *(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)) ; + if (wiringPiDebug) { + printf ("PWM clock divisor %d. Current register: 0x%08X\n", divisor, *(clk + PWMCLK_DIV)); + } } } @@ -1424,7 +1543,7 @@ void gpioClockSet (int pin, int freq) { int divi, divr, divf ; - FailOnModel5(); + FailOnModel5("gpioClockSet"); pin &= 63 ; /**/ if (wiringPiMode == WPI_MODE_PINS) @@ -1438,9 +1557,9 @@ void gpioClockSet (int pin, int freq) divr = 19200000 % freq ; divf = (int)((double)divr * 4096.0 / 19200000.0) ; - if (divi > 4095) - divi = 4095 ; - + if (divi > PWMCLK_DIVI_MAX) { + divi = PWMCLK_DIVI_MAX; + } *(clk + gpioToClkCon [pin]) = BCM_PASSWORD | GPIO_CLOCK_SOURCE ; // Stop GPIO Clock while ((*(clk + gpioToClkCon [pin]) & 0x80) != 0) // ... and wait ; @@ -1613,9 +1732,6 @@ int requestLine(int pin, unsigned int lineRequestFlags) { void pinModeAlt (int pin, int mode) { - int fSel, shift ; - - RETURN_ON_MODEL5 setupCheck ("pinModeAlt") ; if ((pin & PI_GPIO_MASK) == 0) // On-board pin @@ -1628,20 +1744,49 @@ void pinModeAlt (int pin, int mode) return ; if (PI_MODEL_5 == RaspberryPiModel) { - //confusion! diffrent to to BCM! this is taking directly the value for the register - /* - "alt0" 0b100 - "alt1" 0b101 - "alt2" 0b110 - "alt3" 0b111 - "alt4" 0b011 - "alt5" 0b010 - */ - gpio[2*pin+1] = (mode & RP1_FSEL_NONE_HW) | RP1_DEBOUNCE_DEFAULT; //0-4 function, 5-11 debounce time + int modeRP1; + switch(mode) { + case FSEL_ALT0: + modeRP1 = 0; + break; + case FSEL_ALT1: + modeRP1 = 1; + break; + case FSEL_ALT2: + modeRP1 = 2; + break; + case FSEL_ALT3: + modeRP1 = 3; + break; + case FSEL_ALT4: + modeRP1 = 4; + break; + case FSEL_ALT5: + modeRP1 = 5; + break; + case FSEL_ALT6: + modeRP1 = 6; + break; + case FSEL_ALT7: + modeRP1 = 7; + break; + case FSEL_ALT8: + modeRP1 = 8; + break; + case FSEL_OUTP: + case FSEL_INPT: + modeRP1 = RP1_FSEL_GPIO; + break; + default: + fprintf(stderr, "pinModeAlt: invalid mode %d\n", mode); + return; + } + //printf("pinModeAlt: Pi5 alt pin %d to %d\n", pin, modeRP1); + gpio[2*pin+1] = (modeRP1 & RP1_FSEL_NONE_HW) | RP1_DEBOUNCE_DEFAULT; //0-4 function, 5-11 debounce time } else { - fSel = gpioToGPFSEL [pin] ; - shift = gpioToShift [pin] ; + int fSel = gpioToGPFSEL [pin] ; + int shift = gpioToShift [pin] ; *(gpio + fSel) = (*(gpio + fSel) & ~(7 << shift)) | ((mode & 0x7) << shift) ; } @@ -1749,6 +1894,15 @@ void pinMode (int pin, int mode) } else { *(gpio + fSel) = (*(gpio + fSel) & ~(7 << shift)) ; // Sets bits to zero = input } + if (PM_OFF==mode && !usingGpioMem && pwm && gpioToPwmALT[pin]>0) { //PWM pin -> reset + pwmWrite(origPin, 0); + int channel = gpioToPwmPort[pin]; + if (channel>=0 && channel<=3 && PI_MODEL_5 == RaspberryPiModel) { + unsigned int ctrl = pwm[RP1_PWM0_GLOBAL_CTRL]; + pwm[RP1_PWM0_GLOBAL_CTRL] = (ctrl & ~(1<0x%08X)\n", channel, ctrl, pwm[RP1_PWM0_GLOBAL_CTRL]); + } + } } else if (mode == OUTPUT) { if (PI_MODEL_5 == RaspberryPiModel) { pads[1+pin] = (pin<=8) ? RP1_PAD_DEFAULT_0TO8 : RP1_PAD_DEFAULT_FROM9; @@ -1766,22 +1920,41 @@ void pinMode (int pin, int mode) pinMode (origPin, PWM_OUTPUT) ; // Call myself to enable PWM mode pwmSetMode (PWM_MODE_MS) ; } - else if (mode == PWM_OUTPUT) - { - RETURN_ON_MODEL5 - if ((alt = gpioToPwmALT [pin]) == 0) // Not a hardware capable PWM pin - return ; + else if (PWM_OUTPUT==mode || PWM_MS_OUTPUT==mode || PWM_BAL_OUTPUT==mode) { - usingGpioMemCheck ("pinMode PWM") ; + usingGpioMemCheck("pinMode PWM") ; // exit on error! + alt = gpioToPwmALT[pin]; + if (0==alt) { // Not a hardware capable PWM pin + return; + } + int channel = gpioToPwmPort[pin]; + if (PI_MODEL_5 == RaspberryPiModel) { + if (channel>=0 && channel<=3) { + // enable channel pwm m:s mode + pwm[RP1_PWM0_CHAN_START+RP1_PWM0_CHAN_OFFSET*channel+RP1_PWM0_CHAN_CTRL] = (RP1_PWM_TRAIL_EDGE_MS | RP1_PWM_FIFO_POP_MASK); + // enable pwm global + unsigned int ctrl = pwm[RP1_PWM0_GLOBAL_CTRL]; + pwm[RP1_PWM0_GLOBAL_CTRL] = ctrl | (1<0x%08X)\n", channel, ctrl, pwm[RP1_PWM0_GLOBAL_CTRL]); + //change GPIO mode + pads[1+pin] = RP1_PAD_DEFAULT_FROM9; // enable output + pinModeAlt(origPin, alt); //switch to PWM mode + } + } else { + // Set pin to PWM mode + *(gpio + fSel) = (*(gpio + fSel) & ~(7 << shift)) | (alt << shift) ; + delayMicroseconds (110) ; // See comments in pwmSetClockWPi -// 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 + if (PWM_OUTPUT==mode || PWM_BAL_OUTPUT==mode) { + pwmSetMode(PWM_MODE_BAL); // Pi default mode + } else { + pwmSetMode(PWM_MODE_MS); + } + } + if (PWM_OUTPUT==mode) { // predefine + pwmSetRange (1024) ; // Default range of 1024 + pwmSetClock (32) ; // 19.2 / 32 = 600KHz - Also starts the PWM + } } else if (mode == GPIO_CLOCK) { @@ -2121,7 +2294,6 @@ void pwmWrite (int pin, int value) { struct wiringPiNodeStruct *node = wiringPiNodes ; - FailOnModel5(); setupCheck ("pwmWrite") ; if ((pin & PI_GPIO_MASK) == 0) // On-Board Pin @@ -2133,8 +2305,29 @@ void pwmWrite (int pin, int value) else if (wiringPiMode != WPI_MODE_GPIO) return ; + /* would be possible on ms mode but not on bal, deactivated, use pwmc modify instead + if (piGpioBase == GPIO_PERI_BASE_2711) { + value = (OSC_FREQ_BCM2711*value)/OSC_FREQ_DEFAULT; + } + */ usingGpioMemCheck ("pwmWrite") ; - *(pwm + gpioToPwmPort [pin]) = value ; + int channel = gpioToPwmPort[pin]; + int readback = 0x00; + if (PI_MODEL_5 == RaspberryPiModel ) { + if (channel>=0 && channel<=3) { + unsigned int addr = RP1_PWM0_CHAN_START+RP1_PWM0_CHAN_OFFSET*channel+RP1_PWM0_CHAN_DUTY; + pwm[addr] = value; + readback = pwm[addr]; + } else { + fprintf(stderr, "pwmWrite: invalid channel at GPIO pin %d \n", pin); + } + } else { + *(pwm + channel) = value ; + readback = *(pwm + channel); + } + if (wiringPiDebug) { + printf ("PWM value(duty): %u. Current register: 0x%08X\n", value, readback); + } } else { @@ -2191,8 +2384,6 @@ void analogWrite (int pin, int value) void pwmToneWrite (int pin, int freq) { - FailOnModel5(); - setupCheck ("pwmToneWrite") ; if (freq == 0) @@ -2230,7 +2421,7 @@ void digitalWriteByte (const int value) int mask = 1 ; int pin ; - FailOnModel5(); + FailOnModel5("digitalWriteByte"); if (wiringPiMode == WPI_MODE_GPIO_SYS) { @@ -2259,7 +2450,7 @@ unsigned int digitalReadByte (void) uint32_t raw ; uint32_t data = 0 ; - FailOnModel5(); + FailOnModel5("digitalReadByte"); if (wiringPiMode == WPI_MODE_GPIO_SYS) { @@ -2290,7 +2481,7 @@ unsigned int digitalReadByte (void) void digitalWriteByte2 (const int value) { - FailOnModel5(); + FailOnModel5("digitalWriteByte2"); if (wiringPiMode == WPI_MODE_GPIO_SYS) { @@ -2306,7 +2497,7 @@ unsigned int digitalReadByte2 (void) { uint32_t data = 0 ; - FailOnModel5(); + FailOnModel5("digitalReadByte2"); if (wiringPiMode == WPI_MODE_GPIO_SYS) { @@ -2722,6 +2913,15 @@ unsigned int micros (void) return (uint32_t)(now - epochMicro) ; } + +unsigned long long piMicros64(void) { + struct timespec ts; + + clock_gettime (CLOCK_MONOTONIC_RAW, &ts) ; + uint64_t now = (uint64_t)ts.tv_sec * (uint64_t)1000000 + (uint64_t)(ts.tv_nsec / 1000) ; + return (now - epochMicro) ; +} + /* * wiringPiVersion: * Return our current version number @@ -2868,6 +3068,17 @@ int wiringPiSetup (void) if (PI_MODEL_5 == model) { gpiomemGlobal = pciemem_RP1; gpiomemModule = gpiomem_RP1; + + // PWM alt pins @RP1 - need to be translated to RP1_FSEL with pinModeAlt + gpioToPwmALT[12] = FSEL_ALT0; + gpioToPwmALT[13] = FSEL_ALT0; + gpioToPwmALT[18] = FSEL_ALT3; + gpioToPwmALT[19] = FSEL_ALT3; + //PWM0 channel @RP1 + gpioToPwmPort[12] = 0; + gpioToPwmPort[13] = 1; + gpioToPwmPort[18] = 2; + gpioToPwmPort[19] = 3; } usingGpioMem = FALSE; @@ -2894,10 +3105,10 @@ int wiringPiSetup (void) if (PI_MODEL_5 != model) { //Set the offsets into the memory interface. - GPIO_PADS = piGpioBase + 0x00100000 ; - GPIO_CLOCK_BASE = piGpioBase + 0x00101000 ; + GPIO_PADS = piGpioBase + 0x00100000 ; + GPIO_CLOCK_ADR = piGpioBase + 0x00101000 ; GPIO_BASE = piGpioBase + 0x00200000 ; - GPIO_TIMER = piGpioBase + 0x0000B000 ; + GPIO_TIMER = piGpioBase + 0x0000B000 ; GPIO_PWM = piGpioBase + 0x0020C000 ; GPIO_RIO = 0x00 ; @@ -2917,7 +3128,7 @@ int wiringPiSetup (void) // Clock control (needed for PWM) - clk = (uint32_t *)mmap(0, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, GPIO_CLOCK_BASE) ; + clk = (uint32_t *)mmap(0, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, GPIO_CLOCK_ADR) ; if (clk == MAP_FAILED) return wiringPiFailure (WPI_ALMOST, "wiringPiSetup: mmap (CLOCK) failed: %s\n", strerror (errno)) ; @@ -2953,7 +3164,7 @@ int wiringPiSetup (void) unsigned int MMAP_size = (usingGpioMem) ? gpiomem_RP1_Size : pciemem_RP1_Size; GPIO_PADS = (RP1_PADS0_Addr-RP1_IO0_Addr) ; - GPIO_CLOCK_BASE = 0x00; + GPIO_CLOCK_ADR = (RP1_CLOCK_Addr-RP1_BASE_Addr); GPIO_BASE = (RP1_IO0_Addr-RP1_BASE_Addr) ; GPIO_TIMER = 0x00; GPIO_PWM = RP1_PWM0_Addr-RP1_BASE_Addr; @@ -2965,8 +3176,12 @@ int wiringPiSetup (void) return wiringPiFailure (WPI_ALMOST, "wiringPiSetup: mmap failed: %s\n", strerror (errno)) ; if (usingGpioMem) { gpio = base; // RP1 start adress of map memory for gpio (same as module memory) + pwm = NULL; // outside of mapped memory, PWM not available from none root + clk = NULL; // outside of mapped memory, CLK main not available from none root } else { - gpio = &base[GPIO_BASE/4];// RP1 start adress of map memory for gpio + gpio = &base[GPIO_BASE/4]; // RP1 start adress of map memory for gpio + pwm = &base[GPIO_PWM/4]; // RP1 start adress of map memory for pwm0 + clk = &base[GPIO_CLOCK_ADR/4]; // RP1 start adress of map memory for clocks_main } pads = &gpio[GPIO_PADS/4]; // RP1 start adress of map memory for pads rio = &gpio[GPIO_RIO/4]; // RP1 start adress of map memory for rio @@ -2976,19 +3191,19 @@ int wiringPiSetup (void) // Export the base addresses for any external software that might need them _wiringPiBase = base ; _wiringPiGpio = gpio ; - _wiringPiPwm = NULL ; - _wiringPiClk = NULL ; + _wiringPiPwm = pwm ; + _wiringPiClk = clk ; _wiringPiPads = pads ; _wiringPiTimer = NULL ; _wiringPiRio = rio ; } if (wiringPiDebug) { - printf ("wiringPi: memory map gpio 0x%x %s\n", GPIO_BASE , _wiringPiGpio ? "valid" : "invalid"); - printf ("wiringPi: memory map pads 0x%x %s\n", GPIO_PADS , _wiringPiPads ? "valid" : "invalid"); - printf ("wiringPi: memory map rio 0x%x %s\n", GPIO_RIO , _wiringPiRio ? "valid" : "invalid"); - printf ("wiringPi: memory map pwm 0x%x %s\n", GPIO_PWM , _wiringPiPwm ? "valid" : "invalid"); - printf ("wiringPi: memory map clk 0x%x %s\n", GPIO_CLOCK_BASE, _wiringPiClk ? "valid" : "invalid"); - printf ("wiringPi: memory map timer 0x%x %s\n", GPIO_TIMER,_wiringPiTimer ? "valid" : "invalid"); + printf ("wiringPi: memory map gpio 0x%x %s\n", GPIO_BASE , _wiringPiGpio ? "valid" : "invalid"); + printf ("wiringPi: memory map pads 0x%x %s\n", GPIO_PADS , _wiringPiPads ? "valid" : "invalid"); + printf ("wiringPi: memory map rio 0x%x %s\n", GPIO_RIO , _wiringPiRio ? "valid" : "invalid"); + printf ("wiringPi: memory map pwm0 0x%x %s\n", GPIO_PWM , _wiringPiPwm ? "valid" : "invalid"); + printf ("wiringPi: memory map clocks 0x%x %s\n", GPIO_CLOCK_ADR, _wiringPiClk ? "valid" : "invalid"); + printf ("wiringPi: memory map timer 0x%x %s\n", GPIO_TIMER ,_wiringPiTimer ? "valid" : "invalid"); } initialiseEpoch () ; diff --git a/wiringPi/wiringPi.h b/wiringPi/wiringPi.h index 9a26b74..79a895a 100644 --- a/wiringPi/wiringPi.h +++ b/wiringPi/wiringPi.h @@ -58,10 +58,12 @@ // Pin modes -#define INPUT 0 -#define OUTPUT 1 -#define PWM_OUTPUT 2 -#define GPIO_CLOCK 3 +#define INPUT 0 +#define OUTPUT 1 +#define PWM_OUTPUT 2 +#define PWM_MS_OUTPUT 8 +#define PWM_BAL_OUTPUT 9 +#define GPIO_CLOCK 3 #define SOFT_PWM_OUTPUT 4 #define SOFT_TONE_OUTPUT 5 #define PWM_TONE_OUTPUT 6 @@ -268,6 +270,7 @@ extern int wiringPiSetupPiFaceForGpioProg (void) ; // Don't use this - for gpio extern int piGpioLayout (void) ; extern int piBoardRev (void) ; // Deprecated, but does the same as piGpioLayout extern void piBoardId (int *model, int *rev, int *mem, int *maker, int *overVolted) ; +extern int piBoard40Pin (void) ; // Interface V3.7 extern int wpiPinToGpio (int wpiPin) ; extern int physPinToGpio (int physPin) ; extern void setPadDrive (int group, int value) ; @@ -308,6 +311,8 @@ extern void delayMicroseconds (unsigned int howLong) ; extern unsigned int millis (void) ; extern unsigned int micros (void) ; +extern unsigned long long piMicros64(void); // Interface V3.7 + #ifdef __cplusplus } #endif