//#DOC Main, program entry and main loop
// From: https://github.com/Billwilliams1952/AD9833-Library-Arduino
#include "main.h"
#include <otSpi.h>
#include <otSerial.h>
#include <otTimer.h>
#include <otPort.h>
#include <otMalloc.h>
#include <otPrintf.h>
#include <otAD9833.h>
void PrintMenu(char ch, bool outputOn);
void IncrementFrequencyTest();
void CycleWaveformsTest();
void SwitchFrequencyRegisterTest();
void PhaseTest();
#define RUNNING "\tRUNNING\n"
#define NOT_RUNNING "\n"
#define ON "ON\n"
#define OFF "OFF\n"
#define YIELD_ON_CHAR if(UartCheckChar(UART0)) { UartChar(UART0); return; }
#define DELAY_WITH_YIELD for(U8 i = 0; i < 10; i++ ) { YIELD_ON_CHAR DelayMs(100); }
otAD9833 dds;
void setup()
{
UartEnableSignals(UART0);
UartSetBaud(UART0, 115200);
_initMalloc();
setStdout(4096, UART0); // printf output is now directed on UART0
PortConfig(PP_F, 10, P_OUT);
PortConfig(PP_F, 11, P_OUT);
PPF_SET = BIT(10) | BIT(11);
dds.init(SA_OFFBOARD);
PPF_CLR = BIT(11);
dds.applySignal(otAD9833::SINE_WAVE, otAD9833::REG0, 1000);
dds.enableOutput(true);
DelayMs(1000);
PrintMenu(0, false);
}
void loop()
{
static bool outputOn = false;
if(UartCheckChar(UART0))
{
char ch = UartChar(UART0);
PrintMenu(ch, outputOn);
switch(ch)
{
case '1':
IncrementFrequencyTest();
break;
case '2':
CycleWaveformsTest();
break;
case '3':
SwitchFrequencyRegisterTest();
break;
case '4':
PhaseTest();
break;
case '6':
outputOn = ! outputOn;
dds.enableOutput(outputOn); // Turn off output
break;
default:
printf("*** Invalid command ***\n");
break;
}
}
}
/*
* Setup a manual ramp from a Start frequency to a Stop frequency in some increment
* over a ramp time.
*/
void IncrementFrequencyTest()
{
F32 startHz = 1000, stopHz = 5000, incHz = 1, sweepTimeSec = 5.0;
// Calculate the delay between each increment.
U16 numMsecPerStep = (sweepTimeSec * 1000.0) / ((U16) ((stopHz - startHz) / incHz) + 1);
if(numMsecPerStep == 0)
numMsecPerStep = 1;
// Apply a signal to the output. If phaseReg is not supplied, then
// a phase of 0.0 is applied to the same register as freqReg
dds.applySignal(otAD9833::SINE_WAVE, otAD9833::REG1, startHz);
while(true)
{
dds.setFrequency(otAD9833::REG1, startHz - incHz);
for(F32 i = startHz; i <= stopHz; i += incHz)
{
YIELD_ON_CHAR
dds.incrementFrequency(otAD9833::REG1, incHz);
DelayMs(numMsecPerStep);
}
}
}
/*
* Cycle through all of the waveform types. Also cycle the
* frequency registers.
*/
void CycleWaveformsTest()
{
U16 waveType = otAD9833::SINE_WAVE;
dds.setFrequency(otAD9833::REG0, 10000.0); // Load values
dds.setFrequency(otAD9833::REG1, 1000.0);
// We don't care about phase for this test
while(true)
{
dds.setWaveform(otAD9833::REG1,waveType); // Next waveform
dds.setWaveform(otAD9833::REG0,waveType);
dds.setOutputSource(otAD9833::REG1); // Output 1000 Hz waveform
// Hack to allow I'm alive lamp a chance to blink and give a better
// response to user input
DELAY_WITH_YIELD
dds.setOutputSource(otAD9833::REG0); // Output 10000 Hz waveform
DELAY_WITH_YIELD
switch(waveType) // Cycle through all the waveform types
{
case otAD9833::SINE_WAVE:
waveType = otAD9833::TRIANGLE_WAVE;
break;
case otAD9833::TRIANGLE_WAVE:
waveType = otAD9833::SQUARE_WAVE;
break;
case otAD9833::SQUARE_WAVE:
waveType = otAD9833::HALF_SQUARE_WAVE;
break;
case otAD9833::HALF_SQUARE_WAVE:
waveType = otAD9833::SINE_WAVE;
break;
}
}
}
/*
* Fast switching example.
* I use the FFT display capability on my scope
*/
void SwitchFrequencyRegisterTest()
{
dds.applySignal(otAD9833::SINE_WAVE, otAD9833::REG0, 500000);
dds.applySignal(otAD9833::SINE_WAVE, otAD9833::REG1, 100000);
dds.setPhase(otAD9833::REG1, 180); // Offset second freq by 180 deg
dds.reset();
while(true) // This takes time
{
YIELD_ON_CHAR // This takes more time
dds.setOutputSource(otAD9833::REG0); // This takes about 18 usec
dds.setOutputSource(otAD9833::REG1); // This takes about 18 usec
// What ends up is REG0 frequency is active a shorter amount of time
// then REG1 frequency. In the sepctrum, the duty cycle differences will
// show up (power is lower by 10log(DC))
}
}
/*
* Phase shift between REG0 and REG1. Use a oscilloscope set to Normal
* triggering, AC coupling, 500usec/div, 100 mV/div. This will display
* about two cycles for register 0, 4 cycle for register 1, plus dead
* time for the Reset.
* Use Normal triggering so the display remains even when triggering is
* lost. Can use any waveform for this test. Remember that the square
* wave is about 5v-pp while sine and triangle are about 600 mv-pp
*/
void PhaseTest()
{
dds.applySignal(otAD9833::TRIANGLE_WAVE, otAD9833::REG0, 1000);
dds.applySignal(otAD9833::SINE_WAVE, otAD9833::REG1, 2000);
bool reverse = true;
while(true)
{
reverse = ! reverse;
for(S16 i = 0; i <= 360; i += 1 )
{
if(!reverse )
dds.incrementPhase(otAD9833::REG1, -1);
else
dds.incrementPhase(otAD9833::REG1, 1);
YIELD_ON_CHAR
/*
* Display ~ 2 cycles using REG0 phase. If no REG is supplied for phase,
* defaults to REG specified for frequency. RESET is removed during this
* function call.
*/
dds.setOutputSource(otAD9833::REG0);
/*
* This is just a wag to try to get exactly 2 cycles of the waveform.
* It makes the phase alignments easier to verify.
*/
DelayUs(1900);
YIELD_ON_CHAR
/* This also works if you keep using REG1 for frequency
* Now display ~ 4 cycles using REG1
*/
dds.setOutputSource(otAD9833::REG1);
DelayUs(1950);
/*
* Turn off for remaining trace. Reset the registers so triggering occurs
* on the start of REG0 signal. Reset() includes 15 msec delay which is good
* to ensure sweep is completed.
* I tried using EnableOutput(true) then EnableOutput(false) in this
* loop but could not get reliable triggering on the scope.
*
* The difference between Reset() and EnableOutput(false) is that EnableOutput(false)
* keeps the AD9833 in RESET until you specifically remove the RESET using
* EnableOutput(true). However, after a call to Reset(), calls to ANY function
* EXCEPT Set/Increment Phase will also remove the RESET.
*
*/
dds.reset();
if(i % 90 == 0)
DelayMs(1000); // Stop and show phase alignment between REG0 REG1
}
}
}
/*
* Display the command menu
*/
void PrintMenu(char ch, bool outputOn)
{
printf("\n\n");
printf("****** AD9833 Test Menu ******\n\n");
printf("'1' IncrementFrequencyTest");
if(ch == '1') printf(RUNNING); else printf(NOT_RUNNING);
printf("'2' CycleWaveformsTest\t");
if(ch == '2') printf(RUNNING); else printf(NOT_RUNNING);
printf("'3' SwitchFrequencyRegisterTest");
if(ch == '3') printf(RUNNING); else printf(NOT_RUNNING);
printf("'4' PhaseTest\t\t");
if(ch == '4') printf(RUNNING); else printf(NOT_RUNNING);
printf("'5' RequestedvsProgrammedValues");
if(ch == '5') printf(RUNNING); else printf(NOT_RUNNING);
printf("'6' Output ");
if(ch == '6')
{
if(outputOn) printf(OFF);
else printf(ON);
}
else
{
if(outputOn) printf(ON);
else printf(OFF);
}
printf("Enter a number 1 to 6 >");
}