From 76bdcc71255fefc7dff2492458bd9abf1b9ad24e Mon Sep 17 00:00:00 2001 From: Rob Tillaart Date: Wed, 10 Jul 2024 11:25:40 +0200 Subject: [PATCH] 0.2.0 MCP4261 --- libraries/MCP4261/CHANGELOG.md | 17 ++ libraries/MCP4261/MCP4261.cpp | 189 ++++++++++--- libraries/MCP4261/MCP4261.h | 76 ++++-- libraries/MCP4261/README.md | 251 ++++++++++++------ .../examples/MCP4261_demo/MCP4261_demo.ino | 36 +-- .../MCP4261_demo_eeprom.ino | 58 ++++ .../MCP4261_demo_read/MCP4261_demo_read.ino | 44 +++ .../MCP4261_demo_tcon/MCP4261_demo_tcon.ino | 46 ++++ .../MCP4261_performance.ino | 19 +- .../MCP4261_performance/performance_0.2.0.txt | 57 ++++ .../MCP4261_register_read/.arduino-ci.yml | 32 +++ .../MCP4261_register_read.ino | 48 ++++ libraries/MCP4261/keywords.txt | 14 +- libraries/MCP4261/library.json | 2 +- libraries/MCP4261/library.properties | 2 +- libraries/MCP4261/test/unit_test_001.cpp | 6 +- 16 files changed, 711 insertions(+), 186 deletions(-) create mode 100644 libraries/MCP4261/examples/MCP4261_demo_eeprom/MCP4261_demo_eeprom.ino create mode 100644 libraries/MCP4261/examples/MCP4261_demo_read/MCP4261_demo_read.ino create mode 100644 libraries/MCP4261/examples/MCP4261_demo_tcon/MCP4261_demo_tcon.ino create mode 100644 libraries/MCP4261/examples/MCP4261_performance/performance_0.2.0.txt create mode 100644 libraries/MCP4261/examples/MCP4261_register_read/.arduino-ci.yml create mode 100644 libraries/MCP4261/examples/MCP4261_register_read/MCP4261_register_read.ino diff --git a/libraries/MCP4261/CHANGELOG.md b/libraries/MCP4261/CHANGELOG.md index 4f96407a..b0618d75 100644 --- a/libraries/MCP4261/CHANGELOG.md +++ b/libraries/MCP4261/CHANGELOG.md @@ -6,5 +6,22 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [0.2.0] - 2024-07-09 +- refactor 0.1.0 implementation + - use less magic numbers + - reimplement protected functions +- remove parameter from **begin()** reads value from device. (breaking) +- fix the supported range to 0..256 (was 255). +- add **uint16_t getValueDevice(uint8_t pm)** +- add **uint16_t getValueNV(uint8_t pm)** +- add **void setTCONMask(uint16_t mask)** +- add **uint16_t getTCONMask()** +- add **uint16_t getStatusMask()** +- add **bool setEEPROM(uint8_t index, uint16_t value)** +- add **uint16_t getEEPROM(uint8_t index)** +- update readme.md +- minor edits. + + ## [0.1.0] - 2024-02-21 - initial version diff --git a/libraries/MCP4261/MCP4261.cpp b/libraries/MCP4261/MCP4261.cpp index 1da20392..23217729 100644 --- a/libraries/MCP4261/MCP4261.cpp +++ b/libraries/MCP4261/MCP4261.cpp @@ -1,7 +1,7 @@ // // FILE: MCP4261.cpp // AUTHOR: Rob Tillaart -// VERSION: 0.1.0 +// VERSION: 0.2.0 // DATE: 2024-02-21 // PURPOSE: Arduino library for MCP4261 SPI based digital potentiometers. // URL: https://github.com/RobTillaart/MCP4261 @@ -10,18 +10,21 @@ #include "MCP4261.h" -// see page 18 datasheet -#define MCP4261_IGNORE_CMD 0x00 -#define MCP4261_WRITE_CMD 0x00 -#define MCP4261_SHUTDOWN_CMD 0x20 -#define MCP4261_NONE_CMD 0x30 +#define MCP4261_CMD_WRITE 0x00 +#define MCP4261_CMD_INCR 0x04 +#define MCP4261_CMD_DECR 0x08 +#define MCP4261_CMD_READ 0x0C + +#define MCP4261_REG_TCON 0x04 +#define MCP4261_REG_STATUS 0x05 +#define MCP4261_REG_EEPROM(x) (0x06 + x) // HARDWARE SPI MCP4261::MCP4261(uint8_t select, uint8_t shutdown, __SPI_CLASS__ * mySPI) { _pmCount = 2; - _maxValue = 255; + _maxValue = MCP42XX_MAX_VALUE; _select = select; _shutdown = shutdown; _dataIn = 255; @@ -37,7 +40,7 @@ MCP4261::MCP4261(uint8_t select, uint8_t shutdown, __SPI_CLASS__ * mySPI) MCP4261::MCP4261(uint8_t select, uint8_t shutdown, uint8_t dataIn, uint8_t dataOut, uint8_t clock) { _pmCount = 2; - _maxValue = 255; + _maxValue = MCP42XX_MAX_VALUE; _select = select; _shutdown = shutdown; _dataIn = dataIn; @@ -48,7 +51,7 @@ MCP4261::MCP4261(uint8_t select, uint8_t shutdown, uint8_t dataIn, uint8_t dataO } -void MCP4261::begin(uint16_t value) +void MCP4261::begin() { pinMode(_select, OUTPUT); digitalWrite(_select, HIGH); @@ -71,7 +74,9 @@ void MCP4261::begin(uint16_t value) digitalWrite(_dataOut, LOW); digitalWrite(_clock, LOW); } - reset(value); + // get initial values from device. + getValueDevice(0); + getValueDevice(1); } @@ -83,6 +88,11 @@ void MCP4261::reset(uint16_t value) } +uint8_t MCP4261::pmCount() +{ + return _pmCount; +} + ///////////////////////////////////////////////////////////////////////////// // @@ -102,10 +112,9 @@ bool MCP4261::setValue(uint8_t pm, uint16_t value) if (value > _maxValue) return false; _value[pm] = value; - uint8_t cmd = 0x00; - if (pm == 1) cmd = 0x10; - if (value > 0xFF) cmd |= (value >> 8); // high bits - writeDevice(2, cmd, value & 0xFF); + uint8_t value1 = MCP4261_CMD_WRITE | (pm << 4); + if (value > 0xFF) value1 |= (value >> 8); // high bits + writeRegister2(value1, value & 0xFF); return true; } @@ -117,15 +126,23 @@ uint16_t MCP4261::getValue(uint8_t pm) } +uint16_t MCP4261::getValueDevice(uint8_t pm) +{ + if (pm >= _pmCount) return 0; + uint8_t value1 = MCP4261_CMD_READ | (pm << 4); + _value[pm] = readRegister(value1); + return _value[pm]; +} + + bool MCP4261::incrValue(uint8_t pm) { if (pm >= _pmCount) return false; if (_value[pm] >= _maxValue) return false; _value[pm]++; - uint8_t cmd = 0x04; - if (pm == 1) cmd = 0x14; - writeDevice(1, cmd, cmd); // value2 = DUMMY + uint8_t value1 = MCP4261_CMD_INCR | (pm << 4); + writeRegister1(value1); return true; } @@ -136,9 +153,8 @@ bool MCP4261::decrValue(uint8_t pm) if (_value[pm] == 0) return false; _value[pm]--; - uint8_t cmd = 0x08; - if (pm == 1) cmd = 0x18; - writeDevice(1, cmd, cmd); // value2 = DUMMY + uint8_t value1 = MCP4261_CMD_DECR | (pm << 4); + writeRegister1(value1); return true; } @@ -153,24 +169,81 @@ bool MCP4261::setValueNV(uint8_t pm, uint16_t value) if (pm >= _pmCount) return false; if (value > _maxValue) return false; - uint8_t cmd = 0x20; - if (pm == 1) cmd = 0x30; - if (value > 0xFF) cmd |= (value >> 8); // high bits - writeDevice(2, cmd, value & 0xFF); + uint8_t value1 = MCP4261_CMD_WRITE | ((pm + 2) << 4); + if (value > 0xFF) value1 |= (value >> 8); // high bits + writeRegister2(value1, value & 0xFF); return true; } +uint16_t MCP4261::getValueNV(uint8_t pm) +{ + if (pm >= _pmCount) return 0; + uint8_t value1 = MCP4261_CMD_READ | ((pm + 2) << 4); + return readRegister(value1); +} + + ///////////////////////////////////////////////////////////////////////////// // -// MISC +// TERMINAL CONTROL // -uint8_t MCP4261::pmCount() +void MCP4261::setTCONMask(uint16_t mask) { - return _pmCount; + uint8_t value1 = MCP4261_CMD_WRITE | (MCP4261_REG_TCON << 4); + mask &= 0xFF; // only lower 8 bits used. + writeRegister2(value1, mask); } +uint16_t MCP4261::getTCONMask() +{ + uint8_t value1 = MCP4261_CMD_READ | (MCP4261_REG_TCON << 4); + uint16_t mask = readRegister(value1); + return mask & 0x01FF; // 0x1FF to get bit 8 which is always HIGH. +} + + +///////////////////////////////////////////////////////////////////////////// +// +// STATUS +// +uint16_t MCP4261::getStatusMask() +{ + uint8_t value1 = MCP4261_CMD_READ | (MCP4261_REG_STATUS << 4); + uint16_t mask = readRegister(value1); + return mask & 0x1F; +} + + +///////////////////////////////////////////////////////////////////////////// +// +// EEPROM +// +bool MCP4261::setEEPROM(uint8_t index, uint16_t value) +{ + if (index > 9) return false; + if (value > 0x1FF) return false; // 511 + uint8_t value1 = MCP4261_CMD_WRITE | (MCP4261_REG_EEPROM(index) << 4); + if (value > 0xFF) value1 |= (value >> 8); + writeRegister2(value1, value & 0xFF); + return true; +} + + +uint16_t MCP4261::getEEPROM(uint8_t index) +{ + if (index > 9) return 0; // 0xFFFF ? + uint8_t value1 = MCP4261_CMD_READ | (MCP4261_REG_EEPROM(index) << 4); + uint16_t value = readRegister(value1); + return value; +} + + +///////////////////////////////////////////////////////////////////////////// +// +// POWER +// void MCP4261::powerOn() { digitalWrite(_shutdown, HIGH); @@ -217,48 +290,86 @@ bool MCP4261::usesHWSPI() // PROTECTED // -/* -TODO - see page 46 -- need read8, read16 -- bid8, bidi16 -*/ - - // // USES SPI MODE 0 // -void MCP4261::writeDevice(uint8_t count, uint8_t value1, uint8_t value2) +void MCP4261::writeRegister1(uint8_t value1) { digitalWrite(_select, LOW); if (_hwSPI) { _mySPI->beginTransaction(_spi_settings); _mySPI->transfer(value1); - if (count == 2) _mySPI->transfer(value2); _mySPI->endTransaction(); } else // Software SPI { swSPI_write(value1); - if (count == 2) swSPI_write(value2); } digitalWrite(_select, HIGH); } +void MCP4261::writeRegister2(uint8_t value1, uint8_t value2) +{ + digitalWrite(_select, LOW); + if (_hwSPI) + { + _mySPI->beginTransaction(_spi_settings); + _mySPI->transfer(value1); + _mySPI->transfer(value2); + _mySPI->endTransaction(); + } + else // Software SPI + { + swSPI_write(value1); + swSPI_write(value2); + } + digitalWrite(_select, HIGH); +} + + +uint16_t MCP4261::readRegister(uint8_t value) +{ + uint16_t rv = 0; + digitalWrite(_select, LOW); + if (_hwSPI) + { + _mySPI->beginTransaction(_spi_settings); + rv += _mySPI->transfer(value); + rv <<= 8; + rv += _mySPI->transfer(0x00); + _mySPI->endTransaction(); + } + else // Software SPI + { + rv += swSPI_write(value); + rv <<= 8; + rv += swSPI_write(0x00); + } + digitalWrite(_select, HIGH); + rv &= 0x01FF; // 9 bits data only + return rv; +} + + // MSBFIRST -void MCP4261::swSPI_write(uint8_t value) +uint8_t MCP4261::swSPI_write(uint8_t value) { uint8_t clk = _clock; uint8_t dao = _dataOut; - + uint8_t dai = _dataIn; + uint8_t val = 0; + // MSBFIRST for (uint8_t mask = 0x80; mask; mask >>= 1) { digitalWrite(dao,(value & mask)); digitalWrite(clk, HIGH); + if (digitalRead(dai)) val |= mask; digitalWrite(clk, LOW); } + return val; } diff --git a/libraries/MCP4261/MCP4261.h b/libraries/MCP4261/MCP4261.h index 75b94642..9a9c3f44 100644 --- a/libraries/MCP4261/MCP4261.h +++ b/libraries/MCP4261/MCP4261.h @@ -2,7 +2,7 @@ // // FILE: MCP4261.h // AUTHOR: Rob Tillaart -// VERSION: 0.1.0 +// VERSION: 0.2.0 // DATE: 2024-02-21 // PURPOSE: Arduino library for MCP4261 SPI based digital potentiometers. // URL: https://github.com/RobTillaart/MCP4261 @@ -12,7 +12,7 @@ #include "SPI.h" -#define MCP4261_LIB_VERSION (F("0.1.0")) +#define MCP4261_LIB_VERSION (F("0.2.0")) #ifndef MCP41XX_MIDDLE_VALUE @@ -20,16 +20,16 @@ #endif #ifndef MCP41XX_MAX_VALUE -#define MCP41XX_MAX_VALUE 129 +#define MCP41XX_MAX_VALUE 128 #endif #ifndef MCP42XX_MIDDLE_VALUE -#define MCP42XX_MIDDLE_VALUE 129 +#define MCP42XX_MIDDLE_VALUE 128 #endif #ifndef MCP42XX_MAX_VALUE -#define MCP42XX_MAX_VALUE 257 +#define MCP42XX_MAX_VALUE 256 #endif @@ -53,36 +53,71 @@ public: // SOFTWARE SPI MCP4261(uint8_t select, uint8_t shutdown, uint8_t dataIn, uint8_t dataOut, uint8_t clock); - void begin(uint16_t value); + void begin(); void reset(uint16_t value); uint8_t pmCount(); - + // + // SET VOLATILE VALUE + // // set both potmeters (just a wrapper) bool setValue(uint16_t value); // set single potmeter (0 or 1) bool setValue(uint8_t pm, uint16_t value); + // from cache or device uint16_t getValue(uint8_t pm = 0); + uint16_t getValueDevice(uint8_t pm = 0); - bool incrValue(uint8_t pm); - bool decrValue(uint8_t pm); + bool incrValue(uint8_t pm = 0); + bool decrValue(uint8_t pm = 0); - // Set non volatile potmeter - // not tested + // + // SET NON-VOLATILE VALUE + // bool setValueNV(uint8_t pm, uint16_t value); + uint16_t getValueNV(uint8_t pm); // always from device + // + // TERMINAL CONTROL (bit masks to elaborate) + // + void setTCONMask(uint16_t mask); // only lower 9 bits used. + uint16_t getTCONMask(); + + // + // STATUS read only (not implemented High Voltage commands). + // + // BIT Description + // -------------------------- + // 0 Write Protect + // 1 Shut Down + // 2 Wiper Lock 0 + // 3 Wiper Lock 1 + // 4 EEPROM Write Active + uint16_t getStatusMask(); + + // + // EEPROM + // + // index = 0..9 value = 0..511 + bool setEEPROM(uint8_t index, uint16_t value); + uint16_t getEEPROM(uint8_t index); + + // + // POWER + // + void powerOn(); + void powerOff(); + bool isPowerOn(); + + // + // SPI + // // speed in Hz void setSPIspeed(uint32_t speed); uint32_t getSPIspeed(); bool usesHWSPI(); // debugging - // MISC - void powerOn(); - void powerOff(); - bool isPowerOn(); - - protected: uint8_t _dataIn; uint8_t _dataOut; @@ -96,8 +131,11 @@ protected: uint8_t _pmCount; uint16_t _maxValue; - void writeDevice(uint8_t count, uint8_t value1, uint8_t value2); - void swSPI_write(uint8_t value); + + void writeRegister1(uint8_t value1); + void writeRegister2(uint8_t value1, uint8_t value2); + uint16_t readRegister(uint8_t reg); + uint8_t swSPI_write(uint8_t value); __SPI_CLASS__ * _mySPI; SPISettings _spi_settings; diff --git a/libraries/MCP4261/README.md b/libraries/MCP4261/README.md index 7d4ca383..fb78583d 100644 --- a/libraries/MCP4261/README.md +++ b/libraries/MCP4261/README.md @@ -8,8 +8,6 @@ [![GitHub release](https://img.shields.io/github/release/RobTillaart/MCP4261.svg?maxAge=3600)](https://github.com/RobTillaart/MCP4261/releases) [![PlatformIO Registry](https://badges.registry.platformio.org/packages/robtillaart/library/MCP4261.svg)](https://registry.platformio.org/libraries/robtillaart/MCP4261) -[![Commits since latest](https://img.shields.io/github/commits-since/RobTillaart/MCP4261/latest)](https://github.com/RobTillaart/MCP4261/commits/master) - # MCP4261 @@ -24,87 +22,122 @@ The MCP4261 library supports both hardware SPI and software SPI up to 10 MHz. These series of potmeters (rheostats) come in values of 5, 10, 50 and 100 kΩ (±20%). -Where other potmeters uses a range of 0..127 or 0.255, these series potmeters -use a range 0..129 and 0..257. This implies some extra care to set the wiper -to end position. +Where other potmeters uses a range of 0..127 or 0..255, these series potmeters +use a range 0..128 (129 steps) and 0..256 (257 steps). +This implies some extra care to set the wiper to end position. The library supports setting the value of the potmeters and caches this setting. -This way it can be retrieved very fast. +This way it can be retrieved very fast. +The library also support to fetch the value from the device. +This is useful to get the start value in **begin()** and more. Furthermore the library has two functions to increase and decrease the value of a potmeter. +The library supports reading / writing the **Terminal Control** register. +This allows one to disconnect either, A, B or the Wiper pin from the internal +resistor array. (needs investigation how it works in detail). -Feedback, issues and improvements are welcome, -Please open an issue on GitHub. +The library can read the **Status** register, this is e.g. needed to see if one can +write to EEPROM or to the NON-Volatile registers (power on values). +Note the library does not check this. +Furthermore the status register shows the status of both the shutdown and write +protect pins. +The **Status** register also shows the wiper lock state, however the library +does not support locking/unlocking the wipers. + +Finally the library supports setting and getting from the 10 **EEPROM** locations. +Only 9 bits, so the values 0..511 are supported. +E.g. one could use these to store 5 x 2 wiper states or something more exciting. -### Not implemented yet +#### Obsolete -The library is under development and not all functionality is implemented. -(as I only needed to set values). +Version 0.2.0 has many additional functions and some were fixed so 0.1.x versions +are obsolete. -- EEPROM, 10 addresses of 10 bits. (MCP4261 et al ) -- Non volatile registers for power on setup.. (partially) -- read back from device -- read status -- TCON register -- High Voltage something? + +#### Feedback + +The library is tested only limited with a MCP4261, so feedback, issues and +improvements are as always welcome. Please open an issue on GitHub. ### Compatibles These are the devices that should work with this library. -Only the 4261 is tested. +However only the MCP4261 is tested. | Number | Type | pots | POR | MaxValue | Notes | |:--------:|:--------:|:------:|:--------:|:----------:|:--------| -| MCP4141 | Potmeter | 1 | NV-Wiper | 129 | -| MCP4142 | Rheostat | 1 | NV-Wiper | 129 | -| MCP4161 | Potmeter | 1 | NV-Wiper | 257 | -| MCP4162 | Rheostat | 1 | NV-Wiper | 257 | -| MCP4241 | Potmeter | 2 | NV-Wiper | 129 | -| MCP4242 | Rheostat | 2 | NV-Wiper | 129 | -| MCP4261 | Potmeter | 2 | NV-Wiper | 257 | base class -| MCP4262 | Rheostat | 2 | NV-Wiper | 257 | +| MCP4141 | Potmeter | 1 | NV-Wiper | 128 | +| MCP4142 | Rheostat | 1 | NV-Wiper | 128 | +| MCP4161 | Potmeter | 1 | NV-Wiper | 256 | +| MCP4162 | Rheostat | 1 | NV-Wiper | 256 | +| MCP4241 | Potmeter | 2 | NV-Wiper | 128 | +| MCP4242 | Rheostat | 2 | NV-Wiper | 128 | +| MCP4261 | Potmeter | 2 | NV-Wiper | 256 | base class, under test. +| MCP4262 | Rheostat | 2 | NV-Wiper | 256 | -To investigate: + +#### To investigate: MCP4131/32/51/52, MCP4231/32/51/52, these have no NV RAM so they -have a POR power on reset of middle value (= half max value. - - -MCP4151 Reichelt -MCP4262 Mouser +have a POR (power on reset) of the middle value (= half max value). +Support EEPROM? ### Related -TODO: list of other digital pot meters / rheostats. +Mainly other digital potentiometers / rheostats. + +- https://github.com/RobTillaart/AD520x +- https://github.com/RobTillaart/AD524X +- https://github.com/RobTillaart/AD5245 +- https://github.com/RobTillaart/AD5144A +- https://github.com/RobTillaart/AD5263 +- https://github.com/RobTillaart/MCP_POT +- https://github.com/RobTillaart/MCP4261 +- https://github.com/RobTillaart/X9C10X -#### Related +#### Registers +Table 4.1 from datasheet (p 29) - -| Address | Function | Memory Type | -|:---------:|:------------------------:|:--------------| -| 00h | Volatile Wiper 0 | RAM | -| 01h | Volatile Wiper 1 | RAM | -| 02h | Non-Volatile Wiper 0 | EEPROM | -| 03h | Non-Volatile Wiper 1 | EEPROM | +| Address | Function | Memory Type | Notes | +|:---------:|:------------------------:|:--------------|:--------| +| 00h | Volatile Wiper 0 | RAM | range 0..256 (128) +| 01h | Volatile Wiper 1 | RAM | range 0..256 (128) +| 02h | Non-Volatile Wiper 0 | EEPROM | set Power on Reset, range idem. +| 03h | Non-Volatile Wiper 1 | EEPROM | set Power on Reset, range idem. | 04h | Volatile TCON Register | RAM | -| 05h | Status Register | RAM | -| 06h | Data EEPROM | EEPROM | -| 07h | Data EEPROM | EEPROM | -| 08h | Data EEPROM | EEPROM | -| 09h | Data EEPROM | EEPROM | -| 0Ah | Data EEPROM | EEPROM | -| 0Bh | Data EEPROM | EEPROM | -| 0Ch | Data EEPROM | EEPROM | -| 0Dh | Data EEPROM | EEPROM | -| 0Eh | Data EEPROM | EEPROM | -| 0Fh | Data EEPROM | EEPROM | +| 05h | Status Register | RAM | read only +| 06-0Fh | Data EEPROM | EEPROM | 10 values, range 0..511 + +Is this overview needed? + +## Performance indication + +See performance example. + +Indicative times in microseconds, version 0.2.0 + +| | function | 1 MHz | 2 MHz | 4 MHz | 8 MHz | SW SPI | +|:-----:|:-----------------|:-------:|:-------:|:-------:|:-------:|:--------:| +| UNO | setValue | 34.4 | 26.4 | 22.3 | 20.3 | 312.5 | +| UNO | getValue | 0.8 | 0.8 | 0.8 | 0.8 | 0.8 | +| UNO | getValueDevice | 36.4 | 28.34 | 24.4 | 22.3 | 314.3 | +| UNO | incrValue | 24.2 | 20.2 | 18.2 | 17.2 | 162.9 | +| UNO | decrValue | 23.9 | 20.0 | 17.9 | 16.9 | 162.6 | + +Other boards might be added. + +Notes: +- EEPROM and NON-Volatile registers read performance is in the same order as the +getValueDevice() function. +- EEPROM and NON-Volatile registers write performance is in the same order as the +setValue() function, however depends on if there was a recent write (< 5 ms ago). ## Interface @@ -117,50 +150,90 @@ TODO: list of other digital pot meters / rheostats. ### Constructor - **MCP4261(uint8_t select, uint8_t shutdown, \__SPI_CLASS__ \* mySPI = &SPI)** -HW SPI constructor. +HW SPI constructor. If the shutDown pin is not used, is should be set to 255. - **MCP4261(uint8_t select, uint8_t shutdown, uint8_t dataIn, uint8_t dataOut, uint8_t clock)** -SW SPI Constructor. The dataIn pin is not used yet. -- **void begin(uint8_t value)** user must explicit set initial value. -- **void reset(uint8_t value)** user must explicit set initial value. +SW SPI Constructor. If the shutDown pin is not used, is should be set to 255. +- **void begin()** initializes the device and reads the default values of the two potmeters +from the device. These values are cached. +- **void reset(uint16_t value)** resets the device, and sets both wipers to an explicit value. - **uint8_t pmCount()** returns 1 or 2, depending on device type. ### Set Volatile Values -- **bool setValue(uint8_t value)** set all potmeters to the same value. (wrapper). +- **bool setValue(uint16_t value)** set all potmeters to the same value. (wrapper). +Value can be 0..256 (128 depending on type). Returns true. -- **bool setValue(uint8_t pm, uint8_t value)** set single potmeter (0 or 1). -Returns false if pm > pmCount. -- **uint8_t getValue(uint8_t pm = 0)** returns value from cache. -- **bool incrValue(uint8_t pm)** -- **bool decrValue(uint8_t pm)** +- **bool setValue(uint8_t pm, uint16_t value)** set a single potmeter (0 or 1). +Returns false if pm > pmCount or if value too large. +- **uint16_t getValue(uint8_t pm = 0)** returns value from cache. (fast). +- **uint16_t getValueDevice(uint8_t pm = 0)** returns value from the device. (robust). +- **bool incrValue(uint8_t pm = 0)** increments potmeter by 1 if possible. +Returns false if this fails, e.g. max value reached. +- **bool decrValue(uint8_t pm = 0)** decrements potmeter by 1 if possible. +Returns false if this fails, e.g. zero reached. -### Set Volatile Values +### Set NON-Volatile Values -**Experimental** +- **bool setValueNV(uint8_t pm, uint16_t value)** set the power on reset value for potmeter. +- **uint16_t getValueNV(uint8_t pm)** retrieves set value from device. -TODO: how does this fit in interface as reset() always sets a value. +The NV functions do not check the status register if an EEPROM write is pending. +If you want to write both registers you need to pause 5-10 ms between the calls. -- **bool setValueNV(uint8_t pm, uint16_t value)** + +### Terminal Control register (TCON) + +Needs more investigation how this works in detail. + +To connect and disconnect the A, B and Wiper from the internal resistor array. +See datasheet form details. + +- **void setTCONMask(uint16_t mask)** +- **uint16_t getTCONMask()** + + +### Status register + +See datasheet form details. + +- **uint16_t getStatusMask()** read only bit mask, (5 bits used). + +| bit | Description | notes | +|:-----:|:----------------------|:--------| +| 0 | Write Protect | +| 1 | Shut Down | +| 2 | Wiper Lock 0 | not supported +| 3 | Wiper Lock 1 | not supported +| 4 | EEPROM Write Active | ### EEPROM -Not implemented yet +- **bool setEEPROM(uint8_t index, uint16_t value)** index = 0..9 value = 0..511. +Returns false if index > 9 or value out of range. +- **uint16_t getEEPROM(uint8_t index)** Returns set value, or 0 if index > 9. + +The EEPROM functions do not check the status register if an EEPROM write is +pending. (might take up to 10 ms). + +If you want to write multiple registers you need to pause 5-10 ms between the calls. ### SPI Note changing the SPI speed might affect other devices on the same SPI bus. So use with care. -- **void setSPIspeed(uint32_t speed)** default 1MHz, typical 4 MHz. +- **void setSPIspeed(uint32_t speed)** default 1 MHz, typical 4 MHz. - **uint32_t getSPIspeed()** idem. -- **bool usesHWSPI()** idem. +- **bool usesHWSPI()** idem, depends on which constructor used. ### Power +Uses the shutdown pin from the constructor. + - **void powerOn()** idem. - **void powerOff()** idem. - **bool isPowerOn()** idem. @@ -174,29 +247,37 @@ So use with care. #### Should -- read registers. - - read values. - - status register - - TCON +- investigate compatibility (what is supported) + - MCP4131/32/51/52, + - MCP4231/32/51/52 +- pending EEPROM write? + - blocking 5-10 ms?, see page 10 datasheet. + - status check for EEPROM? (robust, also in MT environment) + - lastEE_timestamp? (fast, works if long ago) + - mix? + - What is a good strategy? + - **bool isEEWritePending()**? should be non-blocking. + - Also for the NV write function as one needs to write two registers. +- elaborate TCON settings. + - investigate behaviour. #### Could -- EEPROM support - - 10 registers, 10 bit - - bool writeEEPROM(address, uint16_t value); - - uint16_t readEEPROM(address); -- write to non-volatile register 02/03 to set Power On Reset values - - read when reset? - - how to fit in use model. -- unit tests -- examples -- error handling +- support write protect pin. + - define pin, set, get +- investigate error handling - improve return values -- investigate performance - - AVR SW SPI? +- investigate performance + - extend performance sketch. + - other boards, extend table. + - AVR SW SPI? (only on request) + - const usage? #### Won't +- add unit tests (takes too much time). +- High voltage functions + ## Support diff --git a/libraries/MCP4261/examples/MCP4261_demo/MCP4261_demo.ino b/libraries/MCP4261/examples/MCP4261_demo/MCP4261_demo.ino index f6faf4f7..7b0f35f5 100644 --- a/libraries/MCP4261/examples/MCP4261_demo/MCP4261_demo.ino +++ b/libraries/MCP4261/examples/MCP4261_demo/MCP4261_demo.ino @@ -25,13 +25,12 @@ void setup() SPI.begin(); - pot.begin(0); // initial value + pot.begin(); - // test_extremes(); - //test_sinus(); + test_extremes(); + test_sinus(); test_sawtooth(); test_incr_decr(); - //test_timing(); Serial.println("\nDone..."); } @@ -115,33 +114,4 @@ void test_incr_decr() } -void test_timing() -{ - Serial.println(__FUNCTION__); - delay(10); - - start = micros(); - for (int i = 0; i < 1000; i++) - { - pot.setValue(0, i++); // auto wrap is fast... - } - stop = micros(); - Serial.print("1000 x setValue():\t"); - Serial.println(stop - start); - delay(10); - - volatile int x = 0; - start = micros(); - for (int i = 0; i < 500; i++) - { - x += pot.getValue(0); - x += pot.getValue(1); - } - stop = micros(); - Serial.print("1000 x getValue():\t"); - Serial.println(stop - start); - delay(10); -} - - // -- END OF FILE -- diff --git a/libraries/MCP4261/examples/MCP4261_demo_eeprom/MCP4261_demo_eeprom.ino b/libraries/MCP4261/examples/MCP4261_demo_eeprom/MCP4261_demo_eeprom.ino new file mode 100644 index 00000000..46717e01 --- /dev/null +++ b/libraries/MCP4261/examples/MCP4261_demo_eeprom/MCP4261_demo_eeprom.ino @@ -0,0 +1,58 @@ +// +// FILE: MCP4261_demo_eeprom.ino +// AUTHOR: Rob Tillaart +// PURPOSE: demo +// URL: https://github.com/RobTillaart/MCP4261 + + +#include "MCP4261.h" + + +// select, shutdown, dataIn, dataOut, clock == SOFTWARE SPI +// MCP4261 pot(10, 6, 7, 8, 9); + +// select, shutdown, &SPI === HW SPI UNO clock = 13, dataOut = 11 +MCP4261 pot(10, 6, &SPI); + + +void setup() +{ + Serial.begin(115200); + Serial.println(__FILE__); + + SPI.begin(); + + pot.begin(); + + for (int address = 0; address < 10; address++) + { + Serial.print(address); + Serial.print("\t"); + Serial.println(pot.getEEPROM(address)); + } + + for (int address = 0; address < 10; address++) + { + int x = random(512); + pot.setEEPROM(address, x); + delay(20); + Serial.println(x); + } + + for (int address = 0; address < 10; address++) + { + Serial.print(address); + Serial.print("\t"); + Serial.println(pot.getEEPROM(address)); + } + + Serial.println("\nDone..."); +} + + +void loop() +{ +} + + +// -- END OF FILE -- diff --git a/libraries/MCP4261/examples/MCP4261_demo_read/MCP4261_demo_read.ino b/libraries/MCP4261/examples/MCP4261_demo_read/MCP4261_demo_read.ino new file mode 100644 index 00000000..cee2f292 --- /dev/null +++ b/libraries/MCP4261/examples/MCP4261_demo_read/MCP4261_demo_read.ino @@ -0,0 +1,44 @@ +// +// FILE: MCP4261_demo_read.ino +// AUTHOR: Rob Tillaart +// PURPOSE: demo +// URL: https://github.com/RobTillaart/MCP4261 + + +#include "MCP4261.h" + + +// select, shutdown, dataIn, dataOut, clock == SOFTWARE SPI +// MCP4261 pot(10, 6, 7, 8, 9); + +// select, shutdown, &SPI === HW SPI UNO clock = 13, dataOut = 11 +MCP4261 pot(10, 6, &SPI); + + +void setup() +{ + Serial.begin(115200); + Serial.println(__FILE__); + + SPI.begin(); + pot.begin(); + + for (int v = 0; v < 258; v++) + { + pot.setValue(1, v); + int x = pot.getValueDevice(1); + Serial.print(v); + Serial.print("\t"); + Serial.println(x); + } + + Serial.println("\nDone..."); +} + + +void loop() +{ +} + + +// -- END OF FILE -- diff --git a/libraries/MCP4261/examples/MCP4261_demo_tcon/MCP4261_demo_tcon.ino b/libraries/MCP4261/examples/MCP4261_demo_tcon/MCP4261_demo_tcon.ino new file mode 100644 index 00000000..f6512c4c --- /dev/null +++ b/libraries/MCP4261/examples/MCP4261_demo_tcon/MCP4261_demo_tcon.ino @@ -0,0 +1,46 @@ +// +// FILE: MCP4261_demo_tcon.ino +// AUTHOR: Rob Tillaart +// PURPOSE: demo +// URL: https://github.com/RobTillaart/MCP4261 +// +// needs investigation + +#include "MCP4261.h" + + +// select, shutdown, dataIn, dataOut, clock == SOFTWARE SPI +// MCP4261 pot(10, 6, 7, 8, 9); + +// select, shutdown, &SPI === HW SPI UNO clock = 13, dataOut = 11 +MCP4261 pot(10, 6, &SPI); + + +void setup() +{ + Serial.begin(115200); + Serial.println(__FILE__); + + SPI.begin(); + + pot.begin(); + pot.setValue(123); + + int status = pot.getStatusMask(); + Serial.println(status); + + int tcon = pot.getTCONMask(); + Serial.println(tcon, BIN); // bit 8 always 1 + + // ??? TODO + + Serial.println("\nDone..."); +} + + +void loop() +{ +} + + +// -- END OF FILE -- diff --git a/libraries/MCP4261/examples/MCP4261_performance/MCP4261_performance.ino b/libraries/MCP4261/examples/MCP4261_performance/MCP4261_performance.ino index e57ea985..00505dc3 100644 --- a/libraries/MCP4261/examples/MCP4261_performance/MCP4261_performance.ino +++ b/libraries/MCP4261/examples/MCP4261_performance/MCP4261_performance.ino @@ -13,10 +13,10 @@ uint32_t start, stop; // select, shutdown, dataIn, dataOut, clock == SOFTWARE SPI -// MCP4261 pot(10, 6, 7, 8, 9); +MCP4261 pot(10, 6, 7, 8, 9); // select, shutdown, &SPI === HW SPI UNO clock = 13, data = 11 -MCP4261 pot(10, 6, &SPI); +// MCP4261 pot(10, 6, &SPI); void setup() @@ -29,7 +29,7 @@ void setup() SPI.begin(); - pot.begin(0); // initial value + pot.begin(); pot.setSPIspeed(1000000); test_timing(); pot.setSPIspeed(2000000); @@ -84,6 +84,19 @@ void test_timing() Serial.println((stop - start) / 500.0); delay(100); + start = micros(); + for (int i = 0; i < 250; i++) + { + x += pot.getValueDevice(0); + x += pot.getValueDevice(1); + } + stop = micros(); + Serial.print("500 x getValueDevice():\t"); + Serial.print(stop - start); + Serial.print("\t"); + Serial.println((stop - start) / 500.0); + delay(100); + pot.setValue(0); start = micros(); for (int i = 0; i < 250; i++) diff --git a/libraries/MCP4261/examples/MCP4261_performance/performance_0.2.0.txt b/libraries/MCP4261/examples/MCP4261_performance/performance_0.2.0.txt new file mode 100644 index 00000000..361e6c84 --- /dev/null +++ b/libraries/MCP4261/examples/MCP4261_performance/performance_0.2.0.txt @@ -0,0 +1,57 @@ + +BOARD: Arduino UNO +IDE: 1.8.19 + +MCP4261_performance.ino +MCP4261_LIB_VERSION: 0.2.0 + +HW SPI + + +test_timing +SPI: 1000000 +500 x setValue(): 156244 312.49 +500 x getValue(): 388 0.78 +500 x getValueDevice(): 157156 314.31 +500 x incrValue(): 81420 162.84 +500 x decrValue(): 81304 162.61 + +test_timing +SPI: 2000000 +500 x setValue(): 156240 312.48 +500 x getValue(): 380 0.76 +500 x getValueDevice(): 157156 314.31 +500 x incrValue(): 81428 162.86 +500 x decrValue(): 81296 162.59 + +test_timing +SPI: 4000000 +500 x setValue(): 156236 312.47 +500 x getValue(): 380 0.76 +500 x getValueDevice(): 157152 314.30 +500 x incrValue(): 81424 162.85 +500 x decrValue(): 81296 162.59 + +test_timing +SPI: 8000000 +500 x setValue(): 156240 312.48 +500 x getValue(): 380 0.76 +500 x getValueDevice(): 157148 314.30 +500 x incrValue(): 81428 162.86 +500 x decrValue(): 81296 162.59 + + + + +SW SPI + +500 x setValue(): 156240 312.48 +500 x getValue(): 380 0.76 +500 x getValueDevice(): 157148 314.30 +500 x incrValue(): 81428 162.86 +500 x decrValue(): 81296 162.59 + + + + +Done... diff --git a/libraries/MCP4261/examples/MCP4261_register_read/.arduino-ci.yml b/libraries/MCP4261/examples/MCP4261_register_read/.arduino-ci.yml new file mode 100644 index 00000000..34ae20e5 --- /dev/null +++ b/libraries/MCP4261/examples/MCP4261_register_read/.arduino-ci.yml @@ -0,0 +1,32 @@ +platforms: + rpipico: + board: rp2040:rp2040:rpipico + package: rp2040:rp2040 # name:architecture:board + gcc: + features: + defines: + - ARDUINO_ARCH_RP2040 + warnings: + flags: + + +packages: + rp2040:rp2040: + url: https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json + teensy:Teensy: + url: https://www.pjrc.com/teensy/package_teensy_index.json + + +compile: + # Choosing to run compilation tests on 2 different Arduino platforms + platforms: + # - uno + # - due + # - zero + # - leonardo + # - m4 + # - esp32 + # - esp8266 + # - mega2560 + # - rpipico + diff --git a/libraries/MCP4261/examples/MCP4261_register_read/MCP4261_register_read.ino b/libraries/MCP4261/examples/MCP4261_register_read/MCP4261_register_read.ino new file mode 100644 index 00000000..b1843405 --- /dev/null +++ b/libraries/MCP4261/examples/MCP4261_register_read/MCP4261_register_read.ino @@ -0,0 +1,48 @@ +// +// FILE: MCP4261_register_read.ino +// AUTHOR: Rob Tillaart +// PURPOSE: demo +// URL: https://github.com/RobTillaart/MCP4261 +// +// Works when readRegister() is made public. +// meant for testing only. + +#include "MCP4261.h" + + +uint32_t start, stop; + + +// select, shutdown, dataIn, dataOut, clock == SOFTWARE SPI +// MCP4261 pot(10, 6, 7, 8, 9); + +// select, shutdown, &SPI === HW SPI UNO clock = 13, dataOut = 11 +MCP4261 pot(10, 6, &SPI); + + +void setup() +{ + Serial.begin(115200); + Serial.println(__FILE__); + + SPI.begin(); + + pot.begin(); + + for (int r = 0; r < 16; r++) + { + Serial.print(r); + Serial.print("\t"); + Serial.println(pot.readRegister(r)); + } + + Serial.println("\nDone..."); +} + + +void loop() +{ +} + + +// -- END OF FILE -- diff --git a/libraries/MCP4261/keywords.txt b/libraries/MCP4261/keywords.txt index e48e0142..580afe78 100644 --- a/libraries/MCP4261/keywords.txt +++ b/libraries/MCP4261/keywords.txt @@ -19,21 +19,31 @@ pmCount KEYWORD2 setValue KEYWORD2 getValue KEYWORD2 +getValueDevice KEYWORD2 incrValue KEYWORD2 decrValue KEYWORD2 setValueNV KEYWORD2 +getValueNV KEYWORD2 -setSPIspeed KEYWORD2 -getSPIspeed KEYWORD2 +setTCONMask KEYWORD2 +getTCONMask KEYWORD2 +getStatusMask KEYWORD2 + +setEEPROM KEYWORD2 +getEEPROM KEYWORD2 powerOn KEYWORD2 powerOff KEYWORD2 isPowerOn KEYWORD2 +setSPIspeed KEYWORD2 +getSPIspeed KEYWORD2 + usesHWSPI KEYWORD2 + # Constants (LITERAL1) MCP4261_LIB_VERSION LITERAL1 diff --git a/libraries/MCP4261/library.json b/libraries/MCP4261/library.json index 740ffc2e..e3f37e9b 100644 --- a/libraries/MCP4261/library.json +++ b/libraries/MCP4261/library.json @@ -15,7 +15,7 @@ "type": "git", "url": "https://github.com/RobTillaart/MCP4261.git" }, - "version": "0.1.0", + "version": "0.2.0", "license": "MIT", "frameworks": "*", "platforms": "*", diff --git a/libraries/MCP4261/library.properties b/libraries/MCP4261/library.properties index 1d2bf69f..bb2498de 100644 --- a/libraries/MCP4261/library.properties +++ b/libraries/MCP4261/library.properties @@ -1,5 +1,5 @@ name=MCP4261 -version=0.1.0 +version=0.2.0 author=Rob Tillaart maintainer=Rob Tillaart sentence=Arduino library for MCP4261 SPI based digital potentiometers. diff --git a/libraries/MCP4261/test/unit_test_001.cpp b/libraries/MCP4261/test/unit_test_001.cpp index 7ff9eaae..5ca8363d 100644 --- a/libraries/MCP4261/test/unit_test_001.cpp +++ b/libraries/MCP4261/test/unit_test_001.cpp @@ -40,9 +40,9 @@ unittest_teardown() unittest(test_constants) { assertEqual(64, MCP41XX_MIDDLE_VALUE); - assertEqual(129, MCP41XX_MAX_VALUE); - assertEqual(129, MCP42XX_MIDDLE_VALUE); - assertEqual(257, MCP42XX_MAX_VALUE); + assertEqual(128, MCP41XX_MAX_VALUE); + assertEqual(128, MCP42XX_MIDDLE_VALUE); + assertEqual(256, MCP42XX_MAX_VALUE); }