Forum: Compiler & IDEs 2 x 7Seg Steuerung


von Richard Brose (Gast)


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:
1
/*
2
#  Autor: Richard Brose
3
#   Title: eClock V0.1
4
*/
5
6
/*
7
7-SEG:
8
   PB0 - 1-e
9
   PB1 - 2-d
10
   PB2 - 4-c
11
   PB3 - 6-b
12
   PB4 - 7-a
13
   PB5 - 9-f
14
   PB6 - 10-g         
15
*/
16
17
#include <avr/io.h>
18
#include <stdint.h>
19
#include <avr/pgmspace.h>
20
#include <avr/interrupt.h>
21
22
#ifndef F_CPU
23
#define F_CPU   4000000     
24
#endif
25
26
#include <util/delay.h>   
27
28
29
#define IRQS_PER_SECOND   8000  /* 500 us */
30
31
#define IRQS_PER_10MS     (IRQS_PER_SECOND / 100)
32
33
#if (F_CPU/IRQS_PER_SECOND > 65536) || (IRQS_PER_10MS < 1) || (IRQS_PER_10MS > 255)
34
#   error Diese Werte fuer F_CPU und IRQS_PER_SECOND
35
#   error sind ausserhalb des gueltigen Bereichs!
36
#endif
37
38
39
#if (F_CPU % IRQS_PER_SECOND != 0) || (IRQS_PER_SECOND % 100 != 0)
40
#   warning Das Programm arbeitet nicht mit optimaler Genauigkeit.
41
#endif
42
43
// Prototypen
44
void wait_10ms (const uint8_t);
45
void init_timer0(void);
46
void init_timer1(void);
47
48
// Zähler-Variable. Wird in der ISR erniedrigt und in wait_10ms benutzt.
49
static volatile uint8_t timer_10ms;
50
51
#define timer (256-F_CPU/64/1000)
52
int ms; 
53
54
volatile uint8_t toggleSecment;
55
56
57
58
const uint8_t c_led[10] PROGMEM = {0b00111111,  //0
59
           0b00001100,  //1
60
           0b01011011,  //2
61
           0b01011110,  //3
62
           0b01101100,  //4
63
           0b01110110,  //5
64
           0b01110111,  //6
65
           0b00011100,  //7
66
           0b01111111,  //8
67
           0b01111110,};//9
68
69
70
/* Einfache Funktion zum Entprellen eines Tasters */
71
inline uint8_t debounce(volatile uint8_t *port, uint8_t pin)
72
{
73
    if ( ! (*port & (1 << pin)) )
74
    {
75
        /* Pin wurde auf Masse gezogen, 100ms warten   */
76
        _delay_ms(100);
77
        if ( *port & (1 << pin) )
78
        {
79
            /* Anwender Zeit zum Loslassen des Tasters geben */
80
            _delay_ms(100);
81
            return 1;
82
        }
83
    }
84
    return 0;
85
}
86
87
volatile uint8_t countTimer0;  
88
89
90
void wait_10ms (const uint8_t t)
91
{
92
    timer_10ms = t;
93
    while (timer_10ms);
94
}
95
96
97
//////////////////////////////////////////////////////////////////////
98
// Die Interrupt Service Routine (ISR).
99
// In interrupt_num_10ms werden die IRQs gezählt.
100
// Sind IRQS_PER_10MS Interrups geschehen, 
101
// dann sind 10 ms vergangen.
102
// timer_10ms wird alle 10 ms um 1 vermindert und bleibt bei 0 stehen.
103
SIGNAL (SIG_OUTPUT_COMPARE1A)
104
{
105
    static uint8_t interrupt_num_10ms;
106
107
    // interrupt_num_10ms erhöhen und mit Maximalwert vergleichen
108
    if (++interrupt_num_10ms == IRQS_PER_10MS)
109
    {
110
        // 10 Millisekunden sind vorbei
111
        // interrupt_num_10ms zurücksetzen
112
        interrupt_num_10ms = 0;
113
114
        // Alle 10ms wird timer_10ms erniedrigt, falls es nicht schon 0 ist.
115
        // Wird verwendet in wait_10ms
116
        if (timer_10ms != 0)
117
            timer_10ms--;
118
    }
119
}
120
121
122
123
ISR(SIG_TIMER0_OVF)                                 //Timer Interrupt Vector
124
{
125
 //TCNT0 = timer;
126
  ms++;
127
if(ms > 100) {
128
  if(toggleSecment) {
129
     // deaktiviere 7-SEG nr. 1
130
     PORTD &= ~(1 << PD4);
131
     // aktive 7-SEG nr. 2
132
     PORTD |= (1 << PD5);
133
    toggleSecment=0;
134
   }
135
   else
136
   {
137
     PORTD |= (1 << PD4);
138
     // aktive 7-SEG nr. 2
139
     PORTD &= ~(1 << PD5);
140
    toggleSecment=1;
141
   }
142
   ms=0;
143
 }
144
} 
145
146
void init_timer0(void)
147
{
148
    /* Timer0 ohne Prescaler starten */
149
 //   TCCR0A |= (1 << CS00) | (1 << CS02);
150
   TCCR0B |= (1 << CS00);
151
 
152
    /* Timer0-Overflow-Interrupt aktivieren */
153
    TIMSK |= (1 << TOIE0);
154
}
155
156
void init_timer1(void)
157
{
158
  /*TCCR1A |= (1<<WGM12);
159
  TCCR1B |= (1<<CS10); // PS = 1
160
  OCR1A = 39999; // 1 sec
161
  
162
  TIMSK |= (1 << OCIE1A ); */
163
  
164
      // Timer1: keine PWM
165
    TCCR1A = 0;
166
167
    // Timer1 ist Zähler: Clear Timer on Compare Match (CTC, Mode #4)
168
    // Timer1 läuft mit vollem MCU-Takt: Prescale = 1
169
#if defined (CTC1) && !defined (WGM12)
170
   TCCR1B = (1 << CTC1)  | (1 << CS10);
171
#elif !defined (CTC1) && defined (WGM12)
172
   TCCR1B = (1 << WGM12) | (1 << CS10);
173
#else
174
#error Keine Ahnung, wie Timer1 fuer diesen AVR zu initialisieren ist!
175
#endif
176
      // OutputCompare für gewünschte Timer1 Frequenz
177
    // TCNT1 zählt immer 0...OCR1A, 0...OCR1A, ... 
178
    // Beim überlauf OCR1A -> OCR1A+1 wird TCNT1=0 gesetzt und im nächsten
179
    // MCU-Takt eine IRQ erzeugt.
180
    OCR1A = (unsigned short) ((unsigned long) F_CPU / IRQS_PER_SECOND-1);
181
182
    // OutputCompareA-Interrupt für Timer1 aktivieren
183
#if defined (TIMSK1)
184
    TIMSK1 |= (1 << OCIE1A);
185
#elif defined (TIMSK)
186
    TIMSK  |= (1 << OCIE1A);
187
#else   
188
#error Keine Ahnung, wie IRQs fuer diesen AVR zu initialisieren sind!
189
#endif
190
}
191
192
193
int main(void) 
194
{
195
 // port init
196
 DDRB = 0xff;   //output
197
 PORTB = 0x00;    //GND
198
 
199
 DDRD |= (1 << PD4) | (1 << PD5);   //PD4 und PD5 7-SEG Ein/aus
200
 DDRD &= ~(1 << PD0); //Input
201
 DDRD &= ~(1 << PD1); //Input
202
203
 PORTD |= (1 << PD0) | (1 << PD1);  // Pull-Up Widerstand aktivieren
204
205
206
 // deaktiviere 7-SEG nr. 1
207
 PORTD &= ~(1 << PD4);
208
 // aktive 7-SEG nr. 2
209
 PORTD |= (1 << PD5);
210
 
211
  
212
 toggleSecment=0;
213
  uint8_t i=0;
214
 
215
 //PORTB = pgm_read_byte(&c_led[i]);
216
 init_timer0();
217
 init_timer1();
218
 sei();
219
220
 while(1) 
221
 {
222
   
223
     wait_10ms (100);
224
   PORTB = pgm_read_byte(&c_led[i]);
225
   i++;
226
   if(i > 9)
227
   {
228
    i=0;
229
   }
230
      
231
       
232
 }
233
 return 1;
234
}


Vielen dank im voraus.

von Falk B. (falk)


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

von Karl H. (kbuchegg)


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:
1
uint8_t  Stelle1;
2
uint8_t  Stelle2;
3
4
....
5
6
ISR( ... )
7
{
8
  if( .... ) {
9
    ...
10
    PORTB = Stelle1;
11
    ...
12
  }
13
  else {
14
    ...
15
    PORTB = Stelle2;
16
    ...
17
  }
18
}
19
20
int main()
21
{
22
  ...
23
24
25
   Zehner = i / 10;
26
   Einer  = i % 10;
27
28
   Stelle1 = pgm_read_byte(&c_led[Zehner]);
29
   Stelle2 = pgm_read_byte(&c_led[Einer]);
30
31
   i++;
32
   if( i > 99 )
33
     i = 0;
34
35
   ...
36
}

von Richard Brose (Gast)


Lesenswert?

Vielen Dank,

hab das in zwischen so gelöst
1
ISR(SIG_TIMER0_OVF)                                 //Timer Interrupt Vector
2
{
3
 //TCNT0 = timer;
4
  ms++;
5
if(ms > 100) {
6
  if(toggleSecment) {
7
    PORTB=currentSeg2;
8
     // deaktiviere 7-SEG nr. 1
9
     PORTD &= ~(1 << PD4);
10
     // aktive 7-SEG nr. 2
11
     PORTD |= (1 << PD5);
12
    toggleSecment=0;
13
   }
14
   else
15
   {
16
     PORTB=currentSeg1;
17
     PORTD |= (1 << PD4);
18
     
19
     PORTD &= ~(1 << PD5);
20
    toggleSecment=1;
21
   }
22
   ms=0;
23
 }
24
} 
25
26
27
while(1) 
28
 {             
29
     wait_10ms (100);   
30
   currentSeg1 = pgm_read_byte(&c_led[i]);   
31
   if(i == 0)
32
   {
33
    i=10;    
34
    x--;
35
    currentSeg2 = pgm_read_byte(&c_led[x]);
36
    if(x == 0)
37
    {
38
      x=10;
39
    }
40
   }
41
   i--;             
42
 }


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

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.