0.2.0 MCP4261

This commit is contained in:
Rob Tillaart 2024-07-10 11:25:40 +02:00
parent 4dfce7b8ef
commit 76bdcc7125
16 changed files with 711 additions and 186 deletions

View File

@ -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/). 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 ## [0.1.0] - 2024-02-21
- initial version - initial version

View File

@ -1,7 +1,7 @@
// //
// FILE: MCP4261.cpp // FILE: MCP4261.cpp
// AUTHOR: Rob Tillaart // AUTHOR: Rob Tillaart
// VERSION: 0.1.0 // VERSION: 0.2.0
// DATE: 2024-02-21 // DATE: 2024-02-21
// PURPOSE: Arduino library for MCP4261 SPI based digital potentiometers. // PURPOSE: Arduino library for MCP4261 SPI based digital potentiometers.
// URL: https://github.com/RobTillaart/MCP4261 // URL: https://github.com/RobTillaart/MCP4261
@ -10,18 +10,21 @@
#include "MCP4261.h" #include "MCP4261.h"
// see page 18 datasheet #define MCP4261_CMD_WRITE 0x00
#define MCP4261_IGNORE_CMD 0x00 #define MCP4261_CMD_INCR 0x04
#define MCP4261_WRITE_CMD 0x00 #define MCP4261_CMD_DECR 0x08
#define MCP4261_SHUTDOWN_CMD 0x20 #define MCP4261_CMD_READ 0x0C
#define MCP4261_NONE_CMD 0x30
#define MCP4261_REG_TCON 0x04
#define MCP4261_REG_STATUS 0x05
#define MCP4261_REG_EEPROM(x) (0x06 + x)
// HARDWARE SPI // HARDWARE SPI
MCP4261::MCP4261(uint8_t select, uint8_t shutdown, __SPI_CLASS__ * mySPI) MCP4261::MCP4261(uint8_t select, uint8_t shutdown, __SPI_CLASS__ * mySPI)
{ {
_pmCount = 2; _pmCount = 2;
_maxValue = 255; _maxValue = MCP42XX_MAX_VALUE;
_select = select; _select = select;
_shutdown = shutdown; _shutdown = shutdown;
_dataIn = 255; _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) MCP4261::MCP4261(uint8_t select, uint8_t shutdown, uint8_t dataIn, uint8_t dataOut, uint8_t clock)
{ {
_pmCount = 2; _pmCount = 2;
_maxValue = 255; _maxValue = MCP42XX_MAX_VALUE;
_select = select; _select = select;
_shutdown = shutdown; _shutdown = shutdown;
_dataIn = dataIn; _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); pinMode(_select, OUTPUT);
digitalWrite(_select, HIGH); digitalWrite(_select, HIGH);
@ -71,7 +74,9 @@ void MCP4261::begin(uint16_t value)
digitalWrite(_dataOut, LOW); digitalWrite(_dataOut, LOW);
digitalWrite(_clock, 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; if (value > _maxValue) return false;
_value[pm] = value; _value[pm] = value;
uint8_t cmd = 0x00; uint8_t value1 = MCP4261_CMD_WRITE | (pm << 4);
if (pm == 1) cmd = 0x10; if (value > 0xFF) value1 |= (value >> 8); // high bits
if (value > 0xFF) cmd |= (value >> 8); // high bits writeRegister2(value1, value & 0xFF);
writeDevice(2, cmd, value & 0xFF);
return true; 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) bool MCP4261::incrValue(uint8_t pm)
{ {
if (pm >= _pmCount) return false; if (pm >= _pmCount) return false;
if (_value[pm] >= _maxValue) return false; if (_value[pm] >= _maxValue) return false;
_value[pm]++; _value[pm]++;
uint8_t cmd = 0x04; uint8_t value1 = MCP4261_CMD_INCR | (pm << 4);
if (pm == 1) cmd = 0x14; writeRegister1(value1);
writeDevice(1, cmd, cmd); // value2 = DUMMY
return true; return true;
} }
@ -136,9 +153,8 @@ bool MCP4261::decrValue(uint8_t pm)
if (_value[pm] == 0) return false; if (_value[pm] == 0) return false;
_value[pm]--; _value[pm]--;
uint8_t cmd = 0x08; uint8_t value1 = MCP4261_CMD_DECR | (pm << 4);
if (pm == 1) cmd = 0x18; writeRegister1(value1);
writeDevice(1, cmd, cmd); // value2 = DUMMY
return true; return true;
} }
@ -153,24 +169,81 @@ bool MCP4261::setValueNV(uint8_t pm, uint16_t value)
if (pm >= _pmCount) return false; if (pm >= _pmCount) return false;
if (value > _maxValue) return false; if (value > _maxValue) return false;
uint8_t cmd = 0x20; uint8_t value1 = MCP4261_CMD_WRITE | ((pm + 2) << 4);
if (pm == 1) cmd = 0x30; if (value > 0xFF) value1 |= (value >> 8); // high bits
if (value > 0xFF) cmd |= (value >> 8); // high bits writeRegister2(value1, value & 0xFF);
writeDevice(2, cmd, value & 0xFF);
return true; 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() void MCP4261::powerOn()
{ {
digitalWrite(_shutdown, HIGH); digitalWrite(_shutdown, HIGH);
@ -217,48 +290,86 @@ bool MCP4261::usesHWSPI()
// PROTECTED // PROTECTED
// //
/*
TODO - see page 46
- need read8, read16
- bid8, bidi16
*/
// //
// USES SPI MODE 0 // 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); digitalWrite(_select, LOW);
if (_hwSPI) if (_hwSPI)
{ {
_mySPI->beginTransaction(_spi_settings); _mySPI->beginTransaction(_spi_settings);
_mySPI->transfer(value1); _mySPI->transfer(value1);
if (count == 2) _mySPI->transfer(value2);
_mySPI->endTransaction(); _mySPI->endTransaction();
} }
else // Software SPI else // Software SPI
{ {
swSPI_write(value1); swSPI_write(value1);
if (count == 2) swSPI_write(value2);
} }
digitalWrite(_select, HIGH); 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 // MSBFIRST
void MCP4261::swSPI_write(uint8_t value) uint8_t MCP4261::swSPI_write(uint8_t value)
{ {
uint8_t clk = _clock; uint8_t clk = _clock;
uint8_t dao = _dataOut; uint8_t dao = _dataOut;
uint8_t dai = _dataIn;
uint8_t val = 0;
// MSBFIRST // MSBFIRST
for (uint8_t mask = 0x80; mask; mask >>= 1) for (uint8_t mask = 0x80; mask; mask >>= 1)
{ {
digitalWrite(dao,(value & mask)); digitalWrite(dao,(value & mask));
digitalWrite(clk, HIGH); digitalWrite(clk, HIGH);
if (digitalRead(dai)) val |= mask;
digitalWrite(clk, LOW); digitalWrite(clk, LOW);
} }
return val;
} }

View File

@ -2,7 +2,7 @@
// //
// FILE: MCP4261.h // FILE: MCP4261.h
// AUTHOR: Rob Tillaart // AUTHOR: Rob Tillaart
// VERSION: 0.1.0 // VERSION: 0.2.0
// DATE: 2024-02-21 // DATE: 2024-02-21
// PURPOSE: Arduino library for MCP4261 SPI based digital potentiometers. // PURPOSE: Arduino library for MCP4261 SPI based digital potentiometers.
// URL: https://github.com/RobTillaart/MCP4261 // URL: https://github.com/RobTillaart/MCP4261
@ -12,7 +12,7 @@
#include "SPI.h" #include "SPI.h"
#define MCP4261_LIB_VERSION (F("0.1.0")) #define MCP4261_LIB_VERSION (F("0.2.0"))
#ifndef MCP41XX_MIDDLE_VALUE #ifndef MCP41XX_MIDDLE_VALUE
@ -20,16 +20,16 @@
#endif #endif
#ifndef MCP41XX_MAX_VALUE #ifndef MCP41XX_MAX_VALUE
#define MCP41XX_MAX_VALUE 129 #define MCP41XX_MAX_VALUE 128
#endif #endif
#ifndef MCP42XX_MIDDLE_VALUE #ifndef MCP42XX_MIDDLE_VALUE
#define MCP42XX_MIDDLE_VALUE 129 #define MCP42XX_MIDDLE_VALUE 128
#endif #endif
#ifndef MCP42XX_MAX_VALUE #ifndef MCP42XX_MAX_VALUE
#define MCP42XX_MAX_VALUE 257 #define MCP42XX_MAX_VALUE 256
#endif #endif
@ -53,36 +53,71 @@ public:
// SOFTWARE SPI // SOFTWARE SPI
MCP4261(uint8_t select, uint8_t shutdown, uint8_t dataIn, uint8_t dataOut, uint8_t clock); 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); void reset(uint16_t value);
uint8_t pmCount(); uint8_t pmCount();
//
// SET VOLATILE VALUE
//
// set both potmeters (just a wrapper) // set both potmeters (just a wrapper)
bool setValue(uint16_t value); bool setValue(uint16_t value);
// set single potmeter (0 or 1) // set single potmeter (0 or 1)
bool setValue(uint8_t pm, uint16_t value); bool setValue(uint8_t pm, uint16_t value);
// from cache or device
uint16_t getValue(uint8_t pm = 0); uint16_t getValue(uint8_t pm = 0);
uint16_t getValueDevice(uint8_t pm = 0);
bool incrValue(uint8_t pm); bool incrValue(uint8_t pm = 0);
bool decrValue(uint8_t pm); bool decrValue(uint8_t pm = 0);
// Set non volatile potmeter //
// not tested // SET NON-VOLATILE VALUE
//
bool setValueNV(uint8_t pm, uint16_t 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 // speed in Hz
void setSPIspeed(uint32_t speed); void setSPIspeed(uint32_t speed);
uint32_t getSPIspeed(); uint32_t getSPIspeed();
bool usesHWSPI(); // debugging bool usesHWSPI(); // debugging
// MISC
void powerOn();
void powerOff();
bool isPowerOn();
protected: protected:
uint8_t _dataIn; uint8_t _dataIn;
uint8_t _dataOut; uint8_t _dataOut;
@ -96,8 +131,11 @@ protected:
uint8_t _pmCount; uint8_t _pmCount;
uint16_t _maxValue; 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; __SPI_CLASS__ * _mySPI;
SPISettings _spi_settings; SPISettings _spi_settings;

View File

@ -8,8 +8,6 @@
[![GitHub release](https://img.shields.io/github/release/RobTillaart/MCP4261.svg?maxAge=3600)](https://github.com/RobTillaart/MCP4261/releases) [![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) [![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 # 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%). 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 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 use a range 0..128 (129 steps) and 0..256 (257 steps).
to end position. 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. 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 Furthermore the library has two functions to increase and decrease
the value of a potmeter. 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, The library can read the **Status** register, this is e.g. needed to see if one can
Please open an issue on GitHub. 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. Version 0.2.0 has many additional functions and some were fixed so 0.1.x versions
(as I only needed to set values). are obsolete.
- EEPROM, 10 addresses of 10 bits. (MCP4261 et al )
- Non volatile registers for power on setup.. (partially) #### Feedback
- read back from device
- read status The library is tested only limited with a MCP4261, so feedback, issues and
- TCON register improvements are as always welcome. Please open an issue on GitHub.
- High Voltage something?
### Compatibles ### Compatibles
These are the devices that should work with this library. 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 | | Number | Type | pots | POR | MaxValue | Notes |
|:--------:|:--------:|:------:|:--------:|:----------:|:--------| |:--------:|:--------:|:------:|:--------:|:----------:|:--------|
| MCP4141 | Potmeter | 1 | NV-Wiper | 129 | | MCP4141 | Potmeter | 1 | NV-Wiper | 128 |
| MCP4142 | Rheostat | 1 | NV-Wiper | 129 | | MCP4142 | Rheostat | 1 | NV-Wiper | 128 |
| MCP4161 | Potmeter | 1 | NV-Wiper | 257 | | MCP4161 | Potmeter | 1 | NV-Wiper | 256 |
| MCP4162 | Rheostat | 1 | NV-Wiper | 257 | | MCP4162 | Rheostat | 1 | NV-Wiper | 256 |
| MCP4241 | Potmeter | 2 | NV-Wiper | 129 | | MCP4241 | Potmeter | 2 | NV-Wiper | 128 |
| MCP4242 | Rheostat | 2 | NV-Wiper | 129 | | MCP4242 | Rheostat | 2 | NV-Wiper | 128 |
| MCP4261 | Potmeter | 2 | NV-Wiper | 257 | base class | MCP4261 | Potmeter | 2 | NV-Wiper | 256 | base class, under test.
| MCP4262 | Rheostat | 2 | NV-Wiper | 257 | | 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 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. have a POR (power on reset) of the middle value (= half max value).
Support EEPROM?
MCP4151 Reichelt
MCP4262 Mouser
### Related ### 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 | Notes |
| Address | Function | Memory Type | |:---------:|:------------------------:|:--------------|:--------|
|:---------:|:------------------------:|:--------------| | 00h | Volatile Wiper 0 | RAM | range 0..256 (128)
| 00h | Volatile Wiper 0 | RAM | | 01h | Volatile Wiper 1 | RAM | range 0..256 (128)
| 01h | Volatile Wiper 1 | RAM | | 02h | Non-Volatile Wiper 0 | EEPROM | set Power on Reset, range idem.
| 02h | Non-Volatile Wiper 0 | EEPROM | | 03h | Non-Volatile Wiper 1 | EEPROM | set Power on Reset, range idem.
| 03h | Non-Volatile Wiper 1 | EEPROM |
| 04h | Volatile TCON Register | RAM | | 04h | Volatile TCON Register | RAM |
| 05h | Status Register | RAM | | 05h | Status Register | RAM | read only
| 06h | Data EEPROM | EEPROM | | 06-0Fh | Data EEPROM | EEPROM | 10 values, range 0..511
| 07h | Data EEPROM | EEPROM |
| 08h | Data EEPROM | EEPROM | Is this overview needed?
| 09h | Data EEPROM | EEPROM |
| 0Ah | Data EEPROM | EEPROM | ## Performance indication
| 0Bh | Data EEPROM | EEPROM |
| 0Ch | Data EEPROM | EEPROM | See performance example.
| 0Dh | Data EEPROM | EEPROM |
| 0Eh | Data EEPROM | EEPROM | Indicative times in microseconds, version 0.2.0
| 0Fh | Data EEPROM | EEPROM |
| | 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 ## Interface
@ -117,37 +150,75 @@ TODO: list of other digital pot meters / rheostats.
### Constructor ### Constructor
- **MCP4261(uint8_t select, uint8_t shutdown, \__SPI_CLASS__ \* mySPI = &SPI)** - **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)** - **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. SW SPI Constructor. If the shutDown pin is not used, is should be set to 255.
- **void begin(uint8_t value)** user must explicit set initial value. - **void begin()** initializes the device and reads the default values of the two potmeters
- **void reset(uint8_t value)** user must explicit set initial value. 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. - **uint8_t pmCount()** returns 1 or 2, depending on device type.
### Set Volatile Values ### 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. Returns true.
- **bool setValue(uint8_t pm, uint8_t value)** set single potmeter (0 or 1). - **bool setValue(uint8_t pm, uint16_t value)** set a single potmeter (0 or 1).
Returns false if pm > pmCount. Returns false if pm > pmCount or if value too large.
- **uint8_t getValue(uint8_t pm = 0)** returns value from cache. - **uint16_t getValue(uint8_t pm = 0)** returns value from cache. (fast).
- **bool incrValue(uint8_t pm)** - **uint16_t getValueDevice(uint8_t pm = 0)** returns value from the device. (robust).
- **bool decrValue(uint8_t pm)** - **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 ### 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 ### SPI
@ -156,11 +227,13 @@ So use with care.
- **void setSPIspeed(uint32_t speed)** default 1 MHz, typical 4 MHz. - **void setSPIspeed(uint32_t speed)** default 1 MHz, typical 4 MHz.
- **uint32_t getSPIspeed()** idem. - **uint32_t getSPIspeed()** idem.
- **bool usesHWSPI()** idem. - **bool usesHWSPI()** idem, depends on which constructor used.
### Power ### Power
Uses the shutdown pin from the constructor.
- **void powerOn()** idem. - **void powerOn()** idem.
- **void powerOff()** idem. - **void powerOff()** idem.
- **bool isPowerOn()** idem. - **bool isPowerOn()** idem.
@ -174,29 +247,37 @@ So use with care.
#### Should #### Should
- read registers. - investigate compatibility (what is supported)
- read values. - MCP4131/32/51/52,
- status register - MCP4231/32/51/52
- TCON - 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 #### Could
- EEPROM support - support write protect pin.
- 10 registers, 10 bit - define pin, set, get
- bool writeEEPROM(address, uint16_t value); - investigate error handling
- 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
- improve return values - improve return values
- investigate performance - investigate performance
- AVR SW SPI? - extend performance sketch.
- other boards, extend table.
- AVR SW SPI? (only on request)
- const usage?
#### Won't #### Won't
- add unit tests (takes too much time).
- High voltage functions
## Support ## Support

View File

@ -25,13 +25,12 @@ void setup()
SPI.begin(); SPI.begin();
pot.begin(0); // initial value pot.begin();
// test_extremes(); test_extremes();
//test_sinus(); test_sinus();
test_sawtooth(); test_sawtooth();
test_incr_decr(); test_incr_decr();
//test_timing();
Serial.println("\nDone..."); 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 -- // -- END OF FILE --

View File

@ -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 --

View File

@ -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 --

View File

@ -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 --

View File

@ -13,10 +13,10 @@ uint32_t start, stop;
// select, shutdown, dataIn, dataOut, clock == SOFTWARE SPI // 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 // select, shutdown, &SPI === HW SPI UNO clock = 13, data = 11
MCP4261 pot(10, 6, &SPI); // MCP4261 pot(10, 6, &SPI);
void setup() void setup()
@ -29,7 +29,7 @@ void setup()
SPI.begin(); SPI.begin();
pot.begin(0); // initial value pot.begin();
pot.setSPIspeed(1000000); pot.setSPIspeed(1000000);
test_timing(); test_timing();
pot.setSPIspeed(2000000); pot.setSPIspeed(2000000);
@ -84,6 +84,19 @@ void test_timing()
Serial.println((stop - start) / 500.0); Serial.println((stop - start) / 500.0);
delay(100); 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); pot.setValue(0);
start = micros(); start = micros();
for (int i = 0; i < 250; i++) for (int i = 0; i < 250; i++)

View File

@ -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...

View File

@ -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

View File

@ -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 --

View File

@ -19,21 +19,31 @@ pmCount KEYWORD2
setValue KEYWORD2 setValue KEYWORD2
getValue KEYWORD2 getValue KEYWORD2
getValueDevice KEYWORD2
incrValue KEYWORD2 incrValue KEYWORD2
decrValue KEYWORD2 decrValue KEYWORD2
setValueNV KEYWORD2 setValueNV KEYWORD2
getValueNV KEYWORD2
setSPIspeed KEYWORD2 setTCONMask KEYWORD2
getSPIspeed KEYWORD2 getTCONMask KEYWORD2
getStatusMask KEYWORD2
setEEPROM KEYWORD2
getEEPROM KEYWORD2
powerOn KEYWORD2 powerOn KEYWORD2
powerOff KEYWORD2 powerOff KEYWORD2
isPowerOn KEYWORD2 isPowerOn KEYWORD2
setSPIspeed KEYWORD2
getSPIspeed KEYWORD2
usesHWSPI KEYWORD2 usesHWSPI KEYWORD2
# Constants (LITERAL1) # Constants (LITERAL1)
MCP4261_LIB_VERSION LITERAL1 MCP4261_LIB_VERSION LITERAL1

View File

@ -15,7 +15,7 @@
"type": "git", "type": "git",
"url": "https://github.com/RobTillaart/MCP4261.git" "url": "https://github.com/RobTillaart/MCP4261.git"
}, },
"version": "0.1.0", "version": "0.2.0",
"license": "MIT", "license": "MIT",
"frameworks": "*", "frameworks": "*",
"platforms": "*", "platforms": "*",

View File

@ -1,5 +1,5 @@
name=MCP4261 name=MCP4261
version=0.1.0 version=0.2.0
author=Rob Tillaart <rob.tillaart@gmail.com> author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com> maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=Arduino library for MCP4261 SPI based digital potentiometers. sentence=Arduino library for MCP4261 SPI based digital potentiometers.

View File

@ -40,9 +40,9 @@ unittest_teardown()
unittest(test_constants) unittest(test_constants)
{ {
assertEqual(64, MCP41XX_MIDDLE_VALUE); assertEqual(64, MCP41XX_MIDDLE_VALUE);
assertEqual(129, MCP41XX_MAX_VALUE); assertEqual(128, MCP41XX_MAX_VALUE);
assertEqual(129, MCP42XX_MIDDLE_VALUE); assertEqual(128, MCP42XX_MIDDLE_VALUE);
assertEqual(257, MCP42XX_MAX_VALUE); assertEqual(256, MCP42XX_MAX_VALUE);
} }