Forum: Projekte & Code Stoppuhr-Anfängerprojekt


von Joachim (Gast)


Lesenswert?

Morgen!
Ich wollte mal rumhören, ob grundsätzliches Interesse an einem 
Stoppuhr-Code bestehet. Es handelt sich dabei eher um ein 
Anfängerprojekt. Ein bißchen mit Timer, Interrupts und so. Also nichts 
mit I2C oder Flash oder so ;) ...
Wenn Ja kann ich heute mittag gern mal meinen Code und eine Beschreibung 
posten...

Gruß,
Joachim

von STK500-Besitzer (Gast)


Lesenswert?

Mach es doch einfach!
Es wird vermutlich zu einer Diskussion führen; vielleicht aber auch 
nicht.
Trau dich!

von Joachim (Gast)


Angehängte Dateien:

Lesenswert?

Ok, also hier hab ich mal mein Stoppuhr-Programm angehäng. Wie gesagt, 
es ist ein Projekt von einen Anfänger für Anfänger.
Aber es funktioniert gut und zuverlässig.

Allgemeines:
Als Hardware verwende ich das Pollin-Evaluation-Board. Hier ist es so, 
daß die vorhandenen Taster der PortPin auf high ziehen. Gefällt mir zwar 
nicht, ist aber so. Darum sprechen die Interrupts auf "rising edges" an.
µC ist ein ATmega32, der mit nem externen 16 MHz-Quarz läuft.

Ursprünglich war es für einen mega8 gedacht, aber da hat mich das Board 
etwas eingeschränkt, weil das dann mit den INT-Pins nicht mehr gepasst 
hätte. Man könnte es zwar umschreiben, daß es auf nem mega8 läuft, aber 
hmmm... wird das nächste Update =). Ja, PortA hab ich mal frei gelassen, 
falls man den ADC mal noch braucht.

Die sechs 7-Segment-Anzeigen, auf denen die Ausgabe stattfindet, sind 
wie im Multiplex-Tutorial auf dieser Seite angeschlossen, nur eben an 
anderen Ports.

Die Stoppuhr hat eine Auflösung von 0,0001 Sekunden, oder 100 µs. Dafür 
wird Timer2 verwendet weil.. oh, das weiß ich nicht mehr. Man könnte 
auch einen anderen nehmen. Der Vorteiler steht auf 32. dabei ergibt sich 
folgende Rechnung:

16000000/32 = 500000 -> in einer Sek wird also bis 500000 gezählt. D.h. 
in 1/10000 Sek zählt er bis 50. Daher ist die OCR2 = 49. Warum das mit 
50 nicht ging weiß ich nicht so genau. Aber mit 49 läuft das laut 
Simulator 100%ig genau. Bei z.B. 4 MHz müßte der Vorteiler einfach auf 8 
stehen.

Bei sechs Anzeigestellen kann man so Zeiten bis max 99,9999 Sek stoppen.

So, bei Fragen aber auch Anregungen immer raus damit. Weitere 
Erläuterungen stehen als Comments im Code. Aber nicht sooo viel nörgeln, 
ok? Wie gesagt, das Prog ist natürlich nicht perfekt (ISRs zu lang, ich 
weiß... ;) ) und sooo lange mach ich das noch nicht.

So long,
Gruß,
Joachim

ACHTUNG: es ist ein verfluchtes Programm: 666 Bytes >:-]
Hell yeah!

von Ulrich (Gast)


Lesenswert?

sieht doch schonmal gar nicht so schlecht aus. Ich mache schon seit ein 
paar Jahren mit avrs rum und hätte es fast nicht anders gemacht. Schön 
ist es auch dass du für adresse und anzwert tabellen(array) genommen 
hast. Kleiner Tipp noch zum eindämmen der Schreibarbeit:

Solchen statischen arrays würde ich so schreiben:

uint8_t anzwert[]={0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 
0x90};

außerdem kann man bei adresse und anzwert das volatile weglassen. Das 
spart enorm viel Code da sonst der optimierer nicht "loslegt"

von Joachim (Gast)


Lesenswert?

Hallo Ulrich!

danke für die Antwort!
Das mit dem Füller der Arrays ist echt gut. Hab mich beim programmieren 
auch öffters gefragt, ob ich die dinger nicht "in einem Rutsch" füllen 
kann. Aber ok, so wirds gehn :) .
Muß auch gestehen, daß ich nicht ganz gecheckt habe, was volatile macht. 
Ich wußte nur, daß es die Register für Hardware-Zugriffe freigibt. Und 
da dachte ich, daß das ja nicht schädlich sein kann...

Ach ja: die Taster der Interrupts sind nicht entprellt, da die Schalter 
hardwaremäßig schon entprellt sind und auch so recht wenig prellen. Habe 
daher auf eine Software-Entprellung verzichtet. Es wurde also nicht 
einfach "vergessen", m'kay?

Aber wenn ich das Programm änder... dann hab ich nicht mehr die coole 
Größe von 666 Bytes ;) !

Gruß,
Joachim

von jo (Gast)


Lesenswert?

das zählen / die überlauferkennung kann man bestimmt noch kompakter 
/eleganter machen!?

von Peter D. (peda)


Lesenswert?

Ulrich wrote:
> sieht doch schonmal gar nicht so schlecht aus. Ich mache schon seit ein
> paar Jahren mit avrs rum und hätte es fast nicht anders gemacht.

Naja, Multiplexen in der Mainloop ohne Timerinterrupt ist schon ziemlich 
strange, würde mir nie einfallen. Damit verbaut man sich gründlich das 
Programmieren größerer Projekte.

Der Code erzeugt auch Geisterdigits, da das nächste Digit erst nach dem 
Einschalten des Digits ausgeben wird.
Also erst PORTB ausschalten, PORTC ändern und PORTB wieder einschalten.

Zählen und Rechnen würde ich immer im Lieblings-Format der CPU, also 
binär.
Nur zur Ausgabe würde ich dezimal wandeln. Damit ergibt sich kleinerer 
Code und weniger CPU-Last.


Peter

von Mike (Gast)


Lesenswert?

5x (fast) der gleiche Code hintereinander schreit eigentlich nach einer 
Funktion.

Du kannst auch (wie von Peter Dannegger bereits erwähnt) auch binär 
rechnen und dann den Wert z.B. mit itoa/ltoa umwandeln.

von Joachim (Gast)


Lesenswert?

Hi nochmal!
Danke für die Tipps.
@Peter:
es ist aber nenmal kein größeres Projekt. Denn wie mehrfach erwähnt kann 
ich noch keine 10-jährige Programmiererfahrung ausweisen wie z.B. 
Ulrich.
Geisterdigits werden nicht erzeugt. Zum einen, weil jeder Anzeigewert 
1 ms gehalten wird (das wäre im Verhältnis zu einer Werteüberlappung 
sehr lange). Aber die Werte überlappen sich nicht, da in Line 109 (glaub 
ich) einmal 0xFF ausgegeben wird, was invertiert für "alles aus" steht.

Zu binär/dezimal: das versteh ich nicht ganz. Mir ist nicht aufgefallen, 
daß ich überhaupt irgendwo in dezimal "rechne". Die Werte der Anzeige 
werden doch allen in HEX übergeben. Mit Dezimal kann die Anzeige ja 
nüscht anfangen grübel... ok, ich könnte den Grenzwert von 49 noch in 
0x31 umändern. Aber obs was bringt?!

@Mike
du meinst sicher die ISR vom Timer2. Da, das fiel mir auch auf. Aber 
irgendwie hatte ich keine zündende Idee, um das schlanker zu machen. Und 
so fiel es mir leichter den Überblick zu behalten. Abr recht hast du. Es 
mag zweifelsohne elegantere Lösungen geben. Ich üb' ja noch...

Gruß,
Joachim

von Peter D. (peda)


Lesenswert?

Joachim wrote:
> es ist aber nenmal kein größeres Projekt. Denn wie mehrfach erwähnt kann
> ich noch keine 10-jährige Programmiererfahrung ausweisen wie z.B.
> Ulrich.

Willst Du jetzt etwa aufhören?

Oder willst Du auch mal größere Sachen machen?
Dann ist es durchaus sinnvoll, sich nicht erst Techniken anzugewöhnen, 
die später hinderlich sind.


> Geisterdigits werden nicht erzeugt.

Mach einfach mal das Zimmer dunkel, dann siehst Du es.


> Zum einen, weil jeder Anzeigewert
> 1 ms gehalten wird (das wäre im Verhältnis zu einer Werteüberlappung
> sehr lange). Aber die Werte überlappen sich nicht, da in Line 109 (glaub
> ich) einmal 0xFF ausgegeben wird, was invertiert für "alles aus" steht.

Aber Du schaltest wieder ein, bevor der neue Code ausgegeben wird.
Ausschalten nützt garnichts, wenn man es in der falschen Reihenfolge 
macht.


> Zu binär/dezimal: das versteh ich nicht ganz. Mir ist nicht aufgefallen,
> daß ich überhaupt irgendwo in dezimal "rechne".

Solltest Du Deinen eigenen Code nicht verstehen?
1
if(zeiger[0] > 9)
2
  {
3
    zeiger[0]=0;
4
    zeiger[1]++;
5
  }
Du zählst nur von 0..9 und das nennt sich dezimal.

Binär heißt, ein Byte zählt von 0..255, 2 Byte von 0..65535 usw.


Peter

von Mike (Gast)


Lesenswert?

> Geisterdigits werden nicht erzeugt.

Es gibt sie ganz sicher und sie nerven...

Eine Variante für
1
  if(zeiger[0] > 9)
2
  {
3
    zeiger[0]=0;
4
    zeiger[1]++;
5
  }
6
7
(noch 4x)

wäre auch:
1
   for(i = 0; i < 6; i++) {
2
    if(zeiger[i] > 9)
3
    {
4
    zeiger[i]=0;
5
    zeiger[i+1]++;
6
    }
7
   }

Sieht so deutlich eleganter aus ;).

Gleich binär zu rechnen hätte den Vorteil das du nur einen Zähler 
erhöhen musst. Du rechnest den Wert dann nur noch zur Anzeige um.

von lkmiller (Gast)


Lesenswert?

@Joachim:
Mit dieser Interrupt-Programmiertaktik wirst du recht bald auf der Nase 
landen. Klar klappt das mit 2 Tastern und einem Timer noch recht 
überschaubar.
Aber was passiert, wenn mitten im Timer-Interrupt noch der 
Reset-Taster-Interrupt kommt? Oder der Start-Stop-Interrupt? Dürfen sich 
die Interrupts gegenseitig unterbrechen? Oder stoppt die Start-Taste 
auch die Interruptbearbeitung des Timers? Was passiert, wenn da noch ein 
paar andere Eingangssignale dazukommen?

Machs doch wie in der SPS-Programmierung (oder wie Windows):
Durchlaufe dauernd eine Hauptschleife (so etwa wie die MUX-Schleife) mit 
Vollgas, und schau dabei nach ob sich was getan hat (Taster, Timer...) 
und reagiere dann darauf. Die Interrupt-Routinen setzen dann bestenfalls 
nur Flags (z.B. der Timer ein 100us-Flag und ein 1ms-Flag), die dann in 
der Hauptschleife abgefragt werden. Gerechnet wird in einem Interrupt 
nichts! Auf diese Art und Weise kann ich einfach eine weitere Aufgabe 
(neudeutsch: Task) mit in die Hauptschleife einbinden.

etwa so:
1
:
2
:
3
char tic100us; // Flags
4
char cnt100us;
5
char tic1ms;
6
char keyStartStop;
7
char keyStartStopOld;
8
char keyReset;
9
char keyResetOld;
10
char lauf;
11
:
12
:
13
main()
14
  Init(); // Timer, Ports...
15
  lauf = 0; 
16
  for(;;){
17
    // Eingänge abholen
18
    keyStartStopOld = keyStartStop;    // merken für Flankenauswertung
19
    keyResetOld     = keyReset;        // 
20
    keyStartStop    = PX&TASTE_STARTSTOP; // Taste einlesen
21
    keyReset        = PX&TASTE_RESET;     // 
22
    
23
    // Task 1
24
    if(tic100us) {
25
       if(lauf) Zaehlerroutine_aufrufen();
26
       if(++cnt100us>=10) {   // mitzählen für millisec
27
          cnt100us=0;         
28
          tic1ms++;           // schon wieder 1ms vorbei
29
       }
30
       tic100us--;
31
    }
32
33
    // Task 2
34
    if(tic1ms) {
35
       MuxRoutine_aufrufen();
36
       tic1ms--;
37
    }
38
39
    // Task 3
40
    if(keyStartStopOld!=keyStartStop && keyStartStop) { // steigende Flanke
41
       lauf = !lauf ;         // Start-Stop bearbeiten
42
    }
43
44
    // Task 4
45
    if(keyResetOld!=keyReset && keyReset) { // steigende Flanke
46
       // Reset bearbeiten
47
    }
48
49
    // Task 5..n
50
    falls_das {
51
       // dann tu was      
52
    }
53
54
  }
55
}
56
57
// einziger Interrupt
58
Timer_Int() // Aufruf alle 100us
59
{
60
  tic100us++; // Flag setzen
61
}

Ich habe hier Zähler als Flags verwendet, dann geht mir keiner der 
100us-Tics verloren, auch wenn die Bearbeitung der Hauptschleife mal 
länger dauert.

von Joachim (Gast)


Lesenswert?

@Mike:
ui, danke :)
das mit dem "zeiger[i+1]++;" war das Entscheidende, was mir bei meinen 
Überlegungen gefehlt hat.
Das ist wirklich wesentlich hübschen. Hab das gleich mal umgesetzt.
das ganze sieht in der ISR jetzt also so aus:
1
ISR(TIMER2_COMP_vect)
2
{
3
  zeiger[0]++;
4
5
  for(i=0; i<6; i++)
6
  {
7
    if(zeiger[i] > 9)
8
    {
9
    zeiger[i]=0;
10
    zeiger[i+1]++;
11
12
    if(zeiger[5] > 9)
13
    {  
14
      zeiger[0]=0;
15
      zeiger[1]=0;
16
      zeiger[2]=0;
17
      zeiger[3]=0;
18
      zeiger[4]=0;
19
      zeiger[5]=0;
20
    }
21
  }   
22
}
23
}

damit hat der code "nurnoch" 632 Byte... :(
Das geistern hab ich behoben, indem ich einfach nicht die Adressen, 
sondern die Daten einmal aus gemacht habe. Wenn sich da nicht wieder ein 
hinterhältiger Denkfehler eingeschlichen hat müßte das so richtig sein.

Eins noch: wenn ich da, wo "zeiger[0]++;" steht nen Breakpoint hinmache 
und das Prog laufen lasse braucht der Simulator immer 100µs. Aber rund 
alle 22 Klicks braucht er 160µs, weil er die main() von vorne beginnt 
und nicht einfach in der while-Schleife bleibt. Wisst ihr warum?!

Danke nochmal und viel Spaß noch!
Joachim

von Joachim (Gast)


Lesenswert?

@ lkmiller
klar, da hast du recht. Taktisch ausgereift war meine Lösung auch nicht. 
Das habe ich aber oben weiter schon mehrfach erwähnt. Trotzdem danke für 
den Tip. An diese ollen Sachen mit "Priorität hier" und 
"zulassen/sperren da" muß ich mich noch etwas gewöhnen. Aber wie 
gesagt... das mit der Übung, nech?

MfG

von Karl H. (kbuchegg)


Lesenswert?

Joachim wrote:

> Das ist wirklich wesentlich hübschen. Hab das gleich mal umgesetzt.
> das ganze sieht in der ISR jetzt also so aus:

Vorsicht!
Wenn du Indexberechnung in Arrays machst, dann überleg lieber
3 mal, ob der Index auch immer im erlaubten Bereich bleibt.

zeiger ist ein Array mit 6 Elementen: 0, 1, 2, 3, 4, 5

> ISR(TIMER2_COMP_vect)
> {
>   zeiger[0]++;
>
>   for(i=0; i<6; i++)

i nimmt nacheinander die Werte 0, 1, 2, 3, 4, 5 an, damit ...

>   {
>     if(zeiger[i] > 9)
>     {
>     zeiger[i]=0;
>     zeiger[i+1]++;

ergibt i + 1 hier die Werte  1, 2, 3, 4, 5, 6

Autsch. Ein Arrayelement zeiger[6] existiert aber nicht!

> alle 22 Klicks braucht er 160µs, weil er die main() von vorne beginnt
> und nicht einfach in der while-Schleife bleibt. Wisst ihr warum?!

Könnte eine fehlerhafte Spannungsversorgung sein, die einen
Reset auslöst.

von lkmiller (Gast)


Lesenswert?

@Joachim:
Das wird schon.
"Übung macht den Kleister" sagt der Malerlehrling.
Schon gar nicht ohne, wie du jetzt schon mit Pointern um dich wirfst ;-)
:
:
1
   PORTC = anzwert[zeiger[a]];  // Daten an PORTC
:
:
weiter so.

von Joachim (Gast)


Lesenswert?

@Karl heinz Buchegger
jo, für einen Moment hab ich da auch drüber nachgedacht. Aber es 
funktionierte dann doch letztendlich. Ich dachte wenn was nicht stimmt 
wird der Compiler oder so schon nörgeln... ;)
Das mit der fehlerhaften Spannungsversorgung glaub ich nicht. Die hätte 
ja mit dem Simulator nix zu tun. Und in real zählen die sechs Anzeigen 
bei mir auf dem Schreibtisch ja von 000000 bis 999999. Aber nett, daß du 
dir Gedanken gemacht hast.

@lkmiller
wie meinste das? Ist das dämlich? Ich hatte sowas schonmal etwas kleiner 
in ASM gemacht. Da fand ich das mit den Pointern gar nicht so doof. Und 
da fand ich das noch viel schlimmer mit indirekter Adressierung und so. 
Ok, das war aber auch auf nem PIC.
Aber im nachhinein find ich es immer noch nicht doof, weil ich dann in 
jedem "zeiger" den dezimal-wert der jeweiligen Anzeigenstelle stehen 
hab. Das könnte ich dann nämlich gebrauchen, wenn das mal auf einem LCD 
ausgegeben werden soll. Dann muß ich von zeiger[0-5] nurnoch nen String 
oder so machen. also so war meine grobe Überlegung. wie das geht weiß 
ich aber noch nicht... ;)

von lkmiller (Gast)


Lesenswert?

@kbuchegg
:
:> und das Prog laufen lasse braucht der Simulator immer 100µs. Aber 
rund
:> alle 22 Klicks braucht er 160µs, weil er die main() von vorne beginnt
:> und nicht einfach in der while-Schleife bleibt. Wisst ihr warum?!
:
:Könnte eine fehlerhafte Spannungsversorgung sein, die einen
:Reset auslöst.
:
Wird der Simulator mit Spannung versorgt ;-)

@Joachim:
Was sind 22 Klicks??
Kommt da evtl. der Watchdog??

von Karl H. (kbuchegg)


Lesenswert?

Joachim wrote:
> @Karl heinz Buchegger
> jo, für einen Moment hab ich da auch drüber nachgedacht. Aber es
> funktionierte dann doch letztendlich.

Vorsicht: Es scheint zu funktionieren.

Dein Problem ist, dass du eine Speicherstelle veränderst, die
nicht zum Array gehört. Wenn du Glück hast, ist das harmlos.
Wenn du Pech hast, liegt genau an dieser Stelle eine andere
Variable im Speicher, die dann plötzlich scheinbar von alleine
ihren Wert ändert.


> Ich dachte wenn was nicht stimmt
> wird der Compiler oder so schon nörgeln... ;)

Der Compiler ist nur dafür zuständig, dass du die Grammatikregeln
einhältst. Die korrekte Logik ist dein Bier.

"Der Mann liest das Auto." ist auch ein grammatikalisch korrekter
deutscher Satz. Er besteht aus Subjekt Verb Objekt. Trotzdem ist
seine 'Logik' fehlerhaft indem er keinen Sinn ergibt. Der Compiler
prüft nur ob die Grammatik stimmt und wird diesen Satz als
deutschen Satz durchgehen lassen.

> Das mit der fehlerhaften Spannungsversorgung glaub ich nicht. Die hätte
> ja mit dem Simulator nix zu tun.

Es gibt auch noch andere Möglichkeiten, die ich aber im Code nicht
kontrolliert habe.
Eine ist zb. wenn du einen Interrupt frei gibst für den es keine
ISR gibt.

> wie meinste das? Ist das dämlich?

     anzwert[zeiger[a]];

Überhaupt nicht.
Ganz im Gegenteil. Du glaubst gar nicht, wie lange viele Neulinge
brauchen, bis sie so ein Konstrukt sich einzusetzen wagen.

> Aber im nachhinein find ich es immer noch nicht doof, weil ich dann in
> jedem "zeiger" den dezimal-wert der jeweiligen Anzeigenstelle stehen
> hab.

In deinem Fall, wenn du praktisch nichts mit der Zeit machst, ist
das schon ok. Du brauchst die 'Zeit' nur für Ausgabezwecke und dort
wiederrum benötigst du sie als einzelne Ziffern. Ergo ist diese
Repräsentierung dafür optimal.
Sobald du aber damit etwas rechnen musst, ändert sich das Blatt.
Dann wird diese Darstellung eher hinderlich.

Die einzelnen Ziffern aus einer Zahl zu extrahieren, ist kein
großes Problem. Und mit Zahlen im Bereich 0 bis 10000, die auch
tatsächlich Zahlen sind, rechnet es sich nun mal leichter, als
mit den einzelnen Ziffern.

von Mike (Gast)


Lesenswert?

@Joachim
Sorry wegen dem etwas zu hohem Index im Beispiel, aber da siehst du mal 
wie leicht man mit der Indexzählerei in C auf die Nase fallen kann ;). 
Das ist einer der Fehler bei denen sich das Programm sehr merkwürdig 
verhält und die man dann ewig sucht...

Die meisten anderen Programmiersprachen hauen einem so etwas zur 
Laufzeit um die Ohren.

von Joachim (Gast)


Lesenswert?

@lkmiller
Ja, das mit den Klicks hätt ich n bissl genauer erklären können: Ich 
habe bei der Stelle "zeiger[0]++;" einen Breakpoint gemacht und klicke 
dann immer auf "Run", bis er da stehen bleibt. Da braucht er bei den 
ersten 21 Durchgängen immer 100µs und beim 22. Mal dann 160µs. So war 
das gemeint...

@Karl heinz Buchegger
dann fällt mir spontan als Lösung ein einfach das Array zeiger um 1 
größer zu machen. Dann ist der Speicher reserviert und der Logik genüge 
getan... dann "fährts" auch ;). Ich habs aber grad nicht vorliegen, war 
nur ne spontane Idee.

Wo ich schonmal dabei bin: ich will das ganze nachher/irgendwann (oder 
nächstes Jahr...) mal in Geschwindigkeit umrechnen. Dafür will ich alle 
Werte in zeiger[0-5] in einem Variablen zusammenfassen, daß da dann zum 
Beispiel ne Variable namens "zeit" ist, in der dann 846218 steht. Wie 
kann ich das machen? Was muß ich beachten?
Und damit will/muß ich dann ja auch noch rechnen. Was wird alles schief 
gehen?
Ich weiß, mir fehlen "etwas" die C-Grundlagen. Könnt ihr mir ein Buch 
über µC-Programmierung in C empfehlen?

MfG

von Karl H. (kbuchegg)


Lesenswert?

Joachim wrote:

> @Karl heinz Buchegger
> dann fällt mir spontan als Lösung ein einfach das Array zeiger um 1
> größer zu machen. Dann ist der Speicher reserviert und der Logik genüge
> getan... dann "fährts" auch ;). Ich habs aber grad nicht vorliegen, war
> nur ne spontane Idee.

Kannst du so machen.
Die Alternative dazu wäre mit einem if zu entscheiden ob es noch
eine nächste Stelle gibt und dann nur dann zu erhöhen, wenn 'links'
von der aktuellen Ziffer noch etwas ist.
Sprich: Das i abtesten, ob i+1 nicht größer als 5 wird.

> Wo ich schonmal dabei bin: ich will das ganze nachher/irgendwann (oder
> nächstes Jahr...) mal in Geschwindigkeit umrechnen. Dafür will ich alle
> Werte in zeiger[0-5] in einem Variablen zusammenfassen, daß da dann zum
> Beispiel ne Variable namens "zeit" ist, in der dann 846218 steht. Wie
> kann ich das machen? Was muß ich beachten?

Beobachtung:
  Du hast die beiden Ziffern 1 5   (welche eine gedachte 15 ergeben)

Was ergibt dann 1 * 10 + 5?

In deinem Fall würde das dann lauten:

      Ziffer[0]
    + Ziffer[1] * 10
    + Ziffer[2] * 100
    + Ziffer[3] * 1000
    + Ziffer[4] * 10000
    + Ziffer[5] * 100000

Beachten musst du lediglich, dass dir keine Berechnung überläuft.
Ein 16 Bit int zb. geht nur bis 32767 hoch. Im obigen sind alle
(bis auf eine) Multiplikationen und Additionen int Berechnungen.

Problematisch ist zb   Ziffer[4] * 10000

Wenn Ziffer[4] den Wert 2 hat, dann ergibt 2 * 10000 = 20000
Das wäre noch im Bereich, den ein 16-Bit int abdecken kann
(Obergrenze: 32767). Aber wenn Ziffer[4] den Wert 7 hat, dann
hast du bereits einen Overflow, da ja 7 * 10000 = 70000 schon
über den maximal möglichen 32767 liegt.

Also musst du den Compiler zwingen, die Berechnugn nicht im
Zahlenraum int zu machen, sondern im Zahlenraum long (=32 Bit)
Der Compiler orientiert sich immer an den Operanden einer Operation.
Sind beide int, dann wird auch eine int-Berechnung gemacht. Ist
einer davon größer (zb. long), dann wird auch der andere zuvor
auf den größeren Datentyp hochgeholt und die Operation dann in
diesem Datentyp durchgeführt. Es reicht also, wenn einer der
beiden Operanden ein long ist, damit die Multiplikation als
32-Bit Multiplikation durchgeführt wird (und auch ein 32-Bit
Ergebnis liefert). Am einfachsten ist das bei den 10000. Einfach
ein L anhängen, und schon sind die 10000 als long-Zahl markiert.

     Ziffer[0]
    + Ziffer[1] * 10
    + Ziffer[2] * 100
    + Ziffer[3] * 1000
    + Ziffer[4] * 10000L
    + Ziffer[5] * 100000L

Bei den 100000 wäre das theoretisch nicht notwendig. Denn 100000
sind für einen int sowieso zu groß und der Compiler muss als
Datentyp für die 100000 sowieso long annehmen. Aber zu Doku
Zwecken ist es immer gut explizit zu sein.

> Ich weiß, mir fehlen "etwas" die C-Grundlagen. Könnt ihr mir ein Buch
> über µC-Programmierung in C empfehlen?

Über µc Programmierung an sich nicht. Aber das hat auch relativ
wenig mit µC an sich zu tun. Das sind Dinge, die auf allen Systemen
gleich sind, weil sie so in der Sprache C verankert sind.
Und dafür ist der Klassiker K&R immer noch ein empfehlenswertes
Buch:

Kernighan & Ritchie
Programmieren in C

von ---- (Gast)


Lesenswert?

Dass ein Anfänger hier seinen Code den Löwen zum Fraß vorwirft finde ich 
mutig.
Dass es dann auch zu vielen Verbesserungsvorschlägen kommt war ja 
abzusehen.
Aber dass Joachim dann die (ernst gemeinten) Kritiken, konstruktiv 
aufgefasst hat, und nicht als persönlichen Angriff gewertet hat, hat mir 
am meisten imponiert!
Dieser Thread ist das perfekte Beispiel dafür, dass es meist am 
Threadersteller selbst hängt, wie sich die Diskussion entwickelt. In 
diesem Falle macht es richtig Spass den Lernfortschritt mitzulesen. 
Weiter so!

----, (QuadDash).

von Joachim (Gast)


Lesenswert?

@ Karl heinz Buchegger
ja, sowas mit dem Bereich hab ich mir schon gedacht. Nur die 
Vorgehensweise hab ich mir anders vorgestellt. Ich dachte eigentlich ich 
könnte die Zahlen in den Zeigern einfach hintereinander reihen, quasi 
4+6+3+8+7+1=463871 und das dann irgendwie als Zahl (long) 
interpretieren. Aber deine Methode mit dem multipizieren und dem 
aufsummieren ist auch gut. Und irgendwie auch einfach :). Manchmal hab 
ich nen Brett vorm Kopp...

Jetzt muß ich "nurnoch" den UART programmieren, damit ich mir meine 
Ergebnisse auch angucken kann. Mist, das hab ich ja noch nie gemacht... 
;)
Hoffentlich kommt das LCD bald.

@QuadDash
jaaa, das ist ja so: ich hab ja nie den Anspruch gestellt, daß das Prog 
fertig und perfekt ist. Es tat das, was es sollte und das wollte ich 
nicht für mich behalten. Und wenn ich dann noch "erwarte", daß hier und 
da Leute was anders/besser machen würden ist das ja ok, warum auch 
nicht? Das hab ich ja auch an mehreren Stellen erwähnt. Darum ist jetzt 
die ISR auch wesentlich kürzer und so. Und wenn sich Leute wie Karl 
Heinz oder lkmiller schon Gedanken machen und Verbesserungsvorschläge 
anständig rüberbringen gibt es ja keinen Grund aus zu rasten und zu 
sagen "Ihr seid alle gegen mich!"... Zehn Jahre Jugendarbeit sollten 
sich doch bemerkbar machen -> "Kritik üben und annehmen"

Insgesamt habt ihr mir bisher echt gut weiter geholfen. Danke nochmal. 
Vielleicht findet sich ja wirklich wer, der denk "och, so eine stoppuhr 
wollte ich immer schonmal haben..."
In nächster Zeit muß ich viel für Regelungstechnik lernen, darum werd 
ich das Programmieren erstmal hinten anstellen (müssen). Aber wenn es 
Updates gibt kann ich es ja wieder den Löwen zur Diskussion stellen ;) 
...

MfG

von Karl H. (kbuchegg)


Lesenswert?

Joachim wrote:

> aufsummieren ist auch gut. Und irgendwie auch einfach :). Manchmal hab
> ich nen Brett vorm Kopp...

:-)
Viele Dinge sind eigentlich sehr einfach, wenn man sie erst mal
verstanden hat.

Konkret für diese Aufgabenstellung: Genau so ist unser Zahlensystem
aufgebaut. Das ist das Wesen unseres Stellensystems: Die jeweils
nächste Stelle links hat immer eine um einen bestimmten Faktor
größere Wertigkeit. Du bist gewohnt, daß dieser Faktor 10 ist
(daher nennen wir unser Zahlensystem auch Dezimalsystem). Der
muss nicht 10 sein. Im Binärsystem ist er 2, im Hexadezimalsystem
ist er 16. Aber das Prinzip ini all diesen Systemen ist immer
dasselbe: Aus den Einern werden Zehner, indem diselben Ziffern
mit 10 multipliziert werden. VOn den Zehnern zu den Hundertern ist
es wieder der Faktor 10. 100->1000 schon wieder der Faktor 10.
Das muss auch so sein, weil genau so das von uns verwendete
Zahlensystem funktioniert.

Geht es auch anders? Aber sicher doch! Schau dir mal die
römischen Zahlen an. Und dann überleg mal um wieviel einfacher
doch unsere Stellenwertsystem ist und um wieviel simpler es
sich damit rechnen lässt.


> jaaa, das ist ja so: ich hab ja nie den Anspruch gestellt, daß das Prog
> fertig und perfekt ist.

Stell dein Licht nicht unter den Scheffel. Dein Programm ist gut.
Arbeiten kann man an allem und es werden sich immer irgendwelche
Dinge in einem Programm finden, die man besser machen kann.
Das ist ja die Krux am Programmieren. Es sieht zunächst so einfach
aus, entpuppt sich aber dann als eine harte Nuss. Nicht umsonst sagt
man, dass es ein paar Jährchen dauert, bis man das Zeug für ein
mittleres Industrieprojekt hat.

> Es tat das, was es sollte und das wollte ich
> nicht für mich behalten. Und wenn ich dann noch "erwarte", daß hier und
> da Leute was anders/besser machen würden ist das ja ok, warum auch
> nicht?

Das ist die richtige Einstellung. Ich bin jetzt seit über 20 Jahren
im Geschäft und lerne immer noch dazu.

> Insgesamt habt ihr mir bisher echt gut weiter geholfen.

Freut mich wenn du die Anregungen annimmst und umsetzt. Sowas ist
immer das Salz in der Suppe. Vor allen Dingen wenn man merkt: "Jo,
jetzt hat er's verstanden und tippt nicht einfach nur stumpfsinnig
Code ab". Grade in der Programmierung ist das Verstehen des Prinzips
(das wir Algorithmus nennen) das Allerwichtigste. Eine Programmier-
sprache ist nur ein Werkzeug um die Algorithmen umzusetzen.

von Yasi K. (aresblue)


Angehängte Dateien:

Lesenswert?

hallo leute kann mir jemand helfen
ich muss ein stopuhr mit atmega32 durch C programmieren
haber versucht aber zwecklos hoffe dass jemand mir hilfen kann
also ich will ihnen das programm beschreiben
1) Einfache Stopuhr
Unsere Stopuhr soll nun mit der Taste PA1 gestartet und mit PA2 
angehalten werden( PA1 und PA2 stehen für den bit nummer 1 bzw nummer 2 
von PINA)
können (Pause). PA3 dient zum Rücksetzen der Stopuhr.
Der Schleifendurchlauf soll nicht mehr als 250ms verzögert werden, damit 
die Tastereingaben
auch bei kurzem Druck erkannt werden.
Die Anzeige soll am LCD Display in Sekunden erfolgen.
2) Zwischenzeit erfassen
PA4 soll die Anzeige anhalten und die Zwischenzeit anzeigen. Während der 
Zwischenzeitanzeige
soll aber die Zeit weiter unsichtbar mitgezählt werden.
Damit man diesen Zustand vom Pausenzustand (mit PA2) unterscheiden kann, 
soll
eine LED blinken.
Wenn die Start-Taste gedrückt wird, soll die Zeit wieder normal 
angezeigt werden.

von neuer (Gast)


Lesenswert?

...Eine Programmiersprache ist nur ein Werkzeug um die Algorithmen 
umzusetzen.....


ach!

von Yasi K. (aresblue)


Lesenswert?

hallo
danke für deine schnelle Antwort.
hmm eigentlich ich was was du gemeint hast aber meine aufgabe ist :
ich soll der program in AVR studio ergänzen und genau diese punkt ist 
mein problem
übersetzen und auf den bord hochladen kann ich schon
kannst du mir trozdem bitte hilfen?

von Karl H. (kbuchegg)


Lesenswert?

Yasi Karmu wrote:

> ich soll der program in AVR studio ergänzen

'ergänzen' ist gut.
Da ist noch nicht mal ansatzweise eine Stoppuhr zu erkennen
und das ist mir dann doch zuwenig um dir die Arbeit abzunehmen.

Ausserdem hab ich im Code einen Kommentar gefunden, dass
das Ganze ein Praktikum der FH Regensburg ist. Von einem
FH Absolventen erwarte ich mir mehr (*), als ein lapidares: "Ich
wills noch nicht mal versuchen. Bitte macht mal!"

(*)
Speziell wenn man berücksichtigt, dass jetzt Semesterende ist
und du eigentlich dieses Semester so manche Übung gemacht
haben müsstest.

Aber einen Tipp gebe ich dir:
Eine Stoppuhr ist auch nichts anderes als ein etwas seltsamer
Zähler, der einfach nur ständig hochzählt. Dein erster Ansatz
könnte also sein, einfach mal eine Variable um 1 hochzuzählen
und diesen Zähler am LCD auszugeben. Das wird rasend schnell
gehen, daher ist der nächste Schritt: Der Zähler soll im Sekunden-
takt um 1 erhöht werden.

Eine Stoppuhr ist auch nur so ein Zähler. Nach 60 Sekunden ist
eine Minute rum (also wirst du wohl eine 2.te Variable für die
Minuten brauchen), und du setzt die Sekunden wieder auf 0 und
dafür die Minuten um 1 höher.

von Yasi K. (aresblue)


Lesenswert?

achsooo jetzt verstehe ich
und wie kann ich dieser zähler durch den AD wandler erzeugen??

von Karl H. (kbuchegg)


Lesenswert?

Yasi Karmu wrote:
> achsooo jetzt verstehe ich
> und wie kann ich dieser zähler durch den AD wandler erzeugen??

Ähm. Du weißt schon, was ein AD-Wandler macht?

Du lange Antwort: gar nicht.
Stoppuhr und AD Wandler haben rein gar nichts miteinander zu tun.
(Es sei denn am AD-Wandler hängt eine Signal, welches die
Stoppuhr startet bzw. stoppt. Aber den Fall hast du ja nicht)

von Yasi K. (aresblue)


Lesenswert?

ja ja ich weiss was AD wandler macht aber hier steht , man erzeugt ein 
wert mit einem potentiometer und der AD wandler wandelt den wert den 
potentiometer ist im PIN0 verbunden.
du hast gesagt ich muss eine variabl für minuten und sekunden 
deklarieren
und ich soll diese variable jedesmal inkrementieren wenn sie an 60 
ankomment soll diese variable zurückgesetzt werden und wird die variable 
von minuten incrementiert
meine frage wie kann ich die inkrenmentierte variable jedesmal am 
bildschirm von den Bord zeigen?
danke schön

von Yasi K. (aresblue)


Lesenswert?

?? will jemand mir hilfen???

von Rahul D. (rahul)


Lesenswert?

Kann es sein, dass du die Aufgaben nicht ganz verstanden hast?

Die ADC- und LCD-Sachen sind Vorübungen, um sich mit dem Board vertraut 
zu machen. Für die eigentliche Stoppuhr werden die nur teilweise 
(LCD-Ausgabe) gebraucht.

>wie kann ich die inkrenmentierte variable jedesmal am
>bildschirm von den Bord zeigen?

Wenn sie geändert wurde, gibt man sie auf dem LCD aus.
Das kann / sollte man maximal jede Sekunde machen, weil der Mensch eh 
nicht wesentlich schneller gucken kann.
Man lässt also einen Timer auf Tastendruck so laufen, dass er eine 
sinnvolle Zeitbasis liefert (z.B. Interrupt alle 10ms).
In der Interrupt-Routine inkrmetiert man eine Variable und guckt nach, 
ob es zu einem Überlauf kam (100*10ms = 1 Sekunde). Entweder tritt 
dieser auf, was dann zu einem Sekunden-Inkrement und dem Zurücksetzen 
der 10ms-Variable führt, oder eben nicht.
Genauso geht man dann mit den "nachfolgenden" Variabeln (Sekunden, 
Minuten, Stunden...) um.

Man sollte dann auch noch abfragen, ob die Stopp-Taste gedrückt wurde.
Und bei einer entsprechenden Betätigung die aktuelle Zeit sichern bzw. 
den Timer anhalten.

von Yasi K. (aresblue)


Lesenswert?

danke schöööööööööööön für deine Antowrt
hmm ich werde mal versuchen
( ich bin im 2. semester und habe noch nie ein bord programmiert deshalb 
kann ich nicht ganz verstehen)
^^

von Yasi K. (aresblue)


Lesenswert?

also ich soll ein timer erzeugen
das kann ich in der bibliothek time.h
aber welche funktion oder befehl soll ich benuzten um diese timer 
aufzurufen??

von Roland P. (pram)


Lesenswert?

Wie man den Timer benutzt siehst du im Beispielprogramm von Joachim 
recht gut.

Wie er funktioniert und welche Register für was zuständig sind, steht im 
Datenblatt des Conrtrollers

Auch im Tutorial sind einige Tips zu Timern. Eine Bibliothek time.h 
brauchst du (vorerst) dafür nicht

Gruß
Roland

von Karl H. (kbuchegg)


Lesenswert?

Ich denke nicht, das sein Lehrer auf einen Timer aus ist. Die
Aufgabenstellung liest sich für mich nach der Forderung nach
ganz banalen delays um die Uhr zum laufen zu bringen.
Das wird wohl ein Zugeständnis an die Kentnisse der Schüler
sein. Irgendwo muss man ja schliesslich auch anfangen.
Deshalb auch die Forderung, dass die Hauptschleife nicht länger
als 1/4 Sekunde verzögert werden soll um die Bedienung der Tasten
noch halbwegs angenehm zu machen.

Also in etwa so
1
  ...
2
  unsigned char Zehntel, Sekunden, Minuten;
3
  char Buffer[20];
4
5
  ...
6
7
  while( 1 ) {
8
9
    Zehntel ++;
10
    if( Zehntel == 10 )
11
      Zehntel = 0;
12
13
      Sekunden++;
14
      if( Sekunden == 60 ) {
15
        Minuten++;
16
        if( Minuten == 60 ) {
17
          Minuten = 0;
18
        }
19
      }
20
21
      // Minuten und Sekunden auf dem LCD ausgeben
22
      sprintf( Buffer, "%02d:%02d", Minuten, Sekunden );
23
      lcdPrint( 0, 0, Buffer );
24
    }
25
26
    delay_ms( 100 );
27
  }

Aber die Tastenauswertung bzw. den Umbau zu einer
Stoppuhr musst du jetzt schon selbst hinkriegen :-)

http://www.mikrocontroller.net/articles/FAQ#Wie_kann_ich_Zahlen_auf_LCD.2FUART_ausgeben.3F

von Yasi K. (aresblue)


Lesenswert?

also für die tasteneingabe habe ich so geschrieben
habe eine variable deklariert
pina
und der inhalt von porta wird auf pina zugewiesen
den zweiten bit entspricht das laufen von der stopuhr 3.bit anhalten und 
4 reset
also
habe der 2 bit auf lauf gesetzt den 3. auf halt und 4 auf reset
und so schaut mein program aus?
 unsigned short pina,halt, lauf, reset;
pina=~PINA ;
halt, lauf, reset
lauf=(pina>>1)&0x01;
halt(pina>>2)&0x01;
reset=(pina>>3)&0x01;
while (lauf==1)
{

while( 1 ) {

    Zehntel ++;
    if( Zehntel == 10 )
      Zehntel = 0;

      Sekunden++;
      if( Sekunden == 60 ) {
        Minuten++;
        if( Minuten == 60 ) {
          Minuten = 0;
        }
      }

      // Minuten und Sekunden auf dem LCD ausgeben
      sprintf( Buffer, "%02d:%02d", Minuten, Sekunden );
      lcdPrint( 0, 0, Buffer );
    }

    delay_ms( 100 );
  }
}
while(halt==1)
{
lauf=0;
}
while(reset==1)
{
lauf=1;
}

von yassi karmu (Gast)


Lesenswert?

das ist richtig oder???
:-)

von Karl H. (kbuchegg)


Lesenswert?

Nein. Das ist komplett daneben.

von yassi karmu (Gast)


Lesenswert?

hmmm wieso denn was ist falsches ???

von Karl H. (kbuchegg)


Lesenswert?

yassi karmu wrote:
> hmmm wieso denn was ist falsches ???

So ziemlich alles bis auf den Teil den du von mir übernommen
hast.

Das Teil sieht aus als ob du noch nie ein Programm geschrieben
hättest. Und ich meine überhaupt noch nie, nicht nur noch nie
auf einem µC

von yassi karmu (Gast)


Lesenswert?

keine hilfe???

von Frank (Gast)


Lesenswert?

Hallo,
ich bin dabei auch eine Stoppuhr aufzubauen. Leider bin ich in der 
Programmierung blutiger Anfänger. Vielleicht ist ja jemand hier, der mir 
etwas unter den Armen greifen kann.
Folgendes soll diese machen
Impuls am Eingang1 = Start der Uhr und gleichzeitig deaktivieren des 
Eingangs für 5 sek.
Anzeige der Zeit in sek und 10.tel und 100.tel    (1235,23 sek)
Erneuter Impuls am Eingang 1 stoppen der Uhr und wieder 5 sek den 
Eingang deaktivieren.

Impuls am Eingang 2 die Anzeige auf 0,00 setzen und den Wert Speichern.
Maximale Speicherung - die letzten 5 Messungen
Impuls am Eingang 3 Durchblättern der letzten Werte.
Vor den letzten Wert soll dann 0-5 stehen 0 für aktuelle Messung und 5 
für die älteste Messung

0- 125,36
1- 236,36
2- 456,36
3- 251,31
4- 452,14
5- 547,88

Wer kann helfen?

von STK500-Besitzer (Gast)


Lesenswert?

Frank schrieb:
> gleichzeitig deaktivieren des
> Eingangs für 5 sek.

Warum?
Es gibt ja nicht schon hunderte Stoppuhr-Projekte im Internet zu finden.

Frank schrieb:
> Anzeige der Zeit in sek und 10.tel und 100.tel    (1235,23 sek)

Bischen rechnen und fertig.

von Stefan (Gast)


Lesenswert?


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.