TeensyDuino source code: FM DDS demo 2 on bare Teensy 3.6 - double float timing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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
More details on my blog at https://wp.me/poZKh-7tP