Forum: Compiler & IDEs Umsetzung von Hardware-ungekoppeltem Code; Zeiger in C?


von Christian W. (clupus)


Angehängte Dateien:

Lesenswert?

Hallo allerseits,

ich hab schon oft gehört, dass es sinnvoll sei, im Code keine direkten
Port-Bezeicher & Co. einzuarbeiten. Ist logisch.
Nun hab ich aber das Problem, dass ich für eine Uhr so 17 LEDs und 2
Schlter (hört sich wenig an) an einen Mega8 rankriegen muss. Ich will
aber, da ich noch nicht weiß, an welchen Ports welche LED rankommt
(kann sein, dass sich da noch das ein oder andere ändert), das so
programmieren, dass ich in der hardware.h-Datei (so heißt das Ding halt
bei mir) alles eingeben kann.

So nun zu meinem Problem:
Ich hab es einmal geschrieben und gesen, dass es sehr viel Aufwand ist,
wenn ich mir das alles als Makros definiere. Da muss ich dann Unmengen
an #define-Anweisungen setzen und auch das Ansprechen ist nicht so ohne
(Ich muss ja für jede LED einzeln prüfen, ob sie leuchten soll, und dann
im jeweiligen IO-Register ein-/ausschalten.) Das ist im auskommentieren
Code ja teilweise zu sehen!

Daher dachte ich mir es ginge einfacher, wenn ich Arrays verwende. In
die Arrays kommen die Zeiger auf verschiedene Register (DDR, Port, PIN,
...). Damit kann ich einzelne LEDs ihrer Priorität nach mit LED_SEC[0]
bis LED_SEC[5] (z.B.) ansprechen. Ich hab also die Zeiger mithilfe der
&-Operator in die Arrays eingegeben. (Vgl. Anhang; die erste Reihe an
Arrays haben in Nachhinein einen * verpasst bekommen, dazu später)

Nun zum ersten Problem, bei dem ich C nicht verstehe:
wenn ich eine Funktion definiere mit
test_func(int * param) {...}
und mit test_func(a) aufrufe, "sieht" die Funktion:
*param = <Wert von a>
param = <Adresse von a>
=> *param = a könnte man sagen

rufe ich aber nun test_func(&a) auf, ist das nicht mehr das selbe; die
Funktion sieht (in Klammer: solle analog sehen):
*param = <Wert von a> (<Adresse von a>)
param = <Adresse von a>
**param = >Mist< (<Wert von a>)
=> Auch hier könnte man *param = a sagen, obwohl eigentlich *param = &a
logisch wäre, oder?

Ziel sind die Funktionen LED_xxx_OFF und LED_xxx_ON. Hier wird die
Adresse aus den Zeigern ausgewertet. Zuerst hatte ich hier die
Parameter ohne die * angegeben. Dann bekam ich vom gcc eine
Fehlermeldung. Nur interessehalber: Was hab ich da "falsch" gemacht?
(Damit mir das nicht noch mal passiert?

2. Problem (viel wichtiger, da ungelöst!!!!!):
Weiter unten (Z. 274ff) versuche ich auf die Werte der oben
eingesetzten Zeiger zugreifen, um die jeweiligen LEDs
ein-/auszuschalten. Da bekomme ich nur folgende Meldung vom gcc:

hardware.h: In function 'Init_HW':
hardware.h:274: error: assignment of read-only location

Hier hab ich dann oben die * eingefügt, bei den Arrays, hat aber nix
außer der zusätzlichen Meldungen des GCC gebracht:

hardware.h:30: warning: initialization discards qualifiers from pointer
target type

Das wird dann für jedes Element wiederholt.

Was kann ich da machen? Oder gibt es eine bessere Möglichkeit das mit
dem HW-unabhängigen Code zu machen, so dass es auch
Programmiertechnisch im Rahmen bleibt und möglichst übersichtlich
bleibt (die Kommentare mit dem alten Code kommen klar noch raus, aber
jetzt sind sie zu Debug-Zwecken noch drinnen)?

MfG
Christian

PS: Mir wäre v.a. mit der 2. Antwort geholfen. Und noch eine
Kleinigket: Ich habe hier kein C-Buch. Also so Kommentare wie "Schalg
mal dein C-Buch auf und ließ!" ziehen nicht. Ich hab das bisschen C-
das ich kann zum einen Lerning-By-Doing und zum anderen aus dem Netz
gelernt. Und meine Quellen geben zu dem Thema nix her.

von Peter D. (peda)


Lesenswert?

Sieht aus, als willst Du ne Binäruhr bauen.

Ich programmiere ja schon viele Jahre, aber so auf die schnelle binär
dekodieren zu müssen, wäre mir zu mühsam.
Ich würde eine Ziffern- oder Zeigerdarstellung nehmen.


Etwas grundsätzliches zu Feldern, sie beginnen immer bei 0 und gehen
bis n-1.
D.h. bei einer Feldgröße von 6 sind nur die Indizes 0..5 gültig.


Was Du mit dem vielen Geschreibsel erreichen willst, ist mir nicht
klar. Zumindest ist es sehr schwer zu verstehen.

Ich würde den Portpins per define Namen zuweisen und sie dann im
Programm direkt über ihren Namen ansprechen.

Bevorzugt würde ich zusammengehörende Pins im gleichen Port
unterbringen, muß aber nicht sein. Der Codeaufwand ist dann aber
geringer.


Peter

von Christian W. (clupus)


Lesenswert?

Genau darum geht's. Weil ich noch nicht weiß, ob die Pins so bleiben,
will ich die Adresse zu jedem Pin (DDR-, PORT-Register, Pin-Nummer) in
einer Definition am Anfang eingeben. Wenn ich das via #define mache,
sind da ziemlich viele defines notwendig und v.a. ist das Problemchen,
dass ich für jede LED einzeln mit if oder ?: prüfen muss, ob sie
leuchten soll oder nicht. Das ist nicht wenig Tipparbeit. Da ist eine
for-Schleife, die das ganze durchläuft deutlich praktischer. Das geht
aber nicht mit #define-Anweisungen.
zum Thema Anzeige: Das das Ding binär anzeigt, ist gewollt. Es ist eine
Eigenkonstruktion, weil eine gute Freundin so ein Ding haben wollte. Ich
allerdings sehe darin eine gute Übung und auch ggf. die Mgl das Ding auf
Armabduhr-Format zu bringen. Dehalb der modulare Aufbau.

Einen Teil der Probleme hab ich übrigens gelöst: Ich muss folgendes
machen:

Oben definieren:
const uint8_t DDR_LED_SEC[6] = {&DDRC,&DDRC,&DDRC,&DDRC,&DDRC,&DDRC};

Weiter unten dann entweder verwenden:

*(uint8_t *)DDR_LED_SEC[...] = abc;

oder eben mit Unterfunktion:
LED_OFF_ll(uint8_t LED,uint8_t * PORT){ *PORT |= _BV(LED); }
aufzurufen mit LED_OFF_ll(2,&PORT_LED_SEC[...]);

Es bleibt also die Frage, wie ich die Arrays in den Programmspeicher
bekomme und ob es irgendwie besser geht.

MfG
Christian

von Christian W. (clupus)


Lesenswert?

» Etwas grundsätzliches zu Feldern, sie beginnen immer bei 0 und gehen
bis n-1.
» D.h. bei einer Feldgröße von 6 sind nur die Indizes 0..5 gültig.

Was meinst du damit? Habe ich das irgendwo verletzt? Ich meine nämlich,
mich immer an die Regel gehalten zu haben.

MfG
Christian

von Reiner (Gast)


Lesenswert?

Hallo,
Hier noch ein Gedanke um die defines zu vereinfachen:

#define LED_MIN_PORT  PORTB
#define LED_HOUR_PORT PORTD
#define SWITCH_PORT   PORTC
#define LED_MIN_1_pin     1
#define LED_MIN_2_pin     2
#define SW_1_pin          5

#define PIN(x) (*(&x - 2)) //address of input register port x
#define DDR(x) (*(&x - 1)) //address of data direction register port x

...

DDR(LED_MIN_PORT) |= _BV(LED_MIN1_pin);  //MIN1 LED is output
DDR(SWITCH_PORT)  &= ~_BV(SW_1_pin);     //SW_1 is input

....

LED_MIN_PORT |= _BV(LED_MIN_1);     //LED 1 on
if (PIN(SWITCH_PORT) & SW_1) ......

Reiner

von Peter D. (peda)


Lesenswert?

@Christian

"Was meinst du damit? Habe ich das irgendwo verletzt?"


Ja, so kann man die Leute verarschen.

Du solltest lesbar schreiben, also nicht reservierte Syntaxelemente
zweckentfremden.

Jeder, der xx[1] sieht, denkt natürlich, das ist ein Feldelement und
kein define.


Peter

von Peter D. (peda)


Lesenswert?

Wenn Du die LEDs binär ordnen würdest, könnte man mittels
Schiebeoperatoren ausgeben.


Für die wahlfreie Zuordnung, muß man aber jede LED per Abfrage separat
schalten, das könnte z.B. so aussehen:
1
#include <io.h>
2
3
#define LED_MIN0        A, 0
4
#define LED_MIN1        B, 6
5
#define LED_MIN2        C, 5
6
#define LED_MIN3        D, 0
7
#define LED_MIN4        B, 7
8
#define LED_MIN5        A, 1
9
10
11
#define OUT_HIGH(x) _SH(x)
12
#define OUT_LOW(x) _SL(x)
13
#define OUT_DIR(x) _SD(x)
14
15
#define _SH(x,y) (PORT##x |= (1<<y))
16
#define _SL(x,y) (PORT##x &= ~(1<<y))
17
#define _SD(x,y) (DDR##x |= (1<<y))
18
19
20
void out_minute( unsigned char val )
21
{
22
  OUT_DIR( LED_MIN0 );
23
  OUT_DIR( LED_MIN1 );
24
  OUT_DIR( LED_MIN2 );
25
  OUT_DIR( LED_MIN3 );
26
  OUT_DIR( LED_MIN4 );
27
  OUT_DIR( LED_MIN5 );
28
29
  val &  1 ? OUT_LOW( LED_MIN0 ) : OUT_HIGH( LED_MIN0 );
30
  val &  2 ? OUT_LOW( LED_MIN1 ) : OUT_HIGH( LED_MIN1 );
31
  val &  4 ? OUT_LOW( LED_MIN2 ) : OUT_HIGH( LED_MIN2 );
32
  val &  8 ? OUT_LOW( LED_MIN3 ) : OUT_HIGH( LED_MIN3 );
33
  val & 16 ? OUT_LOW( LED_MIN4 ) : OUT_HIGH( LED_MIN4 );
34
  val & 32 ? OUT_LOW( LED_MIN5 ) : OUT_HIGH( LED_MIN5 );
35
}

Die doppelten Macros sind dazu da, um den Präprozessor zu überlisten,
der macht die Argumentenüberprüfung nur bei der ersten Macroexpansion.


Peter

von Peter D. (peda)


Lesenswert?

P.S.:

Überlisten ist nicht ganz richtig, sondern man nutzt aus, daß der
Präprozessor mehrere Durchläufe macht:

Im ersten Durchlauf stellt er fest, daß ein Macro mit einem Parameter
auch einen Parameter bekommt und ist glücklich. Dann expandiert er die
erste Instanz an Macros.

Im nächsten Durchlauf findet er wieder ein Macro, aber diesmal mit 2
Parametern, das nun auch 2 Parameter bekommt und ist wieder glücklich.


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.