12 Bit ADC waarde omzetten naar LOG bereik

blackdog

Golden Member

Hi,

Ik heb al een tijdje behoefte aan een generator die ik tot 1MHZ ka sweepen met de hand in een aantal bereiken.
Deze genrator zal worden uitgerust met zo'n klein AD9833 DDS printje aangestuurd door een Arduino.

Wat wil ik nu gaan doen om de frequenty in te stellen...
Ik heb een hele mooie soepel lopende 330° potmeter, die wil ik inlezen via een ADS1115 ADC en dan wat standaard bereiken maken, zodat ik snel kan swepen over een bereik.
Vooral bij het meten aan filters en frequentie responce van een D.U.T is dat heel makkelijk.
Dit is een beetje een drama, bij de digitale generatoren die ik heb(4 stuks)

Voorbeelden bereiken
1Hz tot 1kHz
10Hz tot 50khz
1kHz tot 500kHz
10kHz tot 1MHz
100kHz tot 3MHz

Variaties kan ik later nog doen als nodig.
De bedoeling is dat de bereiken net iets meer van de hierboven gestelde waarden kunnen halen, dus het uiteindelijke bereik zal iets lager en iets hoger worden, net als bij een normale Sinus genrator.

Bij de oude generatoren zoals de HP3310A zie je een lineaire schaal van 1 maar eigenlijk van 5 tot 50 schaaldelen.
En de Philips PM 5132 heeft een schaal die loopt van 0.6 maar eigenlijk 1 tot 20 met overloop naar 22 schaaldelen.

Ik ben er nog niet uit wat wenselijk is tijdens het gebruik, vroeger had ik een leuke XR2206 genrator die ik in 1x van 10Hz tot ruim 50kHz kon sweepen met één draai aan de potmeter waar een schaaltje op zat.
Als extra zat er dan een fijnregel potmeter bij, dat kan bij mijn opset ook nog steeds door een kleine waarde bij de hoofdpotmeter te mengen.

Wat ik nu wil doen is dit, Een mooi Referentie IC de frequentie potmeter laten voeden, wat filtering aan de loper en dan op een ingang van de ADS1115 ADC zetten.
Dan komt daar een 15 Bits waarde uit aan de hand van de potmeter stand en dit moet dan geschaald worden naar de waarde die naar de AD9833 DDS chip gaat.

Dus 1 tot 32768 moet dan worden omgezet in 10Hz tot 50kHz bereik, zoiets als dit hieronder.

c code:

Freq = map(analogRead(potmeter), 5, 32760, 10, 50000 );

Maar nu dan de vraag, als ik dat bereik nu logaritmisch wil hebben, hoe werkt dat schalen dan?

Er komen nog meer vragen over de software, maar dit wordt de basis van dit meetinstrumentje waarme ik wil gaan testen.

Nog wat info
Verder probeer het zo vlak mogelijk te maken, wat de frequentie karakteristiek betreft.
Gisteren vier spoelen gewikkeld op Neosid spoelvormen voor het negende order filter volgens Chebychev, deze bouwsetjes voor spoelen heb ik al zeker 30 jaar liggen. :)
Volgens mij nog gekocht bij de Electronica winkel vlak bij het Okura hotel hier in Amsterdam.

Bij het filter heb ik gekozen voor transformatie van de impedantie, het pakket van Iowa Hills RF Filter Designer in de enige software die ik geinstalleerd heb waarmee dat kan.
De ingang van het filter is gekozen op 200Ω en de uitgang op 50Ω, met de frequentie rond 3.3MHz heb ik een beetje geschoven, zodat ik redelijke bruikbare condensator waarden kreeg.

Het filter is tot iets meer dan 3MHZ plank recht, minder dan 0,1dB, dit natuurlijk wel als ik de goede componenten gebruik en de opbouw klopt.
Maar uitgangspunt is max 1MHz, ik heb nog nooit met dit DDS IC gespeeld, dus ik moet eerst nog zien hoe goed het is.

Het gaat mij dus vooral om een aantal makkelijk te gebruiken sweep bereiken op deze generator.
Precisie frequentie instellingen doe ik wel met een van de andere genratoren die ik heb, net als vervormings metingen, deze chip komt toch niet verder dan 0,1%.

Vragen opmerkingen, ik hoor het graag!

Groet,
Bram

You have your way. I have my way. As for the right way, the correct way, and the only way, it does not exist.
Arco

Special Member

Ik heb het met toetsen gedaan. (ik probeer alles wat draait te vermijden; het gaat toch vroeger of later kapot... ;)
De AD9833 kun je met de +/- instellen. (na 1 sec. herhaalt die met 10 stapjes/sec en na 4 sec. met 100 stapjes/sec)
Met de up/down kun je de stapgrootte instellen: steeds maal 10 of gedeeld door 10. (in mijn geval in stapjes van 0.025/0.25/2.5Hz)

Werkt best handig en snel.

Arco - "Simplicity is a prerequisite for reliability" - hard-, firm-, en software ontwikkeling: www.arcovox.com
blackdog

Golden Member

Hi Arco,

Dat is absoluut niet wat ik wil hebben! :+

Dat is een drama voor het meten aan filters en of frequentie responces van versterkers.
Er is voldoende goed materiaal te verkrijgen, ik gebruik hiervoor geen 2€ potmeter!

Ik heb knoppen zat op mijn vier moderne functie generatoren om te sweepen tot op een uH de frequentie in te stellen enz , enz.
Het gaat om een goede draaiknop die de frequentie van de generator bepaald in een aantal bereiken.
Als je veel van dat soort metingen zou doen, dan begrijp je direct wat ik bedoel. ;)

Groet,
Bram

You have your way. I have my way. As for the right way, the correct way, and the only way, it does not exist.

Kan ook met een rotary encoder.

Rotary steps omrekenen naar frequentie kan bijv met 40 steps per decade:

code:



// Convert LogSteps to Frequency in Hz
// - LogSteps is an actual measure for the frequency.
//    - 40 steps per decade :  
//        LogSteps  Frequency  
//      -   0   ->     1 Hz
//      -  40   ->    10 Hz 
//      -  80   ->   100 Hz 
//      - 120   ->  1000 Hz 
//      - 160   ->    10 kHz
//      - 200   ->   100 kHz
//      - 240   ->  1000 kHz
//      - 280   ->    10 MHz
 
float CalculateLogFrequency(uint16_t LogSteps)
{  
   // Start with a medium frequency
   float Frequency = 1.0000;

   // First handle Steps within Decade
   uint16_t Steps = LogSteps % 40;
   while(Steps)
   {  Steps -= 1;
      // Multiply by 1.0592 for each step
      // - (Results in factor 10 after 40 multiplications)
      Frequency *= 1.0592;
   }

   // Then goto the required decade
   uint16_t Decade = LogSteps / 40;
   while(Decade)
   {  Decade -= 1;
      Frequency *= 10.;
   }
   return Frequency;
}
blackdog

Golden Member

Hi deKees,

Nop, ik wil geen rotary encoder, alhoewel ik nog wel een nieuwe hele mooie HP heb liggen, optisch met 256 pulsen per omwenteling.

Mijn 330° potmeter met 15 bit resolutie zal veel mooier draaien, welke prettige is bij het meten, dan b.v. een rotatie encoder waar ook geen stop op zit.

Groet,
Bram

You have your way. I have my way. As for the right way, the correct way, and the only way, it does not exist.
Arco

Special Member

Een potmeter met 15 bit resolutie klinkt toch echt als een rotary encoder... :) (een analoge pot heeft geen 'bits resolutie')
Met toetsen is ook een prima 'sweep' interface te maken: bereik en snelheid 1x instellen en met 1 toetsdruk kun je dan 'sweepen'...

Arco - "Simplicity is a prerequisite for reliability" - hard-, firm-, en software ontwikkeling: www.arcovox.com
blackdog

Golden Member

Hi Arco,

En ik denk dat je het niet begrijpt hoe er gemeten wordt of bedoeling is. ;)
Kan ook aan mij liggen, dat ik het niet duidelijk genoeg uitleg, maar ik weer zeker dat Gertjan Miedema mij direct begrijpt wat ik wil bereiken.

Ik zie jouw niet heen en weer draaien met 1-Toets...

Groet,
Bram

You have your way. I have my way. As for the right way, the correct way, and the only way, it does not exist.
benleentje

Golden Member

Kan ook aan mij liggen, dat ik het niet duidelijk genoeg uitleg,

Het is mij ook in het geheel niet duidelijk wat je wilt met het logbereik. Feitlijk zit er geen verschil in een lineair of logbereik alleen de schaling is anders en is eigenlijk alleen in grafieken belangrijk omdat als die lineair waren ze heel erg groot zijn.

Als je een lineaire potmeter digitaal inleest dan kan je daar via software wel een logaritme aangeven door elke volgende stap steeds een grotere frequentie sprong te laten maken dat is wat deKees laat zien.

Mijn 330° potmeter met 15 bit resolutie zal veel mooier draaien,

De potmeter zal wellicht mooier draaier maar softwarematig moet er meer gebeuren en je zit met de traagheid van de ADC zelf, dan heb je met een encoder minder vertraging want die kan op een interrupt pin worden aangesloten. Je kan de ADS1115 een stuk sneller uitlezen maar dan heb je geen 15 bit meer aan resolutie die word lager. Dat zal je zelf moeten testen wat het beste resultaat geeft.

Mensen zijn soms net als een gelijkrichter, ze willen graag hun gelijk hebben.

Wat je zoekt is de exp(x) funktie.
Die is beschikbaar in #include <math.h>

Maar die werkt met grondtal e. Omrekenen naar grondtal 10 is vermenigvuldingen met log(10);

Funktie wordt dan:

code:


   float f = exp(x * log(10));

Dit geeft 10x.

Als je een bereik van 1 Hz tot 1 MHz wilt, dan moet x dus tussen 0 en 6 blijven. (100..106)

Dus de omrekenen gaat dan:

code:


#include <math.h>

float Frequency(int16_t x)
{  return exp( (x * 6. / 32768) * log(10) );
}

PS: Let op de punt bij '6.' Die zorgt ervoor dat het resultaat een float wordt. Je wilt hier geen integer als parameter voor de exp() funktie.

[Bericht gewijzigd door deKees op zondag 8 november 2020 01:23:49 (12%)

blackdog

Golden Member

Hi benleentje,

Het enige waar ik voor nu hulp bij nodig heb is het stukje code dat de ADC waarde omzet naar een logaritmische schaal voor de frequentie zoals ik het al heb aangegeven.
Ik weet dus niet hoe dit:

c code:

Freq = map(analogRead(potmeter), 5, 32760, 10, 51000);

kan omzetten naar een logaritmische versie.

Alle andere manieren om de frequentie in te stellen zijn niet van belang, ik had al aangegeven dat ik daar vier andere generatoren voor heb.
Het logaritmisch omzetten zorgt er voor dat de frequenties in het onderste bereik nog steeds redelijk zijn in te stellen.
Jammer genoeg kan ik op het ogenblik geen foto vinden van mijn oude generator, dit om het nog wat duidelijker te maken...

Groet,
Bram

You have your way. I have my way. As for the right way, the correct way, and the only way, it does not exist.

Het is super simpel.... Mits je voldoende computer-kracht hebt waar die berekening moet plaatsvinden.

De totale range gaat van 10 tot 50000 Hz. Dat is een factor 5000. Neem de natuurlijke log daarvan: ln (5000) = 8.5172 .

De 15bit range 0...32767 willen we dan op 0...8.5172 mappen, dus delen door 32767 en vermenigvuldigen met 8.5172.

c code:


#define MAXF 50000
#define MINF 10.0
#define ADCMAX 32767
#define CONSTANT (log(MAXF/MINF)/ADCMAX)
in = read_adc ();
freq = MINF * exp (in*CONSTANT);  

Moet het met minder rekenkracht (kan je je de "exp" functie niet veroorloven) of geheel analoog, dan wil ik nog steeds wel meedenken, maar dat kost wat meer moeite.

Edit: Voor de zekerheid nog even zelf geprobeerd: Werkte in 1x:

c code:


#include <math.h>
#include <stdio.h>
#include <stdlib.h>

int main (int argc, char **argv)
{
  int in; 
  double freq;

  if (argc > 1) in = atoi (argv[1]);
  else in = rand () % 32768;

#define MAXF 50000
#define MINF 10.0
#define ADCMAX 32767
#define CONSTANT (log(MAXF/MINF)/ADCMAX)
//in = read_adc ();
  freq = MINF * exp (in*CONSTANT);  

  printf ("%d -> %lf\n", in, freq);
  exit (0);
}

Ohja, Als het goed is, dan wordt de log in de CONSTANT door de compiler tijdens het compileren uitgerekend, dus dat gebeurt niet run-time. Als je je daar zorgen over maakt kan je hem zelf uitrekenen

code:


//#define CONSTANT (log(MAXF/MINF)/ADCMAX)
#define CONSTANT .00025993204112113521

[Bericht gewijzigd door rew op zondag 8 november 2020 10:30:27 (37%)

four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/
blackdog

Golden Member

Hi,

Heren, dank vast voor de input!

rew, ik had al een vermoede dat die log functie al ik zoiets zou hebben, aardig wat processorkracht zou gaan opeten...
Het doel is nu juist om soepeltjes over een vrij groot gebied te sweepen en de responce van het draaien aan de potmeter "analoog" verloopt.
Ik heb hier wel een aantal krachtige Teensy printjes liggen, en dat is niet de Teensy LC die wat vaker bij mij voorbij komt, maar hun beste model.

Dus misschien gaat het daarmee wel soepeler, een tweede belemmerende factor, is het analoge deel de potmeter moet "ontruist" worden.
Dat kan door vaker te samplen en dan te middelen of de ADC langzamer te laten te laten samplen, De ADS1115 heeft daar meerdere settings voor.

Asl ik een analoge temperatuur sensor middel, dan kan dit over seconden in tijd worden gedaan, bij een goed analoog gevoel zoals bij een spanings gestuurde functie generator als de XR2206 is "redelijk direct" reageren mijn uitgangspunt.
Het is niet de bedoeling dat de frequentie achter mijn draaibeweging van de frequentie knop aanhobbeld. :)

Ik zal gewoon wat moeten gaan proberen, en als ik denk aa nrew zijn opmerking betreffende de processor kracht die misschien nodig is,
dat ik begin met een dikke microcontroler!
Het display moet ook worden ge-updated op een redelijk vloeiende manier, en dat vreet meestal tijd.
Er is zijn eena aantal displays onderweg uit China die een snelle SPI bus hebben, we zullen het gaan zien.

Eerst maar eens proberen een basis Sinus uit het printje krijgen en dit dan samen met de lineaire frequentie functie om het "gevoel" te testen bij frequentie verandering.

Groet,
Bram

You have your way. I have my way. As for the right way, the correct way, and the only way, it does not exist.

Valt reuze mee.

Die exp() funktie geeft een antwoord in 0.2 milliseconde op een UNO R3. Total code size = 4K.

code:



#include <math.h>

float K = (6. / 32768) * log(10);

void Frequency(uint16_t Count)
{
   uint32_t T = micros();
   float f = exp( Count * K );
   T = micros() - T;
   
   Serial.print("Frequency : ");
   Serial.print(Count);
   Serial.print("->");
   Serial.print(f);
   Serial.print(", T = " );
   Serial.print(T);
   Serial.println("uS");
 }


void loop() 
{
  Serial.print("Hello World\n");
  Frequency(0); 
  Frequency(2); 
  Frequency(32765); 
  Frequency(32766); 
  Frequency(32767); 
  Frequency(32768); 
  delay(1000);
}

En inderdaad. De compiler is slim genoeg om de log tijdens compileren al uit te rekenen. Dit geeft hetzelfde resultaat, ook in 200 microseconden.

code:


  float f = exp( Count * (6. / 32768) * log(10) );

Een opmerking terzijde, maar bij het uitlezen van een potentiometer is men enkel geïnteresseerd in de verhouding van de weerstanden.

Wat ik nu wil doen is dit, Een mooi Referentie IC de frequentie potmeter laten voeden, wat filtering aan de loper en dan op een ingang van de ADS1115 ADC zetten.

Een goede referentie is daarom niet nodig, mits deze maar dezelfde is voor de potmeter en de ADC.

Persoonlijk zou ik in combinatie met een uC tegenwoordig ook liever een encoder pakken, maar dat moet hier dan wel een absolute zijn (of men moet altijd eerst naar 0 draaien om het Z kanaal te gebruiken voor een reset). Als dat geen bezwaar is volstaat een goedkope 2000 ppr volgens mij zeker, dat zijn 8000 overgangen.
De beschikbare libraries zijn erg goed en nemen vrijwel geen processortijd, zeker als er interrupts beschikbaar zijn.

Leuk project, ik ben ongeveer hetzelfde aan het maken met de AD9850. Deze had ik nog liggen anders had ik ook de AD9833 gepakt. Die kan ook driehoek en blok maken. Als processor heb ik de ESP32 gepakt, dit vanwege de wifi zodat ik hem later via de pc kan aansturen.

Wellicht wil je in software ook nog oplossen dat je ADC waarde niet constant staat te flipperen als de spanning tussen twee waardes in zit. Ik weet dat je geen rotary encoder wilt, maar er zijn ook encoders die aanvoelen als een potmeter. De SM3300 serie van Delta is hiermee uitgerust. Een voordeel hiervan is dat je het probleem van de ADC niet hebt. Een ander voordeel, stel je wilt hem ook kunnen besturen vanaf de pc, dan kun je de waarde gewoon overschrijven. De encoder is namelijk gewoon een teller in software. Als je een potmeter pakt, hoe ga je dat dan oplossen? Maar als je daar allemaal geen behoefte aan hebt dan is dat verder geen issue natuurlijk.

Ik ben ook erg benieuwd naar de versterker die hierachter komt. Zeker als er een offset regeling in komt of zelfs bestuurbaar via software.

PE2BAS

Toch knap dat je een potmeter over 330 gaden handmatig kunt instellen in 4096 stapjes :)
Lijkt me dat de ADC van die arduino met z'n 1024 stappen prima voldoet. Maar misschien trillen mijn handen meer dan de jouwe ;)

blackdog

Golden Member

Hi,

Pff wat een dag!
MS die met zijn Laatste Windows 10 versie 20H2 het netwerk verkeer omzeep helpt, rond de 4 uur aan het uitzoeken geweest naar het probleem.
Een vriend bracht de oplossing, in het register de waarde voor het starten van de DNS client van 4 naar 2 zetten.
En Walla je netwerkshares zijn weer bereikbaar, kloothommels van MS!!!!

Waarom zeg ik dit hier, omdat al die tijd hierdoor niet voor mijn leuke projecten beschikbaar was.
En misschien hebben jullie er ook nog iets aan, dus kan je wel je Server zien, maar de share er op niet gebruiken, zoek dan op wat ik hierboven aangaf.

OK weer even helemaal on topic en we beginen bij Zonnepaneeltje. ;)
1024 stapjes is bij gebruik van een flinke knop eigenlijk te weinig, en 12 bit met zijn ruim 4000 stapjes kan je een aardig analoog gevoel opleveren.
Ik heb het hier wel over een echte potmeter b.v. een Cermet model.

Wat betreft het trillen, al ik net de slaapkamermuren heb geschuurd, is 8 bit ook voldoende. *grin*

Aart en hardbass
Jullie opmerkingen hebben mij aan het denken gezet...
Ik hield bij de eerste gedachten vast aan de goede potmeters die ik op voorraad heb en mijn ervaring met soortgelijke meetapparatuur, wwaar zo'n uitgebreide logschaal knop op zat met een 300 tot 330 graden schaal.

Maar toen schoot mij te binnen dat ik ook nog mooie echte rotary encoders heb, he HP NEDS-7500 en dit China model LPD3806-600BM-G5-24C welke 600 pulsen is per omwenteling.

Dan kan ik kiezen voor b.v. een up/down counter voor twee volle omwentelingen en dit dan schalen naar de gewenste frequentie band.
Misschien drie omwentelingen om een zeer groot bereik te beslaan van b.v. 10Hz tot 100kHz met een logaritmisch gedrag.

Het 600-pulsen per omwenteling model loopt zeet mooi soepel en is uitgerust met kochellagers.
En omdat er dan geen ADC nodig is met filtering wordt het geheel simpeler, dus dat ga ik als eerste testen.

Maar ik ga morgen pas wat testen, ben vandaag al genoeg mentaal leeg gelopen. :+

Groet,
Bram

You have your way. I have my way. As for the right way, the correct way, and the only way, it does not exist.

Rustig aan :)

Ik heb even gekeken wat ik precies gebruik, en ben te spreken over deze library voor encoders: https://github.com/PaulStoffregen/Encoder

blackdog

Golden Member

Hi Aart,

Voor ik nu plat ga om een tukkie te doen, heb ik nog even naar de library van Paul Stoffregen gekeken voor de encoders.

Ik heb nog nooit een zo onduidelijke library omschrijving gezien, ligt dit aan mij, of aan de schrijver hiervan?

Ik heb ook nog even getest met de 600-pulsen per omwenteling encoder, geeft perfecte pulsen zonder dender, en daar ben ik blij mee.
De flank snelheid is een kleine 60nSec en dat is ook mooi.

Morgen misschien meer...

Groet,
Bram

You have your way. I have my way. As for the right way, the correct way, and the only way, it does not exist.

Paul is net als ik een beetje dyslectish. Maar technisch is ie heel goed. Ik verwacht redelijk betrouwbare software van hem.

Dat gezegd zijnde ik ben gebrowsed tot de voorpagina van die library en zie dan meteen dat kennelijk de doelgroep zo'n encoder is met 16 stapjes in een omwenteling. Hmmm. Ik weet niet of dat soepeltjes gaat werken voor de 600 stapjes per omwenteling die je hier hebt.

Ik heb voor de STM32 een project met encoder. Daar heb ik zelf een counter ingesteld in "encoder" mode. Vervolgens kan je gewoon 100x per seconde kijken of de counter veranderd is en dan zo nodig de frequentie uitrekenen en instellen. Wil je snellere code dan gewoon de EXP functie aanroepen dan kan dat ook. Effe puzzlen.

OK. Done.

c code:



#include <stdio.h>
#include <stdlib.h> 
#include <math.h>

int tbl[65];


unsigned int myexp (int v)
{
  int p1,p2, p3;
  int r; 


  // split v into bitfields: 4/6/5 bits. 
  p1 = (v >> 11) & 0xf; 
  p2 = (v >> 5) & 0x3f;
  p3 = (v >> 0) & 0x1f;
  
  r = (32 - p3) * tbl[p2] + p3 * tbl[p2+1];
  
  return (r >> (20-p1));
}




int main (int argc, char **argv)
{
  int i;
  for (i=0;i<65;i++)
    tbl [i] = 32768 * (exp (i/64.0 * log (2.0)));
  if (0) {
    printf ("int tbl[65] = {\n   ");
    for (i=0;i<64;i++)
      printf ("%d,%s", tbl[i],  ((i-7)&7)?" ":"\n   ");
    printf ("%d};\n\n", tbl[i]);
  }
  for (i=0;i<32768;i++) 
    printf ("%d: %d %d\n", 
	   i,
	   myexp (i), 
	   (int) (  exp (i / 2048.0 * log (2.0))));
  

}

Voor "embedded spul" doe je natuurlijk de tabel in een statische tabel in flash. Effe vantevoren uitrekenen. Voor m'n test met een 3GHz 64 bit intel CPU... reken ik hem "live" uit bij het opstarten van het testprogramma.... In Gnuplot liggen deze twee curves behoorlijk dicht bij mekaar. De stuksgewijze lineaire stukken zijn niet te zien. In de gauwigheid zie ik dat ik op 32768 input waardes 503 keer 1 te laag zit, 31661 keer precies hetzelfde als de floating point waarde en 604 keer zit ik 1 te hoog. Geen afwijkingen om over naar huis te schrijven.

Dit voorbeeld is niet netjes geparametriseerd. Dus wil je de tabel kleiner maken (hetgeen goed lijkt te kunnen) dan zul je wat "magic numbers" moeten veranderen.

Oh. Voor deze efficientie moet je betalen: Met het 15-bit input bereik krijg je een 16-bit (logarithmish) uitvoer bereik.

@dekees, Het zou leuk zijn om deze ook nog door jou test-infrastructuur te halen. :-) Haal ik 100x sneller? Nee, zal wel niet(*). Die R3 is nog steeds op de 8-bitter atmega328 gebaseerd. Bram geeft aan naar een 32-bit arm aan het kijken te zijn, dus ik ging er van uit dat 32-bit integers "gratis" zijn.

Op mijn 48MHz ARM duurt 1 aanroep van mijn exp functie ongeveer 700ns. (32768 aanroepen in 21.9 milliseconde).

(*) Nouja bij nader inzien... IK haal 300x sneller dan jou Uno R3. :-)

Update: Iets gelezen over de encoder lib van Paul. Hij heeft het over interrupt frequentie en efficientie. Leuk voor hem, maar mijn (goedkope) STM32 heeft een encoder mode in de counter/timer modules, zodat ik alleen het CNT register hoef uit te lezen wanneer ik dat wil weten. Nu haalt ie rond de 8 microseconden per interrupt... dat vind ik eigenlijk ook belabberd. Even getest... Als gewone functie (dus zonder overhead voor een interrupt context save) kost mijn software-encoder versie 480ns per call (ik heb de functie 1M keer aangeroepen in 482 milliseconde).

four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/

Ik vind het knap. Je hebt hier dus een eigen functie gemaakt om de exp(x) uit te rekenen, maar dan met integers.

Ik heb het in de test gezet (op een 8-bit Uno R3) en zie dat die ongeveer een factor 10 sneller is.
- exp(x) doet alle 32768 mogelijkheden in 6595888 microseconden.
Dus gemiddeld 200 micro seconden per berekening.

- MyExp(x) doet het in 657468 microseconden
Dus gemiddeld 20 microseconden per keer, factor 10 sneller.

Maar ja, dan krijg je wel een integer resultaat. En dat is lastig :
Count = 0 --> 1 Hz
Count = 2048 --> 2 Hz
Count = 3246 --> 3 Hz
Count = 4097 --> 4 Hz
Count = 4756 --> 5 Hz
Count = 5295 --> 6 Hz
Count = 5750 --> 7 Hz
Count = 6145 --> 8 Hz
Count = 6493 --> 9 Hz
Count = 6804 --> 10 Hz

Enz.

Dus vooral in het begin stijgt de frequentie maar langzaam, dus dan heb je volgens mij de fracties van de frequentie ook nodig. Die krijg je met de exp(x) gratis kado.

En dan nog, als je 5000 keer per seconde de exp(x) kunt uitrekenen dan is dat meer dan snel genoeg voor een user interface. Verdere optimalisatie is dan niet erg nuttig meer volgens mij.

Inderdaad knap stukje code!

De reden dat dit zo snel is, is mede dankzij het werken met integers. Als je een processor zonder FPU hebt tenminste. Je levert ook iets aan geheugen in voor de snelheid, als is dat maar 260 bytes, Er vanuit gaande dat een int 32 bit is. Persoonlijk ben ik meer fan van de stdint lib, dan weet je altijd met wat je werkt, ongeacht de processor. Wel een beetje lastig te begrijpen. Voor velen is dit zoon stukje code "use and dont ask why". Zelf heb ik hem ook nog niet helemaal uitgeplozen. :)

@deKees, Kun je de input niet gewoon x10 doen en het resultaat delen door 10? Dan hou je 1 decimaal over. Ik weet alleen niet uit m'n Hoofd of dat goed komt met exponenten.

Qua processorkeuze, zelf ben ik al lang afgestapt van de 8 bitters. De 32 bit processoren zijn niet zo heel veel duurder meer en je krijgt er een hoop snelheid flash en ram voor terug. Let op dat het schrijven van efficiëntere code ook duur is. Niet alleen qua tijd, maar zeker als er concessies in zitten waardoor de code niet 100% hetzelfde resultaat geeft.

De lib van Paul vind ik best duidelijk. Hij geeft een mooi voorbeeldje van hoe het werkt op zijn site. Maar goed, wat duidelijk is, verschilt per persoon. https://www.pjrc.com/teensy/td_libs_Encoder.html

@Blackdog, ik zou gewoon de math.h library gebruiken. Is iets trager, maar ik denk dat het snel genoeg is voor jouw toepassing. Mocht het niet zo zijn kun je altijd nog iets anders proberen.

PE2BAS

Ik weet hier niet 100% wat er met het resultaat van de berekening moet gebeuren. Maar als het een digitale frequentie-generator in moet die een frequentie-stap van 1Hz heeft, dan is het afronden op hele Hz geen enkel probleem: Met bits achter de komma had je toch niets gekund.

Zie je die 20 in de buurt van het eind van "myexp"? Als je daar 15 van maakt, dan krijg je een fixed-point formaat als resultaat met 5 bits achter de komma. Effe testen... Yup, werkt als een tierelier. Ik heb nu 23000 keer tot op 5 bits achter de komma hetzelfde resultaat als de float-versie. En totaal 10x van de 32k zit ik er 30+ naast. Je moet je wel realiseren dat dit niet in het begin gebeurt. de eerste keer dat ik er 30 naast zit is bij input waarde 31232. De output is zo groot dat die 30 er naast 0.002% is....

Op 9 november 2020 01:50:02 schreef deKees:
- MyExp(x) doet het in 657468 microseconden
Dus gemiddeld 20 microseconden per keer, factor 10 sneller.

Maar ook leuk om te zien dat bij dit soort dingen 32-bits tegelijk veel sneller is. Mijn STM32 heeft een 3x snellere klok maar blijkt alles bij mekaar 50x sneller te zijn.

Op 9 november 2020 01:50:02 schreef deKees:
En dan nog, als je 5000 keer per seconde de exp(x) kunt uitrekenen dan is dat meer dan snel genoeg voor een user interface. Verdere optimalisatie is dan niet erg nuttig meer volgens mij.

Daar heb je gelijk in. Ik ben een groot voorstander van "niet te vroeg met optimalisatie beginnen".

Anderzijds, als je een beetje nadenkt over hoe dingen moeten, dan kan je vantevoren al er voor kiezen om dingen op een handige manier te doen.

Ik heb een 4 digit 7 segment display aan m'n STM32 aangesloten. In de timer interrupt ga ik dus niet 1000x per seconde uitrekenen welke bitjes er aan en uit moeten. Iedere keer als het display verandert reken ik opnieuw uit welke bits voor die positie aan en uit moeten. Dat betekent ook dat ik een "niet in cijfers en letters uit te drukken" symbool kan laten zien door direct de "bitmap" te manipuleren.

c code:

  *(volatile uint32_t *) &GPIOC->BSRR = sevensegbits[dis] | (1 << (8+dis));
dis = (dis+1)&3;

(*) Ik had ook 200ms gelezen waar je 200µs had geschreven. Dat was voor mij: whoa, dan moet het even sneller. Daarnaast kan het zijn dat je je die 4k code niet kan veroorloven.

four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/

Ik vind die angst voor floats niet helemaal terecht. Je ziet hier duidelijk dat zo een simpel 8 bittertje meer dan genoeg performance heeft om die exp() met floats uit te rekenen.

Dan kun je moeilijk gaan doen om het met ints te proberen, maar de winst die je daarmee haalt daar merk je niks van. Dus daar heb je -vaak- niks aan. Maar je programma wordt wel een heel stuk complexer en je loopt het risico op overrun errors tgv scaling (al zal dat in dit geval wel meevallen).

En inderdaad, die ARM's zijn niks duurder en wel veel krachtiger dan 8-bitters. Maar toch, ook een heel stuk komplexer in gebruik. Dus voor mij blijft er ook plaats voor die AVR processortjes.

Al ben ik de laatste tijd weer helemaal op 32-bit, ESP32 op 240 MHz. Ook leuk, dat geeft weer een hele nieuwe wereld aan mogelijkheden.