12 Bit ADC waarde omzetten naar LOG bereik

blackdog

Golden Member

Hi,

Als eerste weer dank voor het meedenken.

Ik heb nog wat zitten spelen en nadenken wat handig zou zijn voor de LOG bereiken die ik op deze genrator wil hebben.

Op de 600 pulsen per omwenteling encoder het ik een 45mm knop gezet en gekeken wat de frequentie is als ik normaal, wat sneller en gek doe. :+

Normaal draaiend komt de frequentie niet boven de 1kHz uit.
Snel draaiend dan is het 2,5khz.
En een sweeper aan de knop geven is ongeveer 5kHz.

Laten we er vanuit gaan dat ik deze microcontroler ga gebruiken, lijstje met gegevens komt van het Internet en betreft een Teensy-4

Specificaties
ARM Cortex-M7 at 600 MHz
1024K RAM (512K is tightly coupled)
2048K Flash (64K gereserveerd voor recovery & EEPROM emulatie)
2 USB poorten, beiden 480 MBit/sec
3 CAN Bus (1 with CAN FD)
2 I2S Digital Audio
1 S/PDIF Digital Audio
1 SDIO (4 bit) native SD
3 SPI, met 16 word FIFO
3 I2C, met 4 byte FIFO
7 Serial, met 4 byte FIFO
32 general purpose DMA kanalen
31 PWM pinnen
40 digital pinnen (interrupt)
14 analog pinnen, 2 ADCs on chip
Cryptographic Acceleration
Random Number Generator
RTC voor het bijhouden van de tijd
Programmable FlexIO
Pixel Processing Pipeline
Peripheral cross triggering
Power On/Off management

Log bereik No1
zeg van 10Hz tot 50kHz, zodat ik in 1x het hele audio bereik kan sweepen.
Na wat spelen met de grote knop op de encoder heb ik besloten dat 4 omwentelingen, dat is dus 2400 pulsen moeten worden omgezet naar het gewenste LOG frequentie gebied.
Het mooie van het LOG bereik is nu juist dat je in de lage frequenties ook wat resolutie over houd.
De drie printjes die ik hier heb zijn alle drie uitgerust met een 25MHz clock, dat houd in dat de resoutie van de in te stellen frequentie 0,1Hz is.
Dus de laagste frequentie zou dan ook 0,1 Hz moeten zijn, maar voor mijn LOG sweep voor audio hou ik een start frequentie aan van 10,0Hz en stop bij 50kHz.

Dan wordt dan 0, 2400(4x omwenteling encoder) als LOG worden omgezet naar 10,0, 50000 Hz.
Bij de lage frequenties heb je dan in iedere geval de mogelijkheid die frequenties redelijk goed in te stellen.
Zonder LOF functie is het na een paar graden draaien al 200Hz. :)

En dan is het misschien handig om bij frequenties boven 1kHz de 0,1Hz resolutie te laten vallen, van mij kan het bij het meten,
ik heb 0,1Hz resolutie boven de 1kHz niet nodig, maar levert het problemen op met een goede LOG conversie of werkt het dan beter?

Log bereik No2
Het tweede LOG bereik zou dan van 800Hz naar 2 á 3MHz gaan, daar is zeker geen 0,1Hz resolutie nodig.

De normale lineaire bereiken zou ik zelf redelijk moeten kunnen coderen.
Ik vraag mij zelf af of er echt wel meerdere lineair bereiken nodig zijn door het mooie lopen van de encoder met zijn relatief zware knop er op.
Denk daar nog over na.

Ik weet niet of jullie wel eens met een communicatie ontvanger hebben gespeeld die een soepel lopende afstemknop heeft, zo mooi loopt de encoder die ik hier heb ook.
Ook sommige Philips en Grundig buizen radio's hadden het zelfde gedrag van de afstem knop.

OK nog een vraag, door het mooie lopen en de grote knop wil ik dat de up/down couter voor de Lineaire bereiken de hogere frequenties uit de encoder aan kan, laten we een veilige waarde nemen van 10kHz.
Er is geen dender op de uitgang van de encoder aanwezig, dus daar hoeft geen rekening mee gehouden te worden, dus hoe pak ik de up/down counter code hiervoor aan.
De meeste Library's voor encoders zijn voor rond de 24 stappen modelen per omwenteling met debouce ingebouwd.

Ik heb daar moet ik toegeven, nog niet naar gezocht, misschien lukt dit vandaag nog om er iets voor te vinden.
De frequenties uit mijn snel draaiend encoder zou bij de moderne microcontroler voor een up/down counters geen probleem moeten zijn, of zie ik dit verkeert?

Dank en 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.
elmowww

Golden Member

Pin change interrupt er op, (bij éen of beiden van de signaallijnen). In de interrupt routine kijken naar de flank en de status van de andere lijn, aan de hand daarvan beslissen of het omhoog of omlaag is.

Hier zou je een counter kunnen ophogen/verlagen, en dan daarna een conversie van lineair naar logarithmisch doen.

Maar ik denk eigenlijk dat het beter werkt om een factor te gebruiken, bijvoorbeeld 1.0025 (0.25% per stapje).

Dan ben je met 2400 stapjes een dymanisch bereik van 400 verder.
Eventueel doe je dan nog een mooie afronding erover om op ronde getallen uit te komen.

1.0025 & 2400 = ~400

Helpt dat? Om een steeds een echte log uit te gaan rekenen gaat denk ik zijn doel voorbij, dat wil je ook niet op 5 kHz gaan doen denk ik.

PA0EJE - www.eje-electronics.nl - e.jongerius[aapje]eje-electronics.nl - EJE Electronics - Elektronica/firmware ontwikkeling

Formule wordt dan:

code:



#include <math.h>

float LogMinF  = log(10.0);
float LogMaxF  = log(50000.0);
float MaxCount = 2400;

void SetRange(float MinFrequency, float MaxFrequency)
{  LogMinF = log(MinFrequency);
   LogMaxF = log(MaxFrequency);
}

// x = Count from potmeter/encoder. Range 0 .. MaxCount.
// Returns frequency as float. Between MinFrequency and MaxFrequency.
float Frequency(float x)
{  return exp( LogMinF + ( (x / MaxCount) * (LogMaxF - LogMinF) ) );
}

De meeste ARMs hebben een quadratuur mode op de counters. Die kunnen rotary encoders volgen in hardware, en dat gaat goed tot 20 MHz of zo. Daar kan geen pin change interrupt tegen op.

@elmoww:
En een 8-bit AVR kan al echte logs uitrekenen op 5 kHz. Zo een 32-bit teensy doet dat dus helemaal op zijn sloffen (0.7 microseconden volgens rew hierboven). Dus waarom nog moeilijk doen?

Op 9 november 2020 12:38:20 schreef deKees:
De meeste ARMs hebben een quadratuur mode op de counters. Die kunnen rotary encoders volgen in hardware, en dat gaat goed tot 20 MHz of zo. Daar kan geen pin change interrupt tegen op.

Ik heb het datasheet van de iMXRT1062 er bij gezocht. Waar de timers in de STM32 ook PWM doen, heeft de NXP processor dit in een aparte module. Of de timers (GPT) quadratuur encoding kunnen doen kon ik niet vinden in het datasheet. En ik moet inloggen om de reference manual te mogen bekijken. Ik geef het op.

Op 9 november 2020 12:38:20 schreef deKees:
En een 8-bit AVR kan al echte logs uitrekenen op 5 kHz. Zo een 32-bit teensy doet dat dus helemaal op zijn sloffen (0.7 microseconden volgens rew hierboven).

Ik heb hier getest op een 48MHz cortex-M0 zonder FPU. Bram heeft een 600MHz cortex-M7 met FPU....

Ik vond het "integer versie schrijven" een leuk side-projectje om te doen, omdat wat ik anders zou doen minder leuk was. (nadat ik zaterdag had zitten vechten met "slechte USB" op m'n PC besloten een RPI te gebruiken voor embedded-arm-ontwikkelwerk. Nadat die naar wens was geinstalleerd bleek dat de arm compiler alleen op 64-bit draait en ik had een RPI2 met 32-bit processor gepakt. )

[Bericht gewijzigd door rew op maandag 9 november 2020 12:58:28 (39%)

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

Golden Member

Ik weet niet of jullie wel eens met een communicatie ontvanger hebben gespeeld die een soepel lopende afstemknop heeft, zo mooi loopt de encoder die ik hier heb ook.
Ook sommige Philips en Grundig buizen radio's hadden het zelfde gedrag van de afstem knop.

Dat is het grote verschil in een mechanische encoder (met schakelaars) en een encoder met een Ir-led en ontvanger. Of de hele oude met magnetisch opneem spoel.
Ik vind het wel een ontzetten mooi idee om daar een industriele encoder voor te gebruiken, het is iets duurder maar wel 1000x beter.

Mocht de 600 stappen per omwenteling nog tegenvallen er zijn er ook met 1000, 2000 en meer stappen. Gezien de snelheid van de teensy moet dat ook geen probleem zijn.

OK nog een vraag, door het mooie lopen en de grote knop wil ik dat de up/down couter voor de Lineaire bereiken de hogere frequenties uit de encoder aan kan, laten we een veilige waarde nemen van 10kHz.

Dat heeft niets met de software te maken maar puur met de hardware. Een AVR op 20Hz kan vaak zelfs tot ca 40 - 70 Mhz tellen omdat de counters afzonderlijke onderdelen buiten de cpu zijn.

De meeste Library's voor encoders zijn voor rond de 24 stappen modelen per omwenteling met debouce ingebouwd.

Pulsen per omwenteling maakt in principe niets uit de microcontroller kijkt alleen naar getelde waarde en niet naar hoeveel keer de knop rond gaat. Een simpele econder knop heeft daarvoor ook geen uitgang die de 0-postie van de encoder weergeeft.

ik heb 0,1Hz resolutie boven de 1kHz niet nodig, maar levert het problemen op met een goede LOG conversie of werkt het dan beter?

In een logfunctie word de volgde stap grote (resolutie) automatisch steeds een stukje groter. Als je van het bereik 10Hz / 50Khz naar 1kHz / 5 Mhz gaat is de kleinste stap ook automatisch 100x groter. Dat is voor een lineaire of logaritmische schaal verdeling gelijk. Enkel blijft bij een lineaire schaal de stap grote constant en bij een logaritmische schaalverdeling worden de stappen steeds groter.

De code van deKees geeft onderstaande verdeling. Dit is dus geheel op basis van je vraag 10Hz - 50kHz in 2400 logaritmische stapjes.

0 , 10
1 , 10.0356 Stap grote hier is 0,035 Hz
2 , 10.0712
3 , 10.107
4 , 10.143
5 , 10.179
6 , 10.2152
7 , 10.2515
8 , 10.288
9 , 10.3246
10 , 10.3613
11 , 10.3981
12 , 10.4351
13 , 10.4722
14 , 10.5094
15 , 10.5467
16 , 10.5842
17 , 10.6219
18 , 10.6596
19 , 10.6975
20 , 10.7356
21 , 10.7737
22 , 10.812
23 , 10.8505
24 , 10.889
25 , 10.9278
26 , 10.9666
27 , 11.0056 En hier al 0.039 Hz na 27 stappen
28 , 11.0447
29 , 11.084
......
2373 , 45431.4
2374 , 45593 Na 2374 stappen is de stap grote 161 Hz
2375 , 45755
2376 , 45917.7
2377 , 46081
2378 , 46244.7
2379 , 46409.2
2380 , 46574.2
2381 , 46739.7
2382 , 46905.9
2383 , 47072.6
2384 , 47240
2385 , 47408
2386 , 47576.5
2387 , 47745.7
2388 , 47915.3
2389 , 48085.7
2390 , 48256.7
2391 , 48428.2
2392 , 48600.4
2393 , 48773.1
2394 , 48946.6
2395 , 49120.6
2396 , 49295.2
2397 , 49470.5
2398 , 49646.4
2399 , 49822.8 En op het einde is die 176.4 Hz

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

Golden Member

Hi,

Jammer genoeg weinig tijd gehad om met jullie voorstellen aan het werk te gaan...

Benleentje, dank voor het stukje van de logaritmische waarden lijst gemaakt door de software van deKees, makkelijk voor mij nu om een indruk te krijgen van de stapjes!
Natuurlijk wel wat gedaan aan verschillende projecten maar zeer versprokkeld en daar komt deze week ook geen einde aan.

Als eerste dit waar ik over na heb gedacht is dit, de encoder levert dus max, zeg 10kHz aan signalen aan twee IRQ ingangen (benleentje, ik weet dat de digitale ingangen veel sneller kunnen :-))
Dan komt de software in werking voor de vergelijking van die twee ingangen, log conversie en dan word de logwaarde in de AD9833 geschreven.
En dan ook nog regelmatig het display updaten.

Na nog wat gelezen te hebben over microcontrolers kwam ik er achter dat de ESP32 twee kernen heeft, kan je de workload zelf verdelen, eerste kenr b.v. de twee ingangen en hun bewerking en de tweede kern b.v. het display updaten?
Behalve wat schakelaars uitlezen en een paar relais aansturen om het uitgangsniveau in te stellen, gaat de controler verder weinig doen.

Ik heb ook omdat ik met andere projectjes tegen de display snelheid aanliep, een paar displays besteld die een stuk sneller zouden zijn dan het gemddelde display
dat je voor de microcontrolers kan kopen, ik kwam het hier getoonde type sinds kort pas tegen, maar het schijnt er al bijna een jaar te zijn.

Het gaat om een IPS type van 1.3 Inch SPI Interface en het driver IC is een ST7789, de resolutie is 240x240 pixels.
Dit is een link naar een video van educ8s.tv :https://www.youtube.com/watch?v=-nECx4DOE84

Hier een paar plaatjes van het display zoals het uit de verpakking komt, de batterij is voor de verhoudingen.
Het display zit in een doosje met twee verschillende print pennetjes, recht en haaks, wat één luxe!
https://www.bramcam.nl/NA/NA-AD9833-Gen/NA-AD9833-Gen-05.png

.
Maar wat is dat voor goorheid!
https://www.bramcam.nl/NA/NA-AD9833-Gen/NA-AD9833-Gen-02.png

.
Een tandenborstel, wattipjes en IPA was de oplossing voor dit probleem, ik heb niet gesoldeerd aan de verbindingen.
Mijn ervaring is niet zo best met dit soort print materiaal.
Met het loopje bekeken bleken de solderingen goed genoeg.
https://www.bramcam.nl/NA/NA-AD9833-Gen/NA-AD9833-Gen-03.png

.
Dit is de hele achterzijde en als het display aan een microcontroler hangt zal ik ook het plaatje laten zien.
https://www.bramcam.nl/NA/NA-AD9833-Gen/NA-AD9833-Gen-04.png

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.

Een paar gedachten kronkels,

De encoder zou ik via interrupts laten werken. In de interrupt routine update je de counter en zet je een flag die aangeeft dat de waarde in de counter is veranderd. In de main routine lees je die flag uit als deze true is update je de AD9833 en daarna het scherm. Ook de flag moet weer naar false worden gezet, anders zal de volgende iteratie het scherm en de AD9833 weer opnieuw worden aangestuurd.
Nu komt meteen het volgende probleem aan bod, threadsafe oplossen van de IRQ. Wat als nu de waarde veranderd nadat je de AD9833 hebt uitgestuurd en voordat je het display hebt geupdate? Nu geeft het scherm een andere waarde aan dan dat de AD9833 genereert. Je kunt de flag als eerste terug op false zetten en daarna het scherm en de AD9833 updaten. Als de counter veranderd tijdens het updaten dan is de flag weer true gezet door de interrupt en zal de volgende iteratie weer worden geupdate.

Voorbeeldje:

c code:


int counter = 0;
bool counterChanged = false;


void InterruptRoutine()
{
	counter = ReadInputsAndUpdateCounter();
	
	counterChanged = true;	
}


void main()
{
	while(true)
	{
		if(counterChanged)
		{
			counterChanged = false;
			UpdateAD9833(counter);
			UpdateDisplay(counter);
		}
		
	}
}

Ik heb ergens gelezen dat als je een ESP32 programmeert via het Arduino platform dat alles op 1 core draait. Dit heb ik ergens gelezen, ik weet niet in hoeverre dit klopt en of je hier iets aan kan veranderen. Zelf gebruik ik het ESP_IDF platform. Dat is voor beginners lastiger op te zetten, maar als je eruit komt is het erg krachtig. Als je dat dan doet maak je gebruik van FreeRTOS. Dit distribueert voor jou de load van de software echter komt het met een hoop nieuwe problemen. Als je dit niet kent zou ik het toch niet aanraden. Denk hierbij aan termen als:
- Threadsafe
- Mutex / semaphore

Aan de andere kant is de documentatie van FreeRTOS wel erg netjes. Ik kan je ten sterkste aanraden om je hier eens in te verdiepen. Het is super interessante materie en als je wat serieuzere projecten wilt gaan maken, zeker met de ESP32, een must.

In mijn projectje is de code eigenlijk al af, die heeft een AD9850, een display met ST7735 en een toetsenbord. Ik wil er nog een encoder bij programmeren. Ik heb zelf ook net een AD9833 besteld. Ik verwacht dat het redelijk eenvoudig is om dat te ondersteunen naast de AD9850. Als ik dat aan de gang heb mag je de code zo gebruiken. Dat is dan wel op het ESP_IDF platform.

[Bericht gewijzigd door hardbass op dinsdag 10 november 2020 17:05:27 (13%)

PE2BAS
benleentje

Golden Member

Als eerste dit waar ik over na heb gedacht is dit, de encoder levert dus max, zeg 10kHz aan signalen aan twee IRQ ingangen (benleentje, ik weet dat de digitale ingangen veel sneller kunnen :-))
Dan komt de software in werking voor de vergelijking van die twee ingangen,

De teensy-4 heeft een zeer uitgebreide timer module, die direct een encoder kan uitlezen. Staat toch duidelijk op pagina 2941 ;). De Teensy 4.0 is echt super uitgebreid. Zelfs de hond uitlaat functie als ik niet thuis ben zit erin (watchdog).

In de software hoef je dan enkel die hardware counter uit te lezen en die waarde naar logaritme om te zetten. Echter of de Arduino ide die quadrature encoder mode kan aansturen dat weet ik niet.

code:


Chapter 51
Timers Overview

51.1Overview
The following timers are supported in this chip:
•  General Purpose Timer (GPT): A 32-bit up-counter with 12-bit pre-scaler
•  Periodic Interrupt Timer (PIT): A 32-bit counter timer that features programmablecount modulus, clock division features etc.
•  Quad Timer (TMR): It provides four timer channels with variety of controls forindividual and multi-channel features
•  Quadrature Encoder/Decoder (ENC): It provides interfacing capability to position/speed sensors
•  Enhanced FlexPWM: It contains PWM submodules each of which is set up to controla single half bridge power stage
•  Watchdog Timer (WDOG1,2): The WDOG1 and WDOG2 protect against systemfailures by providing a method by which to escape from unexpected events orprogramming errors
•  Watchdog timer (RTWDOG/WDOG3): It is a high reliability independent timer thatis available for system use
•  External Watchdog Monitor (EWM): It is designed to monitor external circuits, aswell as the MCU software flow
Mensen zijn soms net als een gelijkrichter, ze willen graag hun gelijk hebben.

Even voor de goede orde: Dat "counter" in software bijhouden doet bijna iedereen slecht (als in gaat in bepaalde gevallen tellen terwijl de encoder niet verplaatst) of langzaam.

Ik schreef voor de test: (ONGETESTE CODE)

c code:


int count;

void swencoder (void)
{
  static int t;
  static const int etbl[16] = {
     0,  1, -1,  0, 
    -1,  0,  0,  1, 
     1,  0,  0, -1,
     0, -1,  1,  0};
  t = ((t&3) << 2) | (GPIOC->IDR >> 4) & 3;
  count += etbl[t];
}

Als je dit aanroept als er 1 van de twee verandert dan zit je gebakken. Als je geen STM32 gebruikt zal de "GPIOC->IDR" anders zijn. Om daar niet met de twee individuele bits te hoeven gaan prutsen is het handig als de bitjes naast mekaar in een poortregister zitten. Maar zoveel scheelt het niet.

De nullen op de diagonaal zijn: "Geen verandering". Als je dit op een pin-change interrupt laat aanroepen dan is kennelijk de input al weer terugveranderd voordat we konden kijken. De nullen op de andere diagonaal zijn eigenlijk: "En nu is het fout!" Als je wilt kan je daar een andere waarde zetten en op checken. Als dat voorkomt dan weet je dat je counter onbetrouwbaar is geworden want kennelijk heb je een change gemist. Huidige situatie is dat ie ongeveer 20 clocks nodig zou hebben.

Maar ik zou nog steeds proberen de boel aan te sluiten op de juiste pins om het gewoon in hardware te laten doen.

@hardbass: Ik zou gewoon in main of in een timer-interrupt

c code:

if (oudecount != count) { 
   oudecount = count; 
   // update freq generator (gebruik "oudecount"!). 
   // update display (idem!). 
}

doen!

Geen gezeik met een vlaggetje wat race-condities kan veroorzaken(*). Wat je nu weet is dat wat er in "oudecount" staat ook op het display en in de freq chip zit. En als de boel weer verandert voordat je klaar bent? Nou dan zie je dat de volgende keer wel weer!

Deze techniek wordt in de "formele verificatie van software" gebruikt: Je hebt een invariant. "Oudecount staat op het display" en als je iets nauwkeuriger gaat kijken: "behalve als we bezig zijn het te veranderen op regel xyz". Als je het helemaal formeel wilt doen dan wordt het iets als "staat op het display of we zijn er mee bezig". En dat moet dan natuurlijk vast nog formeler dan dat ik het hier doe.

Maar zonder de formaliteiten is het ook al een bruikbare techniek.

@blackdog: Het staat je vrij om een CPU te kiezen die je wilt, maar mijn advies is: Ga niet te vaak switchen. Er zijn allerlei details anders tussen de processoren en het is fijn als je daar een beetje mee bekend wordt. Als je je tot de basics beperkt en steeds de arduino IDE gebruikt kan het zijn dat je relatief makkelijk kan switchen, maar ik heb er een hard hoofd in. Ik wil nog steeds arduino-voor-STM32 aan de praat krijgen en als ik het installeer en probeer krijg ik een een of andere foutmelding waar ik weinig mee kan.

(*) Ik DENK dat jij het vaker gedaan hebt: Jou code ziet er ook gewoon goed uit. Het probleem met dit soort code is dat als je een stuk code van Hardbass, dekees of rew copieert en subtiel aanpast, dan kan het zomaar zijn dat je de boel kapot maakt. Het "en het werkt altijd" is soms heel subtiel. Denk aan het verplaatsen van "counterchanged" in hardbass' code. Zet je die NA het updaten van het scherm, dan is de kans ineens dat je een oude waarde op het scherm hebt terwijl de counter veranderd is (tijdens het schrijven naar het scherm). Nu is dit een vrij "obvious" geval en zijn we gewaarschuwd dat dit kan gebeuren. Dus nu trappen we er niet meer in. Maar als je het zelf moet maken of denkt: ik verander het zo, dat zou niet mogen uitmaken, kan het zomaar verkeerd gaan.

[Bericht gewijzigd door rew op dinsdag 10 november 2020 18:07:23 (12%)

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

Golden Member

Als je je tot de basics beperkt en steeds de arduino IDE gebruikt kan het zijn dat je relatief makkelijk kan switchen, maar ik heb er een hard hoofd in.

Als je generieke bibliotheken gebruikt die niet hardware specifieke registers gebruikt kan je met de Arduino ide makkelijk van hardware wisselen.

Echter is dit niet vaak het geval en zit je met uitgebreide functies toch aan de hardware vast.

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

Golden Member

...

[Bericht gewijzigd door benleentje op dinsdag 10 november 2020 18:18:36 (99%)

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

Dat is ook wat ik toelicht in mijn verhaaltje, als je de flag op de verkeerde positie reset kan het fout gaan. Als ik je uitleg goed begrijp zou je het zo doen:

c code:



int newCounter = 0;
int prevCounter = 0;

void InterruptRoutine()
{
	newCounter = ReadInputsAndUpdateCounter();
}

void main()
{
	while(true)
	{
		if(newCounter != prevCounter )
		{
			prevCounter = newCounter
			UpdateAD9833(prevCounter);
			UpdateDisplay(prevCounter);
		}
	}
}

Dit is een goede oplossing, het is netter dan met een flag. Let wel op dat beide oplossingen niet threadsafe zijn. Als je met RTOS gaat werken moet je een semaphore gebruiken. Anders kan hij zijn dat newCounter half geschreven is en dan een taskswitch voorkomt.

(Bij ISR is dat niet altijd het geval, de scheduler stopt dan. En het schijven van een int is vermoedelijk 1 instructie. Maar als je strings gaat kopiëren o.i.d. dan is een semaphore noodzakelijk. Er speelt onderwater nog veel meer mee waarom het wel of niet goed zou gaan maar omdat dit allemaal platform afhankelijk is en veel te diep gaat om goed uit te leggen in een post laat ik het hier even bij. Maar goed, ik denk dat we elkaar wel begrijpen :))

Wat betreft het ontcijferen van het quadrature signaal. Als de processor hier hardware voor heeft, zeker gebruiken! De ESP en de teensy hebben dit allebei. Ik ga eens kijken of ik dat aan de gang kan krijgen.

Ohja, het updaten van de AD9833 en het scherm wil je nooit in de interrupt routine doen. Deze routine moet kort blijven.

PE2BAS

Ik heb ergens gelezen dat als je een ESP32 programmeert via het Arduino platform dat alles op 1 core draait.

Klopt wel bijna. Als je de toverwoorden niet weet om de tweede core aan het werk te zetten dan doet de niks. Geldt ook voor de ESP-IDF trouwens. :)

blackdog

Golden Member

Hi,

Ik heb een nieuwe Teensy-4 van printpennen voorzien en de encoder software hiervoor geladen.

Deze test setup kan bijna niet simpeler. :)
https://www.bramcam.nl/NA/NA-AD9833-Gen/NA-AD9833-Gen-12.png

.
En dit is hoe het is aangesloten, ik was blij met de open collector uitgangen van de encoder, daar de encoder beneden de 5V geen mooie pulsen meer levert.
3.3V doet hij wel, maar sommige flanken zijn dan niet mooi meer, rond 4V voeding begint hij goed te werken, dus 5V voro de lectronica is OK en dan de 3K3 pull up aan de 3.3V en klaar!
https://www.bramcam.nl/NA/NA-AD9833-Gen/NA-AD9833-Gen-11.png

.
En dit is het stukje code samen met een stuk uit de serial monitor.
Ik heb geprobeerd de ancoder van slag te brengen, maar waar ik ook in de lijst kijk, blijft hij mooi in de pas lopen.
Ook heb ik met de scoop de twee kanalen uitgelezen en de encoder as net alsof je met je vingers "klak" doet maar dan met het asje van de encoder er tussen er tussen en daarbij dus ook geen enkel probleem.
https://www.bramcam.nl/NA/NA-AD9833-Gen/NA-AD9833-Gen-10.png

.
De code die trouwens niet zo goed leesbaar is in dit plaatje, was voor twee encoders, één encoder heb ik er uit ge-edit maar er is nog wat residu aanwezig dat er nog uit moet.

De volgende stap is het nieuwe display aan te sluiten en kijken hoe dit met de encoder er aan reageert op de Teensy-4.
Zo wil ik steeds een stukje verder gaan met testen en als het display werkt, de logcode toe gaan passen.

Dank weer en 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

IK heb ook gelijk maar 2 encoders en een teensy 4.0 in bestelling staan.

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

oeh die encoder is chique, de mijne is een alie ding...

Toch maar even een demonstratie, daar zit dan niet de log in waar dit topic nou juist om ging. Zo nu ben ik weer klaar met topic kapen :)

https://www.youtube.com/watch?v=D0djJH5IyV4

Nog een dingetje, stel je wilt de frequentie ook aanpassen met een numeriek toetsenbord of vanaf de pc dan loop je tegen het probleem aan dat de teller van de encoder niet wordt aangepast. Dit heb ik zo opgelost met de code van deKees met een kleine aanpassing om terug te kunnen rekenen. Bij mij werkt dit perfect. Wat ook wel mooi is, als je de frequentie aanpast in de AD9850 maakt hij geen sprongetje in de sinus. Vermoedelijk doet de AD9833 dat ook, maar dat weet ik niet zeker.

c code:



#define FMAX 	50000.0
#define FMIN 	10.0
#define STEPS 	2400


float a = (log(FMAX) - log(FMIN)) / STEPS;
float b = log(FMIN);

float EncToFreq(float x)
{
	return exp( a * x + b );
}

float FreqToEnc(float x)
{
	return  (log(x) - b) / a;
}

void NewFrequencyFromKeypad(float freq)
{
	encoder->SetCount((int32_t)FreqToEnc(freq));
	SetFreq(freq);
}

void EncoderValueChanged(int32_t count)
{
	float freq = EncToFreq(count);
	SetFreq(freq);
}

[Bericht gewijzigd door hardbass op dinsdag 10 november 2020 20:54:09 (67%)

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

Golden Member

Hi,

Even als afsluiter van de dag, de hele avond bezig geweest iets op de ST7789 displays te krijgen met een Teensy-4 en een Teensy-LC.

Op een of andere manier zijn de coders voor de library's alleen maar bezig zoveel mogelijk functies tegelijk in de library's en de voorbeeld codes op te nemen.
Dit terwijl je bijna altijd door dit gedrag van de coders, veel tijd kwijt bent met het uitzoeken alleen al hoe je de SPI touwtjes aan je microcontroler knoopt.
Dan presteren sommige ook nog om de basis code aan SPI2 te hangen van de Teensy-4!

Tja ik klink een beetje gefrustreed en dat ben ik ook door de gebrekkige didactische kwaliteiten die ik vandaag ben tegen gekomen.
En ja, ik ken ook voorbeelden die het zeer goed doen, bijna alles wat ik met displays heb gedaan aangstuurd met de librarys van Oliekraus werkt gewoon.

Ik heb een post gedaan op het Teensy forum, kijken of daar iets goeds uit voor komt en dan, nu tukkie doen :)

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.
blackdog

Golden Member

Hi,

Eindelijk heb ik het TFT IPS display werkend...

Door wat opmerkingen van iemand op het Teensy forum heb ik na nog zeker 5 pogingen mijn eerste lijn op het scherm gekregen.
De code liep niet verder door, maar een lijn betekend in princiepe dat het display werkte.

Dus daarna al die onzin van: "kijk eens wat ik allemaal in die voorbeeld code kwijt kan" weggehald en een soort "Hallo Wereld" alleen naar het display geschreven.
En wat denken jullie, dat werkte gewoon. :-) draaien van het scherm, tekstkleur aanpassen enz, enz.
Ik krijg steeds meer het gevoel dat men bezig is met "hun" luchtje bij de boom achter te laten i.p.v. er voor te zorgen dat er simpele voorbeeld code bij de library aanwezig is.
Lekker veel verschillende display in één library plakken en zoek het maar uit...
Oja, in het geheel is het niet mijn onrust, ga maar eens zoeken naar dit display en het aansturen er van...

Bij nader inzien, doe dat maar niet, hieronder de opset voor in ieder geval de Teensy-LC en de Teensy-4 om het diplay te laten werken.
Er staat ook wat code bij van mijn rotaty encoder

c code:



#include <Adafruit_GFX.h>                 // Core graphics library
#include <Adafruit_ST7789.h>              // Hardware-specific library for ST7789
#include <ST7789_t3.h>
#include <SPI.h>

#include "QuadEncoder.h"                  // Encoder software voor de Teensy-4
QuadEncoder knobLeft(1, 0, 1);

#define TFT_CS        -1                  // -1 = Set deze functie uit
#define TFT_RST        9
#define TFT_DC         8

// For a 1.3 TFT with ST7789 240x240 IPS No CS Pin
Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);



void setup() {
  
Serial.begin(115200);
  Serial.println("TwoKnobs Encoder Test:");

  /* Initialize Encoder/knobLeft. */
  knobLeft.setInitConfig();
  knobLeft.init();
   
    tft.init(240, 240, SPI_MODE2);         // initialize a ST7789 chip, 240x240 pixels

 
    tft.setSPISpeed(40000000);             // Set SPI Speed

    tft.setRotation(tft.getRotation() + 2);
    tft.fillScreen(ST77XX_BLACK);
    tft.setFont();

    tft.setCursor(0, 5);
    tft.setTextColor(ST77XX_RED);
    tft.setTextSize(2);
    tft.print("Its Alive, a live...");
}

long positionLeft  = -999;


void loop() {
  long newLeft;
  newLeft = knobLeft.read();
  if (newLeft != positionLeft) {
  positionLeft = newLeft;
  }

    tft.setFont(&FreeMonoBoldOblique12pt7b);
    tft.setCursor(20, 135);
    tft.fillRect(21, 104 , 130, 45, 0xF800); 
    tft.setTextColor(ST77XX_YELLOW);
    tft.setTextSize(2);
    tft.print(newLeft);                     // Plaatst de Rotary encoder waarde op het scherm maar flikkert sterk
    delay(1);
}

.
Mooi dat werkt dus, maar het flikkeren van het scherm is niet om aan te zien, het gaat extreem snel, het golft een beetje zoals zweving tussen twee tonen,
Dit weer afhankelijk van het aantal cijfers dat ik in beeld breng.
Ik overschrijf de oude karakters met een net groot genoeg gevult kader, die de achtergrondkleur heeft.
Nu kan ik natuurlijk kijken of de teller waarde is veranderd en dan alleen het display op die plek beschrijven, dan is het flikkeren als de knop niet meer draaid weg.

Maar ik vond dat niet een galante oplossing en ben daardoor verder gaan zoeken voor een beter werkende oplossing.
Ik kwam uit op het arduino.cc forum waar de gebruiker TFTLCDCyg een voorbeeldje gaf, hoe hij dit oplosde zonder veel uitleg, behalve dit: spintf

.
De code boven de Loop functie is weer wat nodig is voor het display en de encoder(hier nu niet gebruikt)
Het enige wat extra is, is dit: char TX[50];
Waar het om gaat staat in de loop functie, er worden twee regels met random waarden gescheven in verschillende font grote's
Ik begrijp de sprintf functie niet, ik weet nog dat deKees mij jaren geleden nog eens geholpen heeft met de tijd goed in beeld te brengen met deze functie en dat werkte goed.
Ook nu weer werkt deze functie geweldig, geen enkel geflikker en er blijft ook geen residu van de vorige waarde achter, eigenlijk precies zoals je het hebben wil!

c code:


#include <Adafruit_GFX.h>             // Core graphics library
#include <Adafruit_ST7789.h>          // Hardware-specific library for ST7789
#include <ST7789_t3.h>
#include <SPI.h>
#include <Fonts/FreeMonoBoldOblique12pt7b.h>
#include <Fonts/FreeSerif9pt7b.h>


char TX[50];


#include "QuadEncoder.h"
QuadEncoder knobLeft(1, 0, 1);

#define TFT_CS        -1
#define TFT_RST        9               // Or set to -1 and connect to Arduino RESET pin
#define TFT_DC         8

// For 1.14", 1.3", 1.54", and 2.0" TFT with ST7789:
Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);

void setup() {

  Serial.begin(115200);
  Serial.println("TwoKnobs Encoder Test:");
  /* Initialize Encoder/knobLeft. */
  knobLeft.setInitConfig();
  knobLeft.init();
   
   // Use this initializer (uncomment) if you're using a 1.54" 240x240 TFT
   tft.init(240, 240, SPI_MODE2);       // initialize a ST7789 chip, 240x240 pixels

 
    tft.setSPISpeed(40000000);

    tft.setRotation(tft.getRotation() + 2);
    tft.fillScreen(ST77XX_BLACK);
    tft.setFont();

    tft.setCursor(0, 5);
    tft.setTextColor(ST77XX_RED);
    tft.setTextSize(2);
    tft.print("Its Alive, a live...");
}

long positionLeft  = -999;


void loop() {
  tft.setTextColor(ST7735_WHITE, ST7735_BLACK);
  int number = random(25555);
  int intPart = number/100;
  int decimalPart = number - 100*intPart;
  sprintf(TX,"%03d.%02d", intPart, decimalPart); //  XXX.XX
  tft.setTextSize(2);    
  tft.setCursor(50, 30);  
  tft.print(TX);  
  
  tft.setTextSize(3);    
  tft.setCursor(30, 65);  
  tft.print(TX);    
}

Dus als jullie deze ouwe aap eens willen uitleggen waarom dit zo mooi werkt, dan ben ik weer extra happy!

Dank en 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 flikkeren komt door de fillrect(). Die haalt alles weg en maakt het zwart, en dan duurt het even voordat de nieuwe text komt.

Maar die kun je niet weglaten in het eerste voorbeeld want je weet nooit hoeveel cijfers worden afgedrukt door print(newleft), en dan blijft er dus soms een stuk oude text staan.

De sprintf maakt nu eerst een string die altijd 6 letters heeft.
"%03.02f" : 3 cijfers, een punt en 2 cijfers.

Dus dan worden alle 6 letters altijd overschreven en dan is de fillrect niet meer nodig.

PS1
Je kunt de voorloopnullen vervangen door spaties met %3.02f", maar dat werkt alleen als een spatie net zo breed is als een cijfer.

PS2
int decimalPart = number - 100*intPart;
Kan ook als:
int decimalPart = number % 100;
De % operator (modulo).

PS3
Adafruit gebruikt de 'standaard' print() funktie uit de arduino omgeving. Die heeft dus dezelfde mogelijkheden die je ook bij Serial.print() hebt.
Maar de formattering die je nodig hebt zit er niet in. Erg jammer, maar wel een zwaar manco in de arduino omgeving.

[Bericht gewijzigd door deKees op woensdag 11 november 2020 22:33:12 (15%)

blackdog

Golden Member

Hi deKees, :-)

Dit: char TX[50]; de gereserveerde ruimte voor dit: "%03d.%02d"
Als ik met mijn counter b.v. tot 50000 wil gaan moet die Char waarde en de "%03d.%02d" worden anagepast.
Zeg dat ik alleen 1 decimaal wil hebben en max. 500000, wordt dit dan: "%06d.%01d" ?

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

sprintf(TX,"%03d.%02d", intPart, decimalPart);

TX is string variabele waarvan je de naam zelf mag kiezen in dit voorbeeld dus de naam TX maar mag je ook tekst oid noemen. De inhoud van de string staat tussen de "" tekens.
% is de verwijzing naar een variabele die dan ook in de opmaak van de functie moet worden meegegeven. Omdat dit de eerste verwijzing is word ook automtisch de eerste gelinkte variabele gebruikt in dit geval intpart.
03d is de opmaak van de variabele, d is voor decimaal met 3 digids en de 0 is voor voorgaande nullen meenemen.
. is in dit geval ook een . in de string
% is verwijzing naar de volgende variabele in dit geval decimalpart
02d decimaal met 2 digids + voorgaande nullen

De inhoud van de string TX word dan xxx.xx

Dit: char TX[50]; de gereserveerde ruimte voor dit: "%03d.%02d"
Als ik met mijn counter b.v. tot 50000 wil gaan moet die Char waarde en de "%03d.%02d" worden anagepast.
Zeg dat ik alleen 1 decimaal wil hebben en max. 500000, wordt dit dan: "%06d.%01d" ?

Ja dat klopt maar als dat al vast ligt is het wel onzinnig om char TX[50] te geven dan heb je aan 9 plaatsen genoeg.
De string TX %06.%01 is 6 plaatsen voor intpart, 1 plaats voor de punt of komma, 1 plaats voor achter de komma + 1 plaats voor de null termintor \0 die altijd in een char array zit om aan te geven waar de inhoud van de array eindigt. Het kan en werkt soms ook zonder maar niet altijd met alle ander char array functies.

De sprintf functie zet de \0 automatisch en je hebt dan minimaal 9 plaatsen nodig.

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

Ja, dat hangt ervan af.

Op een teensy 4 (32 bit machientje) dan is een int groot genoeg.
Op een UNO is een int 16 bits, dus dan kun je niet verder dan 32768.

Als je zeker wilt spelen dan kun je beter int32_t gebruiken. Maar dat geeft dan weer problemen met de format string. Dan moet je op de UNO "%03ld.%02ld" gebruiken om aan te geven dat je met long ints werkt. Maar op de teensy weer niet, want die werkt normaal al in 32 bit.

Als je de format string aanpast, dan moet je ook de deelfactor aanpassen:

2 cijfers achter de komma:

code:


  int intPart = number/100;
  int decimalPart = number % 100;
  sprintf(TX,"%03d.%02d", intPart, decimalPart); //  XXX.XX

1 cijfer achter de komma:

code:


  int intPart = number/10;
  int decimalPart = number % 10;
  sprintf(TX,"%06d.%01d", intPart, decimalPart); //  XXXXXX.X

Zelf zou ik met floats werken. Veel gemakkelijker:

code:


  float Frequentie = 500000.0;
  sprintf(TX, "%6.1f", Frequentie);

Dat geeft een string van minimaal 6 cijfers, met 1 cijfer achter de komma.
" 1.3" 6 cijfers (3 spaties vooraan)
"2500.5" 6 cijfers
"25000.8" 7 cijfers
"500000.0" 8 cijfers

Je kunt de string afkappen op 6 cijfers. Dan valt de decimaal weg vanaf 10000 Hz:

code:


  float Frequentie = 50000.0;
  sprintf(TX, "%6.1f", Frequentie);
  TX[6] = 0; 

En je kunt ook een langere string maken als dat in het display past, en dan dus altijd de decimaal mee afdrukken:

"%8.1f" geeft altijd 8 cijfers bij getallen onder 1 miljoen.

code:


  float Frequentie = 500000.0;
  sprintf(TX, "%8.1f", Frequentie);

PS,

Als je met integers werkt, met 2 decimalen, dan moet je wel de frequentie aanleveren in 'centiHerz', dus 100 cHz = 1.00 Hz, en 50000000 cHz voor 500kHz.

Als je met 1 decimaal afdrukt, dan moet je werken met 'deciHz'.

Met floats heb je daar allemaal geen last van.