Compare commits
12 Commits
Author | SHA1 | Date |
---|---|---|
|
ec1fccf424 | |
|
c2feff6816 | |
|
95c808bb7f | |
|
b66c2088b9 | |
|
bcd6d7e6b3 | |
|
0e8d42699a | |
|
3d476bf1c2 | |
|
f9322214a9 | |
|
6726836631 | |
|
7ebe201f96 | |
|
c4daa7941b | |
|
6f4d8a93c5 |
|
@ -9,6 +9,9 @@
|
||||||
|
|
||||||
#define PWM_PIN 3
|
#define PWM_PIN 3
|
||||||
|
|
||||||
|
#define DDS_USE_ONLY_TIMER2 true
|
||||||
|
#define TIMER2_PHASE_ADVANCE 24
|
||||||
|
|
||||||
DDS dds;
|
DDS dds;
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
|
@ -16,6 +19,7 @@ void setup() {
|
||||||
digitalWrite(PWM_PIN, LOW);
|
digitalWrite(PWM_PIN, LOW);
|
||||||
|
|
||||||
dds.start();
|
dds.start();
|
||||||
|
dds.startPhaseAccumulator(DDS_USE_ONLY_TIMER2);
|
||||||
dds.playWait(600, 3000);
|
dds.playWait(600, 3000);
|
||||||
dds.on();
|
dds.on();
|
||||||
//dds.setAmplitude(31);
|
//dds.setAmplitude(31);
|
||||||
|
@ -25,9 +29,14 @@ void loop() {
|
||||||
dds.setFrequency(2200);
|
dds.setFrequency(2200);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DDS_USE_ONLY_TIMER2
|
|
||||||
|
#if DDS_USE_ONLY_TIMER2
|
||||||
ISR(TIMER2_OVF_vect) {
|
ISR(TIMER2_OVF_vect) {
|
||||||
dds.clockTick();
|
static unsigned char tcnt = 0;
|
||||||
|
if(++tcnt == TIMER2_PHASE_ADVANCE) {
|
||||||
|
tcnt = 0;
|
||||||
|
dds.clockTick();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#else // Use the ADC timer instead
|
#else // Use the ADC timer instead
|
||||||
ISR(ADC_vect) {
|
ISR(ADC_vect) {
|
||||||
|
|
38
keywords.txt
38
keywords.txt
|
@ -6,24 +6,36 @@
|
||||||
# Datatypes (KEYWORD1)
|
# Datatypes (KEYWORD1)
|
||||||
#######################################
|
#######################################
|
||||||
|
|
||||||
DDS KEYWORD1
|
DDS KEYWORD1
|
||||||
|
|
||||||
#######################################
|
#######################################
|
||||||
# Methods and Functions (KEYWORD2)
|
# Methods and Functions (KEYWORD2)
|
||||||
#######################################
|
#######################################
|
||||||
#begin KEYWORD2
|
|
||||||
#exists KEYWORD2
|
start KEYWORD2
|
||||||
#mkdir KEYWORD2
|
isRunning KEYWORD2
|
||||||
#remove KEYWORD2
|
stop KEYWORD2
|
||||||
#rmdir KEYWORD2
|
startPhaseAccumulator KEYWORD2
|
||||||
#open KEYWORD2
|
isTimer2Only KEYWORD2
|
||||||
#close KEYWORD2
|
on KEYWORD2
|
||||||
#seek KEYWORD2
|
off KEYWORD2
|
||||||
#position KEYWORD2
|
play KEYWORD2
|
||||||
#size KEYWORD2
|
playWait KEYWORD2
|
||||||
|
calcFrequency KEYWORD2
|
||||||
|
getPhaseAdvance KEYWORD2
|
||||||
|
setFrequency KEYWORD2
|
||||||
|
setPrecalcFrequency KEYWORD2
|
||||||
|
setPhaseDeg KEYWORD2
|
||||||
|
setAmplitude KEYWORD2
|
||||||
|
changePhaseDeg KEYWORD2
|
||||||
|
setReferenceClock KEYWORD2
|
||||||
|
getReferenceClock KEYWORD2
|
||||||
|
setReferenceOffset KEYWORD2
|
||||||
|
getReferenceOffset KEYWORD2
|
||||||
|
getDutyCycle KEYWORD2
|
||||||
|
clockTick KEYWORD2
|
||||||
|
|
||||||
#######################################
|
#######################################
|
||||||
# Constants (LITERAL1)
|
# Constants (LITERAL1)
|
||||||
#######################################
|
#######################################
|
||||||
#FILE_READ LITERAL1
|
|
||||||
#FILE_WRITE LITERAL1
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
name=DDS
|
name=DDS
|
||||||
version=1.0.2
|
version=1.0.3
|
||||||
author=Morgan Redfield <morgan@enhancedradio.com>, Casey Halverson <casey@enhancedradio.com>
|
author=Morgan Redfield <morgan@enhancedradio.com>, Casey Halverson <casey@enhancedradio.com>
|
||||||
maintainer=Morgan Redfield <morgan@enhancedradio.com>
|
maintainer=Morgan Redfield <morgan@enhancedradio.com>
|
||||||
sentence=A library for use with HamShield by Enhanced Radio Devices.
|
sentence=Generate sinusoids on AVR-based Arduinos at varying frequencies. Requires an analog low-pass filter.
|
||||||
paragraph=
|
paragraph=
|
||||||
category=Device Control
|
category=Signal Input/Output
|
||||||
url=http://www.hamshield.com
|
url=http://www.hamshield.com
|
||||||
architectures=*
|
architectures=avr
|
||||||
includes=DDS.h
|
includes=DDS.h
|
68
src/DDS.cpp
68
src/DDS.cpp
|
@ -13,6 +13,7 @@ void DDS::start() {
|
||||||
#ifdef DDS_PWM_PIN_3
|
#ifdef DDS_PWM_PIN_3
|
||||||
TCCR2A = (TCCR2A | _BV(COM2B1)) & ~(_BV(COM2B0) | _BV(COM2A1) | _BV(COM2A0)) |
|
TCCR2A = (TCCR2A | _BV(COM2B1)) & ~(_BV(COM2B0) | _BV(COM2A1) | _BV(COM2A0)) |
|
||||||
_BV(WGM21) | _BV(WGM20);
|
_BV(WGM21) | _BV(WGM20);
|
||||||
|
//TCCR2B = (TCCR2B & ~(_BV(CS22) | _BV(CS21))) | _BV(CS20) | _BV(WGM22);
|
||||||
TCCR2B = (TCCR2B & ~(_BV(CS22) | _BV(CS21))) | _BV(CS20) | _BV(WGM22);
|
TCCR2B = (TCCR2B & ~(_BV(CS22) | _BV(CS21))) | _BV(CS20) | _BV(WGM22);
|
||||||
#else
|
#else
|
||||||
// Alternatively, use pin 11
|
// Alternatively, use pin 11
|
||||||
|
@ -33,42 +34,49 @@ void DDS::start() {
|
||||||
OCR2A = 0;
|
OCR2A = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef DDS_USE_ONLY_TIMER2
|
|
||||||
TIMSK2 |= _BV(TOIE2);
|
}
|
||||||
#endif
|
|
||||||
|
void DDS::stop() {
|
||||||
|
// TODO: Stop the timers.
|
||||||
|
if(!timer2only){
|
||||||
|
TCCR1B = 0;
|
||||||
|
}
|
||||||
|
TCCR2B = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DDS::startPhaseAccumulator(bool use_only_timer_2){
|
||||||
|
timer2only = use_only_timer_2;
|
||||||
|
|
||||||
|
if(timer2only){
|
||||||
|
TIMSK2 |= _BV(TOIE2);
|
||||||
|
}
|
||||||
|
|
||||||
// Second, setup Timer1 to trigger the ADC interrupt
|
// Second, setup Timer1 to trigger the ADC interrupt
|
||||||
// This lets us use decoding functions that run at the same reference
|
// This lets us use decoding functions that run at the same reference
|
||||||
// clock as the DDS.
|
// clock as the DDS.
|
||||||
// We use ICR1 as TOP and prescale by 8
|
// We use ICR1 as TOP and prescale by 8
|
||||||
/* TCCR1B = _BV(CS10) | _BV(WGM13) | _BV(WGM12);
|
if(!timer2only){
|
||||||
TCCR1A = 0;
|
TCCR1B = _BV(CS10) | _BV(WGM13) | _BV(WGM12);
|
||||||
ICR1 = ((F_CPU / 1) / refclk) - 1;
|
TCCR1A = 0;
|
||||||
#ifdef DDS_DEBUG_SERIAL
|
ICR1 = ((F_CPU / 1) / refclk) - 1;
|
||||||
Serial.print(F("DDS SysClk: "));
|
#ifdef DDS_DEBUG_SERIAL
|
||||||
Serial.println(F_CPU/8);
|
Serial.print(F("DDS SysClk: "));
|
||||||
Serial.print(F("DDS RefClk: "));
|
Serial.println(F_CPU/8);
|
||||||
Serial.println(refclk, DEC);
|
Serial.print(F("DDS RefClk: "));
|
||||||
Serial.print(F("DDS ICR1: "));
|
Serial.println(refclk, DEC);
|
||||||
Serial.println(ICR1, DEC);
|
Serial.print(F("DDS ICR1: "));
|
||||||
#endif
|
Serial.println(ICR1, DEC);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Configure the ADC here to automatically run and be triggered off Timer1
|
// Configure the ADC here to automatically run and be triggered off Timer1
|
||||||
ADMUX = _BV(REFS0) | _BV(ADLAR) | 0; // Channel 0, shift result left (ADCH used)
|
ADMUX = _BV(REFS0) | _BV(ADLAR) | 0; // Channel 0, shift result left (ADCH used)
|
||||||
DDRC &= ~_BV(0);
|
DDRC &= ~_BV(0);
|
||||||
PORTC &= ~_BV(0);
|
PORTC &= ~_BV(0);
|
||||||
DIDR0 |= _BV(0);
|
DIDR0 |= _BV(0);
|
||||||
ADCSRB = _BV(ADTS2) | _BV(ADTS1) | _BV(ADTS0);
|
ADCSRB = _BV(ADTS2) | _BV(ADTS1) | _BV(ADTS0);
|
||||||
ADCSRA = _BV(ADEN) | _BV(ADSC) | _BV(ADATE) | _BV(ADIE) | _BV(ADPS2); // | _BV(ADPS0);
|
ADCSRA = _BV(ADEN) | _BV(ADSC) | _BV(ADATE) | _BV(ADIE) | _BV(ADPS2); // | _BV(ADPS0);
|
||||||
*/
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void DDS::stop() {
|
|
||||||
// TODO: Stop the timers.
|
|
||||||
#ifndef DDS_USE_ONLY_TIMER2
|
|
||||||
TCCR1B = 0;
|
|
||||||
#endif
|
|
||||||
TCCR2B = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set our current sine wave frequency in Hz
|
// Set our current sine wave frequency in Hz
|
||||||
|
@ -88,6 +96,8 @@ ddsAccumulator_t DDS::calcFrequency(unsigned short freq) {
|
||||||
newStep = (1500.0 / (DDS_REFCLK_DEFAULT+DDS_REFCLK_OFFSET)) * pow(2,ACCUMULATOR_BITS);
|
newStep = (1500.0 / (DDS_REFCLK_DEFAULT+DDS_REFCLK_OFFSET)) * pow(2,ACCUMULATOR_BITS);
|
||||||
} else if (freq == 600) {
|
} else if (freq == 600) {
|
||||||
newStep = (600.0 / (DDS_REFCLK_DEFAULT+DDS_REFCLK_OFFSET)) * pow(2,ACCUMULATOR_BITS);
|
newStep = (600.0 / (DDS_REFCLK_DEFAULT+DDS_REFCLK_OFFSET)) * pow(2,ACCUMULATOR_BITS);
|
||||||
|
} else {
|
||||||
|
newStep = pow(2,ACCUMULATOR_BITS)*freq / (refclk+refclkOffset);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
newStep = pow(2,ACCUMULATOR_BITS)*freq / (refclk+refclkOffset);
|
newStep = pow(2,ACCUMULATOR_BITS)*freq / (refclk+refclkOffset);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#ifndef _DDS_H_
|
#ifndef _DDS_H_
|
||||||
#define _DDS_H_
|
#define _DDS_H_
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
#include <avr/pgmspace.h>
|
#include <avr/pgmspace.h>
|
||||||
|
|
||||||
// Use pin 3 for PWM? If not defined, use pin 11
|
// Use pin 3 for PWM? If not defined, use pin 11
|
||||||
|
@ -10,7 +11,7 @@
|
||||||
|
|
||||||
// Normally, we turn on timer2 and timer1, and have ADC sampling as our clock
|
// Normally, we turn on timer2 and timer1, and have ADC sampling as our clock
|
||||||
// Define this to only use Timer2, and not start the ADC clock
|
// Define this to only use Timer2, and not start the ADC clock
|
||||||
// #define DDS_USE_ONLY_TIMER2
|
//#define DDS_USE_ONLY_TIMER2
|
||||||
|
|
||||||
// Use a short (16 bit) accumulator. Phase accuracy is reduced, but speed
|
// Use a short (16 bit) accumulator. Phase accuracy is reduced, but speed
|
||||||
// is increased, along with a reduction in memory use.
|
// is increased, along with a reduction in memory use.
|
||||||
|
@ -133,7 +134,7 @@ class DDS {
|
||||||
public:
|
public:
|
||||||
DDS(): refclk(DDS_REFCLK_DEFAULT), refclkOffset(DDS_REFCLK_OFFSET),
|
DDS(): refclk(DDS_REFCLK_DEFAULT), refclkOffset(DDS_REFCLK_OFFSET),
|
||||||
accumulator(0), running(false),
|
accumulator(0), running(false),
|
||||||
timeLimited(false), tickDuration(0), amplitude(255)
|
timeLimited(false), tickDuration(0), amplitude(255), timer2only(false)
|
||||||
{};
|
{};
|
||||||
|
|
||||||
// Start all of the timers needed
|
// Start all of the timers needed
|
||||||
|
@ -142,6 +143,9 @@ public:
|
||||||
const bool isRunning() { return running; };
|
const bool isRunning() { return running; };
|
||||||
// Stop the DDS timers
|
// Stop the DDS timers
|
||||||
void stop();
|
void stop();
|
||||||
|
// Start the phase accumulator
|
||||||
|
void startPhaseAccumulator(bool use_only_timer_2 = false);
|
||||||
|
bool isTimer2Only() { return timer2only; };
|
||||||
|
|
||||||
// Start and stop the PWM output
|
// Start and stop the PWM output
|
||||||
void on() {
|
void on() {
|
||||||
|
@ -214,6 +218,7 @@ public:
|
||||||
void clockTick();
|
void clockTick();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
volatile bool timer2only;
|
||||||
volatile bool running;
|
volatile bool running;
|
||||||
volatile unsigned long tickDuration;
|
volatile unsigned long tickDuration;
|
||||||
volatile bool timeLimited;
|
volatile bool timeLimited;
|
||||||
|
|
Loading…
Reference in New Issue