mirror of
https://github.com/RobTillaart/Arduino.git
synced 2024-10-03 18:09:02 -04:00
0.2.1 X9C10X
This commit is contained in:
parent
f24d899e68
commit
7f65d0c013
@ -13,8 +13,10 @@ Arduino Library for X9C10X series digital potentiometer.
|
||||
|
||||
## Description
|
||||
|
||||
This **experimental** library provides a X9C base class, a X9C10X class and
|
||||
four derived classes for specific digital potentiometer.
|
||||
This **experimental** library provides
|
||||
- a minimal X9C base class,
|
||||
- an elaborated X9C10X class and
|
||||
- four derived classes for specific digital potentiometer.
|
||||
|
||||
| class | resistance | tested | notes |
|
||||
|:------:|:----------:|:-------:|:-------------|
|
||||
@ -29,10 +31,12 @@ four derived classes for specific digital potentiometer.
|
||||
_Note: Ω Ohm sign = ALT-234_
|
||||
|
||||
The X9C10X object keeps track of the position of the potentiometer,
|
||||
but the user should set it with **setPosition(pos, true);**
|
||||
but the user should set it with **setPosition(position, true);**
|
||||
Otherwise the library and device will probably not be in sync.
|
||||
|
||||
Since 0.2.0 the library has a minimal X9C class. See below.
|
||||
Since 0.2.1 the library also supports **restoreInternalPosition(position)**
|
||||
to set the internal position with the value from the latest **store()** call.
|
||||
See the examples.
|
||||
|
||||
|
||||
### Multiple devices
|
||||
@ -40,11 +44,12 @@ Since 0.2.0 the library has a minimal X9C class. See below.
|
||||
Multiple devices can be controlled by assigning them an unique selectPin (CS).
|
||||
This behaviour is similar to the SPI select pin.
|
||||
|
||||
It should be possible to share the U/D and INC lines (not tested) when controlling multiple X9C devices.
|
||||
It should be possible to share the U/D and INC lines (not tested) when controlling
|
||||
multiple X9C devices.
|
||||
|
||||
Note: one should select one device at a time.
|
||||
Sharing a CS pin or sending pulses to multiple devices at the same time will
|
||||
cause the library and devices get oout of sync.
|
||||
cause the library and devices get out of sync.
|
||||
|
||||
|
||||
### PINOUT
|
||||
@ -79,13 +84,14 @@ quality can become an issue. (not investigated further)
|
||||
|
||||
## Interface
|
||||
|
||||
```cpp
|
||||
#include "X9C10X.h"
|
||||
```
|
||||
|
||||
## X9C base class
|
||||
|
||||
This is the most minimalistic base class.
|
||||
It does not provide position information but that is sometimes enough.
|
||||
|
||||
Use **\#include "X9C10X.h"**
|
||||
It does not provide position information but sometimes that is just enough.
|
||||
|
||||
- **X9C()** Constructor.
|
||||
- **void begin(uint8_t pulsePin, uint8_t directionPin, uint8_t selectPin)**
|
||||
@ -95,14 +101,12 @@ Note: **begin()** has a hard coded 500uS delay so the device can wake up.
|
||||
- **void decr()** moves one position down (if possible).
|
||||
- **void store()** stores the current position in NV-RAM to be used at the next restart.
|
||||
Does not return a value as the position cannot be read from the device.
|
||||
So the user should keep track of the position if needed.
|
||||
So the user must keep track of the position if needed.
|
||||
|
||||
|
||||
## X9C10X base class
|
||||
|
||||
This class is derived from the X9C class but adds position, Ohm and type information.
|
||||
|
||||
Use **\#include "X9C10X.h"**
|
||||
This class is derived from the X9C class and adds position, Ohm and type information.
|
||||
|
||||
- **X9C10X(uint32_t Ohm = 10000)** Constructor, default initializes the resistance to 10000 Ω.
|
||||
To calibrate one can fill in any other (measured) value e.g. 9950 Ω.
|
||||
@ -110,12 +114,14 @@ This can be useful e.g. if one sets a fixed resistor parallel over the X9C one.
|
||||
- **void begin(uint8_t pulsePin, uint8_t directionPin, uint8_t selectPin)**
|
||||
sets the INC, UD and CS pins used by the device.
|
||||
Note: **begin()** has a hard coded 500uS delay so the device can wake up.
|
||||
- **void setPosition(uint8_t position, bool forced = false)** sets the wiper
|
||||
- **uint8_t setPosition(uint8_t position, bool forced = false)** sets the wiper
|
||||
to a position between 0 and 99.
|
||||
The movement is relative to the current (internal) position.
|
||||
If forced is set to true, the wiper will be moved to the closest "end" position
|
||||
and from there moved to the requested position.
|
||||
The internal position is replaced by the new position.
|
||||
If the new position > 99 the new position is truncated to 99.
|
||||
Returns new position 0 .. 99.
|
||||
- **uint8_t getPosition()** returns the current (internal) position. 0..99
|
||||
- **bool incr()** moves one position up (if possible).
|
||||
Returns true if moved and false if already at end position
|
||||
@ -123,10 +129,19 @@ according to internal position math.
|
||||
- **bool decr()** moves one position down (if possible).
|
||||
Returns true if moved and false if already at begin position
|
||||
according to internal position math.
|
||||
- **uint8_t store()** stores the current position in the NVRAM of the device,
|
||||
and returns the current position so it can later be used as position parameter for **setPosition()**.
|
||||
Warning: use with care (not tested).
|
||||
Note: **store()** blocks for 20 milliseconds.
|
||||
- **uint8_t store()** stores the current position in the NVRAM of the device.
|
||||
Returns the current position so it can later be used as position parameter
|
||||
for **setPosition()** or **restoreInternalPosition()**.
|
||||
- Warning: use with care (not tested).
|
||||
- Note: **store()** blocks for 20 milliseconds.
|
||||
- **uint8_t restoreInternalPosition(uint8_t position)** hard overwrite of the current
|
||||
(internal) position to initialize the library with the value returned by **store()**.
|
||||
The potentiometer will not be moved() in this process, and the user is responsible
|
||||
to provide the right value.
|
||||
Returns new position 0 .. 99.
|
||||
This function allows users e.g. to save the position returned by **store()** in EEPROM
|
||||
to initialize the library with this EEPROM value after a reboot.
|
||||
- Warning: use with care (not tested).
|
||||
|
||||
Note: **begin()** changed in 0.2.0 as the implicit parameter position
|
||||
was removed for the explicit function call to **setPosition()**.
|
||||
@ -135,6 +150,13 @@ value as position. Unfortunately the position cannot be read from the device.
|
||||
This will result in a mismatch between the internal position and the
|
||||
external one.
|
||||
|
||||
Since 0.2.1 the function **uint8_t restoreInternalPosition(uint8_t position)**
|
||||
gives some means to solve this, see examples.
|
||||
Be aware that if a system resets and the position has been changed since last
|
||||
**store()** the restore and therefore the library will not be in sync with the device.
|
||||
To create a fool proof system additional hardware is needed, see Concept read position below.
|
||||
|
||||
|
||||
|
||||
#### Ohm
|
||||
|
||||
@ -165,7 +187,7 @@ These classes have the same interface as the X9C10X base class.
|
||||
The only difference is that the type is set to a non zero value.
|
||||
|
||||
|
||||
#### Performance
|
||||
## Performance
|
||||
|
||||
The table below is tested on a (relative slow) Arduino UNO 16 MHz with IDE 1.18.19.
|
||||
Other processors might give similar or faster times. See performance example.
|
||||
@ -191,6 +213,8 @@ X9C10X_LIB_VERSION: 0.1.2
|
||||
|
||||
Time per step is 780 / 99 = ~8 us per step on an UNO.
|
||||
|
||||
Note: no performance improvements since 0.1.2
|
||||
|
||||
|
||||
## Operation
|
||||
|
||||
@ -206,9 +230,30 @@ A voltage of **3V3** would be **setPosition(66)**.
|
||||
Note: check datasheet for the range of the max voltage and current allowed.
|
||||
|
||||
|
||||
#### Concept read position
|
||||
|
||||
If you need to make a robust system with X9C devices you can solder two devices "in parallel".
|
||||
One to control whatever you need to control, and the other to create a feedback loop through analogRead().
|
||||
Lets name them feedback device and control device.
|
||||
The two devices should share the select, direction and pulse pins in hardware.
|
||||
This way they will get the exact same pulses and signals and would therefore be in the exact same position
|
||||
after initialization.
|
||||
|
||||
The feedback device would be a voltage divider, splitting 5 Volts in 100 level.
|
||||
To read these levels you need at least an 8 bit ADC or better.
|
||||
This setup would allow you to read the position in the control device 100% of the time.
|
||||
|
||||
The price is at least twice as high in terms of hardware, the performance will be less at some times
|
||||
and the code will be slightly more complex
|
||||
|
||||
It might be possible to measure the voltage of the wiper of the control device.
|
||||
However that might not always be easy or possible, due to voltage used, etc.
|
||||
|
||||
|
||||
## Future
|
||||
|
||||
- update documentation
|
||||
- concept of **read()** => put 2 X9C parallel and read one with analogRead().
|
||||
- test different platforms
|
||||
- add error codes ?
|
||||
- add examples
|
||||
@ -225,5 +270,5 @@ Note: check datasheet for the range of the max voltage and current allowed.
|
||||
- **getOhm()** ==> **getValue()**
|
||||
- **getMaxOhm()** ==> **getMaxValue()**
|
||||
- think milliVolt, ohm, lux, speed, etc.
|
||||
User can do this too with **getPosition() * factor**
|
||||
User can do this too with **getPosition() \* factor**
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
//
|
||||
// FILE: X9C10X.cpp
|
||||
// AUTHOR: Rob Tillaart
|
||||
// VERSION: 0.2.0
|
||||
// VERSION: 0.2.1
|
||||
// PURPOSE: Arduino Library for X9C10X series digital potentiometer.
|
||||
// URL: https://github.com/RobTillaart/X9C10X
|
||||
//
|
||||
@ -15,12 +15,18 @@
|
||||
// 0.2.0 2022-07-09 fix #7 incorrect signal during initialize
|
||||
// remove position parameter from begin()
|
||||
// to make setting position more explicit.
|
||||
// update readme
|
||||
// update readme.md
|
||||
// add uint8_t Ohm2Position()
|
||||
// 0.2.1 2022-07-23 fix #9 add restoreInternalPosition(pos)
|
||||
// change return type setPosition() to indicate truncation
|
||||
// update readme.md and comments
|
||||
// update build-CI tests
|
||||
|
||||
|
||||
|
||||
#include "X9C10X.h"
|
||||
|
||||
|
||||
// minimum pulse width CLOCK = ? us (datasheet);
|
||||
// digitalWrite takes enough time on UNO / AVR so clock_delay == 0
|
||||
// Note that if clock pulses are long enough the data pulses are too.
|
||||
@ -33,6 +39,8 @@
|
||||
#define X9C10X_UP HIGH
|
||||
#define X9C10X_DOWN LOW
|
||||
|
||||
#define X9C10X_MAXPOT 99
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////
|
||||
//
|
||||
@ -122,8 +130,6 @@ void X9C::_move(uint8_t direction, uint8_t steps)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////
|
||||
//
|
||||
// X9C10X BASE CLASS
|
||||
@ -134,14 +140,14 @@ X9C10X::X9C10X(uint32_t maxOhm) : X9C()
|
||||
}
|
||||
|
||||
|
||||
void X9C10X::setPosition(uint8_t position, bool forced)
|
||||
uint8_t X9C10X::setPosition(uint8_t position, bool forced)
|
||||
{
|
||||
if (position > 99) position = 99;
|
||||
// reference 0.1.0
|
||||
// while (position > _position) incr();
|
||||
// while (position < _position) decr();
|
||||
if (position > 99)
|
||||
{
|
||||
position = 99;
|
||||
}
|
||||
|
||||
// force to nearest end position first to minimize steps.
|
||||
// force to nearest end position first to minimize number of steps.
|
||||
if (forced)
|
||||
{
|
||||
if (position < 50)
|
||||
@ -165,6 +171,7 @@ void X9C10X::setPosition(uint8_t position, bool forced)
|
||||
}
|
||||
|
||||
_position = position;
|
||||
return _position;
|
||||
}
|
||||
|
||||
|
||||
@ -193,6 +200,17 @@ uint8_t X9C10X::store()
|
||||
}
|
||||
|
||||
|
||||
uint8_t X9C10X::restoreInternalPosition(uint8_t position)
|
||||
{
|
||||
if (position > 99)
|
||||
{
|
||||
position = 99;
|
||||
}
|
||||
_position = position;
|
||||
return _position;
|
||||
}
|
||||
|
||||
|
||||
// rounding needed!
|
||||
uint32_t X9C10X::getOhm()
|
||||
{
|
||||
|
@ -2,14 +2,14 @@
|
||||
//
|
||||
// FILE: X9C10X.h
|
||||
// AUTHOR: Rob Tillaart
|
||||
// VERSION: 0.2.0
|
||||
// VERSION: 0.2.1
|
||||
// PURPOSE: Arduino Library for X9C10X series digital potentiometer.
|
||||
// URL: https://github.com/RobTillaart/X9C10X
|
||||
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
#define X9C10X_LIB_VERSION (F("0.2.0"))
|
||||
#define X9C10X_LIB_VERSION (F("0.2.1"))
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////
|
||||
@ -25,6 +25,7 @@ public:
|
||||
void begin(uint8_t pulsePin, uint8_t directionPin, uint8_t selectPin);
|
||||
|
||||
// step size 1.
|
||||
// return false if end of range reached.
|
||||
bool incr();
|
||||
bool decr();
|
||||
|
||||
@ -51,18 +52,27 @@ public:
|
||||
X9C10X(uint32_t maxOhm = 10000);
|
||||
|
||||
// position = 0..99
|
||||
// values > 99 are truncated.
|
||||
// forced = true will ignore the cached position
|
||||
// takes up to 150 steps as one cannot read the position from device.
|
||||
// forced = default false as that is safer and backwards compatible.
|
||||
void setPosition(uint8_t position, bool forced = false);
|
||||
// returns new position 0..99
|
||||
uint8_t setPosition(uint8_t position, bool forced = false);
|
||||
uint8_t getPosition() { return _position; };
|
||||
|
||||
// step size 1.
|
||||
// return false if end of range reached.
|
||||
bool incr();
|
||||
bool decr();
|
||||
|
||||
// use with care
|
||||
// returns new position 0..99
|
||||
uint8_t store();
|
||||
// note: restoreInternalPosition() is not available in X9C base class.
|
||||
// position = 0..99
|
||||
// values > 99 are truncated.
|
||||
// returns new position 0..99
|
||||
uint8_t restoreInternalPosition(uint8_t position);
|
||||
|
||||
// current resistance in ohm.
|
||||
uint32_t getOhm();
|
||||
|
@ -153,6 +153,8 @@ void setup()
|
||||
Serial.print("10 x decr():\t");
|
||||
Serial.println(stop - start);
|
||||
delay(100);
|
||||
|
||||
Serial.println("\ndone...");
|
||||
}
|
||||
|
||||
|
||||
|
14
libraries/X9C10X/examples/X9C10X_store/.arduino-ci.yml
Normal file
14
libraries/X9C10X/examples/X9C10X_store/.arduino-ci.yml
Normal file
@ -0,0 +1,14 @@
|
||||
compile:
|
||||
# Choosing to run compilation tests on 2 different Arduino platforms
|
||||
platforms:
|
||||
- uno
|
||||
# - due
|
||||
# - zero
|
||||
# - leonardo
|
||||
# - m4
|
||||
# - esp32
|
||||
# - esp8266
|
||||
- mega2560
|
||||
|
||||
libraries:
|
||||
- "printHelpers"
|
109
libraries/X9C10X/examples/X9C10X_store/X9C10X_store.ino
Normal file
109
libraries/X9C10X/examples/X9C10X_store/X9C10X_store.ino
Normal file
@ -0,0 +1,109 @@
|
||||
//
|
||||
// FILE: X9C10X_store.ino
|
||||
// AUTHOR: Rob Tillaart
|
||||
// PURPOSE: demo store
|
||||
|
||||
// NOTE: AVR Arduino UNO only
|
||||
// be aware the internal EEPROM can wear out !
|
||||
|
||||
// WARNING: the restore mechanism demonstrated here will only work if the device
|
||||
// has not moved since the last call to store() as in this example.
|
||||
// It is the responsibility of the user to verify if that is the case.
|
||||
//
|
||||
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "X9C10X.h"
|
||||
#include "EEPROM.h"
|
||||
|
||||
|
||||
// PINOUT X9C10X TOP VIEW (see datasheet)
|
||||
//
|
||||
// +--------+
|
||||
// INC | o o | VCC
|
||||
// U/D | o o | CS
|
||||
// RH | o o | RL
|
||||
// GND | o o | Rwiper
|
||||
// +--------+
|
||||
//
|
||||
// INC pulses
|
||||
// U/D UP = 1 DOWN = 0
|
||||
// VCC +5V
|
||||
// GND ground
|
||||
// RH resistor high end
|
||||
// RL resistor low end
|
||||
// Rwiper resistor wiper
|
||||
// CS chip select
|
||||
//
|
||||
|
||||
|
||||
X9C10X pot(12345); // 100KΩ (ALT-234)
|
||||
|
||||
|
||||
const uint8_t POT_ADDR = 0x10; // for EEPROM
|
||||
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
while (!Serial);
|
||||
|
||||
Serial.println();
|
||||
Serial.print("X9C10X_LIB_VERSION: ");
|
||||
Serial.println(X9C10X_LIB_VERSION);
|
||||
Serial.println();
|
||||
|
||||
pot.begin(8, 9, 10); // pulse, direction, select
|
||||
|
||||
uint8_t pos = 0;
|
||||
EEPROM.get(POT_ADDR, pos);
|
||||
// if EEPROM has no valid value or not been used (255).
|
||||
if (pos > 99)
|
||||
{
|
||||
pot.setPosition(0, true); // adjust to your needs.
|
||||
}
|
||||
else
|
||||
{
|
||||
pot.restoreInternalPosition(pos);
|
||||
}
|
||||
|
||||
Serial.print("RESTORE:\t");
|
||||
Serial.println(pot.getPosition());
|
||||
}
|
||||
|
||||
|
||||
void loop()
|
||||
{
|
||||
if (Serial.available() > 0)
|
||||
{
|
||||
int c = Serial.read();
|
||||
switch (c)
|
||||
{
|
||||
case '+' :
|
||||
pot.incr();
|
||||
Serial.print("POS:\t");
|
||||
Serial.println(pot.getPosition());
|
||||
break;
|
||||
|
||||
case '-' :
|
||||
pot.decr();
|
||||
Serial.print("POS:\t");
|
||||
Serial.println(pot.getPosition());
|
||||
break;
|
||||
|
||||
// QUIT
|
||||
case 'q' :
|
||||
uint8_t p = pot.store();
|
||||
EEPROM.update(POT_ADDR, p);
|
||||
Serial.print("\nSTORE:\t");
|
||||
Serial.println(pot.getPosition());
|
||||
Serial.println("\nreset to start again...");
|
||||
while (1); // block forever after quit.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -- END OF FILE --
|
||||
|
@ -21,6 +21,7 @@ getMaxOhm KEYWORD2
|
||||
Ohm2Position KEYWORD2
|
||||
|
||||
store KEYWORD2
|
||||
restoreInternalPosition KEYWORD2
|
||||
getType KEYWORD2
|
||||
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
"type": "git",
|
||||
"url": "https://github.com/RobTillaart/X9C10X.git"
|
||||
},
|
||||
"version": "0.2.0",
|
||||
"version": "0.2.1",
|
||||
"license": "MIT",
|
||||
"frameworks": "arduino",
|
||||
"platforms": "*",
|
||||
|
@ -1,5 +1,5 @@
|
||||
name=X9C10X
|
||||
version=0.2.0
|
||||
version=0.2.1
|
||||
author=Rob Tillaart <rob.tillaart@gmail.com>
|
||||
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
|
||||
sentence=Arduino Library for X9C10X series digital potentiometer.
|
||||
|
@ -28,12 +28,12 @@
|
||||
#include "X9C10X.h"
|
||||
|
||||
|
||||
|
||||
unittest_setup()
|
||||
{
|
||||
fprintf(stderr, "X9C10X_LIB_VERSION: %s\n", (char *) X9C10X_LIB_VERSION);
|
||||
}
|
||||
|
||||
|
||||
unittest_teardown()
|
||||
{
|
||||
}
|
||||
@ -81,6 +81,26 @@ unittest(test_X9C10X_constructor)
|
||||
}
|
||||
|
||||
|
||||
unittest(test_X9C10X_restore_internal_position)
|
||||
{
|
||||
X9C10X dp0;
|
||||
|
||||
dp0.begin(7, 8, 9);
|
||||
assertEqual(0, dp0.restoreInternalPosition(0));
|
||||
assertEqual(0, dp0.getPosition());
|
||||
assertEqual(50, dp0.restoreInternalPosition(50));
|
||||
assertEqual(50, dp0.getPosition());
|
||||
|
||||
// test truncation
|
||||
assertEqual(99, dp0.restoreInternalPosition(99));
|
||||
assertEqual(99, dp0.getPosition());
|
||||
assertEqual(99, dp0.restoreInternalPosition(100));
|
||||
assertEqual(99, dp0.getPosition());
|
||||
assertEqual(99, dp0.restoreInternalPosition(255));
|
||||
assertEqual(99, dp0.getPosition());
|
||||
}
|
||||
|
||||
|
||||
unittest(test_X9C10X_position)
|
||||
{
|
||||
X9C10X dp0;
|
||||
@ -88,12 +108,40 @@ unittest(test_X9C10X_position)
|
||||
dp0.begin(7, 8, 9);
|
||||
assertEqual(0, dp0.getPosition());
|
||||
|
||||
fprintf(stderr, "setPosition step 9\n");
|
||||
for (uint8_t pos = 0; pos < 100; pos += 9)
|
||||
fprintf(stderr, "setPosition step 19\n");
|
||||
for (uint8_t pos = 0; pos < 100; pos += 19)
|
||||
{
|
||||
dp0.setPosition(pos);
|
||||
assertEqual(pos, dp0.setPosition(pos));
|
||||
assertEqual(pos, dp0.getPosition());
|
||||
}
|
||||
|
||||
// test truncation
|
||||
assertEqual(99, dp0.setPosition(99));
|
||||
assertEqual(99, dp0.setPosition(100));
|
||||
assertEqual(99, dp0.setPosition(255));
|
||||
}
|
||||
|
||||
|
||||
unittest(test_X9C10X_store)
|
||||
{
|
||||
X9C10X dp0;
|
||||
|
||||
dp0.begin(7, 8, 9);
|
||||
|
||||
fprintf(stderr, "store step 19\n");
|
||||
for (uint8_t pos = 0; pos < 100; pos += 19)
|
||||
{
|
||||
dp0.setPosition(pos);
|
||||
assertEqual(pos, dp0.store());
|
||||
}
|
||||
|
||||
// test truncation
|
||||
dp0.setPosition(99);
|
||||
assertEqual(99, dp0.store());
|
||||
dp0.setPosition(100);
|
||||
assertEqual(99, dp0.store());
|
||||
dp0.setPosition(255);
|
||||
assertEqual(99, dp0.store());
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user