0.3.0 AD9833

This commit is contained in:
Rob Tillaart 2023-12-18 11:23:18 +01:00
parent 5f6d3a6196
commit 623a49ac56
12 changed files with 323 additions and 53 deletions

View File

@ -2,23 +2,27 @@
// FILE: AD9833.cpp
// AUTHOR: Rob Tillaart
// PURPOSE: Arduino library for AD9833 function generator
// VERSION: 0.2.0
// VERSION: 0.3.0
// URL: https://github.com/RobTillaart/AD9833
//
#include "AD9833.h"
// FREQUENCY REGISTER BITS
#define AD9833_FREG1 0x8000
#define AD9833_FREG0 0x4000
// CONTROL REGISTER BITS
#define AD9833_B28 (1 << 13)
#define AD9833_HLB (1 << 12)
#define AD9833_FSELECT (1 << 11)
#define AD9833_PSELECT (1 << 10)
#define AD9833_RESET (1 << 8)
#define AD9833_SLEEP1 (1 << 7)
#define AD9833_SLEEP12 (1 << 6)
#define AD9833_OPBITEN (1 << 5)
#define AD9833_DIV2 (1 << 3)
#define AD9833_MODE (1 << 1)
#define AD9833_B28 (1 << 13)
#define AD9833_HLB (1 << 12)
#define AD9833_FSELECT (1 << 11)
#define AD9833_PSELECT (1 << 10)
#define AD9833_RESET (1 << 8)
#define AD9833_SLEEP1 (1 << 7)
#define AD9833_SLEEP12 (1 << 6)
#define AD9833_OPBITEN (1 << 5)
#define AD9833_DIV2 (1 << 3)
#define AD9833_MODE (1 << 1)
@ -71,14 +75,14 @@ void AD9833::begin()
void AD9833::reset()
{
hardwareReset();
_control = AD9833_B28;
_control = AD9833_B28; // implicit select sine wave.
writeControlRegister(_control);
}
void AD9833::hardwareReset()
{
writeControlRegister(_control | 0x0100);
writeControlRegister(_control | AD9833_RESET);
// reset all library variables to be in "sync" with hardware.
_control = 0;
_waveform = AD9833_OFF;
@ -148,7 +152,7 @@ float AD9833::setFrequency(float freq, uint8_t channel)
// rounding
uint32_t fr = round(_freq[channel] * (268435456.0 / 25000000.0));
writeFreqRegister(channel, fr);
writeFrequencyRegister(channel, fr);
return _freq[channel];
}
@ -233,7 +237,7 @@ bool AD9833::usesHWSPI()
////////////////////////////////////////////////////////////////
//
// PRIVATE
// LOW LEVEL API - Expert users only
//
void AD9833::writeControlRegister(uint16_t value)
{
@ -242,35 +246,91 @@ void AD9833::writeControlRegister(uint16_t value)
}
void AD9833::writeFreqRegister(uint8_t reg, uint32_t freq)
void AD9833:: writeFrequencyRegister(uint8_t channel, uint32_t freq)
{
uint16_t data = 0;
if (reg > 1) return;
if (reg == 0) data = 0x4000; // bit 15 and 14 01
if (reg == 1) data = 0x8000; // bit 15 and 14 10
uint16_t LSB = 0;
uint16_t MSB = 0;
if (channel > 1) return;
if (channel == 0) LSB = AD9833_FREG0; // bit 15 and 14 01
if (channel == 1) LSB = AD9833_FREG1; // bit 15 and 14 10
// copy channel mask.
MSB = LSB;
// be sure B28 bit is set.
_control |= AD9833_B28;
writeControlRegister(_control);
// 28 bits in two sets of 14
data |= (freq & 0x3FFF); // least significant 14 bits
writeData(data);
LSB |= (freq & 0x3FFF);
MSB |= ((freq >> 14) & 0x3FFF);
data &= 0xC000; // remove freq data LSB
data |= (freq >> 14); // most significant 14 bits
writeData(data);
// faster to write them in one SPI transaction
writeData28(LSB, MSB);
// first send the least significant 14 bits
// writeData(LSB);
// then send the most significant 14 bits
// writeData(MSB);
}
void AD9833::writePhaseRegister(uint8_t reg, uint16_t value)
void AD9833::writePhaseRegister(uint8_t channel, uint16_t value)
{
uint16_t data = 0;
if (reg > 1) return;
if (reg == 0) data = 0xC000; // bit 15 and 14 and 13 110
if (reg == 1) data = 0xE000; // bit 15 and 14 and 13 111
if (channel > 1) return;
if (channel == 0) data = 0xC000; // bit 15 and 14 and 13 110
if (channel == 1) data = 0xE000; // bit 15 and 14 and 13 111
data |= (value & 0x0FFF);
writeData(data);
}
///////////////////////////////////////////////////////////////////
//
// EXPERIMENTAL
//
void AD9833::writeFrequencyRegisterLSB(uint8_t channel, uint16_t LSB)
{
if (channel > 1) return;
// force 14 bit
LSB &= 0x3FFF;
if (channel == 0) LSB |= AD9833_FREG0; // bit 15 and 14 01
if (channel == 1) LSB |= AD9833_FREG1; // bit 15 and 14 10
// be sure B28 and HLB bit is cleared.
_control &= ~AD9833_B28;
_control &= ~AD9833_HLB;
writeControlRegister(_control);
// send the least significant 14 bits
writeData(LSB);
}
void AD9833::writeFrequencyRegisterMSB(uint8_t channel, uint16_t MSB)
{
if (channel > 1) return;
// force 14 bit
MSB &= 0x3FFF;
if (channel == 0) MSB |= AD9833_FREG0; // bit 15 and 14 01
if (channel == 1) MSB |= AD9833_FREG1; // bit 15 and 14 10
// be sure B28 is cleared and HLB bit is set.
_control &= ~AD9833_B28;
_control |= AD9833_HLB;
writeControlRegister(_control);
// send the least significant 14 bits
writeData(MSB);
}
///////////////////////////////////////////////////////////////////
//
// PRIVATE
//
void AD9833::writeData(uint16_t data)
{
if (_useSelect) digitalWrite(_selectPin, LOW);
@ -298,4 +358,41 @@ void AD9833::writeData(uint16_t data)
}
void AD9833::writeData28(uint16_t LSB, uint16_t MSB)
{
if (_useSelect) digitalWrite(_selectPin, LOW);
if (_hwSPI)
{
_mySPI->beginTransaction(_spi_settings);
_mySPI->transfer16(LSB);
_mySPI->transfer16(MSB);
_mySPI->endTransaction();
}
else
{
// local variables is fast.
uint8_t clk = _clockPin;
uint8_t dao = _dataPin;
// MSBFIRST
for (uint16_t mask = 0x8000; mask; mask >>= 1)
{
digitalWrite(dao, (LSB & mask) !=0 ? HIGH : LOW);
digitalWrite(clk, LOW);
digitalWrite(clk, HIGH);
}
for (uint16_t mask = 0x8000; mask; mask >>= 1)
{
digitalWrite(dao, (MSB & mask) !=0 ? HIGH : LOW);
digitalWrite(clk, LOW);
digitalWrite(clk, HIGH);
}
digitalWrite(dao, LOW);
}
if (_useSelect) digitalWrite(_selectPin, HIGH);
}
// -- END OF FILE --

View File

@ -3,7 +3,7 @@
// FILE: AD9833.h
// AUTHOR: Rob Tillaart
// PURPOSE: Arduino library for AD9833 function generator.
// VERSION: 0.2.0
// VERSION: 0.3.0
// URL: https://github.com/RobTillaart/AD9833
@ -11,13 +11,15 @@
#include "SPI.h"
#define AD9833_LIB_VERSION (F("0.2.0"))
#define AD9833_LIB_VERSION (F("0.3.0"))
#if defined(ARDUINO_ARCH_RP2040)
#define __SPI_CLASS__ SPIClassRP2040
#else
#define __SPI_CLASS__ SPIClass
#if !defined(__SPI_CLASS__)
#if defined(ARDUINO_ARCH_RP2040)
#define __SPI_CLASS__ SPIClassRP2040
#else
#define __SPI_CLASS__ SPIClass
#endif
#endif
@ -77,12 +79,18 @@ public:
// LOW LEVEL API - Expert users only
void writeControlRegister(uint16_t value);
void writeFreqRegister(uint8_t reg, uint32_t freq);
void writePhaseRegister(uint8_t reg, uint16_t value);
void writeFrequencyRegister(uint8_t channel, uint32_t freq);
void writePhaseRegister(uint8_t channel, uint16_t value);
// EXPERIMENTAL HLB MODE (14 bit)
void writeFrequencyRegisterLSB(uint8_t channel, uint16_t LSB);
void writeFrequencyRegisterMSB(uint8_t channel, uint16_t MSB);
private:
void writeData(uint16_t data);
void writeData28(uint16_t LSB, uint16_t MSB);
bool _hwSPI = true;
uint32_t _SPIspeed = 8000000;

View File

@ -6,6 +6,20 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
## [0.3.0] - 2023-11-26
- fix #10, explicit write control register fixes glitch.
- rename **writeFreqRegister()** to **writeFrequencyRegister()**
- refactor **writeFrequencyRegister()**
- rename parameters reg => channel
- catch define conflict __SPI_CLASS__
- update readme.md
- add experimental support for HLB write mode
- **void writeFrequencyRegisterLSB(uint8_t reg, uint16_t LSB)**
- **void writeFrequencyRegisterMSB(uint8_t reg, uint16_t MSB)**
- update keywords.txt
----
## [0.2.0] - 2023-11-26
- refactor constructor/begin interface - breaking changes.
- minimize conditional code. -- create SPI_CLASS macro to solve it.

View File

@ -203,13 +203,32 @@ Use at your own risk, please read the datasheet carefully.
Since version 0.1.1 writing to the registers is made public.
By using the low level API to access the registers directly, one has maximum
control over the AD9833 device.
Especially frequency setting is improved as the float **setFrequency()** does
Especially frequency setting is improved as the float parameter of **setFrequency()** does
not have the 28 bits precision of the register.
- **void writeControlRegister(uint16_t value)** see datasheet
- **void writeFreqRegister(uint8_t reg, uint32_t freq)** reg = 0 or 1, freq = 0 .. 134217728
- **void writePhaseRegister(uint8_t reg, uint16_t value)** reg = 0 or 1, freq = 0 .. 4095
- **void writeFrequencyRegister(uint8_t channel, uint32_t freq)** channel = 0 or 1, freq = 0 .. 134217728
- **void writePhaseRegister(uint8_t channel, uint16_t value)** channel = 0 or 1, value = 0 .. 4095
#### HLB mode
To support the HLB mode the library supports (experimental 0.3.0) two new calls.
These functions allow one to set the frequency in coarse steps (MSB)
of around 3050 Hz and fine steps (LSB) of around 0.093 Hz.
- **void writeFrequencyRegisterLSB(uint8_t channel, uint16_t LSB)** channel = 0 or 1, LSB = 0 .. 32767
- **void writeFrequencyRegisterMSB(uint8_t channel, uint16_t MSB)** channel = 0 or 1, MSB = 0 .. 32767 (in theory)
Note the HLB calls take only 2 SPI calls to adjust the frequency.
Therefore they are slightly faster than the **setFrequency()** (uses 3 SPI calls)
if you only need to modify one of the two frequency registers.
Only using the LSB register allows one to go from 0 .. 3050 Hz.
In piano scales this covers C0 (16.35 Hz) to F#7 (2959.96 Hz).
https://pages.mtu.edu/~suits/notefreqs.html
## External FSYNC
@ -243,11 +262,9 @@ As this implementation is experimental, the interface might change in the future
#### Must
- update documentation
- get hardware to test
#### Should
- investigate HLB mode versus B28 mode
- investigate external clock
- investigate timing (response time)
- change freq
@ -260,8 +277,6 @@ As this implementation is experimental, the interface might change in the future
- add examples
- for ESP32 HWSPI interface
- solve MAGIC numbers (defaults)
- setting half freq register for performance mode.
- HLB mode
- extend performance measurements
- investigate compatibility AD9834 a.o.
- add **setPhaseRadians(float radians, uint8_t channel)** wrapper.

View File

@ -0,0 +1,34 @@
// FILE: AD9833_HLB_MODE.ino
// AUTHOR: Rob Tillaart
// PURPOSE: test
// URL: https://github.com/RobTillaart/AD9833/issues/10
#include "AD9833.h"
AD9833 AD(10, 11, 13); // SW SPI UNO
void setup()
{
Serial.begin(115200);
while(!Serial);
Serial.println(__FILE__);
AD.begin();
AD.setFrequency(1000, 0);
AD.setFrequency(1000, 0);
AD.setWave(2);
}
void loop()
{
AD.writeFrequencyRegisterLSB(0, 10000); // 931.32 Hz
delay(2000);
AD.writeFrequencyRegisterLSB(0, 5000); // 466.66 Hz
delay(2000);
}
// -- END OF FILE --

View File

@ -7,7 +7,12 @@
#include "AD9833.h"
// ESP32
// SPIClass * myspi = new SPIClass(VSPI);
// AD9833 AD(5, myspi);
// AD9833 AD(15, 13, 14); // SW SPI
// UNO
// AD9833 AD(10, 11, 13); // software SPI
AD9833 AD(10); // hardware SPI
@ -48,6 +53,20 @@ void setup()
Serial.print("setWave:\t");
Serial.println(stop - start);
delay(10);
start = micros();
AD.writeFrequencyRegisterLSB(0, 10000);
stop = micros();
Serial.print("writeFrequencyRegisterLSB:\t");
Serial.println(stop - start);
delay(10);
start = micros();
AD.writeFrequencyRegisterMSB(0, 10000);
stop = micros();
Serial.print("writeFrequencyRegisterMSB:\t");
Serial.println(stop - start);
delay(10);
}

View File

@ -0,0 +1,24 @@
Board: ESP32 (240 Mhz)
IDE: 1.8.19
AD9833_performance.ino
AD9833_LIB_VERSION: 0.3.0
hardware: 1 (hardware SPI)
setFrequency: 48 (increased due to fix #10)
setPhase: 20
setWave: 14
writeFrequencyRegisterLSB: 23
writeFrequencyRegisterMSB: 23
hardware: 0 (software SPI)
setFrequency: 48 (increased due to fix #10)
setPhase: 15
setWave: 10
writeFrequencyRegisterLSB: 16
writeFrequencyRegisterMSB: 16

View File

@ -0,0 +1,22 @@
Board: UNO (16 Mhz)
IDE: 1.8.19
AD9833_performance.ino
AD9833_LIB_VERSION: 0.3.0
hardware: 1 (hardware SPI)
setFrequency: 44 (increased due to fix #10)
setPhase: 24
setWave: 24
writeFrequencyRegisterLSB: 40
writeFrequencyRegisterMSB: 40
hardware: 0 (software SPI)
setFrequency: 628 (increased due to fix #10)
setPhase: 220
setWave: 220
writeFrequencyRegisterLSB: 428
writeFrequencyRegisterMSB: 432

View File

@ -0,0 +1,34 @@
// FILE: AD9833_test_10.ino
// AUTHOR: Rob Tillaart
// PURPOSE: test
// URL: https://github.com/RobTillaart/AD9833/issues/10
#include "AD9833.h"
AD9833 AD(10, 11, 13);
void setup()
{
Serial.begin(115200);
while(!Serial);
Serial.println(__FILE__);
AD.begin();
AD.setFrequency(1000, 0);
AD.setFrequency(1000, 0);
AD.setWave(2);
}
void loop()
{
AD.setFrequency(5000, 0);
delay(10);
AD.setFrequency(6000, 0);
delay(10);
}
// -- END OF FILE --

View File

@ -27,11 +27,13 @@ setSPIspeed KEYWORD2
getSPIspeed KEYWORD2
usesHWSPI KEYWORD2
selectHSPI KEYWORD2
selectVSPI KEYWORD2
usesHSPI KEYWORD2
usesVSPI KEYWORD2
setGPIOpins KEYWORD2
# LOW LEVEL API
writeControlRegister KEYWORD2
writeFrequencyRegister KEYWORD2
writePhaseRegister KEYWORD2
writeFrequencyRegisterLSB KEYWORD2
writeFrequencyRegisterMSB KEYWORD2
# Constants (LITERAL1)
@ -44,4 +46,5 @@ AD9833_OFF LITERAL1
AD9833_SINE LITERAL1
AD9833_SQUARE1 LITERAL1
AD9833_SQUARE2 LITERAL1
AD9833_TRIANGLE LITERAL1
AD9833_TRIANGLE LITERAL1

View File

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

View File

@ -1,5 +1,5 @@
name=AD9833
version=0.2.0
version=0.3.0
author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=Arduino library for AD9833 function generator. Supports hardware SPI and software SPI.