0.2.0 MCP_DAC

This commit is contained in:
Rob Tillaart 2023-03-24 11:08:34 +01:00
parent 5d929b5140
commit 0d8cf41f85
6 changed files with 233 additions and 101 deletions

View File

@ -6,6 +6,17 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
## [0.2.0] - 2023-03-23
- fix #21 bug LDAC invert
- fix **setPercentage()**
- move code from .h to .cpp
- update readme.md
- update GitHub actions
- update license 2023
- minor edits
----
## [0.1.8] - 2022-10-18
- improve RP2040 support (kudos to Intubun)
- add CHANGELOG.md

View File

@ -1,16 +1,15 @@
//
// FILE: MCP_DAC.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.1.8
// VERSION: 0.2.0
// DATE: 2021-02-03
// PURPOSE: Arduino library for MCP_DAC
// URL: https://github.com/RobTillaart/MCP_DAC
//
// HISTORY see changelog.md
#include "MCP_DAC.h"
#if defined (ARDUINO_ARCH_RP2040)
MCP_DAC::MCP_DAC(uint8_t dataOut, uint8_t clock, SPIClassRP2040 *inSPI)
#else
@ -27,6 +26,7 @@ MCP_DAC::MCP_DAC(uint8_t dataOut, uint8_t clock, SPIClass *inSPI)
reset();
}
void MCP_DAC::reset()
{
_gain = 1;
@ -76,6 +76,18 @@ void MCP_DAC::begin(uint8_t select)
}
uint8_t MCP_DAC::channels()
{
return _channels;
}
uint16_t MCP_DAC::maxValue()
{
return _maxValue;
}
#if defined(ESP32) or defined(ARDUINO_ARCH_RP2040)
void MCP_DAC::setGPIOpins(uint8_t clk, uint8_t miso, uint8_t mosi, uint8_t select)
{
@ -112,6 +124,12 @@ bool MCP_DAC::setGain(uint8_t gain)
}
uint8_t MCP_DAC::getGain()
{
return _gain;
}
bool MCP_DAC::analogWrite(uint16_t value, uint8_t channel)
{
if (channel >= _channels) return false;
@ -135,6 +153,12 @@ bool MCP_DAC::analogWrite(uint16_t value, uint8_t channel)
}
uint16_t MCP_DAC::lastValue(uint8_t channel)
{
return _value[channel];
}
void MCP_DAC::fastWriteA(uint16_t value)
{
transfer(0x3000 | value);
@ -167,7 +191,7 @@ void MCP_DAC::setPercentage(float perc, uint8_t channel)
{
if (perc < 0) perc = 0;
if (perc > 100) perc = 100;
analogWrite(perc * _maxValue, channel);
analogWrite((0.01 * perc * _maxValue), channel);
}
@ -181,7 +205,7 @@ void MCP_DAC::setLatchPin(uint8_t latchPin)
{
_latchPin = latchPin;
pinMode(_latchPin, OUTPUT);
digitalWrite(_latchPin, LOW);
digitalWrite(_latchPin, HIGH);
}
@ -189,9 +213,11 @@ void MCP_DAC::triggerLatch()
{
if (_latchPin != 255)
{
digitalWrite(_latchPin, HIGH);
delayMicroseconds(1); // 100 ns - Page 7
digitalWrite(_latchPin, LOW);
// delay needed == 100 ns - Page 7
// on "slow" devices the next delay can be commented
delayMicroseconds(1);
digitalWrite(_latchPin, HIGH);
}
}
@ -199,7 +225,13 @@ void MCP_DAC::triggerLatch()
void MCP_DAC::shutDown()
{
_active = false;
transfer(0x0000); // a write will reset the values..
transfer(0x0000); // a write will reset the values.
}
bool MCP_DAC::isActive()
{
return _active;
}
@ -210,6 +242,59 @@ void MCP_DAC::setSPIspeed(uint32_t speed)
};
uint32_t MCP_DAC::getSPIspeed()
{
return _SPIspeed;
}
void MCP_DAC::setBufferedMode(bool mode)
{
_buffered = mode;
}
bool MCP_DAC::getBufferedMode()
{
return _buffered;
}
bool MCP_DAC::usesHWSPI()
{
return _hwSPI;
}
#if defined(ESP32)
void MCP_DAC::selectHSPI()
{
_useHSPI = true;
}
void MCP_DAC::selectVSPI()
{
_useHSPI = false;
}
bool MCP_DAC::usesHSPI()
{
return _useHSPI;
}
bool MCP_DAC::usesVSPI()
{
return !_useHSPI;
}
#endif
//////////////////////////////////////////////////////////////////
//
// PROTECTED
@ -249,11 +334,13 @@ uint8_t MCP_DAC::swSPI_transfer(uint8_t val)
return 0;
}
#if defined(ARDUINO_ARCH_RP2040)
/////////////////////////////////////////////////////////////////////////////
//
// MCP4800 series
// MCP4800 series
//
MCP4801::MCP4801(uint8_t dataOut, uint8_t clock, SPIClassRP2040 *inSPI) : MCP_DAC(dataOut, clock, inSPI)
{
@ -299,7 +386,7 @@ MCP4822::MCP4822(uint8_t dataOut, uint8_t clock, SPIClassRP2040 *inSPI) : MCP_DA
/////////////////////////////////////////////////////////////////////////////
//
// MCP4900 series
// MCP4900 series
//
MCP4901::MCP4901(uint8_t dataOut, uint8_t clock, SPIClassRP2040 *inSPI) : MCP_DAC(dataOut, clock, inSPI)
{
@ -343,11 +430,13 @@ MCP4922::MCP4922(uint8_t dataOut, uint8_t clock, SPIClassRP2040 *inSPI) : MCP_DA
_maxValue = 4095;
};
#else
/////////////////////////////////////////////////////////////////////////////
//
// MCP4800 series
// MCP4800 series
//
MCP4801::MCP4801(uint8_t dataOut, uint8_t clock, SPIClass *inSPI) : MCP_DAC(dataOut, clock, inSPI)
{
@ -393,7 +482,7 @@ MCP4822::MCP4822(uint8_t dataOut, uint8_t clock, SPIClass *inSPI) : MCP_DAC(data
/////////////////////////////////////////////////////////////////////////////
//
// MCP4900 series
// MCP4900 series
//
MCP4901::MCP4901(uint8_t dataOut, uint8_t clock, SPIClass *inSPI) : MCP_DAC(dataOut, clock, inSPI)
{
@ -439,5 +528,5 @@ MCP4922::MCP4922(uint8_t dataOut, uint8_t clock, SPIClass *inSPI) : MCP_DAC(data
#endif
// -- END OF FILE --
// -- END OF FILE --

View File

@ -2,24 +2,22 @@
//
// FILE: MCP_DAC.h
// AUTHOR: Rob Tillaart
// VERSION: 0.1.8
// VERSION: 0.2.0
// DATE: 2021-02-03
// PURPOSE: Arduino library for MCP_DAC
// URL: https://github.com/RobTillaart/MCP_DAC
//
#include "Arduino.h"
#include "SPI.h"
#define MCP_DAC_LIB_VERSION (F("0.1.8"))
#define MCP_DAC_LIB_VERSION (F("0.2.0"))
///////////////////////////////////////////////////////////////
//
// BASE CLASS
// BASE CLASS
//
class MCP_DAC
{
@ -31,20 +29,20 @@ public:
MCP_DAC(uint8_t dataOut = 255, uint8_t clock = 255, SPIClass *mySPI = &SPI);
#endif
// if only select is given ==> HW SPI
// if only select is given ==> HW SPI
void begin(uint8_t select);
// 0 or 1
uint8_t channels() { return _channels; };
uint8_t channels();
// 255 (8 bit) or 1023 (10 bit) or 4095 (12 bit)
uint16_t maxValue() { return _maxValue; };
uint16_t maxValue();
// gain = 1 or 2
bool setGain(uint8_t gain = 1);
uint8_t getGain() { return _gain; };
uint8_t getGain();
bool analogWrite(uint16_t value, uint8_t channel = 0);
uint16_t lastValue(uint8_t channel = 0) { return _value[channel]; };
uint16_t lastValue(uint8_t channel = 0);
void fastWriteA(uint16_t value);
void fastWriteB(uint16_t value);
@ -62,30 +60,30 @@ public:
// shutDown - Page 21 ==> write will wake up.
void shutDown();
bool isActive() { return _active; };
bool isActive();
// speed in Hz
void setSPIspeed(uint32_t speed);
uint32_t getSPIspeed() { return _SPIspeed; };
uint32_t getSPIspeed();
//
// MCP49xxx series only
//
// see page 20 ==> not functional for MCP48xx series.
void setBufferedMode(bool mode = false) { _buffered = mode; };
bool getBufferedMode() { return _buffered; };
void setBufferedMode(bool mode = false);
bool getBufferedMode();
// debugging
void reset();
bool usesHWSPI() { return _hwSPI; };
bool usesHWSPI();
#if defined(ESP32) // ESP32 specific
void selectHSPI() { _useHSPI = true; };
void selectVSPI() { _useHSPI = false; };
bool usesHSPI() { return _useHSPI; };
bool usesVSPI() { return !_useHSPI; };
void selectHSPI();
void selectVSPI();
bool usesHSPI();
bool usesVSPI();
// to overrule the ESP32s default hardware pins
void setGPIOpins(uint8_t clk, uint8_t miso, uint8_t mosi, uint8_t select);
@ -99,19 +97,19 @@ public:
protected:
uint8_t _dataOut; // Data out Pin (MOSI)
uint8_t _clock; // Clock Pin (SCK)
uint8_t _select; // Chip Select Pin (CS)
uint8_t _latchPin = 255; // Latch-DAC Pin (LDAC)
bool _hwSPI; // Hardware SPI (true) or Software SPI (false)
uint32_t _SPIspeed = 16000000; // SPI-Bus Frequency
uint8_t _dataOut; // Data out Pin (MOSI)
uint8_t _clock; // Clock Pin (SCK)
uint8_t _select; // Chip Select Pin (CS)
uint8_t _latchPin = 255; // Latch-DAC Pin (LDAC)
bool _hwSPI; // Hardware SPI (true) or Software SPI (false)
uint32_t _SPIspeed = 16000000; // SPI-Bus Frequency
uint8_t _channels; // Number of DAC-Channels of a given Chip
uint16_t _maxValue; // Maximum value of a given Chip
uint16_t _value[2]; // Current value (cache for performance)
uint8_t _gain; // Programmable Gain Amplifier variable
bool _buffered = false; // Buffer for the Reference Voltage of the MCP49XX Series Chips
bool _active = true; // Indicates shutDown mode.
uint8_t _channels; // Number of DAC-Channels of a given Chip
uint16_t _maxValue; // Maximum value of a given Chip
uint16_t _value[2]; // Current value (cache for performance)
uint8_t _gain; // Programmable Gain Amplifier variable
bool _buffered = false; // Buffer for the Reference Voltage of the MCP49XX Series Chips
bool _active = true; // Indicates shutDown mode.
void transfer(uint16_t data);
uint8_t swSPI_transfer(uint8_t d);
@ -140,7 +138,7 @@ protected:
///////////////////////////////////////////////////////////////
//
// MCP4800 Series
// MCP4800 Series
//
class MCP4801 : public MCP_DAC
@ -181,7 +179,7 @@ public:
///////////////////////////////////////////////////////////////
//
// MCP4900 Series
// MCP4900 Series
//
class MCP4901 : public MCP_DAC
@ -225,7 +223,7 @@ public:
///////////////////////////////////////////////////////////////
//
// MCP4800 Series
// MCP4800 Series
//
class MCP4801 : public MCP_DAC
@ -266,7 +264,7 @@ public:
///////////////////////////////////////////////////////////////
//
// MCP4900 Series
// MCP4900 Series
//
class MCP4901 : public MCP_DAC

View File

@ -40,20 +40,24 @@ which is in the range of 2.7V .. 5.5V. Check datasheet for the details.
## Interface
```cpp
#include "MCP_DAC.h"
```
### Constructor
#### Constructor
- **MCP_DAC(uint8_t dataOut = 255, uint8_t clock = 255, SPIClassRP2040 \*mySPI = &SPI)** Constructor base class for RP2040.
- **MCP_DAC(uint8_t dataOut = 255, uint8_t clock = 255, SPIClass \*mySPI = &SPI)** Constructor base class.
Other devices just use their name as class object e.g. MCP4801 with same parameters.
- **begin(uint8_t select)** defines the select pin.
The select pin is used for device selection in case of multiple SPI devices.
- **uint8_t channels()** returns the number of channels.
- **uint8_t channels()** returns the number of channels, 1 or 2.
(note channel numbers are 0 or 1).
- **uint16_t maxValue()** returns the maximum value that can be set.
This relates to the number of bits, see table above.
### Gain
#### Gain
- **bool setGain(uint8_t gain = 1)** gain is 1 (default) or 2.
- **uint8_t getGain()** returns gain set, default 1.
@ -62,15 +66,22 @@ The analog output cannot go beyond the supply voltage.
So if Vref is connected to 5V, gain=2 will not output 10 Volts.
### Write
#### Write
- **bool analogWrite(uint16_t value, uint8_t channel = 0)** writes value to channel. The value is limited to maxValue.
- **bool analogWrite(uint16_t value, uint8_t channel = 0)** writes value to channel.
Default for channel 0 as that works for the single DAC devices.
The value is limited to maxValue.
Returns false in case of an invalid channel.
- **uint16_t lastValue(uint8_t channel = 0)** returns last written value, default for channel 0 as that works for the single DAC devices.
- **void setPercentage(float percentage)** percentage = 0..100.0% Wrapper around **analogWrite()**.
- **float getPercentage(uint8_t channel = 0)** returns percentage. Wrapper around **lastValue()**.
- **void fastWriteA(uint16_t value)** faster version to write to channel 0. Does not check flags and does not update **lastValue()**
- **void fastWriteB(uint16_t value)** faster version to write to channel 1. Does not check flags and does not update **lastValue()**
- **uint16_t lastValue(uint8_t channel = 0)** returns last written value.
Default for channel 0 as that works for the single DAC devices.
- **void setPercentage(float percentage, uint8_t channel = 0)** percentage = 0..100.0%.
Wrapper around **analogWrite()**.
- **float getPercentage(uint8_t channel = 0)** returns percentage.
Reads from cache.
- **void fastWriteA(uint16_t value)** faster version to write to channel 0.
Does not check flags and does not update **lastValue()**
- **void fastWriteB(uint16_t value)** faster version to write to channel 1.
Does not check flags and does not update **lastValue()**
- **bool increment(uint8_t channel = 0)** returns true if channel is incremented, false otherwise.
- **bool decrement(uint8_t channel = 0)** returns true if channel is decremented, false otherwise.
@ -79,44 +90,51 @@ That squeezes the most performance out of it for now.
Code for the other MCP4xxx can be written in same way.
### Shutdown
#### Shutdown
- **void shutDown()** shuts down the device, optional one might need to **triggerLatch()**
- **void shutDown()** shuts down the device, optional one might need to **triggerLatch()**.
- **bool isActive()** returns false if device is in shutdown mode.
Note: **write()** will set active to true again.
Note: any **write()** operation will set active to true again.
### Hardware SPI
#### Hardware SPI
To be used only if one needs a specific speed.
Check datasheet for details.
- **void setSPIspeed(uint32_t speed)** set SPI transfer rate
- **uint32_t getSPIspeed()** returns SPI transfer rate
- **void setSPIspeed(uint32_t speed)** set SPI transfer rate.
- **uint32_t getSPIspeed()** returns SPI transfer rate.
### LDAC
#### LDAC
- **void setLatchPin(uint8_t latchPin)** defines the latchPin, this is optional. The latchPin is used for simultaneous setting a value in multiple devices. Note the latchPin must be the same for all instances that need to be triggered together.
- **void setLatchPin(uint8_t latchPin)** defines the latchPin, this is optional.
The latchPin is used for simultaneous setting a value in both DAC registers.
It can also be used to synchronize the setting of multiple devices.
Note the latchPin must be the same for all instances that need to be triggered together.
- **triggerLatch()** toggles the defined latchPin, and all devices that are connected to it.
### Buffered
MCP49xxx series only, see page 20 ==> not functional for MCP48xx series.
- **void setBufferedMode(bool mode = false)** set buffered mode on/off. The default = false, unbuffered.
- **bool getBufferedMode()** returns set value
Note: pre 0.2.0 versions have the LDAC signal incorrectly inverted.
### Debug
#### Buffered
- **void reset()** resets internal variables to initial value. (use with care!)
**MCP49xxx series only**, see page 20 ==> not functional for MCP48xx series.
- **void setBufferedMode(bool mode = false)** set buffered mode on/off.
The default mode == false == unbuffered.
- **bool getBufferedMode()** returns set value.
#### Debug
- **void reset()** resets internal variables to initial value. (use with care!).
- **bool usesHWSPI()** returns true if HW SPI is used.
## ESP32 specific
### SPI port selection
#### SPI port selection
This functionality is new in 0.1.2 and it is expected that the interface will change
in the future.
@ -130,10 +148,11 @@ The **selectVSPI()** or the **selectHSPI()** needs to be called
BEFORE the **begin()** function.
### Experimental
#### Experimental
- **void setGPIOpins(uint8_t clk, uint8_t miso, uint8_t mosi, uint8_t select)** overrule GPIO pins of ESP32 for hardware SPI. needs to be called
AFTER the **begin()** function.
- **void setGPIOpins(uint8_t clk, uint8_t miso, uint8_t mosi, uint8_t select)**
overrule GPIO pins of ESP32 for hardware SPI.
Needs to be called AFTER the **begin()** function.
```cpp
void setup()
@ -148,19 +167,20 @@ This interface can change in the future as the **select** pin is known
in the code.
### ESP32 connections to MCP4922 (example)
#### ESP32 connections to MCP4922 (example)
ESP32 has **four** SPI peripherals from which two can be used.
SPI0 and SPI1 are used to access flash memory. SPI2 and SPI3 are "user" SPI controllers a.k.a. HSPI and VSPI.
SPI0 and SPI1 are used to access flash memory.
SPI2 and SPI3 are "user" SPI controllers a.k.a. HSPI and VSPI.
| MCP4922 | HSPI = SPI2 | VSPI = SPI3 |
|:--------:|:-------------:|:-------------:|
| CS | SELECT = 15 | SELECT = 5 |
| SCK | SCLK = 14 | SCLK = 18 |
| SDI | MOSI = 13 | MOSI = 23 |
| not used | MISO = 12 | MISO = 19 |
| MCP4922 | HSPI = SPI2 | VSPI = SPI3 |
|:----------:|:-------------:|:-------------:|
| CS | SELECT = 15 | SELECT = 5 |
| SCK | SCLK = 14 | SCLK = 18 |
| SDI | MOSI = 13 | MOSI = 23 |
| not used | MISO = 12 | MISO = 19 |
By using different **SELECT** pins multiple DAC's can be controlled over
one SPI bus.
@ -168,15 +188,23 @@ one SPI bus.
## RP2040 specific
### SPI port selection
#### SPI port selection
The SPI Port selections happens in the constructor with e.g. &SPI or &SPI1. For the pin swap, you need the call the experimental feature **void setGPIOpins**. In the constructor you need to call the parameter dataOut and clock both with 255 (0xff) or otherwise it will use SoftSPI.
The SPI Port selections happens in the constructor with e.g. &SPI or &SPI1.
For the pin swap, you need the call the experimental feature **void setGPIOpins**.
In the constructor you need to call the parameter dataOut and clock both
with 255 (0xff) or otherwise it will use SoftSPI.
### Experimental
#### Experimental
- **void setGPIOpins(uint8_t clk, uint8_t miso, uint8_t mosi, uint8_t select)** overrule GPIO pins of RP2040 for different SPI pins. needs to be called
AFTER the **begin()** function. Selected pins must match the RP2040 pinout! Warning! This command changes the Pins of the Bus not only of a specific device, but all devices, that are connected on that Bus!
- **void setGPIOpins(uint8_t clk, uint8_t miso, uint8_t mosi, uint8_t select)**
overrule GPIO pins of RP2040 for different SPI pins.
Needs to be called AFTER the **begin()** function.
Selected pins must match the RP2040 pinout!
Warning! This command changes the Pins of the bus not only of a specific device,
but all devices, that are connected on that bus!
```cpp
@ -189,7 +217,7 @@ void setup()
}
```
### Pico connections to MCP4922 (example)
#### Pico connections to MCP4922 (example)
The RP2040 has **two** SPI peripherals from which two can be used.
@ -219,18 +247,24 @@ See examples
- test test test and ....
- improve documentation.
#### Should
#### Could
- refactor the API (how).
- minimize conditional in code if possible.
- functional names for magic masks.
#### Wont
- **useSPI1** and **useHSPI** are functional identical indicators.
- how to refactor to a generic model? Should work for all libraries.
- int8_t HWSPIport = 0, 1, 2, 3, 4, .... (-1 == SW SPI ?).
- numbers are not self-documenting.
- **selectSPIport(int)** ?
- would reduce conditional code.
#### Could
- refactor the API (how).
- minimize conditional in code if possible.
- move code from .h to .cpp
- does not improve library

View File

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

View File

@ -1,5 +1,5 @@
name=MCP_DAC
version=0.1.8
version=0.2.0
author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=Arduino library for Microchip SPI DAC, 8, 10, 12 bit; 1 or 2 channel.