Contatore da 1us per periodimetro - Input capture - enum

Ciao a tutti

Vorrei realizzare un periodimetro, cioè un misuratore della durata del tempo a livello alto, di quello a livello basso e del duty cycle di un segnale. Per questo vorrei usare una base dei tempi di 1us, contro i 4us standard di aggiornamento di micros(). Secondo il calcolatore
https://www.arduinoslovakia.eu/application/timer-calculator
ho provato così:

// Interrupt: https://dronebotworkshop.com/interrupts/

#include <PCF8574_HD44780_I2C.h> // LCD I2C.
// LCD 16x2 con indirizzo I2C 0x27:
PCF8574_HD44780_I2C lcd(0x27,16,2);

volatile int32_t microsecondi=0;
uint32_t t=0;

void setup() 
{
pinMode(0, OUTPUT); // TEST.
pinMode(4, INPUT); // INPUT.
pinMode(8, INPUT_PULLUP); // Encoder A.
pinMode(9, INPUT_PULLUP); // Encoder B.
lcd.init();
lcd.backlight();
lcd.print("Start");

  // AVR Timer CTC Interrupts Calculator v8
  // http://www.arduinoslovakia.eu/application/timer-calculator
  // Microcontroller: ATmega328P
  // Created: 2023-02-04T17:43:37.092Z (4/2/2023 h.17.43)
  noInterrupts();
  // Clear registers:
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1 = 0;
  OCR1A = 1;
  TCCR1B |= (1 << WGM12);  // CTC
  TCCR1B |= 2;   // Prescaler 8
  TIMSK1 |= (1 << OCIE1A); // Output Compare Match A Interrupt Enable
  interrupts();
}

void loop() 
{
if(millis()-t>=1000) {t=millis(); lcd.setCursor(0,0); lcd.print(microsecondi);}
}

ISR(TIMER1_COMPA_vect)
{
microsecondi+=1;
}

ma compare solo lo "Start" iniziale... Solo cambiando i valori comincia a funzionare. Sembra proprio che con 16MHz di clock non ce la faccia... E' così? Che schedina/microcontrollore potrei usare? L'ESP8266 o ESP32 (ma il WiFi non mi serve)? Altrimenti quale altra? Dovrà gestire anche un LCD e/o un 7219 con 8 display a 7 segmenti a LED.

Grazie
Gianluca

Li viene utilizzato un timer a 8 bit (Timer0) ... tu ne stai usando uno a 16 bit (Timer1).

Ora, dal ATmega328P datasheet, pag 120:

The PRTIM1 bit in ”PRR – Power Reduction Register” on page 54 must be written to zero to enable Timer/Counter1 module.

... quind, intanto, hai verificato che tale bit sia a zero?

Poi, in modalità CTC, la massima frequenza raggiugibile è

The waveform generated will have a maximum frequency of fOC1A = fclk_I/O/2 when OCR1A is set to zero (0x0000).

... quindi non dovresti avere problemi.

In ogni caso la frequenza è data dalla seguente formula:

fOCnA = fclk_I/O / ( 2 x N x ( 1 + OCRnA ) )

... dove N è il valore del prescaler (1, 8, 64, 256 o 1024).

Guglielmo

P.S.: Consiglio comunque un'attenta lettura del suddetto datasheet, cap. 16, "16-bit Timer/Counter1 with PWM"

Magicamente funziona se al posto di int32_t usi byte. Ho il simulatore aperto è ho provato con uint16_t e non funge.

Questo è il disassemblato della ISR:

ISR(TIMER1_COMPA_vect) {
 67e:	1f 92       	push	r1
 680:	0f 92       	push	r0
 682:	0f b6       	in	r0, 0x3f	; 63
 684:	0f 92       	push	r0
 686:	11 24       	eor	r1, r1
 688:	8f 93       	push	r24
 68a:	9f 93       	push	r25
    counter++;
 68c:	80 91 44 01 	lds	r24, 0x0144	; 0x800144 <__data_end>
 690:	90 91 45 01 	lds	r25, 0x0145	; 0x800145 <__data_end+0x1>
 694:	01 96       	adiw	r24, 0x01	; 1
 696:	90 93 45 01 	sts	0x0145, r25	; 0x800145 <__data_end+0x1>
 69a:	80 93 44 01 	sts	0x0144, r24	; 0x800144 <__data_end>
}
 69e:	9f 91       	pop	r25
 6a0:	8f 91       	pop	r24
 6a2:	0f 90       	pop	r0
 6a4:	0f be       	out	0x3f, r0	; 63
 6a6:	0f 90       	pop	r0
 6a8:	1f 90       	pop	r1
 6aa:	18 95       	reti

Sono 19 istruzioni, supponiamo che ogni istruzione richieda 2 cicli di clock:

19 x 2 x 62.5 = 2375 ns che sono 2,375 us

Ovviamente non ce la fa. Ora provo con il contatore da 8-bit.

ISR(TIMER1_COMPA_vect) {
 67e:	1f 92       	push	r1
 680:	0f 92       	push	r0
 682:	0f b6       	in	r0, 0x3f	; 63
 684:	0f 92       	push	r0
 686:	11 24       	eor	r1, r1
 688:	8f 93       	push	r24
    counter++;
 68a:	80 91 44 01 	lds	r24, 0x0144	; 0x800144 <__data_end>
 68e:	8f 5f       	subi	r24, 0xFF	; 255
 690:	80 93 44 01 	sts	0x0144, r24	; 0x800144 <__data_end>
}
 694:	8f 91       	pop	r24
 696:	0f 90       	pop	r0
 698:	0f be       	out	0x3f, r0	; 63
 69a:	0f 90       	pop	r0
 69c:	1f 90       	pop	r1
 69e:	18 95       	reti

Sono 15 istruzioni 1875ns e non dovrebbe farcela comunque ma funziona.

[edit] funziona perché 16M / 32 = 500000Hz e 1/500000 = 0,000002s (2us)

Però il mio loop è questo:

volatile uint8_t counter;

void loop() {
  if (counter == 255) {
    Serial.println("ok");
  
  }
}

La tua inizializzazione del timer sembra corretta ma non ho controllato nel dettaglio in ogni caso non l'ho modificato ma solo copiata e incollata nel setup.

Ciao.

Grazie, Guglielmo e Mauro.

No, non ci siamo... Non è come con le logiche cablate, in cui semplicemente le porte devono poter lavorare alla frequenza massima del segnale! Serve un microcontrollore più veloce, perché, oltre che incrementare il contatore nell'interrupt, devo anche rilevare i fronti del segnale con un altro interrupt, fare qualche calcolo e visualizzare i risultati...
Posso scrivere la struttura del programma accettando i 4us di risoluzione, ma poi, se voglio 1us di risoluzione, devo cambiare hardware.

Vuoi andare sul sicuro e fare qualsiasi cosa ... passa alle Teensy ... hai l'imbarazzo della scelta per la loro velocità (ti rammento che con la 4.x si può fare overclock anche fino ad 1.2 GHz ... dissipando bene il calore della MCU). :grin:

Oltre tutto, per analisi di segnali, ha delle ottime librerie ...

Guglielmo

Probabilmemte, se non ci preoccupiamo del conteuto di r24 ed r25 e chiediamo al compilatore di eliminare tutto il preambolo e l'epilogo con l'attibuto "ISR_NAKED" ... forse ce la fa ;)

Guglielmo

Sì, avevo acquistato un 3.2 per fare un analizzatore di spettro audio e poi un altro per suddividere la banda e avere una migliore risoluzione alle basse frequenze. Purtroppo è rimasto fermo... Devo riprenderlo!
Per questo, però, è sprecato... Nuova Elettronica lo faceva con tre integrati!

In realtà le Teensy sono sprecate per il 75% dei progetti che normalmente gli utenti fanno, ma ... il costo è allineato ad un normale Arduino e allora perché non approfittare delle potenzialità? :wink:

Guglielmo

Mi sembra che costino parecchio... Considera che il mio metro di paragone è il microcontrollore ATmega328p con il quarzo...
L'ESP32 no?... Nel ho due...

Ovvio, se le compari solo alla MCU e quarzo costano molto di più, ma prima parlavi di Arduino UNO e allora costano praticamente uguali ... :roll_eyes:

Puoi provare ... non ho idea ... considera che ESP32 gira sotto il controllo di FreeRTOS™ (anche se tu, di base, non lo vedi).

Guglielmo

Ho avuto la stessa pensata, ma il registro SREG devo comunque salvarlo e disabilitare gli interrupt. No so ho provato con uint16_t ho 2 push r24, r25 e 2 pop. Senza salvare SREG non funziona, cioè così:

ISR(TIMER1_COMPA_vect, ISR_NAKED) {
  __asm__("push r24");
  __asm__("push r25");
    counter++;
  __asm__("pop r25");
  __asm__("pop r24");
    
    reti();
}

Stampa ok solo la prima volta.

Comunque anche con una MCU più potente sollevare una ISR ogni 1us non è che sia una felice idea.

Ciao.

... concordo :confused:

Guglielmo

No... Spero solo che, se compatibile con l'ambiente Arduino, micros() abbia una risoluzione di 1us... Tutta questa storia era per farmi un mioMicros() con la risoluzione di 1us!

Ci sarebbe anche il Timer2Counter:

ma dichiara esplicitamente:

Note: every now and then, but not very often, you will see that the value returned
by get_T2_count or get_T2_micros is late by ~4us.  This is because Timer2 is only
an 8-bit timer, so every 128us the overflow interrupt is called to increment the
overflow counter, and stepping into and out of the interrupt takes 4~5us

Non potrei modificare i parametri di micros() per avere un incremento ogni 1us? Com'è esattamente la storia dei 4us?

Forse sto sbagliando tutto...
Come si fa a misurare con gli interrupt e i contatori il tempo fra un fronte di salita e uno di discesa (e viceversa) di un segnale esterno con la massima precisione disponibile?

Input Capture!...

Uhmm...!!!

pag.95