PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : 100mm 7-Segment Stoppuhr



Koertis
12.04.2011, 09:30
Hallo,

ich würde gerne eine Stoppuhrbauen mit 9x 100mm große 7Segment anzeigen(hh.mm.ss.ttt). Diese Anzeigen benötigen 10V und die üblichen 20mA pro Segment. Ich würde diese gerne mit einem Atmel Atmega ansteuern.
Mittels externen Interrupt wird die Stoppuhr gestartet bzw. gestoppt.
Meine Frage wäre nun, wie gehe ich das am Sinnvollsten an und gibt es eine elegantere Methode als 17 Transistoren auf di Platine raufzuknallen?

LG Koertis

PICture
12.04.2011, 09:39
Hallo Koertis!

Ich kann dir nur ein Hardware-Vorschlag machen: ULN200X und/oder ULN280X Treiber. :)

BMS
12.04.2011, 09:59
Hallo,
im RN-Wissen hab ich vor kurzem mit dem Artikel Siebensegmentanzeigen angefangen, dort wird auch auf die Ansteuerung eingegangen: http://www.rn-wissen.de/index.php/Siebensegmentanzeige
Es werden viele Varianten gezeigt, u.a. auch mit ULN2003, Multiplexing, Dezimalzähler, BCD-7Segment-Decoder, ...
Das könnte schon mal eine Orientierung sein.

Grüße, Bernhard

Koertis
12.04.2011, 10:28
Super Danke,
Die Lösung mit dem ULN2803 ist sehr verlockend, so spar ich mir schon mal die 8 bzw. 9 Transistoren(für die Kathoden), aber für die 8 Segmente werde ich wohl gezwungen sein, die 8 Transistoren zu nehmen(10V)...

Danke für die schnellen Antworten,

lg Koertis

PICture
12.04.2011, 10:42
Dafür eignen sich UDN2981 bis 4 Treiber. ;)

Koertis
12.04.2011, 11:55
Ja super schon bestellt :)
Softwaremäßig habe ich mir gedacht ich erstellt ein 9x10 Array in denen die Codes für 1,2,3 .. usw. für jedes Segment(9) gespeichert sind.
Somit schreibe ich immer die Zeile aus die gerade notwendig ist... Die Zeilen verändere ich mit einen Timerinterrupt.
Was sagt ihr dazu?

BMS
12.04.2011, 12:31
Hallo,
das mit dem Array sollte eigentlich gut funktionieren. Geschickt ist es auch noch, wenn du alle Segmentleitungen auf einen PORT (also z.B. alle auf PORTB) hängst. Dann kannst du mit PORTB=Array[Index] die Daten schnell ausgeben.
Hast du dir schon Gedanken gemacht, wie du die Zeit präzise hochzählen willst? (Timer, Prescaler, Interrupt) und wie hoch die Wiederholfrequenz bei der Anzeige sein soll? (wird wohl auch mit Timer und Interrupt laufen...). Damit die angezeigte Zeit auch stimmt solltest du unbedingt einen Quarz verwenden an XTAL1/2.
Aber bau erst mal deine Hardware auf ;)
Grüße, Bernhard

PICture
12.04.2011, 12:52
Wenn dein Microcontroller zwei Timer hat, ist es richtg. :)

Sonst würde ich endlose Schleife mit entsprechenden Verzögerungsschleifen für Displaysteuerung erstellen, die immer nur für Zeitaktualisierung durch Timer kurz unterbrochen wird.

Koertis
12.04.2011, 16:41
Ich habe das mit dem Array heute programmiert und es funktioniert super, ich lasse bei einem Interrupt meine Funktion aufrufen bei der die "Linie" (die ausgegeben wird) um 1 Tausenstel erhöht wird.
D.h. meine Vorgangsweise schaut jetzt so aus, ich werde mit meinen Oszi die genaue Quarzfrequenz messen, daraus errechne ich dann wie oft ich den 16bit Timer überlaufen lassen muss, damit ich 1ms bekomme und ab dann spring meine Funktion ein, diese zählt bis 99h :)

Dann fehlt nur noch der Ein/Aus Interrupt bzw. reset knopf(könnt ich auch nur den µC resetten)

Bezüglich der Widerholfrequenzen habe ich mir keinen Kopf gemacht, aber die übrige Zeit die der µC hat(bei 16MHz) kann er schreiben, ich werde es aber so machen dass ich unter 1Mhz bleibe, also so ca. 2-10 us...

Meine Funktion ganz simple:



void func(uint8_t *A) {
A[t]++;
if(A[t] == 9) {
A[t]= 0;
A[hu]++;
if(A[hu] == 9){
A[hu]=0;
A[z]++;
if(A[z] == 9){
A[z] = 0;
A[s]++;
if(A[s] ==9){
A[s] = 0;
A[ds]++;
if(A[ds] == 6){
A[ds] = 0;
A[m]++;
if(A[m] == 9){
A[m] = 0;
A[dm]++;
if(A[dm]==6){
A[dm]=0;
A[h]++;
if(A[h]==9){
A[h]=0;
A[dh]++;
if(A[dh]==9)
A[dh]=0;
}
}
}
}
}
}
}
}
}

in main wir dann mittels einer forschleife die Zeile so ausgegeben:

PORTA= LINE[i][A[8-i]];
PORTD = 0x01<< i;

BMS
12.04.2011, 17:07
Hallo,
der Code sieht ja lustig aus mit den vielen Einrückungen ;)
Aber ich glaube, irgendwas passt noch nicht ganz.
Überleg mal was passiert wenn A[t] vor dem Aufruf der Funktion 8 war. Wird dann ja um eins erhöht, wird also zu einer 9. Dann ist aber die if-Bedingung schon erfüllt, die Stelle wird auf 0 gesetzt und die nächste erhöht. Also wird die 9 komplett übergangen !?

Du musst auch noch die Ziffen in Siebensegmentcodes umwandeln. Das kannst du per Software mit einem weiteren Array machen (oder mit switch case... oder if else if...). Alternativ ein IC einbauen, dass BCD-zu-7-Segment wandelt.
Grüße, Bernhard

Koertis
12.04.2011, 17:11
Ich hatte die 9 eigentlich schon ausgetauscht gehapt zu einer 10, keine Ahnung wie das passieren konnte, aber danke für den Hinweis. In LINE stehen schon die Hexdaten für die Siebensegmentanzeigen. :)

Koertis
12.04.2011, 17:43
Ich habe jetzt alles aufgebaut und auf einem Steckbrett getestet mit 5 kleine Anzeigen. Man sieht bei den letzten 2 Anzeigen bezüglich der Verkabelung, dass diese schwach Flackern, und somit stören. Ist das auf die Frequenz zurück zu führen? Induziere ich in den anderen Leitungen schon eine Spg? Je höher ich die Frequenz mache, desto größer werden die Störungen und bei mehreren Anzeigen. -> Entstörkondensatoren?

Upps... habe gerade rausgefunden dass es ein Softwareproblem war, ich habe den Port zu wenig Zeit gegeben,
entschuldigung für die Störung :)

Koertis
12.04.2011, 19:00
Aber wenn ich in der Hauptschleife die Daten raussschreibe und die neue Zeile generiere und ein _delay_us reinbringe, sodass mein ausgang genau 1ms ist. Dann brauch ich doch keinen Timer... Was sagt ihr dazu?

Besserwessi
12.04.2011, 20:31
Wenn man 9 Stellen muliplexen will, und annähernd die volle Helligkeit braucht, dann braucht man für die Segmente etwa 150-180 mA an Strom. Bei einer 8 wäre das dann bis knapp über 1 A für eine Stelle. Selbst für die Segmente werden die Treiber ICs uln2003 oder UDN2981 knapp, denn der gesamte Strom pro IC ist auch begrenzt (ca. 180 mA bei 7 Kanälen am ULN2003, beim UDN2981 nur etwa 120 mA). Die Treiber ICs haben kein Problem mit 10 V oder 12 V. Für die Auswahl des Stellen braucht man aber mehr Strom, also einzelne Transistoren oder MOSFETs.

Wie viel Strom man wirklich braucht, hängt von der Helligkeit ab. Für einen Innenraum können auch 5-10 mA je Segment ausreichen. Dann wären sogar Schieberegister als Treiber ein mögliche Lösung.

Wenn man sehr schnell multiplext kann man leichte Störungen bekommen. Einige Segmente die eigentlich aus sein sollen können dann leicht leuchten. Mit Entstörkondensatoren kann man da nicht viel machen. Das kann man vermeiden wenn man zwischen den Stellen noch einmal die Segmente ausschaltet und erst dann die nächste Stelle aktiviert.

Koertis
12.04.2011, 20:53
Aber die Segmentanzeigen leuchten ja nicht gleichzeitig, es leuchtet immer nur ein Block der bis zu max. 20mA *8 Segmentteile = 160mA reicht. Und da alle hintereinander leuchten ist der maximale Strom bei 160mA... nicht?

BMS
12.04.2011, 21:12
Das stimmt so nicht. Bei Multiplexing sind ja nicht alle Ziffern gleichzeitig an. Du hast 9 Stellen - dann leuchtet eine Stelle auch nur etwa ein Neuntel der Zeit. Um die gleiche Helligkeit wie bei einer "normalen" Ansteuerung zu erhalten, musst du den Strom verneunfachen. Deswegen kommt Besserwessi auf 20mA*9=180mA für ein Segment (also ein Strich der Anzeige), und 180mA*8=ca.1A wenn alle 8 Segmente (Striche) leuchten sollen.
Wenn man doch den ULN... verwenden will, könnte man ja je zwei Ein- und Ausgänge (oder mehr) parallel schalten, um den Strom treiben zu können. Dann braucht man aber auch mehr ICs davon.

Besserwessi
12.04.2011, 22:18
Wenn man beim ULN2003 2 Kanäle parallel schaltet, verdoppelt sich der maximale Strom nicht, denn der Strom teilt sich nicht gleichmäßig auf, und wenn mehr Kanäle gleichzeitig aktiv sind, reduziert sich der Strom je Kanal. Mit 2 Kanälen sind es maximal knapp 800 mA - für 1 A bräuchte man schon 4-5 Kanäle.
Da nimmt man doch lieber MOSFETs oder Darlington-transistoren.

Koertis
13.04.2011, 10:54
ah ok, also nehme ich für die Busleitungen 8 Transistoren BC546B (Peak current 200mA aber Verkäufer schreiben Ic=200mA drauf) die habe ich bereits.
Für die 9 Anoden könnte ich mir solche Fets besorgen
"MOSFET CoolMOS TO-220 600 V 3.2 A"
oder:
"IRLML 2402 1,2A" und wäre auf smd, dann könnte ich platz sparen...

halten bis zu 3,5 A aus und sind im nA Bereich sowie bis zu 5,5V steuerbar....

Richard
13.04.2011, 13:08
Dafür eignen sich UDN2981 bis 4 Treiber. ;)

Wieso 4 die Dinger haben 8 Treiber....

Gruß Richard

PICture
13.04.2011, 15:23
Hallo!

Sorry für meine Faulheit. Es sollte sein UDN2981 bis UDN2984 Treiber. :D

Koertis
13.04.2011, 15:46
Ok danke, doch jetzt habe ich noch ein Interrupt frage. Ich habe INT0 aktiviert :

GICR |= (1<<INT0);
MCUCR |= (1<<ISC01);
MCUCR &= ~(1<<ISC00);

Interruptaufruf:
ISR(INT0_vect) {
stop = 1;
}

aber mein Problem ist jetzt sobald ich in di nähe des Kabels (INT0) komme schalter schon der Interrupt. Bei jeglichen kleinsten Feld schaltet der Interrupt.
Darauf hin habe ich es mit einen Pullup Widerstand probiert und mit einen Taster gegen Masse zu schalten. Doch es ändert nichts. Mit dem Oszi bekomme ich folgendes Bild:

18546

Hat jemand dazu eine Idee?

Besserwessi
13.04.2011, 16:48
Zum Interupt-problem:
Da sind wohl noch ein paar Störungen auf den Platine. Das 1 kHz Signal kommt wohl am ehesten vom Multiplexen. Zum einen sollte man die Leitungen zu Anzeige von den Eingängen trennen, z.B. Masseleitungen dazwischen, oder ggf. auch Abgeschirmte Leitungen nehmen. Dann kann man den Pullup Widerstand einfach kleiner machen (z.B. 1 KOHm) - die Anzeige braucht ja ohnehin schon relativ viel Strom, das muss man hier nicht so sparsam sein.

Zu den Treibern für die Anzeige:
Es sind da erstmal 2 Dinge zu klären:
1) Wie hell muss die Anzeige sein, d.h. wie viel Strom brauchen die LEDs wirklich bzw. wie hell ist es in der Umgebung ?
2) Sind die Anzeigen mit gemeinsmer Anode oder gemeinsamer Kathode ?

Danach richtet sich die Frage der Treiber. Wenn man wirklich die vollen 20 mA als mittleren Strom braucht, müssen die Treiber pro Segment knapp 200 mA können (z.B: ULN2003, UDN2981 schon mit einschränkungen). Die Treiber zur Auswahl der Stellen müssen dann gut 1 A können - das wären dann wohl MOSFETs (z.B. IRF7103) oder Darlingtons.

Koertis
13.04.2011, 17:28
Also die Anzeige wird auch in Freiem verwendet, das heißt ich werde die vollen 20mA pro Segment speißen. Zu den Treibern werde Mosfets verwenden, ich habe welche zu Hause(BSP318 ), diese liefern einen max. Drainstrom von 2A also kein Problem. SMD also auch kein Platz Problem.

Meine 100mm Anzeigen bekomme ich erst in laufe dieser Woche, diese besitzen eine gemeinsame Anode.

Da ich schon das Programm testen wollte habe ich kleine 1,5 cm Anzeigen genommen, diese haben eine gemeinsame Kathode.

lg Koertis

Besserwessi
13.04.2011, 18:09
Bei Anzeigen mit gemeinsamer kathode könnte man für die Segmente schon mal die ULN2003 nehmen. / Segmente sollten ja reichen, den DP wird man wohl nicht brauchen.

Der Treiber für die Stellen, müsste gegen etwa 10-12 V schalten. Das wären dann P-MOSFETs, oder PNP Darlington transistoren.

Koertis
13.04.2011, 19:57
Doch den DP brauche ich, diese trennen die Uhrsegmente hh.mm.ss.ttt
ja ich habe einen Trafo hier 2x9V damit mache ich mir 18V und baue einen Spannungsregler von 15V ein: dann folgt Mosfet + Widerstand+ Segmente...

Koertis
15.04.2011, 11:51
Ich habe jetzt die Anzegen bekommen, und Mein Mosfet schaltet mit 5V nicht die 15V. Er lässt nur 5V durch.

vieleicht verwende ich doch 2x UDN2981A

Zudem wie soll ich di Widerstände berechnen? einfach nur 15/20mA, aber 15V fallen ja nicht am Widerstand ab... und die Spg VDS gibs da auch noch...
zu dem werde ich Leistungswiderstände verwenden müssen....

Habt ihr ne Ahnung?

Richard
15.04.2011, 13:40
Ich habe jetzt die Anzegen bekommen, und Mein Mosfet schaltet mit 5V nicht die 15V. Er lässt nur 5V durch.

vieleicht verwende ich doch 2x UDN2981A

Zudem wie soll ich di Widerstände berechnen? einfach nur 15/20mA, aber 15V fallen ja nicht am Widerstand ab... und die Spg VDS gibs da auch noch...
zu dem werde ich Leistungswiderstände verwenden müssen....

Habt ihr ne Ahnung?

Ich denke Du brauchst für jedes der 8 Segmente einen eigenen Widerstand wie fiel V über so einem Segment abfallen muss im Datenblatt stehen. Rv = (15 V - U-Segment) / I Segment, und Leistungs Widerstände werden das mit Sicherheit nicht. Eher so um zwischen 560 und 1000 Ohm.

Wenn Dein Mosfed nur 5 V durchlässt bedeutet das er nur teilweise Durch schaltet und den Rest Spannung in Wärme wandelt. Das überlebt der nicht lange. Da hast Du in der Ansteuerung noch einen Fehler. :-(

Gruß Richard

Koertis
15.04.2011, 15:32
Danke,

gibt es eine Möglichkeit den Mosfet komplett durschalten zu lassen? Und sonst muss ich das hässlich machen, d.h. mit einen Transistor den Mosfet ansteuern...

aber das würde ich gerne vermeiden...

Searcher
15.04.2011, 15:54
Hallo Koertis,
poste doch Deine komplette Schaltung.


gibt es eine Möglichkeit den Mosfet komplett durschalten zu lassen?
Natürlich gibt es die, mehrere. Aber welche der Möglichkeiten für Dich paßt???

Gruß
Searcher

Besserwessi
15.04.2011, 16:20
Mit nur einem FET direkt vom µC kann man die 15 V kaum schalten. Da fehlt einfach die Pegelwandlung. Die UDN298x sind von der Leistung zu knapp wenn man die volle Helligkeit haben will - da helfen auch mehrere Kanäle nur wenig. Die Treiber für die Anoden sollten für 1,2 A (8*150 mA) geeignet sein, je nach Anzeige gff. sogar das doppelte. Die 150 mA kommen von 9 mal 20 mA bzw. dem maximalen Pulsstrom (oft 150 mA).

Mein Vorschlag wären PNP Transistoren (für ca. 3 A) und ein ULN2803 bzw. BC548 zur Ansteuerung.

Die Widerstände sollte man erstmal größer wählen so dass nur 20 mA als Strom fließt. Damit passiert bei einem Softwarefehler nichts. Wenn dann die Software stimmt, kann man noch welche parallel schalten um auf etwa 150 mA je Segment zu kommen.

P.S.
Mit den FETs BSP318 wird das so einfach nichts. Das sind N MOSFETs, da bekommt man an Source nur etwa 3 V weniger als am Gate. Als mindeste bräuchte man da erstmal eine Transistor als Pegelwandler um das Gate mit 15 V anzusteuern.

Koertis
15.04.2011, 16:32
18558

Also der Treiber ULN2803A müsste das schon aushalten, aber ja für die Anode,werde ich wohl die Mosfets mit BC547 ansteuern, die habe ich lagernd...

Aber bezüglich der Vorwiderstände wenn (15V-10V)/(20mA*9)=27Ohm.. P = 5V * 0.18= 0,9W,

das ist doch für di kleinen Widerstände zuviel nicht?

Richard
15.04.2011, 16:40
Danke,

gibt es eine Möglichkeit den Mosfet komplett durschalten zu lassen? Und sonst muss ich das hässlich machen, d.h. mit einen Transistor den Mosfet ansteuern...

aber das würde ich gerne vermeiden...

Google einmal nach logig level Fed, die Dinger kann man mit ttl Pegel betreiben. :-)

Gruß Richard

Besserwessi
15.04.2011, 17:28
Die BSP318 sind schon logic level FETs, nur halt N-Kanal. Für die Anoden braucht man High side Treiber, und dafür sind nun mal N Kanal MOSFETs nicht so gut, egal ob als Logic Level oder nicht.

Man kann das noch gerade so machen: Das Gate kriegt über Transistor und Wiederstand bis zu 15 V, notfalls auch bis 18 V. Der Ausgang kriegt dann etwa 3 V weniger als das Gate. 3 V mal 1,2 A sind schon reichlich viel Leistung für den FET, aber das ist ja nur gepulst. Ein Problem ist, das der Spannungsabfall nicht immer gleich ist, es sei denn man hat am Gate z.B. 18 V und an Drain nur 14 V. Man braucht dann aber 2 geregelte Spannungen.

Die kleinen Widerstände (mit Draht) sind bis etwa 250 mW, die üblichen SMD 0805 eher so bis 100 mW. Für 0,9 W braucht man schon etwas größere Widerstände.

Richard
15.04.2011, 18:10
Die BSP318 sind schon logic level FETs, nur halt N-Kanal. Für die Anoden braucht man High side Treiber, und dafür sind nun mal N Kanal MOSFETs nicht so gut, egal ob als Logic Level oder nicht.

Man kann das noch gerade so machen: Das Gate kriegt über Transistor und Wiederstand bis zu 15 V, notfalls auch bis 18 V. Der Ausgang kriegt dann etwa 3 V weniger als das Gate. 3 V mal 1,2 A sind schon reichlich viel Leistung für den FET, aber das ist ja nur gepulst. Ein Problem ist, das der Spannungsabfall nicht immer gleich ist, es sei denn man hat am Gate z.B. 18 V und an Drain nur 14 V. Man braucht dann aber 2 geregelte Spannungen.

Die kleinen Widerstände (mit Draht) sind bis etwa 250 mW, die üblichen SMD 0805 eher so bis 100 mW. Für 0,9 W braucht man schon etwas größere Widerstände.


@Besserwessi Ich habe keinen Schaltplan gesehen und auch kein Datenblatt. Bin einfach davon ausgegangen das die Passenden Bauteile benutzt werden.

Gruß Richard

Koertis
15.04.2011, 21:17
Warum sollte es mit n- Kanal Mosfet nicht funktionieren? verstehe
ich nicht...

Koertis
16.04.2011, 07:45
18563
Jetzt habe ich eine Version gezeichnet mit BC547 Vorschaltung.

wkrug
16.04.2011, 07:58
Auch ein paar Gedanken...
Bei 9 Stellen würde ich das Multiplexing auf 2x aufteilen.
Also einmal 5 und einmal 4 Stellen multiplexen.
Das bedeutet zwar, das man doppelt so viele low Side treiber und Port Ausgänge vom Controller braucht, die Segmente aber doppelt so oft angesteuert werden können.
Das dürfte mehr Helligkeit für den Outdoorbetrieb bringen.

Als Treibertransistoren könnte man z.B. auch BS170 einsetzen.
Da wären dann 16Stück für die Kathoden fällig und 9 zur Ansteuerung der P-Kanal Fets für die Anoden fällig. Bei dem Preis dürfte das aber keine entscheidende Rolle spielen, da die Anzeigen, das teuerste sein dürften.

Der Grund, warum man für die Anoden P-Kanal bzw. PNP Transistoren verwenden sollte ist, das man zwischen Drain und Source bei N-Kanal Typen eine positive Spannung von mindestens 5V braucht.
Man müsste also eine zusätzliche Spannungsquelle haben, die um mindestens 5V höher liegt als die verwendete Segmentspannung. Das erfordert eine Modifikation des Netzteiles.
Beim P-Kanal Typ liegt die Spannung im negativen Bereich.
Da die Source ( Emitter ) aber auf +12V liegt lässt sich das problemlos erreichen.

Das Stoppuhr Timing würd ich über Comparematch Interrupts lösen.
Dann kannst Du auch mal im Hauptprogramm was ändern ohne das Timing der Stoppuhr zu beinflussen.
Das Multiplex Timing hab ich bei meiner Uhr in einer Timer Overflow Routine erledigen lassen.

Ich häng Dir mal den Quellcode meiner DCF 77 Uhr / Stoppuhr / Downcounter Schaltung mit 6 Segmenten an:

/************************************************** ***
Project : DCF77_Stoppuhr
Version : 1.2
Date : 06.01.2009
Author : Wilhelm Krug
Company : 92345 Dietfurt, Germany
Comments:


Chip type : ATmega16L
Program type : Application
Clock frequency : 8,000000 MHz
Memory model : Small
External SRAM size : 0
Data Stack size : 256
************************************************** ***/

#include <mega16.h>
#include <delay.h>
#include <stdlib.h>
#include <string.h>

// Zeichentabelle für 7 Segment Display
//0,1,2,3,4,5,6,7,8,9,A,b,c,d,E,F,h,J,L,n,o,P,r,u,[]
volatile unsigned char uc_chartable[25]={
0b00111111,/*0*/
0b00001100,
0b01011011,
0b01011110,
0b01101100,
0b01110110,
0b01110111,
0b00011100,
0b01111111,
0b01111110,/*9*/
0b01111101,/*A*/
0b01100111,/*b*/
0b01000011,/*c*/
0b01001111,/*d*/
0b01110011,/*E*/
0b01110001,/*F*/
0b01101101,/*H*/
0b00011110,/*J*/
0b00100011,/*L*/
0b01000101,/*n*/
0b01000111,/*o*/
0b01111001,/*p*/
0b01000001,/*r*/
0b00000111,/*u*/
0b00000000};/*Leerzeichen*/


// Segmentbuffer
volatile unsigned char uc_segment[6]={0,0,0,0,0,0};
volatile unsigned char uc_segmentcounter=0;

// Displaysteuerung
volatile unsigned char uc_dispmode=0;

// Stoppuhr Zähler Steuerung
bit ub_counting=0; /* Flag Stoppuhr läuft */
bit ub_stopdelay=0; /* Flag Stopauslöseverzögerung aktiv */
bit ub_autoreset=0; /* Flag Autoreset der Stoppuhr aktiv */
volatile unsigned int ui_stopdelay; /* Wert für das Stopdelay */
volatile unsigned int ui_stopcounter=0; /* Counter für das Stopdelay */
eeprom unsigned int ee_stopdelay=600; /* EEPROM SPeicherplatz für das Stopdelay */
volatile unsigned int ui_autoresetcount=0; /* Downcounter für den Autoreset */
volatile unsigned int ui_autoreset; /* Wert für den Autoreset */
eeprom unsigned int ee_autoreset=500; /* EEPROM Speicherplatz für den Autoreset */

// Stoppuhr UP Zähler
volatile unsigned char uc_countmsek=0;
volatile unsigned char uc_countsek=0;
volatile unsigned char uc_countmin=0;
volatile unsigned char uc_countstd=0;

// Stoppuhr Down Zähler
volatile unsigned char uc_downmsek=0;
volatile unsigned char uc_downsek=0;
volatile unsigned char uc_downmin=0;
volatile unsigned char uc_downstd=0;
eeprom unsigned char ee_downmsek=0;
eeprom unsigned char ee_downsek=0;
eeprom unsigned char ee_downmin=1;
eeprom unsigned char ee_downstd=1;

// DCF Uhr
unsigned int MS_Count = 0,old_ms_count = 0,MeasureBit = 0;
volatile unsigned char cnt_okay = 0;
volatile char sek_count = 0;
volatile unsigned char TimeHours = 0, TimeMinutes = 0, TimeSeconds = 0, DateMonth = 0, DateDay = 0,Weekday = 0;
volatile unsigned int HiLoTimeAverage = 10;
volatile unsigned int TimeMilliseconds = 0;
volatile unsigned char DCF77Bit = 0,NewBit = 0;
volatile unsigned char value = 0;
volatile char startbit,parity;
volatile unsigned char dcf77_hour = 88, dcf77_minute = 88, dcf77_day = 88, dcf77_month = 88, dcf77_wday = 88, dcf77_year = 88;

// Tastaturabfrage zum Setup
volatile unsigned char uc_readtast=0;
volatile unsigned char uc_oldtast=0;

#define RXB8 1
#define TXB8 0
#define UPE 2
#define OVR 3
#define FE 4
#define UDRE 5
#define RXC 7

#define FRAMING_ERROR (1<<FE)
#define PARITY_ERROR (1<<UPE)
#define DATA_OVERRUN (1<<OVR)
#define DATA_REGISTER_EMPTY (1<<UDRE)
#define RX_COMPLETE (1<<RXC)

// USART Transmitter buffer
#define TX_BUFFER_SIZE 40
char tx_buffer[TX_BUFFER_SIZE];

#if TX_BUFFER_SIZE<256
unsigned char tx_wr_index,tx_rd_index,tx_counter;
#else
unsigned int tx_wr_index,tx_rd_index,tx_counter;
#endif

// USART Transmitter interrupt service routine
interrupt [USART_TXC] void usart_tx_isr(void)
{
if (tx_counter)
{
--tx_counter;
UDR=tx_buffer[tx_rd_index];
if (++tx_rd_index == TX_BUFFER_SIZE) tx_rd_index=0;
};
}

#ifndef _DEBUG_TERMINAL_IO_
// Write a character to the USART Transmitter buffer
#define _ALTERNATE_PUTCHAR_
#pragma used+
void putchar(char c)
{
while (tx_counter == TX_BUFFER_SIZE);
#asm("cli")
if (tx_counter || ((UCSRA & DATA_REGISTER_EMPTY)==0))
{
tx_buffer[tx_wr_index]=c;
if (++tx_wr_index == TX_BUFFER_SIZE) tx_wr_index=0;
++tx_counter;
}
else
UDR=c;
#asm("sei")
}
#pragma used-
#endif

// External Interrupt 0 service routine -> Starten der Stoppuhr
interrupt [EXT_INT0] void ext_int0_isr(void)
{
ub_counting=1;
GICR&=0b00111111;
GIFR|=0b11000000;
ui_stopcounter=0;
ub_stopdelay=1;


};

// External Interrupt 1 service routine -> Stoppen der Stopuhr
interrupt [EXT_INT1] void ext_int1_isr(void)
{
unsigned char uc_i,uc_stringlengh;
unsigned char uc_stringbuffer[4];
unsigned char uc_sendstring [40];
ub_counting=0;
ui_stopcounter=0;
GICR&=0b00111111;
// Aktivierung des Autoreset bei Resetzeiten größer 1 Sekunde
if(ui_autoreset>99)
{
ui_autoresetcount=ui_autoreset+100;
ub_autoreset=1;
};

// Nur bei Stoppuhr Betrieb übertragung per seriell zum PC
if(uc_dispmode==0)
{
itoa(uc_countstd,uc_stringbuffer);
if(uc_countstd<10)
{
strcpyf(uc_sendstring,"0");
strcat(uc_sendstring,uc_stringbuffer);
}
else
{
strcpy(uc_sendstring,uc_stringbuffer);
};
strcatf(uc_sendstring,";");
itoa(uc_countmin,uc_stringbuffer);
if(uc_countmin<10){strcatf(uc_sendstring,"0");};
strcat(uc_sendstring,uc_stringbuffer);
strcatf(uc_sendstring,";");
itoa(uc_countsek,uc_stringbuffer);
if(uc_countsek<10){strcatf(uc_sendstring,"0");};
strcat(uc_sendstring,uc_stringbuffer);
strcatf(uc_sendstring,";");
itoa(uc_countmsek,uc_stringbuffer);
if(uc_countmsek<10){strcatf(uc_sendstring,"0");};
strcat(uc_sendstring,uc_stringbuffer);
// Steuerzeichen anfügen
uc_stringlengh=strlen(uc_sendstring);
uc_sendstring[uc_stringlengh]=13; /* Carriage Return 13*/
uc_sendstring[uc_stringlengh+1]=10; /* Line Feed 10 */
uc_sendstring[uc_stringlengh+2]=0; /* Stringende anfügen */

uc_stringlengh=strlen(uc_sendstring);
for(uc_i=0;uc_i<uc_stringlengh;uc_i++)
{
putchar(uc_sendstring[uc_i]);
};
};
// Nur bei Stoppuhr Countdown betrieb übertragung per seriell zum PC
if(uc_dispmode==1)
{
itoa(uc_downstd,uc_stringbuffer);
if(uc_downstd<10)
{
strcpyf(uc_sendstring,"0");
strcat(uc_sendstring,uc_stringbuffer);
}
else
{
strcpy(uc_sendstring,uc_stringbuffer);
};
strcpy(uc_sendstring,uc_stringbuffer);
strcatf(uc_sendstring,";");
itoa(uc_downmin,uc_stringbuffer);
if(uc_downmin<10){strcatf(uc_sendstring,"0");};
strcat(uc_sendstring,uc_stringbuffer);
strcatf(uc_sendstring,";");
itoa(uc_downsek,uc_stringbuffer);
if(uc_downsek<10){strcatf(uc_sendstring,"0");};
strcat(uc_sendstring,uc_stringbuffer);
strcatf(uc_sendstring,";");
itoa(uc_downmsek,uc_stringbuffer);
if(uc_downmsek<10){strcatf(uc_sendstring,"0");};
strcat(uc_sendstring,uc_stringbuffer);
// Steuerzeichen anfügen
uc_stringlengh=strlen(uc_sendstring);
uc_sendstring[uc_stringlengh]=13; /* Carriage Return 13*/
uc_sendstring[uc_stringlengh+1]=10; /* Line Feed 10 */
uc_sendstring[uc_stringlengh+2]=0; /* Stringende anfügen */

uc_stringlengh=strlen(uc_sendstring);
for(uc_i=0;uc_i<uc_stringlengh;uc_i++)
{
putchar(uc_sendstring[uc_i]);
};
};


}

// External Interrupt 2 service routine
interrupt [EXT_INT2] void ext_int2_isr(void)
{
if((MCUCSR&0b01000000)>0)
{

MCUCSR&=0b10111111;
MeasureBit=MS_Count-old_ms_count; // Pausenzeitmessung
old_ms_count=MS_Count;
if(MeasureBit > 160) // Rücksetzen der Sekunden auf 0
{
TimeMilliseconds=0;
sek_count = 0;
};

}
else
{
MCUCSR|=0b01000000;

MeasureBit = MS_Count - old_ms_count; // Impulszeitmessung
if(MeasureBit < 50)
{
if(MeasureBit > HiLoTimeAverage + 3) DCF77Bit = 1;
else DCF77Bit = 0;
}
}
}



// Timer 0 overflow interrupt service routine
interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{
#asm("SEI"); /* Interrupts für Stoppuhr freigeben !*/
switch (uc_segmentcounter)
{
case 0:
PORTC=0;
PORTA.5=0;
delay_us(20);
PORTA.0=1;
PORTC=uc_segment[uc_segmentcounter];
if(PINB.2>0)
{
PORTC.7=1;
};
break;
case 1:
PORTC=0;
PORTA.0=0;
delay_us(20);
PORTA.1=1;
PORTC=uc_segment[uc_segmentcounter];
break;
case 2:
PORTC=0;
PORTA.1=0;
delay_us(20);
PORTA.2=1;
PORTC=uc_segment[uc_segmentcounter];
break;
case 3:
PORTC=0;
PORTA.2=0;
delay_us(20);
PORTA.3=1;
PORTC=uc_segment[uc_segmentcounter];
break;
case 4:
PORTC=0;
PORTA.3=0;
delay_us(20);
PORTA.4=1;
PORTC=uc_segment[uc_segmentcounter];
break;
case 5:
PORTC=0;
PORTA.4=0;
delay_us(20);
PORTA.5=1;
PORTC=uc_segment[uc_segmentcounter];
break;

};
uc_segmentcounter++;
if(uc_segmentcounter>5){uc_segmentcounter=0;};
}

// Timer 1 output compare A interrupt service routine
interrupt [TIM1_COMPA] void timer1_compa_isr(void)
{
// Stopdelay aktiviert ?
if(ub_stopdelay>0)
{
ui_stopcounter++;
};
// Autoreset aktiv ?
if(ub_autoreset>0&&ui_autoresetcount>0)
{
ui_autoresetcount--;
if((ui_autoresetcount+1)==0){ui_autoresetcount=0;} ;
};
// Stoppuhr
if(ub_counting>0)
{
uc_countmsek++;
if (uc_countmsek>99)
{
uc_countmsek=0;
uc_countsek++;
if (uc_countsek>59)
{
uc_countsek=0;
uc_countmin++;
if(uc_countmin>59)
{
uc_countmin=0;
uc_countstd++;
if(uc_countstd>99)
{
uc_countstd=0;
};
};
};
};
// Countdown
if(uc_downmsek==0&&uc_downsek==0&&uc_downmin==0&&uc_downstd==0)
{
// Aktivierung des Autoreset
if(ui_autoreset>99&&ub_autoreset==0&&uc_dispmode==1)
{
ui_autoresetcount=ui_autoreset+100;
ub_autoreset=1;
};
}
else
{
uc_downmsek--;
if(uc_downmsek+1==0)
{
uc_downmsek=99;
uc_downsek--;
if(uc_downsek+1==0)
{
uc_downsek=59;
uc_downmin--;
if(uc_downmin+1==0)
{
uc_downmin=59;
uc_downstd--;
};
};
};

};
};

// static char startbit,parity;
MS_Count++;
TimeMilliseconds++;
if(TimeMilliseconds > 99) // one second
{
NewBit = 1;
TimeMilliseconds = 0;
if(++TimeSeconds >59)
{
TimeSeconds = 0;
if(++TimeMinutes > 59)
{
TimeMinutes = 0;
if(++TimeHours > 23)
{
TimeHours = 0;
}
}
}
switch (sek_count++)
{
case 20: /* startbit */
startbit = DCF77Bit;
if(startbit) cnt_okay++;
break;
/* decode numbers */
case 21: /* minute */
case 29: /* hour */
case 36: /* day */
parity = value = DCF77Bit;
break;
case 22: /* minute */
case 30: /* hour */
case 37: /* day */
case 43: /* weekday */
case 46: /* month */
case 51: /* year */
parity ^= DCF77Bit;
value += (DCF77Bit << 1);
break;
case 23: /* minute */
case 31: /* hour */
case 38: /* day */
case 44: /* weekday */
case 47: /* month */
case 52: /* year */
parity ^= DCF77Bit;
value += (DCF77Bit<<2);
break;
case 24: /* minute */
case 32: /* hour */
case 39: /* day */
case 48: /* month */
case 53: /* year */
parity ^= DCF77Bit;
value += (DCF77Bit << 3);
break;
case 25: /* minute */
case 33: /* hour */
case 40: /* day */
case 49: /* month */
case 54: /* year */
parity ^= DCF77Bit;
if(DCF77Bit == 1) value += 10;
break;
case 26: /* minute */
case 34: /* hour */
case 41: /* day */
case 55: /* year */
parity ^= DCF77Bit;
if(DCF77Bit == 1) value += 20;
break;
case 27: /* minute */
case 56: /* year */
parity ^= DCF77Bit;
if(DCF77Bit) value += 40;
break;
case 57: /* year */
parity ^= DCF77Bit;
if(DCF77Bit) value += 80;
break;
case 28: /* minute */
if(DCF77Bit == parity)
{
dcf77_minute = value;
cnt_okay++;
}
else dcf77_minute = 99;
break;
case 35: /* hour */
if(DCF77Bit == parity)
{
dcf77_hour = value;
cnt_okay++;
}
else dcf77_hour = 99;
break;
case 42: /* weekday */
dcf77_day = value;
parity ^= DCF77Bit;
value = DCF77Bit;
break;
case 45: /* month */
dcf77_wday = value;
parity ^= DCF77Bit;
value = DCF77Bit;
break;
case 50: /* year */
dcf77_month = value;
parity ^= DCF77Bit;
value = DCF77Bit;
break;
case 59:
sek_count = 0;
break;
case 58: /* Take all values */
dcf77_year = value;
if(DCF77Bit == parity) cnt_okay++;
startbit = 0;
break;
case 1: /* Take all values */

if(cnt_okay == 4)
{
TimeHours = dcf77_hour;
TimeMinutes = dcf77_minute;
DateMonth = dcf77_month;
DateDay = dcf77_day;
TimeSeconds = 1;
}
cnt_okay = 0;
break;

}
}


};

void show_display(void)
{
unsigned char uc_buffer,uc_buffer1;
switch (uc_dispmode)
{
case 0:
if(uc_countstd>0)
{
uc_segment[0]=uc_chartable[(uc_countsek%10)];
uc_segment[1]=uc_chartable[(uc_countsek/10)];
uc_segment[2]=uc_chartable[(uc_countmin%10)];
uc_segment[3]=uc_chartable[(uc_countmin/10)];
uc_buffer=uc_chartable[(uc_countstd%10)];
uc_buffer|=0b10000000;
uc_segment[4]=uc_buffer;
uc_segment[5]=uc_chartable[(uc_countstd/10)];
}
else
{
uc_segment[0]=uc_chartable[(uc_countmsek%10)];
uc_segment[1]=uc_chartable[(uc_countmsek/10)];
uc_segment[2]=uc_chartable[(uc_countsek%10)];
uc_segment[3]=uc_chartable[(uc_countsek/10)];
uc_segment[4]=uc_chartable[(uc_countmin%10)];
uc_segment[5]=uc_chartable[(uc_countmin/10)];
};
break;

case 1:
if(uc_downstd>0)
{
uc_segment[0]=uc_chartable[(uc_downsek%10)];
uc_segment[1]=uc_chartable[(uc_downsek/10)];
uc_segment[2]=uc_chartable[(uc_downmin%10)];
uc_segment[3]=uc_chartable[(uc_downmin/10)];
uc_buffer=uc_chartable[(uc_downstd%10)];
uc_buffer|=0b10000000;
uc_segment[4]=uc_buffer;
uc_segment[5]=uc_chartable[(uc_downstd/10)];
}
else
{
uc_segment[0]=uc_chartable[(uc_downmsek%10)];
uc_segment[1]=uc_chartable[(uc_downmsek/10)];
uc_segment[2]=uc_chartable[(uc_downsek%10)];
uc_segment[3]=uc_chartable[(uc_downsek/10)];
uc_segment[4]=uc_chartable[(uc_downmin%10)];
uc_segment[5]=uc_chartable[(uc_downmin/10)];

};
break;

// DCF 77 Funktion
case 2:
uc_segment[0]=uc_chartable[(TimeSeconds%10)];
uc_segment[1]=uc_chartable[(TimeSeconds/10)];
uc_segment[2]=uc_chartable[(TimeMinutes%10)];
uc_segment[3]=uc_chartable[(TimeMinutes/10)];
uc_buffer=uc_chartable[(TimeHours%10)];
uc_buffer|=0b10000000;
uc_segment[4]=uc_buffer;
uc_segment[5]=uc_chartable[(TimeHours/10)];

break;

// DCF 77 Countdown
case 3:
uc_buffer1=59-TimeSeconds;
uc_segment[0]=uc_chartable[(uc_buffer1%10)];
uc_segment[1]=uc_chartable[(uc_buffer1/10)];
uc_buffer1=59-TimeMinutes;
uc_segment[2]=uc_chartable[(uc_buffer1%10)];
uc_segment[3]=uc_chartable[(uc_buffer1/10)];
uc_buffer1=23-TimeHours;
uc_buffer=uc_chartable[(uc_buffer1%10)];
uc_buffer|=0b10000000;
uc_segment[4]=uc_buffer;
uc_segment[5]=uc_chartable[(uc_buffer1/10)];
};


};

unsigned char readtast(void)
{
unsigned char uc_buffer[2];
uc_buffer[0]=PIND&0b00011100; /* Port B einlesen und Tasten separieren */
delay_ms(10); /* 10ms warten */
uc_buffer[1]=PIND&0b00011100; /* Port B nochmals einlesen und Tasten separieren */
if(uc_buffer[0]==uc_buffer[1]) /* Sind beide Tasten gleich -> Werte zurückgeben */
{
return(uc_buffer[0]);
}
else
{
return (0); /* Verschieden = Fehler = 0 zurückgeben */
}
}


void setup(void)
{
unsigned int ui_buffer=0;

#asm("SEI")
uc_readtast=readtast();
uc_oldtast=uc_readtast;
while(uc_readtast==uc_oldtast)
{
uc_readtast=readtast(); // Warten auf loslassen der Taste
uc_segment[0]=uc_chartable[24];
uc_segment[1]=uc_chartable[24];
uc_segment[2]=uc_chartable[21]; /*P*/
uc_segment[3]=uc_chartable[24]; /* Leerzeichen */
uc_segment[4]=uc_chartable[14]; /*E*/
uc_segment[5]=uc_chartable[5]; /*S*/
};

delay_ms(100);

// Einstellung delay der Stoppfunktion
while(uc_readtast!=0b00001100)
{
uc_readtast=readtast();
if(uc_readtast!=uc_oldtast)
{
if (uc_readtast==0b00011000)
{
ui_stopdelay=ui_stopdelay+10;
if(ui_stopdelay>990){ui_stopdelay=0;};
}
if (uc_readtast==0b00010100)
{
ui_stopdelay=ui_stopdelay-10;
if(ui_stopdelay>990){ui_stopdelay=990;};
}
uc_oldtast=uc_readtast;
}
ui_buffer=ui_stopdelay/10;
uc_segment[0]=uc_chartable[(ui_buffer%10)];
uc_segment[1]=uc_chartable[(ui_buffer/10)];
uc_segment[1]|=0b10000000;
uc_segment[2]=uc_chartable[18]; /*L*/
uc_segment[3]=uc_chartable[14]; /*E*/
uc_segment[4]=uc_chartable[13]; /*d*/
uc_segment[5]=uc_chartable[5]; /*S*/
};
if (ui_stopdelay!=ee_stopdelay){ee_stopdelay=ui_stopd elay;};


while(uc_readtast==uc_oldtast)
{
uc_readtast=readtast(); // Warten auf loslassen der Taste
};

delay_ms(100);

// Einstellen des Autoreset wertes
while(uc_readtast!=0b00001100)
{
uc_readtast=readtast();
if(uc_readtast!=uc_oldtast)
{
if (uc_readtast==0b00011000)
{
ui_autoreset=ui_autoreset+100;
if(ui_autoreset>2000){ui_autoreset=2000;};
}
if (uc_readtast==0b00010100)
{
ui_autoreset=ui_autoreset-100;
if(ui_autoreset>32767){ui_autoreset=0;};
}
uc_oldtast=uc_readtast;
}
ui_buffer=ui_autoreset/100;
uc_segment[0]=uc_chartable[(ui_buffer%10)];
uc_segment[1]=uc_chartable[(ui_buffer/10)];
uc_segment[2]=uc_chartable[5]; /*S*/
uc_segment[3]=uc_chartable[14]; /*E*/
uc_segment[4]=uc_chartable[22]; /*r*/
uc_segment[5]=uc_chartable[10]; /*A*/
};
ui_autoreset=ui_buffer*100;
if (ui_autoreset!=ee_autoreset){ee_autoreset=ui_autor eset;};

while(uc_readtast==uc_oldtast)
{
uc_readtast=readtast(); // Warten auf loslassen der Taste
};

delay_ms(100);

// Einstellung Stunden Down Counter
while(uc_readtast!=0b00001100)
{
uc_readtast=readtast();
if(uc_readtast!=uc_oldtast)
{
if (uc_readtast==0b00011000)
{
uc_downstd++;
if(uc_downstd>99){uc_downstd=0;};
}
if (uc_readtast==0b00010100)
{
uc_downstd--;
if(uc_downstd+1==0){uc_downstd=99;};
}
uc_oldtast=uc_readtast;
}
uc_segment[0]=uc_chartable[(uc_downstd%10)];
uc_segment[1]=uc_chartable[(uc_downstd/10)];
uc_segment[2]=uc_chartable[22]; /*r*/
uc_segment[3]=uc_chartable[16]; /*H*/
uc_segment[4]=uc_chartable[24];
uc_segment[5]=uc_chartable[13]; /*d*/
};
if (uc_downstd!=ee_downstd){ee_downstd=uc_downstd;};
while(uc_readtast==uc_oldtast)
{
uc_readtast=readtast(); // Warten auf loslassen der Taste
};

delay_ms(100);
// Einstellung Minuten Down Counter
while(uc_readtast!=0b00001100)
{
uc_readtast=readtast();
if(uc_readtast!=uc_oldtast)
{
if (uc_readtast==0b00011000)
{
uc_downmin++;
if(uc_downmin>59){uc_downmin=0;};
}
if (uc_readtast==0b00010100)
{
uc_downmin--;
if(uc_downmin+1==0){uc_downstd=59;};
}
uc_oldtast=uc_readtast;
}
uc_segment[0]=uc_chartable[(uc_downmin%10)];
uc_segment[1]=uc_chartable[(uc_downmin/10)];
uc_segment[2]=uc_chartable[19]; /*n*/
uc_segment[3]=uc_chartable[19]; /*n*/
uc_segment[4]=uc_chartable[24]; /* */
uc_segment[5]=uc_chartable[13]; /*d*/
};
if (uc_downmin!=ee_downmin){ee_downmin=uc_downmin;};

while(uc_readtast==uc_oldtast)
{
uc_readtast=readtast(); // Warten auf loslassen der Taste
};

delay_ms(100);
// Einstellung Sekunden Down Counter
while(uc_readtast!=0b00001100)
{
uc_readtast=readtast();
if(uc_readtast!=uc_oldtast)
{
if (uc_readtast==0b00011000)
{
uc_downsek++;
if(uc_downsek>59){uc_downsek=0;};
}
if (uc_readtast==0b00010100)
{
uc_downsek--;
if(uc_downsek+1==0){uc_downsek=59;};
}
uc_oldtast=uc_readtast;
}
uc_segment[0]=uc_chartable[(uc_downsek%10)];
uc_segment[1]=uc_chartable[(uc_downsek/10)];
uc_segment[2]=uc_chartable[14]; /*E*/
uc_segment[3]=uc_chartable[5]; /*S*/
uc_segment[4]=uc_chartable[24];
uc_segment[5]=uc_chartable[13]; /*d*/
};
if (uc_downsek!=ee_downsek)
{
ee_downsek=uc_downsek;
ee_downmsek=uc_downmsek;
};
#asm("CLI")
GIFR|=0b11100000; /*Eventuell anstehende Interrupt Flags löschen */
}


void main(void)
{
// Declare your local variables here

// Input/Output Ports initialization
// Port A initialization
// Func7=In Func6=In Func5=Out Func4=Out Func3=Out Func2=Out Func1=Out Func0=Out
// State7=T State6=T State5=0 State4=0 State3=0 State2=0 State1=0 State0=0
PORTA=0x00;
DDRA=0x3F;

// Port B initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTB=0x00;
DDRB=0x00;

// Port C initialization
// Func7=Out Func6=Out Func5=Out Func4=Out Func3=Out Func2=Out Func1=Out Func0=Out
// State7=0 State6=0 State5=0 State4=0 State3=0 State2=0 State1=0 State0=0
PORTC=0x00;
DDRC=0xFF;

// Port D initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTD=0x00;
DDRD=0x00;

// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: 125,000 kHz
// Mode: Normal top=FFh
// OC0 output: Disconnected
TCCR0=0x03;
TCNT0=0x00;
OCR0=0x00;

// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 1000,000 kHz
// Mode: Normal top=FFFFh
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer 1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: On
// Compare B Match Interrupt: Off
// CTC Mode: On
TCCR1A=0x00;
TCCR1B=0x0A;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x27;
OCR1AL=0x0F;
OCR1BH=0x00;
OCR1BL=0x00;

// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer 2 Stopped
// Mode: Normal top=FFh
// OC2 output: Disconnected
ASSR=0x00;
TCCR2=0x00;
TCNT2=0x00;
OCR2=0x00;

// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x11;

ui_autoreset=ee_autoreset;
ui_stopdelay=ee_stopdelay;
uc_downmsek=ee_downmsek;
uc_downsek=ee_downsek;
uc_downmin=ee_downmin;
uc_downstd=ee_downstd;


if(PIND.4==0)
{
setup();
};

// External Interrupt(s) initialization
// INT0: On
// INT0 Mode: Falling Edge
// INT1: Off
// INT1 Mode: Falling Edge
// INT2: On
// INT2 Mode: Rising Edge
GICR|=0xE0;
MCUCR=0x02;
MCUCSR=0x40;
GIFR=0xE0;



// USART initialization
// Communication Parameters: 8 Data, 1 Stop, No Parity
// USART Receiver: Off
// USART Transmitter: On
// USART Mode: Asynchronous
// USART Baud Rate: 38400
UCSRA=0x00;
UCSRB=0x48;
UCSRC=0x86;
UBRRH=0x00;
UBRRL=0x0C;

// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
SFIOR=0x00;

//****** Testroutines
/*uc_dispmode=0;*/

//****** End of Testroutines




// Watchdog Timer initialization
// Watchdog Timer Prescaler: OSC/1024k
#pragma optsize-
WDTCR=0x1E;
WDTCR=0x0E;
#ifdef _OPTIMIZE_SIZE_
#pragma optsize+
#endif

// Global enable interrupts
#asm("sei")

while (1)
{
#asm("wdr");
if (ui_stopcounter>=ui_stopdelay)
{
GIFR|=0b11000000; /* Anstehende Interrupts löschen */
GICR|=0b10000000; /* INT1 = Stopp die Stoppuhr freigeben */
ub_stopdelay=0; /* Stopcounter Flag zurücksetzen */
ui_stopcounter=0; /* Stop Delay Zähler wieder auf 0 zurücksetzen */
};
if(PINB.0>0)
{
uc_dispmode=0; /* Pin B.0 mit Pullup =1 -> Stoppuhrbetrieb */
}
else
{
uc_dispmode=2; /* Pin B.0 mit Schalter auf GND -> DCF 77 Betrieb */
};
if(PINB.1>0)
{
uc_dispmode++; /* Pin B.1 mit Pullup =1 -> Reversebetrieb der Uhren */
};

// Pin D.4 = 0 -> Reset der Stoppuhren
if(PIND.4>0)
{
// Tue nichs
}
else
{
ub_autoreset=0;
ui_autoresetcount=0;
ub_stopdelay=0;
ui_stopcounter=0;
ub_counting=0;
uc_countmsek=0;
uc_countsek=0;
uc_countmin=0;
uc_countstd=0;
uc_downmsek=ee_downmsek;
uc_downsek=ee_downsek;
uc_downmin=ee_downmin;
uc_downstd=ee_downstd;
GIFR|=0b11000000;
GICR&=0b00111111;
GICR|=0b01000000;
};
// Automatischer Reset der Stoppuhr
if(ui_autoresetcount<100&&ub_autoreset>0)
{
ub_autoreset=0;
ui_autoresetcount=0;
ub_stopdelay=0;
ui_stopcounter=0;
ub_counting=0;
uc_countmsek=0;
uc_countsek=0;
uc_countmin=0;
uc_countstd=0;
uc_downmsek=ee_downmsek;
uc_downsek=ee_downsek;
uc_downmin=ee_downmin;
uc_downstd=ee_downstd;
GIFR|=0b11000000;
GICR&=0b00111111;
GICR|=0b01000000;
};

show_display();


};
}
Eventuell kannst Du ja Teile davon gebrauchen...
Schaltplan hab ich auch, aber ich hab hier auch ULN2803 und für die Anoden P-FET's verwendet.
Übrigens hab ich für meine Schaltung ein einstellbares stabilisiertes Steckernetzteil verwendet.
Damit kann man dan die Helligkeit der Segmente relativ einfach einstellbar machen.

Koertis
16.04.2011, 08:27
Mit einem Overflow schreibst du zwar zur gleichen Zeit das gleiche raus, aber warum sollten die Segmente doppelt so schnell angesteuert werden? Geschrieben wird ja ach nach der Reihe. Dann ist es ja wieder das selbe....

Die Idee mit dem Steckernetzteil ist klasse... Ok dann lege ich mir P-FET's zu, das ist das einfachste, aber ich weiß nicht ob ich die Segmente aufteilen werde...

Mein code:


#include <avr/io.h>
#define F_CPU 16000000
#include <util/delay.h>
#include <avr/interrupt.h>

#define ZERO 0x3f
#define ONE 0x06
#define TWO 0x5b
#define THREE 0x4f
#define FOUR 0x66
#define FIFE 0x6d
#define SIX 0x7d
#define SEVEN 0x07
#define EIGHT 0x7f
#define NINE 0x6f


#define t 0
#define hu 1
#define z 2
#define s 3
#define ds 4
#define m 5
#define dm 6
#define h 7
#define dh 8

#define defSek 16000
#define waits 61

volatile uint8_t stop = 0;
volatile uint8_t pause = 0;
volatile uint8_t Freigabe = 0;
volatile uint8_t block = 0;

ISR(INT0_vect) {
if(Freigabe && (stop > waits)){
Freigabe = 0;
block = 0;
}
else if(!Freigabe && (stop >waits)){
block = 0;
Freigabe = 1;
}
}

ISR(TIMER1_COMPA_vect){
pause=0;
}

ISR(TIMER2_OVF_vect){
stop++;
if(stop == 254)
stop = 254;
}



void func(uint8_t *A) {
A[t]++;
if(A[t] == 10) {
A[t]= 0;
A[hu]++;
if(A[hu] == 10){
A[hu]=0;
A[z]++;
if(A[z] == 10){
A[z] = 0;
A[s]++;
if(A[s] ==10){
A[s] = 0;
A[ds]++;
if(A[ds] == 6){
A[ds] = 0;
A[m]++;
if(A[m] == 10){
A[m] = 0;
A[dm]++;
if(A[dm]==6){
A[dm]=0;
A[h]++;
if(A[h]==10){
A[h]=0;
A[dh]++;
if(A[dh]==10)
A[dh]=0;
}
}
}
}
}
}
}
}
}


int main(void)
{

// set Ports
DDRD |= ((1<<PD3)|(1<<PD4)|(1<<PD5)|(1<<PD6)|(1<<PD7));
PORTD &= ~((1<<PD3)|(1<<PD4)|(1<<PD5)|(1<<PD6)|(1<<PD7));
DDRA = 0xff;
PORTA = 0xff;
// set Interrupt 0
GICR |= (1<<INT0);
MCUCR |= (1<<ISC01);

// set TImMER 1 to CTC Mode
TCCR1B |= (1<<CS10)|(1<<WGM12);
TIMSK |= (1<<OCIE1A);

// Set Timer 2 OVF INterrupt
TCCR2 |= (1<<CS22)|(1<<CS21)|(1<<CS20);
TIMSK |= (1<<TOIE2);

uint8_t LINE [9][10];
uint8_t A[9] = {0,0,0,0,0,0,0,0,0};
uint8_t COUNT[12] = {ZERO ,ONE, TWO , THREE, FOUR, FIFE, SIX, SEVEN, EIGHT, NINE};
uint8_t i = 0;
uint8_t j = 0;
OCR1A = defSek;

for (i = 0; i<10; i++)
for(j = 0; j<11; j++){
if(i == 1)
LINE[i][j] = COUNT[j]|0x80;
else
LINE[i][j] = COUNT[j];

}

sei();

while(1){


while(pause){

for(i = 0; i< 5; i++){
PORTD = 0x08<<i;
_delay_us(1);

PORTA = LINE[i][A[4-i]];
_delay_us(194);
PORTD = 0x08<<i;
_delay_us(1);
}

}
if(Freigabe)
func(A);
if(!block){
TCNT2 = 0;
stop = 0;
block = 1;
}
pause = 1;
}


cli();
return 0;
}

Koertis
16.04.2011, 09:44
Aber muss ich die p-Mosfets dann normal anschließen D-S oder "verkehrt" S-D??

Besserwessi
16.04.2011, 12:04
Die P MOSFETs werden schon richtig herum angeschlossen: Source nach 12 V (oder ggf. etwas mehr) und Drain an die Anoden.

Die Aufteilung in 2 Hälften macht schon Sinn: bei 9 Stellen im Mulitplexing kommt man schon in den Bereich wo der maximale Pulsstrom der Anzeigen die Grenze ist. Es gibt z.B. welche mit DC bis 25 mA, aber gepulst nur bis 150 mA. Der maximale Pulsstrom ist dann oft auch nur zulässig wenn die Umschaltung etwas schneller ist als man es für die Anzeige eigentlich bräuchte.

Durch das Aufteilen der Anzeige verschiebt sich die Zahl der Treiber, statt 7 mal Low Side (ulN2003 oder BS170) und 9 mal High Side (z.B. ULN2003+P-FET) sind es dann 14 mal low side und 5 mal High Side (auch nur gut 1 A). Von den Treibern hält sich der Aufwand etwa die Waage.

Koertis
16.04.2011, 12:23
Aus Zeitgründen muss ich heute di Platine ätzen, d.h. ich werde die Segmente aufteilen in 2, und die Anoden steuer ich mit 3k3R BC547 + N-Mosfet an.

Vielen Dank,
lg Koertis

wkrug
16.04.2011, 12:32
Die Aufteilung in 2 Hälften macht schon Sinn: bei 9 Stellen im Mulitplexing kommt man schon in den Bereich wo der maximale Pulsstrom der Anzeigen die Grenze ist.
Mit anderen Worten. Beim Multiplexen von 9 Segmenten ist ein Segment nur für 1/9 der Zeit an. Dadurch müssten die Semente mit sehr hohem Strom beaufschlagt werden, vermutlich mehr als so ein Segment vertragen kann.
Bei 5 Segmenten wird das Verhältnis etwas besser, da ein Segment ja nun 1/5 der Zeit angesteuert wird und somit der Segmentstrom niedriger gewählt werden kann.

Koertis
16.04.2011, 13:01
aber es ist doch immer noch 1/9... da derµC ja immer nur eins nach dem anderen machen kann...

Besserwessi
16.04.2011, 14:20
Wenn man die Anzeigen aufteilt in 2 Gruppen, dann leichten immer 2 Anzeigen gleichzeitig. Das ist dann so als hätte man 5 Stellen mit je 14 Segmenten.

Sinnvolle Optionen für die Steuerung der Anoden sind eigentlich nur PNP Transistoren (wie BD244) oder P-MOSFETs und dann in beiden Fällen so etwas wie ein Gatter des ULN2003 oder BS170 dazu. Wenn man da unbedingt N-MOSFETs nehmen will, wird es kompliziert, man braucht 2 Spannungen (18-20 V + ca. 14 V) und trotzdem noch den ULN2003 oder BS170 dazu.

Koertis
16.04.2011, 14:29
Aber der BD244 hat ja einen Basisstrom von 2A...

aber dann wirds doch der:
IRF 5305

Besserwessi
16.04.2011, 15:12
Der maximale Basisstrom ist 2 A beim BD244 - für einen Strom vom 1,5 A reicht ein Basisstrom von etwa 50-100 mA. Das ist auch schon relativ viel, geht aber noch. Da sollte es auch bessere Transistor Typen geben, die mit etwa dem halben Strom auskommen.

Der Mosfet IRF 5305 geht auch, ist aber relativ groß.

Koertis
16.04.2011, 17:13
Habe den IRF5305 jetzt ausprobiert, der schaltet schon ohne Gate durch, und ich kann ihn nicht stuern :confused:

Richard
16.04.2011, 18:56
Habe den IRF5305 jetzt ausprobiert, der schaltet schon ohne Gate durch, und ich kann ihn nicht stuern :confused:

Du solltest einmal etwas Grundlagen lernen. Fed's sind derartig Hochohmig das sie am Gate einen definierten Pegel brauchen. Das gilt fürs Leiten aber auch fürs Sperren, ein offenes Gate kann für beliebige Zustände sorgen. :-(

Gruß Richard

Koertis
16.04.2011, 19:08
Aber wenn ich auch den Pegel zu +5V bzw. GND schalte... schaltet er nicht.... Hätte ich eigentlich mal gelernt ;)

Also High side Treiber???(BTS432 oder ICL7667)

Ok ich glaube wenn ich den ICL nehme dann wird es endlich passen

Besserwessi
16.04.2011, 20:51
Wenn der FET die +15 V schalten sollte, dann muss man die Gate spannung zwischen etwa +15 V (für aus) und z.B. + 5 V (für ein) schalten. Entsprechend braicht man ja noch wenigstens einen kleinen Transistor und 2 Widerstände dazu. Ein extra high side Treiber ist nicht unbedingt nötig, denn so schnell oder oft muss nicht geschaltet werden.

Koertis
16.04.2011, 20:59
Aber es ist das Platzsparenste :)

Und die Platine wird erheblich einfacher, aber wenn ich diesen High side Treiber bei jedem p-fet vorschalte dann müsste es klappen?

Aber ob ich meinen FET an 15V schalte oder höher, da tut sich nix...

Besserwessi
16.04.2011, 22:12
Die Lösung mit high side Treibern wie ICL7660 geht, ist aber relativ teuer. Für 5 Kanäle bräuchte man schließlich 3 Stück von den ICL7660.

Ein Diskreter Aufbau (z.B. BS170 + 1 Widerstand oder BC548 + 2 Widerstände oder 1/7 ULN2003 + 1 Widerstand) ist zumindest mit SMD Widerstand auch nicht größer, aber günstiger, braucht halt nur etwa 5-10 mA extra.

Die 2 übrigen Kanäle des ULN2003 könnte man ggf. noch für den Dezimalpunkt nutzen. Die Dezimalpunkte könnte man vermutlich aber auch fest verschalten.

wkrug
17.04.2011, 08:44
aber es ist doch immer noch 1/9... da derµC ja immer nur eins nach dem anderen machen kann...Es müssen natürlich immer je ein Segment aus der einen Gruppe und ein Segment aus der anderen Gruppe gleichzeitig bearbeitet werden.
Also Beispielsweise Stunden Zehner und Sekunden Zehner werden zur gleichen Zeit auf Stunden Einer Sekunden Einer umgeswitched.

An eine Platine würde ich erst dann denken, wenn das Konzept fertig ist.
Ich denk mal es wird eine doppelseitige Ausführung nötig werden und so etwas geht als Einzelanfertigung ziemlich ins Geld.

Ich lad Dir mal das Schaltbild von meiner Uhr hoch.
Du bräuchtest da nur die Schaltung um den ULN2803 ( ULN2003 hat nur 7 Ausgänge! ) doppelt.
Die Widerstände am ULN2808 sind bei mir doppelt, weil ich für die Sekunden kleinere 7 Segmentanzeigen verwendet habe, die eine geringere Schwellenspannung hatten.
In die Umschaltung der High Side Fet's musste ich Softwaremässig eine kleine Delayzeit einbauen, weil es sonst zu "Geisteranzeigen" kam.
Die Highside P-Fets sind überdimensioniert, hatte halt gerade diese Typen da.

Koertis
21.04.2011, 19:11
Ich habe jetzt die HIgh side Treiber ausprobiert und ich kann nur 0.5 V schalten.... sind diese MOSFETs zu groß????

Besserwessi
21.04.2011, 20:12
Oben genannten MOSFET sind reichlich groß, aber das beeinflußt nur die Zeit die man zum Umschalten braucht, und ggf. den Preis und die mechanische Größe. Wie sieht denn die Schaltung aus ?

Koertis
22.04.2011, 12:28
Ich schalte mit dem µC den High Side Treiber und dieser schaltet mein Mosfet, aber der Strom wird trotzem abgeschnürt. Jedoch sehe ich am Oszi nur 0.5V unterschied....

Koertis
22.04.2011, 17:15
Nur noch eine Frage zum 2x Multiplexn: also schalt ich immer 2 Anoden gleichzeitig ein(aufgeteilt in 4 und 5) um somit eine hellere Anzeige zu bekommen. Habe ich das richtig verstanden?

Besserwessi
22.04.2011, 21:04
Richtig verstanden, es sind dann normal 2 Stellen gleichzeitig an. Dadurch kann es heller werden, und man spart einige der relativ aufwendigen high side Treiber. Man braucht davon dann auch nur noch 5 statt 9.

Bei der der nicht funktionierenden Schaltung können wir nur helfen mit einer brauchbaren Erklärung, d.h. die Beschreibung müsste so gut sein das man es nachbauen kann, oder halt ein Schaltplan.

Koertis
23.04.2011, 19:30
Habt ihr vileicht eine Idee, warum bei meinen µC am Ausgang PC3-PC4-PC5 nur ca. 1,5 V anliegen um den ULN zu schalten? ich habe alle PORTS auf Ausgag und HIGH geschlten, aber bei diesen 3 PORTS kommt nicht mehr als 1,5 V. Ich hab auch schon µC gewechselt, und es ist das selbe... seltsam oder? Leitende verbindungn habe ich auch kontrolliert... nichts....

Besserwessi
23.04.2011, 20:18
Vermutlich ist das ein MEga32 oder Mega16 : PC3 PC4 PC5 sind da die Pins für das JTAG Interface. Da muss man erst JTAG deaktieren.

Koertis
23.04.2011, 22:39
Genau das wars, vielen vielen Dank.

lg Koertis

Koertis
24.04.2011, 10:37
Noch eine Frage zur Anzeige, ich habe immer noch das Problem das alle Segmente die nicht leuchten sollten, schwach im Hintergrund leuchten. Ich habe die Ports zwar des öfteren auf 0 gesetzt, aber es hilft nicht. Habt ihr eine Idee, wie ich eine klare Anzeige bekomme?

BMS
24.04.2011, 12:07
Ich habe die Ports zwar des öfteren auf 0 gesetzt, aber es hilft nicht.
Du musst die Segmente ausschalten kurz bevor du die nächste Stelle ansprichst.

Koertis
27.04.2011, 11:16
Was wäre da ein gutes Verhältnis zwischen laufzeit und Ausschaltzeit? di restlichen Anzeigen flakern immer noch... ich bekomme das nicht so hin....

Irgendwie hinken die Zeichen nach, sobald ich einen 7er am Segment 1 anzeigen lasse und den Rest 1er, dann sehe ich das Segment a bei jeden 1er leicht leuchten....

Ich habe das Problem jetzt rausgefunden.... Meine Mosfets schalten die Segmente nicht ganz ab... also sind sie wohl zu groß....

Koertis
27.04.2011, 19:09
Offenbar kann ich die Mosfets nicht mit den High side treiber voll durchschalten... was nun?

Besserwessi
27.04.2011, 20:12
Der Leckstrom sollte auch bei großen MOSFETs kaum über 1 µA gehen. Dagegen würde schon ein ziemlich großer Pulldown Widerstand helfen. Das Problem wird eher sein, das der MOSFET nicht schnell genug abschaltet. Über die Kapazität am Gate und den Widerstand kann es da schon mal ein paar µs dauern. Die Pause zwischen den Stellen sollte entsprechend lang sein. Für einen Druchgang mit 5 Stellen hat man rund 10-20 ms, oder 2-4 ms je Stelle. Da sollten 10-50 µs als Verzögerung über sein.

Wie sieht denn der Gate Treiber aus ? Vielleicht geht es da jo noch etwas schneller.

Koertis
28.04.2011, 11:23
Ja aber auch wenn ich die Segmente langsam (1s) beschalte leuchten alle, bis auf die eine die gerade angesprochen wird leuchtet stärker. Als Gate Treiber benutze ich ICL7660 (High side Treiber)... :confused:

Ich muss erst probiern ob es di Geschwindigkeit ist, das kann ich morgen machen. Wenn es die Geschwindigkeit ist, wie kann ich dann alles beschleunigen?

Besserwessi
28.04.2011, 17:50
ICL7660 ist keine Gate Treiber, sondern eine Ladungspumpe, ist es vielleicht ein ICL7667 ?

Mit dem ICL7667 als Gate Treiber ist die Geschwindigkeit kein Problem. Eine weitere Fehlerquelle wäre die Versorgungsspannung - der FET sollte die Gleiche Spannung wie der Gate Treiber bekommen, nicht mehr.

Koertis
28.04.2011, 18:33
Ja ICL 7667. Ja beide haben die gleiche Versorgungsspannung... vileicht Strombegrenzung durch den Versorgungstrafo?

Koertis
29.04.2011, 11:47
Nein meine Mosfets schalten nicht durch, aber laut Datenblatt müsste der Strom ganz abgeschnürrt werden(bei 15V), ist jedoch nicht der Fall... Muss die Gatespannung nicht negativ sein?

Ich hab den Mosfet so angeschlossen: Drain an +15V, source: last, gate: high side treiber

Besserwessi
29.04.2011, 17:02
Der FET (P Kanal !) muss mit Source an die 15 V, und die Last an Drain. Andersherum fließt der Strom durch die Interne Diode weiter.

Die Spannung am Gate ist dann relativ zu Source entweder etwa 0 V oder ca. -15 V.

Koertis
29.04.2011, 19:52
Danke das wars :)