
[![Arduino CI](https://github.com/RobTillaart/DAC8571/workflows/Arduino%20CI/badge.svg)](https://github.com/marketplace/actions/arduino_ci)
[![Arduino-lint](https://github.com/RobTillaart/DAC8571/actions/workflows/arduino-lint.yml/badge.svg)](https://github.com/RobTillaart/DAC8571/actions/workflows/arduino-lint.yml)
[![JSON check](https://github.com/RobTillaart/DAC8571/actions/workflows/jsoncheck.yml/badge.svg)](https://github.com/RobTillaart/DAC8571/actions/workflows/jsoncheck.yml)
[![GitHub issues](https://img.shields.io/github/issues/RobTillaart/DAC8571.svg)](https://github.com/RobTillaart/DAC8571/issues)

[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/RobTillaart/DAC8571/blob/master/LICENSE)
[![GitHub release](https://img.shields.io/github/release/RobTillaart/DAC8571.svg?maxAge=3600)](https://github.com/RobTillaart/DAC8571/releases)
[![PlatformIO Registry](https://badges.registry.platformio.org/packages/robtillaart/library/DAC8571.svg)](https://registry.platformio.org/libraries/robtillaart/DAC8571)


# DAC8571

Arduino library for DAC8571 I2C, 1 channel, 16 bit DAC.

## Description

**Experimental**

The DAC8571 is a small low-power, single channel, 16-bit voltage 
Power-On Reset to Zero output DAC.

The DAC8571 has one 16 bit DAC. The output value can be set from 0..65535.
This results in a voltage which depends on a reference voltage Vref (see datasheet).
At power up the channel always will start with a value of zero.

The DAC8571 has a temporary register in which a value can be preloaded.
This can be used to change the DAC at a later moment.

**Not supported** is the broadcast function to change multiple DAC8571 
(and compatibles) at the same moment by using this temporary register.

As said the library is experimental need to be tested with hardware.
Feedback is always welcome, please open an issue.

Kudos to Paul for first tests.


### 3V3

To connect the DAC8571 to a 3V3 device one need to use level converters.
(Preliminary test Paul)


### Settling time

From datasheet page 24.

Settling time to within the 16-bit accurate range of the DAC8571 is achievable within 
10 μs for a full-scale code change at the input. 
Worst case settling times between consecutive code changes is typically less than 2 μs,
therefore, the update rate is limited by the I2C interface.


## I2C

### Address

The DAC8571 support 2 addresses by means of an A0 address pin.


|  Address  |   A0   |
|:---------:|:------:|
|   0x4C    |   LOW  |
|   0x4E    |  HIGH  |

It might be possible to connect this address pin to an IO pin
and keep only one HIGH and remaining LOW to support many devices.
This is not tested, feedback welcome.


### I2C pull ups

To be able to reach 1 MHz (ESP32) the pull ups need to be fairly strong.
Preliminary tests indicate that 2K work.


### I2C performance

Extend table, use output of **DAC8571_performance.ino**

Time in microseconds.  
array == write(array, length)
Assumption all write modi have similar performance.

Test Arduino UNO  - version 0.1.0

|  Speed   |  function  |  time  |  notes  |
|:--------:|:----------:|:------:|:-------:|
|  100000  |  write()   |        |
|  100000  |  read()    |        |
|  200000  |  write()   |        |
|  200000  |  read()    |        |
|  400000  |  write()   |        |
|  400000  |  read()    |        |


Test ESP32 (Kudos to Paul) - version 0.1.0

|  Speed   |  write()  |  read()  |  write(array)  |  Notes  |
|:--------:|:---------:|:--------:|:--------------:|:--------|
|  50000   |  800.01   |  800.04  |                |
|  100000  |  439.02   |  441.18  |  2100.01       |  array == 10 elements!
|  200000  |  239.12   |  242.41  |  1090.82       |
|  300000  |  178.05   |  181.81  |   757.57       |
|  400000  |  148.94   |  156.83  |   595.24       |
|  500000  |  131.57   |  142.93  |   497.99       |
|  600000  |  120.68   |  136.35  |   428.59       |
|  700000  |  113.19   |  130.40  |   384.64       |
|  800000  |  108.12   |  128.18  |   351.85       |
|  900000  |   98.21   |  106.67  |   317.45       |
|  1000000 |   94.11   |   94.58  |   296.88       |



### I2C multiplexing

Sometimes you need to control more devices than possible with the default
address range the device provides.
This is possible with an I2C multiplexer e.g. TCA9548 which creates up 
to eight channels (think of it as I2C subnets) which can use the complete 
address range of the device. 

Drawback of using a multiplexer is that it takes more administration in 
your code e.g. which device is on which channel. 
This will slow down the access, which must be taken into account when
deciding which devices are on which channel.
Also note that switching between channels will slow down other devices 
too if they are behind the multiplexer.

- https://github.com/RobTillaart/TCA9548


### Related

- https://github.com/RobTillaart/AD5680 (18 bit DAC)
- https://github.com/RobTillaart/DAC8550
- https://github.com/RobTillaart/DAC8551
- https://github.com/RobTillaart/DAC8552
- https://github.com/RobTillaart/DAC8554
- https://github.com/RobTillaart/DAC8571
- https://github.com/RobTillaart/DAC8574
- https://github.com/RobTillaart/MCP_DAC
- https://github.com/RobTillaart/MCP_ADC


## Interface

```cpp
#include "DAC8571.h"
```

### Constructor

- **DAC8571(uint8_t address = 0x4C, TwoWire \*wire = &Wire)** constructor with I2C address.
Default is 0x4C, optional set the WireN I2C bus.
- **bool begin(uint16_t value = 0)** Set initial value for the DAC, typical zero (power on default).
Returns false if address out of range, or if device cannot be seen on the I2C bus.
Returns **true** if successful.
- **bool isConnected()** test to see if address can be reached on the I2C bus.
- **uint8_t getAddress()** returns address set in constructor.


### Core

The DAC8571 has one 16 bit DAC. The output value can be set from 0..65535.

- **bool write(uint16_t value)** writes a value 0..65535 to the DAC.
No default, user must explicit set value.
- **uint16_t lastWrite()** get last value written from cache (fast).
- **uint16_t read()** get last written value from device.

Percentage wrappers

- **bool setPercentage(float perc)** set 0.00 .. 100.00, 
returns true on success.
- **float getPercentage()** returns 0.0 .. 100.0 from cache.


### Write modi

The DAC8571 can be written in different modi (datasheet page 19).
Not all modi are supported yet, these need investigation and testing.

- **void setWriteMode(uint8_t mode = DAC8571_MODE_NORMAL)**
- **uint8_t getWriteMode()**


Setting the mode will be applied for all writes until mode is changed.

|  Mode                    |  Meaning  |
|:-------------------------|:----------|
| DAC8571_MODE_STORE_CACHE |  store value in temporary register. Preparation for (2, 3 and 4)
| DAC8571_MODE_NORMAL      |  update DAC with value written. **DEFAULT**.
| DAC8571_MODE_WRITE_CACHE |  writes the temporary register to DAC, ignores new value.
| DAC8571_MODE_BRCAST_0    |  broadcast update. See below.
| other                    |  maps onto default **DAC8571_MODE_NORMAL**.
| DAC8571_MODE_BRCAST_1    |  broadcast update. **Not supported yet**. See below.
| DAC8571_MODE_BRCAST_2    |  broadcast update. **Not supported yet**. See below.


With **DAC8571_MODE_STORE_CACHE** one can fill a buffer and the device will
set that when a value is written in **DAC8571_MODE_WRITE_CACHE**.
Especially useful for multichannel synchronuous update.


### Write multiple values - High speed mode.

The maximum length depends on the internal I2C BUFFER of the board.
For Arduino this is typical 32 bytes so it allows 14 values.

- **void write(uint16_t arr[n], uint8_t length)** Writes a buffer with 
max 14 values in one I2C call. 
The last value written will be remembered in **lastWrite()**.

This function can be used for a defined fast ramp up / down.


### Power Down mode

To investigate: Mixes also with broadcast ==> complex API.

- **void powerDown(uint8_t pdMode = 0)** default low power.
- **void wakeUp(uint16_t value = 0)** wake up, DAC value set to zero by default.

See table 6, page 22 datasheet for details.

|  Power Down Mode       |  value  |  Meaning  |
|:-----------------------|:-------:|:----------|
|  DAC8571_PD_LOW_POWER  |    0    |  170 uA
|  DAC8571_PD_FAST       |    1    |  250 uA
|  DAC8571_PD_1_KOHM     |    2    |  200 nA, GND 1 KOhm
|  DAC8571_PD_100_KOHM   |    3    |  200 nA, GND 100 KOhm
|  DAC8571_PD_HI_Z       |    4    |  200 nA, open circuit, high impedance


### Broadcast mode

Different ways possible, need to investigate API. (page 19)

Three broadcast commands exists:

|  Mode                   |  Meaning                                 |  Notes  |
|:------------------------|:-----------------------------------------|:--------|
|  DAC8571_MODE_BRCAST_0  | Load all channels from temporary register |  
|  DAC8571_MODE_BRCAST_1  | Load all channels with data.              |  not supported
|  DAC8571_MODE_BRCAST_2  | Power down all devices                   |  not supported


### Error codes

- **int lastError()** always check this value after a read() / write() 
to see if it was DAC8571_OK.
After the call to **lastError()** the error value is reset to DAC8571_OK.

|  Error code             |  Value  |  Notes  |
|:------------------------|:-------:|:--------|
|  DAC8571_OK             |  0x00   |
|  DAC8571_I2C_ERROR      |  0x81   |
|  DAC8571_ADDRESS_ERROR  |  0x82   |
|  DAC8571_BUFFER_ERROR   |  0x83   |  write(arr, length) length error


## Future

#### Must

- get hardware to test
- improve documentation
- test different write modi

#### Should

- extend / fill performance table
- replace magic numbers
- derived classes **DAC7571** (12 bit) **DAC6571** (10 bit)?

#### Could

- add examples

#### Wont

- investigate broadcast support

## Support

If you appreciate my libraries, you can support the development and maintenance.
Improve the quality of the libraries by providing issues and Pull Requests, or
donate through PayPal or GitHub sponsors.

Thank you,


