Files
NeoPixelBus/src/internal/methods/FractionClk.c
T
Michael Miller 8a13d6f0f4 Three four cadences (#827)
i2s x1, x8, x16 now use a 3 step cadence (33% & 66% pulse width) by default saving 25% memory in DMA buffer.
ESP32 & ESP32S2
2024-08-25 19:59:32 -07:00

160 lines
4.7 KiB
C

/*-------------------------------------------------------------------------
NeoPixel library helper functions.
Written by Michael C. Miller.
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
-------------------------------------------------------------------------
This file is part of the Makuna/NeoPixelBus library.
NeoPixelBus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
NeoPixelBus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with NeoPixel. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include "stdlib.h"
// UnitDecimalToFractionClks
// Given unit decimal (floating point value less than 1.0),
// return a fraction numerator and denominator that closely matches that
//
// resultN - the address of the variable to place the numerator result
// resultD - the address of the variable to place the denominator result
// unitDecimal - the decimal value that is less than 1.0 and greater than/equal 0.0
// accuracy - the accuracy needed to match for a fractional (0.000001 is a good start)
//
void UnitDecimalToFractionClks(uint8_t* resultN,
uint8_t* resultD,
double unitDecimal,
double accuracy)
{
if (unitDecimal <= accuracy)
{
// no fractional
*resultN = 0;
*resultD = 1;
return;
}
else if (unitDecimal <= (1.0 / 63.0))
{
// lowest fractional
*resultN = 0;
*resultD = 2;
return;
}
else if (unitDecimal >= (62.0 / 63.0))
{
// highest fractional
*resultN = 2;
*resultD = 2;
return;
}
// printf("\nSearching for %f\n", unitDecimal);
// The lower fraction is 0 / 1
uint16_t lowerN = 0;
uint16_t lowerD = 1;
double lowerDelta = unitDecimal;
// The upper fraction is 1 / 1
uint16_t upperN = 1;
uint16_t upperD = 1;
double upperDelta = 1.0 - unitDecimal;
uint16_t closestN = 0;
uint16_t closestD = 1;
double closestDelta = lowerDelta;
for (;;)
{
// The middle fraction is
// (lowerN + upperN) / (lowerD + upperD)
uint16_t middleN = lowerN + upperN;
uint16_t middleD = lowerD + upperD;
double middleUnit = (double)middleN / middleD;
if (middleD > 63)
{
// exceeded our clock bits so break out
// and use closest we found so far
break;
}
if (middleD * (unitDecimal + accuracy) < middleN)
{
// middle is our new upper
upperN = middleN;
upperD = middleD;
upperDelta = middleUnit - unitDecimal;
}
else if (middleN < (unitDecimal - accuracy) * middleD)
{
// middle is our new lower
lowerN = middleN;
lowerD = middleD;
lowerDelta = unitDecimal - middleUnit;
}
else
{
// middle is our best fraction
*resultN = middleN;
*resultD = middleD;
// printf(" Match %d/%d = %f (%f)\n", middleN, middleD, middleUnit, unitDecimal - middleUnit);
return;
}
// track the closest fraction so far (ONLY THE UPPER, so allow only slower Kbps)
//
//if (upperDelta < lowerDelta)
{
if (upperDelta < closestDelta)
{
closestN = upperN;
closestD = upperD;
closestDelta = upperDelta;
// printf(" Upper %d/%d = %f (%f)\n", closestN, closestD, middleUnit, closestDelta);
}
}
/*
else
{
if (lowerDelta < closestDelta)
{
closestN = lowerN;
closestD = lowerD;
closestDelta = lowerDelta;
printf(" Lower %d/%d = %f (%f)\n", closestN, closestD, middleUnit, closestDelta);
}
}
*/
}
// printf(" Closest %d/%d = %f (%f)\n\n", closestN, closestD, (double)closestN / closestD, closestDelta);
// no perfect match, use the closest we found
//
*resultN = closestN;
*resultD = closestD;
}