Forum: Compiler & IDEs Port Konfigration zur Laufzeit festlegen


von Jürgen S. (jsachs)


Lesenswert?

Hallo,

ich hab eher ein Lösungsansatz Problem.

Ich weis auch nicht ob ich es so Verständlich Beschreibe...

Ich will im Programm meine Port Konfiguration festlegen.
All Ports sind intern durch Nummeriert. Im Programm arbeite ich nur mit 
Nummer. Hintergrund ist der, das das gleiche Programm praktisch 
unverändert in vielen Platinen läuft. Der Einzige Unterschied ist die 
Anordnung der Ausgänge auf der Platine. Es muss also nicht zur Laufzeit 
geändert werden, nur zur Compilezeit.

Bisher habe ich eine Funktion und eine Structur die das machen, aber das 
brauch natürlich Speicher.
Vor der main Loop habe ich etwas das stehen:
1
    setOutputConfig ( 7, OUT_PORTB, PB7 );
2
    setOutputConfig ( 6, OUT_PORTB, PB6 );
3
    setOutputConfig ( 5, OUT_PORTB, PB5 );
4
    setOutputConfig ( 4, OUT_PORTB, PB4 );
5
    setOutputConfig ( 3, OUT_PORTB, PB3 );
6
    setOutputConfig ( 2, OUT_PORTB, PB2 );
7
    setOutputConfig ( 1, OUT_PORTB, PB1 );
8
    setOutputConfig ( 0, OUT_PORTB, PB0 );
setOutputConfig sieht wie folgt aus, wobei das noch irrelevant ist:
1
void setOutputConfig(uint8_t chan, uint8_t port, uint8_t pin)
2
{
3
  outputConfig[chan].min_val   = INT_VAL_MIN;
4
  outputConfig[chan].max_val   = INT_VAL_MAX;
5
  outputConfig[chan].cur_val   = (((outputConfig[chan].max_val-outputConfig[chan].min_val)/2)+outputConfig[chan].min_val);
6
  outputConfig[chan].port   = port;
7
  outputConfig[chan].pinval  = _BV(pin);
8
  setOutputFunc(chan, OUT_TYPE_NONE);
9
}
Danach rufe initOutputs() auf was mir die Ports configuriert:
1
void initOutputs(void)
2
{
3
  for (uint8_t chan=0 ; chan<MAX_CHANNELS ; chan++)
4
  {
5
    uint8_t port = outputConfig[chan].port;
6
    uint8_t pinval = outputConfig[chan].pinval;
7
    
8
    if (port==OUT_PORTA)
9
    {
10
      DDRA|=pinval;
11
      PORTA&=~pinval;  // port low
12
    }
13
    else if (port==OUT_PORTB)
14
    {
15
      DDRB|=pinval;
16
      PORTB&=~pinval;  // port low
17
    }
18
    else if (port==OUT_PORTC)
19
    {
20
      DDRC|=pinval;
21
      PORTC&=~pinval;  // port low
22
    }
23
    else if (port==OUT_PORTD)
24
    {
25
      DDRD|=pinval;
26
      PORTD&=~pinval;  // port low
27
    }
28
  }
29
}
und setOutputChannel() um den Ausgang an zu steuern
1
inline void setOutputChannel(uint8_t chan, uint8_t val)
2
{
3
  uint8_t port = outputConfig[chan].port;
4
  uint8_t pinval = outputConfig[chan].pinval;
5
6
  if ( port == OUT_PORTA )
7
  {
8
    if ( val ) PORTA |= pinval;
9
    else PORTA &= ~pinval;
10
  }
11
  else if ( port == OUT_PORTB )
12
  {
13
    if ( val ) PORTB |= pinval;
14
    else PORTB &= ~pinval;
15
  }
16
  else if ( port == OUT_PORTC )
17
  {
18
    if ( val ) PORTC |= pinval;
19
    else PORTC &= ~pinval;
20
  }
21
  else if ( port == OUT_PORTD )
22
  {
23
    if ( val ) PORTD |= pinval;
24
    else PORTD &= ~pinval;
25
  }
26
}

Ich verdrehe mir gerade die Gehirnwindungen das irgendwie Statisch zur 
Compilezeit zu machen.
Hat mir da jemand einen Tipp ?

Achja, das ganze unter avr-gcc und derzeit mit einem atmega32.

Danke
Juergen

von Marius W. (mw1987)


Lesenswert?

Warum keine Header-Datei, in der die Zuordnung steht? Also sowas zum 
Beispiel:
1
#define GPIO0_PORT   PORTB
2
#define GPIO0_DDR    DDRB
3
#define GPIO0_PIN    PB0

Dann kannst du in deinem Programm ganz allgemein darauf zugreifen. Den 
GPIO auf Ausgang setzen geht dann so:
1
GPIO0_DDR |= (1 << GPIO0_PIN)

Mit etwas Makro-Frickelei kann man das auch noch schöner schreiben.

MfG
Marius

von Jürgen S. (jsachs)


Lesenswert?

Hallo,

das Problem ist bei Macros, dass ich dort nicht per Index darauf 
Zugreifen kann.
Ich hab einige Laufzeitparameter  die in einem Array stehen. Diese 
erreiche ich über den gleichen Index wie die Ports bisher

vereinfacht:
port[channel] = 0xff;
port[channel] = 0xfe;

Da ich im Moment bis zu 24 "channels" habe, wird das mit Macros 
unübersichtlich. und das ganze in einem switch case auswerten alla:
1
switch(channel)
2
{
3
 case 0:
4
  PORT_0 |= _BV(PIN_0);
5
  break;
6
 case .....
ist mühsam. Auch wenn ich dann mal nur 20 channels habe, muss ich schon 
den switch anpassen. Bisher nicht. Nicht genutzte pins sind eben nicht 
genutzt.

Was ich suche ist quasi ein Array, das zur Laufzeit "aufgelöst" wird.

Ich grüble auch ob ich das Array nicht "einfach" in den Flash schieben 
kann, so wie Strings. Ich befürchte nur das dies langsam wird.
Und ich gebe hier Servosignale aus...

Ich hätte dann etwas wie hier:
1
struct _sChannel
2
{
3
 uint8_t port;
4
 uint8_t pin;
5
};
6
7
struct _sChannel channels[] = {
8
 {OUT_PORT_A, PA1},
9
 {OUT_PORT_B, PB1},
10
 {OUT_PORT_A, PA0}
11
}
Jetzt das ganze noch ins flash und nur den Zugriff von 
outputconfig[chan] auf die hier umbiegen mit read_byte(), der Rest der 
Funktionen bleibt. Prickelnd ist das nicht. Und ob ich ein Structurarray 
in den Flash bekomme und wie ich darauf Zugreife, grübel....

Gruss
Juergen

von Karl H. (kbuchegg)


Lesenswert?

Das einzige was mir einfällt ist, dass du mehr mit Pointern arbeitest
1
struct portConfig
2
{
3
  volatile uint8_t * ddr;
4
  volatile uint8_t * port;
5
  volatile uint8_t * pin;
6
           uint8_t   pinMask;
7
};
8
9
#define MAX_CHANNELS 2
10
struct portConfig outputConfig[MAX_CHANNELS] =
11
{
12
 { &DDRA, &PORTA, &PINA, 1 << PA0 },
13
 { &DDRB, &PORTB, &PINB, 1 << PB7 }
14
};
15
16
17
void initOutputs(void)
18
{
19
  for (uint8_t chan=0; chan<MAX_CHANNELS; chan++)
20
  {
21
    *(outputConfig[chan].ddr) |= outputConfig[chan].pinMask;
22
    *(outputConfig[chan].ddr) |= outputConfig[chan].pinMask;
23
  }
24
}
25
26
inline void setOutputChannel(uint8_t chan, uint8_t val)
27
{
28
  if( val )
29
    *(outputConfig[chan].port) |=  outputConfig[chan].pinMask;
30
  else
31
    *(outputConfig[chan].port) &= ~outputConfig[chan].pinMask;
32
}

Schön ist es allerdings nicht unbedingt, weil du dem Compiler jegliche 
Möglichkeit zur Optimierung auf den I/O Operationen raubst, dürfte 
allerdings im Vergleich zu den if-then-else Orgien auch nicht schlechter 
sein.

von Karl H. (kbuchegg)


Lesenswert?

> das Problem ist bei Macros, dass ich dort nicht per
> Index darauf Zugreifen kann.

Das müsstest du dir bei einer Makro Lösung allerdings abschminken.

Allerdings sollte man sich überlegen, ob man wirklich einen Index 
braucht. Wenn du die Zuordnungen nur zur Compilezeit flexibel brauchst, 
dann musst du im Programm auch jeden Kanal tatsächlich einzeln als 
solchen ansprechen und nicht über einen Index.

von Jürgen S. (jsachs)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Das einzige was mir einfällt ist, dass du mehr mit Pointern arbeitest
>
>
1
> struct portConfig
2
> {
3
>   volatile uint8_t * ddr;
4
>   volatile uint8_t * port;
5
>   volatile uint8_t * pin;
6
>            uint8_t   pinMask;
7
> };
8
>

So hab ich halt 7 (3*2+1) Byte Speicher pro Port verbraucht. Daher habe 
ich nur die "Portreferenz" gespeichert und den if Block gebaut. So sind 
es nur 2 Byte.

>
1
> #define MAX_CHANNELS 2
2
> struct portConfig outputConfig[MAX_CHANNELS] =
3
> {
4
>  { &DDRA, &PORTA, &PINA, 1 << PA0 },
5
>  { &DDRB, &PORTB, &PINB, 1 << PB7 }
6
> };
7
> 
8
> 
9
> void initOutputs(void)
10
> {
11
>   for (uint8_t chan=0; chan<MAX_CHANNELS; chan++)
12
>   {
13
>     *(outputConfig[chan].ddr) |= outputConfig[chan].pinMask;
14
>     *(outputConfig[chan].ddr) |= outputConfig[chan].pinMask;
15
>   }
16
> }
17
>

Ist e shie rnich so, das der compiler, vor allem durch volatile, 
mehrfach auf den Struct zugreift ? Und so 2 Speicherzugriffe notwendig 
sind ??? Erst die Struct ermitteln, dann den Offset addieren, dann den 
Zugriff.
Zusätzlich muss er ja erst lesen, neuen wert hinzufügen und 
zurückschreiben. Also 3 Zugriffe auf ein Struct. Aber da bin ich nun 
nicht sicher...

>
1
> inline void setOutputChannel(uint8_t chan, uint8_t val)
2
> {
3
>   if( val )
4
>     *(outputConfig[chan].port) |=  outputConfig[chan].pinMask;
5
>   else
6
>     *(outputConfig[chan].port) &= ~outputConfig[chan].pinMask;
7
> }
8
>
>
> Schön ist es allerdings nicht unbedingt, weil du dem Compiler jegliche
> Möglichkeit zur Optimierung auf den I/O Operationen raubst, dürfte
> allerdings im Vergleich zu den if-then-else Orgien auch nicht schlechter
> sein.
Die Lösung ist nicht schlecht, nur hab ich so 2 Byte mehr pro Port im 
RAM. Ich wollte ja eher versuchen weniger RAM zu benutzen.
Ich brauche es ja nicht Dynamisch zur Laufzeit.

Grübel
Jürgen

von Marius W. (mw1987)


Lesenswert?

Dann bau deinen Code halt so um, dass du die "Verdrahtung" den 
Präprozessor machen lässt. Das heißt du definierst dir die 
entsprechenden Makros. Dann kommst du sogar auf 0 Byte Speicherverbrauch 
im RAM...

MfG
Marius

von JSachs (Gast)


Lesenswert?

Dafür suche im Moment ja eine Lösung.
Wie gesagt habe ich eine Structur Array in dem zur Laufzeit Parameter 
pro Ausgang festgelegt werden können.
Minimalwert, Maximalwert, Mittel Stellung,Failsave aktiv, etc.
Diese Information bringe ich im Moment per Index, die Portnummerierung 
entspricht quasi dem Index im Config Array, in Bezug zueinander.

Mache ich das nun per Makro, kann ich das nicht mehr machen. Da fehlt 
mir im Moment der Ansatz. Mir fehlt hier dann nur ein riesiger switch 
case Block ein. Das will mir nicht so gefallen.

Gruss
Juergen

von Marius W. (mw1987)


Lesenswert?

Dann mach halt deinen riesigen Switch-Case-Block und vergib globale 
GPIO-Nummern, aus denen sich der entsprechende PORT und die Pin-Maske 
berechnen lassen. Ungefähr so:

PORTA, PA0: 0
PORTA, PA1: 1
...
PORTA, PA7: 7
PORTB, PB0: 8
usw.

Die unteren 3 Bit deiner GPIO-Nummer stellen den Pin dar, die restlichen 
sind für den Port. Dann kannst du eine riesige switch-case bauen und 
fertig...

MfG
Marius

von Peter D. (peda)


Lesenswert?

Ne wichtige Frage vorab ist, wie schnell mußt Du darauf zugreifen?

Dieser ganze indirekte Zugriffs- und Umsortierkrams kann schnell mal 50 
Zyklen kosten.
Für ein Relais steuern ist das kein Problem.
Aber für ne SW-PWM kann das die CPU ganz schön ins Schwitzen bringen.


Peter

von Jürgen S. (jsachs)


Lesenswert?

Ich erzeuge Servosignale
Interruptgesteuert. In jedem Interrupt ändert sich der Portzustand eines 
Ausgangs.

In outputConfig[] stehen die die bereits aufbereiteten Werte für das 
OCR1A.
So muss ich in der ISR nichts mehr umrechnen.
Der Index ist hier auch wieder eine Referenz zu der portConfig.

Da die Ports zur Laufzeit von Proportional auf Schalten geändert werden 
kann, prüfe ich immer auf den nächsten Kanal vom Typ OUT_TYPE_PROP.
Die Funktion "setOutputChannel()" Ist ja schon in einem früheren Post.
1
ISR ( TIMER1_COMPA_vect )
2
{
3
4
    static uint8_t chan, lastchan;
5
    static uint16_t val = 24000;
6
    static uint8_t dir;
7
    lastchan = chan;
8
9
10
    if (lastchan < MAX_CHANNELS)
11
        //PORTB &= ~_BV(lastchan);
12
        setOutputChannel ( lastchan, 0 );
13
14
    chan++;
15
    while ( ( outputConfig[chan].type != OUT_TYPE_PROP ) && ( chan < MAX_CHANNELS ) )
16
    {
17
        chan++;
18
    }
19
20
21
    if (chan < MAX_CHANNELS)
22
    {
23
        //PORTB |= _BV(chan);
24
        setOutputChannel ( chan, 1 );
25
        val = rx.ch_data_out[chan];
26
        OCR1A = val;
27
    }
28
    if (chan==(MAX_CHANNELS))
29
    {
30
        syncBreakActive = 1;
31
        OCR1A = 22400;
32
33
        uint8_t ch_cnt = 0;
34
        while (ch_cnt < MAX_CHANNELS)
35
        {
36
            if ( outputConfig[ch_cnt].type == OUT_TYPE_SWITCH )
37
            {
38
                if ( ( uint8_t ) rx.ch_data_out[ch_cnt]&0x80 ) // Ohne & Verknüpfung geht es nicht ?!?!?!
39
                    val = 1;
40
                else
41
                    val = 0;
42
43
                setOutputChannel ( ch_cnt, val );
44
            }
45
            ch_cnt++;
46
        }
47
48
    }
49
    else if (chan>=(MAX_CHANNELS+1))
50
    {
51
        syncBreakActive = 0;
52
        chan = 0;
53
    }
54
}

von Peter D. (peda)


Lesenswert?

Jürgen Sachs schrieb:
> So muss ich in der ISR nichts mehr umrechnen.

Guck Dir mal das Assemblerlisting an. Du wirst staunen, was der Compiler 
im Interrupt so alles rechnen muß. Und dann noch ne Schleife und 
Unterprogrammaufruf.
Ich schätze mal, der kommt leicht auf >200 CPU-Zyklen.


Peter

von Jürgen S. (jsachs)


Lesenswert?

Ja, die ISR ist nicht kurz.

Die habe ich schon deutlich reduziert. Und die Unterprogramm aufrufe 
bringe ich nicht raus. Die hauen richtig rein.

Ich habe auch schon versucht die while auszulagern in einer früheren 
Version. Da konnte ich jedoch nicht mehr das Timing einhalten, da die 
Mainline ziemlich busy ist mit anderen Dingen wie Seriell Parsen und 
ausführen. Hier werden die Daten vom Uart per ISR gesammelt, Flag 
gesetzt und dann per Mainline ausgewertet.

Da fliegt die Kuh :-)

Gruss
Juergen

PS: Im Moment ist der mega32 mit 16MHz getaktet. Später wäre es schön 
mit dem internen 8MHz aus zu kommen. Aber das ist zweitrangig.

von Jürgen S. (jsachs)


Lesenswert?

Da ich noch wusste in einem Thread eine Lösung gefunden zu haben, möchte 
ich nur noch den Link zu dem Post anfügen.
Dann muss ich nicht mehr 3 Stunden danach suchen :-)

Link -> Beitrag "Re: Programm von mega32 auf mega328P convertieren"

Gruss
Juergen

von tictactoe (Gast)


Lesenswert?

Karl Heinz schrieb:
> Schön ist es allerdings nicht unbedingt, weil du dem Compiler jegliche
> Möglichkeit zur Optimierung auf den I/O Operationen raubst, dürfte
> allerdings im Vergleich zu den if-then-else Orgien auch nicht schlechter
> sein.

Der GCC kann ganz schön viel wegoptimieren, wenn man ihn lässt. Eine 
Grundvoraussetzung dazu ist, wie ich habe festgestellt habe, dass man 
alles, was nur geht, als static definiert. Am besten schreibt man den 
ganzen Code in eine Datei. Oder in die Header und definiert die 
Funktionen inline. Oder als C++-Templates (so hab' ich's gemacht).

von Eric B. (beric)


Lesenswert?

Jürgen Sachs schrieb:
1
> void setOutputConfig(uint8_t chan, uint8_t port, uint8_t pin)
2
> {
3
>   outputConfig[chan].min_val   = INT_VAL_MIN;
4
>   outputConfig[chan].max_val   = INT_VAL_MAX;
5
>   outputConfig[chan].cur_val   = 
6
> (((outputConfig[chan].max_val-outputConfig[chan].min_val)/2)+outputConfig[chan].min_val);
7
>   outputConfig[chan].port   = port;
8
>   outputConfig[chan].pinval  = _BV(pin);
9
>   setOutputFunc(chan, OUT_TYPE_NONE);
10
> }

Ich würde zuerst aus dieser outputConfig-Struktur den konstanten Teil 
von dem variabelen Teil trennen. Der konstanter Teil lässt sich dann 
statisch initialisieren und in Flash unterbringen.
Ich kann mir z.B. kaum vorstellen, dass cur_val wirklich einen 
Konfigurationswert ist :-)

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.