Forum: Mikrocontroller und Digitale Elektronik 1 Sekunde 16-Bit-Timer


von Christian Q. (osx)


Lesenswert?

1
//16-Bit Timer
2
TCCR1B |= (1 << WGM12); //CTC Modus
3
TCCR1B |= (0 << CS12) | (1 << CS11) | (0 << CS10);   //Prescaler 256
4
OCR1A = 34286;                 
5
OCR1B |= 1000;
6
TIMSK = (1<<OCIE1A);  
7
TIMSK |= (1<<OCIE1B);

Hallo, ich wollte euch mal fragen ob mein Vorhaben mit dem Timer machbar 
ist: Jede Sekunde soll TIMER1_COMPA_vect aufrufen in der eine Uhr läuft 
und ca. jede ms TIMER1_COMPB_vect.

Stimmt die Konfiguration so? Geht das mit einem Timer oder muss ich 2 
nehmen?

von spess53 (Gast)


Lesenswert?

Hi

>Stimmt die Konfiguration so? Geht das mit einem Timer oder muss ich 2
>nehmen?

Controller, Takt?

MfG Spess

von Christian Q. (osx)


Lesenswert?

Oh, atmega16 - 8Mhz

von Karl H. (kbuchegg)


Lesenswert?

Christian Q. schrieb:
>
1
> //16-Bit Timer
2
> TCCR1B |= (1 << WGM12); //CTC Modus
3
> TCCR1B |= (0 << CS12) | (1 << CS11) | (0 << CS10);   //Prescaler 256
4
> OCR1A = 34286;
5
> OCR1B |= 1000;
6
> TIMSK = (1<<OCIE1A);
7
> TIMSK |= (1<<OCIE1B);
8
>
>
> Hallo, ich wollte euch mal fragen ob mein Vorhaben mit dem Timer machbar
> ist: Jede Sekunde soll TIMER1_COMPA_vect aufrufen in der eine Uhr läuft
> und ca. jede ms TIMER1_COMPB_vect.
>
> Stimmt die Konfiguration so?

Die Bits hast du ja sicherlich mit dem Datenblatt daneben 
zusammengestellt, also wird das wohl stimmen.


> Geht das mit einem Timer oder muss ich 2
> nehmen?

Prinzipiell kannst du dich an beide Compare klemmen.

Aber ob deine Zahlen stimmen?

> Jede Sekunde soll TIMER1_COMPA_vect aufrufen
> und ca. jede ms TIMER1_COMPB_vect.

1 Sekunde hat 1000 Millisekunden. Daher müsste B tausend mal öfter 
aufgerufen werden als A. D.h. der Comparewert für A muss 1000 mal größer 
als der von B sein.

> OCR1A = 34286;
> OCR1B |= 1000;

Das ist aber nicht der Fall :-)
(Und was macht das Oder beim Setzen vvon OCR1B?)

Wozu brauchst du eigentlich den 1 Sekunden Interrupt?
Wenn du im Millisekunden-Interrupt die Aufrufe zählst, dann ist nach dem 
1000-sten Aufruf auch 1 Sekunde vergangen.

von Karl H. (kbuchegg)


Lesenswert?

Christian Q. schrieb:
> Oh, atmega16 - 8Mhz

Und wie bist du da jetzt auf deine Zahlen gekommen?
Lotto - Wasserstand - Luftfeuchte - Aussentemperatur in Haiti - 
sonstiges?

von Christian Q. (osx)


Lesenswert?

Hab ein bisschen probiert bis es ca. hinkam.
Also ich brauch den timer mit 1ms fürs multiplexen von led's. Habe 2 
timer genommen, da ich so besser das timing, getrennt von einander 
regeln kann.

Wie berechne ich die Timer genau?

von Hans_super_schlau (Gast)


Lesenswert?

Christian Q. schrieb:
> Wie berechne ich die Timer genau?

Mit deinem Systemtakt, bzw. dem daraus abgeleiteten Timer-Takt und ein 
bisschen Mathematik?

von spess53 (Gast)


Lesenswert?

Hi

Im Datenblatt findest unter Timer1->Modes of Operation->CTC eine Formel.
Für die Frequenz setzt du 1/2s bzw 1/2ms ein.

MfG Spess

von Christian Q. (osx)


Lesenswert?

8000000 / 256 = 31250
der Timer A Zählt von 0 bis 34286 ..scheinbar zu viel. also
8000000 / 256 = 31250
31250 - 1 = 31249 stimmt das?

von spess53 (Gast)


Lesenswert?

Hi

>31250 - 1 = 31249 stimmt das?

Ja.

MfG Spess

von Christian Q. (osx)


Lesenswert?

Ich betreibe 8 LED, 4 untereinander in 2 Spalten. Wie beziehe ich das in 
die Rechnung für Timer B ein? Man brauch pro LED ja ca. 25Hz. Weiß da 
jemand wie ich das einbaue?

Wofür steht das OCRnA in der Formel vom Datenblatt?

spess53 schrieb:
> Für die Frequenz setzt du 1/2s bzw 1/2ms ein.
Wie bitte?

von Karl H. (kbuchegg)


Lesenswert?

Christian Q. schrieb:
> Ich betreibe 8 LED, 4 untereinander in 2 Spalten. Wie beziehe ich das in
> die Rechnung für Timer B ein? Man brauch pro LED ja ca. 25Hz. Weiß da
> jemand wie ich das einbaue?

Wie hast du das denn hardwaretechnisch gelöst?
Im Idealfall steuerst du ja nicht jede LED einzeln im Multiplex an, 
sondern zb jeweils 4 LED auf einmal.

> Wofür steht das OCRnA in der Formel vom Datenblatt?

n ist die Nummer des Timers. Das Prinzip ist ja bei allen Timern 
identisch. Schliesslich funktionieren ja Timer im Prinzip immer gleich: 
sie zählen und bei bestimmten Zählerständen passiert was.

> spess53 schrieb:
>> Für die Frequenz setzt du 1/2s bzw 1/2ms ein.
> Wie bitte?

Vergiss es, das gilt nur, wenn du toggeln würdest.

von Karl H. (kbuchegg)


Lesenswert?

Christian Q. schrieb:

> Also ich brauch den timer mit 1ms fürs multiplexen von led's.

Nur dass du natürlich mit einem OCR Wert von 1000 keine 1 Millisekunde 
hast. Ist aber egal, fürs Multiplexen brauchst du sowieso keine so kurze 
Zeit

von spess53 (Gast)


Lesenswert?

Hi

>Vergiss es, das gilt nur, wenn du toggeln würdest.

Nein. Die Formel ist zum Ausrechnen der Toggle-Frequenz. Und ein 
Interruptabstand von 1s würde eine Toggle-Frequenz von 0,5Hz=1/2s 
ergeben.

MfG Spess

von Karl H. (kbuchegg)


Lesenswert?

spess53 schrieb:
> Hi
>
>>Vergiss es, das gilt nur, wenn du toggeln würdest.
>
> Nein. Die Formel ist zum Ausrechnen der Toggle-Frequenz. Und ein
> Interruptabstand von 1s würde eine Toggle-Frequenz von 0,5Hz=1/2s
> ergeben.

Sag ich doch (Wir haben aneinander vorbeigeredet)

Wenn ein Timer etwas toggeln soll, kommt der Faktor 1/2 ins Spiel, wenn 
es nur um die reine Aufruffrequenz einer ISR geht, kommt kein Faktor.

Besser?


Genau aus dem Grund mag ich die Formeln im Datenblatt nicht. Erstens 
weiß ich sie nicht auswendig, zweitens werden sie oft hirnlos 
angewendet, drittens braucht man sie nicht, wenn man das Timerprinzip 
verstanden hat. Christian hats ja auch in ein paar Minuten mit Überlegen 
richtig hingekriegt. Spätestens nach dem 3. Einrichten eines Timers hat 
man das ganze durch das Funktionsprinzip schneller berechnet, als man im 
Datenblatt die Formel nachschlagen könnte. Und verstanden hat man dann 
auch, was man da eigentlich warum macht.

von Christian Q. (osx)


Lesenswert?

Die 8 LED teilen sich 4 Pins. Ich schalte dann nur die "Spalten" also 2 
weitere Pins dafür.


Karl heinz Buchegger schrieb:
> fürs Multiplexen brauchst du sowieso keine so kurze
> Zeit

Wie viele ms brauch ich ca?

von Karl H. (kbuchegg)


Lesenswert?

Christian Q. schrieb:
> Die 8 LED teilen sich 4 Pins. Ich schalte dann nur die "Spalten" also 2
> weitere Pins dafür.
>
>
> Karl heinz Buchegger schrieb:
>> fürs Multiplexen brauchst du sowieso keine so kurze
>> Zeit
>
> Wie viele ms brauch ich ca?

Wenn du auf 100 bis 150Hz kommst, dann reicht das.
Der Rest ist wieder Mathe.
Eine 'Schwingung' dauert vom Aufleuchten einer LED-Spalte bis zum 
erneuten Aufleuchten.

Wenn das 100 mal in der Sekunde passieren soll, dann dauert ein 
einmaliger Zyklus logischerweise 1/100 = 0.01 Sekunden.

In diesen 0.01 Sekunden brauchst du aber 2 Ereignisse, denn die 
"Schwingung" geht ja: Einschalten - warten - Ausschalten - warten -
Da sind also 2 Ereignisse, die bearbeitet werden müssen.

D.h. du musst dafür sorgen, dass du alle 0.005 Sekunden einen Interrupt 
bekommst (bei angestrebten 100Hz). Das sind dann 50ms

Den OCR Wert darfst du wieder selber ausrechnen :-)

Es ist aber nicht zeitkritisch. Wenn die ISR zu selten aufgerufen wird, 
dann flackerts wie Sau und das merkst du.

Übrigens:

>> Geht das mit einem Timer oder muss ich 2
>> nehmen?
>
> Prinzipiell kannst du dich an beide Compare klemmen.

Das war Unsinn. Du brauchst 2 Timer wenn du die Uhr vom Multiplex 
trennen willst. Wofür es allerdings keinen wirklichen Grund gibt.

von Christian Q. (osx)


Lesenswert?

Habe 155 ermittelt.

Leider funktioniert meine Software jetzt nichtmehr. Sehe immer nur die 
Oberen LED an. Dieses Einschalten - warten - Ausschalten - warten -
das Warten ist bei mir die Zeit zwischen der ISR.

Also ich schalte im 1. Schritt
Alle LED, Spalten aus.
2. je nach Anzeige den Pin der aktuellen Spalte schalten und Spalte 
einschalten
3. ... Zeit bis zum nächsten ISR Aufruf
4. Alle LED, Spalten aus.
5. .. alles genauso, halt nur mit der nächsten Spalte
... Zeit bis zum nächsten ISR Aufruf .. wieder zu 1.

von Karl H. (kbuchegg)


Lesenswert?

Christian Q. schrieb:
> Habe 155 ermittelt.
>
> Leider funktioniert meine Software jetzt nichtmehr. Sehe immer nur die
> Oberen LED an. Dieses Einschalten - warten - Ausschalten - warten -
> das Warten ist bei mir die Zeit zwischen der ISR.
>
> Also ich schalte im 1. Schritt
> Alle LED, Spalten aus.
> 2. je nach Anzeige den Pin der aktuellen Spalte schalten und Spalte
> einschalten
> 3. ... Zeit bis zum nächsten ISR Aufruf
> 4. Alle LED, Spalten aus.
> 5. .. alles genauso, halt nur mit der nächsten Spalte
> ... Zeit bis zum nächsten ISR Aufruf .. wieder zu 1.


Jetzt kommt mein Lieblingsspiel:

Anstatt die Dinge zu beschreiben - zeige deinen Schaltplan, zeige dein 
Programm. Keine Beschreibung die du geben könntest, zeigt so exakt was 
du tatsächlich gemacht hast wie das tatsächliche Programm/Schaltplan. 
Und einfacher ist es für dich auch :-)


Der Ablauf ist:
in der ISR
1
    alle LED aus
2
    je nachdem die Spalte mit den richtigen Werten beschicken
3
    die betreffende Spalte ein
4
    fertig - raus aus der ISR

von Nn N. (jaytharevo)


Lesenswert?

Such mal AVR CALC!

Ist ein kleines Programm welches bei solchen Berechnungen sehr schnell 
sehr nützlich ist ;).

MfG

von Christian Q. (osx)


Angehängte Dateien:

Lesenswert?

Ich bin mir sich, dass die Umrechnung klappt, liegt also nur am Inhalt 
der ISR. Würd gern auf der Ebene mit den Arrays bleiben. Es hängt nur 
beim Durchlaufen der Spalten.

Im Anhang ist die Schaltung, woebi ich hier erstmal nur wie gesagt 2x4 
(4x2?) aufgebaut habe.
1
#define F_CPU  8000000UL  //  8 MHz 
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
#include <util/delay.h>
5
6
//Variablen für die Zeit
7
volatile unsigned int  millisekunden;
8
//volatile unsigned int sekunde;
9
volatile unsigned int minute;
10
volatile unsigned int stunde;
11
12
int Zeit[6];
13
int dezimalzahl,dezimalzahl_rest,dezimalzahl_new,durchlauf = 0;
14
int bin_zeile = 4;
15
uint8_t temp_bin = 0;
16
int Zeit[6];
17
int binZeit[6][4];
18
int ledMatrix[6][4];
19
20
volatile int sekunde;
21
int timer2 = 0;
22
volatile unsigned char display_dim = 30;
23
int ausgabe_spalte, update_aufruf_spalte,ausgabe_zeile, update_aufruf_zeile;
24
25
int main(void)
26
{
27
  //PINS definieren
28
  DDRD |= (1<<(PD6)) | (1<<(PD5)) | (1<<(PD4)) | (1<<(PD3)) | (1<<(PD2)) | (1<<(PD1)) | (1<<(PD0));
29
  PORTD = 0;
30
31
  DDRB |= (1 << PB0) | (1 << PB1) | (1 << PB2) | (1 << PB3) | (1 << PB4) | (1 << PB5) | (1 << PB6) | (1 << PB7);
32
  PORTB = 0;
33
34
  //16-Bit Timer
35
  TCCR1B |= (1 << WGM12); //CTC Modus
36
  TCCR1B |= (0 << CS12) | (1 << CS11) | (0 << CS10);   //Prescaler 256
37
  OCR1A = 31249;                 
38
  OCR1B |= 155;
39
  TIMSK = (1<<OCIE1A);  
40
  TIMSK |= (1<<OCIE1B);
41
42
43
  //INT0 initialisieren
44
  GICR = (1<< INT0);
45
  MCUCR = (1<<ISC01) | (0<<ISC00);
46
47
  // Global Interrupts aktivieren
48
  sei();
49
50
  while(1)
51
  {
52
    //Einzelne Stellen extrahieren
53
    Zeit[0] = sekunde / 10;
54
    Zeit[1] = sekunde % 10;
55
    Zeit[2] = sekunde / 10;
56
    Zeit[3] = sekunde % 10;
57
    Zeit[4] = sekunde / 10;
58
    Zeit[5] = sekunde % 10;
59
60
    /*
61
  //Einzelne Stellen extrahieren
62
  Zeit[0] = stunde / 10;
63
  Zeit[1] = stunde % 10;
64
  Zeit[2] = minute / 10;
65
  Zeit[3] = minute % 10;
66
  Zeit[4] = sekunde / 10;
67
  Zeit[5] = sekunde % 10;
68
  */  
69
70
    //Stellen in Binär umwandeln und in Array schreiben
71
    for(int ix = 0; ix<6; ix++) {
72
      dezimalzahl = Zeit[ix];
73
      for (int k=0; k<6; k++) {
74
        binZeit[ix][k] = dezimalzahl & 1;
75
        dezimalzahl>>=1;
76
      }
77
    }
78
    
79
    //Binärzahlen-Array in ledMatrix einsetzen
80
    for (int spalte=0; spalte<6; spalte++) {
81
      bin_zeile = 4;
82
      for (int zeile=0; zeile<4; zeile++) {
83
        if(bin_zeile > 0)  { bin_zeile--; }
84
        ledMatrix[spalte][zeile] = binZeit[spalte][bin_zeile];
85
      }
86
    }
87
  }
88
}
89
90
ISR (TIMER1_COMPA_vect)
91
{    
92
  PORTD ^= (1<<(PD6));  
93
  millisekunden++;
94
  if(millisekunden == 1000) {
95
    millisekunden = 0;
96
    sekunde++;
97
    if(sekunde == 60)
98
    {
99
      minute++;
100
      sekunde = 0;
101
    }
102
    if(minute == 60)
103
    {
104
      stunde++;
105
      minute = 0;
106
    }
107
    if(stunde == 24)
108
    {
109
      stunde = 0;
110
    }
111
  }
112
}
113
114
ISR (TIMER1_COMPB_vect)
115
{
116
  PORTD ^= (1<< PD5);  
117
  ausgabe_spalte = update_aufruf_spalte;
118
119
  switch(ausgabe_spalte) {
120
  case 0:
121
    PORTD = (1 << PD0);
122
    break;
123
  case 1:
124
    PORTD = (1 << PD1);
125
    break;
126
  case 2:
127
    PORTD = (1 << PD2);
128
    break;
129
  case 3:
130
    PORTD = (1 << PD3);
131
    break;
132
  case 4:
133
    PORTD = (1 << PD4);
134
    break;
135
  case 5:
136
    PORTD = (1 << PD5);
137
    break;
138
  default:
139
    PORTD = 0;
140
    break;
141
  }
142
143
  //ausgabe_zeile = update_aufruf_zeile;
144
  for (int ausgabe_zeile=0; ausgabe_zeile<4; ausgabe_zeile++) {
145
    
146
    if(ausgabe_zeile == 0) { 
147
      switch(ledMatrix[ausgabe_spalte][ausgabe_zeile]) {
148
      case 0:
149
        PORTB &= (0 << PB0);
150
        break;
151
      case 1:
152
        PORTB |= (1 << PB0);
153
        break;
154
      default:
155
        PORTB = 0;
156
        break;
157
      }
158
    }
159
160
    if(ausgabe_zeile == 1) {
161
      switch(ledMatrix[ausgabe_spalte][ausgabe_zeile]) {
162
      case 0:
163
        PORTB &= (0 << PB1);
164
        break;
165
      case 1:
166
        PORTB |= (1 << PB1);
167
        break;
168
      default:
169
        PORTB = 0;
170
        break;
171
      }
172
    }
173
174
    if(ausgabe_zeile == 2) {
175
      switch(ledMatrix[ausgabe_spalte][ausgabe_zeile]) {
176
      case 0:
177
        PORTB &= (0 << PB2);
178
        break;
179
      case 1:
180
        PORTB |= (1 << PB2);
181
        break;
182
      default:
183
        PORTB = 0;
184
        break;
185
      }
186
    }
187
188
    if(ausgabe_zeile == 3) {
189
      switch(ledMatrix[ausgabe_spalte][ausgabe_zeile]) {
190
      case 0:
191
        PORTB &= (0 << PB3);
192
        break;
193
      case 1:
194
        PORTB |= (1 << PB3);
195
        break;
196
      default:
197
        PORTB = 0;
198
        break;
199
      }
200
    }
201
    if(ausgabe_zeile > 3) { 
202
      update_aufruf_spalte++;
203
    }
204
    if(update_aufruf_spalte++ > 5) { 
205
      update_aufruf_spalte = 0;
206
    }
207
  }
208
  //for (unsigned long i = 0; i < display_dim; i++) { asm volatile("nop"::); }  //  Verzögerung  
209
}
210
211
ISR(INT0_vect) {
212
  //PORTD = (1<< PD0);  
213
}

von Karl H. (kbuchegg)


Lesenswert?

Das kommt mir aber alles sehr bekannt vor!

Ich hab dir doch vor ein paar Tagen die Multiplex Routine geschrieben.
Das war tatsächlich nicht mehr als dieser 5-Zeiler!


Deine LEDs fasst du am besten als ein 6*4 Multiplex auf, bei dem einige 
LED einfach nicht besetzt sind.

von Karl H. (kbuchegg)


Lesenswert?

Christian Q. schrieb:
> der ISR. Würd gern auf der Ebene mit den Arrays bleiben.

Das ist Blödsinn.
Du machst dir nur eine Menge Mehrarbeit mit deinem 2D Array

von Christian Q. (osx)


Lesenswert?

Diesen 5-Zeiler verstehen wir nicht und  wir wollen nicht wieder alles 
mit der Umrechnung neu machen um in diese Datenstruktur zu kommen.
1
uint8_t ledMatrix[4];
2
uint8_t actLine;
3
uint8_t lineCode[] = { 1 << PB0, 1<<PB1, 1<<PB2, 1<<PB3 };
4
5
ISR (TIMER2_COMP_vect)
6
{
7
  PORTB = 0;             // aktuelle Zeile abschalten, welche es auch immer war
8
9
  actLine++;             // welches ist die nächste Zeile?
10
  if( actLine == 4 )
11
    actLine = 0;
12
13
  PORTD = ledMatrix[actLine];  // die enstprechenden Bits für diese Zeile ausgeben
14
  PORTB = lineCode[actLine];   // und die Zeilentreiber entsprechend einschalten.
15
}

Karl heinz Buchegger schrieb:
> Du brauchst jetzt noch eine Funktion, die dir in ledMatrix das jeweils
> richtige Bit setzt.
> zb wenn die LED in der 3. Spalte / 2. Zeile gesetzt werden soll, dann
> muss

von Karl H. (kbuchegg)


Lesenswert?

Christian Q. schrieb:
> Diesen 5-Zeiler verstehen wir nicht und  wir wollen nicht wieder alles
> mit der Umrechnung neu machen
Du brauchst ganze 2 Funktionen um von der Spalten/Zeilen Denkweise auf 
eine Datenstruktur zu kommen, die dir innerhalb der ISR keine 
Sonderfälle aufmacht!

2 Funktionen!

Bei denen es darum geht im richtigen Byte das richtige Bit zu setzen 
oder zu löschen! Und das ist zu schwer?

von Karl H. (kbuchegg)


Lesenswert?

Christian Q. schrieb:

>
1
> uint8_t ledMatrix[4];
2
> uint8_t actLine;
3
> uint8_t lineCode[] = { 1 << PB0, 1<<PB1, 1<<PB2, 1<<PB3 };
4
> 
5
> ISR (TIMER2_COMP_vect)
6
> {
7
>   PORTB = 0;             // aktuelle Zeile abschalten, welche es auch
8
> immer war
9
> 
10
>   actLine++;             // welches ist die nächste Zeile?
11
>   if( actLine == 4 )
12
>     actLine = 0;
13
> 
14
>   PORTD = ledMatrix[actLine];  // die enstprechenden Bits für diese
15
> Zeile ausgeben
16
>   PORTB = lineCode[actLine];   // und die Zeilentreiber entsprechend
17
> einschalten.
18
> }
19
>


Alles was du noch brauchst ist eine Funktion die aus Zeile/Spalte in 
ledMatrix das jeweils richtige Bit ein bzw. ausschaltet.

Hier ist die Zuordnung der LEDs zu den Bits im Array

  Bit    7   6   5   4   3   2   1   0
       +---+---+---+---+---+---+---+---+
       |   |   | 21| 17| 13|  9|  5|  1|  ledMatrix[0]
       +---+---+---+---+---+---+---+---+
       |   |   | 22| 18| 14| 10|  6|  2|  ledMatrix[1]
       +---+---+---+---+---+---+---+---+
       |   |   | 23| 19| 15| 11|  7|   |  ledMatrix[2]
       +---+---+---+---+---+---+---+---+
       |   |   | 24|   | 16|   |  8|   |  ledMatrix[4]
       +---+---+---+---+---+---+---+---+


Um also LED 10 (laut Schaltplan) einzuschalten, muss das Bit 2 in 
ledMatrix[1] gesetzt werden. In Spalten/Zeilen Notation ist das Zeile 1 
(wenn man bei 0 anfängt zu zählen) bzw. Spalte 2 (ebenfalls bei 0 
angefangen)

Eine Setzroutine ist also
1
void SetBit( uint8_t col, uint8_t line )
2
{
3
  ledMatrix[line] |= ( 1 << col );
4
}

und gelöscht wird genau anders rum
1
void ResetBit( uint8_t col, uint8_t line )
2
{
3
  ledMatrix[line] &= ~( 1 << col );
4
}

Und das ist jetzt schwer?

von Karl H. (kbuchegg)


Lesenswert?

Da das ganze aber, so wie es aussieht eine Binäruhr werden soll, würde 
es sich anbieten, den Multiplex genau anders rum zu machen. Also nicht 
über die Zeilen, sondern über die Spalten.
Dann hat man ein 6-er Array. Jedes Array Element steht für genau 1 
Spalte und die werden reihum ausgegeben.

Denn: dann braucht man nämlich überhaupt nicht umrechnen :-)
Jede Ziffer, die aus der Zerlegung der Zeit rauskommt, kann direkt so 
wie sie ist ausgegeben werden. Ihre jeweilige Binärdarstellung 
korrespondiert mit den LEDs.

von Karl H. (kbuchegg)


Lesenswert?

Das hier müsste dein Programm sein

Studiere es und sieh dir an, wie ich Dinge gelöst habe. Ich mach das ja 
nicht, weil es so lustig ist, sondern weil du was lernen sollst. Bringt 
doch nichts, wenn du immer nur immer wieder denselben Stiefel 
programmierst und dir selber das Leben schwer machst.
1
#define F_CPU  8000000UL  //  8 MHz 
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
#include <util/delay.h>
5
6
//Variablen für die Zeit
7
volatile unsigned int  subSec;
8
volatile uint8_t sekunde;
9
volatile uint8_t minute;
10
volatile uint8_t stunde;
11
12
uint8_t ledMatrix[6];
13
uint8_t actCol;
14
uint8_t colCode[] = { 1<<PD0, 1<<PD1, 1<<PD2, 1<<PD3, 1<<PD4, 1<<PD5 };
15
16
int main(void)
17
{
18
  //PINS definieren
19
  DDRD |= 1<<PD5 | 1<<PD4 | 1<<PD3 | 1<<PD2 | 1<<PD1 | 1<<PD0;
20
  PORTD = 0;
21
22
  DDRB |= 1<<PB3 | 1<<PB2 | 1<<PB1 | 1<<PB0;
23
  PORTB = 0;
24
25
  // 16-Bit Timer
26
  TCCR1B |= (1 << WGM12); //CTC Modus
27
  OCR1A  = 13333;                 
28
  TIMSK = (1<<OCIE1A);  
29
  TCCR1B |= (0 << CS12) | (0 << CS11) | (1 << CS10);  // Prescaler 1
30
31
32
  // Global Interrupts aktivieren
33
  sei();
34
35
  while(1)
36
  {
37
  }
38
}
39
40
ISR (TIMER1_COMPA_vect)
41
{
42
  // den Multiplexteil erledigen
43
  //
44
  PORTD = 0;             // aktuelle Spalte  abschalten, welche es auch immer war
45
 
46
  actCol++;             // welches ist die nächste Spalte?
47
  if( actCol == 6 )
48
    actCol = 0;
49
 
50
  PORTB = ledMatrix[actCol];  // die enstprechenden Bits für diese Zeile ausgeben
51
  PORTD = colCode[actCol];   // und die Spaltentreiber entsprechend einschalten.
52
53
  //
54
  // die Uhr weiterstellen falls notwendig
55
  // die ISR wird 600 mal in der Sekunde aufgerufen
56
  // 8000000 Hz
57
  // Prescaler: 1
58
  // Top Wert der CTC: 13333
59
  // 8000000 / 1 / 13333 -> 600
60
  //
61
  subSec++;
62
  if( subSec == 600 ) {
63
    subSec = 0;
64
65
    sekunde++;
66
    if( sekunde == 60 ) {
67
      sekunde = 0;
68
69
      minute++;
70
      if( minute == 60 ) {
71
        minute = 0;
72
73
        stunde++;
74
        if( stunde == 24 )
75
          stunde = 0;
76
      }
77
    }
78
79
    ledMatrix[0] = stunde / 10;
80
    ledMatrix[1] = stunde % 10;
81
82
    ledMatrix[2] = minute / 10;
83
    ledMatrix[3] = minute % 10;
84
85
    ledMatrix[4] = sekunde / 10;
86
    ledMatrix[5] = sekunde % 10;
87
  }
88
}

Disclaimer: Da ich deine Hardware nicht hier habe, kann ich das nur im 
Simulator testen. Bei 8Mhz stimmt das Timing und auch die Portpins 
schalten IMHO so wie es die Schaltung erfordert.

von Karl H. (kbuchegg)


Lesenswert?

Wenn du Verständnisprobleme hast, dann frag nach. Aber erst auf eigene 
Faust versuchen zu verstehen.

Das hier beispielsweie
1
uint8_t colCode[] = { 1<<PD0, 1<<PD1, 1<<PD2, 1<<PD3, 1<<PD4, 1<<PD5 };
2
3
...
4
  PORTD = colCode[actCol];
5
...

macht, wenn du es dir genau überlegst, genau dasselbe wie (aus deinem 
Code)
1
  switch(ausgabe_spalte) {
2
  case 0:
3
    PORTD = (1 << PD0);
4
    break;
5
  case 1:
6
    PORTD = (1 << PD1);
7
    break;
8
  case 2:
9
    PORTD = (1 << PD2);
10
    break;
11
  case 3:
12
    PORTD = (1 << PD3);
13
    break;
14
  case 4:
15
    PORTD = (1 << PD4);
16
    break;
17
  case 5:
18
    PORTD = (1 << PD5);
19
    break;
20
  default:
21
    PORTD = 0;
22
    break;
23
  }

(nur dass es bei dir ausgabe_spalte heißt und bei mir actCol).
Nur ist es kürzer und leichter zu überblicken. Aber funktional (da 
actCol nicht größer als 5 werden kann) sind sie gleichwertig! Ich hab 
einfach nur das jeweilige Bitmuster, dass bei einem bestimmten Wert 
ausgegeben werden muss, in ein Array gelegt und benutze den Wert um 
damit aus dem Array das jeweils richtige Bitmuster zu holen. Du benutzt 
den Wert um in einem switch-case den Fall anzuspringen um dort dann das 
jeweils richtige Bit auszugeben. Im Endeffekt läuft es aber auf exakt 
die gleiche Funktion an den Portpins hinaus. Je nach Wert wird ein 
anderer Portpin auf 1 gesetzt und alle anderen auf 0.

von Karl H. (kbuchegg)


Lesenswert?

Christian Q. schrieb:
> und wir wollen nicht wieder alles
> mit der Umrechnung neu machen

Und manchmal muss man auch akzeptieren, dass man bereits geschriebene 
Dinge wieder verwirft, wenn es einen guten Grund dafür gibt.

Nur Neulinge kleben an einmal geschriebenem Code fest wie das Pech im 
T-Shirt. Ich hab im Lauf der Jahre wahrscheinlich mehr Code umgearbeitet 
und wieder weggeschmissen, als dann tatsächlich übrig geblieben ist. Am 
Anfang wars mehr, mit der Zeit wirds weniger. Man lernt ja auch mit der 
Zeit was funktioniert und was nicht.

von Christian Q. (osx)


Lesenswert?

Dankeschön für deine Mühe.
Wir sind echt verwundert wie "einfach" das zu machen geht. Konnten es 
aber auch erst mit deinem vollständig angepassten Code sehen, wie nun 
letzlich das colCode[] aus dem Vorpost zum Einsatz kam.

Am meisten sind wir überrascht, dass garkeine Umrechnung nach Binär 
gebraucht wird (anscheinend übernimmt uint8_t das von allein). Wir 
denken, das war auch der Punkt warum das in zweidimensionale Arrays 
ausgeartet ist.

1 Sekunde dauert mit dem Code 8. Auf subSec == 75 geändert haut es hin. 
Oder sollte man am Timer ansetzen?

von Karl H. (kbuchegg)


Lesenswert?

Christian Q. schrieb:
> Dankeschön für deine Mühe.
> Wir sind echt verwundert wie "einfach" das zu machen geht.

Ihr habt den Fehler gemacht, euch auf ein Schema einzulassen (2D Array) 
und dann habt ihr das auf Biegen und Brechen durchgezogen ohne euch je 
zu fragen wie sinnvoll das noch ist bzw. wie man es noch anders machen 
könnte.

>
> Am meisten sind wir überrascht, dass garkeine Umrechnung nach Binär
> gebraucht wird (anscheinend übernimmt uint8_t das von allein).

IN einem Computer ist alles Binär!

Alles!
Jede Zahl, jedes Zeichen, jeder Befehl, alles!

Erst bei der Ausgabe trickst man es so hin, dass das Bitmuster 00000110 
mir dem Zeichen '6' auf einer Anzeige angezeigt wird. Oder aber man gibt 
das Bitmuster gleich auf ein paar LED einfach aus und hat dann 2 
leuchtende LED.
Ganz im Gegenteil: eine binäre Ausgabe ist immer am einfachsten. Nur 
wenn man eben nicht binär haben will, muss man Aufwand treiben.

Habt ihr denn nie ausprobiert einfach mal eine Variable hochzählen zu 
lassen und ihr Bitmuster auf einen Port mit 8 LED auszugeben?

> 1 Sekunde dauert mit dem Code 8.

Dann läuft dein µC nicht mit 8Mhz sondern nur mit 1Mhz :-)

> Oder sollte man am Timer ansetzen?
Weder noch.
Du sollst den µC auf die richtige Taktfrequenz einstellen.
Nur dadurch dass du F_CPU auf 8000000 stellst, arbeitet der µC noch 
lange nicht mit 8Mhz

von Christian Q. (osx)


Lesenswert?

Karl heinz Buchegger schrieb:
>> 1 Sekunde dauert mit dem Code 8.
>
> Dann läuft dein µC nicht mit 8Mhz sondern nur mit 1Mhz :-)

Ähm okay.. in den Projekteinstellungen steht 8Mhz. confused

Karl heinz Buchegger schrieb:
> Habt ihr denn nie ausprobiert einfach mal eine Variable hochzählen zu
> lassen und ihr Bitmuster auf einen Port mit 8 LED auszugeben?

Natürlich :) Das Prinzip des Dualen-Systems war nicht das Problem bei 
der Sache, sondern die Umsetzung. (Bitmanipulation, die Bitmuster mit 
ein paar Zeichen ^&~ einfach zu ändern)

//Edit bei den Fuses aus Versehen 8 Mhz ext. Osc statt int. ausgewählt. 
Ist er jetzt tot?

von Karl H. (kbuchegg)


Lesenswert?

Christian Q. schrieb:
> Karl heinz Buchegger schrieb:
>>> 1 Sekunde dauert mit dem Code 8.
>>
>> Dann läuft dein µC nicht mit 8Mhz sondern nur mit 1Mhz :-)
>
> Ähm okay.. in den Projekteinstellungen steht 8Mhz. confused

Auch die Projekteinstellungen sind uninteressant.
Entscheidend ist, wie die Fuses im µC selber stehen!
Die entscheiden wie schnell der µC arbeitet.

Ob Projekteinstellungen oder F_CPU selber setzen: Das hat nur 
informellen Charakter für das C-Programm.

Anders ausgedrückt:
Du kannst einem Trabi einen Tacho einbauen, auf dem 300km/h 
Spitzengeschwindigkeit draufsteht. Kannst du machen. Du kannst auch 500 
ans Ende der Skala schreiben.
Trotzdem wird der real nicht schneller als 90 fahren.
Wenn du dann allerdings ausrechnen musst wie lang du von München nach 
Berlin unterwegs bist und du benutzt deine Tachowerte, laut denen du mit 
knapp 450 Sachen unterwegs bist, dann werden die Ergebnisse nicht 
stimmen :-)

von Christian Q. (osx)


Lesenswert?

Karl heinz Buchegger schrieb:
> Anders ausgedrückt:
> Du kannst einem Trabi einen Tacho einbauen, auf dem 300km/h
> Spitzengeschwindigkeit draufsteht. Kannst du machen. Du kannst auch 500
> ans Ende der Skala schreiben.
> Trotzdem wird der real nicht schneller als 90 fahren.
> Wenn du dann allerdings ausrechnen musst wie lang du von München nach
> Berlin unterwegs bist und du benutzt deine Tachowerte, laut denen du mit
> knapp 450 Sachen unterwegs bist, dann werden die Ergebnisse nicht
> stimmen :-)

Schreibst du das immer? Kommt mir so bekannt vor. Bin aber davon 
ausgegangen, dass AVR Studio die Fuses nach den Projekteinstellungen 
belegt.

Edit bei den Fuses aus Versehen 8 Mhz ext. Osc statt int. ausgewählt.
Ist er jetzt tot? Hab ihn auf dem Pollin Eval Board. Ist doch eigentlich 
ein ext. Osc dran...

von Karl H. (kbuchegg)


Lesenswert?

Christian Q. schrieb:

> //Edit bei den Fuses aus Versehen 8 Mhz ext. Osc statt int. ausgewählt.
> Ist er jetzt tot?

http://www.mikrocontroller.net/articles/AVR_Fuses#Taktquellen_Fuse_Einstellung

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.