TeensyDuino source code: FM DDS demo 2 on bare Teensy 3.6 - double float timing
// FM DDS | |
// Ed Nisley - KE4ZNU | |
// 2017-04-19 Demo 1 | |
#include <IntervalTimer.h> | |
#include <ADC.h> | |
#include <SPI.h> | |
#define PIN_HEART 14 | |
#define PIN_TIMER 15 | |
#define PIN_ANALOG 16 | |
#define PIN_GLITCH 17 | |
#define PIN_AUDIO A9 | |
#define PIN_DDS_FQUD 10 | |
// data to DDS MOSI0 11 | |
// no data from DDS MISO0 12 | |
// DDS clock on SCK0 13 -- also LED | |
#define BUILTIN_LED 13 | |
//--------------------- | |
// Useful constants | |
int SamplePeriod = 25; // microseconds per analog sample | |
//--------------------- | |
// Globals | |
ADC *adc = new ADC(); | |
IntervalTimer timer; | |
volatile int AnalogSample; | |
volatile int AudioMax = -4096; | |
volatile int AudioMin = 4096; | |
typedef struct { | |
uint8_t Phase; | |
uint32_t DeltaPhase; // DDS expects MSB first! | |
} DDS; | |
DDS DDSBuffer; | |
double DDSClock = 180.0e6; // nominal DDS oscillator | |
double CountPerHertz, HertzPerCount; // DDS delta-phase increments | |
double Crystal = 20.0e6; // nominal DDS frequency | |
double Deviation = 5.0e3; // nominal FM signal deviation (one-sided) | |
double TestFreq; | |
//--------------------- | |
// Handy routines | |
void FlipPin(int pin) { | |
digitalWriteFast(pin,!digitalRead(pin)); | |
} | |
void PulsePin(int p) { | |
FlipPin(p); | |
FlipPin(p); | |
} | |
//--------------------- | |
// Timer handler | |
void timer_callback(void) { | |
digitalWriteFast(PIN_TIMER,HIGH); | |
digitalWriteFast(PIN_DDS_FQUD,HIGH); // latch previously shifted bits | |
adc->startSingleRead(PIN_AUDIO, ADC_0); // start ADC conversion | |
analogWriteDAC0(AnalogSample); // show previous audio sample | |
digitalWriteFast(PIN_TIMER,LOW); | |
} | |
//--------------------- | |
// Analog read handler | |
void adc0_isr(void) { | |
int Audio; | |
digitalWriteFast(PIN_ANALOG,HIGH); | |
AnalogSample = adc->readSingle(); // fetch just-finished sample | |
Audio = AnalogSample - 2048; // convert to AC signal | |
DDSBuffer.Phase = 0; | |
SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); | |
digitalWriteFast(PIN_DDS_FQUD, LOW); | |
SPI.transfer(DDSBuffer.Phase); | |
DDSBuffer.DeltaPhase = (uint32_t)((((double)Audio / 2048.0) * Deviation + Crystal) * CountPerHertz); | |
SPI.transfer((uint8_t)(DDSBuffer.DeltaPhase >> 24)); // MSB first! | |
if (Audio > AudioMax) // ignore race conditions | |
AudioMax = Audio; | |
if (Audio < AudioMin) | |
AudioMin = Audio; | |
SPI.transfer((uint8_t)(DDSBuffer.DeltaPhase >> 16)); | |
SPI.transfer((uint8_t)(DDSBuffer.DeltaPhase >> 8)); | |
SPI.transfer((uint8_t)DDSBuffer.DeltaPhase); | |
SPI.endTransaction(); // do not raise FQ_UD until next timer tick! | |
digitalWriteFast(PIN_ANALOG,LOW); | |
} | |
//--------------------- | |
// Hardware setup | |
void setup(void) { | |
pinMode(BUILTIN_LED,OUTPUT); // will eventually become SCK0 | |
pinMode(PIN_HEART, OUTPUT); // show we arrived | |
digitalWrite(PIN_HEART,LOW); | |
PulsePin(PIN_HEART); | |
PulsePin(PIN_HEART); | |
pinMode(PIN_TIMER,OUTPUT); | |
digitalWrite(PIN_TIMER,LOW); | |
pinMode(PIN_GLITCH,OUTPUT); | |
digitalWrite(PIN_GLITCH,LOW); | |
pinMode(PIN_ANALOG,OUTPUT); | |
digitalWrite(PIN_ANALOG,LOW); | |
pinMode(PIN_AUDIO,INPUT); | |
pinMode(PIN_DDS_FQUD,OUTPUT); | |
digitalWriteFast(PIN_DDS_FQUD,HIGH); | |
Serial.begin(115200); | |
int waited = 0; | |
while (!Serial && waited < 3000) { // fall out after a few seconds | |
delay(1); | |
waited++; | |
if (! (waited % 50)) | |
FlipPin(BUILTIN_LED); | |
} | |
Serial.printf("FM Modulated DDS\nEd Nisley KE4ZNU\n"); | |
Serial.printf(" serial wait: %d ms\n\n",waited); | |
SPI.begin(); | |
SPI.usingInterrupt(255); // attached through analog IRQs | |
adc->setAveraging(4); // choices: 0, 4, 8, 16, 32 | |
adc->setResolution(12); // choices: 8, 10, 12, 16 | |
adc->setConversionSpeed(ADC_CONVERSION_SPEED::HIGH_SPEED); | |
adc->setSamplingSpeed(ADC_SAMPLING_SPEED::HIGH_SPEED); | |
adc->enableInterrupts(ADC_0); | |
if (!timer.begin(timer_callback, SamplePeriod)) { | |
Serial.printf("Timer start failed\n"); | |
while (true) { | |
FlipPin(BUILTIN_LED); | |
delay(75); | |
} | |
} | |
CountPerHertz = (1LL << 32) / DDSClock; | |
HertzPerCount = 1.0 / CountPerHertz; | |
Serial.printf("DDS clock: %13.3f Hz\n",DDSClock); | |
Serial.printf("CountPerHertz: %13.3f ct\n",CountPerHertz); | |
Serial.printf("HertzPerCount: %13.3f Hz\n\n",HertzPerCount); | |
TestFreq = Crystal; | |
Serial.printf("Crystal: %13.3f Hz\n",Crystal); | |
Serial.printf("Deviation: %13.3f Hz\n",Deviation); | |
Serial.printf("\nSetup done\n"); | |
} | |
//--------------------- | |
// Do things forever | |
void loop(void) { | |
digitalWrite(PIN_HEART,HIGH); | |
Serial.printf(" %5d to %5d\n",AudioMin,AudioMax); | |
AudioMax = 99*AudioMax/100; // ignore race conditions | |
AudioMin = 99*AudioMin/100; | |
digitalWrite(PIN_HEART,LOW); | |
delay(500); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This comment has been minimized.
More details on my blog at https://wp.me/poZKh-7tP