diff --git a/.arduino-ci.yml b/.arduino-ci.yml index ff5659b..e7cb463 100644 --- a/.arduino-ci.yml +++ b/.arduino-ci.yml @@ -2,6 +2,10 @@ compile: # Choosing to run compilation tests on 2 different Arduino platforms platforms: - uno - - leonardo - - due - - zero + # - due + # - zero + # - leonardo + - m4 + - esp32 + # - esp8266 + # - mega2560 \ No newline at end of file diff --git a/.github/workflows/arduino_test_runner.yml b/.github/workflows/arduino_test_runner.yml index b9b5b10..096b975 100644 --- a/.github/workflows/arduino_test_runner.yml +++ b/.github/workflows/arduino_test_runner.yml @@ -4,9 +4,14 @@ name: Arduino CI on: [push, pull_request] jobs: - arduino_ci: + runTest: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - uses: Arduino-CI/action@v0.1.0 \ No newline at end of file + - uses: ruby/setup-ruby@v1 + with: + ruby-version: 2.6 + - run: | + gem install arduino_ci + arduino_ci.rb diff --git a/AD520X.cpp b/AD520X.cpp index 530e958..c54ed71 100644 --- a/AD520X.cpp +++ b/AD520X.cpp @@ -2,7 +2,7 @@ // FILE: AD520X.cpp // AUTHOR: Rob Tillaart // DATE: 2020-07-24 -// VERSION: 0.1.2 +// VERSION: 0.2.0 // PURPOSE: Arduino library for AD5204 and AD5206 digital potentiometers (+ older AD8400, AD8402, AD8403) // URL: https://github.com/RobTillaart/AD520X // @@ -14,6 +14,8 @@ // 0.1.2 2021-08-19 VSPI / HSPI support for ESP32 only // add setGPIOpins for ESP32 only // add SetSPIspeed (generic) +// 0.2.0 2021-10-16 update build-CI +// add get- and setPercentage() #include "AD520X.h" @@ -31,7 +33,7 @@ AD520X::AD520X(uint8_t select, uint8_t reset, uint8_t shutdown, uint8_t dataOut, } -// initializes the pins and starts SPI in case of hardware SPI +// initializes the pins and starts SPI in case of hardware SPI void AD520X::begin(uint8_t value) { pinMode(_select, OUTPUT); @@ -46,19 +48,19 @@ void AD520X::begin(uint8_t value) if(_hwSPI) { #if defined(ESP32) - if (_useHSPI) // HSPI + if (_useHSPI) // HSPI { mySPI = new SPIClass(HSPI); mySPI->end(); - mySPI->begin(14, 12, 13, _select); // CLK=14 MISO=12 MOSI=13 + mySPI->begin(14, 12, 13, _select); // CLK=14 MISO=12 MOSI=13 } - else // VSPI + else // VSPI { mySPI = new SPIClass(VSPI); mySPI->end(); - mySPI->begin(18, 19, 23, _select); // CLK=18 MISO=19 MOSI=23 + mySPI->begin(18, 19, 23, _select); // CLK=18 MISO=19 MOSI=23 } - #else // generic hardware SPI + #else // generic hardware SPI mySPI = &SPI; mySPI->end(); mySPI->begin(); @@ -86,17 +88,18 @@ void AD520X::setGPIOpins(uint8_t clk, uint8_t miso, uint8_t mosi, uint8_t select pinMode(_select, OUTPUT); digitalWrite(_select, HIGH); - mySPI->end(); // disable SPI and restart + mySPI->end(); // disable SPI and restart mySPI->begin(clk, miso, mosi, select); } #endif -void AD520X::setValue(uint8_t pm, uint8_t value) +bool AD520X::setValue(uint8_t pm, uint8_t value) { - if (pm >= _pmCount) return; + if (pm >= _pmCount) return false; _value[pm] = value; updateDevice(pm); + return true; } @@ -116,6 +119,22 @@ uint8_t AD520X::getValue(uint8_t pm) } +bool AD520X::setPercentage(uint8_t pm, float percentage) +{ + if ((percentage < 0) || (percentage > 100.0)) return false; + return setValue(pm, round(percentage * (255.0 / 100.0))); +} + + +float AD520X::getPercentage(uint8_t pm) +{ + if (pm >= _pmCount) return 0; + uint8_t v = _value[pm]; + if (v == 0) return 0.0; + return (100.0 / 255.0) * v; +} + + void AD520X::reset(uint8_t value) { digitalWrite(_reset, HIGH); @@ -145,7 +164,7 @@ void AD520X::updateDevice(uint8_t pm) mySPI->transfer(_value[pm]); mySPI->endTransaction(); } - else // Software SPI + else // Software SPI { swSPI_transfer(pm); swSPI_transfer(_value[pm]); @@ -154,7 +173,7 @@ void AD520X::updateDevice(uint8_t pm) } -// simple one mode version +// simple one mode version void AD520X::swSPI_transfer(uint8_t val) { uint8_t clk = _clock; @@ -170,7 +189,7 @@ void AD520X::swSPI_transfer(uint8_t val) ///////////////////////////////////////////////////////////////////////////// // -// DERIVED CLASSES +// DERIVED CLASSES // AD5206::AD5206(uint8_t select, uint8_t reset, uint8_t shutdown, uint8_t dataOut, uint8_t clock) : AD520X(select, reset, shutdown, dataOut, clock) diff --git a/AD520X.h b/AD520X.h index 0f31f68..2196252 100644 --- a/AD520X.h +++ b/AD520X.h @@ -3,7 +3,7 @@ // FILE: AD520X.h // AUTHOR: Rob Tillaart // DATE: 2020-07-24 -// VERSION: 0.1.2 +// VERSION: 0.2.0 // PURPOSE: Arduino library for AD5204 and AD5206 digital potentiometers (+ older AD8400, AD8402, AD8403) // URL: https://github.com/RobTillaart/AD520X // @@ -16,7 +16,7 @@ #include "SPI.h" -#define AD520X_LIB_VERSION (F("0.1.2")) +#define AD520X_LIB_VERSION (F("0.2.0")) class AD520X @@ -25,12 +25,15 @@ class AD520X AD520X(uint8_t select, uint8_t reset, uint8_t shutdown, uint8_t dataOut, uint8_t clock); void begin(uint8_t value = 128); - void setValue(uint8_t pm, uint8_t value); + bool setValue(uint8_t pm = 0, uint8_t value = 128); void setAll(uint8_t value); - uint8_t getValue(uint8_t pm); + uint8_t getValue(uint8_t pm = 0); + + bool setPercentage(uint8_t pm = 0, float percentage = 50); + float getPercentage(uint8_t pm = 0); void reset(uint8_t value = 128); - int pmCount() { return _pmCount; }; + uint8_t pmCount() { return _pmCount; }; void powerOn() { digitalWrite(_shutdown, LOW); }; void powerOff() { digitalWrite(_shutdown, HIGH); }; diff --git a/README.md b/README.md index a46089f..af8831b 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,26 @@ [![Arduino CI](https://github.com/RobTillaart/AD520X/workflows/Arduino%20CI/badge.svg)](https://github.com/marketplace/actions/arduino_ci) +[![Arduino-lint](https://github.com/RobTillaart/AD520X/actions/workflows/arduino-lint.yml/badge.svg)](https://github.com/RobTillaart/AD520X/actions/workflows/arduino-lint.yml) +[![JSON check](https://github.com/RobTillaart/AD520X/actions/workflows/jsoncheck.yml/badge.svg)](https://github.com/RobTillaart/AD520X/actions/workflows/jsoncheck.yml) [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/RobTillaart/AD520X/blob/master/LICENSE) [![GitHub release](https://img.shields.io/github/release/RobTillaart/AD520X.svg?maxAge=3600)](https://github.com/RobTillaart/AD520X/releases) + # AD520X Arduino library for SPI AD5204 and AD5206 digital potentiometers Should work for the AD840x series too (not tested). + ## Description The library is still experimental as not all functionality is tested (enough). -The **AD5204** (4 pm) and **AD5206** (6 pm) are SPI based digital potentiometers. +The **AD5204** (4 channels) and **AD5206** (6 channels) are SPI based digital potentiometers. This library consists of a base class **AD520X** that does the work. -The interface is straightforward, one can set a value per pm between 0..255. +The interface is straightforward, one can set a value per channels between 0..255. | type | # channels | |:-------|:----------:| @@ -27,8 +31,8 @@ The interface is straightforward, one can set a value per pm between 0..255. | AD8403 | 4 | -_Although not tested this library should work for the older **AD8400** (1 pm), -the **AD8402** (2 pm) and **AD8403** (4 pm) as the interface is very similar +_Although not tested this library should work for the older **AD8400** (1 channel), +the **AD8402** (2 channels) and **AD8403** (4 channels) as the interface is very similar (datasheet comparison). If you can confirm it works, please let me know._ @@ -37,36 +41,44 @@ the **AD8402** (2 pm) and **AD8403** (4 pm) as the interface is very similar ### Constructors -All pins are type uint8_t - -- **AD520X(select, reset, shutdown, dataOut = 255, clock = 255)** constructor +- **AD520X(uint8_t select, uint8_t reset, uint8_t shutdown, uint8_t dataOut = 255, uint8_t clock = 255)** constructor Base class, not to be used directly. If dataOut and clock are set to 255 (default) it uses hardware SPI. If dataOut and clock are set to another (valid) value) it uses software SPI. reset and shutdown may be set to 255 too, which effectively disables them. +- **AD5204(select, reset, shutdown, dataOut = 255, clock = 255)** has 4 channels. +- **AD5206(select, reset, shutdown, dataOut = 255, clock = 255)** has 6 channels. +- **AD8400(select, reset, shutdown, dataOut = 255, clock = 255)** has 1 channel. +- **AD8402(select, reset, shutdown, dataOut = 255, clock = 255)** has 2 channels. +- **AD8403(select, reset, shutdown, dataOut = 255, clock = 255)** has 4 channels. + Note: hardware SPI is 10+ times faster on an UNO. -- **AD5204(select, reset, shutdown, dataOut = 255, clock = 255)** uses 4 pm. -- **AD5206(select, reset, shutdown, dataOut = 255, clock = 255)** uses 6 pm. -- **AD8400(select, reset, shutdown, dataOut = 255, clock = 255)** uses 1 pm. -- **AD8402(select, reset, shutdown, dataOut = 255, clock = 255)** uses 2 pm. -- **AD8403(select, reset, shutdown, dataOut = 255, clock = 255)** uses 4 pm. ### Base +Since 0.2.0 the functions have more default parameters. Potentiometer is default pot 0 +and value is default the middle value of 128. + - **void begin(uint8_t value = 128)** value is the initial value of all potentiometer. -- **void setValue(uint8_t pm, uint8_t value)** set a potentiometer to a value -- **void setAll(uint8_t value)** set all potentiometers to the same value e.g. 0 or max or mid -- **uint8_t getValue(uint8_t pm)** returns the last set value of a specific potentiometer +- **bool setValue(uint8_t pm = 0, uint8_t value = 128)** set a potentiometer to a value. +Default value is middle value. +Returns true if successful, false if not. +- **void setAll(uint8_t value)** set all potentiometers to the same value e.g. 0 or max or mid value. +- **uint8_t getValue(uint8_t pm = 0)** returns the last set value of a specific potentiometer. - **void reset(uint8_t value = 128)** resets all potentiometers to value, default 128. +- **bool setPercentage(uint8_t pm = 0, float percentage = 50)** similar to setValue, percentage from 0..100% +Returns true when successful, false if not. +- **float getPercentage(uint8_t pm = 0)** return the value of potentiometer pm as percentage. ### Hardware SPI -To be used only if one needs a specific speed. +To be used only if one needs a specific speed for hardware SPI. +Has no effect on software SPI. -- **void setSPIspeed(uint32_t speed)** set SPI transfer rate -- **uint32_t getSPIspeed()** returns SPI transfer rate +- **void setSPIspeed(uint32_t speed)** set SPI transfer rate. +- **uint32_t getSPIspeed()** returns SPI transfer rate. ### ESP32 specific @@ -79,13 +91,12 @@ in the future. - **bool usesHSPI()** returns true if HSPI is used. - **bool usesVSPI()** returns true if VSPI is used. -The **selectVSPI()** or the **selectHSPI()** needs to be called -BEFORE the **begin()** function. +The **selectVSPI()** or the **selectHSPI()** needs to be called BEFORE the **begin()** function. ### Misc -- **int pmCount()** returns the number of internal potentiometers. +- **uint8_t pmCount()** returns the number of internal potentiometers. - **void powerOn()** switches the module on. - **void powerOff()** switches the module off. - **void powerDown()** OBSOLETE => use powerOff() instead. @@ -94,27 +105,10 @@ BEFORE the **begin()** function. ## Future - -### must 0.2.0 - -- **void setPercentage(uint8_t pm, float percentage)** 0..100% -- **float getPercentage(uint8_t pm)** -- **void setValue(uint8_t pm, uint8_t value = 128)** add default.. -- **int pmCount()** should return uint8_t. - - -### should - -Easier than resoldering. - -- **void setInvert(uint8_t pm)** invert flag per potentiometer +- **void setInvert(uint8_t pm)** invert flag per potentiometer. - 0..255 -> 255..0 - 1 uint8_t can hold 8 flags - **bool getInvert(uint8_t pm)** - - -### could - - **void follow(pm_B, pm_A, float percentage = 100)** - makes pm_B follow pm_A unless pm_B is addressed explicitly - e.g. to be used for stereo channels. @@ -123,6 +117,7 @@ Easier than resoldering. - **void setGamma(uint8_t pm, float gamma)** - logarithmic effect? easier with setPercentage() - see gamma library. +- **middle value** 127 ? ## Operations diff --git a/examples/AD5204_demo/AD5204_demo.ino b/examples/AD5204_demo/AD5204_demo.ino index 1a32284..cdf5c4a 100644 --- a/examples/AD5204_demo/AD5204_demo.ino +++ b/examples/AD5204_demo/AD5204_demo.ino @@ -1,21 +1,21 @@ // // FILE: AD5204_demo.ino // AUTHOR: Rob Tillaart -// VERSION: 0.1.0 // PURPOSE: demo // DATE: 2020-07-24 // URL: https://github.com/RobTillaart/AD520X + #include "AD520X.h" uint32_t start, stop; -#define TWOPI (3.14159265 * 2) -// select, reset, shutdown, data, clock -// AD5204 pot(10, 255, 255, 8, 9); // SW SPI +// select, reset, shutdown, data, clock +// AD5204 pot(10, 255, 255, 8, 9); // SW SPI AD5204 pot = AD5204(10, 12, 13); // HW SPI + void setup() { Serial.begin(115200); @@ -30,12 +30,14 @@ void setup() Serial.println("\nDone..."); } + void loop() { } -// connect all A GND and B 5V -// every W will have a different signal (same freq). + +// connect all A GND and B 5V +// every W will have a different signal (same freq). void test_sinus() { Serial.println(__FUNCTION__); @@ -45,7 +47,7 @@ void test_sinus() uint32_t i = 0; while (millis() - start < 10000) { - uint8_t value = 127 * sin(i * TWOPI / 100); + uint8_t value = 127 * sin(i * TWO_PI / 100); pot.setValue(0, 128 + value); pot.setValue(1, 128 + value / 2); pot.setValue(2, 64 + value / 2); @@ -56,7 +58,7 @@ void test_sinus() } } -// straightforward sawtooth. +// straightforward sawtooth. void test_sawtooth() { Serial.println(__FUNCTION__); @@ -66,10 +68,11 @@ void test_sawtooth() uint8_t i = 0; while (millis() - start < 10000) { - pot.setValue(0, i++); // autowrap is fast... + pot.setValue(0, i++); // auto wrap is fast... } } + void test_timing() { Serial.println(__FUNCTION__); @@ -78,7 +81,7 @@ void test_timing() start = micros(); for (int i = 0; i < 10000; i++) { - pot.setValue(0, i++); // autowrap is fast... + pot.setValue(0, i++); // auto wrap is fast... } stop = micros(); Serial.print("10000 x setValue():\t"); @@ -108,7 +111,7 @@ void test_timing() Serial.print("1000 x getValue():\t"); Serial.println(stop - start); delay(10); - } + // -- END OF FILE -- diff --git a/examples/AD5204_demo_HWSPI/AD5204_demo_HWSPI.ino b/examples/AD5204_demo_HWSPI/AD5204_demo_HWSPI.ino index 597ea3e..1d72ba8 100644 --- a/examples/AD5204_demo_HWSPI/AD5204_demo_HWSPI.ino +++ b/examples/AD5204_demo_HWSPI/AD5204_demo_HWSPI.ino @@ -1,7 +1,6 @@ // // FILE: AD5204_demo_HWSPI.ino // AUTHOR: Rob Tillaart -// VERSION: 0.1.2 // PURPOSE: demo // DATE: 2021-08-19 // URL: https://github.com/RobTillaart/AD520X @@ -12,10 +11,10 @@ uint32_t start, stop; -#define TWOPI (3.14159265 * 2) -// param: select, reset, shutdown, data, clock -// AD5204 pot(10, 255, 255, 8, 9); // SW SPI + +// param: select, reset, shutdown, data, clock +// AD5204 pot(10, 255, 255, 8, 9); // SW SPI AD5204 pot = AD5204(10, 12, 13); // HW SPI @@ -56,8 +55,8 @@ void loop() } -// connect all A GND and B 5V -// every W will have a different signal (same freq). +// connect all A GND and B 5V +// every W will have a different signal (same freq). void test_sinus() { Serial.println(__FUNCTION__); @@ -67,7 +66,7 @@ void test_sinus() uint32_t i = 0; while (millis() - start < 10000) { - uint8_t value = 127 * sin(i * TWOPI / 100); + uint8_t value = 127 * sin(i * TWO_PI / 100); pot.setValue(0, 128 + value); pot.setValue(1, 128 + value / 2); pot.setValue(2, 64 + value / 2); @@ -79,7 +78,7 @@ void test_sinus() } -// straightforward sawtooth. +// straightforward sawtooth. void test_sawtooth() { Serial.println(__FUNCTION__); @@ -89,7 +88,7 @@ void test_sawtooth() uint8_t i = 0; while (millis() - start < 10000) { - pot.setValue(0, i++); // autowrap is fast... + pot.setValue(0, i++); // auto wrap is fast... } } @@ -102,7 +101,7 @@ void test_timing() start = micros(); for (int i = 0; i < 10000; i++) { - pot.setValue(0, i++); // autowrap is fast... + pot.setValue(0, i++); // auto wrap is fast... } stop = micros(); Serial.print("10000 x setValue():\t"); diff --git a/examples/AD5204_demo_SWSPI/AD5204_demo_SWSPI.ino b/examples/AD5204_demo_SWSPI/AD5204_demo_SWSPI.ino index 622f6bd..432560d 100644 --- a/examples/AD5204_demo_SWSPI/AD5204_demo_SWSPI.ino +++ b/examples/AD5204_demo_SWSPI/AD5204_demo_SWSPI.ino @@ -14,9 +14,9 @@ uint32_t start, stop; #define TWOPI (3.14159265 * 2) -// param: select, reset, shutdown, data, clock +// param: select, reset, shutdown, data, clock AD5204 pot(10, 255, 255, 8, 9); // SW SPI -// AD5204 pot = AD5204(10, 12, 13); // HW SPI +// AD5204 pot = AD5204(10, 12, 13); // HW SPI void setup() @@ -45,8 +45,8 @@ void loop() } -// connect all A GND and B 5V -// every W will have a different signal (same freq). +// connect all A GND and B 5V +// every W will have a different signal (same freq). void test_sinus() { Serial.println(__FUNCTION__); @@ -68,7 +68,7 @@ void test_sinus() } -// straightforward sawtooth. +// straightforward sawtooth. void test_sawtooth() { Serial.println(__FUNCTION__); @@ -78,7 +78,7 @@ void test_sawtooth() uint8_t i = 0; while (millis() - start < 10000) { - pot.setValue(0, i++); // autowrap is fast... + pot.setValue(0, i++); // auto wrap is fast... } } @@ -91,7 +91,7 @@ void test_timing() start = micros(); for (int i = 0; i < 10000; i++) { - pot.setValue(0, i++); // autowrap is fast... + pot.setValue(0, i++); // auto wrap is fast... } stop = micros(); Serial.print("10000 x setValue():\t"); diff --git a/examples/AD5204_setPercentage/AD5204_setPercentage.ino b/examples/AD5204_setPercentage/AD5204_setPercentage.ino new file mode 100644 index 0000000..4c968f3 --- /dev/null +++ b/examples/AD5204_setPercentage/AD5204_setPercentage.ino @@ -0,0 +1,54 @@ +// +// FILE: AD5204_setPercentage.ino +// AUTHOR: Rob Tillaart +// PURPOSE: demo +// DATE: 2021-10-16 +// URL: https://github.com/RobTillaart/AD520X + + +#include "AD520X.h" + +uint32_t start, stop; + + +// select, reset, shutdown, data, clock +// AD5204 pot(10, 255, 255, 8, 9); // SW SPI +AD5204 pot = AD5204(10, 12, 13); // HW SPI + + +void setup() +{ + Serial.begin(115200); + Serial.println(__FILE__); + + pot.begin(4); + + for (int p = 0; p <= 100; p++) + { + pot.setPercentage(0, p); + float pp = pot.getPercentage(0); + Serial.print(p); + Serial.print('\t'); + Serial.print(pp, 1); + Serial.print('\n'); + } + + Serial.println("\nDone..."); +} + + +void loop() +{ + start = millis(); + uint32_t i = 0; + while (millis() - start < 10000) + { + float perc = 50 + sin(i * TWO_PI * 0.01) * 49.0; + pot.setPercentage(0, perc); + i++; + } + delay(1000); +} + + +// -- END OF FILE -- diff --git a/keywords.txt b/keywords.txt index b0f8c55..60d0ba9 100644 --- a/keywords.txt +++ b/keywords.txt @@ -14,6 +14,10 @@ begin KEYWORD2 setValue KEYWORD2 setAll KEYWORD2 getValue KEYWORD2 + +setPercentage KEYWORD2 +getPercentage KEYWORD2 + reset KEYWORD2 powerOn KEYWORD2 diff --git a/library.json b/library.json index 7e8923a..f4eb6fb 100644 --- a/library.json +++ b/library.json @@ -15,7 +15,7 @@ "type": "git", "url": "https://github.com/RobTillaart/AD520X.git" }, - "version": "0.1.2", + "version": "0.2.0", "license": "MIT", "frameworks": "arduino", "platforms": "*" diff --git a/library.properties b/library.properties index 4ead4c3..7dd12fa 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=AD520X -version=0.1.2 +version=0.2.0 author=Rob Tillaart maintainer=Rob Tillaart sentence=Arduino library for SPI AD5204 and AD5206 digital potentiometers diff --git a/test/unit_test_001.cpp b/test/unit_test_001.cpp index cdebc1a..734cfbd 100644 --- a/test/unit_test_001.cpp +++ b/test/unit_test_001.cpp @@ -66,6 +66,25 @@ unittest(test_setValue) { assertEqual(42, pot.getValue(i)); } + + assertFalse(pot.setValue(6, 10)); + + AD8400 p8400 = AD8400(10, 12, 13); + assertFalse(p8400.setValue(1, 117)); +} + + +unittest(test_setPercentage) +{ + AD5206 pot = AD5206(10, 12, 13); // HW SPI + pot.begin(); + assertEqualFloat(50, pot.getPercentage(0), 0.5); + + for (int i = 0; i < pot.pmCount(); i++) + { + pot.setPercentage(i, 35); + assertEqualFloat(35, pot.getPercentage(i), 0.5); + } }