www.mikrocontroller.net

Forum: Compiler & IDEs 2 x 7Seg Steuerung


Autor: Richard Brose (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen,

ich baue einen 2 Stelligen Timer der jede Sekunde hochzählt.
Jetzt habe ich aber ein Verständnis-Problem.

Wie kann ich die beiden Segmente im Code unterscheiden? Die Segmente 
laufen ihm Multiplexing.
Momentan ist es so das beide Segmente von 0 bis 9 hochzählen und wieder 
von vorne.

Wie mache ich das so das Segment 1 von 0 bis 9 hochzählt und Segment 2 
dann von 1 bis 9 hochzählt wenn Segment 1 bei 9 ist? also getrennt:

Beispiel

2    1
-------
     0
     1
     2
     3
     4
     5
     6
     7
     8
     9
1    0
1    1
1    2

..... usw.


Hier mein aktueller Code:
/*
#  Autor: Richard Brose
#   Title: eClock V0.1
*/

/*
7-SEG:
   PB0 - 1-e
   PB1 - 2-d
   PB2 - 4-c
   PB3 - 6-b
   PB4 - 7-a
   PB5 - 9-f
   PB6 - 10-g         
*/

#include <avr/io.h>
#include <stdint.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>

#ifndef F_CPU
#define F_CPU   4000000     
#endif

#include <util/delay.h>   


#define IRQS_PER_SECOND   8000  /* 500 us */

#define IRQS_PER_10MS     (IRQS_PER_SECOND / 100)

#if (F_CPU/IRQS_PER_SECOND > 65536) || (IRQS_PER_10MS < 1) || (IRQS_PER_10MS > 255)
#   error Diese Werte fuer F_CPU und IRQS_PER_SECOND
#   error sind ausserhalb des gueltigen Bereichs!
#endif


#if (F_CPU % IRQS_PER_SECOND != 0) || (IRQS_PER_SECOND % 100 != 0)
#   warning Das Programm arbeitet nicht mit optimaler Genauigkeit.
#endif

// Prototypen
void wait_10ms (const uint8_t);
void init_timer0(void);
void init_timer1(void);

// Zähler-Variable. Wird in der ISR erniedrigt und in wait_10ms benutzt.
static volatile uint8_t timer_10ms;

#define timer (256-F_CPU/64/1000)
int ms; 

volatile uint8_t toggleSecment;



const uint8_t c_led[10] PROGMEM = {0b00111111,  //0
           0b00001100,  //1
           0b01011011,  //2
           0b01011110,  //3
           0b01101100,  //4
           0b01110110,  //5
           0b01110111,  //6
           0b00011100,  //7
           0b01111111,  //8
           0b01111110,};//9


/* Einfache Funktion zum Entprellen eines Tasters */
inline uint8_t debounce(volatile uint8_t *port, uint8_t pin)
{
    if ( ! (*port & (1 << pin)) )
    {
        /* Pin wurde auf Masse gezogen, 100ms warten   */
        _delay_ms(100);
        if ( *port & (1 << pin) )
        {
            /* Anwender Zeit zum Loslassen des Tasters geben */
            _delay_ms(100);
            return 1;
        }
    }
    return 0;
}

volatile uint8_t countTimer0;  


void wait_10ms (const uint8_t t)
{
    timer_10ms = t;
    while (timer_10ms);
}


//////////////////////////////////////////////////////////////////////
// Die Interrupt Service Routine (ISR).
// In interrupt_num_10ms werden die IRQs gezählt.
// Sind IRQS_PER_10MS Interrups geschehen, 
// dann sind 10 ms vergangen.
// timer_10ms wird alle 10 ms um 1 vermindert und bleibt bei 0 stehen.
SIGNAL (SIG_OUTPUT_COMPARE1A)
{
    static uint8_t interrupt_num_10ms;

    // interrupt_num_10ms erhöhen und mit Maximalwert vergleichen
    if (++interrupt_num_10ms == IRQS_PER_10MS)
    {
        // 10 Millisekunden sind vorbei
        // interrupt_num_10ms zurücksetzen
        interrupt_num_10ms = 0;

        // Alle 10ms wird timer_10ms erniedrigt, falls es nicht schon 0 ist.
        // Wird verwendet in wait_10ms
        if (timer_10ms != 0)
            timer_10ms--;
    }
}



ISR(SIG_TIMER0_OVF)                                 //Timer Interrupt Vector
{
 //TCNT0 = timer;
  ms++;
if(ms > 100) {
  if(toggleSecment) {
     // deaktiviere 7-SEG nr. 1
     PORTD &= ~(1 << PD4);
     // aktive 7-SEG nr. 2
     PORTD |= (1 << PD5);
    toggleSecment=0;
   }
   else
   {
     PORTD |= (1 << PD4);
     // aktive 7-SEG nr. 2
     PORTD &= ~(1 << PD5);
    toggleSecment=1;
   }
   ms=0;
 }
} 

void init_timer0(void)
{
    /* Timer0 ohne Prescaler starten */
 //   TCCR0A |= (1 << CS00) | (1 << CS02);
   TCCR0B |= (1 << CS00);
 
    /* Timer0-Overflow-Interrupt aktivieren */
    TIMSK |= (1 << TOIE0);
}

void init_timer1(void)
{
  /*TCCR1A |= (1<<WGM12);
  TCCR1B |= (1<<CS10); // PS = 1
  OCR1A = 39999; // 1 sec
  
  TIMSK |= (1 << OCIE1A ); */
  
      // Timer1: keine PWM
    TCCR1A = 0;

    // Timer1 ist Zähler: Clear Timer on Compare Match (CTC, Mode #4)
    // Timer1 läuft mit vollem MCU-Takt: Prescale = 1
#if defined (CTC1) && !defined (WGM12)
   TCCR1B = (1 << CTC1)  | (1 << CS10);
#elif !defined (CTC1) && defined (WGM12)
   TCCR1B = (1 << WGM12) | (1 << CS10);
#else
#error Keine Ahnung, wie Timer1 fuer diesen AVR zu initialisieren ist!
#endif
      // OutputCompare für gewünschte Timer1 Frequenz
    // TCNT1 zählt immer 0...OCR1A, 0...OCR1A, ... 
    // Beim überlauf OCR1A -> OCR1A+1 wird TCNT1=0 gesetzt und im nächsten
    // MCU-Takt eine IRQ erzeugt.
    OCR1A = (unsigned short) ((unsigned long) F_CPU / IRQS_PER_SECOND-1);

    // OutputCompareA-Interrupt für Timer1 aktivieren
#if defined (TIMSK1)
    TIMSK1 |= (1 << OCIE1A);
#elif defined (TIMSK)
    TIMSK  |= (1 << OCIE1A);
#else   
#error Keine Ahnung, wie IRQs fuer diesen AVR zu initialisieren sind!
#endif
}


int main(void) 
{
 // port init
 DDRB = 0xff;   //output
 PORTB = 0x00;    //GND
 
 DDRD |= (1 << PD4) | (1 << PD5);   //PD4 und PD5 7-SEG Ein/aus
 DDRD &= ~(1 << PD0); //Input
 DDRD &= ~(1 << PD1); //Input

 PORTD |= (1 << PD0) | (1 << PD1);  // Pull-Up Widerstand aktivieren


 // deaktiviere 7-SEG nr. 1
 PORTD &= ~(1 << PD4);
 // aktive 7-SEG nr. 2
 PORTD |= (1 << PD5);
 
  
 toggleSecment=0;
  uint8_t i=0;
 
 //PORTB = pgm_read_byte(&c_led[i]);
 init_timer0();
 init_timer1();
 sei();

 while(1) 
 {
   
     wait_10ms (100);
   PORTB = pgm_read_byte(&c_led[i]);
   i++;
   if(i > 9)
   {
    i=0;
   }
      
       
 }
 return 1;
}




Vielen dank im voraus.

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ Richard Brose (Gast)

>ich baue einen 2 Stelligen Timer der jede Sekunde hochzählt.
>Jetzt habe ich aber ein Verständnis-Problem.

>Wie kann ich die beiden Segmente im Code unterscheiden? Die Segmente
>laufen ihm Multiplexing.

Indem man für jedes Digit (nicht Segmente, das sind die inzelnen Balken) 
eine Variable nutzt?

>Momentan ist es so das beide Segmente von 0 bis 9 hochzählen und wieder
>von vorne.

>Wie mache ich das so das Segment 1 von 0 bis 9 hochzählt und Segment 2
>dann von 1 bis 9 hochzählt wenn Segment 1 bei 9 ist? also getrennt:

Du hast den Code nicht ein bisschen verstanden und nur 100% kopiert. Da 
musst du wohl oder übel programmieren lernen.

AVR-Tutorial: 7-Segment-Anzeige

Ist zwar Assembler, spielt für das Grundverständnis aber kleine Rolle.

MfG
Falk

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

Bewertung
0 lesenswert
nicht lesenswert
Tja, es reicht nicht, innerhalb der ISR nur eine andere
Anzeige einzuschalten. Du wirst wohl oder übel das für
diese Anzeige vorgesehene Leuchtmuster beim Wechsel
von einer Anzeige zur nächsten umschalten müssen.

Momentan machst du das ja in main()

   PORTB = pgm_read_byte(&c_led[i]);

Hier bestimmst du welche LED in der Anzeige aufleuchten
sollen. Aber das gilt ja für alle Anzeigen, weil die
ISR ja nur eine Anzeige nach der anderen einschaltet und
auf allen taucht dann das an PORTB eingestellte Muster
auf.
Da du aber nicht auf allen Anzeigen dasselbe Muster haben
möchtest, musst du logischerweise beim Wechsel der Anzeigen
auch das auszugebende Muster wechseln.

Da die ISR die auszugebenden Muster sich nicht aus den Fingern
suagen können wird, wirst du wohl ein paar globale Variablen
dafür bereitstellen müssen. Für jede Anzeigestelle eine. In
jeder einzelnen dieser Variablen ist das auzugebende Leuchtmuster
für jeweils eine Anzeigenstelle enthalten. Und in der ISR wird
zunächst reihum eine Anzeige nach der nächsten reihum aktiviert,
wobei dieses zu geschehen hat:
  * aktuelle Anzeige ausschalten
  * neues Lichtmuster aus der zuständigen Variable auf PORTB
    ausgeben
  * die nächste Anzeige aktivieren

Jetzt hast du in deinem Beispiel 2 Variablen, in die das Lichtmuster
für die jeweilige Stelle geschrieben werden muss, damit die ISR dann
auf der Anzeige anzeigt. Aber wie kriegt man die, wenn man (wie
in deinem Fall) einen int hat.

   Zehner = i / 10;
   Einer  = i % 10;

Mit diesen Zahlen, die im Bereich 0 bis 9 sind, bzw. sein sollten
(i nicht größer als 99 werden lassen) gehst du dann einzeln in deine
Code-Tabelle, von der du das Lichtmuster für die jeweilige Anzeige
bekommst:
uint8_t  Stelle1;
uint8_t  Stelle2;

....

ISR( ... )
{
  if( .... ) {
    ...
    PORTB = Stelle1;
    ...
  }
  else {
    ...
    PORTB = Stelle2;
    ...
  }
}

int main()
{
  ...


   Zehner = i / 10;
   Einer  = i % 10;

   Stelle1 = pgm_read_byte(&c_led[Zehner]);
   Stelle2 = pgm_read_byte(&c_led[Einer]);

   i++;
   if( i > 99 )
     i = 0;

   ...
}

Autor: Richard Brose (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vielen Dank,

hab das in zwischen so gelöst
ISR(SIG_TIMER0_OVF)                                 //Timer Interrupt Vector
{
 //TCNT0 = timer;
  ms++;
if(ms > 100) {
  if(toggleSecment) {
    PORTB=currentSeg2;
     // deaktiviere 7-SEG nr. 1
     PORTD &= ~(1 << PD4);
     // aktive 7-SEG nr. 2
     PORTD |= (1 << PD5);
    toggleSecment=0;
   }
   else
   {
     PORTB=currentSeg1;
     PORTD |= (1 << PD4);
     
     PORTD &= ~(1 << PD5);
    toggleSecment=1;
   }
   ms=0;
 }
} 


while(1) 
 {             
     wait_10ms (100);   
   currentSeg1 = pgm_read_byte(&c_led[i]);   
   if(i == 0)
   {
    i=10;    
    x--;
    currentSeg2 = pgm_read_byte(&c_led[x]);
    if(x == 0)
    {
      x=10;
    }
   }
   i--;             
 }



Aber die Idee mit Zehner und einer ist super. Vielen Dank dafür.

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.