Forum: Compiler & IDEs Portzugriffe in eine Schleife packen


von Thomas (Gast)


Lesenswert?

Tach,
ich habe eine 7-Segment-Anzeige an einem Atmega8. Nun muss ich die 
einzelnen Segmente der Anzeigen auf verschiedene Ports aufteilen.

Über eine Reihe von defines sage ich jetzt vorher wo die Segmente 
angeschlossen sind:
1
// Portpins für die einzelnen Segmente
2
#define SEG_PORT_A  PORTD
3
#define SEG_DDR_A  DDRD
4
#define SEG_PIN_A  PD0
5
#define SEG_PORT_B  PORTD
6
#define SEG_DDR_B  DDRD
7
#define SEG_PIN_B  PD1
8
.
9
.
10
.

Eine einzelne Ziffer gebe ich bis jetzt dann so aus:
1
// Einzelne Ziffer ausgeben
2
void sev_seg_display_digit(uint8_t digit)
3
{
4
  uint8_t pattern;
5
  pattern = seg_dat[digit];
6
  
7
  if (pattern & 0x01)
8
    SEG_PORT_A |= (1 << SEG_PIN_A);
9
  else
10
    SEG_PORT_A &= ~(1 << SEG_PIN_A);
11
    
12
  if (pattern & 0x02)
13
    SEG_PORT_B |= (1 << SEG_PIN_B);
14
  else
15
    SEG_PORT_B &= ~(1 << SEG_PIN_B);
16
.
17
.
18
.
Also 10 if-else Abfragen.

Ich würde das ja ganz gerne irgendwie in einer Schleife abarbeiten, aber 
mir ist keine Lösung eingefallen. Wahrscheinlich muss ich dann aber von 
den defines weg oder?
Jemand einen Hinweis?

von Karl H. (kbuchegg)


Lesenswert?

Das kommt drauf an wie portasbel du das ganze haben willst.

> Nun muss ich die
> einzelnen Segmente der Anzeigen auf verschiedene Ports aufteilen.

Das ist schlecht. Denn das bedeutet, dass du tatsächlich
so gut wie jedes Bit (und damit jedes Segment) einzeln
behandeln musst.

Falls es dir auf Portabilität nicht ankommt, dann fass
wenigstens die Pins die an ein und demselben Port liegen
zusammen und mach jeweils eine eigene Tabelle dafür.
Dann brauchst du nur jeweils die für einen Port zuständige
Tabelle aufsuchen, holst dir mit dem auszugebenden
Digit den dafür notwendigen wert und gibst ihn auf
den Port aus.

Wenn du das Ganze allerdings voll portabel haben willst,
dann bleibt dir nichts anderes übrig, als das was du
bereits gemacht hast.

von Thomas (Gast)


Lesenswert?

Naja, ich brauche die Analog-Komparatoren, und wenn man einen Quarz auch 
braucht ist kein vollständiger Port mehr da.
Ist beim Multiplexen ja auch unschön wenn da so lange im Interrupt 
gearbeitet wird.
Ich dachte nur, dass es irgendeine Möglichkeit gäbe das in eine Schleife 
zu packen (evtl. wenn man die Portdaten in eine Struktur packt, aber das 
wär ja auch Overhead).

von Jörg X. (Gast)


Lesenswert?

Mit einer schleife ginge es so:
1
 //...
2
    uint8_t i;
3
    for(i=1; i!=0; i<<=1;) {
4
        if (pattern & i) {
5
            SETPIN();
6
        }
7
        else {
8
            CLRPIN();
9
        }
10
    }
Allerdings fehlt da die Möglichkeit, die Bits den Port zuzuordnen :(

Aber du weißt doch, welche Bits in "pattern" zu welchem Segment gehören 
und im
Optimalfall gehören Bits 'auf einer Seite' (Low-, bzw. Highbits) zu 
einem Port:
1
#define SEG_PORT_A  PORTD
2
#define SEG_DDR_A  DDRD
3
// Wenn jeweils ein Nibble zu einem Port gehört
4
// Pins PD0 bis PD3
5
#define SEG_MASK_A 0x0F
6
7
#define SEG_PORT_B  PORTB
8
#define SEG_DDR_B  DDRB
9
// Wenn jeweils ein Nibble zu einem Port gehört
10
//Pins: PB4-PB7
11
#define SEG_MASK_B 0xF0
12
void sev_seg_display_digit(uint8_t digit)
13
{
14
    uint8_t pattern;
15
    pattern = seg_dat[digit];
16
    // besser/spart RAM (#include <avr/pgmspace.h>, beim AVR-GCC): 
17
    // pattern = pgm_read_byte(segdat[digit]);
18
    SEG_PORT_A = (SEG_PORT_A & ~SEG_MASK_A) | (pattern & SEG_MASK_A);
19
    //           loesche SEG-Bits           setze neue SEG-Bits
20
    SEG_PORT_B = (SEG_PORT_B & ~SEG_MASK_B) | (pattern & SEG_MASK_B);
21
    
22
    /* Falls die Pins nicht exakt auf das High- oder Low-nibble passen:
23
      z.B. PD2-PD5*/
24
#define SEG_A_LOW_PIN 2
25
    SEG_PORT_A = (SEG_PORT_A & ~(SEG_MASK_A<<SEG_A_LOW_PIN)
26
                            | ((pattern & SEG_MASK_A)<<SEG_A_LOW_PIN);
27
    /* Das High-Nibble muss ggf. in die andere Richtung geschoben werden*/
28
}
Die schnellste Möglichkeit wäre jedoch 'pattern' zu vergrößern (z.B. als 
struct),
so dass du für jeden Port gleich das passende Byte ansprechen kannst:
1
struct pins {uint8_t port1, uint8_t port2, uint8_t port3};
2
const struct pins segdat[] = {{0x00, 0x02,0x40}, ...};
3
// um SRAM zu sparen dann: "struct digit PROGMEM segdat[] = "
4
struct pins pattern;
5
pattern = segdat[digit];
6
// in MASKx ist jedes Bit gesetzt, das zu einem segment gehört
7
PORTA = (PORTA & ~(MASKA) | pattern.port1);
8
PORTB = (PORTB & ~(MASKB) | pattern.port2);
9
//etc.

hth. Jörg

von Peter D. (peda)


Lesenswert?

Thomas wrote:
> Ist beim Multiplexen ja auch unschön wenn da so lange im Interrupt
> gearbeitet wird.

Dann bewirkt ne Schleife nur genau das Gegenteil, der Code dauert 
wesentlich länger und wird warscheinlich auch eher länger als kürzer.

Code und Zeit kannst Du aber sparen, indem Du den else-Zweig immer 
ausführst. Der Bittest ist ein Skipbefehl und damit sparst Du 2 RJMP 
Befehle ein. Es stört auch nicht, wenn jede LED kurzzeitig ausgeschaltet 
wird.


Um den Code besser lesbar zu machen, würde ich Dir aber raten, Bitfelder 
zu benutzen. Ein Beispiel findest Du hier:

Beitrag "LCD Treiber Routine 4*40"

Da ist dann auch die kürzere Bitsetz/-lösch Syntax zu sehen.


Peter

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.