Forum: Mikrocontroller und Digitale Elektronik Arduino: Unerklärliche Geschwindigkeitsänderung


von Johnny S. (sgt_johnny)


Angehängte Dateien:

Lesenswert?

Hallo

Ich benutze einen Arduino Uno, um 2 32-bit Shift-Register anzusteuern. 
Die Register sind jeweils verkettet.

Als Code verwende ich:
1
void setOutputs(unsigned long firstValue, unsigned long secondValue){
2
  
3
  digitalWrite(OE, LOW); //Disable Outputs to prevent flicker
4
 
5
  for ( unsigned long i=0; i<32; i++){
6
    digitalWrite(DATA, bitRead(firstValue,i));  //Select postion to Read and set state
7
    digitalWrite(CLK, HIGH);  // Set Clock Pin High,
8
    digitalWrite(CLK,LOW);   // Set Clock Pin Low  
9
  }
10
    for ( unsigned long i=0; i<32; i++){
11
    digitalWrite(DATA, bitRead(secondValue,i));  //Select postion to Read and set state
12
    digitalWrite(CLK, HIGH);  // Set Clock Pin High,
13
    digitalWrite(CLK,LOW);   // Set Clock Pin Low  
14
}
15
  digitalWrite(OE, HIGH); //Enable Outputs
16
  
17
}

Es werden jeweils zwei 32-bit Variablen übergeben. In meinem Versuch 
genau zweimal der selbe inhalt.

Auf der Suche nach der Ursache eines anderen Problems (Flackern) stiess 
ich auf folgendes Phänomen

Beim shiften der 2ten 32-bit Variable, scheint die Clock schneller zu 
laufen,Siehe Screenshot, in der mitte ist genau der Wechsel von Variable 
1 auf 2 zu sehen.

Das es mit der ungenauikeit des Quarz zutun hat bezweifle ich, weil die 
einzelnen HIGH/LOW jeweils genau gleich sind. Und das bei x Durchläufen 
so war.

Auch der Code ist ja exakt der selbe.


Hat jemand irgendeine Idee warum/was da passiert?

: Bearbeitet durch User
von Willi (Gast)


Lesenswert?

Tausch mal in den bitRead() Anweisungen first und secondValue,
vielleicht kommst du der Sache dann näher.

MfG Willi

von Tom E. (Gast)


Lesenswert?

Johnny S. schrieb:
> Auch der Code ist ja exakt der selbe.

Hast du dir den erzeugten Assembler Code angesehen oder wie meinst du 
das?
Der selbe Code kann es schon nicht sein, weil die beiden Argumente 
unterschiedlich liegen.

Mach die Ausgabe mal über eine Zwischenvariable, also
1
  unsigned long  y = firstValue;
2
  for ( unsigned long i=0; i<32; i++){
3
    digitalWrite(DATA, bitRead(y,i));  //Select postion to Read and set state
4
...
5
  y = secondValue;
6
  for ( unsigned long i=0; i<32; i++){
7
    digitalWrite(DATA, bitRead(y,i));  //Select postion to Read and set state

p.s. Man könnte sich auch fragen, warum die Laufvariable mit einem 
Wertebereich von 0..31 unbedingt 32 Bit haben muss. Da würden es auch 
fünf tun oder was dem Prozessor so am bequemsten in ein Register passt.

von Nobody (Gast)


Lesenswert?

Warum so kompliziert....
Arduino kennt shiftOut().

von Johnny S. (sgt_johnny)


Lesenswert?

Willi schrieb:
> Tausch mal in den bitRead() Anweisungen first und secondValue,
> vielleicht kommst du der Sache dann näher.
>
> MfG Willi

Passiert nix...





Tom E. schrieb:
> Johnny S. schrieb:
>> Auch der Code ist ja exakt der selbe.
>
> Hast du dir den erzeugten Assembler Code angesehen oder wie meinst du
> das?
> Der selbe Code kann es schon nicht sein, weil die beiden Argumente
> unterschiedlich liegen.
>
> Mach die Ausgabe mal über eine Zwischenvariable, also
1
  unsigned 
2
> long  y = firstValue;
3
>   for ( unsigned long i=0; i<32; i++){
4
>     digitalWrite(DATA, bitRead(y,i));  //Select postion to Read and set 
5
> state
6
> ...
7
>   y = secondValue;
8
>   for ( unsigned long i=0; i<32; i++){
9
>     digitalWrite(DATA, bitRead(y,i));  //Select postion to Read and set 
10
> state
>
> p.s. Man könnte sich auch fragen, warum die Laufvariable mit einem
> Wertebereich von 0..31 unbedingt 32 Bit haben muss. Da würden es auch
> fünf tun oder was dem Prozessor so am bequemsten in ein Register passt.

Ok werd ich probieren, seh aber nicht warum das etwas bringen sollte, 
und [long y] zerschneided mir doch den code? wenn dann [unsigned long] 
oder.

Wie meinst du mit Laufvariable? es muss doch 32bit sein, weil 32bits 
geshifted werden sollen.

: Bearbeitet durch User
von Nobody (Gast)


Lesenswert?

Johnny S. schrieb:
> Wie meinst du mit Laufvariable? es muss doch 32bit sein, weil 32bits
> geshifted werden sollen.

Da sie nie größer als 32 werden kann, ist es völlig überflüssig sie 32 
Bit breit zu machen.

von Michael U. (amiga)


Lesenswert?

Hallo,

for ( unsigned long i=0; i<32; i++)

Du zählst von 0 bis 31, daß passt in ein byte oder uint8_t oder hier 
auch char.

Gruß aus Berlin
Michael

von Jasson J. (jasson)


Lesenswert?

@Johny

Letztlich hast du da eine in software gelöste SPI.
Mit dem Arduino Framework bist du ja nicht direkt auf der Hardware, 
sondern arbeitest auf einer Art "Abstraction Layer" der dir dann sowas 
wie digitalWrite() anbietet.
In dem Ardu-Framwork laufen im Hintergrund permanent irgendwelche Dinge 
ab, die man nicht unmittelbar sieht. Z.B. gibt es z.B. einen Systick 
basierend auf einem der Hardwaretimer, der dir mittels Interrupt immer 
mal dazwischen funkt und z.B. deine digitalWrite() oder andere 
unterbricht. Das führt dann zu "jitternden" Ausführungszeiten.

Letztlich müsste man um sicher zu gehen auch bei shiftOut() in den 
Quellcode der Bibliothek schauen.
Der springende Punkt sind solche Formulierungen
in den Arduino References (https://www.arduino.cc/en/Reference/ShiftOut) 
:
"This is a software implementation; see also the SPI library, which 
provides a hardware implementation that is faster but works only on 
specific pins. "

von Jasson J. (jasson)


Lesenswert?

>Wie meinst du mit Laufvariable? es muss doch 32bit sein, weil 32bits
>geshifted werden sollen.

Hier verwechselst du Wertebereich und Speicherbreite.
Gemünzt auf unser 10er Zahlensystem würde deine Überlegung bedeuten, 
dass wenn du z.B. eine 1000 darstellen willst, eine Zahl mit 1000 
Ziffern bräuchtest.
Frage google mal nach so etwas wie "Zahlendarstellung im Binärsystem", 
da kommt sicher was.

von Johnny S. (sgt_johnny)


Lesenswert?

Jasson J. schrieb:
> @Johny
>
> Letztlich hast du da eine in software gelöste SPI.
> Mit dem Arduino Framework bist du ja nicht direkt auf der Hardware,
> sondern arbeitest auf einer Art "Abstraction Layer" der dir dann sowas
> wie digitalWrite() anbietet.
> In dem Ardu-Framwork laufen im Hintergrund permanent irgendwelche Dinge
> ab, die man nicht unmittelbar sieht. Z.B. gibt es z.B. einen Systick
> basierend auf einem der Hardwaretimer, der dir mittels Interrupt immer
> mal dazwischen funkt und z.B. deine digitalWrite() oder andere
> unterbricht. Das führt dann zu "jitternden" Ausführungszeiten.
>
> Letztlich müsste man um sicher zu gehen auch bei shiftOut() in den
> Quellcode der Bibliothek schauen.
> Der springende Punkt sind solche Formulierungen
> in den Arduino References (https://www.arduino.cc/en/Reference/ShiftOut)
> :
> "This is a software implementation; see also the SPI library, which
> provides a hardware implementation that is faster but works only on
> specific pins. "

Ich benutze ja nicht die ShiftOut libaray, weil diese nur 8 bit 
unterstützt, es ist ja mit digitalWrite gelöst.

Die SPI kann ich ja nicht nutzen,weil die Pins belegt sind...

von chris (Gast)


Lesenswert?

Johnny S. schrieb:
> Ich benutze ja nicht die ShiftOut libaray, weil diese nur 8 bit
> unterstützt, es ist ja mit digitalWrite gelöst.

Dann ruf sie halt 4 mal auf mit dem jeweiligen Byte deiner 32bit 
Variable.

von Michael K. (Gast)


Lesenswert?

Du siehst doch das digitalWrite unglaublich lange braucht um mal so ein 
Portbit zu setzen. Rechne das mal in Prozesssorzyklen aus.
Der fährt erst drei mal mit dem Fahrad um den Block bis der Pin gesetzt 
wird.

Großes Rätsel was der im Hintergrund tut und vieleicht resultiert daraus 
Deine Abweichung.

von Jasson J. (jasson)


Lesenswert?

>>Ich benutze ja nicht die ShiftOut libaray, weil diese nur 8 bit
>> unterstützt, es ist ja mit digitalWrite gelöst.

>Dann ruf sie halt 4 mal auf mit dem jeweiligen Byte deiner 32bit
>Variable.

Das mach ich immer gerne mit Uninions, da kommt man ohne shifterei an 
die bytes.

von Johnny S. (sgt_johnny)


Lesenswert?

Michael K. schrieb:
> Du siehst doch das digitalWrite unglaublich lange braucht um mal so ein
> Portbit zu setzen. Rechne das mal in Prozesssorzyklen aus.
> Der fährt erst drei mal mit dem Fahrad um den Block bis der Pin gesetzt
> wird.
>
> Großes Rätsel was der im Hintergrund tut und vieleicht resultiert daraus
> Deine Abweichung.

Unglaublich lange?

Also nur so zur info, die dauer des Clock high ist 6us!!!!

Laut IC-Spezifkation muss Data vor Clock anstehen, dazwischen sind vlt 
2-3us wie man erkennen kann.


Kann ich herausfinden was im Hintergrund passiert?

von Wolfgang A. (Gast)


Lesenswert?

Johnny S. schrieb:
> Kann ich herausfinden was im Hintergrund passiert?

Erstmal kannst du das digitalWrite() gegen einen direkten Portzugriff 
tauschen und die Auseinanderprökelei der jeweils 32 Bit mit bitRead(y,i) 
von der Aussendung trennen. Damit hast du schon mal Rechenzeit für 
Bitprökelei von der eigentlichen Aussendung getrennt. Striktes Einhalten 
von EVA kann in solchen Fällen schon mal zur Übersichtlichkeit 
beitragen...

von Wolfgang A. (Gast)


Lesenswert?

Johnny S. schrieb:
> Unglaublich lange?
>
> Also nur so zur info, die dauer des Clock high ist 6us!!!!

Oder miss doch mal aus, wie auf dem Scope eine reine Sequenz aus
1
  digitalWrite(CLK,HIGH);
2
  digitalWrite(CLK,LOW);
3
  digitalWrite(CLK,HIGH);
4
  digitalWrite(CLK,LOW);
aussieht.

von Chr. M. (snowfly)


Lesenswert?

Johnny S. schrieb:
> Kann ich herausfinden was im Hintergrund passiert?
1
void digitalWrite(uint8_t pin, uint8_t val)
2
{
3
  uint8_t timer = digitalPinToTimer(pin);
4
  uint8_t bit = digitalPinToBitMask(pin);
5
  uint8_t port = digitalPinToPort(pin);
6
  volatile uint8_t *out;
7
8
  if (port == NOT_A_PIN) return;
9
10
  // If the pin that support PWM output, we need to turn it off
11
  // before doing a digital write.
12
  if (timer != NOT_ON_TIMER) turnOffPWM(timer);
13
14
  out = portOutputRegister(port);
15
16
  uint8_t oldSREG = SREG;
17
  cli();
18
19
  if (val == LOW) {
20
    *out &= ~bit;
21
  } else {
22
    *out |= bit;
23
  }
24
25
  SREG = oldSREG;
26
}
Die Sourcen dazu liegen unter: 
$PATH\Arduino\hardware\arduino\avr\cores\arduino\

von Michael K. (Gast)


Lesenswert?

Johnny S. schrieb:
> Also nur so zur info, die dauer des Clock high ist 6us!!!!

Und wie viele Clock Cycles sind das bei 16Mhz und wie wiele Befehle 
arbeitet die MCU währenddessen ab ?
Dafür das Du direkt hintereinander sagst mache Pin High, mache Pin Low 
grenzen 6us an kompletter Arbeitsverweigerung.

> Kann ich herausfinden was im Hintergrund passiert?
Ja, indem Du den generierten ASM Code anschaust.

von Johnny S. (sgt_johnny)


Lesenswert?

Wahnsinn, vielen Dank


Ich habe heute eine Messung gemacht

32 x CLOCK High/Low mit digitalWrite dauert 495  macht ca 1ms bei 
2x32bit

danach

32 x CLOCK High/Low mit PORTE |= _BV(PE4) dauert es gerade mal 5us !!!


Werde heute meinen Code umbauen und dann nochmals testen. Ich glaube 
damit ist auch das ursprüngliche Problem erledigt, ich hatte Probleme 
das beim neu setzen der Werte in den 2 Registern die Anzeige kurz 
flackerte, liegt wohl abere daran das ja die Outputs in diesem fall für 
mehr als eine Millisekunde ausgeschaltet sind.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.