C Attiny13 Frequenzmessung

@Dirk macht C daraus ... Das "z++" sollten fünf weitere Takte sein ("LDS->INC->STS")
Das "If..." schlägt mMn mit vier Takten zu ("LDS->CPI->BRanch")

Off Topic. Ich gehe nur mal kurz auf den Zähler z ein. Du wirst dich wundern, wie der Compiler optimiert!
Er inkementiert nicht, er dekrementiert mit subi und vergleicht auf Null mit brne. Ganze drei Maschinenzyklen, mit Sprung zurück (nicht 10 bis 20). Also 100x3x125ns=37,5us, wenn ich richtig rechne.
Im Programm initialisiere ich z mit 0. Der Compiler initialisiert mit 101 und dekrementiert. Er weiß, ich möchte auf >100 prüfen. 100 ist hier bekannt.


CodeBox C
  7be:   c5 e6     ldi   r28, 0x65   ; 101
  7c0:   8c 2f     mov   r24, r28
  7c2:   81 50     subi   r24, 0x01   ; 1
uint8_t z=0;

while (1)
{
   z++;
   if (z > 100)
  7c4:   f1 f7     brne   .-4     ; 0x7c2 <main+0x68>
   {
     z = 0;
     Display_Update();
  7c6:   21 d4     rcall   .+2114     ; 0x100a <Display_Update>
  7c8:   fb cf     rjmp   .-10     ; 0x7c0 <main+0x66>


Zum Thema:
Ralf, baue testweise mal vor der Displayausgabe zB ein _delay_ms(500) ein, damit der Aufruf nicht so schnell hintereinander kommt.
meine_frequenz*60 benötigt noch Zeit und die Displayroutine selber auch. Aber selbst wenn es insgesamt 100us Periodendauer Displayausgabe ist, ist die Zeit viel zu kurz und unnötig. Das wären grob 10.000 Ausgaben je Sekunde.
 
Ok, wie man das also auch in ASM machen würde. Ich war diesbezüglich etwas verunsichert, weil z ja in Zeile 15 als signed word (16bit) initialisiert wurde, also als Variable im SRAM vorgehalten werden soll (oder kann man in C auch beliebig verfügbare Rechenregistervariablen reservieren?). Wenn z also (auch) im SRAM verfügbar und aktuell gehalten werden soll (von wo aus es vielleicht ein IRQ nutzen können wollte), muß z doch nach dem inkrement/dekrement zurückgeschrieben werden.
Natürlich kann der Compiler prüfen, ob z noch irgendwo anders genutzt wird, und auf das ablegen im SRAM komplett verzichten wenns nicht nötig ist, aber das macht natürlich das nachvollziehen der Timings schwerer (verwendet man irgendwo in 'ner ISR das z, ergeben sich andere timings)

P.S.: analog zu 8bit-SUBI könnte man bei 16bit noch mit ADIW arbeiten, ist aber auf die vier Doppelregister ab R24 beschränkt...

Edit: Die zusätzliche Manipulation von z müßte ja nicht mal in irgend'ner ISR selbst stattfinden - von dort könnte ja auch irgendwoanders hin springen lassen (möglicherweise sogar mit zur Compilezeit unbekanntem Ziel (unter ASM geht alles) - wie ists in C, und was macht dann der Optimizer draus?

Jaja, wäre natürlich alles recht konstruiert...)
 
Zuletzt bearbeitet:
Off Topic:
Reservieren nicht direkt, zumindest nicht über Umwege. Es ist auch nicht unbedingt sinnvoll oder man muss sich hier halt keine Gedanken drüber machen. Ja man weiß das Timing nicht genau, kann es nur grob abschätzen. Wenn ich sowas mal wissen muss, schaue ich mir den Assemblercode an oder mache ein Schappschuss mit einem laufenden Timer oder gebe Signale an einem Pin aus und messe mit Oszi.
 
Mir ist nicht klar, wo Du ein Verständnisproblem hast. Deswegen schieb ich jetzt einfach mal ein paar Grundlagen dazwischen.
Die AVR besitzen einen seperaten Programmspeicher (Harvard-Architektur). Dieser ist linear, also eine Liste Words. In jedem Word steht eine Maschinencode-/Assemblerinstruktion (bis auf ganz wenige Ausnahmen, die mehr als ein Word benötigen).
Außerdem gibts einen Programmzähler, der auf das Word mit der nächsten abzuarbeitenden Instruktion zeigt.

Beim Reset wird (unter anderem) dieser Programmzähler auf Null gesetzt (Ausnahme Bootloader?), der Controller beginnt also bei Adresse Null mit der Abarbeitung des Program-Flash-Inhaltes. Die Instruktion wird Verarbeitet, der Programmzähler dabei (automatisch) inkrementiert.
Bei einem Sprung (im Sinne von Goto) wird einfach der entsprechende Wert in den Programmzähler geschrieben.
Bei einem Funktionsaufruf (Call Subroutine/Gosub/...), also wo ein Rücksprung erfolgen muß, muß die Rücksprungadresse gesichert werden. Dazu wird am Ende des SRAM ein Stapel eingerichtet, der von hinten nach vorn wächst (und von vorn nach hinten schrumpft). Beim Aufruf der Funktion wird also die Adresse der nächsten Instruktion auf diesen Stapel gelegt, und die Adresse der Funktion in den Programmzähler geladen. Beim Rücksprung aus der Funktion (Return) wird die Adresse vom Stapel genommen, und in den Programmzähler geladen. Es ist also möglich, mehrere Funktionsaufrufe (auch derselben Funktion) ineinander zu schachteln - solange der Speicher für den Stapel reicht.

So, nach einem Reset beginnt der Controller wie gesagt mit Flash-Adresse Null. Die folgenden Adressen sind mit dem Interrupt-Mechanismus verbunden, deswegen steht in Adresse Null meist ein Sprung in das "Hauptprogramm".
Der Controller arbeitet den Flash-Inhalt also eigentlich linear (mit etwaigen Verzweigungen und Sprüngen) nacheinander ab. Ein Thread. Periphäre Hardware kann aber parallel dazu selbständig arbeiten (die Timer laufen im Hintergrund, und lassen ggf bei PWM die Beine Zappeln, der UART sendet und empfängt bitweise Bytes, der ADC mißt Spannungen, SPI und TWI pumpen/saugen Bytes durch ein Bein usw).
Alle diese Hardware-Module hinterlegen Dir für Dein Programm Informationen in ihren I/O-Registern (manchmal auch Special Function Register oder so genannt).
Du kannst diese Informationen also im Hauptprogramm zyklisch abfragen (pollen).
Oder eben Interrupts nutzen. Was passiert dabei konkret?
Oben hatte ich das Datenblatt des Tiny13 verlinkt, schau mal Kapitel 9 (S. 44) an.
Jede Interrupt-Quelle (beim Tiny13 sinds neun) ist fest mit einer Flash-Adresse verbunden.
Jede Quelle muß erstmal mit einem eigenen Enable-Bit scharfgemacht werden (für den externen Interrupt hast Du das mit "INT0" im "GIMSK" gemacht, für den Timerüberlauf mit "TOIE0" im "TIMSK").
Tritt ein interruptauslösendes Event ein (Timerüberlauf/Flankenwechsel usw) wird automatisch ein entsprechendes Interruptanforderungsflag gesetzt. Ist der Interrupt scharf, und sind Interrupts global enabled, werden automatisch die Interrupts global gesperrt, Flashadresse der eigentlichen nächsten Instruktion auf den Stapel gepackt und die fest mit dem Interrupt verbundene Flash-Adresse angesprungen (also sozusagen ein call subroutine auf diese Adresse mit gleichzeitigem Unterdrücken/Zurückhalten aller Interrupts - außerdem wird dabei meist das Interrupt-Anforderungsflag gelöscht).
Da eine ISR im allgemeinen nicht in diese fest verbundene Adresse paßt, steht dort üblicherweise ein Sprung in die eigentliche Service-Routine, ein Vektor. Deswegen nennt man diesen Flash-Bereich auch InterruptVectorTabelle.
Em Ende der ISR steht dann ein Return from Interrupt, welches die Rücksprungadresse vom Stapel nimmt und anspringt, und gleichzeitig die Interrupts global wieder scharfmacht.
Ein Interrupt kann also normalerweise nicht von einem anderen Interrupt unterbrochen werden.
Bei etwaigen zurückgehaltenen Interrupts, von inzwischen eingetretenen Interruptereignissen, ist ja immer noch das Anforderungsflag gesetzt, sobald die Interrupts global wieder freigegeben werden, werden die wartenden Interrupts wirksam. Die Priorität entspricht der Reihenfolge in der Interruptvektortabelle.
Ob die Interrupts global scharf sind oder nicht, legt das "I"-Bit im Statusregister fest. Dieses kann (neben der automatischen Manipulation durch den Interruptmechanismus) mit SEI (set I) gesetzt werden, mit CLI (clear I) gelöscht werden.
Das macht zB Sinn, wenn eine Operation mit einer Variable mit mehr als einem Byte durch einen Interrupt unterbrochen werden könnte, welcher die Variable zwischendurch verändert. Die AVR arbeiten ja mit einer 8bit-Aritmetik. Um also zwei Words zu addieren, werden zwei Byteadditionen benötigt. Zwischen den beiden Byteadditionen könnte ein Interrupt zuschlagen, der eine der Variablen verändert.
Um so eine Operation unteilbar - atomar - zu bekommen, unterdrückt man also vorher global alle Interrupts (CLI) und macht sie ggf hinterher wieder scharf (SEI).

Also zurück zur Frage, wann die Interrupts global scharfzumachen sind: dann, wenn Du sie das erste mal brauchst, im allgemeinen also erst, wenn alle verwendeten Interruptquellen initialisiert sind.

In dem Zusammenhang fällt mir bei Deinem Code (#18) auf, daß Du den externen Interrupt in der Timerüberlaufs-ISR initialisierst, und zwar bei jedem Aufruf (also bei jedem Überlauf). Da der Timer mindestens einmal überläuft, funktioniert das natürlich, da die Werte der drei Bits in GICR und MCUCR sich nicht ändern stört das ständige Überschreiben (mit denselben Werten) auch nicht weiter - es kostet nur unnötig Zeit.
Es wäre sinniger, das einmal wie beim Timer in der main zu machen.
Ebenso das setzen des Beines als Eingang - das muß auch nicht ständig in der while(true)-Schleife wiederholt werden...

Was soll eigentlich Zeile 42? Der Mega8 hat doch gar kein GIMSK. INT0 bis zum nächsten Timerüberlauf abschalten (eigentlich in GICR)?

Zur Displayausgabe kann ich erstmal nichts weiter sagen, bezüglich der Siebensegmente sehe ich keinen Code, ein TWI-LCD wäre komplexer, wobei Du sicher vorgefertigte C-Routinen verwenden würdest.
Aber nochwas zu Deiner Warteschleife mit "z":
Der Controller rennt mit 8MHz.
Das setzen des Beines als Eingang sollte C mit drei Takten umsetzen (oder gut optimiert mit einem Takt - @Dirk macht C daraus "IN->ANDI->OUT" oder "CBI"?)
Das "z++" sollten fünf weitere Takte sein ("LDS->INC->STS")
Das "If..." schlägt mMn mit vier Takten zu ("LDS->CPI->BRanch")
Also über den Daumen 10-20 Takte, das ganze hundertmal - dann würde disp_Zähler alle 1000-2000 Takte aufgerufen werden, also mit 4-8kHz.
Wie von Dirk angedeutet, verzögern die Interrupts das ganze nochmal ein wenig.

Hi LotadaC,
hi Dirk,
Gimsk war ein Relikt aus dem attiny-Code, ist nun raus.
Die Interruptparametrierung des INT0 findet nun im main-Code einmal statt.
Die z-Inkrementierung war zur Entlastung des Abfrage-Prozesses gedacht.
Der Eingang wird nun auch nur noch einmal in main() gesetzt.
Ein Delay nach der Ausgabe auf die Anzeige bringt nur wenig.
Danke nochmal LotadaC für die Erklärung der Interrupt-Grundlagen.
Jetzt versuche ich der vollständigkeitshalber noch Dirks Code über die Periodendauer.
Habt Ihr auf die Schnelle einen C-Code für das LCD-Display parat?
Als i2c-Treiber ist eine Philips PCF8574 verbaut. Da wäre ich gerne richtig faul.
Es ist ja auch so.... richtig stromsparend sind 7-Segmentanzeigen nicht....
also mal mit 9 V-Block wirds eng...mit Impulszählung in der freien Natur.....
Mit Grauen denke ich an die 7-Segment-Programmierung... :)
Gruß und Dank
Ralf
 
Haste mal in Dinos FAQ-Übersicht gestöbert? Ich kann mich diesbezüglich (paralleles CharLCD am PCF...) nur an Cassio, Klaus (und Dino??) erinnern, war aber mMn alles Bascom.
Grundsätzlich sollten sich LCDs wieder etwas einfacher handhaben lassen als multigeplexte Siebensegmente - das TWI sorgt dann wieder für etwas ausgleichende Herausforderung. (Wenn Dirk und co nicht wider irgendwas aus dem Hut zaubern...)
 
Hi LotadaC,
hi Dirk,
Gimsk war ein Relikt aus dem attiny-Code, ist nun raus.
Die Interruptparametrierung des INT0 findet nun im main-Code einmal statt.
Die z-Inkrementierung war zur Entlastung des Abfrage-Prozesses gedacht.
Der Eingang wird nun auch nur noch einmal in main() gesetzt.
Ein Delay nach der Ausgabe auf die Anzeige bringt nur wenig.
Danke nochmal LotadaC für die Erklärung der Interrupt-Grundlagen.
Jetzt versuche ich der vollständigkeitshalber noch Dirks Code über die Periodendauer.
Habt Ihr auf die Schnelle einen C-Code für das LCD-Display parat?
Als i2c-Treiber ist eine Philips PCF8574 verbaut. Da wäre ich gerne richtig faul.
Es ist ja auch so.... richtig stromsparend sind 7-Segmentanzeigen nicht....
also mal mit 9 V-Block wirds eng...mit Impulszählung in der freien Natur.....
Mit Grauen denke ich an die 7-Segment-Programmierung... :)
Gruß und Dank
Ralf

Da hätte ich was gefunden.... aber die ganzen Include-Dateien wie LiquidCrystal_I2C.h oder Wire.h führen zu weiteren fehlenden Dateien, wie stream.h z.B.
Stream.h ist doch CPP, oder?
http://arduino-info.wikispaces.com/LCD-Blue-I2C#v1
 
Da hätte ich was gefunden.... aber die ganzen Include-Dateien wie LiquidCrystal_I2C.h oder Wire.h führen zu weiteren fehlenden Dateien, wie stream.h z.B.
Stream.h ist doch CPP, oder?
http://arduino-info.wikispaces.com/LCD-Blue-I2C#v1

Hi Dirk.
hi LotadaC,
falls meine vorhergegangene Nachricht Euch irgendwie "off-topic" vorkam, lag es daran, dass ich Eure Nachrichten von Seite 2, nicht gelesen hatte.
ISt mir erst jetzt aufgefallen, als ich meinen Post suchte...

Gruß
 
Der PCF8574 ist ja eigentlich ein I²C-zu-8bit-parallel-Konverter. Ich hab mit sowas noch nichts gemacht.
'Ne fixe Idee wäre allerdings , selbst 'nen universalen CharLCD-Ansteuerungscontroller zusammenzustricken.

Also einen geeigneten Controller wählen, der das Display im 4- oder 8-Bitmodus ansteuert, und der die darzustellenden Daten selbst über einen beliebigen Kanal (TWI, USI, SPI, UART) empfängt. Der Controller könnte mittels PWM 'ne Hintergrundbeleuchtung ansteuern/dimmen, möglicherweise auch den Kontrast regeln.
Man könnte auch über ein paar Taster nachdenken, oder andere Hardwaremodule nutzen, um den eigentlichen Controller zu entlasten.

Wäre vielleicht was für ein gemeinsames Forenprojekt (also insbesondere das Gesamtkonzept betreffend)?

P.S.; sorry, daß es jetzt ganz OT geworden ist...
 
Habt Ihr auf die Schnelle einen C-Code für das LCD-Display parat?
Als i2c-Treiber ist eine Philips PCF8574 verbaut.
Der PCF8574 ist ja eigentlich ein I²C-zu-8bit-parallel-Konverter.

Da ist wahrscheinlich ein alphanumerisches LCD angeschlossen?! Einen fertigen Sourcecode habe ich nicht parat.

In diesem Projekt habe ich I2C verwendet (TWI_master), vielleicht hilft dies etwas weiter.

Vielleicht stellst du aber auch mal deine Routinen für die LED Anzeige ins Forum, und wir schauen uns diese einmal an?!

Dirk :ciao:
 
Der PCF8574 ist ja eigentlich ein I²C-zu-8bit-parallel-Konverter. Ich hab mit sowas noch nichts gemacht.
'Ne fixe Idee wäre allerdings , selbst 'nen universalen CharLCD-Ansteuerungscontroller zusammenzustricken.

Also einen geeigneten Controller wählen, der das Display im 4- oder 8-Bitmodus ansteuert, und der die darzustellenden Daten selbst über einen beliebigen Kanal (TWI, USI, SPI, UART) empfängt. Der Controller könnte mittels PWM 'ne Hintergrundbeleuchtung ansteuern/dimmen, möglicherweise auch den Kontrast regeln.
Man könnte auch über ein paar Taster nachdenken, oder andere Hardwaremodule nutzen, um den eigentlichen Controller zu entlasten.

Wäre vielleicht was für ein gemeinsames Forenprojekt (also insbesondere das Gesamtkonzept betreffend)?

P.S.; sorry, daß es jetzt ganz OT geworden ist...
Da ist wahrscheinlich ein alphanumerisches LCD angeschlossen?! Einen fertigen Sourcecode habe ich nicht parat.

In diesem Projekt habe ich I2C verwendet (TWI_master), vielleicht hilft dies etwas weiter.

Vielleicht stellst du aber auch mal deine Routinen für die LED Anzeige ins Forum, und wir schauen uns diese einmal an?!

Dirk :ciao:

Moin Dirk,
es ist genau dieses Modul hier, das mit der A0 A1 A2-Kennung:
http://arduino-info.wikispaces.com/LCD-Blue-I2C#v1
Datenblatt:
https://www.openhacks.com/uploadsproductos/eone-1602a1.pdf
Und als I2C-Interface der Phillips PCF8574T.
Das Problem ist das Umsetzen der Arduino-Include-Dateien....
Mit Arduino-IDE auf Linux komme ich nicht klar.... include-Dateien, welche sich im selben Ordner wie der Quellcode befinden, will ich mit #include "xyz.h" (nicht <xyz.h> !) einbinden.
Der Compiler findet die Datei nicht...
Den 7-Segment muss ich erst optisch aufaufarbeiten....
jedoch aus Energiegründen, das Ding zieht teilweise bis zu 200 mA, käme mir die LCD-Variane mehr als gelegen......
Wäre in der Tat mal ein neues Projekt wert.... oder?
Immerhin, eine Ausgabe über die RS232 funktioniert.
Aber.... Drehzahlkontrolle eines, z.B. Rasenmähers anhand eines AVRs(über Hall-Sensor) und einen damit verbundenen Notebooks sorgt in der Nachbarschaft für kritische Blicke... oder ist es der Neid... :)
Gruß
Ralf
 
Die .h Dateien sollten normal gefunden werden. Speziell auf Linux kann ich nicht testen.

Info zu include von libs und zusätzlichen Daten findest du hier.

Du nutzt also die Arduino IDE auf Linux, ich bin eher von Atmel Studio ausgegangen.

Ok, an eine Drehzahlmessung für meinen Rasenmäher hatte ich auch noch nicht gedacht. Hast du damit die 500kHz getestet? :D;)


Dirk :ciao:
 
Die .h Dateien sollten normal gefunden werden. Speziell auf Linux kann ich nicht testen.

Info zu include von libs und zusätzlichen Daten findest du hier.

Du nutzt also die Arduino IDE auf Linux, ich bin eher von Atmel Studio ausgegangen.

Ok, an eine Drehzahlmessung für meinen Rasenmäher hatte ich auch noch nicht gedacht. Hast du damit die 500kHz getestet? :D;)


Dirk :ciao:

Im Normalfall nutze ich die Kommandozeile..... bzw. einen Editor wie Geany und einen makefile.....
Die Drehzahlmessung dient zur Kontrolle nachdem der Fliehkraftregler einen Schlag abbekommen hatte.
500 kHz wäre die Solldrehzahl.. ;-)
Das wäre das nächste AVR-Projekt: Elektronische Einspritzung und Zündung ! :)
 
Im Normalfall nutze ich die Kommandozeile..... bzw. einen Editor wie Geany und einen makefile.....
Die Drehzahlmessung dient zur Kontrolle nachdem der Fliehkraftregler einen Schlag abbekommen hatte.
500 kHz wäre die Solldrehzahl.. ;-)
Das wäre das nächste AVR-Projekt: Elektronische Einspritzung und Zündung ! :)

Hier der C-Code für die 7-Segment-Ausgabe......
Anmerkung zur Linguistik bzw. Anglistik:
Schift -> Shift....... da war mir irgendwie aber alles egal....... ;-)



CodeBox C


void disp_zaehler(int32_t wert1);
void schift(void);


#define HC595_PORT   PORTD
#define HC595_DIR   DDRD
#define HC595_PIN  PIND

#define HC595_DATA   PD7  
#define HC595_SHCP   PD6
#define HC595_STCP   PD5

/*
DIO = DATA
SCK = SHCP
RCK = STCP Storage
* */

#define set_ds() HC595_PORT |=  (1  << HC595_DATA);
#define clr_ds() HC595_PORT &= ~(1  << HC595_DATA);

#define set_shift()  HC595_PORT  |=  (1  << HC595_SHCP);
#define clr_shift()  HC595_PORT  &= ~(1  << HC595_SHCP);

#define set_out()  HC595_PORT  |=  (1  << HC595_STCP);
#define clr_out()  HC595_PORT  &= ~(1  << HC595_STCP);

uint8_t bit; //  Schiebt die Bits von Rechts nach Link
uint16_t tmp;  // Eingelesenes Bitmuster    

uint16_t ref;  // Referenz für die Und-Verknüpfung

uint16_t led[11]={
0b0000000011000000, // 0
0b0000000011111001, // 1
0b0000000010100100, // 2
0b0000000010110000, // 3
0b0000000010011001, // 4
0b0000000010010010, // 5
0b0000000010000010, // 6
0b0000000011111000, // 7
0b0000000010000000, // 8
0b0000000010010000,  // 9
0b0000000001111111 // Punkt
};  


void disp_init(void)
   {
HC595_DIR |=  (1<<HC595_DATA) | (1<<HC595_SHCP) | (1<<HC595_STCP);

   }




void disp_zaehler(int32_t wert1)
{
uint8_t i1,i2,i3,i4,i5,i6,i7,i8;

i1=(wert1/10000000)% 10;
i2=(wert1/1000000)% 10;
i3=(wert1/100000)% 10;
i4=(wert1/10000) % 10;
i5=(wert1/1000) % 10;
i6=(wert1/100) % 10;
i7=(wert1/10) % 10;
i8=(wert1/1) % 10;

_delay_us(50);

ref =  0b1000000000000000;


tmp = ( led[i1]|(0b1000000000000000) >>  7 );
HC595_DIR |= ( (1 << HC595_DATA) | (1 << HC595_SHCP ) | (1 << HC595_STCP) );
schift();

tmp = ( led[i2]|(0b1000000000000000) >>  6 );
HC595_DIR |= ( (1 << HC595_DATA) | (1 << HC595_SHCP ) | (1 << HC595_STCP) );
schift();

tmp = ( led[i3]|(0b1000000000000000) >>  5 );
HC595_DIR |= ( (1 << HC595_DATA) | (1 << HC595_SHCP ) | (1 << HC595_STCP) );
schift();

tmp = ( led[i4]|(0b1000000000000000) >>  4 );
HC595_DIR |= ( (1 << HC595_DATA) | (1 << HC595_SHCP ) | (1 << HC595_STCP) );
schift();


tmp = ( led[i5]|(0b1000000000000000) >> 3 );
HC595_DIR |= ( (1 << HC595_DATA) | (1 << HC595_SHCP ) | (1 << HC595_STCP) );
schift();
tmp = ( ( led[i6] ) | (0b1000000000000000) >> 2 );
HC595_DIR |= ( (1 << HC595_DATA) | (1 << HC595_SHCP ) | (1 << HC595_STCP) );
schift();
tmp = ( led[i7]|(0b1000000000000000) >>  1 );
HC595_DIR |= ( (1 << HC595_DATA) | (1 << HC595_SHCP ) | (1 << HC595_STCP) );
schift();
tmp = ( led[i8]|(0b1000000000000000) >>  0 );
HC595_DIR |= ( (1 << HC595_DATA) | (1 << HC595_SHCP ) | (1 << HC595_STCP) );
schift();

//_delay_us(50);


  }



void(schift(void))
{
  
for(bit=0;bit<=15;bit++)  //Bit schieben
  {
  clr_shift();
  clr_out();

  if(tmp & ref)
    {
    set_ds();
    }      
   else
    {
    clr_ds();
    }

  set_shift();
  tmp = (tmp << 1);
  }

set_out();
}

 
Hi Ralf,

ganz verstehe ich den Aufbau deines LED Display noch nicht. Du hast anscheinend 8 Dezimalstellen (jeweils eine Siebensegment LED Anzeige am HC595 8Bit Shift-Register ggf über Treiber). Du überträgst immer 16 Bit je Stelle, ich hätte 8Bit vermutet.
 
Hi Ralf,

ganz verstehe ich den Aufbau deines LED Display noch nicht. Du hast anscheinend 8 Dezimalstellen (jeweils eine Siebensegment LED Anzeige am HC595 8Bit Shift-Register ggf über Treiber). Du überträgst immer 16 Bit je Stelle, ich hätte 8Bit vermutet.
Hi Dirk,
genau 8-Stellen......
Wegen der 16 muss ich selber nochmal gucken....
Gruß
Ralf



CodeBox C
tmp = ( led[die berechnete oder anzuzeigende Ziffer]|(0b1000000000000000) >>  die tatsächliche Stelle );

So wars glaube ich....... ;-)
Warum jeweils die zweite Stelle von Rechts, also die Zehnerstelle, dunkler erscheint als die Anderen..... das weiß ich auch nicht.
 
Zuletzt bearbeitet:
Du überträgst immer 16 Bit je Stelle, ich hätte 8Bit vermutet
Ein serielles Schieberegister für die sieben LEDs und den Punkt(das lowByte), ein Schieberegister für die Digits (highByte). Beide hängen hintereinander.
Digit und Leds werden verORt, und dann durchgeschoben.
 
Ein serielles Schieberegister für die sieben LEDs und den Punkt(das lowByte), ein Schieberegister für die Digits (highByte). Beide hängen hintereinander.
Digit und Leds werden verORt, und dann durchgeschoben.

Ah ok. Jetzt verstehe ich es :)

Warum die 10er Stelle dunkler erscheint, weiß ich auch nicht. Was ungünstig ist, die 1er Stelle ist auf jedenfall länger an, als alle anderen. Die müsste also heller leuchten. Alleine schon wegen dem _delay_us(50) am Anfang der Routine. Allgemein finde ich alles viel zu schnell auf eine andere Dezimalstelle umgeschaltet. Verwende mal testweise nach jedem Shift ein _delay_ms(2). Dadurch bleibt jede Stelle wenigstens 2ms an, bei 8 Stellen sind es dann etwa 16ms, also ca. 60Hz. Wenn das funktioniert, kannst du ggf. die Umschaltung in einem Timer Interrupt realisieren, so dass du kein _delay_ms() benötigst, was dir ja das Hauptprogramm belastet.

Edit:
Noch eine Idee wegen der dunkleren 10er Stelle:
Bei der 1er Stelle entfällt das Shiften für das Digit-Signal
tmp = ( led[i8]|(0b1000000000000000) >> 0 );
Die Ausgabe der 1er Stelle erfolgt noch schneller, der 10er Stelle bleibt kaum An-Zeit. Das ist zwar nur minimal, aber die An-Zeiten sind aktuell auch recht klein, da könnte es etwas ausmachen.
Also allgemein erst mal die An-Zeiten erhöhen.
 
Zuletzt bearbeitet:
Ah ok. Jetzt verstehe ich es :)

Warum die 10er Stelle dunkler erscheint, weiß ich auch nicht. Was ungünstig ist, die 1er Stelle ist auf jedenfall länger an, als alle anderen. Die müsste also heller leuchten. Alleine schon wegen dem _delay_us(50) am Anfang der Routine. Allgemein finde ich alles viel zu schnell auf eine andere Dezimalstelle umgeschaltet. Verwende mal testweise nach jedem Shift ein _delay_ms(2). Dadurch bleibt jede Stelle wenigstens 2ms an, bei 8 Stellen sind es dann etwa 16ms, also ca. 60Hz. Wenn das funktioniert, kannst du ggf. die Umschaltung in einem Timer Interrupt realisieren, so dass du kein _delay_ms() benötigst, was dir ja das Hauptprogramm belastet.

Hi Dirk,
ein delay von 2 ms am Ende der shift-Prozedur bringt in der Tat ein deutliche Verbesserung, optimal. Kein Flackern, und eine homogene Helligkeit.
 
Bei der 1er Stelle entfällt das Shiften für das Digit-Signal
tmp = ( led[i8]|(0b1000000000000000) >> 0 );
Es wird doch immer nur der konstante Teil geschoben, und anschließend verort. Die geschobene Konstante ist selbst auch konstant. Optimiert der Compiler das schieben nicht jedesmal weg?
 
Es wird doch immer nur der konstante Teil geschoben, und anschließend verort. Die geschobene Konstante ist selbst auch konstant. Optimiert der Compiler das schieben nicht jedesmal weg?

Ja stimmt, da hast du recht. Dann habe ich wegen der 10er Stelle auch keine Idee, außer dass die An-Zeiten viel zu kurz sind und dass vielleicht dadurch solch ein Effekt entsteht. Oder man hat nur den Eindruck, dass die 10er Stelle dunkler ist, da die 1er Stelle am hellsten sein sollte.
Also der eigentliche Fehler ist das zu schnelle Umschalten.
 

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)