0.1.7 MCP_DAC

This commit is contained in:
rob tillaart 2022-10-02 17:19:23 +02:00
parent 8e0ab56977
commit c7cc4b04fa
10 changed files with 428 additions and 54 deletions

View File

@ -1,3 +1,18 @@
platforms:
rpipico:
board: rp2040:rp2040:rpipico
package: rp2040:rp2040
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
compile:
# Choosing to run compilation tests on 2 different Arduino platforms
platforms:
@ -9,3 +24,4 @@ compile:
- esp32
# - esp8266
# - mega2560
# - rpipico

View File

@ -1,7 +1,7 @@
//
// FILE: MCP_DAC.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.1.6
// VERSION: 0.1.7
// DATE: 2021-02-03
// PURPOSE: Arduino library for MCP_DAC
// URL: https://github.com/RobTillaart/MCP_DAC
@ -20,6 +20,9 @@
// default parameter 0 for getPercentage()
// extended unit tests
// 0.1.6 2021-12-21 update library.json, license, minor edits
// 0.1.7 2022-10-02 support for RP2040 pico (kudos to Intubin)
// update documentation for RP2040 (kudos to Intubin)
// update build-CI to support RP2040
#include "MCP_DAC.h"
@ -70,6 +73,19 @@ void MCP_DAC::begin(uint8_t select)
mySPI->end();
mySPI->begin(18, 19, 23, select); // CLK=18 MISO=19 MOSI=23
}
#elif defined(ARDUINO_ARCH_RP2040)
if (_useSPI1 == true) {
mySPI = &SPI1;
}else{
mySPI = &SPI;
}
mySPI->end();
mySPI->begin();
#else // generic hardware SPI
mySPI = &SPI;
mySPI->end();
@ -86,7 +102,7 @@ void MCP_DAC::begin(uint8_t select)
}
#if defined(ESP32)
#if defined(ESP32) or defined(ARDUINO_ARCH_RP2040)
void MCP_DAC::setGPIOpins(uint8_t clk, uint8_t miso, uint8_t mosi, uint8_t select)
{
_clock = clk;
@ -96,7 +112,20 @@ void MCP_DAC::setGPIOpins(uint8_t clk, uint8_t miso, uint8_t mosi, uint8_t selec
digitalWrite(_select, HIGH);
mySPI->end(); // disable SPI
#if defined(ESP32)
mySPI->begin(clk, miso, mosi, select);
#elif defined(ARDUINO_ARCH_RP2040)
mySPI->setCS(select);
mySPI->setSCK(clk);
mySPI->setTX(mosi);
mySPI->begin();
#endif
}
#endif

View File

@ -2,7 +2,7 @@
//
// FILE: MCP_DAC.h
// AUTHOR: Rob Tillaart
// VERSION: 0.1.6
// VERSION: 0.1.7
// DATE: 2021-02-03
// PURPOSE: Arduino library for MCP_DAC
// URL: https://github.com/RobTillaart/MCP_DAC
@ -13,7 +13,7 @@
#include "SPI.h"
#define MCP_DAC_LIB_VERSION (F("0.1.6"))
#define MCP_DAC_LIB_VERSION (F("0.1.7"))
@ -74,47 +74,77 @@ public:
void reset();
bool usesHWSPI() { return _hwSPI; };
// ESP32 specific
#if defined(ESP32)
#if defined(ESP32) // ESP32 specific
void selectHSPI() { _useHSPI = true; };
void selectVSPI() { _useHSPI = false; };
bool usesHSPI() { return _useHSPI; };
bool usesVSPI() { return !_useHSPI; };
// to overrule ESP32 default hardware pins
// to overrule the ESP32s default hardware pins
void setGPIOpins(uint8_t clk, uint8_t miso, uint8_t mosi, uint8_t select);
#elif defined(ARDUINO_ARCH_RP2040) // RP2040 specific
// check which SPI-Bus (SPI or SPI1) is used
void selectSPI() { _useSPI1 = false; };
void selectSPI1() { _useSPI1 = true; };
bool usesSPI() { return !_useSPI1; };
bool usesSPI1() { return _useSPI1; };
// to overrule the RP2040s default hardware pins
void setGPIOpins(uint8_t clk, uint8_t miso, uint8_t mosi, uint8_t select);
#endif
protected:
uint8_t _dataOut;
uint8_t _clock;
uint8_t _select;
uint8_t _latchPin = 255;
bool _hwSPI;
uint32_t _SPIspeed = 16000000;
uint8_t _channels;
uint16_t _maxValue;
uint16_t _value[2];
uint8_t _gain;
bool _buffered = false;
bool _active = true;
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 _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);
#if defined(ARDUINO_ARCH_RP2040)
SPIClassRP2040 * mySPI;
#else
SPIClass * mySPI;
#endif
SPISettings _spi_settings;
#if defined(ESP32)
bool _useHSPI = true;
bool _useHSPI = true;
#elif defined(ARDUINO_ARCH_RP2040)
bool _useSPI1 = false;
#endif
};
///////////////////////////////////////////////////////////////
//
// 4800 Series
// MCP4800 Series
//
class MCP4801 : public MCP_DAC
{
@ -160,7 +190,7 @@ public:
///////////////////////////////////////////////////////////////
//
// 4900 Series
// MCP4900 Series
//
class MCP4901 : public MCP_DAC
{

View File

@ -34,7 +34,7 @@ Please post an issue if there are problems.
| MCP4922 | 2 | 12 | 4095 | external |
The output voltage of the MCP_DAC depends on the voltage supplied,
The output voltage of the MCP_DAC depends on the voltage supplied,
which is in the range of 2.7V .. 5.5V. Check datasheet for the details.
@ -45,8 +45,8 @@ which is in the range of 2.7V .. 5.5V. Check datasheet for the details.
- **MCP_DAC(uint8_t dataOut = 255, uint8_t clock = 255)** Constructor base class.
Other devices just use their name as class object e.g. MCP4801 with same parameters.
- **begin(uint8_t select, uint8_t latchPin = 255)** defines the select pin.
The select pin is used for device selection in case of multiple SPI devices.
- **begin(uint8_t select, uint8_t latchPin = 255)** 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.
- **uint16_t maxValue()** returns the maximum value that can be set.
This relates to the number of bits, see table above.
@ -57,13 +57,13 @@ This relates to the number of bits, see table above.
- **bool setGain(uint8_t gain = 1)** gain is 1 (default) or 2.
- **uint8_t getGain()** returns gain set, default 1.
The analog output cannot go beyond the supply voltage.
The analog output cannot go beyond the supply voltage.
So if Vref is connected to 5V, gain=2 will not output 10 Volts.
### 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. 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()**.
@ -73,7 +73,7 @@ Returns false in case of an invalid channel.
- **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.
For fastest speed there is an example added **MCP4921_standalone.ino**.
For fastest speed there is an example added **MCP4921_standalone.ino**.
That squeezes the most performance out of it for now.
Code for the other MCP4xxx can be written in same way.
@ -99,7 +99,7 @@ To be used only if one needs a specific speed.
- **triggerLatch()** toggles the defined latchPin, and all devices that are connected to it.
### Buffered
### Buffered
MCP49xxx series only, see page 20 ==> not functional for MCP48xx series.
@ -113,23 +113,25 @@ MCP49xxx series only, see page 20 ==> not functional for MCP48xx series.
- **bool usesHWSPI()** returns true if HW SPI is used.
### ESP32 specific
## ESP32 specific
### SPI port selection
This functionality is new in 0.1.2 and it is expected that the interface will change
in the future.
in the future.
- **void selectHSPI()** in case hardware SPI, the ESP32 has two options HSPI and VSPI.
- **void selectVSPI()** see above.
- **bool usesHSPI()** returns true if HSPI is used.
- **bool usesVSPI()** returns true if VSPI is used.
The **selectVSPI()** or the **selectHSPI()** needs to be called
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
- **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
@ -145,7 +147,7 @@ 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.
@ -154,20 +156,85 @@ SPI0 and SPI1 are used to access flash memory. SPI2 and SPI3 are "user" SPI cont
| MCP4922 | HSPI = SPI2 | VSPI = SPI3 |
|:--------:|:-------------:|:-------------:|
| CS | SELECT = 15 | SELECT = 5 |
| SCK | SCLK = 14 | SCLK = 18 |
| SDI | MOSI = 13 | MOSI = 23 |
| 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.
## Future
- test test test and ....
- refactor the API
- improve documentation
- ...
## RP2040 specific
### SPI port selection
Select the SPI bus on which the device is on. Both need to be called before the **begin()** function. If the function is called after the **begin()** function, changes will only apply if the **end()** and then the **begin()** functions are called.
- **void selectSPI()** Select the SPI (SPI0) bus. This is the default and does not need to be called.
- **void selectSPI1()** Select the (second) SPI1 bus.
- **bool usesSPI()** returns true if SPI is used
- **bool usesSPI1()** returns true if SPI1 is used
### 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. Selcted pins must match the RP2040 pinout!
```cpp
void setup()
{
MCP.selectSPI(); //use for SPI / SPI0
MCP.selectSPI1(); //use for SPI1
MCP.begin(17);
MCP.setGPIOpins(CLK, MISO, MOSI, SELECT); // SELECT should match the param of begin()
}
```
### Pico connections to MCP4922 (example)
The RP2040 has **two** SPI peripherals from which two can be used.
SPI (SPI0) and SPI1 can both be used to connect devices.
| MCP4922 | SPI / SPI0 | SPI1 |
|:--------:|:-------------:|:-------------:|
| CS | SELECT = 17 | SELECT = 13 |
| SCK | SCLK = 18 | SCLK = 14 |
| SDI | MOSI = 19 | MOSI = 15 |
| not used | MISO = 16 | MISO = 12 |
By using different **SELECT** pins multiple DAC's can be controlled over
one SPI bus.
## Operation
See examples
## Future
#### Must
- test test test and ....
- improve documentation.
#### Should
- **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.

View File

@ -0,0 +1,27 @@
platforms:
rpipico:
board: rp2040:rp2040:rpipico
package: rp2040:rp2040
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
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,198 @@
//
// FILE: MCP4921_wave_generator_RP2040.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo function generators
// DATE: 2021-02-03
// URL: https://github.com/RobTillaart/FunctionGenerator
//
// depending on the platform, the range of "smooth" sinus is limited.
// other signals are less difficult so have a slightly larger range.
//
// PLATFORM SINUS SQUARE SAWTOOTH TRIANGLE
// UNO -100 Hz
// ESP32 -200 Hz -1000 -250 -100
//
/*
SPI Connections:
MOSI: GP 19
SCK: GP 18
CS: GP 17
LDAC: GP 2
SPI1 Connections:
MOSI: GP 15
SCK: GP 14
CS: GP 13
LDAC: GP 3
(Experimental) The Pins can be changed at any time via the setGPIO() command, view the Pico Pinout for compatible Pins
*/
#include "MCP_DAC.h"
uint16_t freq = 10;
uint32_t period = 0;
uint32_t halvePeriod = 0;
// q = square
// s = sinus
// w = sawtooth
// t = triangle
// r = random
char mode = 'q';
MCP4921 MCP;
uint16_t count;
uint32_t lastTime = 0;
// LOOKUP TABLE SINE
uint16_t sine[361];
void setup()
{
Serial.begin(115200);
// fill table with sinus values for fast lookup
for (int i = 0; i < 361; i++)
{
sine[i] = 2047 + round(2047 * sin(i * PI / 180));
}
MCP.selectSPI(); //select SPI
//MCP.selectSPI1(); //select SPI1
MCP.begin(17); // select pin = 17, SPI
//MCP.begin(13); // select pin = 13, SPI1
MCP.fastWriteA(0);
period = 1e6 / freq;
halvePeriod = period / 2;
while (1)
{
// Serial.println(analogRead(A0)); // read output back via A0.
yield();
uint32_t now = micros();
count++;
if (now - lastTime > 100000)
{
lastTime = now;
// Serial.println(count); // show # updates per 0.1 second
count = 0;
if (Serial.available())
{
int c = Serial.read();
switch (c)
{
case '+':
freq++;
break;
case '-':
freq--;
break;
case '*':
freq *= 10;
break;
case '/':
freq /= 10;
break;
case '0' ... '9':
freq *= 10;
freq += (c - '0');
break;
case 'c':
freq = 0;
break;
case 'A':
break;
case 'a':
break;
case 'q':
case 's':
case 'w':
case 't':
case 'r':
case 'z':
case 'm':
case 'h':
mode = c;
break;
default:
break;
}
period = 1e6 / freq;
halvePeriod = period / 2;
Serial.print(freq);
// Serial.print('\t');
// Serial.print(period);
// Serial.print('\t');
// Serial.print(halvePeriod);
Serial.println();
}
}
uint32_t t = now % period;
switch (mode)
{
case 'q':
if (t < halvePeriod ) MCP.fastWriteA(4095);
else MCP.fastWriteA(0);
break;
case 'w':
MCP.fastWriteA(t * 4095 / period );
break;
case 't':
if (t < halvePeriod) MCP.fastWriteA(t * 4095 / halvePeriod);
else MCP.fastWriteA( (period - t) * 4095 / halvePeriod );
break;
case 'r':
MCP.fastWriteA(random(4096));
break;
case 'z': // zero
MCP.fastWriteA(0);
break;
case 'h': // high
MCP.fastWriteA(4095);
break;
case 'm': // mid
MCP.fastWriteA(2047);
break;
default:
case 's':
// reference
// float f = ((PI * 2) * t)/period;
// MCP.setValue(2047 + 2047 * sin(f));
//
int idx = (360 * t) / period;
MCP.fastWriteA(sine[idx]); // lookuptable
break;
}
}
}
void loop()
{
}
// -- END OF FILE --

View File

@ -2,11 +2,13 @@
Note that some examples are ESP32 specific as they use e.g. VSPI()
| example | UNO | ESP32 |
|:-----------------------|:-----:|:-----:|
| MCP4911_test | Y | Y |
| MCP4921_standalone | Y | Y |
| MCP4921_test | Y | Y |
| MCP4921_VSPI | N | Y |
| MCP4921_wave_generator | N | Y |
| example | UNO | ESP32 | RP2040 |
|:-----------------------------:|:-----:|:-----:|:------:|
| MCP4911_test | Y | Y | Y |
| MCP4921_standalone | Y | Y | Y |
| MCP4921_test | Y | Y | Y |
| MCP4921_VSPI | N | Y | N |
| MCP4921_wave_generator | Y | Y | Y |
| MCP4921_wave_generator_ESP32 | N | Y | N |
| MCP4921_wave_generator_RP2040 | N | N | Y |

View File

@ -52,6 +52,11 @@ setGPIOpins KEYWORD2
increment KEYWORD2
decrement KEYWORD2
selectSPI KEYWORD2
selectSPI1 KEYWORD2
usesSPI KEYWORD2
usesSPI1 KEYWORD2
# Instances (KEYWORD2)
# Constants (LITERAL1)

View File

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

View File

@ -1,9 +1,9 @@
name=MCP_DAC
version=0.1.6
version=0.1.7
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.
paragraph= MCP4801, MCP4802, MCP4811,MCP4812, MCP4821, MCP4822, MCP4901, MCP4902, MCP4911, MCP4912, MCP4921, MCP4922
paragraph= MCP4801, MCP4802, MCP4811,MCP4812, MCP4821, MCP4822, MCP4901, MCP4902, MCP4911, MCP4912, MCP4921, MCP4922.
category=Sensors
url=https://github.com/RobTillaart/MCP_DAC
architectures=*