2021-01-29

This commit is contained in:
rob tillaart 2021-01-29 12:31:58 +01:00
parent 72861821fc
commit 7661eee8b4
1414 changed files with 61324 additions and 4630 deletions

View File

@ -1,7 +1,7 @@
# MIT License
Copyright (c) 2010-2020 Rob Tillaart
Copyright (c) 2010-2021 Rob Tillaart
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -1,39 +1,52 @@
# Arduino libraries
**NOTE** these libraries will all get their own repo, so this repository
does not have the latest version of all libs anymore. That said I will update
this bulk repo on request or if time permit.
**NOTE** these libraries all have their own repo under https://github.com/RobTillaart?tab=repositories
So this repository does not have the latest version of all libs anymore.
That said I will update this bulk repo on request or if time permit.
### Introduction
This repository contains several Arduino libraries I have written to be used in applications.
Most of them include example code how the libraries can be used.
Furthermore this repository contains a few stand alone applications.
### Questions
For questions about the usage of the libraries, please post a question on the Arduino
forum at http://forum.arduino.cc/
### Bugs and issues
**Please check if the library has its own repo first and fill an issue there**
For bugs in the libraries, please fill in an issue in Github as that makes it
far easier to track them. If possible provide a minimal code snippet that exposes
the bug. Also proposals for solutions are welcome.
For bugs in the libraries, please fill in an issue in Github as that makes it far easier to track them.
If possible provide a minimal code snippet that exposes the bug.
Add information about platform used and version etc.
Also proposals for solutions are welcome.
Other issues are not directly bugs but still problematic.
E.g. if a library is too slow for your application that is an serious issue, not a bug.
Please fill in an issue and provide as much details about your requirements.
Maybe I can help to fix it.
Issues are not bugs but still possible problematic. E.g. if a library is too slow
for your application that is an issue, not a bug. Please fill in an issue and provide
as much details about your requirements.
### Improvements and changes
For improvements and changes, please provide a pull request. I will try to follow up on them
asap but it can take quite some time. Please try to be generic in your improvements and try to
see "over the needs of your own application".
For improvements and changes, please provide a pull request.
I will try to follow up on them asap but it can take quite some time.
Please try to be generic in your improvements and try to see "over the needs of your own application".
There is no guarantee that pull requests will be honored.
In such case feel free to start your own modified library from a fork.
### License and Warranty
I appreciate if you give credits when appropriate, and if you want to donate, please
donate to charity like "doctors without borders".
I appreciate if you give credits when appropriate.
Please check the file LICENSE.md for the details.

View File

@ -0,0 +1,7 @@
compile:
# Choosing to run compilation tests on 2 different Arduino platforms
platforms:
- uno
- leonardo
- due
- zero

View File

@ -0,0 +1,13 @@
---
name: Arduino CI
on: [push, pull_request]
jobs:
arduino_ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: Arduino-CI/action@master
# Arduino-CI/action@v0.1.1

View File

@ -1,66 +1,68 @@
//
// FILE: ACS712.cpp
// AUTHOR: Rob Tillaart, Pete Thompson
// VERSION: 0.2.0
// VERSION: 0.2.1
// DATE: 2020-08-02
// PURPOSE: ACS712 library - current measurement
//
// HISTORY:
// 0.1.0 2020-03-17 initial version
// 0.1.1 2020-03-18 first release version
// 0.1.2 2020-03-21 automatic formfactor test
// 0.1.2 2020-03-21 automatic formfactor test
// 0.1.3 2020-05-27 fix library.json
// 0.1.4 2020-08-02 Allow for faster processors
// 0.2.0 2020-08-02 Add autoMidPoint
// 0.2.1 2020-12-06 Add arduino-CI + readme + unittest + refactor
#include "ACS712.h"
ACS712::ACS712(uint8_t analogPin, float volts, uint16_t maxADC, uint8_t mVperA)
{
_pin = analogPin;
// 1x 1000 for V -> mV
_mVpstep = 1000.0 * volts / maxADC;
_mVperAmpere = mVperA;
_formFactor = 0.70710678119; // 0.5 * sqrt(2); TODO: should be smaller in practice 0.5 ?
_midPoint = maxADC / 2;
_noisemV = 21; // Noise is 21mV according to datasheet
_pin = analogPin;
_mVpstep = 1000.0 * volts / maxADC; // 1x 1000 for V -> mV
_mVperAmpere = mVperA;
_formFactor = 0.70710678119; // 0.5 * sqrt(2); TODO: should be smaller in practice 0.5 ?
_midPoint = maxADC / 2;
_noisemV = 21; // Noise is 21mV according to datasheet
}
int ACS712::mA_AC(uint8_t freq)
{
uint32_t start = micros();
uint16_t period = ((freq == 60) ? 16670 : 20000);
uint16_t samples = 0;
uint16_t zeros = 0;
int _min, _max;
_min = _max = analogRead(_pin);
while (micros() - start < period) // UNO ~180 samples...
{
samples++;
int val = analogRead(_pin);
if (val < _min) _min = val;
if (val > _max) _max = val;
if (abs(val - _midPoint) <= (_noisemV/_mVpstep)) zeros++;
}
int p2p = (_max - _min);
uint32_t start = micros();
uint16_t period = ((freq == 60) ? 16670 : 20000);
uint16_t samples = 0;
uint16_t zeros = 0;
// automatic determine _formFactor / crest factor
float D = 0;
float FF = 0;
if (zeros > samples * 0.025)
{
D = 1.0 - (1.0 * zeros) / samples; // % SAMPLES NONE ZERO
FF = sqrt(D) * 0.5 * sqrt(2); // ASSUME NON ZERO PART ~ SINUS
}
else // # zeros is small => D --> 1 --> sqrt(D) --> 1
{
FF = 0.5 * sqrt(2);
}
_formFactor = FF;
int _min, _max;
_min = _max = analogRead(_pin);
// math could be partially precalculated: C = 1000.0 * 0.5 * _mVpstep / _mVperAmpere;
// rounding?
return 1000.0 * 0.5 * p2p * _mVpstep * _formFactor / _mVperAmpere;
while (micros() - start < period) // UNO ~180 samples...
{
samples++;
int val = analogRead(_pin);
if (val < _min) _min = val;
if (val > _max) _max = val;
if (abs(val - _midPoint) <= (_noisemV/_mVpstep)) zeros++;
}
int point2point = (_max - _min);
// automatic determine _formFactor / crest factor
float D = 0;
float FF = 0;
if (zeros > samples * 0.025)
{
D = 1.0 - (1.0 * zeros) / samples; // % SAMPLES NONE ZERO
FF = sqrt(D) * 0.5 * sqrt(2); // ASSUME NON ZERO PART ~ SINUS
}
else // # zeros is small => D --> 1 --> sqrt(D) --> 1
{
FF = 0.5 * sqrt(2);
}
_formFactor = FF;
// math could be partially precalculated: C = 1000.0 * 0.5 * _mVpstep / _mVperAmpere;
// rounding?
return 1000.0 * 0.5 * point2point * _mVpstep * _formFactor / _mVperAmpere;
}
int ACS712::mA_DC()

View File

@ -2,7 +2,7 @@
//
// FILE: ACS712.h
// AUTHOR: Rob Tillaart
// VERSION: 0.2.0
// VERSION: 0.2.1
// DATE: 2020-08-02
// PURPOSE: ACS712 library - current measurement
//
@ -11,7 +11,7 @@
#include "Arduino.h"
#define ACS712_LIB_VERSION "0.1.3"
#define ACS712_LIB_VERSION "0.2.1"
class ACS712
@ -37,7 +37,7 @@ class ACS712
int mA_DC();
// midpoint ADC for DC only
inline void setMidPoint(uint16_t mp) { _midPoint = mp; };
inline void setMidPoint(uint16_t mp) { _midPoint = mp; };
inline uint16_t getMidPoint() { return _midPoint; };
inline void incMidPoint() { _midPoint++; };
inline void decMidPoint() { _midPoint--; };
@ -58,8 +58,8 @@ class ACS712
private:
uint8_t _pin;
float _mVpstep; // millivolt per step
float _formFactor; // P2P -> RMS
float _mVpstep; // millivolt per step
float _formFactor; // point2point -> RMS
uint8_t _mVperAmpere;
uint16_t _midPoint;
uint8_t _noisemV;

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2020 Rob Tillaart
Copyright (c) 2020-2021 Rob Tillaart
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -0,0 +1,23 @@
# Syntax Coloring Map For ACS712
# Datatypes (KEYWORD1)
ACS712 KEYWORD1
# Methods and Functions (KEYWORD2)
mA_AC KEYWORD2
mA_DC KEYWORD2
setMidPoint KEYWORD2
getMidPoint KEYWORD2
incMidPoint KEYWORD2
decMidPoint KEYWORD2
setFormFactor KEYWORD2
getFormFactor KEYWORD2
setNoisemV KEYWORD2
getNoisemV KEYWORD2
setmVperAmp KEYWORD2
getmVperAmp KEYWORD2
# Constants (LITERAL1)
ACS712_LIB_VERSION LITERAL1

View File

@ -21,7 +21,7 @@
"type": "git",
"url": "https://github.com/RobTillaart/ACS712"
},
"version":"0.2.0",
"version":"0.2.1",
"frameworks": "arduino",
"platforms": "*"
}

View File

@ -1,5 +1,5 @@
name=ACS712
version=0.2.0
version=0.2.1
author=Rob Tillaart <rob.tillaart@gmail.com>, Pete Thompson <pete.thompson@yahoo.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=ACS712 library for Arduino.

View File

@ -1,6 +1,12 @@
[![Arduino CI](https://github.com/RobTillaart/ACS712/workflows/Arduino%20CI/badge.svg)](https://github.com/marketplace/actions/arduino_ci)
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/RobTillaart/ACS712/blob/master/LICENSE)
[![GitHub release](https://img.shields.io/github/release/RobTillaart/ACS712.svg?maxAge=3600)](https://github.com/RobTillaart/ACS712/releases)
# ACS712
Current Sensor - 5A, 20A, 30A
Library for the ACS712 Current Sensor - 5A, 20A, 30A
## Description
@ -9,8 +15,8 @@ analog output that provides a voltage that is lineair with the current.
The ACS712 library supports only a built in ADC by means of analogRead().
There are 2 core functions:
* **int mA_DC()**
* **int mA_AC()**
- **int mA_DC()**
- **int mA_AC()**
To measure DC current a single analogRead() with some conversion math is sufficient to get
a value. To stabilize the signal analogRead() is called twice.
@ -20,10 +26,50 @@ peak to peak value which is converted to the RMS value. To convert the peak2peak
value to RMS one need the so called crest or form factor. This factor depends heavily
on the signal form. For a perfect sinus the value is sqrt(2)/2.
## Interface
#### Base
- **ACS712(analogPin, volts = 5.0, maxADC = 1023, mVperA = 100)** constructor
- **mA_AC(freq = 50)** blocks ~21 ms to sample a whole 50 or 60 Hz period.
- **mA_DC()**
#### Midpoint
- **setMidPoint(mp)** sets midpoint ADC for DC only
- **autoMidPoint(uint8_t freq = 50)** Auto midPoint, assuming zero DC current or any AC current
- **getMidPoint()** read back setting
- **incMidPoint()** manual increase midpoint (manually)
- **decMidPoint()** manual decrease midpoint (manually)
#### Formfactor
Also known as crest factor; affects AC only.
Default = 0.5 \* sqrt(2) = ~0.70710678...
- **setFormFactor(ff)** manually sets formfactor 0.0 .. 1.0
- **getFormFactor()** returns current formFactor
#### Noise
Default = 21 mV.
- **setNoisemV(noisemV)** set noise level
- **getNoisemV()** returns set value
#### mV per Ampere
Both for AC and DC
- **setmVperAmp(mva)** sets the milliVolt per Ampere measured.
- **getmVperAmp()** returns set value.
## Test
The library is tested with the RobotDyn ACS712 20A breakout and an Arduino UNO.
## Operation
With the constructor the parameters **volts** and **maxADC (steps)** of the ADC are set
@ -41,3 +87,9 @@ To callibrate the noise level (used for AC measurements), 2 functions are availa
get and set the noise in mV.
The examples show the basic working of the functions.
## Future
- mA_AC blocks 20 ms so might affect taskscheduling on a ESP32.
This needs to be investigated.
- int point2point(uint8_t freq) function for AC. Is part of mA_AC() allready.

View File

@ -0,0 +1,141 @@
//
// FILE: unit_test_001.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// DATE: 2020-12-06
// PURPOSE: unit tests for the SHT31 temperature and humidity sensor
// https://github.com/RobTillaart/SHT31
// https://www.adafruit.com/product/2857
// https://github.com/Arduino-CI/arduino_ci/blob/master/REFERENCE.md
//
// supported assertions
// ----------------------------
// assertEqual(expected, actual)
// assertNotEqual(expected, actual)
// assertLess(expected, actual)
// assertMore(expected, actual)
// assertLessOrEqual(expected, actual)
// assertMoreOrEqual(expected, actual)
// assertTrue(actual)
// assertFalse(actual)
// assertNull(actual)
#include <ArduinoUnitTests.h>
#include "Arduino.h"
#include "ACS712.h"
#define A0 0
unittest_setup()
{
}
unittest_teardown()
{
}
unittest(test_mA_DC)
{
GodmodeState* state = GODMODE();
state->reset();
// 0 == A0
ACS712 ACS(0, 5.0, 1023, 100); // analogPin, volts, maxADC, mVperA
// assertEqual(0, ACS.mA_AC(50));
// assertEqual(0, ACS.mA_AC(60));
int future[12] = {0, 0, 100, 100, 200, 200, 511, 511, 900, 900, 1023, 1023};
state->analogPin[0].fromArray(future, 12);
assertEqual(-24975, ACS.mA_DC());
assertEqual(-20087, ACS.mA_DC());
assertEqual(-15200, ACS.mA_DC());
assertEqual(0, ACS.mA_DC());
assertEqual(19012, ACS.mA_DC());
assertEqual(25024, ACS.mA_DC());
}
unittest(test_mA_AC)
{
ACS712 ACS(A0, 5.0, 1023, 100); // analogPin, volts, maxADC, mVperA
// loop with micros and a lot of analogReads - not possible
// assertEqual(0, ACS.mA_AC(50));
// assertEqual(0, ACS.mA_AC(60));
assertEqual(1, 1);
}
unittest(test_midPoint)
{
ACS712 ACS(A0, 5.0, 1023, 100); // analogPin, volts, maxADC, mVperA
// loop with micros and a lot of analogReads - not possible
// ACS.autoMidPoint(50);
// float amp50 = ACS.getMidPoint();
// assertEqual(0, amp50);
//
// ACS.autoMidPoint(60);
// float amp60 = ACS.getMidPoint();
// assertEqual(0, amp60);
ACS.setMidPoint(1000);
float amp = ACS.getMidPoint();
assertEqual(1000, amp);
ACS.incMidPoint();
amp = ACS.getMidPoint();
assertEqual(1001, amp);
ACS.decMidPoint();
amp = ACS.getMidPoint();
assertEqual(1000, amp);
}
unittest(test_formFactor)
{
ACS712 ACS(A0, 5.0, 1023, 100);
// default 0.5 * sqrt(2)
float ff = ACS.getFormFactor();
float eff = 0.5 * sqrt(2);
float delta = abs(eff - ff);
assertMoreOrEqual(0.0001, delta);
ACS.setFormFactor(0.8);
ff = ACS.getFormFactor();
eff = 0.8;
delta = abs(eff - ff);
assertMoreOrEqual(0.0001, delta);
}
unittest(test_Noise)
{
ACS712 ACS(A0, 5.0, 1023, 100);
int nmv = ACS.getNoisemV();
assertEqual(21, nmv); // default value..
ACS.setNoisemV(100);
nmv = ACS.getNoisemV();
assertEqual(100, nmv);
}
unittest(test_mVperAmp)
{
ACS712 ACS(A0, 5.0, 1023, 100);
int mpa = ACS.getmVperAmp();
assertEqual(100, mpa); // default value..
ACS.setmVperAmp(50);
mpa = ACS.getmVperAmp();
assertEqual(50, mpa);
}
unittest_main()
// --------

View File

@ -0,0 +1,7 @@
compile:
# Choosing to run compilation tests on 2 different Arduino platforms
platforms:
- uno
- leonardo
- due
- zero

View File

@ -0,0 +1,13 @@
---
name: Arduino CI
on: [push, pull_request]
jobs:
arduino_ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: Arduino-CI/action@master
# Arduino-CI/action@v0.1.1

148
libraries/AD520X/AD520X.cpp Normal file
View File

@ -0,0 +1,148 @@
//
// FILE: AD520X.cpp
// AUTHOR: Rob Tillaart
// DATE: 2020-07-24
// VERSION: 0.1.1
// PURPOSE: Arduino library for AD5204 and AD5206 digital potentiometers (+ older AD8400, AD8402, AD8403)
// URL: https://github.com/RobTillaart/AD520X
//
// HISTORY:
// 0.0.1 2020-07-24 initial version
// 0.0.2 2020-07-25 support for AD8400 series in documentation.
// 0.1.0 2020-07-26 refactor, fix #2 select pin for HW SPI; add shutdown.
// 0.1.1 2020-12-08 Arduino-CI + unit test + isPowerOn()
#include "AD520X.h"
AD520X::AD520X(uint8_t select, uint8_t reset, uint8_t shutdown, uint8_t dataOut, uint8_t clock)
{
_pmCount = 6;
_select = select;
_data = dataOut;
_clock = clock;
_reset = reset;
_shutdown = shutdown;
_hwSPI = (dataOut == 255) && (clock == 255);
}
// initializes the pins and starts SPI in case of hardware SPI
void AD520X::begin(uint8_t value)
{
pinMode(_select, OUTPUT);
digitalWrite(_select, HIGH);
pinMode(_reset, OUTPUT);
digitalWrite(_reset, LOW);
pinMode(_shutdown, OUTPUT);
digitalWrite(_shutdown, LOW);
if(_hwSPI)
{
SPI.begin();
delay(1);
}
else
{
pinMode(_data, OUTPUT);
pinMode(_clock, OUTPUT);
digitalWrite(_data, LOW);
digitalWrite(_clock, LOW);
}
setAll(value);
}
void AD520X::setValue(uint8_t pm, uint8_t value)
{
if (pm >= _pmCount) return;
_value[pm] = value;
updateDevice(pm);
}
void AD520X::setAll(uint8_t value)
{
for (uint8_t pm = 0; pm < _pmCount; pm++)
{
setValue(pm, value);
}
}
uint8_t AD520X::getValue(uint8_t pm)
{
if (pm >= _pmCount) return 0;
return _value[pm];
}
void AD520X::reset(uint8_t value)
{
digitalWrite(_reset, HIGH);
digitalWrite(_reset, LOW);
setAll(value);
}
void AD520X::updateDevice(uint8_t pm)
{
if (_hwSPI)
{
SPI.beginTransaction(SPISettings(16000000, MSBFIRST, SPI_MODE1));
digitalWrite(_select, LOW);
SPI.transfer(pm);
SPI.transfer(_value[pm]);
digitalWrite(_select, HIGH);
SPI.endTransaction();
}
else // Software SPI
{
digitalWrite(_select, LOW);
swSPI_transfer(pm);
swSPI_transfer(_value[pm]);
digitalWrite(_select, HIGH);
}
}
// simple one mode version
void AD520X::swSPI_transfer(uint8_t value)
{
for (uint8_t mask = 0x80; mask; mask >>= 1)
{
digitalWrite(_data,(value & mask) != 0);
digitalWrite(_clock, HIGH);
digitalWrite(_clock, LOW);
}
}
/////////////////////////////////////////////////////////////////////////////
AD5206::AD5206(uint8_t select, uint8_t reset, uint8_t shutdown, uint8_t dataOut, uint8_t clock)
: AD520X(select, reset, shutdown, dataOut, clock)
{
_pmCount = 6;
}
AD5204::AD5204(uint8_t select, uint8_t reset, uint8_t shutdown, uint8_t dataOut, uint8_t clock)
: AD520X(select, reset, shutdown, dataOut, clock)
{
_pmCount = 4;
}
AD8403::AD8403(uint8_t select, uint8_t reset, uint8_t shutdown, uint8_t dataOut, uint8_t clock)
: AD520X(select, reset, shutdown, dataOut, clock)
{
_pmCount = 4;
}
AD8402::AD8402(uint8_t select, uint8_t reset, uint8_t shutdown, uint8_t dataOut, uint8_t clock)
: AD520X(select, reset, shutdown, dataOut, clock)
{
_pmCount = 2;
}
AD8400::AD8400(uint8_t select, uint8_t reset, uint8_t shutdown, uint8_t dataOut, uint8_t clock)
: AD520X(select, reset, shutdown, dataOut, clock)
{
_pmCount = 1;
}
// -- END OF FILE --

85
libraries/AD520X/AD520X.h Normal file
View File

@ -0,0 +1,85 @@
#pragma once
//
// FILE: AD520X.h
// AUTHOR: Rob Tillaart
// DATE: 2020-07-24
// VERSION: 0.1.1
// PURPOSE: Arduino library for AD5204 and AD5206 digital potentiometers (+ older AD8400, AD8402, AD8403)
// URL: https://github.com/RobTillaart/AD520X
//
// HISTORY:
// see AD520X.cpp file
//
#include "Arduino.h"
#include "SPI.h"
#define AD520X_LIB_VERSION "0.1.1"
class AD520X
{
public:
AD520X(uint8_t select, uint8_t reset, uint8_t shutdown, uint8_t dataOut, uint8_t clock);
void begin(uint8_t value = 128);
void setValue(uint8_t pm, uint8_t value);
void setAll(uint8_t value);
uint8_t getValue(uint8_t pm);
void reset(uint8_t value = 128);
int pmCount() { return _pmCount; };
void powerOn() { digitalWrite(_shutdown, LOW); };
void powerOff() { digitalWrite(_shutdown, HIGH); };
void powerDown() { powerOff(); }; // will become obsolete
bool isPowerOn() { return digitalRead(_shutdown) == LOW; };
protected:
uint8_t _data;
uint8_t _clock;
uint8_t _select;
uint8_t _reset;
uint8_t _shutdown;
bool _hwSPI = 3;
uint8_t _value[6];
uint8_t _pmCount = 6;
void updateDevice(uint8_t pm);
void swSPI_transfer(uint8_t value);
};
/////////////////////////////////////////////////////////////////////////////
class AD5206 : public AD520X
{
public:
AD5206(uint8_t select, uint8_t reset, uint8_t shutdown, uint8_t dataOut = 255, uint8_t clock = 255);
};
class AD5204 : public AD520X
{
public:
AD5204(uint8_t select, uint8_t reset, uint8_t shutdown, uint8_t dataOut = 255, uint8_t clock = 255);
};
class AD8400 : public AD520X
{
public:
AD8400(uint8_t select, uint8_t reset, uint8_t shutdown, uint8_t dataOut = 255, uint8_t clock = 255);
};
class AD8402 : public AD520X
{
public:
AD8402(uint8_t select, uint8_t reset, uint8_t shutdown, uint8_t dataOut = 255, uint8_t clock = 255);
};
class AD8403 : public AD520X
{
public:
AD8403(uint8_t select, uint8_t reset, uint8_t shutdown, uint8_t dataOut = 255, uint8_t clock = 255);
};
// -- END OF FILE --

21
libraries/AD520X/LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020-2021 Rob Tillaart
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,74 @@
[![Arduino CI](https://github.com/RobTillaart/AD520X/workflows/Arduino%20CI/badge.svg)](https://github.com/marketplace/actions/arduino_ci)
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/RobTillaart/AD520X/blob/master/LICENSE)
[![GitHub release](https://img.shields.io/github/release/RobTillaart/AD520X.svg?maxAge=3600)](https://github.com/RobTillaart/AD520X/releases)
# AD520X
Arduino library for SPI AD5204 and AD5206 digital potentiometers
## Description
The library is still experimental as not all functionality is tested (enough).
The **AD5204** (4 pm) and **AD5206** (6 pm) are SPI based digital potentiometers.
This library consists of a base class **AD520X** that does the work.
The interface is straightforward, one can set a value per pm between 0..255.
_Although not tested this library should work for the older **AD8400** (1 pm),
the **AD8402** (2 pm) and **AD8403** (4 pm) as the interface is very similar
(datasheet comparison). If you can confirm it works, please let me know._
## Interface
### Constructors
- **AD520X(select, reset, shutdown, dataOut = 255, clock = 255)** constructor
Base class, not to be used directly.
If dataOut and clock are set to 255 (default) it uses hardware SPI.
If dataOut and clock are set to another (valid) value) it uses software SPI.
reset and shutdown may be set to 255 too, which effectively disables them.
Note: hardware SPI is 10+ times faster on an UNO.
- **AD5204(select, reset, shutdown, dataOut = 255, clock = 255)** uses 4 pm.
- **AD5206(select, reset, shutdown, dataOut = 255, clock = 255)** uses 6 pm.
- **AD8400(select, reset, shutdown, dataOut = 255, clock = 255)** uses 1 pm.
- **AD8402(select, reset, shutdown, dataOut = 255, clock = 255)** uses 2 pm.
- **AD8403(select, reset, shutdown, dataOut = 255, clock = 255)** uses 4 pm.
### Base
- **begin(value = 128)** value is the initial value of all potentiometer.
- **setValue(pm, value)** set a potentiometer to a value
- **setAll(value)** set all potentiometers to the same value e.g. 0 or max or mid
- **getValue(pm)** returns the last set value of a specific potmeter
- **reset(value = 128)** resets all potentiometers to value, default 128.
### Misc
- **pmCount()** returns the number of internal potmeters.
- **powerOn()** switches the module on
- **powerOff()** switches the module off
- **powerDown()** use powerOFf() instead
- **isPowerOn()** returns true if on (default) or
## Future
- **setInvert(pm)** invert flag per potmeter
- 0..255 -> 255..0
- 1 uint8_t can hold 8 flags
- **getInvert(pm)**
- **follow(pm_B, pm_A, percentage = 100)**
- makes pm_B follow pm_A unless pm_B is addressed explicitly
- array cascade = 0xFF or pm_A.
- It will follow pm_A for certain percentage default 100.
- **setPercentage(pm, float value)** 0..100%
- **getPercentage(pm)**
- logarithmic effect? setGamma(pm, value);
easier with setPercentage()
## Operations
See examples.

View File

@ -0,0 +1,114 @@
//
// FILE: AD5204_demo.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: demo
// DATE: 2020-07-24
// URL: https://github.com/RobTillaart/AD520X
#include "AD520X.h"
uint32_t start, stop;
#define TWOPI (3.14159265 * 2)
// select, reset, shutdown, data, clock
// AD5204 pot(10, 255, 255, 8, 9); // SW SPI
AD5204 pot = AD5204(10, 12, 13); // HW SPI
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
pot.begin(4);
test_sinus();
test_sawtooth();
test_timing();
Serial.println("\nDone...");
}
void loop()
{
}
// connect all A GND and B 5V
// every W will have a different signal (same freq).
void test_sinus()
{
Serial.println(__FUNCTION__);
delay(10);
start = millis();
uint32_t i = 0;
while (millis() - start < 10000)
{
uint8_t value = 127 * sin(i * TWOPI / 100);
pot.setValue(0, 128 + value);
pot.setValue(1, 128 + value / 2);
pot.setValue(2, 64 + value / 2);
pot.setValue(3, 192 + value / 2);
pot.setValue(4, 224 + value / 4);
pot.setValue(5, 128 - value);
i++;
}
}
// straightforward sawtooth.
void test_sawtooth()
{
Serial.println(__FUNCTION__);
delay(10);
start = millis();
uint8_t i = 0;
while (millis() - start < 10000)
{
pot.setValue(0, i++); // autowrap is fast...
}
}
void test_timing()
{
Serial.println(__FUNCTION__);
delay(10);
start = micros();
for (int i = 0; i < 10000; i++)
{
pot.setValue(0, i++); // autowrap is fast...
}
stop = micros();
Serial.print("10000 x setValue():\t");
Serial.println(stop - start);
delay(10);
start = micros();
for (int i = 0; i < 1000; i++)
{
pot.setAll(i++);
}
stop = micros();
Serial.print("1000 x setAll():\t");
Serial.println(stop - start);
delay(10);
volatile int x = 0;
start = micros();
for (int i = 0; i < 250; i++)
{
x += pot.getValue(0);
x += pot.getValue(1);
x += pot.getValue(2);
x += pot.getValue(3);
}
stop = micros();
Serial.print("1000 x getValue():\t");
Serial.println(stop - start);
delay(10);
}
// -- END OF FILE --

View File

@ -0,0 +1,24 @@
# Syntax Coloring Map For AD520X
# Datatypes (KEYWORD1)
AD520X KEYWORD1
AD5204 KEYWORD1
AD5206 KEYWORD1
AD8400 KEYWORD1
AD8402 KEYWORD1
AD8403 KEYWORD1
# Methods and Functions (KEYWORD2)
begin KEYWORD2
setValue KEYWORD2
setAll KEYWORD2
getValue KEYWORD2
reset KEYWORD2
powerOn KEYWORD2
powerOff KEYWORD2
powerDown KEYWORD2
isPowerOn KEYWORD2
pmCount KEYWORD2
# Constants (LITERAL1)
AD520X_LIB_VERSION LITERAL1

View File

@ -0,0 +1,21 @@
{
"name": "AD520X",
"keywords": "AD520X, SPI, AD5204, AD5206, AD8400, AD8402, AD8403, potentiometers",
"description": "Arduino library for SPI AD5204 and AD5206 digital potentiometers",
"authors":
[
{
"name": "Rob Tillaart",
"email": "Rob.Tillaart@gmail.com",
"maintainer": true
}
],
"repository":
{
"type": "git",
"url": "https://github.com/RobTillaart/AD520X.git"
},
"version":"0.1.1",
"frameworks": "arduino",
"platforms": "*"
}

View File

@ -0,0 +1,11 @@
name=AD520X
version=0.1.1
author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=Arduino library for SPI AD5204 and AD5206 digital potentiometers
paragraph=Not tested but should work for AD8400, AD8402 and AD8403.
category=Sensors
url=https://github.com/RobTillaart/AD520X
architectures=*
includes=AD520X.h
depends=

View File

@ -0,0 +1,126 @@
//
// FILE: unit_test_001.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// DATE: 2020-12-08
// PURPOSE: unit tests for the AD520X digital potentiometers
// https://github.com/RobTillaart/AD520X
// https://github.com/Arduino-CI/arduino_ci/blob/master/REFERENCE.md
//
// supported assertions
// ----------------------------
// assertEqual(expected, actual)
// assertNotEqual(expected, actual)
// assertLess(expected, actual)
// assertMore(expected, actual)
// assertLessOrEqual(expected, actual)
// assertMoreOrEqual(expected, actual)
// assertTrue(actual)
// assertFalse(actual)
// assertNull(actual)
#include <ArduinoUnitTests.h>
#include "Arduino.h"
#include "AD520X.h"
unittest_setup()
{
}
unittest_teardown()
{
}
unittest(test_begin)
{
AD5204 pot = AD5204(10, 12, 13); // HW SPI
pot.begin();
assertEqual(128, pot.getValue(0));
pot.begin(42);
assertEqual(42, pot.getValue(0));
}
unittest(test_setValue)
{
AD5206 pot = AD5206(10, 12, 13); // HW SPI
pot.begin();
assertEqual(128, pot.getValue(0));
for (int i = 0; i < pot.pmCount(); i++)
{
pot.setValue(i, 35);
assertEqual(35, pot.getValue(i));
}
pot.setAll(42);
for (int i = 0; i < pot.pmCount(); i++)
{
assertEqual(42, pot.getValue(i));
}
}
unittest(test_reset)
{
AD5204 pot = AD5204(10, 12, 13); // HW SPI
pot.begin();
assertEqual(128, pot.getValue(0));
pot.reset(35);
assertEqual(35, pot.getValue(0));
}
unittest(test_power)
{
AD5204 pot = AD5204(10, 12, 13); // HW SPI
pot.begin(213);
assertEqual(213, pot.getValue(0));
assertTrue(pot.isPowerOn());
pot.powerOff();
assertFalse(pot.isPowerOn());
delay(100);
pot.powerOn();
assertTrue(pot.isPowerOn());
delay(100);
for (int i = 0; i < pot.pmCount(); i++)
{
assertEqual(213, pot.getValue(i));
}
}
unittest(test_pm_count)
{
AD5204 pot1 = AD5204(10, 12, 13); // HW SPI
pot1.begin();
assertEqual(4, pot1.pmCount());
AD5206 pot2 = AD5206(10, 12, 13); // HW SPI
pot2.begin();
assertEqual(6, pot2.pmCount());
AD8403 pot3 = AD8403(10, 12, 13); // HW SPI
pot3.begin();
assertEqual(4, pot3.pmCount());
AD8402 pot4 = AD8402(10, 12, 13); // HW SPI
pot4.begin();
assertEqual(2, pot4.pmCount());
AD8400 pot5 = AD8400(10, 12, 13); // HW SPI
pot5.begin();
assertEqual(1, pot5.pmCount());
}
unittest_main()
// --------

View File

@ -0,0 +1,7 @@
compile:
# Choosing to run compilation tests on 2 different Arduino platforms
platforms:
- uno
- leonardo
- due
- zero

View File

@ -0,0 +1,13 @@
---
name: Arduino CI
on: [push, pull_request]
jobs:
arduino_ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: Arduino-CI/action@master
# Arduino-CI/action@v0.1.1

View File

@ -1,7 +1,7 @@
//
// FILE: AD524X.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.2.1
// VERSION: 0.3.1
// PURPOSE: I2C digital potentiometer AD5241 AD5242
// DATE: 2013-10-12
// URL: https://github.com/RobTillaart/AD524X
@ -16,98 +16,143 @@
#define AS524X_O1_HIGH 0x10
#define AS524X_O2_HIGH 0x08
AD524X::AD524X(const uint8_t address)
AD524X::AD524X(const uint8_t address, TwoWire *wire)
{
// address: 0x01011xx = 0x2C - 0x2F
_address = address;
_lastValue[0] = _lastValue[1] = 127; // power on reset => mid position
_O1 = _O2 = 0;
// address: 0x01011xx = 0x2C - 0x2F
_address = address;
_wire = wire;
_lastValue[0] = _lastValue[1] = 127; // power on reset => mid position
_O1 = _O2 = 0;
_pmCount = 2;
}
#if defined (ESP8266) || defined(ESP32)
bool AD524X::begin(uint8_t dataPin, uint8_t clockPin)
{
_wire = &Wire;
if ((dataPin < 255) && (clockPin < 255))
{
_wire->begin(dataPin, clockPin);
} else {
_wire->begin();
}
if (! isConnected()) return false;
reset();
return true;
}
#endif
bool AD524X::begin()
{
_wire->begin();
if (! isConnected()) return false;
reset();
return true;
}
bool AD524X::isConnected()
{
_wire->beginTransmission(_address);
return ( _wire->endTransmission() == 0);
}
uint8_t AD524X::reset()
{
write(0, 127, LOW, LOW);
return write(1, 127);
}
uint8_t AD524X::zeroAll()
{
write(0, 0, LOW, LOW);
return write(1, 0);
write(0, 0, LOW, LOW);
return write(1, 0);
}
uint8_t AD524X::write(const uint8_t rdac, const uint8_t value)
{
if (rdac > 1) return AS524X_ERROR;
if (rdac >= _pmCount) return AS524X_ERROR;
uint8_t cmd = (rdac == 0) ? AS524X_RDAC0 : AS524X_RDAC1;
// apply the output lines
cmd = cmd | _O1 | _O2;
_lastValue[rdac] = value;
return send(cmd, value);
uint8_t cmd = (rdac == 0) ? AS524X_RDAC0 : AS524X_RDAC1;
// apply the output lines
cmd = cmd | _O1 | _O2;
_lastValue[rdac] = value;
return send(cmd, value);
}
uint8_t AD524X::write(const uint8_t rdac, const uint8_t value, const uint8_t O1, const uint8_t O2)
{
if (rdac > 1) return AS524X_ERROR;
if (rdac >= _pmCount) return AS524X_ERROR;
uint8_t cmd = (rdac == 0) ? AS524X_RDAC0 : AS524X_RDAC1;
_O1 = (O1 == LOW) ? 0 : AS524X_O1_HIGH;
_O2 = (O2 == LOW) ? 0 : AS524X_O2_HIGH;
// apply the output lines
cmd = cmd | _O1 | _O2;
_lastValue[rdac] = value;
return send(cmd, value);
uint8_t cmd = (rdac == 0) ? AS524X_RDAC0 : AS524X_RDAC1;
_O1 = (O1 == LOW) ? 0 : AS524X_O1_HIGH;
_O2 = (O2 == LOW) ? 0 : AS524X_O2_HIGH;
// apply the output lines
cmd = cmd | _O1 | _O2;
_lastValue[rdac] = value;
return send(cmd, value);
}
uint8_t AD524X::setO1(const uint8_t value)
{
_O1 = (value == LOW) ? 0 : AS524X_O1_HIGH;
uint8_t cmd = AS524X_RDAC0 | _O1 | _O2;
return send(cmd, _lastValue[0]);
_O1 = (value == LOW) ? 0 : AS524X_O1_HIGH;
uint8_t cmd = AS524X_RDAC0 | _O1 | _O2;
return send(cmd, _lastValue[0]);
}
uint8_t AD524X::setO2(const uint8_t value)
{
_O2 = (value == LOW) ? 0: AS524X_O2_HIGH;
uint8_t cmd = AS524X_RDAC0 | _O1 | _O2;
return send(cmd, _lastValue[0]);
_O2 = (value == LOW) ? 0: AS524X_O2_HIGH;
uint8_t cmd = AS524X_RDAC0 | _O1 | _O2;
return send(cmd, _lastValue[0]);
}
uint8_t AD524X::getO1()
{
return (_O1 > 0);
return (_O1 > 0);
}
uint8_t AD524X::getO2()
{
return (_O2 > 0);
return (_O2 > 0);
}
uint8_t AD524X::read(const uint8_t rdac)
{
return _lastValue[rdac];
return _lastValue[rdac];
}
uint8_t AD524X::readBackRegister()
{
Wire.beginTransmission(_address);
Wire.endTransmission();
Wire.requestFrom(_address, (uint8_t)1);
return Wire.read();
Wire.beginTransmission(_address);
Wire.endTransmission();
Wire.requestFrom(_address, (uint8_t)1);
return Wire.read();
}
uint8_t AD524X::midScaleReset(const uint8_t rdac)
{
if (rdac > 1) return AS524X_ERROR;
if (rdac >= _pmCount) return AS524X_ERROR;
uint8_t cmd = AS524X_RESET;
if (rdac == 1) cmd |= AS524X_RDAC1;
cmd = cmd | _O1 | _O2;
_lastValue[rdac] = 127;
return send(cmd, _lastValue[rdac]);
uint8_t cmd = AS524X_RESET;
if (rdac == 1) cmd |= AS524X_RDAC1;
cmd = cmd | _O1 | _O2;
_lastValue[rdac] = 127;
return send(cmd, _lastValue[rdac]);
}
// read datasheet P.15
uint8_t AD524X::shutDown()
{
uint8_t cmd = AS524X_SHUTDOWN; // TODO TEST & VERIFY
return send(cmd, 0);
}
// TODO read datasheet
// uint8_t AD524X::shutDown()
// {
// uint8_t cmd = AS524X_SHUTDOWN;
// sendCommand(cmd, 0)
// }
//////////////////////////////////////////////////////////
//
@ -115,10 +160,23 @@ uint8_t AD524X::midScaleReset(const uint8_t rdac)
//
uint8_t AD524X::send(const uint8_t cmd, const uint8_t value)
{
Wire.beginTransmission(_address);
Wire.write(cmd);
Wire.write(value);
return Wire.endTransmission();
Wire.beginTransmission(_address);
Wire.write(cmd);
Wire.write(value);
return Wire.endTransmission();
}
/////////////////////////////////////////////////////////////////////////////
AD5241::AD5241(const uint8_t address, TwoWire *wire) : AD524X(address, wire)
{
_pmCount = 1;
};
AD5242::AD5242(const uint8_t address, TwoWire *wire) : AD524X(address, wire)
{
_pmCount = 2;
};
// -- END OF FILE --

View File

@ -2,7 +2,7 @@
//
// FILE: AD524X.h
// AUTHOR: Rob Tillaart
// VERSION: 0.2.1
// VERSION: 0.3.1
// PURPOSE: I2C digital PotentioMeter AD5241 AD5242
// DATE: 2013-10-12
// URL: https://github.com/RobTillaart/AD524X
@ -11,45 +11,73 @@
#include "Arduino.h"
#include "Wire.h"
#define AD524X_VERSION "0.2.1"
#define AS524X_OK 0
#define AS524X_ERROR 100
#define AD524X_VERSION (F("0.3.1"))
#define AS524X_OK 0
#define AS524X_ERROR 100
class AD524X
{
public:
// address
explicit AD524X(const uint8_t);
explicit AD524X(const uint8_t address, TwoWire *wire = &Wire);
uint8_t zeroAll();
// rdac
uint8_t read(const uint8_t);
// for debugging
uint8_t readBackRegister(); // returns the last value written in register.
#if defined (ESP8266) || defined(ESP32)
bool begin(uint8_t sda, uint8_t scl);
#endif
bool begin();
bool isConnected();
uint8_t reset(); // reset both channels to 127 and O1/O2 to LOW
uint8_t zeroAll(); // set both channels to 0 and O1/O2 to LOW
// rdac value
uint8_t write(const uint8_t, const uint8_t);
// rdac value O1 O2
uint8_t write(const uint8_t, const uint8_t, const uint8_t, const uint8_t);
uint8_t read(const uint8_t rdac);
uint8_t write(const uint8_t rdac, const uint8_t value);
uint8_t write(const uint8_t rdac, const uint8_t value, const uint8_t O1, const uint8_t O2);
uint8_t setO1(const uint8_t); // HIGH / LOW
uint8_t setO2(const uint8_t); // HIGH / LOW
uint8_t getO1();
uint8_t getO2();
uint8_t setO1(const uint8_t value = HIGH); // HIGH (default) / LOW
uint8_t setO2(const uint8_t value = HIGH); // HIGH (default) / LOW
uint8_t getO1();
uint8_t getO2();
// rdac
uint8_t midScaleReset(const uint8_t);
//
// uint8_t shutDown();
uint8_t midScaleReset(const uint8_t rdac);
uint8_t pmCount() { return _pmCount; };
// debugging
uint8_t readBackRegister(); // returns the last value written in register.
// experimental - to be tested - use at own risk
uint8_t shutDown(); // datasheet P15
protected:
uint8_t _pmCount = 0;
private:
uint8_t send(const uint8_t, const uint8_t); // cmd value
uint8_t send(const uint8_t, const uint8_t); // cmd value
uint8_t _address;
uint8_t _lastValue[2];
uint8_t _O1;
uint8_t _O2;
uint8_t _address;
uint8_t _lastValue[2];
uint8_t _O1;
uint8_t _O2;
TwoWire* _wire;
};
//////////////////////////////////////////////////////////////
class AD5241 : public AD524X
{
public:
AD5241(const uint8_t address, TwoWire *wire = &Wire);
};
class AD5242 : public AD524X
{
public:
AD5242(const uint8_t address, TwoWire *wire = &Wire);
};
// -- END OF FILE --

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2013 - 2020 Rob Tillaart
Copyright (c) 2013 - 2021 Rob Tillaart
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -1,6 +1,12 @@
[![Arduino CI](https://github.com/RobTillaart/AD524X/workflows/Arduino%20CI/badge.svg)](https://github.com/marketplace/actions/arduino_ci)
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/RobTillaart/AD524X/blob/master/LICENSE)
[![GitHub release](https://img.shields.io/github/release/RobTillaart/AD524X.svg?maxAge=3600)](https://github.com/RobTillaart/AD524X/releases)
# AD524X
I2C digital potentiometer AD5241 AD5242
Arduino class for I2C digital potentiometer AD5241 AD5242
## Description
@ -14,30 +20,66 @@ and can be set in 256 steps.
An important property of the devices is that they defaults
to their mid position at startup.
## I2C address
The AD524X has two address lines to configure the I2C address.
The range is: 0x01011**YX** = 0x2C - 0x2F
The AD524X has two address lines to configure the I2C address.0x2C - 0x2F
| Addr(dec)| Addr(Hex) | AD0 | AD1 |
|:----:|:------:|:----:|:----:|
| 44 | 0x2C | GND | GND |
| 45 | 0x2D | GND | +5V |
| 46 | 0x2E | +5V | GND |
| 47 | 0x2F | +5V | +5V |
## Interface
The library has a number of functions which are all quite straightforward.
One can get / set the value of (both) the potentiometer(s), and the O1 and O2 lines.
### Constructors
- **AD524X(uint8_t address, TwoWire \*wire = &Wire)** base class,
creates an instance with 2 potentiometer.
This class does not distinguish between AD5241 and AD5242.
The developer is responsible for handling this correctly.
- **AD5241(uint8_t address, TwoWire \*wire = &Wire)** create an instance with 1 potentiometer
- **AD5242(uint8_t address, TwoWire \*wire = &Wire)** create an instance with 2 potentiometer
### Wire initialization
- **bool begin(uint8_t sda, uint8_t scl)** ESP32 a.o initializing of Wire
- **bool begin()** for UNO
- **bool isConnected()** See if address set in constructor is on the bus.
### Basic IO
- **uint8_t write(rdac, value)** set channel rdac 0/1 to value 0..255
- **uint8_t write(rdac, value, O1, O2)** idem + set output lines O1 and O2 too
- **uint8_t read(rdac)** read back set value
- **uint8_t setO1(value = HIGH)** value = HIGH (default) or LOW
- **uint8_t setO2(value = HIGH)** value = HIGH (default) or LOW
- **uint8_t getO1()** read back O1 line
- **uint8_t getO2()** read back O2 line
### Misc
- **uint8_t zeroAll()** sets pm's and I/O to 0 or LOW.
- **uint8_t reset()** sets pm's to midpoint = 127 and I/O to LOW. (startup)
- **uint8_t midScaleReset(rdac)** resets one to midpoint = 127.
- **uint8_t readBackRegister()** read register back, for debugging.
### Experimental
- **uint8_t shutDown()** check datasheet, not tested yet, use at own risk.
Where **AD0 = X** and **AD1 = Y**
## Operation
The library has a number of functions which are all quite straightforward.
One can set the value of (both) the potentiometer(s), and the O1 and O2 lines.
* **uint8_t write(rdac, value);** value 0..255
* **uint8_t write(rdac, value, O1, O2);**
* **uint8_t setO1(value);** value = HIGH or LOW
* **uint8_t setO2(value);**
Also one can read the current values
* **uint8_t read(rdac);**
* **uint8_t getO1();**
* **uint8_t getO2();**
Note: the class does not distinguish between AD5241 and AD5242.
The developer is responsible for handling this correctly.
The examples show the basic working of the functions.

View File

@ -1,28 +1,24 @@
//
// FILE: AD524X_followA0.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.01
// VERSION: 0.1.2
// PURPOSE: AD524X demo program
// DATE: 2013-10-12
// URL:
//
// Released to the public domain
//
#include "AD524X.h"
#include "Wire.h"
AD524X AD01(0x2C); // AD0 & AD1 == GND
void setup()
{
Serial.begin(115200);
Serial.print("\nStart AD524X_followA0 : ");
Serial.println(__FILE__);
Serial.println(AD524X_VERSION);
Serial.println("Follow Analog A0");
Wire.begin();
TWBR = 72; // 100 KHz
Wire.setClock(400000);
}
void loop()
@ -41,4 +37,6 @@ void loop()
Serial.println(rv);
delay(100);
}
}
// -- END OF FILE --

View File

@ -0,0 +1,37 @@
//
// FILE: AD524X_isConnected.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: AD524X demo program
// DATE: 2021-01-27
// URL: https://github.com/RobTillaart/AD524X
//
#include "AD524X.h"
AD524X AD01(0x2C); // AD0 & AD1 == GND
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.println(AD524X_VERSION);
Wire.begin();
Wire.setClock(400000);
bool b = AD01.begin();
Serial.println(b?"true":"false");
Serial.println(AD01.isConnected());
Serial.println("\nDone...");
}
void loop()
{
}
// -- END OF FILE --

View File

@ -1,27 +1,24 @@
//
// FILE: AD524X_midScaleReset.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.01
// VERSION: 0.1.2
// PURPOSE: AD524X demo program
// DATE: 2013-10-12
// URL:
//
// Released to the public domain
// URL: https://github.com/RobTillaart/AD524X
//
#include "AD524X.h"
#include "Wire.h"
AD524X AD01(0x2C); // AD0 & AD1 == GND
void setup()
{
Serial.begin(115200);
Serial.print("\nStart AD524X_midScaleReset : ");
Serial.println(__FILE__);
Serial.println(AD524X_VERSION);
Wire.begin();
TWBR = 12; // 400 KHz
Wire.setClock(400000);
}
void loop()
@ -41,4 +38,6 @@ void loop()
Serial.println("reset");
AD01.midScaleReset(1);
delay(1000);
}
}
// -- END OF FILE --

View File

@ -1,27 +1,24 @@
//
// FILE: AD524X_read.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.01
// VERSION: 0.1.2
// PURPOSE: AD524X demo program
// DATE: 2013-10-12
// URL:
//
// Released to the public domain
// URL: https://github.com/RobTillaart/AD524X
//
#include "AD524X.h"
#include "Wire.h"
AD524X AD01(0x2C); // AD0 & AD1 == GND
void setup()
{
Serial.begin(115200);
Serial.print("\nStart AD524X_read : ");
Serial.println(__FILE__);
Serial.println(AD524X_VERSION);
Wire.begin();
TWBR = 12; // 400 KHz
Wire.setClock(400000);
}
void loop()
@ -37,4 +34,6 @@ void loop()
delay(100);
}
delay(1000);
}
}
// -- END OF FILE --

View File

@ -1,27 +1,24 @@
//
// FILE: AD524X_readBackRegister.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.00
// VERSION: 0.1.1
// PURPOSE: AD524X demo program
// DATE: 2016-04-10
// URL:
//
// Released to the public domain
// URL: https://github.com/RobTillaart/AD524X
//
#include "AD524X.h"
#include "Wire.h"
AD524X AD01(0x2C); // AD0 & AD1 == GND
void setup()
{
Serial.begin(115200);
Serial.print("\nStart AD524X_readBackRegister : ");
Serial.println(AD524X_VERSION);
Serial.begin(115200);
Serial.println(__FILE__);
Serial.println(AD524X_VERSION);
Wire.begin();
TWBR = 12; // 400 KHz
Wire.begin();
Wire.setClock(400000);
}
void loop()
@ -48,4 +45,6 @@ void test(uint8_t rdac, uint8_t val)
Serial.print('\t');
Serial.println(y);
delay(100);
}
}
// -- END OF FILE --

View File

@ -1,27 +1,24 @@
//
// FILE: AD524X_sawtooth.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.01
// VERSION: 0.1.2
// PURPOSE: AD524X demo program
// DATE: 2013-10-12
// URL:
//
// Released to the public domain
// URL: https://github.com/RobTillaart/AD524X
//
#include "AD524X.h"
#include "Wire.h"
AD524X AD01(0x2C); // AD0 & AD1 == GND
void setup()
{
Serial.begin(115200);
Serial.print("\nStart AD524X_sawtooth : ");
Serial.println(__FILE__);
Serial.println(AD524X_VERSION);
Wire.begin();
TWBR = 72; // 100KHz
Wire.setClock(400000);
}
void loop()
@ -32,4 +29,6 @@ void loop()
Serial.println(val);
delay(20);
}
}
}
// -- END OF FILE --

View File

@ -1,27 +1,24 @@
//
// FILE: AD524X_setO.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.01
// VERSION: 0.1.2
// PURPOSE: AD524X demo program
// DATE: 2013-10-12
// URL:
//
// Released to the public domain
// URL: https://github.com/RobTillaart/AD524X
//
#include "AD524X.h"
#include "Wire.h"
AD524X AD01(0x2C); // AD0 & AD1 == GND
void setup()
{
Serial.begin(115200);
Serial.print("\nStart AD524X_setO : ");
Serial.println(__FILE__);
Serial.println(AD524X_VERSION);
Wire.begin();
TWBR = 72; // 100 KHz
Wire.setClock(400000);
}
void loop()
@ -41,4 +38,6 @@ void loop()
Serial.print('\t');
Serial.println(AD01.getO2());
delay(x);
}
}
// -- END OF FILE --

View File

@ -1,28 +1,24 @@
//
// FILE: AD524X_write.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.01
// VERSION: 0.1.2
// PURPOSE: AD524X demo program
// DATE: 2013-10-12
// URL:
//
// Released to the public domain
// URL: https://github.com/RobTillaart/AD524X
//
#include "AD524X.h"
#include "Wire.h"
AD524X AD01(0x2C); // AD0 & AD1 == GND
void setup()
{
Serial.begin(115200);
Serial.print("\nStart AD524X_write : ");
Serial.println(__FILE__);
Serial.println(AD524X_VERSION);
Wire.begin();
TWBR = 12; // 400 KHz
Wire.setClock(400000);
}
void loop()
@ -41,4 +37,6 @@ void loop()
Serial.println(val);
delay(20);
}
}
}
// -- END OF FILE --

View File

@ -0,0 +1,44 @@
//
// FILE: AD524X_write_AD5241ino.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: AD524X demo program
// DATE: 2020-12-08
// URL: https://github.com/RobTillaart/AD524X
//
#include "AD524X.h"
AD5241 AD(0x2C); // AD0 & AD1 == GND
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.println(AD524X_VERSION);
Wire.begin();
Wire.setClock(400000);
Serial.println(AD.pmCount());
}
void loop()
{
for (int val = 0; val < 255; val++)
{
AD.write(0, val);
if (val == 200)
{
AD.write(1, val, HIGH, LOW);
}
if (val == 0)
{
AD.write(0, val, LOW, LOW);
}
Serial.println(val);
delay(20);
}
}
// -- END OF FILE --

View File

@ -0,0 +1,44 @@
//
// FILE: AD524X_write_AD5242.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: AD524X demo program
// DATE: 2020-12-08
// URL: https://github.com/RobTillaart/AD524X
//
#include "AD524X.h"
AD5242 AD(0x2C); // AD0 & AD1 == GND
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.println(AD524X_VERSION);
Wire.begin();
Wire.setClock(400000);
Serial.println(AD.pmCount());
}
void loop()
{
for (int val = 0; val < 255; val++)
{
AD.write(1, val);
if (val == 200)
{
AD.write(1, val, HIGH, LOW);
}
if (val == 0)
{
AD.write(1, val, LOW, LOW);
}
Serial.println(val);
delay(20);
}
}
// -- END OF FILE --

View File

@ -0,0 +1,28 @@
# Syntax Coloring Map For AD524X
# Datatypes (KEYWORD1)
AD524X KEYWORD1
AD5241 KEYWORD1
AD5242 KEYWORD1
# Methods and Functions (KEYWORD2)
begin KEYWORD2
reset KEYWORD2
zeroAll KEYWORD2
read KEYWORD2
write KEYWORD2
setO1 KEYWORD2
setO2 KEYWORD2
getO1 KEYWORD2
getO2 KEYWORD2
midScaleReset KEYWORD2
pmCount KEYWORD2
shutDown KEYWORD2
# Constants (LITERAL1)
AD524X_VERSION LITERAL1
AS524X_OK LITERAL1
AS524X_ERROR LITERAL1

View File

@ -1,6 +1,6 @@
{
"name": "AD524X",
"keywords": "I2C,digital,PotentioMeter,AD5241,AD5242",
"keywords": "I2C,digital,PotentioMeter, AD5241, AD5242",
"description": "Library to control digital potentiometer AD5241 AD5242",
"authors":
[
@ -15,7 +15,7 @@
"type": "git",
"url": "https://github.com/RobTillaart/AD524X"
},
"version":"0.2.1",
"version":"0.3.1",
"frameworks": "arduino",
"platforms": "*"
}

View File

@ -1,5 +1,5 @@
name=AD524X
version=0.2.1
version=0.3.1
author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=Arduino Library for AD524X

View File

@ -0,0 +1,133 @@
//
// FILE: unit_test_001.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.3.0
// DATE: 2020-12-03
// PURPOSE: unit tests for I2C digital PotentioMeter AD5241 AD5242
// https://github.com/RobTillaart/AD524X
// https://github.com/Arduino-CI/arduino_ci/blob/master/REFERENCE.md
//
// supported assertions
// ----------------------------
// assertEqual(expected, actual)
// assertNotEqual(expected, actual)
// assertLess(expected, actual)
// assertMore(expected, actual)
// assertLessOrEqual(expected, actual)
// assertMoreOrEqual(expected, actual)
// assertTrue(actual)
// assertFalse(actual)
// assertNull(actual)
#include <ArduinoUnitTests.h>
#include "AD524X.h"
unittest_setup()
{
}
unittest_teardown()
{
}
unittest(test_constructors)
{
Wire.begin();
AD524X ADx(0x2C); // AD0 & AD1 == GND
assertEqual(127, ADx.read(0));
assertEqual(127, ADx.read(1));
AD5241 AD1(0x2C);
assertEqual(127, AD1.read(0));
assertEqual(127, AD1.read(1));
AD5242 AD2(0x2C);
assertEqual(127, AD2.read(0));
assertEqual(127, AD2.read(1));
}
unittest(test_reset)
{
AD524X AD(0x2C); // AD0 & AD1 == GND
Wire.begin();
assertEqual(127, AD.read(0));
assertEqual(127, AD.read(1));
AD.zeroAll();
assertEqual(0, AD.read(0));
assertEqual(0, AD.read(1));
AD.reset();
assertEqual(127, AD.read(0));
assertEqual(127, AD.read(1));
AD.zeroAll();
assertEqual(0, AD.read(0));
assertEqual(0, AD.read(1));
AD.midScaleReset(0);
assertEqual(127, AD.read(0));
AD.midScaleReset(1);
assertEqual(127, AD.read(1));
}
unittest(test_write_read)
{
AD524X AD(0x2C); // AD0 & AD1 == GND
Wire.begin();
assertEqual(127, AD.read(0));
assertEqual(127, AD.read(1));
AD.write(0, 42);
assertEqual(42, AD.read(0));
assertEqual(127, AD.read(1));
AD.write(1, 42);
assertEqual(42, AD.read(0));
assertEqual(42, AD.read(1));
}
unittest(test_O1_O2)
{
AD524X AD(0x2C); // AD0 & AD1 == GND
Wire.begin();
assertEqual(0, AD.getO1());
assertEqual(0, AD.getO2());
AD.setO1();
assertEqual(1, AD.getO1());
assertEqual(0, AD.getO2());
AD.setO2();
assertEqual(1, AD.getO1());
assertEqual(1, AD.getO2());
AD.setO1(0);
assertEqual(0, AD.getO1());
assertEqual(1, AD.getO2());
AD.setO2(0);
assertEqual(0, AD.getO1());
assertEqual(0, AD.getO2());
AD.write(0, 0, 1, 1);
assertEqual(1, AD.getO1());
assertEqual(1, AD.getO2());
}
unittest_main()
// --------

View File

@ -0,0 +1,7 @@
compile:
# Choosing to run compilation tests on 2 different Arduino platforms
platforms:
- uno
- leonardo
- due
- zero

View File

@ -0,0 +1,13 @@
---
name: Arduino CI
on: [push, pull_request]
jobs:
arduino_ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: Arduino-CI/action@master
# Arduino-CI/action@v0.1.1

315
libraries/AD985X/AD985X.cpp Normal file
View File

@ -0,0 +1,315 @@
//
// FILE: AD985X.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.2.2
// DATE: 2019-02-08
// PURPOSE: Class for AD9850 and AD9851 function generator
//
// HISTORY:
// 0.1.0 2019-03-19 initial version
// 0.1.1 2020-12-09 add arduino-ci
// 0.1.2 2020-12-27 add setAutoMode() + offset
// 0.2.0 2020-12-28 major refactor class hierarchy + float frequency
// 0.2.1 2021-01-10 add get- and setARCCutOffFreq()
// 0.2.2 2021-01-24 add manual updating frequency
// get- setManualFQ_UD(), update()
// inverted SELECT line as preparation for multidevice.
#include "AD985X.h"
// UNO HARDWARE SPI PINS
#define SPI_CLOCK 13
#define SPI_MISO 12
#define SPI_MOSI 11
#define AD985X_POWERDOWN 0x04
////////////////////////////////////////////////////////
//
// AD9850
//
AD9850::AD9850()
{
}
void AD9850::begin(int select, int resetPin, int FQUDPin, int dataOut , int clock)
{
_select = select;
_reset = resetPin;
_fqud = FQUDPin;
pinMode(_select, OUTPUT);
pinMode(_reset, OUTPUT);
pinMode(_fqud, OUTPUT);
digitalWrite(_select, LOW); // device select = HIGH See - https://github.com/RobTillaart/AD985X/issues/13
digitalWrite(_reset, LOW);
digitalWrite(_fqud, LOW);
_useHW = true;
// SW SPI
if ((dataOut != 0) && (clock != 0))
{
_dataOut = dataOut;
_clock = clock;
pinMode(_dataOut, OUTPUT);
pinMode(_clock, OUTPUT);
digitalWrite(_dataOut, LOW);
digitalWrite(_clock, LOW);
_useHW = false;
}
if (_useHW)
{
SPI.begin(); // set MOSI & MISO pin right.
}
reset();
}
void AD9850::reset()
{
pulsePin(_reset);
if (_useHW) pulsePin(SPI_CLOCK);
else pulsePin(_clock);
_config = 0; // 0 phase no powerdown
_freq = 0;
_factor = 0;
_offset = 0;
_autoUpdate = true;
writeData();
}
void AD9850::powerDown()
{
_config |= AD985X_POWERDOWN; // keep phase and REFCLK as is.
writeData();
}
void AD9850::powerUp()
{
_config &= ~AD985X_POWERDOWN;
writeData();
}
void AD9850::setPhase(uint8_t phase)
{
if (phase > 31) return;
_config &= 0x07;
_config |= (phase << 3);
writeData();
}
void AD9850::pulsePin(uint8_t pin)
{
digitalWrite(pin, HIGH);
digitalWrite(pin, LOW);
}
void AD9850::writeData()
{
// Serial.println(_factor, HEX);
// Serial.println(_config, HEX);
uint32_t data = _factor;
// used for multidevice config only - https://github.com/RobTillaart/AD985X/issues/13
digitalWrite(_select, HIGH);
if (_useHW)
{
SPI.beginTransaction(SPISettings(2000000, LSBFIRST, SPI_MODE0));
SPI.transfer(data & 0xFF);
data >>= 8;
SPI.transfer(data & 0xFF);
data >>= 8;
SPI.transfer(data & 0xFF);
SPI.transfer(data >> 8);
SPI.transfer(_config & 0xFD); // mask factory test bit
SPI.endTransaction();
}
else
{
swSPI_transfer(data & 0xFF);
data >>= 8;
swSPI_transfer(data & 0xFF);
data >>= 8;
swSPI_transfer(data & 0xFF);
swSPI_transfer(data >> 8);
swSPI_transfer(_config & 0xFD); // mask factory test bit
}
digitalWrite(_select, LOW);
// update frequency + phase + control bits.
// should at least be 4 ns delay - P14 datasheet
if (_autoUpdate) update();
}
// simple one mode version
void AD9850::swSPI_transfer(uint8_t value)
{
// for (uint8_t mask = 0x80; mask; mask >>= 1) // MSBFIRST
for (uint8_t mask = 0x01; mask; mask <<= 1) // LSBFIRST
{
digitalWrite(_dataOut,(value & mask) != 0);
digitalWrite(_clock, HIGH);
digitalWrite(_clock, LOW);
}
}
void AD9850::setFrequency(uint32_t freq)
{
// freq OUT = (Δ Phase × CLKIN)/2^32
// 64 bit math to keep precision to the max
if (freq > AD9850_MAX_FREQ) freq = AD9850_MAX_FREQ;
// _factor = round(freq * 34.359738368); // 4294967296 / 125000000
_factor = (147573952589ULL * freq) >> 32;
_freq = freq;
_factor += _offset;
writeData();
}
// especially for lower frequencies (with decimals)
void AD9850::setFrequencyF(float freq)
{
// freq OUT = (Δ Phase × CLKIN)/2^32
// 64 bit math to keep precision to the max
if (freq > AD9850_MAX_FREQ) freq = AD9850_MAX_FREQ;
_factor = round(freq * 34.359738368); // 4294967296 / 125000000
_freq = freq;
_factor += _offset;
writeData();
}
void AD9850::update()
{
digitalWrite(_select, HIGH);
pulsePin(_fqud);
digitalWrite(_select, LOW);
}
////////////////////////////////////////////////////////
//
// AD9851
//
#define AD9851_REFCLK 0x01 // bit is a 6x multiplier bit P.14 datasheet
void AD9851::setFrequency(uint32_t freq)
{
// PREVENT OVERFLOW
if (freq > AD9851_MAX_FREQ) freq = AD9851_MAX_FREQ;
// AUTO SWITCH REFERENCE FREQUENCY
if (_autoRefClock)
{
if (freq > _ARCCutOffFreq)
{
_config |= AD9851_REFCLK;
}
else
{
_config &= ~AD9851_REFCLK;
}
}
if (_config & AD9851_REFCLK) // 6x 30 = 180 MHz
{
_factor = (102481911520ULL * freq) >> 32; // (1 << 64) / 180000000
}
else // 1x 30 = 30 MHz
{
_factor = (614891469123ULL * freq) >> 32; // (1 << 64) / 30000000
}
_freq = freq;
_factor += _offset;
writeData();
}
// especially for lower frequencies (with decimals)
void AD9851::setFrequencyF(float freq)
{
// PREVENT OVERFLOW
if (freq > AD9851_MAX_FREQ) freq = AD9851_MAX_FREQ;
// AUTO SWITCH REFERENCE FREQUENCY
if (_autoRefClock)
{
if (freq > _ARCCutOffFreq)
{
_config |= AD9851_REFCLK;
}
else
{
_config &= ~AD9851_REFCLK;
}
}
if (_config & AD9851_REFCLK) // 6x 30 = 180 MHz
{
_factor = uint64_t(102481911520ULL * freq) >> 32; // (1 << 64) / 180000000
}
else // 1x 30 = 30 MHz
{
_factor = (6148914691ULL * uint64_t (100 * freq)) >> 32;
}
_freq = freq;
_factor += _offset;
writeData();
}
////////////////////////////////////////////////////////
//
// AD9851 - AUTO REFERENCE CLOCK
//
void AD9851::setAutoRefClock(bool arc)
{
_autoRefClock = arc;
setFrequency(_freq);
};
void AD9851::setRefClockHigh()
{
_config |= AD9851_REFCLK;
setFrequency(_freq);
}
void AD9851::setRefClockLow()
{
_config &= ~AD9851_REFCLK;
setFrequency(_freq);
}
uint8_t AD9851::getRefClock()
{
return (_config & AD9851_REFCLK) ? 180 : 30;
}
void AD9851::setARCCutOffFreq(uint32_t Hz)
{
if (Hz > 30000000UL) Hz = 30000000;
_ARCCutOffFreq = Hz;
};
// -- END OF FILE --

101
libraries/AD985X/AD985X.h Normal file
View File

@ -0,0 +1,101 @@
#pragma once
//
// FILE: AD985X.h
// AUTHOR: Rob Tillaart
// VERSION: 0.2.2
// DATE: 2019-02-08
// PURPOSE: Class for AD9850 and AD9851 function generator
//
// URL: https://github.com/RobTillaart/AD985X
//
#include "Arduino.h"
#include "SPI.h"
#define AD985X_LIB_VERSION (F("0.2.2"))
#define AD9850_MAX_FREQ (40UL * 1000UL * 1000UL)
#define AD9851_MAX_FREQ (70UL * 1000UL * 1000UL)
class AD9850
{
public:
AD9850();
// for HW SPI only use lower 3 parameters.
void begin(int select, int resetPin, int FQUDPin, int dataOut = 0, int clock = 0);
void reset();
void powerDown();
void powerUp();
void setFrequency(uint32_t freq); // 0..AD9850_MAX_FREQ
void setFrequencyF(float freq); // works best for lower frequencies.
float getFrequency() { return _freq; };
uint32_t getMaxFrequency() { return AD9850_MAX_FREQ; };
// 0 .. 31 steps of 11.25 degrees
void setPhase(uint8_t phase = 0);
uint8_t getPhase() { return (_config >> 3); };
// offset to calibrate the frequency (internal counter)
// offset must be stored by the user.
void setCalibration(int32_t offset = 0) { _offset = offset; };
int32_t getCalibration() { return _offset; };
// internal chip factor used for frequency. (debugging only)
uint32_t getFactor() { return _factor; };
// autoUpdate is default true;
void setAutoUpdate(bool update) { _autoUpdate = update; };
bool getAutoUpdate() { return _autoUpdate; };
void update();
protected:
void pulsePin(uint8_t pin);
void writeData();
void swSPI_transfer(uint8_t value);
bool _useHW = true;
uint8_t _dataOut = 0;
uint8_t _clock = 0;
uint8_t _select = 0;
float _freq = 1;
uint32_t _factor = 0;
uint8_t _config = 0;
uint8_t _reset = 0;
uint8_t _fqud = 0;
int32_t _offset = 0;
bool _autoUpdate = true;
};
class AD9851 : public AD9850
{
public:
void setFrequency(uint32_t freq); // 0..AD9851_MAX_FREQ
void setFrequencyF(float freq);
uint32_t getMaxFrequency() { return AD9851_MAX_FREQ; };
void setRefClockHigh(); // 180 MHz
void setRefClockLow(); // 30 MHz
uint8_t getRefClock();
void setAutoRefClock(bool arc);
bool getAutoRefClock() { return _autoRefClock; };
// 10 MHz is default, set in Hz.
// will be kept <= 30 MHz as that is the freq of LOW mode.
void setARCCutOffFreq(uint32_t Hz = 10000000UL );
uint32_t getARCCutOffFreq() { return _ARCCutOffFreq; };
protected:
bool _autoRefClock = false;
uint32_t _ARCCutOffFreq = 10000000UL;
};
// -- END OF FILE --

21
libraries/AD985X/LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 - 2021 Rob Tillaart
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

217
libraries/AD985X/README.md Normal file
View File

@ -0,0 +1,217 @@
[![Arduino CI](https://github.com/RobTillaart/AD985X/workflows/Arduino%20CI/badge.svg)](https://github.com/marketplace/actions/arduino_ci)
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/RobTillaart/AD985X/blob/master/LICENSE)
[![GitHub release](https://img.shields.io/github/release/RobTillaart/AD985X.svg?maxAge=3600)](https://github.com/RobTillaart/AD985X/releases)
# AD985X
Arduino library for AD9850 and AD9851 function generators.
## Description
Library for the AD9850 and AD9851 function generators.
The library has a AD9850 base class that implements the commonalities.
The AD9851 is derived and has its own **setFrequency()** methods.
Furthermore the AD9851 also has function to select the reference clock,
a feature the AD9850 does not have.
**Warning**
The library is not suitable for AD9852 as that is a function generator with
way more functionality.
## Connection
```
TOP VIEW
+-----------+
| X-TAL |
| L |
VCC | o o | VCC
CLK | o o | D0
PUFD | o o | D1
DATA | o o | D2
RESET | o o | D3
GND | o CCC o | D4
QOUT1 | o CCC o | D5
QOUT2 | o o | D6
ZOUT1 | o o | D7 ----- SELECT SERIAL LOW
ZOUT2 | o PP o | GND
| PP |
+-----------+
XTAL = crystal
L = LED
C = chip
P = potmeter => for duty cycle square wave
```
### Multidevice
**Warning**: this setup needs to be confirmed as it is not actually tested by me yet.
Discussed in detail here - https://github.com/RobTillaart/AD985X/issues/13
The AD985X board can be connected with a SPI bus like interface.
However there is **no Chip Select pin (CS)** so one must take other measures to control multiple AD985X devices.
#### Trivial solution
The trivial implementation is to give each device a set of unique pins.
If you have pins to spare this is the perfect solution.
#### Shared line solution
A more common SPI solution is to share the data and clock lines.
However that would typical set all AD985X devices simultaneously.
So extra hardware is needed to prevent this.
A possible solution is to put all needed lines behind an AND port that allows only
communication when the **SELECT** is HIGH.
The **DATA** line of the device is connected to the output of an **AND** port.
The inputs if the **AND** port are (a) the SPI bus **DATA** line and (b) the **SELECT** pin.
Strictly for the **DATA** this is not needed as data will only clock in if there is a **CLOCK**.
The **CLOCK** pin of the device is connected to the output of an **AND** port.
The inputs if the **AND** port are (a) the SPI bus **CLOCK** line and (b) the **SELECT** pin.
The **FQ_UD** pin of the device is connected to the output of an **AND** port.
The inputs if the **AND** port are (a) the MCU **FQ_UD** line and (b) the **SELECT** pin.
See FQ_UD note below.
The **RESET** pin of the device is connected to the output of an **AND** port.
The inputs if the **AND** port are (a) the MCU **RESET** line and (b) the **SELECT** pin.
A typical IC to use is the **74HC08** which has 4 AND ports in it.
In short this setup makes the lines 'switchable' pass through, with the **SELECT** line.
It allows to have multiple AD985X devices, and even to share the SPI bus **DATA** and **CLOCK**
lines with other SPI devices.
TODO - picture.
**Note** Other multiplexing solutions are possible of course.
### FQ_UD note
It might be possible to connect a single FQ_UD line to multiple AD985X devices directly.
The FQ_UD pulse would update the frequency and as this register is not changed, the FQ_UD
pulse might just have no changing effect. To be investigated to confirm this.
If confirmed this would change the above Shared line solution a bit.
If the FQ_UD line can be shared directly it offers a way to start / change multiple
devices at the same time.
## Interface
### Constructors
- **AD9850()** 40 MHz signal generator
- **AD9851()** 70 MHz signal generator, derived from AD9850 with some extra options.
### Common interface
- **begin(selectPin, resetPin, FQUDPin, dataOut = 0, clock = 0)**
For hardware SPI only use the first three parameters,
for SW SPI you need to define the data and clock pin too.
- selectPin = chip select. The library uses HIGH as active and LOW as not selected.
- resetPin = reset
- FQUD = Frequency UpDate Pin
- **reset()** resets the function generator.
- **powerDown()** idem
- **powerUp()** idem
- **setFrequency(uint32_t freq)** SetFrequency sets the frequency and is limited by the
MaxFrequency of the class used. For the AD9850 => 40 MHz, for the AD9851 => 70 MHz.
- Note that the quality of the signal gets less at higher frequencies.
- Note setFrequency is affected by the autoUpdateFlag.
- **setFrequencyF(float freq)** SetFrequencyF sets the frequency with a float with a maximum of **two** decimals.
- Note that a float only has a mantisse of 6-7 digits so above ~1.000.000 decimals are lost.
- Note setFrequencyF is affected by the autoUpdateFlag.
- **getMaxFrequency()** returns the maximum frequency setable. For the AD9850 this is 20 MHz.
For the AD9851 this is 70 MHz.
- **getFrequency()** returns the frequency set. As it returns a float it might loose some accuracy at higher frequencies.
- **setPhase(uint8_t phase = 0)** set the phase in units of 11.25° 0..31 allowed.
Default it sets the phase to 0.
- **getPhase()** returns the phase set, 0 by default.
### Calibration
**Warning:** use with care.
- **void setCalibrationOffset(int32_t offset = 0)** sets an offset to calibrate the frequency.
- **uint32_t getCalibrationOffset()** reads back the offset set.
- **uint32_t getFactor()** internal factor, for debugging
Note: reset() resets the offset to 0..
Note: setting the offset reduces the range of frequencies (at the ends of scale).
### Auto update / manual update
(new since 0.2.2)
**Warning:** use with care.
- **void setAutoUpdate(bool update)** sets the autoUpdate flag, default it is true.
- **bool getAutoUpdate()** reads the autoUpdate flag.
- **void update()** manually toggle the FQ_UD flag to update the frequency.
Manual updating allows one to prepare the frequency, and actually apply
it at a later moment.
The default of the flag is true, and will be reset to true by the **reset()** call.
### AD9850 specific
The AD9850 has no specific functions.
### AD9851 specific
- **setRefClockHigh()** set reference clock to 180 Mhz.
- **setRefClockLow()** set reference clock to 30 Mhz.
- **getRefClock()** returns 30 or 180.
- **setAutoRefClock(bool arc)** sets a flag so the library switches automatically
to the reference clock of 180 MHz when the frequency is set above 10 MHz and
to 30 MHz when the frequency is set to 10 MHz or lower.
The initial value is **false** == OFF for backwards compatibility.
- **getAutoRefClock()** returns true is automode is set.
- **void setARCCutOffFreq(uint32_t Hz = 10000000UL )** set cut off frequency
for the auto reference clock. max value is 30 MHz, typical 10MHz
- **uint32_t getARCCutOffFreq()** returns cut off frequency set.
- Note: the autoRefClock mode does **NOT** automatically adjust the calibration offset.
- Note: **reset()** does **NOT** reset the autoRefClock flag.
## Operation
See examples
### Operational notes
- The quality of the signal becomes less at higher frequencies.
Switch the refclock to find your optimal quality.
- If the calibration offset is not 0, it needs to be set by the user after every startup,
and after switching the reference clock.
The user is also responsible to store it e.g. in EEPROM to make it persistent.
- Experimental parts may change or removed in the future.

View File

@ -0,0 +1,93 @@
//
// FILE: AD9850_demo.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.2.0
// PURPOSE: demo
#include "AD985X.h"
AD9850 freqGen;
uint32_t freq = 0;
uint32_t prev = 0;
uint32_t maxFreq;
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("AD985X_LIB_VERSION: \t");
Serial.println(AD985X_LIB_VERSION);
help();
freqGen.begin(10, 9, 8, 7, 6);
freqGen.powerUp();
maxFreq = freqGen.getMaxFrequency();
Serial.println(maxFreq);
}
void loop()
{
if (Serial.available() > 0)
{
int c = Serial.read();
switch (c)
{
case '?' :
help();
break;
case 'R' :
freqGen.reset();
freq = freqGen.getFrequency();
break;
case 'P' :
freqGen.powerDown();
break;
case 'U' :
freqGen.powerUp();
break;
case '+' :
freq += 1;
break;
case '-' :
freq -= 1;
break;
case '*' :
freq *= 10;
break;
case '/' :
freq /= 10;
break;
}
if (freq > maxFreq) freq = maxFreq;
}
// UPDATE AD985X IF NEW VALUE
if (prev != freq)
{
prev = freq;
freqGen.setFrequency(freq);
Serial.println(freq);
}
}
void help()
{
Serial.println();
Serial.println("+ : f = f + 1");
Serial.println("- : f = f - 1");
Serial.println("* : f = f * 10");
Serial.println("/ : f = f / 10");
Serial.println("? : help");
Serial.println("R : AD9850 reset");
Serial.println("P : AD9850 power down");
Serial.println("U : AD9850 power up");
Serial.println();
}
// -- END OF FILE --

View File

@ -0,0 +1,94 @@
//
// FILE: AD9850_demo_float.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.2.0
// PURPOSE: demo
#include "AD985X.h"
AD9850 freqGen;
uint32_t freq = 0;
uint32_t prev = 0;
uint32_t maxFreq;
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("AD985X_LIB_VERSION: \t");
Serial.println(AD985X_LIB_VERSION);
help();
freqGen.begin(10, 9, 8, 7, 6);
freqGen.powerUp();
maxFreq = freqGen.getMaxFrequency();
Serial.println(maxFreq);
}
void loop()
{
if (Serial.available() > 0)
{
int c = Serial.read();
switch (c)
{
case '?' :
help();
break;
case 'R' :
freqGen.reset();
freq = freqGen.getFrequency();
break;
case 'P' :
freqGen.powerDown();
break;
case 'U' :
freqGen.powerUp();
break;
case '+' :
freq += 1;
break;
case '-' :
freq -= 1;
break;
case '*' :
freq *= 10;
break;
case '/' :
freq /= 10;
break;
}
if (freq > maxFreq) freq = maxFreq;
}
// UPDATE AD985X IF NEW VALUE
if (prev != freq)
{
prev = freq;
freqGen.setFrequencyF(freq * 0.01);
Serial.println(freq);
}
}
void help()
{
Serial.println();
Serial.println("+ : f = f + 0.01");
Serial.println("- : f = f - 0.01");
Serial.println("* : f = f * 10");
Serial.println("/ : f = f / 10");
Serial.println("? : help");
Serial.println("R : AD9850 reset");
Serial.println("P : AD9850 power down");
Serial.println("U : AD9850 power up");
Serial.println();
}
// -- END OF FILE --

View File

@ -0,0 +1,93 @@
//
// FILE: AD9851_demo.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.2.0
// PURPOSE: demo
#include "AD985X.h"
AD9851 freqGen;
uint32_t freq = 0;
uint32_t prev = 0;
uint32_t maxFreq;
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("AD985X_LIB_VERSION: \t");
Serial.println(AD985X_LIB_VERSION);
help();
freqGen.begin(10, 9, 8, 7, 6);
freqGen.powerUp();
maxFreq = freqGen.getMaxFrequency();
Serial.println(maxFreq);
}
void loop()
{
if (Serial.available() > 0)
{
int c = Serial.read();
switch(c)
{
case '?' :
help();
break;
case 'R' :
freqGen.reset();
freq = freqGen.getFrequency();
break;
case 'P' :
freqGen.powerDown();
break;
case 'U' :
freqGen.powerUp();
break;
case '+' :
freq += 1;
break;
case '-' :
freq -= 1;
break;
case '*' :
freq *= 10;
break;
case '/' :
freq /= 10;
break;
}
if (freq > maxFreq) freq = maxFreq;
}
// UPDATE AD985X IF NEW VALUE
if (prev != freq)
{
prev = freq;
freqGen.setFrequency(freq);
Serial.println(freq);
}
}
void help()
{
Serial.println();
Serial.println("+ : f = f + 1");
Serial.println("- : f = f - 1");
Serial.println("* : f = f * 10");
Serial.println("/ : f = f / 10");
Serial.println("? : help");
Serial.println("R : AD9851 reset");
Serial.println("P : AD9851 power down");
Serial.println("U : AD9851 power up");
Serial.println();
}
// -- END OF FILE --

View File

@ -0,0 +1,152 @@
//
// FILE: AD9851_demo_2.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: demo
#include "AD985X.h"
AD9851 freqGen;
uint32_t freq = 0;
uint32_t prev = 0;
uint32_t maxFreq;
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("AD985X_LIB_VERSION: \t");
Serial.println(AD985X_LIB_VERSION);
help();
freqGen.begin(10, 9, 8, 7, 6);
freqGen.powerUp();
maxFreq = freqGen.getMaxFrequency();
Serial.print(" MAX_FREQ: ");
Serial.println(maxFreq);
Serial.print("REF_CLOCK: ");
Serial.println(freqGen.getRefClock());
}
void loop()
{
if (Serial.available() > 0)
{
int c = Serial.read();
switch (c)
{
case '0'...'9' :
freq *= 10;
freq += c - '0';
break;
case '?' :
help();
break;
case 'R' :
freqGen.reset();
freq = freqGen.getFrequency();
break;
case 'P' :
freqGen.powerDown();
break;
case 'U' :
freqGen.powerUp();
break;
case 'A' :
freqGen.setAutoRefClock(true);
Serial.println("REF_CLOCK: AUTO ON");
break;
case 'C' :
freqGen.setAutoRefClock(false);
Serial.println("REF_CLOCK: AUTO OFF");
break;
case 'G' :
Serial.print("REF_CLOCK: ");
Serial.println(freqGen.getRefClock());
break;
case 'H' :
freqGen.setRefClockHigh();
Serial.print("REF_CLOCK: ");
Serial.println(freqGen.getRefClock());
break;
case 'L' :
freqGen.setRefClockLow();
Serial.print("REF_CLOCK: ");
Serial.println(freqGen.getRefClock());
break;
case '+' :
freq += 1;
break;
case '-' :
freq -= 1;
break;
case '*' :
freq *= 10;
break;
case '/' :
freq /= 10;
break;
}
if (freq > maxFreq) freq = maxFreq;
}
// UPDATE AD985X IF NEW VALUE
if (prev != freq)
{
prev = freq;
freqGen.setFrequency(freq);
freq = freqGen.getFrequency();
Serial.println(freq);
// printFreq(freq);
}
}
void help()
{
Serial.println();
Serial.println(" HELP AD985X");
Serial.println("+ : freq = freq + 1");
Serial.println("- : freq = freq - 1");
Serial.println("* : freq = freq * 10");
Serial.println("/ : freq = freq / 10");
Serial.println("? : help");
Serial.println("R : AD9851 reset");
Serial.println("P : AD9851 power down");
Serial.println("U : AD9851 power up");
Serial.println("A : AD9851 set reference clock AUTO ON");
Serial.println("C : AD9851 set reference clock AUTO OFF");
Serial.println("G : AD9851 print reference clock");
Serial.println("H : AD9851 set reference clock HIGH");
Serial.println("L : AD9851 set reference clock LOW");
Serial.println();
}
void printFreq(float freq)
{
if (freq > 1000000)
{
Serial.print(freq / 1e6, 2);
Serial.println(" MHz");
return;
}
if (freq > 1000)
{
Serial.print(freq / 1e3, 2);
Serial.println(" KHz");
return;
}
Serial.print(freq, 2);
Serial.println(" Hz");
}
// -- END OF FILE --

View File

@ -0,0 +1,90 @@
//
// FILE: AD9851_demo_float.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.2.0
// PURPOSE: demo
#include "AD985X.h"
AD9851 freqGen;
uint32_t freq = 0;
uint32_t prev = 0;
uint32_t maxFreq;
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("AD985X_LIB_VERSION: \t");
Serial.println(AD985X_LIB_VERSION);
help();
freqGen.begin(10, 9, 8, 7, 6);
freqGen.powerUp();
maxFreq = freqGen.getMaxFrequency();
Serial.println(maxFreq);
}
void loop()
{
if (Serial.available() > 0)
{
int c = Serial.read();
switch(c)
{
case '?' :
help();
break;
case 'R' :
freqGen.reset();
freq = freqGen.getFrequency();
break;
case 'P' :
freqGen.powerDown();
break;
case '+' :
freq += 1;
break;
case '-' :
freq -= 1;
break;
case '*' :
freq *= 10;
break;
case '/' :
freq /= 10;
break;
}
if (freq > maxFreq) freq = maxFreq;
}
// UPDATE AD985X IF NEW VALUE
if (prev != freq)
{
prev = freq;
freqGen.setFrequency(freq * 0.01);
Serial.println(freq);
}
}
void help()
{
Serial.println();
Serial.println("+ : f = f + 0.01");
Serial.println("- : f = f - 0.01");
Serial.println("* : f = f * 10");
Serial.println("/ : f = f / 10");
Serial.println("? : help");
Serial.println("R : reset AD985X");
Serial.println("P : power down AD985X");
Serial.println();
}
// END OF FILE

View File

@ -0,0 +1,70 @@
//
// FILE: AD9851_manual update.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: demo
#include "AD985X.h"
AD9851 freqGen;
uint32_t freq = 0;
uint32_t maxFreq = 2000000UL;
bool up = true;
uint32_t lastUpdate = 0;
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("AD985X_LIB_VERSION: \t");
Serial.println(AD985X_LIB_VERSION);
freqGen.begin(10, 9, 8, 7, 6);
freqGen.powerUp();
freqGen.setAutoRefClock(true);
freq = 100;
Serial.print("AutoUpdate: ");
Serial.println(freqGen.getAutoUpdate());
freqGen.setAutoUpdate(false);
Serial.print("AutoUpdate: ");
Serial.println(freqGen.getAutoUpdate());
}
void loop()
{
if (up == true) freq *= 1.01;
else freq /= 1.01;
if (freq > maxFreq)
{
freq = maxFreq;
up = false;
}
if (freq < 100)
{
freq = 100;
up = true;
}
freqGen.setFrequency(freq);
// only update once per second
// effectively have a random frequency
if (millis() - lastUpdate >= 1000)
{
lastUpdate = millis();
freqGen.update();
Serial.print(millis());
Serial.print('\t');
Serial.println(freq);
}
}
// -- END OF FILE --

View File

@ -0,0 +1,49 @@
//
// FILE: AD9851_six_potmeter.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: demo
#include "AD985X.h"
AD9851 freqGen;
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("AD985X_LIB_VERSION: \t");
Serial.println(AD985X_LIB_VERSION);
freqGen.begin(10, 9, 8, 7, 6);
}
void loop()
{
float freq = readFreq(true);
freqGen.setFrequencyF(freq);
Serial.println(freq);
delay(1000);
}
float readFreq(bool KHZ)
{
float freq = 0;
freq += (analogRead(A0) / 103) * 1000;
freq += (analogRead(A1) / 103) * 100;
freq += (analogRead(A2) / 103) * 10;
freq += (analogRead(A3) / 103) * 1;
freq += (analogRead(A4) / 103) * 0.1;
freq += (analogRead(A5) / 103) * 0.01;
if (KHZ) freq *= 1000;
return freq;
}
// END OF FILE

View File

@ -0,0 +1,56 @@
//
// FILE: AD9851_sweeper.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.2.0
// PURPOSE: demo
#include "AD985X.h"
AD9851 freqGen;
uint32_t freq = 0;
uint32_t maxFreq = 2000000UL;
bool up = true;
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("AD985X_LIB_VERSION: \t");
Serial.println(AD985X_LIB_VERSION);
freqGen.begin(10, 9, 8, 7, 6);
freqGen.powerUp();
freqGen.setAutoRefClock(true);
freq = 100;
}
void loop()
{
if (up == true) freq *= 1.01;
else freq /= 1.01;
if (freq > maxFreq)
{
freq = maxFreq;
up = false;
}
if (freq < 100)
{
freq = 100;
up = true;
}
freqGen.setFrequency(freq);
Serial.print(millis());
Serial.print('\t');
Serial.println(freq);
delay(50);
}
// -- END OF FILE --

View File

@ -0,0 +1,38 @@
# Syntax Coloring Map For AD985X
# Datatypes (KEYWORD1)
AD9850 KEYWORD1
AD9851 KEYWORD1
# Methods and Functions (KEYWORD2)
begin KEYWORD2
reset KEYWORD2
powerDown KEYWORD2
powerUp KEYWORD2
setFrequencyF KEYWORD2
setFrequency KEYWORD2
getFrequency KEYWORD2
setPhase KEYWORD2
getPhase KEYWORD2
setRefClockHigh KEYWORD2
setRefClockLow KEYWORD2
getRefClock KEYWORD2
setAutoRefClock KEYWORD2
getAutoRefClock KEYWORD2
setARCCutOffFreq KEYWORD2
getARCCutOffFreq KEYWORD2
setCalibration KEYWORD2
getCalibration KEYWORD2
getFactor KEYWORD2
setAutoUpdate KEYWORD2
getAutoUpdate KEYWORD2
update KEYWORD2
# Constants (LITERAL1)
AD9850_MAX_FREQ LITERAL1
AD9851_MAX_FREQ LITERAL1

View File

@ -0,0 +1,21 @@
{
"name": "AD985X",
"keywords": "AD9850 AD9851",
"description": "Arduino library for AD9850 and AD9851 function generators.",
"authors":
[
{
"name": "Rob Tillaart",
"email": "Rob.Tillaart@gmail.com",
"maintainer": true
}
],
"repository":
{
"type": "git",
"url": "https://github.com/RobTillaart/AD985X.git"
},
"version":"0.2.2",
"frameworks": "arduino",
"platforms": "*"
}

View File

@ -0,0 +1,11 @@
name=AD985X
version=0.2.2
author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=Arduino library for AD9850 and AD9851 function generators. Supports both hardware SPI as software SPI.
paragraph=Will not work for the AD9852.
category=Signal Input/Output
url=https://github.com/RobTillaart/AD985X
architectures=*
includes=AD985X.h
depends=

View File

@ -0,0 +1,210 @@
//
// FILE: unit_test_001.cpp
// AUTHOR: Rob Tillaart
// DATE: 2020-12-03
// PURPOSE: unit tests for the AD9850 and AD9851 function generator
// https://github.com/RobTillaart/AD985X
// https://github.com/Arduino-CI/arduino_ci/blob/master/REFERENCE.md
//
// supported assertions
// ----------------------------
// assertEqual(expected, actual)
// assertNotEqual(expected, actual)
// assertLess(expected, actual)
// assertMore(expected, actual)
// assertLessOrEqual(expected, actual)
// assertMoreOrEqual(expected, actual)
// assertTrue(actual)
// assertFalse(actual)
// assertNull(actual)
#include <ArduinoUnitTests.h>
#include "Arduino.h"
#include "AD985X.h"
unittest_setup()
{
}
unittest_teardown()
{
}
unittest(test_constructor)
{
fprintf(stderr, "VERSION: %s\n", AD985X_LIB_VERSION);
AD9850 funcgen0;
AD9851 funcgen1;
assertEqual(AD9850_MAX_FREQ, funcgen0.getMaxFrequency());
assertEqual(AD9851_MAX_FREQ, funcgen1.getMaxFrequency());
}
unittest(test_auto_update)
{
fprintf(stderr, "VERSION: %s\n", AD985X_LIB_VERSION);
AD9850 funcgen0;
AD9851 funcgen1;
funcgen0.begin(4, 5, 6);
funcgen1.begin(10, 11, 16);
assertTrue(funcgen0.getAutoUpdate());
assertTrue(funcgen1.getAutoUpdate());
funcgen0.setAutoUpdate(false);
funcgen1.setAutoUpdate(false);
assertFalse(funcgen0.getAutoUpdate());
assertFalse(funcgen1.getAutoUpdate());
funcgen0.setAutoUpdate(true);
funcgen1.setAutoUpdate(true);
assertTrue(funcgen0.getAutoUpdate());
assertTrue(funcgen1.getAutoUpdate());
}
unittest(test_ad9850)
{
AD9850 funcgen;
funcgen.begin(4, 5, 6);
funcgen.setFrequency(1000);
long freq = funcgen.getFrequency();
assertEqual(1000, freq);
for (int ph = 0; ph < 32; ph += 4)
{
funcgen.setPhase(ph);
int phase = funcgen.getPhase();
assertEqual(ph, phase);
}
}
unittest(test_ad9851)
{
AD9851 funcgen;
funcgen.begin(4, 5, 6);
funcgen.setFrequency(1000);
long freq = funcgen.getFrequency();
assertEqual(1000, freq);
for (int ph = 0; ph < 32; ph += 4)
{
funcgen.setPhase(ph);
int phase = funcgen.getPhase();
assertEqual(ph, phase);
}
funcgen.setRefClockHigh();
assertEqual(180, funcgen.getRefClock());
funcgen.setRefClockLow();
assertEqual(30, funcgen.getRefClock());
}
unittest(test_ad9851_reset)
{
AD9851 funcgen;
funcgen.begin(4, 5, 6);
funcgen.setFrequency(1000);
assertEqual(1000, funcgen.getFrequency());
funcgen.setPhase(14);
assertEqual(14, funcgen.getPhase());
funcgen.setRefClockHigh();
assertEqual(180, funcgen.getRefClock());
funcgen.reset();
assertEqual(0, funcgen.getFrequency());
assertEqual(0, funcgen.getPhase());
assertEqual(30, funcgen.getRefClock());
}
unittest(test_ad9851_autoRefClock)
{
AD9851 funcgen;
funcgen.begin(4, 5, 6);
assertFalse(funcgen.getAutoRefClock());
for (uint32_t freq = 70; freq <= 70000000; freq *= 10)
{
funcgen.setFrequency(freq);
fprintf(stderr, "freq %ld\t", freq);
assertEqual(30, funcgen.getRefClock());
}
funcgen.setAutoRefClock(true);
assertTrue(funcgen.getAutoRefClock());
for (uint32_t freq = 70; freq <= 1000000; freq *= 10)
{
funcgen.setFrequency(freq);
fprintf(stderr, "freq %ld\t", freq);
assertEqual(30, funcgen.getRefClock());
}
funcgen.setFrequency(10000000);
fprintf(stderr, "freq 10000000\t");
assertEqual(30, funcgen.getRefClock());
funcgen.setFrequency(100000001);
fprintf(stderr, "freq 100000001\t");
assertEqual(180, funcgen.getRefClock());
funcgen.setFrequency(70000000);
fprintf(stderr, "freq 70000000\t");
assertEqual(180, funcgen.getRefClock());
fprintf(stderr, "get- setARCCutOffFreq\t");
funcgen.setARCCutOffFreq(5000);
assertEqual(5000, funcgen.getARCCutOffFreq());
funcgen.setARCCutOffFreq(5000000);
assertEqual(5000000, funcgen.getARCCutOffFreq());
funcgen.setARCCutOffFreq(50000000);
assertEqual(30000000, funcgen.getARCCutOffFreq());
}
unittest(test_ad9851_offset)
{
AD9851 funcgen;
funcgen.begin(4, 5, 6);
assertEqual(0, funcgen.getCalibration());
funcgen.setFrequency(1000000);
assertEqual(1000000, funcgen.getFrequency());
for (int32_t offset = -1000; offset <= 1000; offset += 100)
{
funcgen.setCalibration(offset);
fprintf(stderr, "offset %d\t", offset);
assertEqual(offset, funcgen.getCalibration());
}
assertEqual(1000000, funcgen.getFrequency());
funcgen.setCalibration();
assertEqual(0, funcgen.getCalibration());
}
unittest(test_ad9851_float_freq)
{
AD9851 funcgen;
funcgen.begin(4, 5, 6);
for (float f = 100.0; f < 110.0; f += 0.1)
{
funcgen.setFrequencyF(f);
fprintf(stderr, "%ld\t", funcgen.getFactor());
assertEqualFloat(f, funcgen.getFrequency(), 1.0);
}
}
unittest_main()
// --------

View File

@ -0,0 +1,7 @@
compile:
# Choosing to run compilation tests on 2 different Arduino platforms
platforms:
- uno
- leonardo
- due
- zero

View File

@ -0,0 +1,13 @@
---
name: Arduino CI
on: [push, pull_request]
jobs:
arduino_ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: Arduino-CI/action@master
# Arduino-CI/action@v0.1.1

View File

@ -0,0 +1,555 @@
//
// FILE: ADS1X15.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.2.7
// DATE: 2013-03-24
// PUPROSE: Arduino library for ADS1015 and ADS1115
// URL: https://github.com/RobTillaart/ADS1X15
//
// HISTORY:
// 0.0.0 2013-03-24 initial version
// 0.0.1 2013-03-24 first working version
// 0.1.0 2017-07-31 removed pre 1.0 support; added getVoltage
// 0.2.0 2020-04-08 initial release; refactor ad fundum;
// 0.2.1 2020-08-15 fix issue 2 gain; refactor
// 0.2.2 2020-08-18 add begin(sda, scl) for ESP32
// 0.2.3 2020-08-20 add comparator code + async mode
// 0.2.4 2020-08-26 check readme.md and minor fixes
// 0.2.5 2020-08-26 add missing readADC_Differential_X_X()
// 0.2.6 2020-09-01 fix #12 - fix getMaxVoltage + minor refactor
// 0.2.7 2020-09-27 redo readRegister() + getValue() + getError()
#include "ADS1X15.h"
#define ADS1015_CONVERSION_DELAY 1
#define ADS1115_CONVERSION_DELAY 8
// Kept #defines a bit in line with Adafruits library.
// REGISTERS
#define ADS1X15_REG_CONVERT 0x00
#define ADS1X15_REG_CONFIG 0x01
#define ADS1X15_REG_LOW_THRESHOLD 0x02
#define ADS1X15_REG_HIGH_THRESHOLD 0x03
// CONFIG REGISTER
// BIT 15 Operational Status // 1 << 15
#define ADS1X15_OS_BUSY 0x0000
#define ADS1X15_OS_NOT_BUSY 0x8000
#define ADS1X15_OS_START_SINGLE 0x8000
// BIT 12-14 read differential
#define ADS1X15_MUX_DIFF_0_1 0x0000
#define ADS1X15_MUX_DIFF_0_3 0x1000
#define ADS1X15_MUX_DIFF_1_3 0x2000
#define ADS1X15_MUX_DIFF_2_3 0x3000
// read single
#define ADS1X15_READ_0 0x4000 // pin << 12
#define ADS1X15_READ_1 0x5000 // pin = 0..3
#define ADS1X15_READ_2 0x6000
#define ADS1X15_READ_3 0x7000
// BIT 9-11 gain // (0..5) << 9
#define ADS1X15_PGA_6_144V 0x0000 // voltage
#define ADS1X15_PGA_4_096V 0x0200 //
#define ADS1X15_PGA_2_048V 0x0400 // default
#define ADS1X15_PGA_1_024V 0x0600
#define ADS1X15_PGA_0_512V 0x0800
#define ADS1X15_PGA_0_256V 0x0A00
// BIT 8 mode // 1 << 8
#define ADS1X15_MODE_CONTINUE 0x0000
#define ADS1X15_MODE_SINGLE 0x0100
// BIT 5-7 datarate sample per second // (0..7) << 5
/*
differs for different devices, check datasheet or readme.md
| datarate | ADS101x | ADS 111x |
|:----:|----:|----:|
| 0 | 128 | 8 |
| 1 | 250 | 16 |
| 2 | 490 | 32 |
| 3 | 920 | 64 |
| 4 | 1600 | 128 |
| 5 | 2400 | 250 |
| 6 | 3300 | 475 |
| 7 | 3300 | 860 |
*/
// BIT 4 comparator modi // 1 << 4
#define ADS1X15_COMP_MODE_TRADITIONAL 0x0000
#define ADS1X15_COMP_MODE_WINDOW 0x0010
// BIT 3 ALERT active value // 1 << 3
#define ADS1X15_COMP_POL_ACTIV_LOW 0x0000
#define ADS1X15_COMP_POL_ACTIV_HIGH 0x0008
// BIT 2 ALERT latching // 1 << 2
#define ADS1X15_COMP_NON_LATCH 0x0000
#define ADS1X15_COMP_LATCH 0x0004
// BIT 0-1 ALERT mode // (0..3)
#define ADS1X15_COMP_QUE_1_CONV 0x0000 // trigger alert after 1 convert
#define ADS1X15_COMP_QUE_2_CONV 0x0001 // trigger alert after 2 converts
#define ADS1X15_COMP_QUE_4_CONV 0x0002 // trigger alert after 4 converts
#define ADS1X15_COMP_QUE_NONE 0x0003 // dosable comparator
// _CONFIG masks
//
// | bit | description |
// |:----:|:----|
// | 0 | # channels |
// | 1 | - |
// | 2 | resolution |
// | 3 | - |
// | 4 | GAIN supported |
// | 5 | COMPARATOR supported |
// | 6 | - |
// | 7 | - |
#define ADS_CONF_CHAN_1 0x00
#define ADS_CONF_CHAN_4 0x01
#define ADS_CONF_RES_12 0x00
#define ADS_CONF_RES_16 0x04
#define ADS_CONF_NOGAIN 0x00
#define ADS_CONF_GAIN 0x10
#define ADS_CONF_NOCOMP 0x00
#define ADS_CONF_COMP 0x20
/////////////////////////////////////////////////////////////////////////
//
// STATIC MEMBERS
//
static bool writeRegister(uint8_t address, uint8_t reg, uint16_t value)
{
Wire.beginTransmission(address);
Wire.write((uint8_t)reg);
Wire.write((uint8_t)(value >> 8));
Wire.write((uint8_t)(value & 0xFF));
return (Wire.endTransmission() == 0);
}
static uint16_t readRegister(uint8_t address, uint8_t reg)
{
Wire.beginTransmission(address);
Wire.write(reg);
Wire.endTransmission();
int rv = Wire.requestFrom(address, (uint8_t) 2);
if (rv == 2)
{
uint16_t value = Wire.read() << 8;
value += Wire.read();
return value;
}
return 0x0000;
}
//////////////////////////////////////////////////////
//
// BASE CONSTRUCTOR
//
ADS1X15::ADS1X15()
{
setGain(0); // _gain = ADS1X15_PGA_6_144V;
setMode(1); // _mode = ADS1X15_MODE_SINGLE;
setDataRate(4); // middle speed, depends on device.
}
//////////////////////////////////////////////////////
//
// PUBLIC
//
#if defined (ESP8266) || defined(ESP32)
bool ADS1X15::begin(uint8_t sda, uint8_t scl)
{
Wire.begin(sda, scl);
if ((_address < 0x48) || (_address > 0x4B)) return false;
return true;
}
#endif
bool ADS1X15::begin()
{
Wire.begin();
if ((_address < 0x48) || (_address > 0x4B)) return false;
return true;
}
bool ADS1X15::isBusy()
{
uint16_t val = readRegister(_address, ADS1X15_REG_CONFIG);
if ((val & ADS1X15_OS_NOT_BUSY) != 0) return false;
return true;
}
bool ADS1X15::isConnected()
{
Wire.beginTransmission(_address);
return (Wire.endTransmission() == 0);
}
void ADS1X15::setGain(uint8_t gain)
{
if (!(_config & ADS_CONF_GAIN)) gain = 0;
switch (gain)
{
default: // catch invalid values and go for the safest gain.
case 0: _gain = ADS1X15_PGA_6_144V; break;
case 1: _gain = ADS1X15_PGA_4_096V; break;
case 2: _gain = ADS1X15_PGA_2_048V; break;
case 4: _gain = ADS1X15_PGA_1_024V; break;
case 8: _gain = ADS1X15_PGA_0_512V; break;
case 16: _gain = ADS1X15_PGA_0_256V; break;
}
}
uint8_t ADS1X15::getGain()
{
if (!(_config & ADS_CONF_GAIN)) return 0;
switch (_gain)
{
case ADS1X15_PGA_6_144V: return 0;
case ADS1X15_PGA_4_096V: return 1;
case ADS1X15_PGA_2_048V: return 2;
case ADS1X15_PGA_1_024V: return 4;
case ADS1X15_PGA_0_512V: return 8;
case ADS1X15_PGA_0_256V: return 16;
}
_err = ADS1X15_INVALID_GAIN;
return _err;
}
float ADS1X15::toVoltage(int16_t val)
{
if (val == 0) return 0;
float volts = getMaxVoltage();
if (volts < 0) return volts;
volts *= val;
if (_config & ADS_CONF_RES_16)
{
volts /= 32767; // val = 16 bits - sign bit = 15 bits mantissa
}
else
{
volts /= 2047; // val = 12 bits - sign bit = 11 bit mantissa
}
return volts;
}
float ADS1X15::getMaxVoltage()
{
switch (_gain)
{
case ADS1X15_PGA_6_144V: return 6.144;
case ADS1X15_PGA_4_096V: return 4.096;
case ADS1X15_PGA_2_048V: return 2.048;
case ADS1X15_PGA_1_024V: return 1.024;
case ADS1X15_PGA_0_512V: return 0.512;
case ADS1X15_PGA_0_256V: return 0.256;
}
_err = ADS1X15_INVALID_VOLTAGE;
return _err;
}
void ADS1X15::setMode(uint8_t mode)
{
switch (mode)
{
case 0: _mode = ADS1X15_MODE_CONTINUE; break;
default:
case 1: _mode = ADS1X15_MODE_SINGLE; break;
}
}
uint8_t ADS1X15::getMode(void)
{
switch (_mode)
{
case ADS1X15_MODE_CONTINUE: return 0;
case ADS1X15_MODE_SINGLE: return 1;
}
_err = ADS1X15_INVALID_MODE;
return _err;
}
void ADS1X15::setDataRate(uint8_t dataRate)
{
_datarate = dataRate;
if (_datarate > 7) _datarate = 4; // default
_datarate <<= 5; // convert 0..7 to mask needed.
}
uint8_t ADS1X15::getDataRate(void)
{
return (_datarate >> 5); // convert mask back to 0..7
}
int16_t ADS1X15::readADC(uint8_t pin)
{
if (pin >= _maxPorts) return 0;
uint16_t mode = ((4 + pin) << 12); // pin to mask
return _readADC(mode);
}
void ADS1X15::requestADC_Differential_0_1()
{
_requestADC(ADS1X15_MUX_DIFF_0_1);
}
int16_t ADS1X15::readADC_Differential_0_1()
{
return _readADC(ADS1X15_MUX_DIFF_0_1);
}
void ADS1X15::requestADC(uint8_t pin)
{
if (pin >= _maxPorts) return;
uint16_t mode = ((4 + pin) << 12); // pin to mask
_requestADC(mode);
}
int16_t ADS1X15::getValue()
{
int16_t raw = readRegister(_address, ADS1X15_REG_CONVERT);
if (_bitShift) raw >>= _bitShift; // Shift 12-bit results
return raw;
}
void ADS1X15::setComparatorThresholdLow(int16_t lo)
{
writeRegister(_address, ADS1X15_REG_LOW_THRESHOLD, lo);
};
int16_t ADS1X15::getComparatorThresholdLow()
{
return readRegister(_address, ADS1X15_REG_LOW_THRESHOLD);
};
void ADS1X15::setComparatorThresholdHigh(int16_t hi)
{
writeRegister(_address, ADS1X15_REG_HIGH_THRESHOLD, hi);
};
int16_t ADS1X15::getComparatorThresholdHigh()
{
return readRegister(_address, ADS1X15_REG_HIGH_THRESHOLD);
};
int8_t ADS1X15::getError()
{
int8_t rv = _err;
_err = ADS1X15_OK;
return rv;
}
//////////////////////////////////////////////////////
//
// PRIVATE
//
int16_t ADS1X15::_readADC(uint16_t readmode)
{
_requestADC(readmode);
if (_mode == ADS1X15_MODE_SINGLE)
{
while ( isBusy() ) yield(); // wait for conversion; yield for ESP.
}
else
{
delay(_conversionDelay); // TODO needed in continuous mode?
}
return getValue();
}
void ADS1X15::_requestADC(uint16_t readmode)
{
// write to register is needed in continuous mode as other flags can be changed
uint16_t config = ADS1X15_OS_START_SINGLE; // bit 15 force wake up if needed
config |= readmode; // bit 12-14
config |= _gain; // bit 9-11
config |= _mode; // bit 8
config |= _datarate; // bit 5-7
if (_compMode) config |= ADS1X15_COMP_MODE_WINDOW; // bit 4 comparator modi
else config |= ADS1X15_COMP_MODE_TRADITIONAL;
if (_compPol) config |= ADS1X15_COMP_POL_ACTIV_HIGH; // bit 3 ALERT active value
else config |= ADS1X15_COMP_POL_ACTIV_LOW;
if (_compLatch) config |= ADS1X15_COMP_LATCH;
else config |= ADS1X15_COMP_NON_LATCH; // bit 2 ALERT latching
config |= _compQueConvert; // bit 0..1 ALERT mode
writeRegister(_address, ADS1X15_REG_CONFIG, config);
}
///////////////////////////////////////////////////////////////////////////
//
// ADS1013
//
ADS1013::ADS1013(uint8_t address)
{
_address = address;
_config = ADS_CONF_NOCOMP | ADS_CONF_NOGAIN | ADS_CONF_RES_12 | ADS_CONF_CHAN_1;
_conversionDelay = ADS1015_CONVERSION_DELAY;
_bitShift = 4;
_maxPorts = 1;
}
///////////////////////////////////////////////////////////////////////////
//
// ADS1014
//
ADS1014::ADS1014(uint8_t address)
{
_address = address;
_config = ADS_CONF_COMP | ADS_CONF_GAIN | ADS_CONF_RES_12 | ADS_CONF_CHAN_1;
_conversionDelay = ADS1015_CONVERSION_DELAY;
_bitShift = 4;
_maxPorts = 1;
}
///////////////////////////////////////////////////////////////////////////
//
// ADS1015
//
ADS1015::ADS1015(uint8_t address)
{
_address = address;
_config = ADS_CONF_COMP | ADS_CONF_GAIN | ADS_CONF_RES_12 | ADS_CONF_CHAN_4;
_conversionDelay = ADS1015_CONVERSION_DELAY;
_bitShift = 4;
_maxPorts = 4;
}
int16_t ADS1015::readADC_Differential_0_3()
{
return _readADC(ADS1X15_MUX_DIFF_0_3);
}
int16_t ADS1015::readADC_Differential_1_3()
{
return _readADC(ADS1X15_MUX_DIFF_1_3);
}
int16_t ADS1015::readADC_Differential_2_3()
{
return _readADC(ADS1X15_MUX_DIFF_2_3);
}
int16_t ADS1015::readADC_Differential_0_2()
{
return readADC(2) - readADC(0);
}
int16_t ADS1015::readADC_Differential_1_2()
{
return readADC(2) - readADC(1);;
}
void ADS1015::requestADC_Differential_0_3()
{
_requestADC(ADS1X15_MUX_DIFF_0_3);
}
void ADS1015::requestADC_Differential_1_3()
{
_requestADC(ADS1X15_MUX_DIFF_1_3);
}
void ADS1015::requestADC_Differential_2_3()
{
_requestADC(ADS1X15_MUX_DIFF_2_3);
}
///////////////////////////////////////////////////////////////////////////
//
// ADS1113
//
ADS1113::ADS1113(uint8_t address)
{
_address = address;
_config = ADS_CONF_NOCOMP | ADS_CONF_NOGAIN | ADS_CONF_RES_16 | ADS_CONF_CHAN_1;
_conversionDelay = ADS1115_CONVERSION_DELAY;
_bitShift = 0;
_maxPorts = 1;
}
///////////////////////////////////////////////////////////////////////////
//
// ADS1114
//
ADS1114::ADS1114(uint8_t address)
{
_address = address;
_config = ADS_CONF_COMP | ADS_CONF_GAIN | ADS_CONF_RES_16 | ADS_CONF_CHAN_1;
_conversionDelay = ADS1115_CONVERSION_DELAY;
_bitShift = 0;
_maxPorts = 1;
}
///////////////////////////////////////////////////////////////////////////
//
// ADS1115
//
ADS1115::ADS1115(uint8_t address)
{
_address = address;
_config = ADS_CONF_COMP | ADS_CONF_GAIN | ADS_CONF_RES_16 | ADS_CONF_CHAN_4;
_conversionDelay = ADS1115_CONVERSION_DELAY;
_bitShift = 0;
_maxPorts = 4;
}
int16_t ADS1115::readADC_Differential_0_3()
{
return _readADC(ADS1X15_MUX_DIFF_0_3);
}
int16_t ADS1115::readADC_Differential_1_3()
{
return _readADC(ADS1X15_MUX_DIFF_1_3);
}
int16_t ADS1115::readADC_Differential_2_3()
{
return _readADC(ADS1X15_MUX_DIFF_2_3);
}
int16_t ADS1115::readADC_Differential_0_2()
{
return readADC(2) - readADC(0);
}
int16_t ADS1115::readADC_Differential_1_2()
{
return readADC(2) - readADC(1);;
}
void ADS1115::requestADC_Differential_0_3()
{
_requestADC(ADS1X15_MUX_DIFF_0_3);
}
void ADS1115::requestADC_Differential_1_3()
{
_requestADC(ADS1X15_MUX_DIFF_1_3);
}
void ADS1115::requestADC_Differential_2_3()
{
_requestADC(ADS1X15_MUX_DIFF_2_3);
}
// --- END OF FILE

209
libraries/ADS1x15/ADS1X15.h Normal file
View File

@ -0,0 +1,209 @@
#pragma once
//
// FILE: ADS1X15.H
// AUTHOR: Rob Tillaart
// VERSION: 0.2.7
// DATE: 2013-03-24
// PUPROSE: Arduino library for ADS1015 and ADS1115
// URL: https://github.com/RobTillaart/ADS1X15
//
#include "Arduino.h"
#include "Wire.h"
#define ADS1X15_LIB_VERSION "0.2.7"
// allow compile time default address
// address in { 0x48, 0x49, 0x4A, 0x4B }, no test...
#ifndef ADS1015_ADDRESS
#define ADS1015_ADDRESS 0x48
#endif
#ifndef ADS1115_ADDRESS
#define ADS1115_ADDRESS 0x48
#endif
#define ADS1X15_OK 0
#define ADS1X15_INVALID_VOLTAGE -100
#define ADS1X15_INVALID_GAIN 0xFF
#define ADS1X15_INVALID_MODE 0xFE
class ADS1X15
{
public:
#if defined (ESP8266) || defined(ESP32)
bool begin(uint8_t sda, uint8_t scl);
#endif
bool begin();
bool isConnected();
// GAIN
// 0 = ±6.144V default
// 1 = ±4.096V
// 2 = ±2.048V
// 4 = ±1.024V
// 8 = ±0.512V
// 16 = ±0.256V
void setGain(uint8_t gain = 0); // invalid values are mapped to 0 (default).
uint8_t getGain(); // 0xFF == invalid gain error.
// both may return ADS1X15_INVALID_VOLTAGE if the gain is invalid.
float toVoltage(int16_t val = 1); // converts raw to voltage
float getMaxVoltage(); // -100 == invalid voltage error
// 0 = CONTINUOUS
// 1 = SINGLE default
void setMode(uint8_t mode = 1); // invalid values are mapped to 1 (default)
uint8_t getMode(); // 0xFE == invalid mode error.
// 0 = slowest
// 7 = fastest
// 4 = default
void setDataRate(uint8_t dataRate);// invalid values are mapped on 4 (default)
uint8_t getDataRate(); // actual speed depends on device
int16_t readADC(uint8_t pin);
int16_t readADC_Differential_0_1();
// used by continuous mode and async mode.
int16_t getLastValue() { return getValue(); }; // will be obsolete in future
int16_t getValue();
// ASYNC INTERFACE
// requestADC(pin) -> isBusy() or isReady() -> getValue();
// see examples
void requestADC(uint8_t pin);
void requestADC_Differential_0_1();
bool isBusy();
bool isReady() { return isBusy() == false; };
// COMPARATOR
// 0 = TRADITIONAL > high => on < low => off
// else = WINDOW > high or < low => on between => off
void setComparatorMode(uint8_t mode) { _compMode = mode == 0 ? 0 : 1; };
uint8_t getComparatorMode() { return _compMode; };
// 0 = LOW (default)
// else = HIGH
void setComparatorPolarity(uint8_t pol) { _compPol = pol ? 0 : 1; };
uint8_t getComparatorPolarity() { return _compPol; };
// 0 = NON LATCH
// else = LATCH
void setComparatorLatch(uint8_t latch) { _compLatch = latch ? 0 : 1; };
uint8_t getComparatorLatch() { return _compLatch; };
// 0 = trigger alert after 1 conversion
// 1 = trigger alert after 2 conversions
// 2 = trigegr alert after 4 conversions
// 3 = Disable comparator = default, also for all other values.
void setComparatorQueConvert(uint8_t mode) { _compQueConvert = (mode < 3) ? mode : 3; };
uint8_t getComparatorQueConvert() { return _compQueConvert; };
void setComparatorThresholdLow(int16_t lo);
int16_t getComparatorThresholdLow();
void setComparatorThresholdHigh(int16_t hi);
int16_t getComparatorThresholdHigh();
int8_t getError();
protected:
ADS1X15();
// CONFIGURATION
// BIT DESCRIPTION
// 0 # channels 0 == 1 1 == 4;
// 1 0
// 2 # resolution 0 == 12 1 == 16
// 3 0
// 4 has gain 0 = NO 1 = YES
// 5 has comparator 0 = NO 1 = YES
// 6 0
// 7 0
uint8_t _config;
uint8_t _maxPorts;
uint8_t _address;
uint8_t _conversionDelay;
uint8_t _bitShift;
uint16_t _gain;
uint16_t _mode;
uint16_t _datarate;
// COMPARATOR vars
// TODO merge these into one COMPARATOR MASK?
// would speed up code in _requestADC() and save 3 bytes RAM.
uint8_t _compMode = 0;
uint8_t _compPol = 1;
uint8_t _compLatch = 0;
uint8_t _compQueConvert = 3;
int16_t _readADC(uint16_t readmode);
void _requestADC(uint16_t readmode);
int8_t _err = ADS1X15_OK;
};
///////////////////////////////////////////////////////////////////////////
//
// Derived classes from ADS1X15
//
class ADS1013 : public ADS1X15
{
public:
ADS1013(uint8_t Address = ADS1015_ADDRESS);
};
class ADS1014 : public ADS1X15
{
public:
ADS1014(uint8_t Address = ADS1015_ADDRESS);
};
class ADS1015 : public ADS1X15
{
public:
ADS1015(uint8_t Address = ADS1015_ADDRESS);
int16_t readADC_Differential_0_3();
int16_t readADC_Differential_1_3();
int16_t readADC_Differential_2_3();
int16_t readADC_Differential_0_2(); // not possible in async
int16_t readADC_Differential_1_2(); // not possible in async
void requestADC_Differential_0_3();
void requestADC_Differential_1_3();
void requestADC_Differential_2_3();
};
///////////////////////////////////////////////////////////////////////////
//
// Derived classes from ADS1X15
//
class ADS1113 : public ADS1X15
{
public:
ADS1113(uint8_t address = ADS1115_ADDRESS);
};
class ADS1114 : public ADS1X15
{
public:
ADS1114(uint8_t address = ADS1115_ADDRESS);
};
class ADS1115 : public ADS1X15
{
public:
ADS1115(uint8_t address = ADS1115_ADDRESS);
int16_t readADC_Differential_0_3();
int16_t readADC_Differential_1_3();
int16_t readADC_Differential_2_3();
int16_t readADC_Differential_0_2(); // not possible in async
int16_t readADC_Differential_1_2(); // not possible in async
void requestADC_Differential_0_3();
void requestADC_Differential_1_3();
void requestADC_Differential_2_3();
};
// --- END OF FILE ---

21
libraries/ADS1x15/LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2013-2021 Rob Tillaart
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

265
libraries/ADS1x15/README.md Normal file
View File

@ -0,0 +1,265 @@
[![Arduino CI](https://github.com/RobTillaart/ADS1X15/workflows/Arduino%20CI/badge.svg)](https://github.com/marketplace/actions/arduino_ci)
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/RobTillaart/ADS1X15/blob/master/LICENSE)
[![GitHub release](https://img.shields.io/github/release/RobTillaart/ADS1X15.svg?maxAge=3600)](https://github.com/RobTillaart/ADS1X15/releases)
# ADS1X15
Arduino library for I2C ADC ADS1015, ADS1115,
## Description
This library should work for the devices mentioned below,
although not all sensors support all functionality.
| Device | Channels | Resolution | Max sps | Comparator | ProgGainAMP | Notes |
|:----:|:----:|:----:|:----:|:----:|:----:|:----|
| ADS1013 | 1 | 12 | 3300 | N | N | |
| ADS1014 | 1 | 12 | 3300 | Y | Y | |
| ADS1015 | 4 | 12 | 3300 | Y | Y | |
| ADS1113 | 1 | 16 | 860 | N | N | |
| ADS1114 | 1 | 16 | 860 | Y | Y | |
| ADS1115 | 4 | 16 | 860 | Y | Y | Tested |
As the 1015 and the 1115 are both 4 channels these are the most
interesting from functionality point of view as these can also do
differential measurement.
#### Note
This readme file is work in progress.
## Interface
The address of the ADS1113/4/5 is determined by to which pin the ADDR
is connected to:
| ADDR pin connected to | Address | Note |
|:----:|:----:|:----:|
| GND | 0x48 | default |
| VDD | 0x49 | |
| SDA | 0x4A | |
| SCL | 0x4B | |
- **ADS1x15()** constructor, should not be used.
- **ADS1013(address)** constructor
- **ADS1014(address)** constructor
- **ADS1015(address)** constructor
- **ADS1113(address)** constructor
- **ADS1114(address)** constructor
- **ADS1115(address)** constructor
After construction the **ADS.begin()** need to be called. This will return false
if an invalid address is used.
The function **isConnected()** can be used to verify the reading of the ADS.
#### Programmable Gain
- **setGain(gain)** set the gain value, indicating the maxVoltage that can be measured
Adjusting the gain allows one to make more precise measurements.
See table below.
- **getGain()** returns the gain value (index).
| PGA value | Max Voltage | note |
|:----:|:----:|:----:|
| 0 | ±6.144V | default |
| 1 | ±4.096V | |
| 2 | ±2.048V | |
| 4 | ±1.024V | |
| 8 | ±0.512V | |
| 16 | ±0.256V | |
- **getMaxVoltage()** returns the max voltage with the current gain.
- **toVoltage(raw = 1)** converts a raw measurement to a voltage.
Can be used for normal and differential measurements.
The default value of 1 returns the conversion factor for any raw number.
The voltage factor can also be used to set HIGH and LOW threshold registers
with a voltage in the comparator mode.
Check the examples.
```cpp
float f = ADS.toVoltage();
ADS.setComparatorThresholdLow( 3.0 / f );
ADS.setComparatorThresholdLow( 4.3 / f );
```
#### Operational mode
The ADS sensor can operate in single shot or continuous mode.
Depending on how often one needs a conversion one can tune the mode.
- **setMode(mode)** 0 = CONTINUOUS, 1 = SINGLE (default)
- **getMode()** returns current mode 0 or 1, or ADS1X15_INVALID_MODE = 0xFE.
#### Datarate
- **setDataRate(dataRate)** Datarate depends on type of device.
For all devices the index 0..7 can be used, see table below.
Values above 7 ==> will be set to the default 4.
- **getDataRate()** returns the current datarate (index).
The library has no means to convert this index to the actual numbers
as that would take 32 bytes.
Datarate in samples per second, based on datasheet numbers.
| datarate | ADS101x | ADS 111x | Notes & remarks |
|:----:|----:|----:|:----:|
| 0 | 128 | 8 | slowest |
| 1 | 250 | 16 | |
| 2 | 490 | 32 | |
| 3 | 920 | 64 | |
| 4 | 1600 | 128 | default |
| 5 | 2400 | 250 | |
| 6 | 3300 | 475 | |
| 7 | 3300 | 860 | fastest |
#### ReadADC Single mode
Reading the ADC is very straightforward, the **readADC()** function handles
all in one call. Under the hood it uses the asynchronuous calls.
- **int16_t readADC(pin)** normal ADC functionality, pin = 0..3.
If the pinnumber is out of range, this function will return 0.
To read the ADC in an asynchronuous way (e.g. to minimize blocking) one has to use three calls:
- **requestADC(pin)** Start the conversion. pin = 0..3.
- **isBusy()** Is the conversion not ready?
- **isReady()** Is the conversion ready? (= wrapper around **isBusy()** )
- **getValue()** Read the result of the conversion.
in terms of code
```cpp
void setup()
{
// other setup things here
ADS.requestADC(pin);
}
void loop()
{
if (ADS.isReady())
{
val = ADS.getValue();
ADS.requestADC(pin); // request new conversion
}
// do other things here
}
```
See examples
## ReadADC Differential
For reading the ADC in a differential way there are 4 calls possible.
- **readADC_Differential_0_1()** returns the difference between 2 ADC pins.
- **readADC_Differential_0_3()** ADS1x15 only
- **readADC_Differential_1_3()** ADS1x15 only
- **readADC_Differential_2_3()** ADS1x15 only
- **readADC_Differential_0_2()** ADS1x15 only - in software (no async equivalent)
- **readADC_Differential_1_2()** ADS1x15 only - in software (no async equivalent)
The differential reading of the ADC can also be done with asynchronuous calls.
- **requestADC_Differential_0_1()** starts conversion for differential reading
- **requestADC_Differential_0_3()** ADS1x15 only
- **requestADC_Differential_1_3()** ADS1x15 only
- **requestADC_Differential_2_3()** ADS1x15 only
After one of these calls one need to call
- **isBusy()** Is the conversion ready?
- **getValue()** Read the result of the conversion.
#### ReadADC continuous mode
To use the continuous mode one need three calls
- **setMode(0)** 0 = CONTINUOUS, 1 = SINGLE (default)
- **readADC()** or **requestADC()** to get the continuous mode started.
- **getValue()** to return the last value read by the device.
Calling this over and over again can give the same value multiple times.
By using **isBusy()** or **isReady()** one can wait until new data is available.
Or one can use the **ALERT/RDY** pin to trigger via hardware the readyness of the conversion.
See examples.
#### Threshold registers ==> mode RDY pin
If the thresholdHigh is set to 0x0100 and the thresholdLow to 0x0000
the **ALERT/RDY** pin is triggered when a conversion is ready.
- **setComparatorThresholdLow(0x0000)**
- **setComparatorThresholdHigh(0x0100)**
See examples.
## Comparator
Please read Page 15 of the datasheet as the behavior of the
comparator is not trivial.
#### Comparator Mode
When configured as a **TRADITIONAL** comparator, the **ALERT/RDY** pin asserts
(active low by default) when conversion data exceed the limit set in the
high threshold register. The comparator then deasserts when the input
signal falls below the low threshold register value.
If the comparator **LATCH** is set, the **ALERT/RDY** pin asserts and it will be
reset after reading the sensor (conversion register) again.
*An SMB alert command (00011001) on the I2C bus will also reset the alert state.*
*Not implemented in the library (yet)*
In **WINDOW** comparator mode, the **ALERT/RDY** pin asserts if conversion data exceeds
the high threshold register or falls below the low threshold register.
In this mode the alert is held if the **LATCH** is set. This is similar as above.
#### Polarity
Default state of the **ALERT/RDY** pin is **LOW**, can be to set **HIGH**.
#### Latch
Holds the **ALERT/RDY** to **HIGH** (or **LOW** depending on polarity) after triggered
even if actual value has been 'restored to normal' value.
#### QueConvert
Set the number of conversions before trigger activates.
The **setComparatorQueConvert(uint8_t mode)** is used to set the number of
conversions that exceed the threshold before the **ALERT/RDY** pin is set **HIGH**.
A value of 3 (or above) effectively disables the comparator. See table below.
| value | meaning | notes |
|:----:|:----|:----|
| 0 | trigger alert after 1 conversion | |
| 1 | trigger alert after 2 conversions | |
| 2 | trigegr alert after 4 conversions | |
| 3 | Disable comparator | default |
#### Threshold registers comparator mode
Depending on the comparator mode **TRADITIONAL** or **WINDOW** the thresholds registers
mean something different see - Comparator Mode above or datasheet.
- **setComparatorThresholdLow(lo)** set the low threshold; take care the hi >= lo
- **setComparatorThresholdHigh(hi)** set the high threshold; take care the hi >= lo
- **getComparatorThresholdLow()** returns set value
- **getComparatorThresholdHigh()** returns set value
## Future ideas & improvements
- Improve documentation
- more examples?
- SMB alert command (00011001) on I2C bus?
- implement missing Differential reads in software.
- testing....
## Operation
See examples

View File

@ -0,0 +1,47 @@
//
// FILE: ADS_continuous.ino
// AUTHOR: Rob.Tillaart
// VERSION: 0.1.1
// PURPOSE: read analog input
//
// test
// connect 1 potmeter
//
// GND ---[ x ]------ 5V
// |
//
// measure at x (connect to AIN0).
#include "ADS1X15.h"
// choose you sensor
// ADS1013 ADS(0x48);
// ADS1014 ADS(0x48);
// ADS1015 ADS(0x48);
// ADS1113 ADS(0x48);
// ADS1114 ADS(0x48);
ADS1115 ADS(0x48);
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("ADS1X15_LIB_VERSION: ");
Serial.println(ADS1X15_LIB_VERSION);
ADS.begin();
ADS.setGain(0); // 6.144 volt
ADS.setDataRate(7); // fast
ADS.setMode(0); // continuous mode
ADS.readADC(0); // first read to trigger
}
void loop()
{
Serial.println(ADS.getValue());
}
// -- END OF FILE --

View File

@ -0,0 +1,94 @@
//
// FILE: ADS_continuous_4_channel.ino
// AUTHOR: Rob.Tillaart
// VERSION: 0.1.2
// PURPOSE: read multiple analog inputs continuously
// interrupt driven to catch all conversions.
//
// test
// connect multiple potmeters
//
// GND ---[ x ]------ 5V
// |
//
// measure at x - connect to AIN0..4.
//
// for the test it is good to have AIN3 connected to 5V and AIN4 to GND
// so one can see these as references in the output.
//
#include "ADS1X15.h"
// choose you sensor
// ADS1013 ADS(0x48);
// ADS1014 ADS(0x48);
// ADS1015 ADS(0x48);
// ADS1113 ADS(0x48);
// ADS1114 ADS(0x48);
ADS1115 ADS(0x48);
volatile bool RDY = false;
uint8_t channel = 0;
int16_t val[4] = { 0, 0, 0, 0 };
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("ADS1X15_LIB_VERSION: ");
Serial.println(ADS1X15_LIB_VERSION);
pinMode(2, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(2), adsReady, RISING);
ADS.begin();
ADS.setGain(0); // 6.144 volt
ADS.setDataRate(7); // slow
// SET ALERT RDY PIN
ADS.setComparatorThresholdHigh(0x8000);
ADS.setComparatorThresholdLow(0x0000);
ADS.setComparatorQueConvert(0);
// SET INTERRUPT HANDLER TO CATCH CONVERSION READY
pinMode(2, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(2), adsReady, RISING);
ADS.setMode(0); // continuous mode
ADS.readADC(channel); // trigger first read
}
void loop()
{
handleConversion();
for (int i = 0; i < 4; i++)
{
Serial.print(val[i]);
Serial.print('\t');
handleConversion();
}
Serial.println();
delay(100);
}
void adsReady()
{
RDY = true;
}
void handleConversion()
{
if (RDY)
{
// save the value
val[channel] = ADS.getValue();
// request next channel
channel++;
if (channel >= 4) channel = 0;
ADS.readADC(channel);
RDY = false;
}
}
// -- END OF FILE --

View File

@ -0,0 +1,126 @@
//
// FILE: ADS_continuous_8_channel.ino
// AUTHOR: Rob.Tillaart
// VERSION: 0.1.1
// PURPOSE: read multiple analog inputs continuously
// interrupt driven to catch all conversions.
//
// test
// connect multiple potmeters to 2 ADS1115
//
// GND ---[ x ]------ 5V
// |
//
// measure at x - connect to AIN0..4.
//
// for the test it is good to have AIN3 connected to 5V and AIN4 to GND
// so one can see these as references in the output.
//
#include "ADS1X15.h"
// adjust addresses if needed
ADS1115 ADS_1(0x49);
ADS1115 ADS_2(0x48);
volatile bool RDY_1 = false;
volatile bool RDY_2 = false;
uint8_t channel_1 = 0;
uint8_t channel_2 = 0;
int16_t val[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("ADS1X15_LIB_VERSION: ");
Serial.println(ADS1X15_LIB_VERSION);
// SETUP FIRST ADS1115
ADS_1.begin();
ADS_1.setGain(0); // 6.144 volt
ADS_1.setDataRate(7);
// SET ALERT RDY PIN
ADS_1.setComparatorThresholdHigh(0x8000);
ADS_1.setComparatorThresholdLow(0x0000);
ADS_1.setComparatorQueConvert(0);
// SET INTERRUPT HANDLER TO CATCH CONVERSION READY
pinMode(2, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(2), adsReady_1, RISING);
ADS_1.setMode(0); // continuous mode
ADS_1.readADC(channel_1); // trigger first read
// SETUP SECOND ADS1115
ADS_2.begin();
ADS_2.setGain(0); // 6.144 volt
ADS_2.setDataRate(7);
// SET ALERT RDY PIN
ADS_2.setComparatorThresholdHigh(0x8000);
ADS_2.setComparatorThresholdLow(0x0000);
ADS_2.setComparatorQueConvert(0);
// SET INTERRUPT HANDLER TO CATCH CONVERSION READY
pinMode(3, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(3), adsReady_2, RISING);
ADS_2.setMode(0); // continuous mode
ADS_2.readADC(channel_2); // trigger first read
}
void loop()
{
handleConversion();
for (int i = 0; i < 8; i++)
{
Serial.print(val[i]);
Serial.print('\t');
handleConversion();
}
Serial.println();
delay(100);
}
// catch interrupt and set flag
void adsReady_1()
{
RDY_1 = true;
}
void adsReady_2()
{
RDY_2 = true;
}
// handle conversions that are ready
void handleConversion()
{
if (RDY_1)
{
// save the last value
val[channel_1] = ADS_1.getValue();
// request next channel
channel_1++;
if (channel_1 >= 4) channel_1 = 0;
ADS_1.readADC(channel_1);
RDY_1 = false;
}
if (RDY_2)
{
// save the last value
val[4 + channel_2] = ADS_2.getValue();
// request next channel
channel_2++;
if (channel_2 >= 4) channel_2 = 0;
ADS_2.readADC(channel_2);
RDY_2 = false;
}
}
// -- END OF FILE --

View File

@ -0,0 +1,64 @@
//
// FILE: ADS_differential.ino
// AUTHOR: Rob.Tillaart
// VERSION: 0.1.1
// PURPOSE: read differential
//
// test 1
// connect 2 potmeters in series
//
// GND ---[ x ]------[ y ]---- 5V
// | |
//
// measure at x and y (connect to AIN0 and AIN1).
// x should be lower or equal to y
// test 2
// connect 2 potmeters parallel
//
// GND ---[ x ]------ 5V
// |
//
// GND ---[ y ]------ 5V
// |
//
// measure at x and y (connect to AIN0 and AIN1).
// range from -VDD .. +VDD are possible
#include <ADS1X15.h>
ADS1115 ADS(0x48);
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("ADS1X15_LIB_VERSION: ");
Serial.println(ADS1X15_LIB_VERSION);
ADS.begin();
ADS.setGain(0);
}
void loop()
{
int16_t val_01 = ADS.readADC_Differential_0_1();
int16_t val_03 = ADS.readADC_Differential_0_3();
int16_t val_13 = ADS.readADC_Differential_1_3();
int16_t val_23 = ADS.readADC_Differential_2_3();
float volts_01 = ADS.toVoltage(val_01);
float volts_03 = ADS.toVoltage(val_03);
float volts_13 = ADS.toVoltage(val_13);
float volts_23 = ADS.toVoltage(val_23);
Serial.print("\tval_01: "); Serial.print(val_01); Serial.print("\t"); Serial.println(volts_01, 3);
Serial.print("\tval_03: "); Serial.print(val_03); Serial.print("\t"); Serial.println(volts_03, 3);
Serial.print("\tval_13: "); Serial.print(val_13); Serial.print("\t"); Serial.println(volts_13, 3);
Serial.print("\tval_23: "); Serial.print(val_23); Serial.print("\t"); Serial.println(volts_23, 3);
Serial.println();
delay(1000);
}
// -- END OF FILE --

View File

@ -0,0 +1,46 @@
//
// FILE: ADS_minimum.ino
// AUTHOR: Rob.Tillaart
// VERSION: 0.1.0
// PURPOSE: read analog input
//
// test
// connect 1 potmeter
//
// GND ---[ x ]------ 5V
// |
//
// measure at x (connect to AIN0).
// view with Serial Plotter
#include "ADS1X15.h"
// choose you sensor
// ADS1013 ADS(0x48);
// ADS1014 ADS(0x48);
// ADS1015 ADS(0x48);
// ADS1113 ADS(0x48);
// ADS1114 ADS(0x48);
ADS1115 ADS(0x48);
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("ADS1X15_LIB_VERSION: ");
Serial.println(ADS1X15_LIB_VERSION);
ADS.begin();
ADS.setGain(0); // 6.144 volt
Serial.println("Voltage");
}
void loop()
{
int16_t raw = ADS.readADC(0);
Serial.println(ADS.toVoltage(raw), 3);
}
// -- END OF FILE --

View File

@ -0,0 +1,92 @@
//
// FILE: ADS_performance.ino
// AUTHOR: Rob.Tillaart
// VERSION: 0.1.1
// PURPOSE: read analog input
//
// test
// connect 1 potmeter
//
// GND ---[ x ]------ 5V
// |
//
// measure at x (connect to AIN0).
#include "ADS1X15.h"
// choose you sensor
// ADS1013 ADS(0x48);
// ADS1014 ADS(0x48);
// ADS1015 ADS(0x48);
// ADS1113 ADS(0x48);
// ADS1114 ADS(0x48);
ADS1115 ADS(0x48);
uint32_t start, d1, d2;
int x;
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("ADS1X15_LIB_VERSION: ");
Serial.println(ADS1X15_LIB_VERSION);
ADS.begin();
ADS.setGain(0); // 6.144 volt
for (int dr = 0; dr < 8; dr++)
{
ADS.setDataRate(dr);
Serial.print("DR:\t");
Serial.println(dr);
test_single_shot();
test_continuous();
Serial.print("\t\tFACTOR:\t");
Serial.println(1.0 * d1 / d2);
}
Serial.println("\nDone...");
}
void loop()
{
}
void test_single_shot()
{
Serial.print(__FUNCTION__);
ADS.setMode(1);
start = micros();
x = ADS.readADC(0);
for (int i = 0; i < 100; i++)
{
x = ADS.readADC(0);
}
d1 = micros() - start;
Serial.print("\t");
Serial.println(d1);
}
void test_continuous()
{
Serial.print(__FUNCTION__);
ADS.setMode(0);
start = micros();
x = ADS.readADC(0);
for (int i = 0; i < 100; i++)
{
x = ADS.getValue();
}
d2 = micros() - start;
Serial.print("\t\t");
Serial.println(d2);
}
// -- END OF FILE --

View File

@ -0,0 +1,51 @@
//
// FILE: ADS_read.ino
// AUTHOR: Rob.Tillaart
// VERSION: 0.2.1
// PURPOSE: read analog inputs - straightforward.
//
// test
// connect 1 potmeter per port.
//
// GND ---[ x ]------ 5V
// |
//
// measure at x (connect to AIN0).
//
#include "ADS1X15.h"
ADS1115 ADS(0x48);
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("ADS1X15_LIB_VERSION: ");
Serial.println(ADS1X15_LIB_VERSION);
ADS.begin();
}
void loop()
{
ADS.setGain(0);
int16_t val_0 = ADS.readADC(0);
int16_t val_1 = ADS.readADC(1);
int16_t val_2 = ADS.readADC(2);
int16_t val_3 = ADS.readADC(3);
float f = ADS.toVoltage(1); // voltage factor
Serial.print("\tAnalog0: "); Serial.print(val_0); Serial.print('\t'); Serial.println(val_0 * f, 3);
Serial.print("\tAnalog1: "); Serial.print(val_1); Serial.print('\t'); Serial.println(val_1 * f, 3);
Serial.print("\tAnalog2: "); Serial.print(val_2); Serial.print('\t'); Serial.println(val_2 * f, 3);
Serial.print("\tAnalog3: "); Serial.print(val_3); Serial.print('\t'); Serial.println(val_3 * f, 3);
Serial.println();
delay(1000);
}
// -- END OF FILE --

View File

@ -0,0 +1,62 @@
//
// FILE: ADS_read_RDY.ino
// AUTHOR: Rob.Tillaart
// VERSION: 0.1.0
// PURPOSE: read analog inputs - straightforward.
//
// test
// connect 1 potmeter per port.
//
// GND ---[ x ]------ 5V
// |
//
// measure at x (connect to AIN0).
//
// EXPERIMENTAL
//
// The RDY pin (or ALERT Pin) is triggered when conversion is ready
//
#include "ADS1X15.h"
ADS1115 ADS(0x48);
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("ADS1X15_LIB_VERSION: ");
Serial.println(ADS1X15_LIB_VERSION);
ADS.begin();
ADS.setGain(0); // 6.144 volt
ADS.setDataRate(7); // fast
ADS.setMode(1); // continuous mode
ADS.readADC(0); // first read to trigger
// set the thresholds to Trigger RDY pin
ADS.setComparatorThresholdLow(0x0000);
ADS.setComparatorThresholdHigh(0x0200);
ADS.setComparatorQueConvert(0); // enable RDY pin !!
ADS.setComparatorLatch(0);
}
void loop()
{
ADS.setGain(0);
int16_t val_0 = ADS.readADC(0);
float f = ADS.toVoltage(1); // voltage factor
Serial.print("\tAnalog0: ");
Serial.print(val_0);
Serial.print('\t');
Serial.println(val_0 * f, 3);
delay(1000);
}
// -- END OF FILE --

View File

@ -0,0 +1,50 @@
//
// FILE: ADS_read_async.ino
// AUTHOR: Rob.Tillaart
// VERSION: 0.1.1
// PURPOSE: read analog inputs - asynchronous
//
// test
// connect 1 potmeter per port.
//
// GND ---[ x ]------ 5V
// |
//
// measure at x (connect to AIN0).
//
#include "ADS1X15.h"
ADS1115 ADS(0x48);
float f = 0;
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("ADS1X15_LIB_VERSION: ");
Serial.println(ADS1X15_LIB_VERSION);
ADS.begin();
ADS.setGain(0);
f = ADS.toVoltage(); // voltage factor
ADS.requestADC(0);
}
void loop()
{
if (ADS.isBusy() == false)
{
int16_t val_0 = ADS.getValue();
ADS.requestADC(0); // request a new one
Serial.print("\tAnalog0: ");
Serial.print(val_0);
Serial.print('\t');
Serial.println(val_0 * f, 3);
}
// simulate other tasks...
delay(2000);
}
// -- END OF FILE --

View File

@ -0,0 +1,62 @@
//
// FILE: ADS_read_async_rdy.ino
// AUTHOR: Rob.Tillaart
// VERSION: 0.1.1
// PURPOSE: read analog inputs - straightforward.
//
// test
// connect 1 potmeter per port.
//
// GND ---[ x ]------ 5V
// |
//
// measure at x (connect to AIN0).
//
// EXPERIMENTAL
//
// The RDY pin (or ALERT Pin) is triggered when conversion is ready
//
#include "ADS1X15.h"
ADS1115 ADS(0x48);
float f = 0;
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("ADS1X15_LIB_VERSION: ");
Serial.println(ADS1X15_LIB_VERSION);
ADS.begin();
ADS.setGain(0); // 6.144 volt
ADS.setDataRate(0); // slow so the led blinks visible for the eye.
f = ADS.toVoltage(); // voltage factor
ADS.requestADC(0);
// set the thresholds to Trigger RDY pin
ADS.setComparatorThresholdLow(0x0000);
ADS.setComparatorThresholdHigh(0x0200);
ADS.setComparatorQueConvert(0); // enable RDY pin !!
ADS.setComparatorLatch(0);
}
void loop()
{
if (ADS.isReady())
{
int16_t val_0 = ADS.getValue();
ADS.requestADC(0); // request a new one
Serial.print("\tAnalog0: ");
Serial.print(val_0);
Serial.print('\t');
Serial.println(val_0 * f, 3);
}
// simulate other tasks...
delay(2000);
}
// -- END OF FILE --

View File

@ -0,0 +1,82 @@
//
// FILE: ADS_read_comparator_1.ino
// AUTHOR: Rob.Tillaart
// VERSION: 0.1.0
// PURPOSE: read analog inputs - straightforward.
//
// test
// connect 1 potmeter per port.
//
// GND ---[ x ]------ 5V
// |
//
// measure at x (connect to AIN0).
//
//
// GND ---[LED]---[ALERT_PIN]---[ R ]--- 5V
//
// Connect a LED (+ resistor) to ALERT PIN
// and see it trigger at configured way by the comparator.
//
#include "ADS1X15.h"
ADS1115 ADS(0x48);
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("ADS1X15_LIB_VERSION: ");
Serial.println(ADS1X15_LIB_VERSION);
ADS.begin();
// change if needed.
ADS.setComparatorMode(1); // 0 = TRADITIONAL 1 = WINDOW
ADS.setComparatorPolarity(0); // 0 = LOW (default) 1 = HIGH
// note NON-LATCH gives only a short pulse
ADS.setComparatorLatch(1); // 0 = NON LATCH 1 = LATCH
ADS.setComparatorQueConvert(0); // 0 = trigger alert after 1 conversion
// set the thresholds as a number...
// ADS.setComparatorThresholdLow(5000); // change if needed
// ADS.setComparatorThresholdHigh(20000); // change if needed
// set the threshold as a voltage by using the voltage factor.
float f = ADS.toVoltage(1); // voltage factor
ADS.setComparatorThresholdLow(1.234 / f); // convert volts to number needed
ADS.setComparatorThresholdHigh(3.142 / f); // convert volts to number needed
Serial.println(ADS.getComparatorThresholdLow());
Serial.println(ADS.getComparatorThresholdHigh());
}
void loop()
{
ADS.setGain(0);
int16_t val_0 = ADS.readADC(0);
float f = ADS.toVoltage(1); // voltage factor
Serial.print("\tAnalog0: ");
Serial.print(val_0);
Serial.print('\t');
Serial.print(val_0 * f, 3);
Serial.print('\t');
Serial.print(ADS.getComparatorThresholdLow() * f, 3);
Serial.print('\t');
Serial.print(ADS.getComparatorThresholdHigh() * f, 3);
Serial.println();
delay(100);
}
// -- END OF FILE --

View File

@ -0,0 +1,62 @@
# Syntax Coloring Map For ADS1X15
# Datatypes (KEYWORD1)
ADS1X13 KEYWORD1
ADS1014 KEYWORD1
ADS1015 KEYWORD1
ADS1015 KEYWORD1
ADS1113 KEYWORD1
ADS1114 KEYWORD1
ADS1115 KEYWORD1
# Methods and Functions (KEYWORD2)
begin KEYWORD2
isBusy KEYWORD2
isReady() KEYWORD2
isConnected KEYWORD2
setGain KEYWORD2
getGain KEYWORD2
toVoltage KEYWORD2
getMaxVoltage KEYWORD2
setMode KEYWORD2
getMode KEYWORD2
setDataRate KEYWORD2
getDataRate KEYWORD2
readADC KEYWORD2
readADC_Differential_0_1 KEYWORD2
readADC_Differential_0_3 KEYWORD2
readADC_Differential_1_3 KEYWORD2
readADC_Differential_2_3 KEYWORD2
getValue KEYWORD2
setComparatorMode KEYWORD2
getComparatorMode KEYWORD2
setComparatorPolarity KEYWORD2
getComparatorPolarity KEYWORD2
setComparatorLatch KEYWORD2
getComparatorLatch KEYWORD2
setComparatorQueConvert KEYWORD2
getComparatorQueConvert KEYWORD2
setComparatorThresholdLow KEYWORD2
getComparatorThresholdLow KEYWORD2
setComparatorThresholdHigh KEYWORD2
getComparatorThresholdHigh KEYWORD2
getError KEYWORD2
# ASYNC INTERFACE
requestADC KEYWORD2
requestADC_Differential_0_1 KEYWORD2
requestADC_Differential_0_3 KEYWORD2
requestADC_Differential_1_3 KEYWORD2
requestADC_Differential_2_3 KEYWORD2
# Constants (LITERAL1)
ADS1X15_LIB_VERSION LITERAL1
ADS1X15_INVALID_VOLTAGE LITERAL1
ADS1X15_INVALID_GAIN LITERAL1

View File

@ -0,0 +1,21 @@
{
"name": "ADS1X15",
"keywords": "ADS1013, ADS1014, ADS1015, ADS1113, ADS1114, ADS1115, I2C, ADC",
"description": "Arduino library for ADS1015 - I2C 12 bit ADC and ADS1115 I2C 16 bit ADC",
"authors":
[
{
"name": "Rob Tillaart",
"email": "Rob.Tillaart@gmail.com",
"maintainer": true
}
],
"repository":
{
"type": "git",
"url": "https://github.com/RobTillaart/ADS1X15"
},
"version":"0.2.7",
"frameworks": "*",
"platforms": "*"
}

View File

@ -0,0 +1,11 @@
name=ADS1X15
version=0.2.7
author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=Arduino library for ADS1015 - I2C 12 bit ADC and ADS1115 I2C 16 bit ADC
paragraph=Should work for ADS1013, ADS1014, ADS1113 and ADS1114
category=Sensors
url=https://github.com/RobTillaart/ADS1X15
architectures=*
includes=ADS1X15.h
depends=

View File

@ -0,0 +1,81 @@
//
// FILE: unit_test_001.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// DATE: 2020-12-03
// PURPOSE: unit tests for the SHT31 temperature and humidity sensor
// https://github.com/RobTillaart/ADS1X15
//
// supported assertions
// ----------------------------
// assertEqual(expected, actual)
// assertNotEqual(expected, actual)
// assertLess(expected, actual)
// assertMore(expected, actual)
// assertLessOrEqual(expected, actual)
// assertMoreOrEqual(expected, actual)
// assertTrue(actual)
// assertFalse(actual)
// assertNull(actual)
#include <ArduinoUnitTests.h>
#include "Arduino.h"
#include "ADS1X15.h"
unittest_setup()
{
}
unittest_teardown()
{
}
unittest(test_begin)
{
ADS1115 ADS(0x48);
assertTrue(ADS.begin());
assertTrue(ADS.isConnected());
assertTrue(ADS.isBusy());
}
unittest(test_gain)
{
ADS1115 ADS(0x48);
assertTrue(ADS.begin());
assertEqual(0, ADS.getGain());
int gains[6] = { 0,1,2,4,8,16 };
for (int i = 0; i < 6; i++)
{
ADS.setGain(gains[i]);
assertEqual(gains[i], ADS.getGain());
}
ADS.setGain(42);
assertEqual(0, ADS.getGain());
}
unittest(test_Voltage)
{
ADS1115 ADS(0x48);
assertTrue(ADS.begin());
// should test all values?
ADS.setGain(0);
float volts = ADS.getMaxVoltage();
float delta = abs(6.144 - volts);
assertMoreOrEqual(0.001, delta);
ADS.setGain(16);
volts = ADS.getMaxVoltage();
delta = abs(0.256 - volts);
assertMoreOrEqual(0.001, delta);
}
unittest_main()
// --------

View File

@ -0,0 +1,7 @@
compile:
# Choosing to run compilation tests on 2 different Arduino platforms
platforms:
- uno
- leonardo
- due
- zero

View File

@ -0,0 +1,13 @@
---
name: Arduino CI
on: [push, pull_request]
jobs:
arduino_ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: Arduino-CI/action@master
# Arduino-CI/action@v0.1.1

View File

@ -0,0 +1,477 @@
//
// FILE: ADT7470.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.1.2
// PURPOSE: Arduino library for I2C ADT7470 Fan Monitoring
// URL: https://github.com/RobTillaart/ADT7470
// http://forum.arduino.cc/index.php?topic=363218.0
//
// HISTORY:
// 0.0.00 2015-12-02 initial version
// 0.0.01 2015-12-03 first beta
// 0.1.0 2020-07-15 major refactor - first public version
// 0.1.1 2020-08 fixes after testing
// 0.1.2 2020-12-09 arduino-ci
#include "ADT7470.h"
// CONFIG REGISTER 1
#define ADT7470_CONFIG_REGISTER_1 0x40
// bits
#define ADT7470_START 0x01
#define ADT7470_TODIS 0x08
#define ADT7470_LOCK 0x10
#define ADT7470_FAST_TACH 0x20
#define ADT7470_LOW_FREQ_DRIVE 0x40
#define ADT7470_T05_STB 0x80 // full speed pin
// CONFIG REGISTER 2
#define ADT7470_CONFIG_REGISTER_2 0x74
// bits
#define ADT7470_POWERDOWN 0x01
// OTHER REGISTERS
#define ADT7470_DEVICEID_REGISTER 0x3D
#define ADT7470_COMPANYID_REGISTER 0x3E
#define ADT7470_REVISION_REGISTER 0x3F
// TEMPERATURE
#define ADT7470_TEMP_BASE 0x20
#define ADT7470_TEMP_MAX 0x78
#define ADT7470_TEMP_LIMIT_BASE 0x44
// SPEED
#define ADT7470_TACH_BASE 0x2A
#define ADT7470_TACH_LOW_LIMIT_BASE 0x58
#define ADT7470_TACH_HIGH_LIMIT_BASE 0x60
#define ADT7470_FAN_PPR_REGISTER 0x43
#define ADT7470_FAN_PWM_BASE 0x32
// Page 21 invert PWM signal
#define ADT7470_FAN_PWM_CONFIG_1 0x68
#define ADT7470_FAN_PWM_CONFIG_2 0x69
// INTERRUPTS
#define ADT7470_IRQ_STATUS_1 0x41
#define ADT7470_IRQ_STATUS_2 0x42
#define ADT7470_IRQ_MASK_REG_1 0x72
#define ADT7470_IRQ_MASK_REG_2 0x73
//////////////////////////////////////////////////////////////////////////////
//
// PUBLIC INTERFACE
//
ADT7470::ADT7470(uint8_t address)
{
// allowed 0x2C, 0x2E, 0x2F
_address = address;
}
#if defined (ESP8266) || defined(ESP32)
void ADT7470::begin(uint8_t sda, uint8_t scl)
{
Wire.begin(sda, scl);
}
#endif
void ADT7470::begin()
{
Wire.begin();
}
//
// GENERIC
//
boolean ADT7470::isConnected()
{
return ((getDeviceID() == 0x70) && (getCompanyID() == 0x41));
}
uint8_t ADT7470::getRevision()
{
return getReg8(ADT7470_REVISION_REGISTER);
}
uint8_t ADT7470::getDeviceID()
{
return getReg8(ADT7470_DEVICEID_REGISTER);
}
uint8_t ADT7470::getCompanyID()
{
return getReg8(ADT7470_COMPANYID_REGISTER);
}
void ADT7470::startMonitoring()
{
setRegMask(ADT7470_CONFIG_REGISTER_1, ADT7470_START);
}
void ADT7470::stopMonitoring()
{
clrRegMask(ADT7470_CONFIG_REGISTER_1, ADT7470_START);
}
void ADT7470::powerDown()
{
setRegMask(ADT7470_CONFIG_REGISTER_2, ADT7470_POWERDOWN);
}
void ADT7470::powerUp()
{
clrRegMask(ADT7470_CONFIG_REGISTER_2, ADT7470_POWERDOWN);
}
//
// MEASURE TEMPERATURE
//
// TODO:
// make these calls async as waiting up to 2 seconds is an 'eternity'
// void startConversion();
// bool conversionReady();
// int8_t getTemperature(uin8_t idx);
int8_t ADT7470::getTemperature(uint8_t idx)
{
if (idx >= 10) return 0;
// 1. Set Register 40 Bit[7] = 1. This starts the temperature measurements.
setRegMask(ADT7470_CONFIG_REGISTER_1, ADT7470_T05_STB);
// 2. Wait 200 ms for each TMP05/TMP06 in the loop.
delay(2000); // way to long
// 3. Set Register 40 Bit[7] = 0.
clrRegMask(ADT7470_CONFIG_REGISTER_1, ADT7470_T05_STB);
// 4. Read the temperature registers.
return (int8_t) getReg8(ADT7470_TEMP_BASE + idx);
}
int8_t ADT7470::getMaxTemperature()
{
return (int8_t)getReg8(ADT7470_TEMP_MAX);
}
bool ADT7470::setTemperatureLimit(uint8_t idx, int8_t low, int8_t high)
{
if ((idx >= 10) || (low >= high)) return false;
setReg8(ADT7470_TEMP_LIMIT_BASE + idx * 2, low);
setReg8(ADT7470_TEMP_LIMIT_BASE + idx * 2 + 1, high);
return true;
}
int8_t ADT7470::getTemperatureLowLimit(uint8_t idx)
{
int8_t rv = getReg8(ADT7470_TEMP_LIMIT_BASE + 2 * idx);
return rv;
}
int8_t ADT7470::getTemperatureHighLimit(uint8_t idx)
{
int8_t rv = getReg8(ADT7470_TEMP_LIMIT_BASE + 2 * idx + 1);
return rv;
}
//
// SET FAN SPEED
//
bool ADT7470::setPWM(uint8_t idx, uint8_t val)
{
if (idx >= 4) return false;
setReg8(ADT7470_FAN_PWM_BASE + idx, val);
return true;
}
uint8_t ADT7470::getPWM(uint8_t idx)
{
if (idx >= 4) return 0;
return getReg8(ADT7470_FAN_PWM_BASE + idx);
}
bool ADT7470::setFanLowFreq(uint8_t val)
{
if (val > 7) return false;
setRegMask(ADT7470_CONFIG_REGISTER_1, ADT7470_LOW_FREQ_DRIVE);
// make sure all bits are 0.
// Can be more efficient check previous value.
// getReg8()
// if equal co change
// else clr bits & set
// write them
clrRegMask(ADT7470_CONFIG_REGISTER_2, (0x07 << 4)); // clr 3 bits
setRegMask(ADT7470_CONFIG_REGISTER_2, (val << 4));
return true;
}
bool ADT7470::setFanHighFreq(uint8_t val)
{
if (val > 7) return false;
clrRegMask(ADT7470_CONFIG_REGISTER_1, ADT7470_LOW_FREQ_DRIVE);
// make sure all bits are 0 first. // Can be more efficient check previous value.
clrRegMask(ADT7470_CONFIG_REGISTER_2, (0x07 << 4)); // clr 3 bits
setRegMask(ADT7470_CONFIG_REGISTER_2, (val << 4));
return true;
}
void ADT7470::setInvertPWM(uint8_t idx)
{
if (idx == 0) setReg8(ADT7470_FAN_PWM_CONFIG_1, 0x10); // bit 5
if (idx == 1) setReg8(ADT7470_FAN_PWM_CONFIG_1, 0x80); // bit 4
if (idx == 2) setReg8(ADT7470_FAN_PWM_CONFIG_2, 0x10);
if (idx == 3) setReg8(ADT7470_FAN_PWM_CONFIG_2, 0x80);
}
uint8_t ADT7470::getInvertPWM(uint8_t idx)
{
if (idx == 0) return getReg8(ADT7470_FAN_PWM_CONFIG_1) & 0x10;
if (idx == 1) return getReg8(ADT7470_FAN_PWM_CONFIG_1) & 0x80;
if (idx == 2) return getReg8(ADT7470_FAN_PWM_CONFIG_2) & 0x10;
if (idx == 3) return getReg8(ADT7470_FAN_PWM_CONFIG_2) & 0x80;
return 0;
}
//
// MEASURE FAN SPEED
//
bool ADT7470::setPulsesPerRevolution(uint8_t idx, uint8_t val)
{
if (idx >= 4) return false;
if ((val == 0) || (val > 4)) return false;
uint8_t mask = 0x03 << (idx * 2);
uint8_t reg;
_read(ADT7470_FAN_PPR_REGISTER, &reg);
reg &= ~mask;
reg |= ((val - 1) << (idx * 2));
_write(ADT7470_FAN_PPR_REGISTER, reg);
return true;
}
uint8_t ADT7470::getPulsesPerRevolution(uint8_t idx)
{
if (idx >= 4) return 0;
uint8_t reg = getReg8(ADT7470_FAN_PPR_REGISTER);
return ((reg >> (idx * 2)) & 0x03) + 1;
}
void ADT7470::setFastTach()
{
setRegMask(ADT7470_CONFIG_REGISTER_1, ADT7470_FAST_TACH);
}
void ADT7470::setSlowTach()
{
clrRegMask(ADT7470_CONFIG_REGISTER_1, ADT7470_FAST_TACH);
}
uint16_t ADT7470::getTach(uint8_t idx)
{
if (idx >= 4) return 0;
return getReg16(ADT7470_TACH_BASE + idx * 2);
}
uint32_t ADT7470::getRPM(uint8_t idx)
{
if (idx >= 4) return 0;
uint32_t clock = 90000UL;
uint16_t measurementsPerMinute = 60;
uint16_t tach = getTach(idx);
// P23 stalling tach or very slow < 100 ==> 0xFFFF
if (tach == 0xFFFF) return 0;
if (tach == 0) return 0; // explicit prevents divide by zero
// formula P24 // tach/2 == integer rounding of division.
return (clock * measurementsPerMinute + tach/2) / tach;
}
//////////////////////////////////////////////////////////////////////////////
bool ADT7470::setTachLimits(uint8_t idx, uint16_t low, uint16_t high)
{
if ((idx >= 4) || (low >= high)) return false;
setReg16(ADT7470_TACH_LOW_LIMIT_BASE + idx * 2, low);
setReg16(ADT7470_TACH_HIGH_LIMIT_BASE + idx * 2, high);
return true;
}
uint16_t ADT7470::getTachLowLimits(uint8_t idx)
{
uint16_t rv = getReg16(ADT7470_TACH_LOW_LIMIT_BASE + idx * 2);
return rv;
}
uint16_t ADT7470::getTachHighLimits(uint8_t idx)
{
uint16_t rv = getReg16(ADT7470_TACH_HIGH_LIMIT_BASE + idx * 2);
return rv;
}
//
// INTERRUPTS
//
uint16_t ADT7470::getTemperatureIRQstatus()
{
// TODO - NORM bit not handled
// uint8_t NORM = (getReg8(ADT7470_IRQ_STATUS_2) & 0x08) >> 3;
uint16_t val = 0;
val = (getReg8(ADT7470_IRQ_STATUS_2) & 0x07) << 7;
val |= (getReg8(ADT7470_IRQ_STATUS_1) & 0x7F);
return val;
}
uint8_t ADT7470::getFanIRQstatus()
{
uint8_t val = (getReg8(ADT7470_IRQ_STATUS_2) & 0xF0) >> 4;
return val;
}
// TODO MERGE? setTemperatureIRQMask(idx, val); ?
void ADT7470::setTemperatureIRQMask(uint8_t idx)
{
uint8_t reg = ADT7470_IRQ_MASK_REG_1;
if (idx > 7)
{
reg = ADT7470_IRQ_MASK_REG_2;
idx -= 7;
}
uint8_t val = getReg8(reg);
val |= (1 << idx);
setReg8(reg, val);
}
void ADT7470::clrTemperatureIRQMask(uint8_t idx)
{
uint8_t reg = ADT7470_IRQ_MASK_REG_1;
if (idx > 7)
{
reg = ADT7470_IRQ_MASK_REG_2;
idx -= 7;
}
uint8_t val = getReg8(reg);
val &= ~(1 << idx);
setReg8(reg, val);
}
uint8_t ADT7470::getTemperatureIRQMask(uint8_t idx)
{
uint8_t reg = ADT7470_IRQ_MASK_REG_1;
if (idx > 7)
{
reg = ADT7470_IRQ_MASK_REG_2;
idx -= 7;
}
return getReg8(reg) & (1 << idx);
}
void ADT7470::setFanIRQMask(uint8_t idx)
{
uint8_t val = getReg8(ADT7470_IRQ_MASK_REG_2);
val |= (1 << (idx + 4));
setReg8(ADT7470_IRQ_MASK_REG_2, val);
}
void ADT7470::clrFanIRQMask(uint8_t idx)
{
uint8_t val = getReg8(ADT7470_IRQ_MASK_REG_2);
val &= ~(1 << (idx + 4));
setReg8(ADT7470_IRQ_MASK_REG_2, val);
}
uint8_t ADT7470::getFanIRQMask(uint8_t idx)
{
return getReg8(ADT7470_IRQ_MASK_REG_2) & (1 << (idx + 4));
}
//////////////////////////////////////////////////////////////////////////////
//
// REGISTER OPERATORS
//
void ADT7470::setRegMask(uint8_t reg, uint8_t mask)
{
uint8_t t;
_read(reg, &t, (uint8_t)1);
t |= mask;
_write(reg, t);
}
void ADT7470::clrRegMask(uint8_t reg, uint8_t mask)
{
uint8_t t;
_read(reg, &t, (uint8_t)1);
t &= ~mask;
_write(reg, t);
}
uint8_t ADT7470::getReg8(uint8_t reg)
{
uint8_t val;
_read(reg, &val);
return val;
}
void ADT7470::setReg8(uint8_t reg, uint8_t val)
{
_write(reg, val);
}
uint16_t ADT7470::getReg16(uint8_t reg)
{
uint8_t h, l;
_read(reg, &l);
_read(reg + 1, &h);
return (((uint16_t)h) << 8) | l;
}
void ADT7470::setReg16(uint8_t reg, uint16_t val)
{
_write(reg + 1, val & 0xFF);
_write(reg, val >> 8);
}
//////////////////////////////////////////////////////////////////////////////
//
// PRIVATE - LOW LEVEL I2C
//
int ADT7470::_write(const uint8_t reg, uint8_t value)
{
return _write(reg, &value, 1);
}
int ADT7470::_write(const uint8_t reg, uint8_t *buffer, uint8_t length)
{
Wire.beginTransmission(_address);
Wire.write(reg);
for (uint8_t i = 0; i < length; i++) Wire.write(buffer[i]);
int rv = Wire.endTransmission();
return rv;
}
int ADT7470::_read(const uint8_t reg, uint8_t *value)
{
return _read(reg, value, 1);
}
int ADT7470::_read(const uint8_t reg, uint8_t *buffer, uint8_t length)
{
Wire.beginTransmission(_address);
Wire.write(reg);
int rv = Wire.endTransmission(false);
if (rv != 0) return 0; // nothing read
uint8_t len = Wire.requestFrom(_address, length);
uint8_t cnt = 0;
uint32_t before = millis();
while ((cnt < len) && ((millis() - before) < ADT7470_TIMEOUT))
{
if (Wire.available()) buffer[cnt++] = Wire.read();
}
return cnt;
}
// -- END OF FILE --

127
libraries/ADT7470/ADT7470.h Normal file
View File

@ -0,0 +1,127 @@
#pragma once
//
// FILE: ADT7470.h
// AUTHOR: Rob Tillaart
// VERSION: 0.1.2
// PURPOSE: Arduino library for I2C ADT7470 Fan Monitoring
// URL: https://github.com/RobTillaart/ADT7470
// http://forum.arduino.cc/index.php?topic=363218.0
//
// HISTORY:
// see ADT7470.cpp file
#include "Arduino.h"
#include "Wire.h"
#define ADT7470_LIB_VERSION "0.1.1"
#ifndef ADT7470_TIMEOUT
#define ADT7470_TIMEOUT 1000
#endif
#ifndef ADT7470_ADDR_HIGH
#define ADT7470_ADDR_HIGH 0x2F
#endif
#ifndef ADT7470_ADDR_LOW
#define ADT7470_ADDR_LOW 0x2C
#endif
#ifndef ADT7470_ADDR_FLOAT
#define ADT7470_ADDR_FLOAT 0x2E
#endif
class ADT7470
{
public:
ADT7470(uint8_t address);
#if defined (ESP8266) || defined(ESP32)
void begin(uint8_t sda, uint8_t scl);
#endif
void begin();
// GENERIC
boolean isConnected();
uint8_t getRevision();
uint8_t getDeviceID(); // should return 0x70
uint8_t getCompanyID(); // should return 0x41
void startMonitoring();
void stopMonitoring();
void powerDown();
void powerUp();
// MEASURE TEMPERATURE - not tested
// Page 13 daisy chained specific TMP05 / TMP06 sensors
int8_t getTemperature(uint8_t idx);
int8_t getMaxTemperature();
// Page 16
bool setTemperatureLimit(uint8_t idx, int8_t low, int8_t high);
int8_t getTemperatureLowLimit(uint8_t idx);
int8_t getTemperatureHighLimit(uint8_t idx);
// SET FAN SPEED
// Page 25 idx = 0..3
bool setPWM(uint8_t idx, uint8_t val);
uint8_t getPWM(uint8_t idx);
// Page 36 val = 0..7
bool setFanLowFreq(uint8_t val = 0); // PWM freq..
bool setFanHighFreq(uint8_t val = 0);
// Page 21
void setInvertPWM(uint8_t idx);
uint8_t getInvertPWM(uint8_t idx);
// MEASURE FAN SPEED
// Page 23-24 idx = 0..3 val = 1..4
bool setPulsesPerRevolution(uint8_t idx, uint8_t val);
// returns 1..4
uint8_t getPulsesPerRevolution(uint8_t idx);
// P30 FST_TCH fast = measurement/250 ms slow = measurement/1000ms.
void setFastTach();
void setSlowTach();
uint16_t getTach(uint8_t idx);
uint32_t getRPM(uint8_t idx);
// Page 16 TACH/FAN ALARM
bool setTachLimits(uint8_t idx, uint16_t low, uint16_t high);
uint16_t getTachLowLimits(uint8_t idx);
uint16_t getTachHighLimits(uint8_t idx);
// INTERRUPTS
// Page 17 - every bit set 0..10 represents one temp limit (high /low) exceeded
// if it return 0 => no limits exceeded.
uint16_t getTemperatureIRQstatus();
uint8_t getFanIRQstatus();
// idx = 0..9
void setTemperatureIRQMask(uint8_t idx);
void clrTemperatureIRQMask(uint8_t idx);
uint8_t getTemperatureIRQMask(uint8_t idx);
// idx = 0..3
void setFanIRQMask(uint8_t idx);
void clrFanIRQMask(uint8_t idx);
uint8_t getFanIRQMask(uint8_t idx);
// REGISTER OPERATORS
// set/clr one or more bits.
void setRegMask(uint8_t reg, uint8_t mask);
void clrRegMask(uint8_t reg, uint8_t mask);
// low level register interface
uint8_t getReg8(uint8_t reg);
void setReg8(uint8_t reg, uint8_t val);
uint16_t getReg16(uint8_t reg);
void setReg16(uint8_t reg, uint16_t val);
private:
// LOW LEVEL I2C
int _write(const uint8_t reg, uint8_t value);
int _write(const uint8_t reg, uint8_t *buffer, uint8_t length);
int _read(const uint8_t reg, uint8_t *value);
int _read(const uint8_t reg, uint8_t *buffer, uint8_t length);
uint8_t _address = 0;
};
// -- END OF FILE --

21
libraries/ADT7470/LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2015-2021 Rob Tillaart
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

107
libraries/ADT7470/README.md Normal file
View File

@ -0,0 +1,107 @@
[![Arduino CI](https://github.com/RobTillaart/ADT7470/workflows/Arduino%20CI/badge.svg)](https://github.com/marketplace/actions/arduino_ci)
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/RobTillaart/ADT7470/blob/master/LICENSE)
[![GitHub release](https://img.shields.io/github/release/RobTillaart/ADT7470.svg?maxAge=3600)](https://github.com/RobTillaart/ADT7470/releases)
# ADT7470 Library
Arduino library for I2C ADT7470 Fan Monitoring
## Description
The ADT7470 Fan Monitoring library offers an I2C device that can
monitor and control up to four fans. Further this module can daisy
chain up to 10 (specific TMP05/06) temperature sensors.
Please read datasheet carefully before working with the module.
**Experimental**
This library was build in 2015 from datasheet (PDF) on request and
is never tested by me. So it is experimental at best and if you have the
hardware and are able to try this library I would really appreciate it
as it is a quite unique module.
That said the library is supporting setting the fan speed and measure
the RPM, so it should be usable e.g. for a climate controlled room or
cabinet.
**Warning**
Do not forget to put a diode over the Fan to prevent damage due to
inductive pulse when switched off.
## ADT7470 Address Select Mode
(from datasheet)
| Pin 11 | (ADDR) State | Address |
|:----:|:----:|:----:|
| High (10 kΩ to VCC) | 010 1111 (0x5E left-justified or 0x2F right-justified) |
| Low (10 kΩ to GND) | 010 1100 (0x58 left-justified or 0x2C right-justified) |
| Floating (no pull-up) | 010 1110 (0x5C left-justified or 0x2E right-justified) |
## Interface
The interface consists of:
- **ADT7470()** constructor
- **begin()** initialize the I2C bus
- **isConnected()** check if the module is connected to the I2C bus
- **getRevision()** version of the firmware
- **getDeviceID()** should return 0x70
- **getCompanyID()** should return 0x41
- **startMonitoring()**
- **stopMonitoring()**
- **powerDown()** energy save mode
- **powerUp()** active mode
- **getTemperature(idx)** idx = 0..9; if connected it returns the temperature
of sensor idx. Temperature sensors are daisy chaned.
- **getMaxTemperature()** get max temperature of connected temperature sensors.
- **setTemperatureLimit(idx, low, high)** for ALARM function
- **getTemperatureLowLimit(idx)**
- **getTemperatureHighLimit(idx)**
- **setPWM(idx, val)** set the speed of the fan at idx
- **getPWM(idx)** read back the speed set.
- **setFanLowFreq(val = 0)**
- **setFanHighFreq(val = 0)**
- **setInvertPWM(idx)**
- **getInvertPWM(idx)**
- **setPulsesPerRevolution(idx, val)** val should be 1..4 as a fan gives 1..4 pulses per revolution.
This valus is needed to calculate a correct tach and RPM.
- **getPulsesPerRevolution(idx)** read back PulsePerRevolution. returns 1..4.
- **setFastTach()** Tach register is updated 4x per second.
- **setSlowTach()** Tach register is updated 1x per second.
- **getTach(idx)** get the raw pulses.
- **getRPM(idx)** get a Revolutions Per Minute, based upon **getTach()**
- **setTachLimits(idx, low, high)**
- **getTachLowLimits(idx)**
- **getTachHighLimits(idx)**
- **getTemperatureIRQstatus()**
- **setTemperatureIRQMask(idx)**
- **clrTemperatureIRQMask(idx)**
- **getTemperatureIRQMask(idx)**
- **getFanIRQstatus()**
- **setFanIRQMask(idx)**
- **clrFanIRQMask(idx)**
- **getFanIRQMask(idx)**
The descriptions are short and need to be extended.
## Todo / investigate / not implemented yet
- get the hardware to test
- change pins from PWM to digital IO
- temperature sensors (functions are prepared)
- How to connect temp sensors (daisy chained)
https://ez.analog.com/temperature_sensors/f/discussions/77540/adt7470-and-tmp05-daisy-chain-temeparure-sensing
- FULLSPEED pin, must it be in the library?
software version ==> fullspeed(idx)
- automode
- improve documentation, readme.md file.
- ...
## Operation
See examples

Binary file not shown.

View File

@ -0,0 +1,222 @@
//
// FILE: adt7470_demo.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.1
// PURPOSE: demo ADT7470 library
// DATE: 2015-12-02
#include <Wire.h>
#include "ADT7470.h"
ADT7470 ADT(ADT7470_ADDR_FLOAT);
void setup()
{
Wire.begin();
Serial.begin(115200);
Serial.print(F("\n\nStart "));
Serial.println(__FILE__);
Serial.println();
if (!ADT.isConnected())
{
Serial.println("Cannot connect ADT7470...\n");
// while(1);
}
// else
{
testStart();
testRevision();
testTemp();
testPWM();
testTach();
testFanSpeed();
testStop();
}
Serial.println("Done");
}
void testStart()
{
Serial.println(F("ADT7470 testStart"));
ADT.powerUp();
ADT.startMonitoring();
Serial.println();
Serial.println();
delay(1000);
}
void testRevision()
{
Serial.print("ADT7470_LIB_VERSION:\t");
Serial.println(ADT7470_LIB_VERSION);
Serial.print("ADT7470 getRevision:\t");
Serial.println(ADT.getRevision());
Serial.print("ADT7470 getDeviceID:\t");
Serial.println(ADT.getDeviceID());
Serial.print("ADT7470 getCompanyID:\t");
Serial.println(ADT.getCompanyID());
Serial.println();
Serial.println();
delay(10);
}
void testTemp()
{
Serial.println(F("ADT7470 testTemp 0..9"));
Serial.print("temp:");
for (uint8_t i = 0; i < 10; i++)
{
Serial.print("\t");
Serial.print(ADT.getTemperature(i));
}
Serial.println();
Serial.print("max:\t");
Serial.println(ADT.getMaxTemperature());
Serial.println();
Serial.println();
delay(10);
}
void testPWM()
{
Serial.println(F("ADT7470 getPWM 0..3"));
Serial.print(F("set:"));
for (int i = 0; i < 4; i++)
{
uint8_t pwm = random(255);
ADT.setPWM(i, pwm);
Serial.print("\t");
Serial.print(pwm);
}
Serial.println();
Serial.print(F("get:"));
for (int i = 0; i < 4; i++)
{
uint8_t pwm = ADT.getPWM(i);
Serial.print("\t");
Serial.print(pwm);
}
Serial.println();
Serial.println();
delay(10);
}
void testTach()
{
uint8_t ppr[4];
Serial.println(F("ADT7470 testTach 0..3"));
Serial.print(F("getPPR: "));
for (uint8_t i = 0; i < 4; i++)
{
ppr[i] = ADT.getPulsesPerRevolution(i);
Serial.print("\t");
Serial.print(ppr[i]);
}
Serial.println();
Serial.print(F("setPPR: "));
for (uint8_t i = 0; i < 4; i++)
{
ADT.setPulsesPerRevolution(i, ppr[i]);
bool b = (ppr[i] == ADT.getPulsesPerRevolution(i));
Serial.print("\t");
Serial.print(b ? "T" : "F"); // expect TTTT
}
Serial.println();
ADT.setSlowTach();
Serial.println(F("setSlowTach"));
Serial.print(F("getTach:"));
for (uint8_t i = 0; i < 4; i++)
{
uint16_t tach = ADT.getTach(i);
Serial.print("\t");
Serial.print(tach);
}
Serial.println();
Serial.print(F("getRPM :"));
for (int i = 0; i < 4; i++)
{
uint32_t rpm = ADT.getRPM(i);
Serial.print("\t");
Serial.print(rpm);
}
Serial.println();
ADT.setFastTach();
Serial.println(F("setFastTach"));
Serial.print(F("getTach:"));
for (uint8_t i = 0; i < 4; i++)
{
uint16_t tach = ADT.getTach(i);
Serial.print("\t");
Serial.print(tach);
}
Serial.println();
Serial.print(F("getRPM :"));
for (int i = 0; i < 4; i++)
{
uint32_t rpm = ADT.getRPM(i);
Serial.print("\t");
Serial.print(rpm);
}
Serial.println();
Serial.println();
delay(10);
}
void testFanSpeed()
{
Serial.println(F("ADT7470 testFanSpeed"));
Serial.print("low:\t");
for (uint8_t i = 0; i < 8; i++)
{
ADT.setFanLowFreq(i);
Serial.print(i);
Serial.print("\t");
delay(1000);
}
Serial.println();
Serial.print("high:\t");
for (uint8_t i = 0; i < 8; i++)
{
ADT.setFanHighFreq(i);
Serial.print(i);
Serial.print("\t");
delay(1000);
}
Serial.println();
ADT.setFanHighFreq(2);
Serial.println();
Serial.println();
delay(10);
}
void testStop()
{
Serial.println(F("ADT7470 testStop"));
ADT.stopMonitoring();
ADT.powerDown();
delay(2000);
// TODO how to check if it is down - datasheet.
}
void loop()
{
}
// -- END OF FILE --

View File

@ -0,0 +1,55 @@
# Syntax Coloring Map For ADT7470
# Datatypes (KEYWORD1)
ADT7470 KEYWORD1
# Methods and Functions (KEYWORD2)
isConnected KEYWORD2
getRevision KEYWORD2
getDeviceID KEYWORD2
getCompanyID KEYWORD2
startMonitoring KEYWORD2
stopMonitoring KEYWORD2
powerDown KEYWORD2
powerUp KEYWORD2
getTemperature KEYWORD2
getMaxTemperature KEYWORD2
setTemperatureLimit KEYWORD2
getTemperatureLowLimit KEYWORD2
getTemperatureHighLimit KEYWORD2
setPWM KEYWORD2
getPWM KEYWORD2
setFanLowFreq KEYWORD2
setFanHighFreq KEYWORD2
setInvertPWM KEYWORD2
getInvertPWM KEYWORD2
setPulsesPerRevolution KEYWORD2
getPulsesPerRevolution KEYWORD2
setFastTach KEYWORD2
setSlowTach KEYWORD2
getTach KEYWORD2
getRPM KEYWORD2
setTachLimits KEYWORD2
getTachLowLimits KEYWORD2
getTachHighLimits KEYWORD2
getTemperatureIRQstatus KEYWORD2
setTemperatureIRQMask KEYWORD2
clrTemperatureIRQMask KEYWORD2
getTemperatureIRQMask KEYWORD2
getFanIRQstatus KEYWORD2
setFanIRQMask KEYWORD2
clrFanIRQMask KEYWORD2
getFanIRQMask KEYWORD2
# Constants (LITERAL1)
ADT7470_LIB_VERSION LITERAL1
ADT7470_TIMEOUT LITERAL1
ADT7470_ADDR_HIGH LITERAL1
ADT7470_ADDR_LOW LITERAL1
ADT7470_ADDR_FLOAT LITERAL1

View File

@ -0,0 +1,21 @@
{
"name": "ADT7470",
"keywords": "ADT7470, Fan, monitoring",
"description": "Arduino library for I2C ADT7470 Fan Monitoring.",
"authors":
[
{
"name": "Rob Tillaart",
"email": "Rob.Tillaart@gmail.com",
"maintainer": true
}
],
"repository":
{
"type": "git",
"url": "https://github.com/RobTillaart/ADT7470.git"
},
"version":"0.1.2",
"frameworks": "arduino",
"platforms": "*"
}

View File

@ -0,0 +1,11 @@
name=ADT7470
version=0.1.2
author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=ADT7470 Library
paragraph=Arduino library for I2C ADT7470 Fan Monitoring
category=Device Control
url=https://github.com/RobTillaart/ADT7470
architectures=*
includes=ADT7470.h
depends=

View File

@ -0,0 +1,48 @@
//
// FILE: unit_test_001.cpp
// AUTHOR: Rob Tillaart
// DATE: 2020-12-09
// PURPOSE: unit tests for the ADT7470 Fan Monitoring library
// https://github.com/RobTillaart/ADT7470
// https://github.com/Arduino-CI/arduino_ci/blob/master/REFERENCE.md
//
// supported assertions
// ----------------------------
// assertEqual(expected, actual)
// assertNotEqual(expected, actual)
// assertLess(expected, actual)
// assertMore(expected, actual)
// assertLessOrEqual(expected, actual)
// assertMoreOrEqual(expected, actual)
// assertTrue(actual)
// assertFalse(actual)
// assertNull(actual)
#include <ArduinoUnitTests.h>
#include "ADT7470.h"
unittest_setup()
{
}
unittest_teardown()
{
}
unittest(test_constructor)
{
ADT7470 ADT(0x2C);
ADT.begin();
assertFalse(ADT.isConnected());
}
unittest_main()
// --------

View File

@ -0,0 +1,7 @@
compile:
# Choosing to run compilation tests on 2 different Arduino platforms
platforms:
- uno
- leonardo
- due
- zero

View File

@ -0,0 +1,14 @@
---
name: Arduino CI
on: [push, pull_request]
jobs:
arduino_ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: Arduino-CI/action@master
# Arduino-CI/action@v0.1.1
# Arduino-CI/action@master

View File

@ -1,46 +1,70 @@
//
// FILE: AM232X.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.2.3
// VERSION: 0.2.4
// PURPOSE: AM232X library for AM2320 for Arduino.
//
// HISTORY:
// 0.1.0 2017-12-11 initial version
// 0.1.1 2017-12-12 added CRC checking
// 0.1.2 2017-12-12 get and set functions.
// 0.1.3 2017-12-19 added ESP8266 - issue #86
// tested by Viktor Balint
// 0.1.4 2018-10-24 fixed temperature formula - #114
// thanks to 9a4gl
// 0.1.5 2020-03-25 refactor, add read() to begin()
// 0.2.0 2020-05-03 made temperature + humidity private, add wrapper functions.
// 0.2.1 2020-05-06 fix temperature function (thanks Chade)
// 0.2.2 2020-05-12 added ESP32 support
// 0.2.3 2020-05-27 update library.json
//
// 0.1.0 2017-12-11 initial version
// 0.1.1 2017-12-12 added CRC checking
// 0.1.2 2017-12-12 get and set functions.
// 0.1.3 2017-12-19 added ESP8266 - issue #86
// tested by Viktor Balint
// 0.1.4 2018-10-24 fixed temperature formula - #114
// thanks to 9a4gl
// 0.1.5 2020-03-25 refactor, add read() to begin()
// 0.2.0 2020-05-03 made temperature + humidity private, add wrapper functions.
// 0.2.1 2020-05-06 fix temperature function (thanks Chade)
// 0.2.2 2020-05-12 added ESP32 support
// 0.2.3 2020-05-27 update library.json
// 0.2.4 2020-12-09 arduino-ci
// 0.3.0 2021-01-12 isConnected() + Wire0..Wire5 support
#include <AM232X.h>
#define AM232X_ADDRESS ((uint8_t)0x5C)
#include "AM232X.h"
#define AM232X_ADDRESS ((uint8_t)0x5C)
////////////////////////////////////////////////////////////////////
//
// PUBLIC
//
#if defined (ESP8266) || defined(ESP32)
void AM232X::begin(uint8_t sda, uint8_t scl)
AM232X::AM232X(TwoWire *wire)
{
Wire.begin(sda, scl);
_wire = wire;
}
#if defined (ESP8266) || defined(ESP32)
bool AM232X::begin(uint8_t sda, uint8_t scl)
{
_wire = &Wire;
_wire->begin(sda, scl);
if (! isConnected()) return false;
this->read();
return true;
}
#endif
void AM232X::begin()
bool AM232X::begin()
{
Wire.begin();
_wire->begin();
if (! isConnected()) return false;
this->read();
return true;
}
bool AM232X::isConnected()
{
_wire->beginTransmission(AM232X_ADDRESS);
return ( _wire->endTransmission() == 0);
}
int AM232X::read()
{
// READ HUMIDITY AND TEMPERATURE REGISTERS
@ -58,6 +82,7 @@ int AM232X::read()
return AM232X_OK;
}
int AM232X::getModel()
{
int rv = _readRegister(0x08, 2);
@ -66,6 +91,7 @@ int AM232X::getModel()
return (bits[2] * 256) + bits[3];
}
int AM232X::getVersion()
{
int rv = _readRegister(0x0A, 1);
@ -74,6 +100,7 @@ int AM232X::getVersion()
return bits[2];
}
uint32_t AM232X::getDeviceID()
{
int rv = _readRegister(0x0B, 4);
@ -85,6 +112,7 @@ uint32_t AM232X::getDeviceID()
return _deviceID;
}
int AM232X::getStatus()
{
int rv = _readRegister(0x0F, 1);
@ -93,6 +121,7 @@ int AM232X::getStatus()
return bits[2];
}
int AM232X::getUserRegisterA()
{
int rv = _readRegister(0x10, 2);
@ -101,6 +130,7 @@ int AM232X::getUserRegisterA()
return (bits[2] * 256) + bits[3];
}
int AM232X::getUserRegisterB()
{
int rv = _readRegister(0x12, 2);
@ -109,6 +139,7 @@ int AM232X::getUserRegisterB()
return (bits[2] * 256) + bits[3];
}
int AM232X::setStatus(uint8_t value)
{
int rv = _writeRegister(0x0F, 1, value);
@ -117,6 +148,7 @@ int AM232X::setStatus(uint8_t value)
return AM232X_OK;
}
int AM232X::setUserRegisterA(int value)
{
int rv = _writeRegister(0x10, 2, value);
@ -125,6 +157,7 @@ int AM232X::setUserRegisterA(int value)
return AM232X_OK;
}
int AM232X::setUserRegisterB(int value)
{
int rv = _writeRegister(0x12, 2, value);
@ -133,6 +166,7 @@ int AM232X::setUserRegisterB(int value)
return AM232X_OK;
}
////////////////////////////////////////////////////////////////////
//
// PRIVATE
@ -140,25 +174,25 @@ int AM232X::setUserRegisterB(int value)
int AM232X::_readRegister(uint8_t reg, uint8_t count)
{
// wake up the sensor - see 8.2
Wire.beginTransmission(AM232X_ADDRESS);
int rv = Wire.endTransmission();
_wire->beginTransmission(AM232X_ADDRESS);
int rv = _wire->endTransmission();
delayMicroseconds(1000); // TODO tune
// request the data
Wire.beginTransmission(AM232X_ADDRESS);
Wire.write(0x03);
Wire.write(reg);
Wire.write(count);
rv = Wire.endTransmission();
_wire->beginTransmission(AM232X_ADDRESS);
_wire->write(0x03);
_wire->write(reg);
_wire->write(count);
rv = _wire->endTransmission();
if (rv < 0) return rv;
// request 4 extra, 2 for cmd + 2 for CRC
uint8_t length = count + 4;
int bytes = Wire.requestFrom(AM232X_ADDRESS, length);
int bytes = _wire->requestFrom(AM232X_ADDRESS, length);
for (int i = 0; i < bytes; i++)
{
bits[i] = Wire.read();
bits[i] = _wire->read();
}
// ANALYZE ERRORS
// will not detect if we requested 1 byte as that will
@ -186,11 +220,12 @@ int AM232X::_readRegister(uint8_t reg, uint8_t count)
return AM232X_OK;
}
int AM232X::_writeRegister(uint8_t reg, uint8_t cnt, int16_t value)
{
// wake up the sensor - see 8.2
Wire.beginTransmission(AM232X_ADDRESS);
int rv = Wire.endTransmission();
_wire->beginTransmission(AM232X_ADDRESS);
int rv = _wire->endTransmission();
delayMicroseconds(1000); // TODO tune
// prepare data to send
@ -211,24 +246,24 @@ int AM232X::_writeRegister(uint8_t reg, uint8_t cnt, int16_t value)
// send data
uint8_t length = cnt + 3; // 3 = cmd, startReg, #bytes
Wire.beginTransmission(AM232X_ADDRESS);
_wire->beginTransmission(AM232X_ADDRESS);
for (int i=0; i< length; i++)
{
Wire.write(bits[i]);
_wire->write(bits[i]);
}
// send the CRC
uint16_t crc = crc16(bits, length);
Wire.write(crc & 0xFF);
Wire.write(crc >> 8);
_wire->write(crc & 0xFF);
_wire->write(crc >> 8);
rv = Wire.endTransmission();
rv = _wire->endTransmission();
if (rv < 0) return rv;
// wait for the answer
int bytes = Wire.requestFrom(AM232X_ADDRESS, length);
int bytes = _wire->requestFrom(AM232X_ADDRESS, length);
for (int i = 0; i < bytes; i++)
{
bits[i] = Wire.read();
bits[i] = _wire->read();
}
// ANALYZE ERRORS
@ -258,6 +293,7 @@ int AM232X::_writeRegister(uint8_t reg, uint8_t cnt, int16_t value)
return AM232X_OK;
}
uint16_t AM232X::crc16(uint8_t *ptr, uint8_t len)
{
uint16_t crc =0xFFFF;

View File

@ -3,27 +3,41 @@
// FILE: AM232X.h
// AUTHOR: Rob Tillaart
// PURPOSE: AM232X library for Arduino
// VERSION: 0.2.3
// VERSION: 0.3.0
// HISTORY: See AM232X.cpp
// URL: https://github.com/RobTillaart/AM232X
//
#include "Wire.h"
// Bottom view
// +---+
// VDD |o |
// SDA |o |
// GND |o |
// SCL |o |
// +---+
#include "Arduino.h"
#include "Wire.h"
#define AM232X_LIB_VERSION (F("0.3.0"))
#define AM232X_OK 0
#define AM232X_ERROR_UNKNOWN -10
#define AM232X_ERROR_CONNECT -11
#define AM232X_ERROR_FUNCTION -12
#define AM232X_ERROR_ADDRESS -13
#define AM232X_ERROR_REGISTER -14
#define AM232X_ERROR_CRC_1 -15
#define AM232X_ERROR_CRC_2 -16
#define AM232X_ERROR_WRITE_DISABLED -17
#define AM232X_ERROR_WRITE_COUNT -18
#define AM232X_MISSING_BYTES -19
#define AM232X_LIB_VERSION "0.2.3"
#define AM232X_OK 0
#define AM232X_ERROR_UNKNOWN -10
#define AM232X_ERROR_CONNECT -11
#define AM232X_ERROR_FUNCTION -12
#define AM232X_ERROR_ADDRESS -13
#define AM232X_ERROR_REGISTER -14
#define AM232X_ERROR_CRC_1 -15
#define AM232X_ERROR_CRC_2 -16
#define AM232X_ERROR_WRITE_DISABLED -17
#define AM232X_ERROR_WRITE_COUNT -18
#define AM232X_MISSING_BYTES -19
/*
* from datasheet
* 0x80: not support function code
@ -36,36 +50,40 @@
class AM232X
{
public:
#if defined (ESP8266) || defined(ESP32)
void begin(uint8_t sda, uint8_t scl);
#endif
void begin();
explicit AM232X(TwoWire *wire = &Wire);
int read();
int getModel();
int getVersion();
#if defined (ESP8266) || defined(ESP32)
bool begin(uint8_t sda, uint8_t scl);
#endif
bool begin();
bool isConnected();
int read();
int getModel();
int getVersion();
uint32_t getDeviceID();
int getStatus();
int getUserRegisterA();
int getUserRegisterB();
int getStatus();
int getUserRegisterA();
int getUserRegisterB();
int setStatus(uint8_t value);
int setUserRegisterA(int value);
int setUserRegisterB(int value);
int setStatus(uint8_t value);
int setUserRegisterA(int value);
int setUserRegisterB(int value);
inline float getHumidity() { return humidity; };
inline float getHumidity() { return humidity; };
inline float getTemperature() { return temperature; };
private:
uint8_t bits[8];
float humidity;
float temperature;
int _readRegister(uint8_t reg, uint8_t cnt);
int _writeRegister(uint8_t reg, uint8_t cnt, int16_t value);
uint8_t bits[8];
float humidity;
float temperature;
int _readRegister(uint8_t reg, uint8_t cnt);
int _writeRegister(uint8_t reg, uint8_t cnt, int16_t value);
uint16_t crc16(uint8_t *ptr, uint8_t len);
TwoWire* _wire;
};
// -- END OF FILE --

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2017-2020 Rob Tillaart
Copyright (c) 2017-2021 Rob Tillaart
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -1,27 +1,88 @@
[![Arduino CI](https://github.com/RobTillaart/AM232X/workflows/Arduino%20CI/badge.svg)](https://github.com/marketplace/actions/arduino_ci)
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/RobTillaart/AM232X/blob/master/LICENSE)
[![GitHub release](https://img.shields.io/github/release/RobTillaart/AM232X.svg?maxAge=3600)](https://github.com/RobTillaart/AM232X/releases)
# AM232X
Arduino library for AM2320 AM2321 and AM2322 I2C temperature and humidity sensor
## Description
AM232X is a sensor similar to the DHT12 with an I2C interface.
Although in theory this could enable multiple sensors on one bus
the AM232X has a fixed address 0x5C.
the AM232X has a fixed address **0x5C** so
Typical parameters
| | range | accuracy | repeatability
|:-------|:------:|:------:|:------:|
| Temperature | -40 - 80 | 0.5°C | ±0.1 |
| Humidity | 0.0 - 99.9 | 3% | ±0.1 |
| Sample time | 2 seconds | | |
```
// Bottom view
// +---+
// VDD |o |
// SDA |o |
// GND |o |
// SCL |o |
// +---+
```
## Interface
### Constructor
- **AM232X(TwoWire \*wire = &Wire)** constructor, optionally set Wire0..WireN.
- **bool begin(uint8_t sda, uint8_t scl)** for ESP32 alike devices, returns true if device is connected
- **bool begin()** for AVR alike devices, returns true if device is connected
- **bool isConnected()** returns true if device-address is found on I2C bus.
### Base calls
- **int read()** fetches the values from the sensor
- **float getHumidity()** returns the last read humidity
- **float getTemperature()** returns the last read temperature
### Misc
check datasheet for details.
- **int getModel()** idem
- **int getVersion()** idem
- **uint32_t getDeviceID()** idem
- **int getStatus()**
- **int getUserRegisterA()**
- **int getUserRegisterB()**
- **int setStatus(uint8_t value)**
- **int setUserRegisterA(int value)**
- **int setUserRegisterB(int value)**
## Operation
See examples
In setup() you have to call the **begin()** to initialize
the Wire library and do an initial **read()** to fill the
variables temperature and humidity.
To access these values one must use **getTemperature()** and **getHhumidity()**.
To access these values one must use **getTemperature()** and **getHumidity()**.
## Planned changes
Fix several TODO's in the code.
## Warning
The library has several open ends so it is not suitable yet for
any serious application.
See also LICENCE
## Warning
The library has several open ends so use at own risk.
See also LICENSE

Some files were not shown because too many files have changed in this diff Show More