Introduce README, move software into Firmware folder

And create the still unused Hardware folder
main
EmaMaker 2021-02-13 15:56:36 +01:00
parent f9f2cbe89b
commit a31cf8db96
29 changed files with 35 additions and 592 deletions

View File

@ -12,6 +12,7 @@ void setupWaves(){
analogWriteResolution(12); analogWriteResolution(12);
// Enable DAC0, from framework's analogWriteDAC0, place here from higher speeds // Enable DAC0, from framework's analogWriteDAC0, place here from higher speeds
// Reference voltage is 1.2V
SIM_SCGC2 |= SIM_SCGC2_DAC0; SIM_SCGC2 |= SIM_SCGC2_DAC0;
DAC0_C0 = DAC_C0_DACEN | DAC_C0_DACRFS; DAC0_C0 = DAC_C0_DACEN | DAC_C0_DACRFS;
} }

View File

@ -1,39 +0,0 @@
This directory is intended for project header files.
A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.
```src/main.c
#include "header.h"
int main (void)
{
...
}
```
Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.
In C, the usual convention is to give header files names that end with `.h'.
It is most portable to use only letters, digits, dashes, and underscores in
header file names, and at most one dot.
Read more about using header files in official GCC documentation:
* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html

View File

@ -1,7 +0,0 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
]
}

View File

@ -1,39 +0,0 @@
This directory is intended for project header files.
A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.
```src/main.c
#include "header.h"
int main (void)
{
...
}
```
Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.
In C, the usual convention is to give header files names that end with `.h'.
It is most portable to use only letters, digits, dashes, and underscores in
header file names, and at most one dot.
Read more about using header files in official GCC documentation:
* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html

View File

@ -1,46 +0,0 @@
This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into executable file.
The source code of each library should be placed in a an own separate directory
("lib/your_library_name/[here are source files]").
For example, see a structure of the following two libraries `Foo` and `Bar`:
|--lib
| |
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
| |
| |--Foo
| | |- Foo.c
| | |- Foo.h
| |
| |- README --> THIS FILE
|
|- platformio.ini
|--src
|- main.c
and a contents of `src/main.c`:
```
#include <Foo.h>
#include <Bar.h>
int main (void)
{
...
}
```
PlatformIO Library Dependency Finder will find automatically dependent
libraries scanning project source files.
More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html

View File

@ -1,14 +0,0 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:teensy35]
platform = teensy
board = teensy35
framework = arduino

View File

@ -1,52 +0,0 @@
#pragma once
//From: https://github.com/jameskeaveney/Teensy-SineWaveGenerator
#define maxSamplesNum 512 // 12-bit
static int waveformsTable[maxSamplesNum] = {
// Sin wave
0x800,0x819,0x832,0x84c,0x865,0x87e,0x897,0x8b0,0x8c9,0x8e2,0x8fb,0x914,
0x92d,0x946,0x95f,0x978,0x990,0x9a9,0x9c2,0x9da,0x9f3,0xa0b,0xa23,0xa3b,
0xa54,0xa6c,0xa84,0xa9b,0xab3,0xacb,0xae2,0xafa,0xb11,0xb28,0xb3f,0xb56,
0xb6d,0xb84,0xb9a,0xbb1,0xbc7,0xbdd,0xbf3,0xc09,0xc1f,0xc34,0xc4a,0xc5f,
0xc74,0xc89,0xc9d,0xcb2,0xcc6,0xcda,0xcee,0xd02,0xd15,0xd29,0xd3c,0xd4f,
0xd62,0xd74,0xd86,0xd98,0xdaa,0xdbc,0xdcd,0xddf,0xdf0,0xe00,0xe11,0xe21,
0xe31,0xe41,0xe51,0xe60,0xe6f,0xe7e,0xe8d,0xe9b,0xea9,0xeb7,0xec4,0xed2,
0xedf,0xeec,0xef8,0xf04,0xf10,0xf1c,0xf27,0xf32,0xf3d,0xf48,0xf52,0xf5c,
0xf66,0xf6f,0xf78,0xf81,0xf8a,0xf92,0xf9a,0xfa2,0xfa9,0xfb0,0xfb7,0xfbe,
0xfc4,0xfca,0xfcf,0xfd5,0xfda,0xfde,0xfe3,0xfe7,0xfeb,0xfee,0xff1,0xff4,
0xff7,0xff9,0xffb,0xffd,0xffe,0xfff,0x1000,0x1000,0x1000,0x1000,0xfff,0xffe,
0xffd,0xffc,0xffa,0xff8,0xff5,0xff3,0xff0,0xfec,0xfe9,0xfe5,0xfe1,0xfdc,
0xfd7,0xfd2,0xfcd,0xfc7,0xfc1,0xfba,0xfb4,0xfad,0xfa6,0xf9e,0xf96,0xf8e,
0xf86,0xf7d,0xf74,0xf6b,0xf61,0xf57,0xf4d,0xf43,0xf38,0xf2d,0xf22,0xf16,
0xf0a,0xefe,0xef2,0xee5,0xed8,0xecb,0xebe,0xeb0,0xea2,0xe94,0xe85,0xe77,
0xe68,0xe58,0xe49,0xe39,0xe29,0xe19,0xe09,0xdf8,0xde7,0xdd6,0xdc5,0xdb3,
0xda1,0xd8f,0xd7d,0xd6b,0xd58,0xd45,0xd32,0xd1f,0xd0c,0xcf8,0xce4,0xcd0,
0xcbc,0xca8,0xc93,0xc7e,0xc69,0xc54,0xc3f,0xc2a,0xc14,0xbfe,0xbe8,0xbd2,
0xbbc,0xba6,0xb8f,0xb79,0xb62,0xb4b,0xb34,0xb1d,0xb06,0xaee,0xad7,0xabf,
0xaa7,0xa90,0xa78,0xa60,0xa48,0xa2f,0xa17,0x9ff,0x9e6,0x9ce,0x9b5,0x99d,
0x984,0x96b,0x952,0x93a,0x921,0x908,0x8ef,0x8d6,0x8bd,0x8a4,0x88a,0x871,
0x858,0x83f,0x826,0x80d,0x7f3,0x7da,0x7c1,0x7a8,0x78f,0x776,0x75c,0x743,
0x72a,0x711,0x6f8,0x6df,0x6c6,0x6ae,0x695,0x67c,0x663,0x64b,0x632,0x61a,
0x601,0x5e9,0x5d1,0x5b8,0x5a0,0x588,0x570,0x559,0x541,0x529,0x512,0x4fa,
0x4e3,0x4cc,0x4b5,0x49e,0x487,0x471,0x45a,0x444,0x42e,0x418,0x402,0x3ec,
0x3d6,0x3c1,0x3ac,0x397,0x382,0x36d,0x358,0x344,0x330,0x31c,0x308,0x2f4,
0x2e1,0x2ce,0x2bb,0x2a8,0x295,0x283,0x271,0x25f,0x24d,0x23b,0x22a,0x219,
0x208,0x1f7,0x1e7,0x1d7,0x1c7,0x1b7,0x1a8,0x198,0x189,0x17b,0x16c,0x15e,
0x150,0x142,0x135,0x128,0x11b,0x10e,0x102,0xf6,0xea,0xde,0xd3,0xc8,
0xbd,0xb3,0xa9,0x9f,0x95,0x8c,0x83,0x7a,0x72,0x6a,0x62,0x5a,
0x53,0x4c,0x46,0x3f,0x39,0x33,0x2e,0x29,0x24,0x1f,0x1b,0x17,
0x14,0x10,0xd,0xb,0x8,0x6,0x4,0x3,0x2,0x1,0x0,0x0,
0x0,0x0,0x1,0x2,0x3,0x5,0x7,0x9,0xc,0xf,0x12,0x15,
0x19,0x1d,0x22,0x26,0x2b,0x31,0x36,0x3c,0x42,0x49,0x50,0x57,
0x5e,0x66,0x6e,0x76,0x7f,0x88,0x91,0x9a,0xa4,0xae,0xb8,0xc3,
0xce,0xd9,0xe4,0xf0,0xfc,0x108,0x114,0x121,0x12e,0x13c,0x149,0x157,
0x165,0x173,0x182,0x191,0x1a0,0x1af,0x1bf,0x1cf,0x1df,0x1ef,0x200,0x210,
0x221,0x233,0x244,0x256,0x268,0x27a,0x28c,0x29e,0x2b1,0x2c4,0x2d7,0x2eb,
0x2fe,0x312,0x326,0x33a,0x34e,0x363,0x377,0x38c,0x3a1,0x3b6,0x3cc,0x3e1,
0x3f7,0x40d,0x423,0x439,0x44f,0x466,0x47c,0x493,0x4aa,0x4c1,0x4d8,0x4ef,
0x506,0x51e,0x535,0x54d,0x565,0x57c,0x594,0x5ac,0x5c5,0x5dd,0x5f5,0x60d,
0x626,0x63e,0x657,0x670,0x688,0x6a1,0x6ba,0x6d3,0x6ec,0x705,0x71e,0x737,
0x750,0x769,0x782,0x79b,0x7b4,0x7ce,0x7e7,0x800
};

View File

@ -1,26 +0,0 @@
#include "test.h"
#ifdef TEST6
#include "Audio.h"
#include ""
AudioSynthWaveformSine sine1;
AudioSynthWaveformSine sine2;
AudioOutputAnalog dac1(0);
AudioOutputAnalog dac2(1);
AudioConnection patchCord1(sine1, dac1);
AudioConnection patchCord1(sine2, dac2);
void setup (void) {
AudioMemory(12);
sine1.amplitude(1.0);
sine1.frequency(100);
sine2.amplitude(2.0);
sine2.frequency(200);
}
void loop (void) {
}
#endif

View File

@ -1,76 +0,0 @@
#include "test.h"
#ifdef TEST1
//The analogWrite function takes an int as argument. The DAC is 12 bit. So max int value we can use is 2^16
//Precalculate it, don't use a define
int MAX_VALUE = pow(2, 12);
int MAX_VALUE_HALF = MAX_VALUE / 2;
//DAC voltage value goes from 0V (analogWrite(0)) to 3.3V (analogWrite(pow(2,12)))
//Function declaration
void sineWave();
void resetSineWave();
void sawTooth(boolean);
float sineApprox(float);
float freq = 1000; //Hertz
void setup()
{
//Set DACs resolution to 12 bits
analogWriteResolution(12);
Serial.begin(9600);
resetSineWave();
}
float amplitude = 1;
void loop()
{
sineWave();
}
float t = 0;
float omega = freq * 2 * PI;
int oldValue = 0;
unsigned long lastTime = 0;
unsigned long time = 0;
//This is not frequency accurate. Frequency changes by changing the t value, which is totally not correct
void sineWave()
{
int y1 = MAX_VALUE_HALF + MAX_VALUE_HALF * sin(omega + t );
analogWriteDAC0(y1);
t += 0.26;
}
void resetSineWave()
{
t = 0;
}
void sawTooth(boolean delay_)
{
//Sawthoot wave
for (int i = 0; i < MAX_VALUE; i++)
{
analogWriteDAC0(i);
analogWriteDAC1(i);
if (delay_)
delay(100);
}
}
//Sine approximation function https://en.wikipedia.org/wiki/Bhaskara_I%27s_sine_approximation_formula
float sineApprox(float x)
{
return (16 * x * (PI - x) / (5 * PI * PI - 4 * x * (PI - x)));
}
#endif

View File

@ -1,4 +0,0 @@
#pragma once
#define TEST6
#include <Arduino.h>

View File

@ -1,65 +0,0 @@
#include "test.h"
#ifdef TEST3
//Lookup table test
//Use a lookup table to generate the sine wave. This works well and generates a good 0-3.3V sine wave
//After playing around with the sampling freq, this is good enough but still has some artifacts on the wave, probably due to the phase accumulator wraparound. But definitely the way to go here
//Above a certain value (like 130khz) linear interpolation is NEEDED to smooth out the wave, probably due to the sampling rate of the DAC
//Next-day note: the artifacts on the wave definetely were because of the phase wraparound, changing the way the DAC was driven and the phase accumulated this now works fine
//Linear interpolation is still needed though
//The analogWrite function takes an int as argument. The DAC is 12 bit. So max int value we can use is 2^12
//Precalculate it, don't use a define
int MAX_VALUE = pow(2, 12);
int MAX_VALUE_HALF = MAX_VALUE / 2;
//How many samples of the wave we want
#define LUT_SIZE 1024
int LUT[LUT_SIZE];
const int BUFF_SIZE = 1024; // size of output buffer (samples)
int16_t buff[BUFF_SIZE]; // output buffer
//DAC voltage value goes from 0V (analogWrite(0)) to 3.3V (analogWrite(pow(2,12)))
void calculateLookupTable();
FASTRUN void setup() {
//Set DACs resolution to 12 bits
analogWriteResolution(12);
//Disable interrupts for extra speed
noInterrupts();
calculateLookupTable();
}
long sampleFreq = 1818181; //DAC sampling time(1428571,428571429), then i played around with it
const int f = 50000; // frequency we want to generate (Hz)
const float delta_phi = (float)f / sampleFreq * LUT_SIZE; // phase increment
int phase = 0.0f; // phase accumulator
FASTRUN void loop() {
// generate buffer of output
for (int i = 0; i < BUFF_SIZE; ++i) {
buff[i] = LUT[phase]; // get sample value from LUT
phase = (phase + delta_phi) % LUT_SIZE; // increment phase
if (phase >= (float)LUT_SIZE) // handle wraparound
phase -= (float)LUT_SIZE;
analogWriteDAC0(buff[i]); // write the selected waveform on DAC
}
}
//Using the analitic formula for a sine wave, calculate the lookup table
void calculateLookupTable() {
for (int i = 0; i < LUT_SIZE; i++) {
LUT[i] = MAX_VALUE_HALF + MAX_VALUE_HALF * sin((2 * PI * (float)i) / LUT_SIZE);
}
}
#endif

View File

@ -1,44 +0,0 @@
#include "test.h"
#ifdef TEST2
//Lookup table test
//Use a lookup table to generate the sine wave. This works well and generates a good 0-3.3V sine wave
//Frequency is modulated using "samplerate", which waits a defined time between outputting a point and another.
//The digital oscilloscope reports the frequency to be in a range 10-100 hertz from the defined requency (drifting higher the higher the frequency)
//Frequency caps at 1.5kHz, but it's stabler the lower it goes
//delay functions in teensy take long as arguments, eliminating the decimal part and giving us less numbers to use (samplerate becomes lower and lower the higher the frequency)
//Approximation errors errors cap the frequency and make it less accurate
//The analogWrite function takes an int as argument. The DAC is 12 bit. So max int value we can use is 2^12
//Precalculate it, don't use a define
int MAX_VALUE = pow(2, 12);
int MAX_VALUE_HALF = MAX_VALUE / 2;
//DAC voltage value goes from 0V (analogWrite(0)) to 3.3V (analogWrite(pow(2,12)))
volatile int i = 0;
float freq = 100; //Hertz
FASTRUN void setup() {
//Set DACs resolution to 12 bits
analogWriteResolution(12);
//Disable interrupts for extra speed
noInterrupts();
}
//This is how much time can pass between two points
//We wait micros (10^-6) to mult by 10^6
float samplerate = ((1/freq)/maxSamplesNum)*pow(10, 6);
FASTRUN void loop() {
analogWriteDAC0(waveformsTable[i]); // write the selected waveform on DAC
i = (i+1)%maxSamplesNum;
//Using delay instead of micros() produces defined waves that don't wobble because of the approximation errors, since delayMicroseconds takes a long and not a float as argument
delayMicroseconds(samplerate);
}
#endif

View File

@ -1,81 +0,0 @@
#include "test.h"
#ifdef TEST4
//Lookup table test
//Use a lookup table to generate a quarter of a sine sine wave. This eliminates asimmetries and random frequency shiftings (very slight but noticeable enough)
//It is also a faster algo
//This however has some problems in the zones when a quarter if shifted, giving some artifacts that point to MAX_VALUE_HALF, like if there was MAX_VALUE_HALF in the table at that point
//The analogWrite function takes an int as argument. The DAC is 12 bit. So max int value we can use is 2^12
//Precalculate it, don't use a define
int MAX_VALUE = pow(2, 12);
int MAX_VALUE_HALF = MAX_VALUE / 2;
//How many samples of the wave we want
#define LUT_SIZE 1024
int LUT[LUT_SIZE];
const int BUFF_SIZE = 4096; // size of output buffer (samples)
int16_t buff[BUFF_SIZE]; // output buffer
//DAC voltage value goes from 0V (analogWrite(0)) to 3.3V (analogWrite(pow(2,12)))
void calculateLookupTable();
FASTRUN void setup() {
//Set DACs resolution to 12 bits
analogWriteResolution(12);
//Disable interrupts for extra speed
noInterrupts();
calculateLookupTable();
Serial.begin(115200);
}
long sampleFreq = 1428571; //DAC sampling time(1428571,428571429), then i played around with it
const int f = 10000; // frequency we want to generate (Hz)
const float delta_phi = (float)f / sampleFreq * (LUT_SIZE*4); // phase increment
int phase = 0.0f; // phase accumulator
int LUT_SIZE_QUARTER = LUT_SIZE * 2;
int LUT_SIZE_HALF = LUT_SIZE * 3;
int LUT_SIZE_3QUARTERS = LUT_SIZE * 4;
FASTRUN void loop() {
// generate buffer of output
for (int i = 0; i < BUFF_SIZE; ++i) {
phase += delta_phi; // increment phase
int phase_i = (int)phase; // get integer part of our phase
//For the first quarter of the lut, generate a quarter of a sine wave
if(phase >= 0 && phase < LUT_SIZE) buff[i] = LUT[phase_i];
else if(phase >= LUT_SIZE && phase < LUT_SIZE*2) buff[i] = LUT[(2*LUT_SIZE-phase_i)%LUT_SIZE];
else if(phase >= LUT_SIZE*2 && phase < LUT_SIZE*3) buff[i] = MAX_VALUE-LUT[(2*LUT_SIZE+phase_i)%LUT_SIZE];
else if(phase >= LUT_SIZE*3 && phase <= LUT_SIZE*4) buff[i] = MAX_VALUE-LUT[(4*LUT_SIZE-phase_i)%LUT_SIZE];
else if (phase > (float)LUT_SIZE*4) phase -= (float)(LUT_SIZE*4); // handle wraparound
//This is a very ugly fix to the spikes that sometimes happed when changing quadrant
// if (abs(oldValue-buff[i]) >= MAX_VALUE_HALF*0.65) buff[i] = oldValue;
// else oldValue = buff[i]
analogWriteDAC0(buff[i]);
}
}
//Calculate the lookup table for a quarter (pi/2) of the sinewave
void calculateLookupTable() {
for (int i = 0; i < LUT_SIZE; i++) {
LUT[i] = MAX_VALUE_HALF + MAX_VALUE_HALF * sin((PI/2) * i / LUT_SIZE);
}
}
#endif

View File

@ -1,68 +0,0 @@
#include "test.h"
#ifdef TEST5
//Lookup table test
//Generate a full lookup table by using simmetries of the first quarter. Works fine, i'd use this as the base for the complete project.
//Usual artifacts from 100kHz on, but i think it just needs to switch from analogWrite to the use of registers, to drive the DAC as fast as possible.
int MAX_VALUE = pow(2, 12);
int MAX_VALUE_HALF = MAX_VALUE / 2;
//How many samples of the wave we want
#define LUT_SIZE 1024
int LUT[LUT_SIZE];
const int BUFF_SIZE = 4096; // size of output buffer (samples)
int16_t buff[BUFF_SIZE]; // output buffer
//DAC voltage value goes from 0V (analogWrite(0)) to 3.3V (analogWrite(pow(2,12)))
void calculateLookupTable();
FASTRUN void setup() {
//Set DACs resolution to 12 bits
analogWriteResolution(12);
//Disable interrupts for extra speed
noInterrupts();
calculateLookupTable();
}
long sampleFreq = 1817571; //DAC sampling time(1428571,428571429), then i played around with it
const int f = 250075; // frequency we want to generate (Hz)
const float delta_phi = (float)f / sampleFreq * (LUT_SIZE); // phase increment
float phase = 0.0f; // phase accumulator
int oldValue;
FASTRUN void loop() {
// generate buffer of output
for (int i = 0; i < BUFF_SIZE; ++i) {
int phase_i = (int)phase; // get integer part of our phase
buff[i] = LUT[phase_i]; // get sample value from LUT
phase += delta_phi; // increment phase
if (phase >= (float)LUT_SIZE) // handle wraparound
phase -= (float)LUT_SIZE;
analogWriteDAC0(buff[i]); // write the selected waveform on DAC
}
}
//Calculate a quarter of a sine wave, the other three quadrants are simmetris of it
void calculateLookupTable() {
int LUT_SIZE_QUARTER = LUT_SIZE * 0.25;
int LUT_SIZE_HALF = LUT_SIZE * 0.5;
for (int i = 0; i < LUT_SIZE; i++) {
//For the first quarter of the lut, generate a quarter of a sine wave
if(i >= 0 && i <= LUT_SIZE_QUARTER) LUT[i] = MAX_VALUE_HALF + MAX_VALUE_HALF * sin((2 * PI * (float)i) / LUT_SIZE);
else if(i > LUT_SIZE_QUARTER && i <= LUT_SIZE_HALF) LUT[i] = LUT[(LUT_SIZE_HALF-i)];
else LUT[i] = MAX_VALUE-LUT[(LUT_SIZE-i)];;
}
}
#endif

View File

@ -1,11 +0,0 @@
This directory is intended for PlatformIO Unit Testing and project tests.
Unit Testing is a software testing method by which individual units of
source code, sets of one or more MCU program modules together with associated
control data, usage procedures, and operating procedures, are tested to
determine whether they are fit for use. Unit testing finds problems early
in the development cycle.
More information about PlatformIO Unit Testing:
- https://docs.platformio.org/page/plus/unit-testing.html

34
README.md Normal file
View File

@ -0,0 +1,34 @@
# Teensy 3.5 DDS Function Generator
Using a Teensy 3.5 and the integrated DAC to generate arbitrary periodic waveforms<br>
## How it works
The [Teensy 3.5](https://www.pjrc.com/store/teensy35.html) is a powerful Arduino-compatible microcontroller running an ARM Cortex-M4 at 120 MHz<br>
For the purpose of this project, it has been overclocked to 168MHz, running with the Fastest Pure Code+LTO optimizer<br>
It has two integrated DACs whose output can vary between 0v and 3.3V<br>
Using classical techniques from DDS (Direct Digital Synthesis) the Teensy can become a fully functional function generator<br>
In particular, the technique used here is the classic Phase Accumulator, together with a Lookup Table. This allows to precompute arbitrary periodic waveforms and then output them at the wanted frequency.<br>
Square waves of variable duty cycles can be generated by simply toggling a digital pin high and low and the correct time
Despite both sine and square wave being output by the teensy are quite clean and seem to have low noise and spurs levels at naked eye, advanced filtering techniques, like Chebyshev, Butterworth or Bessel filters with multiple stages are required.
The desired waveform, frequency and duty cycle can be selected using the I2C LCD Display and an incremental encoder with pushbutton
## Amplification and offset
Sill WIP
## Filters
Still WIP
## Credits
A lot of choices come from following this Degree dissertation paper (Direct Digital Synthesizers: Theory, Design and Applications by Jouko Vankka, Helsinki University of Technology, November 2000): http://lib.tkk.fi/Diss/2000/isbn9512253186/isbn9512253186.pdf<br>
<br>Analog Devices offers a lot of cool guides and papers about DDS, this is one of them: https://www.analog.com/media/en/training-seminars/design-handbooks/Technical-Tutorial-DDS/Section8.pdf<br>
<br>The website [ZipCPU](zipcpu.com) offers a variety of articles about sine wave generation using FPGAs<br>
- https://zipcpu.com/dsp/2017/08/26/quarterwave.html<br>
- https://zipcpu.com/dsp/2017/07/11/simplest-sinewave-generator.html
<br>Stackoverflow and the rest of the internet of course<br>
- https://stackoverflow.com/questions/13466623/how-to-look-up-sine-of-different-frequencies-from-a-fixed-sized-lookup-table<br>
- https://stackoverflow.com/questions/16889426/fm-synthesis-using-phase-accumulator<br>
- https://electronics.stackexchange.com/questions/438935/how-to-calculate-sampling-rate-for-dac-from-its-data-sheet<br>
- https://www.daycounter.com/Calculators/Sine-Generator-Calculator.phtml<br>

View File

@ -1,20 +0,0 @@
From my current research, using the analitic formula for the sine wave in not frequency-accurate and frequency changes by changing the incremental step factor t
Where y=A*sin(wt+q)
Apparently using a lookup table is much easier, since the wave gets precomputed and stored in an array, and then it can be modulated in amplitude and frequency
https://www.daycounter.com/Calculators/Sine-Generator-Calculator.phtml
https://en.wikipedia.org/wiki/Numerically-controlled_oscillator
But how to modulate in frequency?
Apparently phase accumulator is the answer:
https://stackoverflow.com/questions/13466623/how-to-look-up-sine-of-different-frequencies-from-a-fixed-sized-lookup-table (First answer works but i can't understand how the sample rate is involved)
https://stackoverflow.com/questions/16889426/fm-synthesis-using-phase-accumulator
https://zipcpu.com/dsp/2017/07/11/simplest-sinewave-generator.html
And this is how you calculate the sample rate (of the DAC). Look at teensy's uC datasheet. Then just play around with it to suit your needs
https://electronics.stackexchange.com/questions/438935/how-to-calculate-sampling-rate-for-dac-from-its-data-sheet
Even better than using a normal lookup table, is using a lookup table that stores only a quarter of a sine wave (0 to 90°), and then getting the other 3/4 by simmetry
https://zipcpu.com/dsp/2017/08/26/quarterwave.html