add code for sound board. Use attiny-sound-32bit-final
parent
625e4d923b
commit
755befece0
|
@ -0,0 +1,5 @@
|
|||
.pio
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
||||
.vscode/ipch
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"platformio.platformio-ide"
|
||||
],
|
||||
"unwantedRecommendations": [
|
||||
"ms-vscode.cpptools-extension-pack"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
|
||||
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
|
|
@ -0,0 +1,46 @@
|
|||
|
||||
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
|
|
@ -0,0 +1,14 @@
|
|||
; 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:ATtiny412]
|
||||
platform = atmelmegaavr
|
||||
board = ATtiny412
|
||||
framework = arduino
|
|
@ -0,0 +1,44 @@
|
|||
#include <Arduino.h>
|
||||
|
||||
constexpr uint8_t LUT_SIZE = 50;
|
||||
constexpr uint8_t LUT_SIZE_HALF = LUT_SIZE*0.5;
|
||||
constexpr uint8_t LUT_SIZE_QUARTER = LUT_SIZE*0.25;
|
||||
constexpr uint8_t MAX_VALUE = 255;
|
||||
|
||||
constexpr float SAMPLE_FREQ = 73550.7437f;
|
||||
|
||||
uint8_t SINE_LUT[LUT_SIZE];
|
||||
|
||||
//constexpr float delta_phi = 4000.0f / SAMPLE_FREQ * (float)LUT_SIZE;
|
||||
constexpr float delta_phi = 0.054384 * (float)LUT_SIZE;
|
||||
float phase = 0;
|
||||
|
||||
// constexpr uint8_t ddelta_phi = 0.05465 * LUT_SIZE;
|
||||
// uint8_t phase = 0;
|
||||
|
||||
void setup() {
|
||||
DACReference(INTERNAL4V3);
|
||||
|
||||
for (uint8_t i = 0; i < LUT_SIZE; i++) {
|
||||
// SINE_LUT[i] = (0.5f + 0.5*sin((2 * PI * (float)i) / LUT_SIZE)) * MAX_VALUE;
|
||||
|
||||
//For the first quarter of the lut, generate a quarter of a sine wave
|
||||
if(i >= 0 && i <= LUT_SIZE_QUARTER) SINE_LUT[i] = (0.5f + 0.5*sin((2 * PI * (float)i) / LUT_SIZE)) * MAX_VALUE; // (uint8_t) (MAX_VALUE_HALF + (MAX_VALUE_HALF-1) * sin((2 * PI * (float)i) / LUT_SIZE));
|
||||
else if(i > LUT_SIZE_QUARTER && i <= LUT_SIZE_HALF) SINE_LUT[i] = SINE_LUT[(LUT_SIZE_HALF-i)];
|
||||
else SINE_LUT[i] = MAX_VALUE-SINE_LUT[(LUT_SIZE-i)];
|
||||
}
|
||||
|
||||
DAC0.CTRLA |= 0x41;
|
||||
uint8_t t = 0;
|
||||
while(1){
|
||||
// increment phase
|
||||
phase += delta_phi;
|
||||
// handle wraparound
|
||||
if (phase >= LUT_SIZE) phase -= LUT_SIZE;
|
||||
DAC0.DATA = SINE_LUT[(uint8_t)phase];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
|
||||
This directory is intended for PlatformIO Test Runner 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/en/latest/advanced/unit-testing/index.html
|
|
@ -0,0 +1,70 @@
|
|||
// Able to achieve precise 4kHz with AtTiny clocking at 20MHz, LUT_SIZE = 32
|
||||
// dreg needs to be calculated as shown in the paper and then fiddled with by hand, as I really do not know what Fs actually is
|
||||
// Going lower than LUT_SIZE=32 makes the oscilloscope "lose" the trigger point more easily, but is still stable in frequency. A lower LUT_SIZE has the advantage of being easier to tune by hand, but admitingly once you establish a baseline for dreg, using simple proportions to tune the value exactly is not a problem
|
||||
// Using this snippet I calculated Fsample (the frequency with which a generation cycle is executed) to be 1Mhz
|
||||
// It outputs a triangle wave, Fsample is the inverse of an half period (double the frequency of the wave), since a cycle computes the rising edge and the following computes the falling edge
|
||||
/*
|
||||
// increment phase
|
||||
phase_accumulator += dreg;
|
||||
t = SINE_LUT[(phase_accumulator >> 8) & 0x7F];
|
||||
DAC0.DATA = t1;
|
||||
t1 = 255 -t1;
|
||||
*/
|
||||
|
||||
// AtTiny412 running at 20MHz
|
||||
|
||||
// https://www.nxp.com/docs/en/application-note/AN1771.pdf
|
||||
|
||||
// Not all AtTiny's reach exactly 4khz, because of the granularity (as explained in the article) of the decimal part.
|
||||
// My solution: use a 4byte integer instead of a 2 byte one, use a single byte for the integer part and 3 bytes for the decimal part
|
||||
// I limited the decimal part to 1 byte since the AtTiny only has 256B of RAM, the LUT is in RAM, and a 128B LUT is the biggest I can fit in memory while still using power of two
|
||||
|
||||
// try to keep LUT_SIZE to a power of two, so that the masking can be done efficiently
|
||||
constexpr uint8_t LUT_SIZE = 32;
|
||||
constexpr uint8_t LUT_MASK = LUT_SIZE - 1;
|
||||
constexpr uint8_t LUT_SIZE_HALF = LUT_SIZE*0.5;
|
||||
constexpr uint8_t LUT_SIZE_QUARTER = LUT_SIZE*0.25;
|
||||
constexpr uint8_t MAX_VALUE = 255;
|
||||
|
||||
uint8_t SINE_LUT[LUT_SIZE];
|
||||
|
||||
// Delta = (LUT_SIZE * FREQ) / SAMPLE_FREQ
|
||||
// Assume SAMPLE_FREQ 2.26khz
|
||||
// Delta = 128 * 4000 / 1000000 = 0.512
|
||||
// Integer part = 0 mask 0x0
|
||||
// Decimal part = 0.512slightly
|
||||
// 0.512 * 2^24 = 8589934.592, round to 8589935, mask 0x8589935 (calculated with gnome calc)
|
||||
|
||||
// by hand, slightly different than calculated (0x83)
|
||||
uint32_t dreg = 0x42EACB;
|
||||
|
||||
uint32_t phase_accumulator = 0;
|
||||
|
||||
void setup() {
|
||||
DACReference(INTERNAL4V3);
|
||||
noInterrupts();
|
||||
|
||||
for (uint8_t 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) SINE_LUT[i] = (0.5f + 0.5f*sin((2 * PI * (float)i) / LUT_SIZE)) * MAX_VALUE;
|
||||
// For the second quarter, mirror the first quarter along the y axis
|
||||
else if(i > LUT_SIZE_QUARTER && i <= LUT_SIZE_HALF) SINE_LUT[i] = SINE_LUT[(LUT_SIZE_HALF-i)];
|
||||
// For the second half, mirror the first half along the x axis
|
||||
else SINE_LUT[i] = MAX_VALUE-SINE_LUT[(LUT_SIZE-i)];
|
||||
}
|
||||
|
||||
// From megatinycore analogWrite(PIN_PA6)
|
||||
DAC0.CTRLA |= 0x41;
|
||||
while(1){
|
||||
// increment phase. We do not care about overflowing, it's actually beneficial because with can discard the carry bit and wrap back to zero, and start again from the beginning of the LUT without using if statements
|
||||
phase_accumulator += dreg;
|
||||
|
||||
// Output to DAC
|
||||
// Shift to the right to extract the integer portion, mask the first 4 bits (size of LUT)
|
||||
DAC0.DATA = SINE_LUT[(phase_accumulator >> 24) & LUT_MASK];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
// AtTiny412
|
||||
// 16Mhz tuned following https://github.com/SpenceKonde/megaTinyCore/blob/master/megaavr/extras/Ref_Tuning.md
|
||||
// millis/micros disabled
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
constexpr uint8_t LUT_SIZE = 32;
|
||||
constexpr uint8_t LUT_SIZE_HALF = LUT_SIZE*0.5;
|
||||
constexpr uint8_t LUT_SIZE_QUARTER = LUT_SIZE*0.25;
|
||||
constexpr uint8_t MAX_VALUE = 64;
|
||||
|
||||
constexpr float SAMPLE_FREQ = 57500.0;
|
||||
|
||||
uint8_t SINE_LUT[LUT_SIZE];
|
||||
|
||||
//constexpr float delta_phi = 4000.0f / SAMPLE_FREQ * (float)LUT_SIZE;
|
||||
constexpr float delta_phi = 0.068987212 * (float)LUT_SIZE;
|
||||
float phase = 0;
|
||||
|
||||
// constexpr uint8_t ddelta_phi = 0.05465 * LUT_SIZE;
|
||||
// uint8_t phase = 0;
|
||||
|
||||
void setup() {
|
||||
DACReference(INTERNAL4V3);
|
||||
|
||||
for (uint8_t i = 0; i < LUT_SIZE; i++) {
|
||||
// SINE_LUT[i] = (0.5f + 0.5*sin((2 * PI * (float)i) / LUT_SIZE)) * MAX_VALUE;
|
||||
|
||||
//For the first quarter of the lut, generate a quarter of a sine wave
|
||||
if(i >= 0 && i <= LUT_SIZE_QUARTER) SINE_LUT[i] = (0.5f + 0.5*sin((2 * PI * (float)i) / LUT_SIZE)) * MAX_VALUE; // (uint8_t) (MAX_VALUE_HALF + (MAX_VALUE_HALF-1) * sin((2 * PI * (float)i) / LUT_SIZE));
|
||||
else if(i > LUT_SIZE_QUARTER && i <= LUT_SIZE_HALF) SINE_LUT[i] = SINE_LUT[(LUT_SIZE_HALF-i)];
|
||||
else SINE_LUT[i] = MAX_VALUE-SINE_LUT[(LUT_SIZE-i)];
|
||||
}
|
||||
|
||||
DAC0.CTRLA |= 0x41;
|
||||
uint8_t t = 0;
|
||||
uint16_t t1 = 0, t2 = 1010;
|
||||
while(1){
|
||||
t1 += t2;
|
||||
if(t1 >= 10000){
|
||||
//t = (t+1) % LUT_SIZE;
|
||||
|
||||
// a bit of bit-shifting magic
|
||||
t *= ! ((t >> 5) & 1);
|
||||
t1 -= 10000;
|
||||
DAC0.DATA = SINE_LUT[t++];
|
||||
}
|
||||
|
||||
|
||||
// less noise but less control in frequency
|
||||
//if (t == LUT_SIZE) t = 0 ;
|
||||
// increment phase
|
||||
|
||||
/*phase += delta_phi;
|
||||
// handle wraparound
|
||||
if (phase >= LUT_SIZE) phase -= LUT_SIZE;
|
||||
DAC0.DATA = SINE_LUT[(uint8_t)phase];
|
||||
*/
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
// AtTiny412
|
||||
// 16Mhz tuned following https://github.com/SpenceKonde/megaTinyCore/blob/master/megaavr/extras/Ref_Tuning.md
|
||||
// millis/micros disabled
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
constexpr uint8_t LUT_SIZE = 24;
|
||||
constexpr uint8_t LUT_SIZE_HALF = LUT_SIZE*0.5;
|
||||
constexpr uint8_t LUT_SIZE_QUARTER = LUT_SIZE*0.25;
|
||||
constexpr uint8_t MAX_VALUE = 64;
|
||||
|
||||
constexpr float SAMPLE_FREQ = 57500.0;
|
||||
|
||||
uint8_t SINE_LUT[LUT_SIZE];
|
||||
|
||||
//constexpr float delta_phi = 4000.0f / SAMPLE_FREQ * (float)LUT_SIZE;
|
||||
constexpr float delta_phi = 0.07052234f * (float)LUT_SIZE;
|
||||
float phase = 0;
|
||||
|
||||
// constexpr uint8_t ddelta_phi = 0.05465 * LUT_SIZE;
|
||||
// uint8_t phase = 0;
|
||||
|
||||
void setup() {
|
||||
DACReference(INTERNAL4V3);
|
||||
|
||||
for (uint8_t i = 0; i < LUT_SIZE; i++) {
|
||||
// SINE_LUT[i] = (0.5f + 0.5*sin((2 * PI * (float)i) / LUT_SIZE)) * MAX_VALUE;
|
||||
|
||||
//For the first quarter of the lut, generate a quarter of a sine wave
|
||||
if(i >= 0 && i <= LUT_SIZE_QUARTER) SINE_LUT[i] = (0.5f + 0.5*sin((2 * PI * (float)i) / LUT_SIZE)) * MAX_VALUE; // (uint8_t) (MAX_VALUE_HALF + (MAX_VALUE_HALF-1) * sin((2 * PI * (float)i) / LUT_SIZE));
|
||||
else if(i > LUT_SIZE_QUARTER && i <= LUT_SIZE_HALF) SINE_LUT[i] = SINE_LUT[(LUT_SIZE_HALF-i)];
|
||||
else SINE_LUT[i] = MAX_VALUE-SINE_LUT[(LUT_SIZE-i)];
|
||||
}
|
||||
|
||||
DAC0.CTRLA |= 0x41;
|
||||
while(1){
|
||||
|
||||
phase += delta_phi;
|
||||
// handle wraparound
|
||||
if (phase >= LUT_SIZE) phase -= LUT_SIZE;
|
||||
DAC0.DATA = SINE_LUT[(uint8_t)phase];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
}
|
Loading…
Reference in New Issue