mikrocontroller.net

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


Autor: Christian Q. (osx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
//16-Bit Timer
TCCR1B |= (1 << WGM12); //CTC Modus
TCCR1B |= (0 << CS12) | (1 << CS11) | (0 << CS10);   //Prescaler 256
OCR1A = 34286;                 
OCR1B |= 1000;
TIMSK = (1<<OCIE1A);  
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?

Autor: spess53 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi

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

Controller, Takt?

MfG Spess

Autor: Christian Q. (osx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Oh, atmega16 - 8Mhz

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Christian Q. schrieb:
>
> //16-Bit Timer
> TCCR1B |= (1 << WGM12); //CTC Modus
> TCCR1B |= (0 << CS12) | (1 << CS11) | (0 << CS10);   //Prescaler 256
> OCR1A = 34286;
> OCR1B |= 1000;
> TIMSK = (1<<OCIE1A);
> 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?

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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Christian Q. schrieb:
> Oh, atmega16 - 8Mhz

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

Autor: Christian Q. (osx)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Hans_super_schlau (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Christian Q. schrieb:
> Wie berechne ich die Timer genau?

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

Autor: spess53 (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Christian Q. (osx)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: spess53 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi

>31250 - 1 = 31249 stimmt das?

Ja.

MfG Spess

Autor: Christian Q. (osx)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: spess53 (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Christian Q. (osx)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Christian Q. (osx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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
    alle LED aus
    je nachdem die Spalte mit den richtigen Werten beschicken
    die betreffende Spalte ein
    fertig - raus aus der ISR

Autor: Nn Nn (jaytharevo)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Such mal AVR CALC!

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

MfG

Autor: Christian Q. (osx)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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.
#define F_CPU  8000000UL  //  8 MHz 
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

//Variablen für die Zeit
volatile unsigned int  millisekunden;
//volatile unsigned int sekunde;
volatile unsigned int minute;
volatile unsigned int stunde;

int Zeit[6];
int dezimalzahl,dezimalzahl_rest,dezimalzahl_new,durchlauf = 0;
int bin_zeile = 4;
uint8_t temp_bin = 0;
int Zeit[6];
int binZeit[6][4];
int ledMatrix[6][4];

volatile int sekunde;
int timer2 = 0;
volatile unsigned char display_dim = 30;
int ausgabe_spalte, update_aufruf_spalte,ausgabe_zeile, update_aufruf_zeile;

int main(void)
{
  //PINS definieren
  DDRD |= (1<<(PD6)) | (1<<(PD5)) | (1<<(PD4)) | (1<<(PD3)) | (1<<(PD2)) | (1<<(PD1)) | (1<<(PD0));
  PORTD = 0;

  DDRB |= (1 << PB0) | (1 << PB1) | (1 << PB2) | (1 << PB3) | (1 << PB4) | (1 << PB5) | (1 << PB6) | (1 << PB7);
  PORTB = 0;

  //16-Bit Timer
  TCCR1B |= (1 << WGM12); //CTC Modus
  TCCR1B |= (0 << CS12) | (1 << CS11) | (0 << CS10);   //Prescaler 256
  OCR1A = 31249;                 
  OCR1B |= 155;
  TIMSK = (1<<OCIE1A);  
  TIMSK |= (1<<OCIE1B);


  //INT0 initialisieren
  GICR = (1<< INT0);
  MCUCR = (1<<ISC01) | (0<<ISC00);

  // Global Interrupts aktivieren
  sei();

  while(1)
  {
    //Einzelne Stellen extrahieren
    Zeit[0] = sekunde / 10;
    Zeit[1] = sekunde % 10;
    Zeit[2] = sekunde / 10;
    Zeit[3] = sekunde % 10;
    Zeit[4] = sekunde / 10;
    Zeit[5] = sekunde % 10;

    /*
  //Einzelne Stellen extrahieren
  Zeit[0] = stunde / 10;
  Zeit[1] = stunde % 10;
  Zeit[2] = minute / 10;
  Zeit[3] = minute % 10;
  Zeit[4] = sekunde / 10;
  Zeit[5] = sekunde % 10;
  */  

    //Stellen in Binär umwandeln und in Array schreiben
    for(int ix = 0; ix<6; ix++) {
      dezimalzahl = Zeit[ix];
      for (int k=0; k<6; k++) {
        binZeit[ix][k] = dezimalzahl & 1;
        dezimalzahl>>=1;
      }
    }
    
    //Binärzahlen-Array in ledMatrix einsetzen
    for (int spalte=0; spalte<6; spalte++) {
      bin_zeile = 4;
      for (int zeile=0; zeile<4; zeile++) {
        if(bin_zeile > 0)  { bin_zeile--; }
        ledMatrix[spalte][zeile] = binZeit[spalte][bin_zeile];
      }
    }
  }
}

ISR (TIMER1_COMPA_vect)
{    
  PORTD ^= (1<<(PD6));  
  millisekunden++;
  if(millisekunden == 1000) {
    millisekunden = 0;
    sekunde++;
    if(sekunde == 60)
    {
      minute++;
      sekunde = 0;
    }
    if(minute == 60)
    {
      stunde++;
      minute = 0;
    }
    if(stunde == 24)
    {
      stunde = 0;
    }
  }
}

ISR (TIMER1_COMPB_vect)
{
  PORTD ^= (1<< PD5);  
  ausgabe_spalte = update_aufruf_spalte;

  switch(ausgabe_spalte) {
  case 0:
    PORTD = (1 << PD0);
    break;
  case 1:
    PORTD = (1 << PD1);
    break;
  case 2:
    PORTD = (1 << PD2);
    break;
  case 3:
    PORTD = (1 << PD3);
    break;
  case 4:
    PORTD = (1 << PD4);
    break;
  case 5:
    PORTD = (1 << PD5);
    break;
  default:
    PORTD = 0;
    break;
  }

  //ausgabe_zeile = update_aufruf_zeile;
  for (int ausgabe_zeile=0; ausgabe_zeile<4; ausgabe_zeile++) {
    
    if(ausgabe_zeile == 0) { 
      switch(ledMatrix[ausgabe_spalte][ausgabe_zeile]) {
      case 0:
        PORTB &= (0 << PB0);
        break;
      case 1:
        PORTB |= (1 << PB0);
        break;
      default:
        PORTB = 0;
        break;
      }
    }

    if(ausgabe_zeile == 1) {
      switch(ledMatrix[ausgabe_spalte][ausgabe_zeile]) {
      case 0:
        PORTB &= (0 << PB1);
        break;
      case 1:
        PORTB |= (1 << PB1);
        break;
      default:
        PORTB = 0;
        break;
      }
    }

    if(ausgabe_zeile == 2) {
      switch(ledMatrix[ausgabe_spalte][ausgabe_zeile]) {
      case 0:
        PORTB &= (0 << PB2);
        break;
      case 1:
        PORTB |= (1 << PB2);
        break;
      default:
        PORTB = 0;
        break;
      }
    }

    if(ausgabe_zeile == 3) {
      switch(ledMatrix[ausgabe_spalte][ausgabe_zeile]) {
      case 0:
        PORTB &= (0 << PB3);
        break;
      case 1:
        PORTB |= (1 << PB3);
        break;
      default:
        PORTB = 0;
        break;
      }
    }
    if(ausgabe_zeile > 3) { 
      update_aufruf_spalte++;
    }
    if(update_aufruf_spalte++ > 5) { 
      update_aufruf_spalte = 0;
    }
  }
  //for (unsigned long i = 0; i < display_dim; i++) { asm volatile("nop"::); }  //  Verzögerung  
}

ISR(INT0_vect) {
  //PORTD = (1<< PD0);  
}

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Christian Q. (osx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Diesen 5-Zeiler verstehen wir nicht und  wir wollen nicht wieder alles 
mit der Umrechnung neu machen um in diese Datenstruktur zu kommen.
uint8_t ledMatrix[4];
uint8_t actLine;
uint8_t lineCode[] = { 1 << PB0, 1<<PB1, 1<<PB2, 1<<PB3 };

ISR (TIMER2_COMP_vect)
{
  PORTB = 0;             // aktuelle Zeile abschalten, welche es auch immer war

  actLine++;             // welches ist die nächste Zeile?
  if( actLine == 4 )
    actLine = 0;

  PORTD = ledMatrix[actLine];  // die enstprechenden Bits für diese Zeile ausgeben
  PORTB = lineCode[actLine];   // und die Zeilentreiber entsprechend einschalten.
}

Karl heinz Buchegger schrieb im Beitrag #1980138:
> 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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Christian Q. schrieb:

>
> uint8_t ledMatrix[4];
> uint8_t actLine;
> uint8_t lineCode[] = { 1 << PB0, 1<<PB1, 1<<PB2, 1<<PB3 };
> 
> ISR (TIMER2_COMP_vect)
> {
>   PORTB = 0;             // aktuelle Zeile abschalten, welche es auch
> immer war
> 
>   actLine++;             // welches ist die nächste Zeile?
>   if( actLine == 4 )
>     actLine = 0;
> 
>   PORTD = ledMatrix[actLine];  // die enstprechenden Bits für diese
> Zeile ausgeben
>   PORTB = lineCode[actLine];   // und die Zeilentreiber entsprechend
> einschalten.
> }
> 


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
void SetBit( uint8_t col, uint8_t line )
{
  ledMatrix[line] |= ( 1 << col );
}

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

Und das ist jetzt schwer?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.


#define F_CPU  8000000UL  //  8 MHz 
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

//Variablen für die Zeit
volatile unsigned int  subSec;
volatile uint8_t sekunde;
volatile uint8_t minute;
volatile uint8_t stunde;

uint8_t ledMatrix[6];
uint8_t actCol;
uint8_t colCode[] = { 1<<PD0, 1<<PD1, 1<<PD2, 1<<PD3, 1<<PD4, 1<<PD5 };

int main(void)
{
  //PINS definieren
  DDRD |= 1<<PD5 | 1<<PD4 | 1<<PD3 | 1<<PD2 | 1<<PD1 | 1<<PD0;
  PORTD = 0;

  DDRB |= 1<<PB3 | 1<<PB2 | 1<<PB1 | 1<<PB0;
  PORTB = 0;

  // 16-Bit Timer
  TCCR1B |= (1 << WGM12); //CTC Modus
  OCR1A  = 13333;                 
  TIMSK = (1<<OCIE1A);  
  TCCR1B |= (0 << CS12) | (0 << CS11) | (1 << CS10);  // Prescaler 1


  // Global Interrupts aktivieren
  sei();

  while(1)
  {
  }
}

ISR (TIMER1_COMPA_vect)
{
  // den Multiplexteil erledigen
  //
  PORTD = 0;             // aktuelle Spalte  abschalten, welche es auch immer war
 
  actCol++;             // welches ist die nächste Spalte?
  if( actCol == 6 )
    actCol = 0;
 
  PORTB = ledMatrix[actCol];  // die enstprechenden Bits für diese Zeile ausgeben
  PORTD = colCode[actCol];   // und die Spaltentreiber entsprechend einschalten.

  //
  // die Uhr weiterstellen falls notwendig
  // die ISR wird 600 mal in der Sekunde aufgerufen
  // 8000000 Hz
  // Prescaler: 1
  // Top Wert der CTC: 13333
  // 8000000 / 1 / 13333 -> 600
  //
  subSec++;
  if( subSec == 600 ) {
    subSec = 0;

    sekunde++;
    if( sekunde == 60 ) {
      sekunde = 0;

      minute++;
      if( minute == 60 ) {
        minute = 0;

        stunde++;
        if( stunde == 24 )
          stunde = 0;
      }
    }

    ledMatrix[0] = stunde / 10;
    ledMatrix[1] = stunde % 10;

    ledMatrix[2] = minute / 10;
    ledMatrix[3] = minute % 10;

    ledMatrix[4] = sekunde / 10;
    ledMatrix[5] = sekunde % 10;
  }
}

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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

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

Das hier beispielsweie
uint8_t colCode[] = { 1<<PD0, 1<<PD1, 1<<PD2, 1<<PD3, 1<<PD4, 1<<PD5 };

...
  PORTD = colCode[actCol];
...

macht, wenn du es dir genau überlegst, genau dasselbe wie (aus deinem 
Code)
  switch(ausgabe_spalte) {
  case 0:
    PORTD = (1 << PD0);
    break;
  case 1:
    PORTD = (1 << PD1);
    break;
  case 2:
    PORTD = (1 << PD2);
    break;
  case 3:
    PORTD = (1 << PD3);
    break;
  case 4:
    PORTD = (1 << PD4);
    break;
  case 5:
    PORTD = (1 << PD5);
    break;
  default:
    PORTD = 0;
    break;
  }

(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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Christian Q. (osx)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Christian Q. (osx)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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 :-)

Autor: Christian Q. (osx)
Datum:

Bewertung
0 lesenswert
nicht 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...

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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#...

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.