Hierbij een sketch van een frekwentiemeter. De timer maakt gebruik van DI5 als ingang. Jammer genoeg heb ik op mijn print mijn A5 gebruikt als ingang. Het wijzigen van DI5 naar A5 lijkt mij niet zo van zelfsprekend. Ik vind de software wijzigen mooier dan baantjes doorsnijden. Kan iemand mij verder helpen en tonen welke regels dienen te worden gewijzigd of toegevoegd. Alvast dank. Marc


// NIXIE DISPLAY RADIO
// put a nixie tube display to show the frequency of the old 1940's
// radio.
// Based on:
//  * DESIGN: GLASSLINGER (YOUTUBE)
//  * 06/03/2019
//  * ( https://www.youtube.com/watch?v=mvCwzLowmOo&t=2365s )
//
// Based on:
//
//  * Arduino frequency counter from a few Hz up to 6 MHz
//  * One Transistor, 2018
//  * https://www.onetransistor.eu/
//
// Based on:
//
//  * Frequency Counter sketch by Nick Gammon (CC BY 3.0 AU)
//    http://www.gammon.com.au/timers
//  * FreqCounter library by Martin Nawrath (LGPL 2.1)
//    http://interface.khm.de/index.php/lab/interfaces-advanced/arduino-frequency-counter-library/
//
// Changes made by ON4DM (08/04/2024)

unsigned int ones;
unsigned int tens;
unsigned int hunds;
unsigned long thous;
unsigned long tenthous;

unsigned long freq = 0;
unsigned long locosc = 0;

// set sampling period here (in milliseconds):
unsigned int samplingPeriod = 200;

// Timer 1 overflows counter
volatile unsigned long overflow1;

void init_Timer1() {
  overflow1 = 0; // reset overflow counter

  // Set control registers (see datasheet)
  TCCR1A = 0; // normal mode of operation
  TCCR1B = bit(CS12) | bit(CS11) | bit(CS10); // use external clock source

  TCNT1 = 0; // set current timer value to 0

  TIMSK1 = bit(TOIE1); // enable interrupt on overflow
}

ISR(TIMER1_OVF_vect) {
  overflow1++; // increment overflow counter
}

// Timer 2 overflows counter
volatile unsigned int overflow2;

void init_Timer2() {
  overflow2 = 0; // reset overflow counter

  GTCCR = bit(PSRASY); // reset prescalers

  // Set control registers (see datasheet)
  TCCR2A = bit(WGM21); // CTC mode
  TCCR2B = bit(CS22) | bit(CS20); // prescaler set to 1/128, "ticks" at 125 kHz
  OCR2A = 124; // counts from 0 to 124, then fire interrupt and reset;

  TCNT2 = 0; // set current timer value to 0

  TIMSK2 = bit(OCIE2A); // enable interrupt
}

// interrupt happens at each 125 counts / 125 kHz = 0.001 seconds = 1 ms
ISR(TIMER2_COMPA_vect) {
  if (++overflow2 < samplingPeriod) // add an overflow and check if it's ready
    return; // still sampling

  unsigned long totalSamples = (overflow1 << 16) + TCNT1;

  float freqHz = totalSamples * 1000.0 / samplingPeriod;

  locosc = ((unsigned long)freqHz) / 1000;

  freq = locosc - 455;//loc osc - IF

  tenthous = freq / 10000;//get rid of counts
  thous = freq % 10000;//get rid of counts
  hunds = thous % 1000;//get rid of counts
  tens = hunds % 100;//get rid of counts
  ones = tens % 10;//this directly the hunds
  thous = thous - hunds;//this strips the hunds
  hunds = hunds - tens;//this strips the thens
  thous = thous / 1000;//convert to digit
  hunds = hunds / 100;//convert to digit
  tens = tens - ones;//this strips the ones
  tens = tens / 10;//convert to digit


  digitalWrite(0, bitRead(ones, 0));
  digitalWrite(1, bitRead(ones, 1));
  digitalWrite(2, bitRead(ones, 2));
  digitalWrite(3, bitRead(ones, 3));
  digitalWrite(4, bitRead(tens, 0));
  digitalWrite(6, bitRead(tens, 1));
  digitalWrite(7, bitRead(tens, 2));
  digitalWrite(8, bitRead(tens, 3));
  digitalWrite(9, bitRead(hunds, 0));
  digitalWrite(10, bitRead(hunds, 1));
  digitalWrite(11, bitRead(hunds, 2));
  digitalWrite(12, bitRead(hunds, 3));
  digitalWrite(A0, bitRead(thous, 0));
  digitalWrite(A1, bitRead(thous, 1));
  digitalWrite(A2, bitRead(thous, 2));
  digitalWrite(A3, bitRead(thous, 3));

  // reset timers
  TCNT1 = 0; overflow1 = 0;
  TCNT2 = 0; overflow2 = 0;
}

void setup() {
  tenthous = 0;
  thous = 0;
  hunds = 0;
  tens = 0;
  ones = 0;


  pinMode(0, OUTPUT);
  pinMode(1, OUTPUT);
  pinMode(2, OUTPUT);
  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(7, OUTPUT);
  pinMode(8, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(11, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(A0, OUTPUT);
  pinMode(A1, OUTPUT);
  pinMode(A2, OUTPUT);
  pinMode(A3, OUTPUT);

  // Disable Timer0; millis() will no longer work
  TCCR0A = 0; TCCR0B = 0;

  // start timer 1 (count frequency)
  init_Timer1();
  init_Timer2();
}

void loop() {
  // nothing here; interrupts perform everything
  // you can add user input that changes sampling period
}

[Bericht gewijzigd door Henry S. op (0%)]

geen zin om hele je code te doorgronden, maar ik denk zo snel te zien dat ze gebruik maken van of een interrupt pin of de interne capture compare module van een hardware timer.
Je kunt die pin dus niet zonder meer naar een andere willekeurige input leggen.

Verder geen idee om welke micro/arduino het gaat.
ps. plaats je code voortaan in een code blok.
Ik krijg jeuk zulke lappen code te lezen zonder fatsoenlijke uitgelijnde opmaak :)

Ook niet de bedoeling van een heel programma in de interrupt routine te steken. Maar daar kan TS niks aan doen. Heeft het tenslotte op het net gevonden.

Op dinsdag 16 april 2024 15:56:04 schreef buckfast_beekeeper:
Ook niet de bedoeling van een heel programma in de interrupt routine te steken. Maar daar kan TS niks aan doen. Heeft het tenslotte op het net gevonden.

Als de mainloop toch niks doet maakt 't weinig uit hoeveel er in de interrupt gebeurt, zolang het maar binnen 1 interruptcyclus uitgevoerd kan worden.

Het wijzigen van DI5 naar A5 lijkt mij niet zo van zelfsprekend.

En zie ook https://arduinogetstarted.com/faq/arduino-uno-pinout
Bij arduino lopen de digitale ingangen als
D0 - D13
A0 - A5
D20 en D21

D14 - D19 lijkt dus te ontbreken maar dat is niet waar
D14 = A0
D15 = A1
Enz

Op dinsdag 16 april 2024 15:56:04 schreef buckfast_beekeeper:
Ook niet de bedoeling van een heel programma in de interrupt routine te steken. Maar daar kan TS niks aan doen. Heeft het tenslotte op het net gevonden.

Wat een onzin, er zit helemaal niet zoveel code in de ISR, de code die erin zit is simpel en snel uitgevoerd, en het draait maar op 1kHz. Wat een echt lelijk ontwerp zou zijn, is in die interrupt alleen de timer uitlezen, een vlag zetten dat die er is, en in de main loop keihard rondjes lopen om die vlag. Er is, wat dat betreft in ieder geval, helemaal niets mis met die code.

@TS: er wordt gebruik gemaakt van 2 timers, één om de periode van de meting te bepalen, en één om het aantal ontvangen pulsjes binnen deze periode te tellen. In dit geval draait timer 1 in "externe clock modus", dat wil zeggen dat deze timer pulsjes telt op een ingang.

Een normale Arduino Uno (met een ATMEGA328P) heeft maar 2 pinnen die dat kunnen, D4 en D5. Het is dus niet mogelijk om dit te wijzigen naar pin A5.

Nu ik weet dat dat ik pin A5 niet kan gebruiken als ingang zal ik mijn PCB aanpassen. Ook goed te weten dat er meer dan 13 Digitale I/O zijn op de UNO. Maar mijn PCB is voorzien voor de Arduino nano. Ik zal eens opzoeken of ik bij de nano ook wat extra Dig. I/O's heb. Het is de bedoeling 5 nixie buis drivers aan te sturen (SN74141). Bij vier digitale uitgangen per buisje is er nood aan 20 uitgangen.

Alle io die op de Uno zit vind je ook op de Nano. De Nano heeft als extra A6 en A7.

Nu ik weet dat dat ik pin A5 niet kan gebruiken

JE moet voordat je een definitieve PCB gaat maken eerst goed weten welke pinnen voor een bepaalde functie echt belangrijk zijn. Zo zin er bv ook maar 2 interrupt pinnen op de uno of nano. Zo hebben de pinnen A6 en A7 beperkte functionaliteit.

[Bericht gewijzigd door benleentje op (65%)]

Op dinsdag 16 april 2024 16:43:36 schreef Arco:
[...]
Als de mainloop toch niks doet maakt 't weinig uit hoeveel er in de interrupt gebeurt, zolang het maar binnen 1 interruptcyclus uitgevoerd kan worden.

En dan is de stap snel gezet naar een programma met missing interrupts.

Op dinsdag 16 april 2024 19:32:07 schreef SparkyGSX:
[...]Wat een onzin, er zit helemaal niet zoveel code in de ISR, de code die erin zit is simpel en snel uitgevoerd, en het draait maar op 1kHz. Wat een echt lelijk ontwerp zou zijn, is in die interrupt alleen de timer uitlezen, een vlag zetten dat die er is, en in de main loop keihard rondjes lopen om die vlag. Er is, wat dat betreft in ieder geval, helemaal niets mis met die code.

[...]

Denk je echt dat het programma nu geen rondjes loopt? Proces blijft gewoon lopen. Op welke frequentie het ook is.

Op dinsdag 16 april 2024 19:32:07 schreef SparkyGSX:
[...]
Een normale Arduino Uno (met een ATMEGA328P) heeft maar 2 pinnen die dat kunnen, D4 en D5. Het is dus niet mogelijk om dit te wijzigen naar pin A5.

Een Atmega328P heeft volgens de datasheet 18 PCINT ingangen, die allemaal als interrupt kunnen gebruikt worden. Het vraagt wel wat aanpassing aan de code. ADC5 = PCINT13.

[Bericht gewijzigd door buckfast_beekeeper op (19%)]

Altijd lastig om zoiets goed te krijgen. Ook hier gaat het soms fout:


unsigned long totalSamples = (overflow1 << 16) + TCNT1;

Deze code bepaalt de getoonde frequentie door het aantal klokpulsen van timer1 te tellen. Het gaat fout bij een T1 overflow vlak voor het moment van lezen van TCNT1. Dan kan het gebeuren dat overflow1 nog niet is bijgewerkt. TCNT1 is dan al naar 0 terwijl de gelezen overflow1 nog niet is bijgewerkt. De gelezen waarde van totalSamples zit er dan 65538 pulsen naast.

Maar goed, de kans dat het fout gaat is klein, meestal gaat het welgoed.

Dat kun je oplossen door te controleren dat overflow1 niet veranderd is na het uitlezen van TCNT1. Het bepalen van totalSamples wordt dan bijvoorbeeld:


unsigned long totalSamples;
unsigned long overflow1_copy;

do
{  overflow1_copy = overflow1;
   totalSamples   = (overflow1 << 16) + TCNT1;
} while (overflow1_copy != overflow1);

Op woensdag 17 april 2024 08:55:42 schreef buckfast_beekeeper:
En dan is de stap snel gezet naar een programma met missing interrupts.

Onzin, zoals ik al zei, er zit bijna geen code in die interrupt. Het is totaal niet boeiend hoeveel regels het zijn, maar hoeveel CPU cycles je nodig hebt. Daarbij zou je alleen interrupts kunnen missen als er 2 of meer komen voordat de vorige ISR klaar is, en je kunt triviaal zien dat de periode nu 1ms is. Dat is best wel lang.

Denk je echt dat het programma nu geen rondjes loopt? Proces blijft gewoon lopen. Op welke frequentie het ook is.

Je ziet toch zelf ook wel dat de main loop leeg is. Daar kun je de processor gewoon laten slapen.

Een Atmega328P heeft volgens de datasheet 18 PCINT ingangen, die allemaal als interrupt kunnen gebruikt worden. Het vraagt wel wat aanpassing aan de code. ADC5 = PCINT13.

Leuk, maar irrelevant, want het draait niet op pin interrupts, maar op timers. Misschien de volgende keer even de code lezen, of opzoeken wat het verschil is tussen een timer en een pin change interrupt.

Ik gebruik altijd een outputpin (als er eentje over is) om te togglen bij iedere interrupt.
Dan zie je met de scoop meteen of de timing klopt, en of er interrupts gemist worden...

De originele code werkt dus door in het te meten signaal als klokbron te gebruiken voor TMR1, en vervolgens met TMR2 het aantal klokpulsen uit te lezen van TMR1 dat binnen een bepaalde tijd is gemeten.

TMR2 loopt op 1kHz en reset TMR1 weer. Daardoor kan je met een resolutie van 1000Hz meten. Je kan de frequentieteller accurater maken door langer te meten (bijvoorbeeld pulsen tellen gedurende 1sec ipv 1msec).

TMR1 loopt zo snel als de input, maar wel gedeeld door 65536. Volgens de comments kan dit project tot 6MHz tellen omdat het tellen in de hardware gebeurd. De interrupt van TMR1 loopt dan op max 6M/65536=91Hz.

Deze AVR staat daardoor grotendeel van de tijd eigenlijk "niks" te doen. Dat TMR2 interrupt iets langer is dan niet heel erg meer. Er zijn wel meer projecten die alles met interrupts afhandelen.

Je zou de code kunnen herbouwen door PCINTs te "misbruiken" ipv TMR1, maar dan moet je in software puls voor puls gaan tellen. Om genoeg pulsen bij te houden moet je dat met 16 of 32-bit integers doen, wat op zo'n ATMEGA trager zal gaan. Het betreden en verlaten van een interrupt zal ook wat CPU cycles kosten.. Ik schat in dat zo'n AVR dat nooit gaat bijbenen boven de (paar) honderd kHz ..

Klopt, op deze manier kun je een veel hogere frequentie tellen dan wanneer je pin interrupts zou gebruiken. Het nadeel is dat het minder goed werkt voor lage frequenties, je kunt dan de periode lange maken, de metingen van meerdere periodes gebruiken om het gemiddelde te bepalen, of in plaats het aantal pulsen in een gegeven tijd te tellen, de tijd meten over een aantal pulsen; ofwel tussen 2 opvolgende pulsen, of een groter aantal. Je laat dan de eerst timer bijvoorbeeld tot 10 tellen, stopt de tweede timer, en kijkt hoeveel tijd er verstreken was. De andere methode is de input capture functie gebruiken, dan krijgt je bij elke puls en interrupt met de tijd, waardoor je de interval kunt bepalen, maar dat zou je met zo'n relatief trage processor maar tot een tiental kHz kunnen doen.