Skip to content

Commit f10adf3

Browse files
committed
0.1.1 AD7390
1 parent bceb17f commit f10adf3

File tree

15 files changed

+534
-85
lines changed

15 files changed

+534
-85
lines changed

libraries/AD7390/AD7390.cpp

Lines changed: 46 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//
22
// FILE: AD7390.cpp
33
// AUTHOR: Rob Tillaart
4-
// VERSION: 0.1.0
4+
// VERSION: 0.1.1
55
// DATE: 2025-06-14
66
// PURPOSE: Arduino library for AD7390/AD7391 12/10 bit SPI DAC.
77
// URL: https://github.com/RobTillaart/AD7390
@@ -12,25 +12,27 @@
1212
// SOFTWARE SPI
1313
AD7390::AD7390(uint8_t select, uint8_t clear, uint8_t dataOut, uint8_t clock)
1414
{
15-
_select = select;
16-
_clear = clear;
17-
_dataOut = dataOut;
18-
_clock = clock;
19-
_hwSPI = false;
20-
_mySPI = NULL;
21-
_maxValue = 4095;
15+
_select = select;
16+
_clear = clear;
17+
_dataOut = dataOut;
18+
_clock = clock;
19+
_hwSPI = false;
20+
_mySPI = NULL;
21+
_maxValue = 4095;
22+
_refVoltage = 0;
2223
}
2324

2425
// HARDWARE SPI
2526
AD7390::AD7390(uint8_t select, uint8_t clear, __SPI_CLASS__ * mySPI)
2627
{
27-
_select = select;
28-
_clear = clear;
29-
_dataOut = 255;
30-
_clock = 255;
31-
_hwSPI = true;
32-
_mySPI = mySPI;
33-
_maxValue = 4095;
28+
_select = select;
29+
_clear = clear;
30+
_dataOut = 255;
31+
_clock = 255;
32+
_hwSPI = true;
33+
_mySPI = mySPI;
34+
_maxValue = 4095;
35+
_refVoltage = 0;
3436
}
3537

3638
// initializes the pins and starts SPI in case of hardware SPI
@@ -67,7 +69,7 @@ void AD7390::begin(uint16_t value)
6769
//
6870
bool AD7390::setValue(uint16_t value)
6971
{
70-
if (_value > _maxValue) return false;
72+
if (value > _maxValue) return false;
7173
_value = value;
7274
updateDevice(value);
7375
return true;
@@ -104,6 +106,31 @@ void AD7390::clear()
104106
_value = 0;
105107
}
106108

109+
bool AD7390::setRefVoltage(float volts)
110+
{
111+
if ((volts < 0) || (volts > 5.5)) return false;
112+
_refVoltage = volts;
113+
return true;
114+
}
115+
116+
float AD7390::getRefVoltage()
117+
{
118+
return _refVoltage;
119+
}
120+
121+
bool AD7390::setVoltage(float volts)
122+
{
123+
if ((volts < 0) || (volts > _refVoltage)) return false;
124+
return setValue(round(volts * (_maxValue / _refVoltage)));
125+
}
126+
127+
float AD7390::getVoltage()
128+
{
129+
uint16_t v = _value;
130+
if (v == 0) return 0.0;
131+
return (_refVoltage / _maxValue) * v;
132+
}
133+
107134

108135
/////////////////////////////////////////////////////////////////////////////
109136
//
@@ -133,13 +160,13 @@ bool AD7390::usesHWSPI()
133160
void AD7390::updateDevice(uint16_t value)
134161
{
135162
digitalWrite(_select, HIGH);
136-
if (_hwSPI)
163+
if (_hwSPI) // hardware SPI
137164
{
138165
_mySPI->beginTransaction(_spi_settings);
139166
_mySPI->transfer16(value);
140167
_mySPI->endTransaction();
141168
}
142-
else // Software SPI
169+
else // software SPI
143170
{
144171
swSPI_transfer(value);
145172
}
@@ -150,7 +177,7 @@ void AD7390::swSPI_transfer(uint16_t value)
150177
{
151178
uint8_t clk = _clock;
152179
uint8_t dao = _dataOut;
153-
for (uint16_t mask = 0x8000; mask; mask >>= 1) // 0x0800 performance?
180+
for (uint16_t mask = 0x8000; mask; mask >>= 1)
154181
{
155182
digitalWrite(dao,(value & mask));
156183
digitalWrite(clk, HIGH);

libraries/AD7390/AD7390.h

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// FILE: AD7390.h
44
// AUTHOR: Rob Tillaart
5-
// VERSION: 0.1.0
5+
// VERSION: 0.1.1
66
// DATE: 2025-06-14
77
// PURPOSE: Arduino library for AD7390/AD7391 12/10 bit SPI DAC.
88
// URL: https://github.com/RobTillaart/AD7390
@@ -11,7 +11,7 @@
1111
#include "SPI.h"
1212

1313

14-
#define AD7390_LIB_VERSION (F("0.1.0"))
14+
#define AD7390_LIB_VERSION (F("0.1.1"))
1515

1616

1717
#ifndef __SPI_CLASS__
@@ -41,15 +41,17 @@ class AD7390
4141
bool setValue(uint16_t value);
4242
uint16_t getValue();
4343
uint16_t getMaxValue();
44+
4445
bool setPercentage(float percentage);
4546
float getPercentage();
4647

4748
void clear(); // set DAC to zero.
4849

49-
// REFVOLTAGE possible interface addition
50-
// bool setRefVoltage(float volts);
51-
// bool setVoltage(float volts);
52-
// float getVoltage();
50+
// ref voltage API
51+
bool setRefVoltage(float volts);
52+
float getRefVoltage();
53+
bool setVoltage(float volts);
54+
float getVoltage();
5355

5456
// speed in Hz
5557
void setSPIspeed(uint32_t speed);
@@ -70,6 +72,7 @@ class AD7390
7072

7173
uint16_t _value;
7274
uint16_t _maxValue;
75+
float _refVoltage;
7376

7477
void updateDevice(uint16_t value);
7578
void swSPI_transfer(uint16_t value);

libraries/AD7390/CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
66
and this project adheres to [Semantic Versioning](http://semver.org/).
77

88

9+
## [0.1.1] - 2025-06-16
10+
- add four voltage functions.
11+
- fix setValue()
12+
- add examples
13+
- update keywords.txt
14+
- update readme.md
15+
- minor edits
16+
917
## [0.1.0] - 2025-06-14
1018
- initial version
1119

libraries/AD7390/README.md

Lines changed: 65 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,23 @@ The library is experimental as not all functionality is tested with hardware.
2828

2929
Check datasheet for all details.
3030

31-
The AD7390 has a Vref input which can be set to any voltage between 0 and Vdd (2.7 .. 5.5 Volt.
32-
This allows the output to vary between 0 volt and approx. Vref -1 LSB.
31+
The AD7390 has a Vref input which can be set to any voltage between 0 and Vdd = 2.7 .. 5.5 Volt.
32+
This allows the output to vary between 0 volt and approx. Vref -1 LSB.
33+
The AD7390 can do this in 4096 levels, the AD7391 has 1024 levels.
34+
3335
See datasheet page 8.
3436

35-
The device does not support reading the set value, so the library caches the
36-
last value set to provide this.
37-
Furthermore the library provides a "percentage" functions to set and get the output
38-
in a range from 0 to 100.0 %.
37+
The device does not support reading back the set value, so the library caches the
38+
last value set to mimic this functionality.
39+
40+
Furthermore the library provides "percentage" wrapper functions to set and get the
41+
output in a range from 0 to 100.0 %. Note that setPercentage() will round to the
42+
nearest valid integer value.
43+
44+
Finally the library provides "voltage" wrapper functions to set and get the
45+
output in a range from 0 to Vref. Note that setVoltage() will round to the nearest
46+
valid integer value. Be aware that the actual Vref must be set by setRefVoltage()
47+
and kept in sync with the actual voltage to have the voltage functions work correctly.
3948

4049
As always feedback is welcome.
4150

@@ -72,7 +81,26 @@ Mainly other DAC libraries.
7281

7382
## Performance
7483

75-
TODO
84+
Times in microseconds per 1000 calls.
85+
86+
| mode | function | UNO R3 | ESP32 | notes |
87+
|:------:|:----------------|:--------:|:-------:|:--------|
88+
| HW-SPI | setValue | 15404 | |
89+
| HW-SPI | getValue | 884 | | cached value
90+
| HW-SPI | setPercentage | 93764 | |
91+
| | | | |
92+
| SW-SPI | setValue | 204332 | |
93+
| SW-SPI | getValue | 884 | | cached value
94+
| SW-SPI | setPercentage | 282692 | |
95+
96+
97+
Note: 15404 micros for 1000 calls, means the max update speed
98+
is in the order of 60k calls per second in theory.
99+
In practice one needs to calculate values or read them from some source,
100+
or do other tasks. So actual speeds depends on your project.
101+
102+
The setPercentage and setVoltage functions have additional overhead
103+
due to the use of float math.
76104

77105

78106
## Interface
@@ -108,12 +136,31 @@ Returns false if value > maxValue.
108136

109137
### Percentage
110138

139+
Wrapper functions.
140+
111141
- **bool setPercentage(float percentage)** sets the value of the DAC as percentage 0..100.
112142
Note the actual value set gets rounded.
113143
Returns false if percentage < 0 or > 100.
114144
- **float getPercentage()** returns the percentage set, calculated from the value.
115145
The percentage can differ a small bit from the percentage set.
116146

147+
### Voltage
148+
149+
Wrapper functions.
150+
151+
Before calling **setVoltage()** one must set the reference voltage correctly,
152+
otherwise the functions do not work.
153+
The user is responsible to keep **setRefVoltage()** in sync with the actual Vref.
154+
155+
- **bool setRefVoltage(float volts)** set Reference Voltage of Vref pin.
156+
Returns false if volts is out of range 0..5.5 Volt.
157+
- **float getRefVoltage()** return last set Reference Voltage, default 0.
158+
- **bool setVoltage(float volts)** sets the value of the DAC as a voltage 0..Vref.
159+
Note the actual value set gets rounded.
160+
Returns false if volts is out of range 0..Vref.
161+
- **float getVoltage()** returns the voltage set, calculated from the value.
162+
The voltage can differ a small bit from the voltage set.
163+
117164
### Hardware SPI
118165

119166
To be used only if one needs a specific speed for hardware SPI.
@@ -137,16 +184,24 @@ Has no effect on software SPI.
137184

138185
#### Should
139186

140-
- add reference voltage functions. (See .h)
141187
- extend unit tests
142-
- add examples
143188

144189
#### Could
145190

191+
- **getLastUpdate()** track millis
192+
- if new value == cached value skip
193+
- optimize AVR SW SPI
194+
- optimize float math setPercentage() and setVoltage()
195+
- calculate and cache the conversion Pfactor and Vfactor. (8 bytes)
196+
- remove range test as setValue() does this?
197+
- test SW-SPI with 0x0800 mask (clock only 12 bits does that work).
198+
- add examples
146199

147200
#### Wont
148201

149-
202+
- build in low pass filter (too complex)
203+
- should percentage and voltage API clip or fail if out of range?
204+
- fail seems safest.
150205

151206
## Support
152207

libraries/AD7390/examples/AD7390_demo/AD7390_demo.ino

Lines changed: 10 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@
77

88
#include "AD7390.h"
99

10-
uint32_t start, stop;
11-
10+
uint32_t start;
1211

1312
// select, reset, data, clock == SOFTWARE SPI
1413
// AD7390 myDAC(6, 7, 11, 13);
@@ -30,10 +29,9 @@ void setup()
3029
SPI.begin();
3130
myDAC.begin(0);
3231

33-
// test_extremes();
34-
// test_sinus();
35-
// test_sawtooth();
36-
test_timing();
32+
test_extremes();
33+
test_sinus();
34+
test_sawtooth();
3735

3836
Serial.println("\nDone...");
3937
}
@@ -50,15 +48,12 @@ void test_extremes()
5048

5149
Serial.println("0");
5250
myDAC.setValue(0);
53-
delay(2000);
5451

5552
Serial.println("2047");
5653
myDAC.setValue(2047);
57-
delay(2000);
5854

5955
Serial.println("4095");
6056
myDAC.setValue(4095);
61-
delay(2000);
6257
}
6358

6459

@@ -72,8 +67,9 @@ void test_sinus()
7267
uint32_t i = 0;
7368
while (millis() - start < 10000)
7469
{
75-
int8_t value = 2047 * sin(i * TWO_PI / 100);
70+
int16_t value = 2047 * sin(i * TWO_PI / 100);
7671
myDAC.setValue(2047 + value);
72+
Serial.println(2047 + value);
7773
i++;
7874
}
7975
}
@@ -86,40 +82,13 @@ void test_sawtooth()
8682

8783
start = millis();
8884
uint16_t i = 0;
89-
while (millis() - start < 25500)
85+
while (millis() - start < 10000)
9086
{
9187
if (i >= 4095) i = 0;
92-
myDAC.setValue(i++); // auto wrap is fast...
93-
delay(100);
94-
}
95-
}
96-
97-
98-
void test_timing()
99-
{
100-
Serial.println(__FUNCTION__);
101-
delay(10);
102-
103-
start = micros();
104-
for (int i = 0; i < 1000; i++)
105-
{
106-
myDAC.setValue(i++); // auto wrap is fast...
107-
}
108-
stop = micros();
109-
Serial.print("1000 x setValue():\t");
110-
Serial.println(stop - start);
111-
delay(10);
112-
113-
volatile int x = 0;
114-
start = micros();
115-
for (int i = 0; i < 1000; i++)
116-
{
117-
x += myDAC.getValue();
88+
myDAC.setValue(i++);
89+
Serial.println(i);
90+
delay(1);
11891
}
119-
stop = micros();
120-
Serial.print("1000 x getValue():\t");
121-
Serial.println(stop - start);
122-
delay(10);
12392
}
12493

12594

0 commit comments

Comments
 (0)