0.3.1 AD985X

This commit is contained in:
rob tillaart 2021-08-25 09:52:59 +02:00
parent 8eb4fbc88f
commit 3caa56c6cd
8 changed files with 227 additions and 91 deletions

View File

@ -4,10 +4,14 @@ name: Arduino CI
on: [push, pull_request]
jobs:
arduino_ci:
runTest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: Arduino-CI/action@master
# Arduino-CI/action@v0.1.1
- uses: ruby/setup-ruby@v1
with:
ruby-version: 2.6
- run: |
gem install arduino_ci
arduino_ci.rb

View File

@ -1,7 +1,7 @@
//
// FILE: AD985X.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.3.0
// VERSION: 0.3.1
// DATE: 2019-02-08
// PURPOSE: Class for AD9850 and AD9851 function generator
//
@ -16,6 +16,9 @@
// inverted SELECT line as preparation for multidevice.
// 0.3.0 2021-06-06 fix factory bit mask + new examples + some refactor
// added multi device document
// 0.3.1 2021-08-25 VSPI / HSPI support for ESP32
// faster software SPI
// minor optimizations / refactor
#include "AD985X.h"
@ -42,47 +45,82 @@ AD9850::AD9850()
void AD9850::begin(uint8_t select, uint8_t resetPin, uint8_t FQUDPin, uint8_t dataOut , uint8_t clock)
{
_select = select;
_reset = resetPin;
_fqud = FQUDPin;
_select = select;
_reset = resetPin;
_fqud = FQUDPin;
_dataOut = dataOut;
_clock = clock;
// following 3 are always set.
pinMode(_select, OUTPUT);
pinMode(_reset, OUTPUT);
pinMode(_fqud, OUTPUT);
digitalWrite(_select, LOW); // device select = HIGH See - https://github.com/RobTillaart/AD985X/issues/13
// device select = HIGH See - https://github.com/RobTillaart/AD985X/issues/13
digitalWrite(_select, LOW);
digitalWrite(_reset, LOW);
digitalWrite(_fqud, LOW);
_useHW = true;
// SW SPI
if ((dataOut != 0) && (clock != 0))
_hwSPI = ((dataOut == 0) || (clock == 0));
_spi_settings = SPISettings(2000000, LSBFIRST, SPI_MODE0);
if (_hwSPI)
{
_dataOut = dataOut;
_clock = clock;
pinMode(_dataOut, OUTPUT);
pinMode(_clock, OUTPUT);
digitalWrite(_dataOut, LOW);
digitalWrite(_clock, LOW);
_useHW = false;
#if defined(ESP32)
if (_useHSPI) // HSPI
{
mySPI = new SPIClass(HSPI);
mySPI->end();
mySPI->begin(14, 12, 13, select); // CLK=14 MISO=12 MOSI=13
}
else // VSPI
{
mySPI = new SPIClass(VSPI);
mySPI->end();
mySPI->begin(18, 19, 23, select); // CLK=18 MISO=19 MOSI=23
}
#else // generic hardware SPI
mySPI = &SPI;
mySPI->end();
mySPI->begin();
#endif
}
if (_useHW)
else // software SPI
{
SPI.begin(); // set MOSI & MISO pin right.
pinMode(_dataOut, OUTPUT);
pinMode(_clock, OUTPUT);
digitalWrite(_dataOut, LOW);
digitalWrite(_clock, LOW);
}
reset();
}
#if defined(ESP32)
void AD9850::setGPIOpins(uint8_t clk, uint8_t miso, uint8_t mosi, uint8_t select)
{
_clock = clk;
_dataOut = mosi;
_select = select;
pinMode(_select, OUTPUT);
digitalWrite(_select, LOW);
mySPI->end(); // disable SPI
mySPI->begin(clk, miso, mosi, select);
}
#endif
void AD9850::reset()
{
digitalWrite(_select, HIGH); // be sure to select the correct device
// be sure to select the correct device
digitalWrite(_select, HIGH);
pulsePin(_reset);
if (_useHW) pulsePin(SPI_CLOCK);
if (_hwSPI) pulsePin(SPI_CLOCK);
else pulsePin(_clock);
digitalWrite(_select, LOW);
_config = 0; // 0 phase no powerdown
_config = 0; // 0 phase no power down
_freq = 0;
_factor = 0;
_offset = 0;
@ -121,6 +159,13 @@ void AD9850::pulsePin(uint8_t pin)
}
void AD9850::setSPIspeed(uint32_t speed)
{
_SPIspeed = speed;
_spi_settings = SPISettings(_SPIspeed, LSBFIRST, SPI_MODE0);
};
void AD9850::writeData()
{
// Serial.println(_factor, HEX);
@ -129,17 +174,17 @@ void AD9850::writeData()
// used for multidevice config only - https://github.com/RobTillaart/AD985X/issues/13
digitalWrite(_select, HIGH);
if (_useHW)
if (_hwSPI)
{
SPI.beginTransaction(SPISettings(2000000, LSBFIRST, SPI_MODE0));
SPI.transfer(data & 0xFF);
mySPI->beginTransaction(_spi_settings);
mySPI->transfer(data & 0xFF);
data >>= 8;
SPI.transfer(data & 0xFF);
mySPI->transfer(data & 0xFF);
data >>= 8;
SPI.transfer(data & 0xFF);
SPI.transfer(data >> 8);
SPI.transfer(_config & 0xFC); // mask factory test bit
SPI.endTransaction();
mySPI->transfer(data & 0xFF);
mySPI->transfer(data >> 8);
mySPI->transfer(_config & 0xFC); // mask factory test bit
mySPI->endTransaction();
}
else
{
@ -160,14 +205,16 @@ void AD9850::writeData()
// simple one mode version
void AD9850::swSPI_transfer(uint8_t value)
void AD9850::swSPI_transfer(uint8_t val)
{
uint8_t clk = _clock;
uint8_t dao = _dataOut;
// 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);
digitalWrite(dao, (val & mask));
digitalWrite(clk, HIGH);
digitalWrite(clk, LOW);
}
}

View File

@ -2,7 +2,7 @@
//
// FILE: AD985X.h
// AUTHOR: Rob Tillaart
// VERSION: 0.3.0
// VERSION: 0.3.1
// DATE: 2019-02-08
// PURPOSE: Class for AD9850 and AD9851 function generator
//
@ -14,7 +14,7 @@
#include "SPI.h"
#define AD985X_LIB_VERSION (F("0.3.0"))
#define AD985X_LIB_VERSION (F("0.3.1"))
#define AD9850_MAX_FREQ (40UL * 1000UL * 1000UL)
@ -53,13 +53,40 @@ public:
bool getAutoUpdate() { return _autoUpdate; };
void update();
// speed in Hz
void setSPIspeed(uint32_t speed);
uint32_t getSPIspeed() { return _SPIspeed; };
// debugging
bool usesHWSPI() { return _hwSPI; };
// ESP32 specific
#if defined(ESP32)
void selectHSPI() { _useHSPI = true; };
void selectVSPI() { _useHSPI = false; };
bool usesHSPI() { return _useHSPI; };
bool usesVSPI() { return !_useHSPI; };
// to overrule ESP32 default hardware pins
void setGPIOpins(uint8_t clk, uint8_t miso, uint8_t mosi, uint8_t select);
#endif
protected:
void pulsePin(uint8_t pin);
void writeData();
void swSPI_transfer(uint8_t value);
void swSPI_transfer(uint8_t val);
bool _hwSPI = true;
uint32_t _SPIspeed = 2000000;
SPIClass * mySPI;
SPISettings _spi_settings;
#if defined(ESP32)
bool _useHSPI = true;
#endif
bool _useHW = true;
// PINS
uint8_t _dataOut = 0;
uint8_t _clock = 0;

View File

@ -1,8 +1,11 @@
[![Arduino CI](https://github.com/RobTillaart/AD985X/workflows/Arduino%20CI/badge.svg)](https://github.com/marketplace/actions/arduino_ci)
[![JSON check](https://github.com/RobTillaart/AD985X/actions/workflows/jsoncheck.yml/badge.svg)](https://github.com/RobTillaart/AD985X/actions/workflows/jsoncheck.ym)l
[![Arduino-lint](https://github.com/RobTillaart/AD985X/actions/workflows/arduino-lint.yml/badge.svg)](https://github.com/RobTillaart/AD985X/actions/workflows/arduino-lint.yml)
[![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.
@ -11,7 +14,7 @@ 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 library has a AD9850 as 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.
@ -152,28 +155,28 @@ devices at the same time.
### Common interface
- **begin(selectPin, resetPin, FQUDPin, dataOut = 0, clock = 0)**
- **void 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
- **void reset()** resets the function generator.
- **void powerDown()** idem
- **void powerUp()** idem
- **void 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.
- **void setFrequencyF(float freq)** SetFrequencyF sets the frequency with a float with a maximum of **two** decimals.
- Note that a float only has a mantissa of 6-7 digits so for frequencies above above ~1.000.000 = 1MHz all decimals are lost.
- Note setFrequencyF is affected by the autoUpdateFlag.
- **getMaxFrequency()** returns the maximum frequency setable. For the AD9850 this is 20 MHz.
- **uint32_t getMaxFrequency()** returns the maximum frequency that can be set. 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.
- **float getFrequency()** returns the frequency set. As it returns a float it might loose some accuracy at higher frequencies.
- **void 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.
- **uint8_t getPhase()** returns the phase set, 0 by default. One need to multiply by 11.25° to get the actual angle.
### Calibration
@ -194,7 +197,7 @@ Note: setting the offset reduces the range of frequencies (at the ends of scale)
**Warning:** use with care.
- **void setAutoUpdate(bool update)** sets the autoUpdate flag, default it is true.
- **void setAutoUpdate(bool update)** sets the autoUpdate flag, default set to true.
- **bool getAutoUpdate()** reads the autoUpdate flag.
- **void update()** manually toggle the FQ_UD flag to update the frequency.
@ -205,20 +208,59 @@ The default of the flag is true, and will be reset to true by the **reset()** ca
### Hardware SPI
To be used only if one needs a specific speed.
- **void setSPIspeed(uint32_t speed)** set SPI transfer rate
- **uint32_t getSPIspeed()** returns SPI transfer rate
- **bool usesHWSPI()** returns true if HW SPI is used.
### ESP32 specific
This functionality is new in 0.3.1.
- **void selectHSPI()** in case hardware SPI, the ESP32 has two options HSPI and VSPI.
- **void selectVSPI()** see above.
- **bool usesHSPI()** returns true if HSPI is used.
- **bool usesVSPI()** returns true if VSPI is used.
The **selectVSPI()** or the **selectHSPI()** needs to be called
BEFORE the **begin()** function.
#### ESP32 experimental
- **void setGPIOpins(clk, miso, mosi, select)** overrule GPIO pins of ESP32 for hardware SPI. needs to be called
AFTER the **begin()** function.
```cpp
void setup()
{
freqGen.selectVSPI();
freqGen.begin(15);
freqGen.setGPIOpins(CLK, MISO, MOSI, SELECT); // SELECT should match the param of begin()
...
}
```
### 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
- **void setRefClockHigh()** set reference clock to 180 Mhz.
- **void setRefClockLow()** set reference clock to 30 Mhz.
- **uint8_t getRefClock()** returns 30 or 180.
- **void 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.
- **bool 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.
@ -236,9 +278,16 @@ See examples
### Operational notes
- The quality of the signal becomes less at higher frequencies.
Switch the refclock to find your optimal quality.
Switch the reference clock 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.
## Future
- test library with an ESP32 (check details)
- examples for ESP32 HWSPI interface
- performance measurements

View File

@ -7,7 +7,7 @@
#include "AD985X.h"
AD9851 freqGen;
AD9851 freqGen;
uint32_t freq = 0;
uint32_t prev = 0;
@ -35,33 +35,33 @@ void loop()
if (Serial.available() > 0)
{
int c = Serial.read();
switch(c)
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;
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;
}

View File

@ -1,6 +1,6 @@
# Syntax Coloring Map For AD985X
# Syntax Colouring Map For AD985X
# Datatypes (KEYWORD1)
# Data types (KEYWORD1)
AD9850 KEYWORD1
AD9851 KEYWORD1
@ -25,7 +25,6 @@ getAutoRefClock KEYWORD2
setARCCutOffFreq KEYWORD2
getARCCutOffFreq KEYWORD2
setCalibration KEYWORD2
getCalibration KEYWORD2
getFactor KEYWORD2
@ -33,6 +32,16 @@ setAutoUpdate KEYWORD2
getAutoUpdate KEYWORD2
update KEYWORD2
setSPIspeed KEYWORD2
getSPIspeed KEYWORD2
usesHWSPI KEYWORD2
selectHSPI KEYWORD2
selectVSPI KEYWORD2
usesHSPI KEYWORD2
usesVSPI KEYWORD2
setGPIOpins KEYWORD2
# Constants (LITERAL1)
AD9850_MAX_FREQ LITERAL1

View File

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

View File

@ -1,5 +1,5 @@
name=AD985X
version=0.3.0
version=0.3.1
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.