Forum: Compiler & IDEs C - Ersatz von switch case.


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von C-Anfänger (Gast)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,

ich bin noch dabei, C zu lernen.

Aktuell benötige ich eine Funktion, die die Nummer eines Kanals und die 
dorthin zu schreibenden Daten als Parameter erhält und dann mittels 
einfachem bitbanging des latch enable eines 74HC573 die Daten in diesen 
reinschreibt. Die clk_regx_oben/clk_regx_unten sind Symbole für 
Bitpositionen in den Ausgangsregistern der Ports, die an die jeweiligen 
LEs angeschlossen sind, latch ist ein Symbol für den Ausgangsport=Bus zu 
den 74HC573.

Im Prinzip sind es in allen case-Fällen die gleichen Abläufe, nur eben 
mit unterschiedlichen Bits(=latch enable) verschiedener Portregister.

Ausdrücklich sei erwähnt: es funktioniert gut, ich suche also keine 
Fehler.

Die Umsetzung in Assembler (siehe Anhang) ist sehr schnell, hat aber 
recht großen Platzbedarf. Im aktuellen Projekt stört das auch überhaupt 
nicht, aber geht es in C nicht noch irgendwie besser?

(Wie ich es in Assembler besser löse, weiß ich und das ist auch nicht 
die Frage)

Compiler ist der IAR, Optimierung auf Platzbedarf, Prozessor der 
STM8S105.

Auszug aus meiner bisherigen Lösung:
1
#define clk_reg1_oben PC_ODR_ODR3
2
#define clk_reg2_oben PC_ODR_ODR2
3
#define clk_reg3_oben PC_ODR_ODR1
4
#define clk_reg1_unten PA_ODR_ODR6
5
#define clk_reg2_unten PA_ODR_ODR5
6
#define clk_reg3_unten PA_ODR_ODR4
7
#define latch PB_ODR
8
9
...
10
11
void latching_data(int channel, int data)
12
{
13
14
  // Daten auf den Bus legen
15
  
16
  latch = data;
17
18
  // LE des Registers 1 auf low setzen (sollte ohnehin low sein)
19
  //    clk_reg1_oben = OFF;
20
  // LE für mindestens 10 Takte auf high setzen    
21
  //    clk_reg1_oben = ON;
22
  //    shortwait();
23
  // LE wieder auf low setzen, mit diesem Pegelwechsel
24
  // geschieht die Datenübernahme ins latch
25
  //    clk_reg1_oben = OFF;
26
27
28
  switch(channel) {
29
      case 1 :
30
         clk_reg1_oben = OFF;
31
         clk_reg1_oben = ON;
32
         shortwait();
33
         clk_reg1_oben = OFF;
34
         break;
35
      case 2 :
36
         clk_reg2_oben = OFF;
37
         clk_reg2_oben = ON;
38
         shortwait();
39
         clk_reg2_oben = OFF;
40
         break;        
41
      case 3 :
42
         clk_reg3_oben = OFF;
43
         clk_reg3_oben = ON;
44
         shortwait();
45
         clk_reg3_oben = OFF;
46
         break;
47
      case 4 :
48
         clk_reg1_unten = OFF;
49
         clk_reg1_unten = ON;
50
         shortwait();
51
         clk_reg1_unten = OFF;
52
         break;
53
      case 5 :
54
         clk_reg2_unten = OFF;
55
         clk_reg2_unten = ON;
56
         shortwait();
57
         clk_reg2_unten = OFF;
58
         break;
59
      case 6 :
60
         clk_reg3_unten = OFF;
61
         clk_reg3_unten = ON;
62
         shortwait();
63
         clk_reg3_unten = OFF;
64
         break;
65
      default :
66
         ;
67
   }
68
  return;
69
}

von NJ (Gast)


Lesenswert?

Hi!

Warum nimmst Du nicht clk_regx, statt "channel" als Parameter der 
Funktion?

von C-Anfänger (Gast)


Lesenswert?

Ich bin da noch nicht 100% sattelfest, die Bits der Register sind wohl 
als bitfield (?) definiert:

Auszug aus iostm8s105s4.h:
1
typedef struct
2
{
3
  unsigned char ODR0        : 1;
4
  unsigned char ODR1        : 1;
5
  unsigned char ODR2        : 1;
6
  unsigned char ODR3        : 1;
7
  unsigned char ODR4        : 1;
8
  unsigned char ODR5        : 1;
9
  unsigned char ODR6        : 1;
10
  unsigned char ODR7        : 1;
11
} __BITS_PB_ODR;

Heißt das dann also, ich müßte die Funktion so:
1
void latching_data(__BITS_PB_ODR channel, int data)

definieren?

Aber wie verwende ich das dann weiter in der Funktion? Setze ich dann 
einfach channel an die Stelle von clk_reg1_oben in meiner Funktion?

(große Fragezeichen über dem Kopf)

von C-Anfänger (Gast)


Lesenswert?

Hab das mal schnell ausprobiert.

Hmm, das geht nur, wenn ich mein restliches Programm umschreibe, weil 
ich dort natürlich überall channel als int einsetze, (ich seh gerade, 
char bzw. besser uint8_t würde reichen) wenn ich die Funktion aufrufe. 
Das macht natürlich die weitere Nutzung dieser Funktion schwieriger.

von C-Anfänger (Gast)


Lesenswert?

Mir wäre jetzt doch eine Lösung lieber, die als Parameter die Nummer des 
Channels und das zu schreibende Byte übernimmt. Das kommt meiner 
Denkweise im restlichen Programm mehr entgegen als die Übergabe der 
Bitbezeichnung.

von NJ (Gast)


Lesenswert?

Falls ich nichts übersehen habe und OFF=0 und ON=1...

Lesbarer ist switch-case ;)
1
uint8_t *temp;
2
uint8_t offset = 0;
3
4
temp = (uint8_t*)__BITS_PB_ODR;
5
6
if (channel > 3) offset = 6;
7
8
9
*temp &= ~(1 << (offset + 4 - channel));
10
*temp |= (1 << (offset + 4 - channel));
11
shortwait();
12
*temp &= ~(1 << (offset + 4 - channel));

von Bitschubsen (Gast)


Lesenswert?

>   clk_reg1_oben = OFF;
>   clk_reg1_oben = ON;
>   shortwait();
>   clk_reg1_oben = OFF;


Die erste Zeile ist überflüssig. Der Normalzustand ist ja wohl off.

So bleibt nur eine Funktion, die etwas "on" setzt, wartet und es dann 
wieder "off" setzt.

Ich würd das Port auf on setzen.
Warten.
Alle auf off setzen.

von Falk B. (falk)


Lesenswert?

@C-Anfänger (Gast)

>Im Prinzip sind es in allen case-Fällen die gleichen Abläufe, nur eben
>mit unterschiedlichen Bits(=latch enable) verschiedener Portregister.

>Ausdrücklich sei erwähnt: es funktioniert gut, ich suche also keine
>Fehler.

>Die Umsetzung in Assembler (siehe Anhang) ist sehr schnell, hat aber
>recht großen Platzbedarf.

Wie groß? die paar Bytes können kaum ins gewicht fallen. Eine generische 
Funktion mit den Portnummern als Parameter wird auch nicht kleiner, denn 
die muss das alles ausrechnen.

> Im aktuellen Projekt stört das auch überhaupt
>nicht, aber geht es in C nicht noch irgendwie besser?

Was ist daran nicht gut? Klar kann man die Portnummern als Adresse oder 
Index übergeben, aber das macht es nicht notwenigerweise besser oder 
kleiner.

Besonders als Anfänger und C-Einsteiger sollte man seine Zeit und 
Energie nicht mit Pseudo-Optimierungen verschwenden. Als 
fortgeschittener und Profi auch nicht.

https://www.mikrocontroller.net/articles/AVR-GCC-Codeoptimierung#Prinzipien_der_Optimierung

von NJ (Gast)


Lesenswert?

Manchmal hilft es aber beim Verständnis (evtl. auch für spätere 
Projekte) sich mehrere Möglichkeiten anzuschauen oder zu überlegen.

Deswegen ist der Gedanke "gehts auch anders oder sogar besser" gar nicht 
schlecht.

von Michael S. (schiko)


Lesenswert?

Das veranschaulicht schön, warum ich Bitfelder nicht mag.
1
uint8_t Channel_2_PB_ODR_BitNr( ChannelType channel )
2
{
3
  switch( channel )
4
  {
5
  case 0: return 3; //kein offsetof möglich bei bitfields...
6
  case 1: return 2; 
7
  case 2: return 1; 
8
  case 3: return 6; 
9
  case 4: return 5; 
10
  case 5: return 4; 
11
  default: return 0; 
12
  }
13
}
14
//alternativ
15
uint8_t arChannelBitNr[6] =  {3,2,1,6,5,4}
16
17
void latching_data(ChannelType channel, int data)
18
{
19
  latch = data;
20
  uint8_t bitNr  = Channel_2_PB_ODR_BitNr( channel ); 
21
//oder 
22
  uint8_t bitNr  = arChannelBitNr[channel];
23
//wenn sowieso nur ein Bit gleichzeitig gesetzt wird
24
  __BITS_PB_ODR  = 1 << bitNr;
25
  shortwait(..)
26
  __BITS_PB_ODR  = 0;
27
//ansonsten
28
  __BITS_PB_ODR  |= 1 << bitNr;
29
  shortwait(..)
30
  __BITS_PB_ODR  &= ~(1 << bitNr);
31
  
32
}

von C-Anfänger (Gast)


Lesenswert?

Vorab vielen Dank für die Antworten und freundlichen Anregungen.

@NJ
> OFF=0 und ON=1...

Ja, hab ich vergessen zu kopieren.

Vielen Dank für Deine pfiffige Lösung, die allerdings bei anderer 
Verteilung der Portbits nicht mehr funktioniert. :-)

@bitschubser
> Die erste Zeile ist überflüssig. Der Normalzustand ist ja wohl off.

Ja, das ist eine Angstzeile, die von den ersten Versuchen noch übrig 
ist. Ist tatsächlich überflüssig.

@Falk: Ja, Du hast recht, das Codegrößenargument/der Optimierungsgedanke 
ist dumm. Ich komme ursprünglich eher von Seiten der Hochsprachen. (hab 
viel Turbopascal programmiert früher) Da stößt man sich daran, dass man 
sechsmal praktisch das Gleiche im Quelltext hinschreibt. Der Gedanke, 
dass das vielleicht irgendwie eleganter geht und man selbst nur 
Scheuklappen vor den Augen hat, drängt sich förmlich auf. Deswegen 
fragte ich hier. Mein Gedanke war, dass es doch irgendwie möglich sein 
muss, die sechs Bitbezeichnungen zu strukturieren und in eine 
durchzählbare arrayähnliche Aufzählung zu fassen, sowas wie Enumeration 
oder eben ein Array.

@Michael:
Deine Lösung habe ich noch nicht ganz verstanden, geht aber, glaub ich 
zumindest verstehen zu können, in meine gedankliche Richtung. Vielen 
Dank dafür. Damit beschäftige ich noch mal intensiver.

Leider muss ich jetzt erst familiären und gesellschaftlichen overhead 
abarbeiten, bevor ich wieder ans Hobby darf. ;-)

von Falk B. (falk)


Angehängte Dateien:

Lesenswert?

@ C-Anfänger (Gast)

>fragte ich hier. Mein Gedanke war, dass es doch irgendwie möglich sein
>muss, die sechs Bitbezeichnungen zu strukturieren und in eine
>durchzählbare arrayähnliche Aufzählung zu fassen, sowas wie Enumeration
>oder eben ein Array.

Das ist machbar, läuft aber auch wieder auf eine mehr oder minder 
manuelle Unterscheidung in einer untergeordneten Funktion hinaus. So 
macht es der Arduino. Das ist aber weder kleiner noch schneller. Die 
C-Freaks machen das mit bösen Macros. Das willst du aber nicht wirklich.

Arrayzugriffe auf IO-Register oder gar IO-Bits sind und bleiben eher 
exotisch und anstrengend. Da sollte man lieber die Schreibarbeit 
investieren und es gut sein lassen. Es gibt wichtiger Dinge in der 
Programmierung.

Hier mal ein Beispiel für eine Macrolösung, wenn gleich das Problem 
etwas anders lag. Siehe Anhang.

von NJ (Gast)


Lesenswert?

C-Anfänger schrieb:
> @NJ
>> OFF=0 und ON=1...
>
> Ja, hab ich vergessen zu kopieren.
>
> Vielen Dank für Deine pfiffige Lösung, die allerdings bei anderer
> Verteilung der Portbits nicht mehr funktioniert. :-)

Ja, sollte nur eine Möglichkeit für den speziellen Fall zeigen ;)

Ansonsten hat Falk schon recht, um die manuelle Unterscheidung kommst Du 
nicht herum und da lohnt sich i.d.R. der Aufwand nicht.

Ist aber doch immerhin eine Erkenntnis :D

von C-Anfänger (Gast)


Lesenswert?

NJ schrieb:
> Ist aber doch immerhin eine Erkenntnis :D

Auf jeden Fall, vielen Dank dafür. :-)

Das Gefühl für die Möglichkeiten und Grenzen von C, die Vertrautheit mit 
der Sprache muss sich bei mir noch bilden.

Falk B. schrieb:
> Hier mal ein Beispiel für eine Macrolösung, wenn gleich das Problem
> etwas anders lag. Siehe Anhang.

Du meinst diesen Teil hier?
1
// clever bit generator macro
2
3
#define PAT(port, bit) ((port==&PORTA) ? (1L<<(bit+24)) : ( \
4
                        (port==&PORTB) ? (1L<<(bit+16)) : ( \
5
                        (port==&PORTC) ? (1L<<(bit+8))  : ( \
6
                        (port==&PORTD) ? (1L<<(bit+0))  : 0))))

Das schaue ich mir mal in Ruhe an, vielen Dank.

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]
  • [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.