0.1.2 AD520X

This commit is contained in:
rob tillaart 2021-08-22 10:47:15 +02:00
parent 348f6cceff
commit 627e4e3e64
10 changed files with 501 additions and 62 deletions

View File

@ -9,5 +9,4 @@ jobs:
steps:
- uses: actions/checkout@v2
- uses: Arduino-CI/action@master
# Arduino-CI/action@v0.1.1
- uses: Arduino-CI/action@v0.1.0

View File

@ -2,15 +2,18 @@
// FILE: AD520X.cpp
// AUTHOR: Rob Tillaart
// DATE: 2020-07-24
// VERSION: 0.1.1
// VERSION: 0.1.2
// PURPOSE: Arduino library for AD5204 and AD5206 digital potentiometers (+ older AD8400, AD8402, AD8403)
// URL: https://github.com/RobTillaart/AD520X
//
// HISTORY:
// 0.0.1 2020-07-24 initial version
// 0.0.2 2020-07-25 support for AD8400 series in documentation.
// 0.1.0 2020-07-26 refactor, fix #2 select pin for HW SPI; add shutdown.
// 0.1.1 2020-12-08 Arduino-CI + unit test + isPowerOn()
// 0.0.1 2020-07-24 initial version
// 0.0.2 2020-07-25 support for AD8400 series in documentation.
// 0.1.0 2020-07-26 refactor, fix #2 select pin for HW SPI; add shutdown.
// 0.1.1 2020-12-08 Arduino-CI + unit test + isPowerOn()
// 0.1.2 2021-08-19 VSPI / HSPI support for ESP32 only
// add setGPIOpins for ESP32 only
// add SetSPIspeed (generic)
#include "AD520X.h"
@ -20,13 +23,14 @@ AD520X::AD520X(uint8_t select, uint8_t reset, uint8_t shutdown, uint8_t dataOut,
{
_pmCount = 6;
_select = select;
_data = dataOut;
_dataOut = dataOut;
_clock = clock;
_reset = reset;
_shutdown = shutdown;
_hwSPI = (dataOut == 255) && (clock == 255);
}
// initializes the pins and starts SPI in case of hardware SPI
void AD520X::begin(uint8_t value)
{
@ -37,22 +41,57 @@ void AD520X::begin(uint8_t value)
pinMode(_shutdown, OUTPUT);
digitalWrite(_shutdown, LOW);
_spi_settings = SPISettings(_SPIspeed, MSBFIRST, SPI_MODE1);
if(_hwSPI)
{
SPI.begin();
#if defined(ESP32)
if (_useHSPI) // HSPI
{
mySPI = new SPIClass(HSPI);
mySPI->end();
mySPI->begin(14, 12, 13, _select); // CLK=14 MISO=12 MOSI=13
}
else // VSPI
{
mySPI = new SPIClass(VSPI);
mySPI->end();
mySPI->begin(18, 19, 23, _select); // CLK=18 MISO=19 MOSI=23
}
#else // generic hardware SPI
mySPI = &SPI;
mySPI->end();
mySPI->begin();
#endif
delay(1);
}
else
{
pinMode(_data, OUTPUT);
pinMode(_clock, OUTPUT);
digitalWrite(_data, LOW);
digitalWrite(_clock, LOW);
pinMode(_dataOut, OUTPUT);
pinMode(_clock, OUTPUT);
digitalWrite(_dataOut, LOW);
digitalWrite(_clock, LOW);
}
setAll(value);
}
#if defined(ESP32)
void AD520X::setGPIOpins(uint8_t clk, uint8_t miso, uint8_t mosi, uint8_t select)
{
_clock = clk;
_dataOut = mosi;
_select = select;
pinMode(_select, OUTPUT);
digitalWrite(_select, HIGH);
mySPI->end(); // disable SPI and restart
mySPI->begin(clk, miso, mosi, select);
}
#endif
void AD520X::setValue(uint8_t pm, uint8_t value)
{
if (pm >= _pmCount) return;
@ -60,6 +99,7 @@ void AD520X::setValue(uint8_t pm, uint8_t value)
updateDevice(pm);
}
void AD520X::setAll(uint8_t value)
{
for (uint8_t pm = 0; pm < _pmCount; pm++)
@ -68,12 +108,14 @@ void AD520X::setAll(uint8_t value)
}
}
uint8_t AD520X::getValue(uint8_t pm)
{
if (pm >= _pmCount) return 0;
return _value[pm];
}
void AD520X::reset(uint8_t value)
{
digitalWrite(_reset, HIGH);
@ -81,64 +123,83 @@ void AD520X::reset(uint8_t value)
setAll(value);
}
void AD520X::setSPIspeed(uint32_t speed)
{
_SPIspeed = speed;
_spi_settings = SPISettings(_SPIspeed, MSBFIRST, SPI_MODE1);
};
/////////////////////////////////////////////////////////////////////////////
//
// PROTECTED
//
void AD520X::updateDevice(uint8_t pm)
{
digitalWrite(_select, LOW);
if (_hwSPI)
{
SPI.beginTransaction(SPISettings(16000000, MSBFIRST, SPI_MODE1));
digitalWrite(_select, LOW);
SPI.transfer(pm);
SPI.transfer(_value[pm]);
digitalWrite(_select, HIGH);
SPI.endTransaction();
mySPI->beginTransaction(_spi_settings);
mySPI->transfer(pm);
mySPI->transfer(_value[pm]);
mySPI->endTransaction();
}
else // Software SPI
{
digitalWrite(_select, LOW);
swSPI_transfer(pm);
swSPI_transfer(_value[pm]);
digitalWrite(_select, HIGH);
}
digitalWrite(_select, HIGH);
}
// simple one mode version
void AD520X::swSPI_transfer(uint8_t value)
void AD520X::swSPI_transfer(uint8_t val)
{
uint8_t clk = _clock;
uint8_t dao = _dataOut;
for (uint8_t mask = 0x80; mask; mask >>= 1)
{
digitalWrite(_data,(value & mask) != 0);
digitalWrite(_clock, HIGH);
digitalWrite(_clock, LOW);
digitalWrite(dao,(val & mask));
digitalWrite(clk, HIGH);
digitalWrite(clk, LOW);
}
}
/////////////////////////////////////////////////////////////////////////////
//
// 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)
{
_pmCount = 6;
}
AD5204::AD5204(uint8_t select, uint8_t reset, uint8_t shutdown, uint8_t dataOut, uint8_t clock)
: AD520X(select, reset, shutdown, dataOut, clock)
{
_pmCount = 4;
}
AD8403::AD8403(uint8_t select, uint8_t reset, uint8_t shutdown, uint8_t dataOut, uint8_t clock)
: AD520X(select, reset, shutdown, dataOut, clock)
{
_pmCount = 4;
}
AD8402::AD8402(uint8_t select, uint8_t reset, uint8_t shutdown, uint8_t dataOut, uint8_t clock)
: AD520X(select, reset, shutdown, dataOut, clock)
{
_pmCount = 2;
}
AD8400::AD8400(uint8_t select, uint8_t reset, uint8_t shutdown, uint8_t dataOut, uint8_t clock)
: AD520X(select, reset, shutdown, dataOut, clock)
{

View File

@ -3,7 +3,7 @@
// FILE: AD520X.h
// AUTHOR: Rob Tillaart
// DATE: 2020-07-24
// VERSION: 0.1.1
// VERSION: 0.1.2
// PURPOSE: Arduino library for AD5204 and AD5206 digital potentiometers (+ older AD8400, AD8402, AD8403)
// URL: https://github.com/RobTillaart/AD520X
//
@ -11,10 +11,13 @@
// see AD520X.cpp file
//
#include "Arduino.h"
#include "SPI.h"
#define AD520X_LIB_VERSION "0.1.1"
#define AD520X_LIB_VERSION (F("0.1.2"))
class AD520X
{
@ -34,48 +37,81 @@ public:
void powerDown() { powerOff(); }; // will become obsolete
bool isPowerOn() { return digitalRead(_shutdown) == LOW; };
// speed in Hz
void setSPIspeed(uint32_t speed);
uint32_t getSPIspeed() { return _SPIspeed; };
// debugging
bool usesHWSPI() { return _hwSPI; };
// ESP32 specific
#if defined(ESP32)
void selectHSPI() { _useHSPI = true; };
void selectVSPI() { _useHSPI = false; };
bool usesHSPI() { return _useHSPI; };
bool usesVSPI() { return !_useHSPI; };
// to overrule ESP32 default hardware pins
void setGPIOpins(uint8_t clk, uint8_t miso, uint8_t mosi, uint8_t select);
#endif
protected:
uint8_t _data;
uint8_t _clock;
uint8_t _select;
uint8_t _reset;
uint8_t _shutdown;
bool _hwSPI = 3;
uint8_t _value[6];
uint8_t _pmCount = 6;
uint8_t _dataOut;
uint8_t _clock;
uint8_t _select;
uint8_t _reset;
uint8_t _shutdown;
bool _hwSPI = 3;
uint32_t _SPIspeed = 16000000;
void updateDevice(uint8_t pm);
void swSPI_transfer(uint8_t value);
uint8_t _value[6];
uint8_t _pmCount = 6;
void updateDevice(uint8_t pm);
void swSPI_transfer(uint8_t value);
SPIClass * mySPI;
SPISettings _spi_settings;
#if defined(ESP32)
bool _useHSPI = true;
#endif
};
/////////////////////////////////////////////////////////////////////////////
//
// DERIVED CLASSES
//
class AD5206 : public AD520X
{
public:
AD5206(uint8_t select, uint8_t reset, uint8_t shutdown, uint8_t dataOut = 255, uint8_t clock = 255);
};
class AD5204 : public AD520X
{
public:
AD5204(uint8_t select, uint8_t reset, uint8_t shutdown, uint8_t dataOut = 255, uint8_t clock = 255);
};
class AD8400 : public AD520X
{
public:
AD8400(uint8_t select, uint8_t reset, uint8_t shutdown, uint8_t dataOut = 255, uint8_t clock = 255);
};
class AD8402 : public AD520X
{
public:
AD8402(uint8_t select, uint8_t reset, uint8_t shutdown, uint8_t dataOut = 255, uint8_t clock = 255);
};
class AD8403 : public AD520X
{
public:

View File

@ -7,6 +7,8 @@
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).
@ -16,15 +18,27 @@ 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.
| type | # channels |
|:-------|:----------:|
| AD5204 | 4 |
| AD5206 | 6 |
| AD8400 | 1 |
| AD8402 | 2 |
| 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
(datasheet comparison). If you can confirm it works, please let me know._
## Interface
### Constructors
All pins are type uint8_t
- **AD520X(select, reset, shutdown, dataOut = 255, clock = 255)** constructor
Base class, not to be used directly.
If dataOut and clock are set to 255 (default) it uses hardware SPI.
@ -37,36 +51,78 @@ Note: hardware SPI is 10+ times faster on an UNO.
- **AD8402(select, reset, shutdown, dataOut = 255, clock = 255)** uses 2 pm.
- **AD8403(select, reset, shutdown, dataOut = 255, clock = 255)** uses 4 pm.
### Base
- **begin(value = 128)** value is the initial value of all potentiometer.
- **setValue(pm, value)** set a potentiometer to a value
- **setAll(value)** set all potentiometers to the same value e.g. 0 or max or mid
- **getValue(pm)** returns the last set value of a specific potmeter
- **reset(value = 128)** resets all potentiometers to value, default 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
- **void reset(uint8_t value = 128)** resets all potentiometers to value, default 128.
### Hardware SPI
To be used only if one needs a specific speed.
- **void setSPIspeed(uint32_t speed)** set SPI transfer rate
- **uint32_t getSPIspeed()** returns SPI transfer rate
### ESP32 specific
This functionality is new in 0.1.2 and it is expected that the interface will change
in the future.
- **void selectHSPI()** in case hardware SPI, the ESP32 has two options HSPI and VSPI.
- **void selectVSPI()** see above.
- **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.
### Misc
- **pmCount()** returns the number of internal potmeters.
- **powerOn()** switches the module on
- **powerOff()** switches the module off
- **powerDown()** use powerOFf() instead
- **isPowerOn()** returns true if on (default) or
- **int 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.
- **bool isPowerOn()** returns true if on (default) or false if off.
## Future
- **setInvert(pm)** invert flag per potmeter
### 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
- 0..255 -> 255..0
- 1 uint8_t can hold 8 flags
- **getInvert(pm)**
- **bool getInvert(uint8_t pm)**
- **follow(pm_B, pm_A, percentage = 100)**
### 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.
- array cascade = 0xFF or pm_A.
- It will follow pm_A for certain percentage default 100.
- **setPercentage(pm, float value)** 0..100%
- **getPercentage(pm)**
- logarithmic effect? setGamma(pm, value);
easier with setPercentage()
- **void setGamma(uint8_t pm, float gamma)**
- logarithmic effect? easier with setPercentage()
- see gamma library.
## Operations

View File

@ -0,0 +1,138 @@
//
// FILE: AD5204_demo_HWSPI.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.2
// PURPOSE: demo
// DATE: 2021-08-19
// URL: https://github.com/RobTillaart/AD520X
#include "AD520X.h"
uint32_t start, stop;
#define TWOPI (3.14159265 * 2)
// param: 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__);
Serial.print("AD520X_LIB_VERSION:\t");
Serial.println(AD520X_LIB_VERSION);
pot.begin(4);
Serial.print("\tusesHWSPI:\t");
Serial.println(pot.usesHWSPI());
test_sinus();
test_sawtooth();
pot.setSPIspeed(500000);
test_timing();
pot.setSPIspeed(1000000);
test_timing();
pot.setSPIspeed(2000000);
test_timing();
pot.setSPIspeed(4000000);
test_timing();
pot.setSPIspeed(8000000);
test_timing();
pot.setSPIspeed(16000000); // no effect on 16 MHz UNO
test_timing();
Serial.println("\nDone...");
}
void loop()
{
}
// connect all A GND and B 5V
// every W will have a different signal (same freq).
void test_sinus()
{
Serial.println(__FUNCTION__);
delay(10);
start = millis();
uint32_t i = 0;
while (millis() - start < 10000)
{
uint8_t value = 127 * sin(i * TWOPI / 100);
pot.setValue(0, 128 + value);
pot.setValue(1, 128 + value / 2);
pot.setValue(2, 64 + value / 2);
pot.setValue(3, 192 + value / 2);
pot.setValue(4, 224 + value / 4);
pot.setValue(5, 128 - value);
i++;
}
}
// straightforward sawtooth.
void test_sawtooth()
{
Serial.println(__FUNCTION__);
delay(10);
start = millis();
uint8_t i = 0;
while (millis() - start < 10000)
{
pot.setValue(0, i++); // autowrap is fast...
}
}
void test_timing()
{
Serial.println(__FUNCTION__);
delay(10);
start = micros();
for (int i = 0; i < 10000; i++)
{
pot.setValue(0, i++); // autowrap is fast...
}
stop = micros();
Serial.print("10000 x setValue():\t");
Serial.println(stop - start);
delay(10);
start = micros();
for (int i = 0; i < 1000; i++)
{
pot.setAll(i++);
}
stop = micros();
Serial.print("1000 x setAll():\t");
Serial.println(stop - start);
delay(10);
volatile int x = 0;
start = micros();
for (int i = 0; i < 250; i++)
{
x += pot.getValue(0);
x += pot.getValue(1);
x += pot.getValue(2);
x += pot.getValue(3);
}
stop = micros();
Serial.print("1000 x getValue():\t");
Serial.println(stop - start);
delay(10);
}
// -- END OF FILE --

View File

@ -0,0 +1,127 @@
//
// FILE: AD5204_demo_SWSPI.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.2
// PURPOSE: demo
// DATE: 2021-08-19
// URL: https://github.com/RobTillaart/AD520X
#include "AD520X.h"
uint32_t start, stop;
#define TWOPI (3.14159265 * 2)
// param: 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__);
Serial.print("AD520X_LIB_VERSION:\t");
Serial.println(AD520X_LIB_VERSION);
pot.begin(4);
Serial.print("\tusesHWSPI:\t");
Serial.println(pot.usesHWSPI());
test_sinus();
test_sawtooth();
test_timing();
Serial.println("\nDone...");
}
void loop()
{
}
// connect all A GND and B 5V
// every W will have a different signal (same freq).
void test_sinus()
{
Serial.println(__FUNCTION__);
delay(10);
start = millis();
uint32_t i = 0;
while (millis() - start < 10000)
{
uint8_t value = 127 * sin(i * TWOPI / 100);
pot.setValue(0, 128 + value);
pot.setValue(1, 128 + value / 2);
pot.setValue(2, 64 + value / 2);
pot.setValue(3, 192 + value / 2);
pot.setValue(4, 224 + value / 4);
pot.setValue(5, 128 - value);
i++;
}
}
// straightforward sawtooth.
void test_sawtooth()
{
Serial.println(__FUNCTION__);
delay(10);
start = millis();
uint8_t i = 0;
while (millis() - start < 10000)
{
pot.setValue(0, i++); // autowrap is fast...
}
}
void test_timing()
{
Serial.println(__FUNCTION__);
delay(10);
start = micros();
for (int i = 0; i < 10000; i++)
{
pot.setValue(0, i++); // autowrap is fast...
}
stop = micros();
Serial.print("10000 x setValue():\t");
Serial.println(stop - start);
delay(10);
start = micros();
for (int i = 0; i < 1000; i++)
{
pot.setAll(i++);
}
stop = micros();
Serial.print("1000 x setAll():\t");
Serial.println(stop - start);
delay(10);
volatile int x = 0;
start = micros();
for (int i = 0; i < 250; i++)
{
x += pot.getValue(0);
x += pot.getValue(1);
x += pot.getValue(2);
x += pot.getValue(3);
}
stop = micros();
Serial.print("1000 x getValue():\t");
Serial.println(stop - start);
delay(10);
}
// -- END OF FILE --

View File

@ -1,6 +1,6 @@
# Syntax Coloring Map For AD520X
# Syntax Colouring Map For AD520X
# Datatypes (KEYWORD1)
# Data types (KEYWORD1)
AD520X KEYWORD1
AD5204 KEYWORD1
AD5206 KEYWORD1
@ -8,17 +8,31 @@ AD8400 KEYWORD1
AD8402 KEYWORD1
AD8403 KEYWORD1
# Methods and Functions (KEYWORD2)
begin KEYWORD2
setValue KEYWORD2
setAll KEYWORD2
getValue KEYWORD2
reset KEYWORD2
powerOn KEYWORD2
powerOff KEYWORD2
powerDown KEYWORD2
isPowerOn KEYWORD2
pmCount KEYWORD2
setSPIspeed KEYWORD2
getSPIspeed KEYWORD2
selectHSPI KEYWORD2
selectVSPI KEYWORD2
usesHSPI KEYWORD2
usesVSPI KEYWORD2
setGPIOpins KEYWORD2
# Constants (LITERAL1)
AD520X_LIB_VERSION LITERAL1

View File

@ -15,7 +15,7 @@
"type": "git",
"url": "https://github.com/RobTillaart/AD520X.git"
},
"version": "0.1.1",
"version": "0.1.2",
"license": "MIT",
"frameworks": "arduino",
"platforms": "*"

View File

@ -1,5 +1,5 @@
name=AD520X
version=0.1.1
version=0.1.2
author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=Arduino library for SPI AD5204 and AD5206 digital potentiometers

View File

@ -20,6 +20,7 @@
// assertFalse(actual)
// assertNull(actual)
#include <ArduinoUnitTests.h>
#include "Arduino.h"
@ -30,12 +31,15 @@ unittest_setup()
{
}
unittest_teardown()
{
}
unittest(test_begin)
{
fprintf(stderr, "AD520X_LIB_VERSION:\t%s", AD520X_LIB_VERSION);
AD5204 pot = AD5204(10, 12, 13); // HW SPI
pot.begin();
assertEqual(128, pot.getValue(0));
@ -44,6 +48,7 @@ unittest(test_begin)
assertEqual(42, pot.getValue(0));
}
unittest(test_setValue)
{
AD5206 pot = AD5206(10, 12, 13); // HW SPI
@ -71,7 +76,10 @@ unittest(test_reset)
assertEqual(128, pot.getValue(0));
pot.reset(35);
assertEqual(35, pot.getValue(0));
for (int i = 0; i < pot.pmCount(); i++)
{
assertEqual(35, pot.getValue(i));
}
}