micros() Funktion - Bremse?

Hallo,

ich bastel ja gerade mit dem ATtiny rum und muß viele Funktionen selbst hinzufügen.
Beim nachbauen der millis() und micros() Funktion viel mir auf, dass die micros eine übelste Bremse darstellt.
Jetzt finde ich aber im IDE Installationpfad die millis/micros Funktion nicht. Wo befinden sich diese?

Beim ATtiny mit 8MHz hat er für 1ms mit seinen Timer Compare Match ISR satte 64*124 Takte Zeit.
Für 1µs allerdings nur 7 Takte. Er würde permanent im Interrupt hängen. Sinnlos.
Ohne micros stimmen die gemessenen Zeiten.
Mit Micros kommt er nicht mehr hinterher, da ist eine µs schon 14µs und 1ms dann 14ms

ISR(TIMER0_COMPA_vect)
{
	static uint32_t local_micros = 0;
	static uint32_t local_millis = 0;
		
	micros++;
	local_micros++;
        PORTB ^= (1<<PB0);		// toogle
	
	if(local_micros > 999) {
		millis++;
		local_millis++;
		local_micros = 0;
		PORTB ^= (1<<PB1);	// toogle
	}
	
	if(local_millis > 999) {
		second++;
		local_millis = 0;
		PORTA ^= (1<<PA7);	// toogle
	}
}
void set_Timer0 ()		// micros
{
	cli();								// Interupts ausschalten
	TCNT0 = 0;				
	TCCR0A = 0;
	TCCR0B = 0;
	TIMSK0 = 0;
	TCCR0A = (1 << WGM01);		// CTC Modus
	OCR0A = 7;			// TOP
	TIMSK0 = (1 << OCIE0A);		// Interupts einschalten
	TCCR0B = (1 << CS00);		// Prescaler 1, Timer starten
	sei();
}

Beim Arduino mit 16MHz wären das eigentlich immer noch mickrige 15 Takte.
Jetzt wollte ich mal nachschauen ob das “Arduino” vielleicht anders gelöst hat.
Wo finde ich das zum vergleichen?

Jetzt finde ich aber im IDE Installationpfad die millis/micros Funktion nicht. Wo befinden sich diese?

Bei mir in:
E:\Programme\Arduino\hardware\arduino\avr\cores\arduino\wiring.c

Und ja, das ist anders gelöst!
:wink:

Hallo,

Danke, da bin ich aber jetzt mal gespannt wie ein Honigkuchenpferd … :slight_smile:

Hallo,

Timer 0 arbeitet mit einem Prescaler von 64. Erstaunlich. Ist scheinbar alles für millis eingestellt.
Diese Formel für micros ist demnach der Trick oder Knackpunkt.

return ((m << 8) + t) * (64 / clockCyclesPerMicrosecond());

Wo finde ich die konkreten Settings vom Timer 0?
Um das noch besser nachvollziehen zu können.

In wiring.c !
In der init()

Hmmm ....
Warum verwendest du nicht:

??
Das greift dann auf die original Funktionen in wiring.c zurück.
Brauchst dir also keine Arbeit damit zu machen....

Hallo,

ich hatte mir mal das hier importiert.

Aber irgendwie komme ich mit dem Sketch Upload und meinen mkII nicht klar.
"Hochladen mit Programmer" sollte eigentlich funktionieren. Bei mir nicht. Keine Ahnung warum.
Direkt aus Atmel Studio klappt alles mit dem mkII. Seitdem muß ich tiefer vordringen ins Controller Dickicht.

Ist denn mit der Bezeichnung ATtiny84 und ATtiny841 der gleiche gemeint? Sicherlich nicht.
Arbeitest du mit dem Import?

Danke für den weiteren Hinweis. Hatte gedacht da fehlt nochwas. Es wird also kein weiteres Compare Register o.ä. vorgeladen. Weil streng genommen müßte ein Compare Register auf 249 gesetzt werden um auf genau 1ms zukommen. Oder der Zähler müßte vorgeladen werden. Aber gut, man war minimal großzügig nehme ich an. Oder weil die Compares für die PWMs belegt sind und deswegen wurde das so vereinfacht gemacht.

Ist denn mit der Bezeichnung ATtiny84 und ATtiny841 der gleiche gemeint? Sicherlich nicht.

OK, die 1 ist bei mir durchgerutscht…
Und ja, wird sich unterscheiden.

Arbeitest du mit dem Import?

Ich arbeite mit der Lib… aber nur mit dem AtTiny85, bisher.
Und dann auch nur wenig.

Danke für den weiteren Hinweis. Hatte gedacht da fehlt nochwas. Es wird also kein weiteres Compare Register o.ä. vorgeladen. Weil streng genommen müßte ein Compare Register auf 249 gesetzt werden um auf genau 1ms

Ja??
Das Register bleibt auf 0, also alle 256 Ticks ein Tack.
Und die Korrekturrechnung wird in der Timer ISR gemacht.

Ich kann dir mal meine TimerLibs geben… (siehe Anhang)
Die benutze ich, wenn ich Timer0 für andere Zwecke als Millis gebrauche.
Micros() können die allerdings auch nicht. Gab noch keine Notwendigkeit.
(irgendwo geklaut und für Arduino angepasst)

Timers.zip (4.21 KB)

Hallo,

deine Funktionen sehen “einfacher” aus. :slight_smile:

Zurück zu den Arduino Funktionen.

Ganz ehrlich, ich sehe keine Korrektur im ISR(TIMER0_OVF_vect).
Bzw. verstehe ich das mit den FRACT nicht.

Und was ich überhaupt nicht nachvollziehen kann ist in der micros() Funktion, dieses hier

if ((TIFR0 & _BV(TOV0)) && (t < 255))
  m++;

Es wird geprüft ob das Overflow Flag gesetzt wurde. Also ob ein Overflow stattgefunden hat.
Gleichzeitig wird überprüft ob der Timer noch kleiner 255 ist. Also kleiner seinem Maximum.
Ähm was? Beides schließt sich doch gegenseitig aus. Entweder der Timer lief über 255 und löst damit einen Overflow aus oder es passiert nichts. Außerdem wird das Overflowflag doch sofort wieder gelöscht. Es müßte Zufall sein genau im gesetzten Moment das Flag abzufragen. Da komme ich nicht mit. Kann du das erklären, bitte?

Hat der Overflow Interrupt irgendeinen zeitlichen Vorteil im Vergleich zum Compare Match Interrupt?
Ich würde versuchen meinen Ansatz mit dem Arduino micros Trick zu kombinieren. Sollte doch klappen?

Da habe ich mich noch nicht genug rein gekniet, um dir das erklären zu können.
:frowning: :frowning: :frowning:

Hallo,

okay macht nichts.
Du darfst auch mal etwas nicht sofort wissen wenn ich aus der kalten tausend Löcher in den Bauch frage. :slight_smile:
Habe nun meinen Ansatz mit dem Arduino Trick kombiniert.
Was hälste davon?
Verbesserungen im Speed möglich?

/* *** Funktionen *** */

ISR(TIMER0_COMPA_vect)
{
	static uint32_t local_millis = 0;
		
	millis_count++;
	local_millis++;
	PORTB ^= (1<<PB1);		// toogle jede Millisekunde

	if(local_millis > 999) {
		second_count++;
		local_millis = 0;
		PORTA ^= (1<<PA7);	// toogle jede Sekunde
	}
}


void set_Timer0 ()		// millis
{
	cli();					// Interupts ausschalten
	TCNT0 = 0;				// Register Reset
	TCCR0A = 0;
	TCCR0B = 0;
	TIMSK0 = 0;
	TCCR0A = (1 << WGM01);			// CTC Modus
	OCR0A = 124;				// TOP = (CPU Takt / 2 / Prescaler / Takt) - 1
	TIMSK0 |= (1 << OCIE0A);		// Interupts einschalten
	TCCR0B |= (1 << CS01) | (1 << CS00);	// Prescaler 64, Timer starten
	sei();
}


uint32_t micros()
{
	uint32_t m = 0;
	uint8_t  t = 0;
	ATOMIC_BLOCK (ATOMIC_RESTORESTATE)    // cli und sei mit SREG Sicherung
	{
		m = millis_count;
		t = TCNT0;
	}
	return ( (m*1000)+(t*8) );
}


uint32_t millis()
{
	uint32_t value = 0;
	ATOMIC_BLOCK (ATOMIC_RESTORESTATE)    // cli und sei mit SREG Sicherung
	{
		value = millis_count;
	}
	return value;
}


uint32_t second()
{
	uint32_t value = 0;
	ATOMIC_BLOCK (ATOMIC_RESTORESTATE)    // cli und sei mit SREG Sicherung
	{
		value = second_count;
	}
	return value;
}

Rechnerisch haut es hin. Nur wie die µs praktisch überprüfen? Toogeln klappt ja nicht.
Achso, Die Rechnungen und Formel ist auf einen 8MHz µC bezogen.

millis_ TCNT0 _micros =
0 0 0
0 1 8
0 2 16
0 32 256
0 64 512
0 96 768
0 124 992
1 0 1000
1 32 1256
1 64 1512
1 96 1768
1 124 1992
2 0 2000
2 32 2256
2 64 2512
2 96 2768
2 124 2992
3 0 3000

Hallo,

wenn ich das zum testen auf die serielle ausgeben lasse, dann müßte ich doch zwischen millis und micros Ausgabe immer eine konstante Differenz drin haben? Sehe ich das richtig? Ich mach gern Vorüberlegungen.

Hallo,

die Differenz läuft nicht weg, schwankt im “unteren Rahmen”, bedingt durch die seriellen Ausgaben und Zahlenlänge, nehme ich an. Sollte demnach passen. :slight_smile: Jetzt kann ich das für den ATtiny nehmen.

Arduino millis | neue millis | neue micros | Differenz neue micros zu neue millis
micros.png

Kann man die micros Funktion noch zu optimieren in Bezug auf Geschwindigkeit?

/*
 * Created: 04.10.2016 14:58:34
 * Author: Doc_Arduino
 */ 

#include <util/atomic.h>    // für cli() und sei() mit SREG Sicherung

volatile unsigned long micros_count;
volatile unsigned long millis_count;
volatile unsigned long second_count;

unsigned long last_millis;
long temp;
unsigned long millis0_now;
unsigned long millis2_now;
unsigned long micros2_now;

void setup()
{  
    
  Serial.begin(250000);
  set_Timer2(); 
}
      
void loop()
{ 
  millis0_now = millis();
  millis2_now = millis_();
  micros2_now = micros_();
      
  if (millis0_now-last_millis > 999) {
    Serial.print(millis0_now);  Serial.print('\t');
    Serial.print(millis2_now);  Serial.print('\t');
    Serial.print(micros2_now);  Serial.print('\t');
    temp = millis2_now*1000 - micros2_now;
    Serial.println(temp);
    last_millis = millis0_now;
  }     
}


/* *** Funktionen *** */

ISR(TIMER2_COMPA_vect)
{
  static uint32_t local_millis = 0;
    
  millis_count++;
  local_millis++;

  if(local_millis > 999) {
    second_count++;
    local_millis = 0;
  }
}


void set_Timer2 ()    // millis
{
  cli();                    // Interupts ausschalten
  TCNT2 = 0;                // Register Reset
  TCCR2A = 0;
  TCCR2B = 0;
  TIMSK2 = 0;
  TCCR2A = (1 << WGM21);          // CTC Modus
  OCR2A = 249;                    // TOP = (CPU Takt / 2 / Prescaler / Takt) - 1
  TIMSK2 |= (1 << OCIE2A);        // Interupts einschalten
  TCCR2B |= (1 << CS22);          // Prescaler 64, Timer starten
  sei();
}


uint32_t micros_()
{
  uint32_t m = 0;
  uint8_t  t = 0;
  ATOMIC_BLOCK (ATOMIC_RESTORESTATE)    // cli und sei mit SREG Sicherung
  {
    m = millis_count;
    t = TCNT2;
  }
  return ( (m*1000)+(t*4) );
}


uint32_t millis_()
{
  uint32_t value = 0;
  ATOMIC_BLOCK (ATOMIC_RESTORESTATE)    // cli und sei mit SREG Sicherung
  {
    value = millis_count;
  }
  return value;
}


uint32_t second_()
{
  uint32_t value = 0;
  ATOMIC_BLOCK (ATOMIC_RESTORESTATE)    // cli und sei mit SREG Sicherung
  {
    value = second_count;
  }
  return value;
}

(kleine Korrektur der loop)

Kann man die micros Funktion noch zu optimieren in Bezug auf Geschwindigkeit?

Glaube ich nicht.

Hallo,

okay, Danke dir und für alles überhaupt.

Hatte doch noch einen Rechenfehler drin im

return ( (m*1000)+(t*4) );

t4 statt vorher falsch t16. Hatte das für die 16MHz falsch geändert.
Weil ein TCNT2 Count 4µs entsprechen mit Prescaler 64.
Die Abweichung aus der seriellen sind nun auch kleiner, konstanter und plausibler.
Deswegen kann der micros Zähler beim Arduino (und bei mir) auch nur in 4er Sprüngen zählen. Weiteres Rätsel gelöst.

Hallo nochmal,

noch eine kurze Erklärung warum ich leider deine tolle Timer Funktion so nicht nutzen kann.
Ich möchte auf dem ATtiny anstehende Signallängen auswerten/ausmessen.
Damit das ganze schneller geht, ich selbst kann die Längen auf einem anderen µC anpassen, wollte ich die Zeiten verkürzen von ms in µs und dazu brauche ich einen funktionierenden micros Zähler.
Dem steht jetzt nichts mehr im Wege.