Skip to content

Instantly share code, notes, and snippets.

@ericfont
Last active July 28, 2022 04:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ericfont/847ce9a645cf1d3ee89cf1508e926a0f to your computer and use it in GitHub Desktop.
Save ericfont/847ce9a645cf1d3ee89cf1508e926a0f to your computer and use it in GitHub Desktop.
fixed-point-sine outputs on Trinket M0+ to DAC at rate of 291 kHz per sample
/*
Fixed-point sine by Andrew Steadman copied from https://www.nullhardware.com/blog/fixed-point-sine-and-cosine-for-embedded-systems/
Implements the 5-order polynomial approximation to sin(x).
@param i angle (with 2^15 units/circle)
@return 16 bit fixed point Sine value (4.12) (ie: +4096 = +1 & -4096 = -1)
The result is accurate to within +- 1 count. ie: +/-2.44e-4.
*/
int16_t sin_fixedpoint(int16_t i)
{
i <<= 1;
uint8_t c = i<0; //set carry for output pos/neg
if(i == (i|0x4000)) // flip input value to corresponding value in range [0..8192)
i = (1<<15) - i;
i = (i & 0x7FFF) >> 1;
enum {A=3370945099UL, B=2746362156UL, C=292421UL};
enum {n=13, p=32, q=31, r=3, a=12};
uint32_t y = (C*((uint32_t)i))>>n;
y = B - (((uint32_t)i*y)>>r);
y = (uint32_t)i * (y>>n);
y = (uint32_t)i * (y>>n);
y = A - (y>>(p-q));
y = (uint32_t)i * (y>>n);
y = (y+(1UL<<(q-a-1)))>>(q-a); // Rounding
return c ? -y : y;
}
//Cos(x) = sin(x + pi/2)
#define cos_fixedpoint(i) sin_fixedpoint((int16_t)(((uint16_t)(i)) + 8192U))
// rest of test code by Eric Fontaine:
void setup() {
analogWriteResolution(10); // Set analog out resolution to max, 10-bits
analogReadResolution(12); // Set analog input resolution to max, 12-bits
Serial.begin(9600);
}
void loop() {
uint32_t start_microseconds = micros();
for( int i=0; i<32768; i++) {
// very slow math library call
//int16_t biased_sin = (sin(i * 2 * PI / 32768.0f) + 1.0f) * 511.5f;
// much faster integer fixed point sine function
// convert 13-bit signed value to 10-bit unsigned from 0..1023 centered around 511.5
int16_t biased_sin = (sin_fixedpoint(i) + 4096) * 1023 >> 13;
analogWrite(A0, biased_sin);
}
uint32_t end_microseconds = micros();
uint32_t microseconds = end_microseconds - start_microseconds;
uint32_t nanoseconds_per_compute = microseconds * 1000 / 32768;
uint32_t clockcycles_per_compute = microseconds * 48 / 32768;
Serial.printf("%d total µs, averaging %d ns or %d clocks per compute.\n", microseconds, nanoseconds_per_compute, clockcycles_per_compute);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment