0.3.5 AD985X

This commit is contained in:
rob tillaart 2023-01-11 16:49:14 +01:00
parent 78d08d65d2
commit ec8922a413
11 changed files with 172 additions and 58 deletions

View File

@ -6,7 +6,7 @@ jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: arduino/arduino-lint-action@v1
with:
library-manager: update

View File

@ -8,7 +8,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: ruby/setup-ruby@v1
with:
ruby-version: 2.6

View File

@ -10,7 +10,7 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: json-syntax-check
uses: limitusus/json-syntax-check@v1
with:

View File

@ -43,7 +43,7 @@ void AD9850::begin(uint8_t select, uint8_t resetPin, uint8_t FQUDPin, uint8_t da
pinMode(_reset, OUTPUT);
pinMode(_fqud, OUTPUT);
// device select = HIGH See - https://github.com/RobTillaart/AD985X/issues/13
digitalWrite(_select, LOW);
digitalWrite(_select, LOW);
digitalWrite(_reset, LOW);
digitalWrite(_fqud, LOW);
@ -93,7 +93,7 @@ void AD9850::setGPIOpins(uint8_t clk, uint8_t miso, uint8_t mosi, uint8_t select
pinMode(_select, OUTPUT);
digitalWrite(_select, LOW);
mySPI->end(); // disable SPI
mySPI->end(); // disable SPI
mySPI->begin(clk, miso, mosi, select);
}
#endif
@ -101,7 +101,7 @@ void AD9850::setGPIOpins(uint8_t clk, uint8_t miso, uint8_t mosi, uint8_t select
void AD9850::reset()
{
// be sure to select the correct device
// be sure to select the correct device
digitalWrite(_select, HIGH);
pulsePin(_reset);
if (_hwSPI)
@ -109,7 +109,7 @@ void AD9850::reset()
#if defined(ESP32)
if (_useHSPI) pulsePin(14); // HSPI magic number clock
else pulsePin(18); // VSPI magic number clock
#else
#else
// UNO hardware SPI
pulsePin(SPI_CLOCK);
#endif
@ -140,12 +140,19 @@ void AD9850::powerUp()
}
void AD9850::setPhase(uint8_t phase)
bool AD9850::setPhase(uint8_t phase)
{
if (phase > 31) return;
if (phase > 31) return false;
_config &= 0x07;
_config |= (phase << 3);
writeData();
return true;
}
uint8_t AD9850::getPhase()
{
return (_config >> 3);
}
@ -170,7 +177,7 @@ void AD9850::writeData()
uint32_t data = _factor;
// used for multi device configuration only - https://github.com/RobTillaart/AD985X/issues/13
digitalWrite(_select, HIGH);
digitalWrite(_select, HIGH);
if (_hwSPI)
{
mySPI->beginTransaction(_spi_settings);
@ -216,37 +223,61 @@ void AD9850::swSPI_transfer(uint8_t val)
}
void AD9850::setFrequency(uint32_t freq)
bool AD9850::setFrequency(uint32_t freq)
{
bool rv = true;
// freq OUT = (Δ Phase × CLKIN)/2^32
// 64 bit math to keep precision to the max
if (freq > AD9850_MAX_FREQ) freq = AD9850_MAX_FREQ;
if (freq > AD9850_MAX_FREQ)
{
rv = false;
freq = AD9850_MAX_FREQ;
}
// _factor = round(freq * 34.359738368); // 4294967296 / 125000000
_factor = (147573952589ULL * freq) >> 32;
_freq = freq;
_factor += _offset;
writeData();
return rv;
}
// especially for lower frequencies (with decimals)
void AD9850::setFrequencyF(float freq)
bool AD9850::setFrequencyF(float freq)
{
bool rv = true;
// freq OUT = (Δ Phase × CLKIN)/2^32
// 64 bit math to keep precision to the max
if (freq > AD9850_MAX_FREQ) freq = AD9850_MAX_FREQ;
if (freq > AD9850_MAX_FREQ)
{
rv = false;
freq = AD9850_MAX_FREQ;
}
_factor = round(freq * 34.359738368); // 4294967296 / 125000000
_freq = freq;
_factor += _offset;
writeData();
return rv;
}
float AD9850::getFrequency()
{
return _freq;
}
uint32_t AD9850::getMaxFrequency()
{
return AD9850_MAX_FREQ;
}
void AD9850::update()
{
digitalWrite(_select, HIGH);
digitalWrite(_select, HIGH);
pulsePin(_fqud);
digitalWrite(_select, LOW);
digitalWrite(_select, LOW);
}
@ -255,13 +286,19 @@ void AD9850::update()
// AD9851
//
#define AD9851_REFCLK 0x01 // bit is a 6x multiplier bit P.14 datasheet
// bit is a 6x multiplier bit P.14 datasheet
#define AD9851_REFCLK 0x01
void AD9851::setFrequency(uint32_t freq)
bool AD9851::setFrequency(uint32_t freq)
{
bool rv = true;
// PREVENT OVERFLOW
if (freq > AD9851_MAX_FREQ) freq = AD9851_MAX_FREQ;
if (freq > AD9851_MAX_FREQ)
{
rv = false;
freq = AD9851_MAX_FREQ;
}
// AUTO SWITCH REFERENCE FREQUENCY
if (_autoRefClock)
{
@ -286,14 +323,20 @@ void AD9851::setFrequency(uint32_t freq)
_freq = freq;
_factor += _offset;
writeData();
return rv;
}
// especially for lower frequencies (with decimals)
void AD9851::setFrequencyF(float freq)
bool AD9851::setFrequencyF(float freq)
{
bool rv = true;
// PREVENT OVERFLOW
if (freq > AD9851_MAX_FREQ) freq = AD9851_MAX_FREQ;
if (freq > AD9851_MAX_FREQ)
{
rv = false;
freq = AD9851_MAX_FREQ;
}
// AUTO SWITCH REFERENCE FREQUENCY
if (_autoRefClock)
@ -320,9 +363,16 @@ void AD9851::setFrequencyF(float freq)
_freq = freq;
_factor += _offset;
writeData();
return rv;
}
uint32_t AD9851::getMaxFrequency()
{
return AD9851_MAX_FREQ;
};
////////////////////////////////////////////////////////
//
// AD9851 - AUTO REFERENCE CLOCK
@ -334,6 +384,12 @@ void AD9851::setAutoRefClock(bool arc)
};
bool AD9851::getAutoRefClock()
{
return _autoRefClock;
};
void AD9851::setRefClockHigh()
{
_config |= AD9851_REFCLK;
@ -353,6 +409,7 @@ uint8_t AD9851::getRefClock()
return (_config & AD9851_REFCLK) ? 180 : 30;
}
void AD9851::setARCCutOffFreq(uint32_t Hz)
{
if (Hz > 30000000UL) Hz = 30000000;
@ -360,5 +417,11 @@ void AD9851::setARCCutOffFreq(uint32_t Hz)
};
// -- END OF FILE --
uint32_t AD9851::getARCCutOffFreq()
{
return _ARCCutOffFreq;
};
// -- END OF FILE --

View File

@ -2,18 +2,17 @@
//
// FILE: AD985X.h
// AUTHOR: Rob Tillaart
// VERSION: 0.3.4
// VERSION: 0.3.5
// 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.3.4"))
#define AD985X_LIB_VERSION (F("0.3.5"))
#define AD9850_MAX_FREQ (40UL * 1000UL * 1000UL)
@ -31,34 +30,41 @@ public:
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; };
// returns false if limited to AD9850_MAX_FREQ
bool setFrequency(uint32_t freq); // 0..AD9850_MAX_FREQ
bool setFrequencyF(float freq); // works best for low frequencies.
float getFrequency();
uint32_t getMaxFrequency();
// 0 .. 31 steps of 11.25 degrees
void setPhase(uint8_t phase = 0);
uint8_t getPhase() { return (_config >> 3); };
// returns false if phase > 31.
bool setPhase(uint8_t phase = 0);
uint8_t getPhase();
// 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 = true) { _autoUpdate = update; };
void setAutoUpdate(bool update = true) { _autoUpdate = update; };
bool getAutoUpdate() { return _autoUpdate; };
void update();
// speed in Hz
void setSPIspeed(uint32_t speed);
uint32_t getSPIspeed() { return _SPIspeed; };
// debugging
bool usesHWSPI() { return _hwSPI; };
// internal chip factor used for frequency. (debugging only)
uint32_t getFactor() { return _factor; };
// ESP32 specific
#if defined(ESP32)
void selectHSPI() { _useHSPI = true; };
@ -102,24 +108,33 @@ protected:
};
/////////////////////////////////////////////////////////////////////
//
// DERIVED CLASS
//
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; };
// returns false if limited to AD9851_MAX_FREQ
bool setFrequency(uint32_t freq); // 0..AD9851_MAX_FREQ
bool setFrequencyF(float freq); // works best for low frequencies.
uint32_t getMaxFrequency();
void setRefClockHigh(); // 180 MHz
void setRefClockLow(); // 30 MHz
uint8_t getRefClock(); // returns 180 or 30
void setRefClockHigh(); // 180 MHz
void setRefClockLow(); // 30 MHz
uint8_t getRefClock();
void setAutoRefClock(bool arc);
bool getAutoRefClock() { return _autoRefClock; };
bool getAutoRefClock();
// 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; };
uint32_t getARCCutOffFreq();
protected:
bool _autoRefClock = false;

View File

@ -6,12 +6,19 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
## [0.3.5] - 2023-01-11
- update GitHub actions
- update license
- add bool return value to setFrequency()
- add bool return value to setPhase()
- add test_constants in unit test
## [0.3.4] - 2022-10-25
- add changelog.md
- add RP2040 in build-CI
- add URL to examples
## [0.3.3] - 2021-12-10
- update library.json, license, readme.md
- fix reset() for ESP32 hardware SPI

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2019-2022 Rob Tillaart
Copyright (c) 2019-2023 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

@ -178,20 +178,25 @@ for SW SPI you need to define the data and clock pin too.
- resetPin = reset
- FQUD = Frequency UpDate Pin
- **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.
- **void powerDown()** idem.
- **void powerUp()** idem.
- **bool setFrequency(uint32_t freq)** SetFrequency sets the frequency and is limited by the
MaxFrequency of the class used.
Returns false if limited.
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.
- **void setFrequencyF(float freq)** SetFrequencyF sets the frequency with a float with a maximum of **two** decimals.
- **bool 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.
The frequency is limited by the MaxFrequency of the class used.
Returns false if limited.
- **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.
- **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.
- **bool setPhase(uint8_t phase = 0)** set the phase in units of 11.25° 0..31 allowed.
Default it sets the phase to 0.
Returns false if phase > 31, no change to phase in that case.
- **uint8_t getPhase()** returns the phase set, 0 by default. One need to multiply by 11.25° to get the actual angle.
@ -270,7 +275,7 @@ The AD9850 has no specific functions.
### AD9851 specific
- **void setRefClockHigh()** set reference clock to 180 Mhz.
- **void setRefClockLow()** set reference clock to 30 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
@ -278,7 +283,8 @@ to 30 MHz when the frequency is set to 10 MHz or lower.
The initial value is **false** == OFF for backwards compatibility.
- **bool getAutoRefClock()** returns true if autoRefClock is set.
- **void setARCCutOffFreq(uint32_t Hz = 10000000UL )** set cut off frequency
for the auto reference clock. Maximum value is 30 MHz, typical 10 MHz.
for the auto reference clock.
Maximum value is 30 MHz, typical is 10 MHz.
- **uint32_t getARCCutOffFreq()** returns cut off frequency set.
@ -303,8 +309,24 @@ The user is also responsible to store it e.g. in EEPROM to make it persistent.
## Future
#### Must
#### Should
- examples for ESP32 HWSPI interface
- do tests on ESP32
- performance measurements
- move code to .cpp
- unit tests for
- bool setFrequency
- bool setPhase
#### Could
- move code to .cpp
- create defines for MAGIC numbers (defaults)
- should other void function return bool?
- setARCCutOffFreq() ?
#### Wont

View File

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

View File

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

View File

@ -27,7 +27,7 @@
unittest_setup()
{
fprintf(stderr, "VERSION: %s\n", AD985X_LIB_VERSION);
fprintf(stderr, "AD985X_LIB_VERSION: %s\n", AD985X_LIB_VERSION);
}
@ -36,6 +36,13 @@ unittest_teardown()
}
unittest(test_constants)
{
assertEqual((40UL * 1000UL * 1000UL), AD9850_MAX_FREQ);
assertEqual((70UL * 1000UL * 1000UL), AD9851_MAX_FREQ);
}
unittest(test_constructor)
{
AD9850 funcgen0;