Forum: Mikrocontroller und Digitale Elektronik PIN und PORT-Konstanten eigener Bibli übergeben


von Felix (Gast)


Lesenswert?

Hallo, ich habe folgendes Problem:

Ich habe ein Atmel-Studio-Projekt mit je einer
- main.c
- hc165.c
- hc165.h
Datei. Jetzt will ich aus meiner main heraus die in der Datei hc165.c 
befindliche Funktion

"hc165_init(char Pin1[4],char Port1[6])" ausführen.
(Anm.: "PA3" wird als Chararray übergeben sowie "PORTA".

Mein Problem dabei ist folgendes....
ich weiß nicht, wie ich z.b. die
"Port1"-Variable sowie die
"Pin1"-Variable
an die Funktion übergeben kann, dass ich nachher in der Funktion einfach 
schreiben kann:

Port1 = (1<<Pin1);

und in Wirklichkeit hat es die Bedeutung wie z.b. PORTA = (1<<PA3);

Ich denke, das ließe sich irgendwie mit Makros lösen, oder ???

PS: Ich habe in der ursprünglichen Version mit Switch und Case alle 
Möglichkeiten abgefragt...also ob der Übergebene String bzw. Char-Array
an Stelle 5 ein A,B,C oder D enthält (z.B. PORTA,PORTB, etc)
und dann jeweils dem Port A oder B etc. zugewiesen.

Das müsste ich aber in jeder Funktion machen, die ich in meinem hc165.c 
- FIle benutze. Das gäbe Unmengen von Code......

Danke schon mal im Voraus,

LG

von Ralf G. (ralg)


Lesenswert?

Du übergibst der Funktion, die natürlich entsprechend dklariert sein 
muss, die Adresse des entsprechenden Ports.

Das ist mir allerdings vollig unverständlich:
Felix schrieb:
> "hc165_init(char Pin1[4],char Port1[6])" ausführen.
> (Anm.: "PA3" wird als Chararray übergeben sowie "PORTA".

von Mike H (Gast)


Lesenswert?

Übergib einfach einen Zeiger auf den Port. PORTx  sind in den 
RAM-Adressraum gemappt.

hc165_init(char *myport, char portbit)
{
  *myport |= (1 << portbit);
}

Aufruf mit:

hc165_init(&PORTA, PA3)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Felix schrieb:
> Ich denke, das ließe sich irgendwie mit Makros lösen, oder ???

Für eine richtige Bibliothek nicht, da diese vorcompiliert binär
vorliegt.  Da geht nur die Variante mit der Übergabe eines Zeigers.

Falls „Bibliothek“ bei dir nur ein Platzhalter für „separate
Implementierungsdatei“ ist, d. h. alles wird gemeinsam im endgültigen
Projekt compiliert, dann geht das auch mit Makros.

Vorteil der Makro-Variante: der Compiler kann bei den entsprechenden
Ports wieder IN, OUT, CBI und SBI benutzen.  Bei der Variante mit den
Zeigern muss er auf die langsameren Speicherzugriffs-Implementierungen
ausweichen.

Nachteil der Makro-Variante: die Implementierung sieht extrem K***ke
aus und ist auf den ersten Blick völlig unverständlich.  Beispiel
(kein HC595 sondern HD44780):
1
/* single-bit macros, used for control bits */
2
#define SET_(what, p, m) GLUE(what, p) |= (1 << (m))
3
#define CLR_(what, p, m) GLUE(what, p) &= ~(1 << (m))
4
#define GET_(/* PIN, */ p, m) GLUE(PIN, p) & (1 << (m))
5
#define SET(what, x) SET_(what, x)
6
#define CLR(what, x) CLR_(what, x)
7
#define GET(/* PIN, */ x) GET_(x)
8
9
/* nibble macros, used for data path */
10
#define ASSIGN_(what, p, m, v) GLUE(what, p) = (GLUE(what, p) & \
11
                                                ~((1 << (m)) | (1 << ((m) + 1)) | \
12
                                                  (1 << ((m) + 2)) | (1 << ((m) + 3)))) | \
13
                                                ((v) << (m))
14
#define READ_(what, p, m) (GLUE(what, p) & ((1 << (m)) | (1 << ((m) + 1)) | \
15
                                            (1 << ((m) + 2)) | (1 << ((m) + 3)))) >> (m)
16
#define ASSIGN(what, x, v) ASSIGN_(what, x, v)
17
#define READ(what, x) READ_(what, x)

Zusammen mit defines.h:
1
/* HD44780 LCD port connections */
2
#define HD44780_RS A, 6
3
#define HD44780_RW A, 5
4
#define HD44780_E  A, 4
5
/* The data bits have to be not only in ascending order but also consecutive. */
6
#define HD44780_D4 A, 0

… werden sie dann im Code so benutzt:
1
  CLR(PORT, HD44780_RW);
2
  if (rs)
3
    SET(PORT, HD44780_RS);
4
  else
5
    CLR(PORT, HD44780_RS);
6
  ASSIGN(PORT, HD44780_D4, n);

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Mike H schrieb:
> Übergib einfach einen Zeiger auf den Port. PORTx  sind in den
> RAM-Adressraum gemappt.
>
> hc165_init(char *myport, char portbit)
> {
>   *myport |= (1 << portbit);
> }

Da fehlt eine "volatile"-Qualifikation.

Außerdem sollte man "char" nur für tatsächliche Zeichen benutzen,
sonst besser "uint8_t" (so sind die Ports auch im Header deklariert).

von NuMal Langsam (Gast)


Lesenswert?

Felix schrieb:
> Danke schon mal im Voraus,

Die Vorredner stochern im Nebel. Oder ich blicke nicht durch.

Mir erschliesst sich nicht warum "ich" mehrere Ports mit HC165
initialisieren möchte.

Typischerweise - wenn ich mehrere HC165 habe - schliesse ich
diese kaskadiert an einen SPI Port an. Dazu muss ich nicht
variabel über eine Funktion mehrere Ports ansprechen, das SPI
initialisiere ich nur einmal zentral.

Jeweils einen HC165 an einen (eignen) Port hängen macht jedenfalls
wenig Sinn, dazu dann noch Soft-SPI?

von Ralf G. (ralg)


Lesenswert?

NuMal Langsam schrieb:
> Die Vorredner stochern im Nebel. Oder ich blicke nicht durch.

Durch den Nebel, in dem du stocherst? ;-)

NuMal Langsam schrieb:
> Mir erschliesst sich nicht warum "ich" mehrere Ports mit HC165
> initialisieren möchte.
Ralf G. schrieb:
> Das ist mir allerdings vollig unverständlich:
> Felix schrieb:
>> "hc165_init(char Pin1[4],char Port1[6])" ausführen.
>> (Anm.: "PA3" wird als Chararray übergeben sowie "PORTA".

Die Antwort darauf könnte den Nebel etwas lichten.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Ralf G. schrieb:
>>> (Anm.: "PA3" wird als Chararray übergeben sowie "PORTA".
>
> Die Antwort darauf könnte den Nebel etwas lichten.

Wobei das natürlich die ineffizienteste Form ist, das als String zu
übergeben.  Sowas macht man vielleicht in einem Interpreter, aber in
einem C-Programm?

Sorry, diese Anmerkung hatte ich so nicht wahr genommen.  Dafür passt
natürlich der von mir gepostete Makro-Salat ganz und gar nicht.

von Mike H (Gast)


Lesenswert?

Jörg W. schrieb:

> Da fehlt eine "volatile"-Qualifikation.

Hab den Code auch nur mal so aus dem Ärmel geschüttelt also keine Gewähr 
für Fehler. Würde mich aber mal interessieren wo da volatile fehlt und 
vor allem warum.

> Außerdem sollte man "char" nur für tatsächliche Zeichen benutzen,
> sonst besser "uint8_t" (so sind die Ports auch im Header deklariert).

Stimmt

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Mike H schrieb:
> Würde mich aber mal interessieren wo da volatile fehlt und vor allem
> warum.

Dann schau dir doch mal an, wie bpsw. "PORTA" im Headerfile
deklariert ist …  Der Grund ist einfach, dass IO-Register inhärent
volatile sind, d. h. der Compiler darf beim Zugriff auf diese
nichts wegwerfen.

Ansonsten:

http://nongnu.org/avr-libc/user-manual/FAQ.html#faq_port_pass

von NuMal Langsam (Gast)


Lesenswert?

Jörg W. schrieb:
> Dafür passt
> natürlich der von mir gepostete Makro-Salat ganz und gar nicht.

Ja danke sehr.

Ralf G. schrieb:
> Die Antwort darauf könnte den Nebel etwas lichten.

Es sieht jedenfalls ganz danach aus also ob der TO selbst
nicht genau weiss was er will.

Zumindest hat er das nicht genau genug beschrieben.

von Mike H (Gast)


Lesenswert?

Jörg W. schrieb:
> Mike H schrieb:
>> Würde mich aber mal interessieren wo da volatile fehlt und vor allem
>> warum.
>
> Dann schau dir doch mal an, wie bpsw. "PORTA" im Headerfile
> deklariert ist …  Der Grund ist einfach, dass IO-Register inhärent
> volatile sind, d. h. der Compiler darf beim Zugriff auf diese
> nichts wegwerfen.
>
> Ansonsten:
>
> http://nongnu.org/avr-libc/user-manual/FAQ.html#fa...

Hab es auch gefunden aber ich verstehe es trotzdem nicht ganz.

PORTA muss volatile sein, da man ja den Wert unmittelbar (also nicht 
über Register) ändern möchte. Soweit ist alles klar.

myport ist aber ein Zeiger auf eine Speicherzelle. Ich möchte ja nicht 
den Wert von myport ändern (d.h. die Stelle auf die er zeigt) sondern 
den Wert der referenzierten Speicherzelle.

von Ralf G. (ralg)


Lesenswert?

Mike H schrieb:
> myport ist aber ein Zeiger auf eine Speicherzelle. Ich möchte ja nicht
> den Wert von myport ändern (d.h. die Stelle auf die er zeigt) sondern
> den Wert der referenzierten Speicherzelle.

... und die ist vom Datentyp 'volatile uint8_t'. Gehört einfach mit 
dazu.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Mike H schrieb:
> myport ist aber ein Zeiger auf eine Speicherzelle.

Das "volatile" bezieht sich ja in diesem Falle nicht auf den Zeiger,
sondern auf das Ziel des Zeigers (so, wie auch das "uint8_t").

von Peter D. (peda)


Lesenswert?

Felix schrieb:
> "hc165_init(char Pin1[4],char Port1[6])" ausführen.
> (Anm.: "PA3" wird als Chararray übergeben sowie "PORTA".

Was soll das bringen, außer extreme Verschwendung von RAM und CPU-Zeit?
Du weißt doch schon zur Compilezeit die Pinbelegung, warum soll sich 
dann die CPU zur Laufzeit damit abquälen und alles mühsam 
interpretieren.

Ich mache sowas einfach über Defines, hier mal ein Ausschnitt einer 
"hardware.h":
1
#define LED_GN          PORT_A4
2
#define LED_GN_oe        DDR_A4
3
#define LED_RD          PORT_A5
4
#define LED_RD_oe        DDR_A5
5
#define xADC0           PORT_B0
6
#define xADC0_oe         DDR_B0
7
#define SCK             PORT_B1
8
#define SCK_oe           DDR_B1
9
#define MOSI            PORT_B2
10
#define MOSI_oe          DDR_B2
11
#define xDAC0           PORT_B4
12
#define xDAC0_oe         DDR_B4
13
#define xDAC1           PORT_B5
14
#define xDAC1_oe         DDR_B5

von Bernd K. (prof7bit)


Lesenswert?

Je nach Anwendungszweck kann man es auch mit hook-Funktionen lösen. Zum 
Beispiel hab ich einen UART-Treiber der soll (optional) vor dem Senden 
einen beliebigen Pin auf High und nach dem Senden wieder auf Low setzen 
können.

Dazu ruft mein Treiber einfach zwei leere und als weak deklarierte 
Hook-Funktionen auf:
1
#define WEAK __attribute((weak))
2
3
/*
4
 * empty default hooks, 
5
 * application can override these
6
 */
7
8
WEAK void uart_on_tx_begin(void) {}
9
10
WEAK void uart_on_tx_end(void) {}
11
12
13
// und weiter unten irgendwo im Interrupt (sinngemäß)
14
15
    if (fifo_get(tx_fifo, &b)) {
16
        if (state == RX) {
17
            uart_on_tx_begin();
18
            state = TX;
19
        }
20
        UART->D = b;
21
        // und 
22
        // so
23
        // weiter
24
25
// und woanders:
26
    if (UART->S & UART_S_TC_MASK) {
27
        uart_on_tx_end();
28
        state = RX;
29
        // und 
30
        // so
31
        // weiter


und in der Anwendung dann kann (muss aber nicht) die beiden schwachen 
leeren Funktionen mit eigenen Implementationen überschreiben:
1
/*
2
 * implement hook functions from UART module
3
 * to activate or deactivate the transceiver
4
 * and also to drive the blinkenlight.
5
 */
6
7
void uart_on_tx_begin(void) {
8
    gpio_max485_de_high();
9
    gpio_txled_high();
10
}
11
12
void uart_on_tx_end(void) {
13
    gpio_max485_de_low();
14
    gpio_txled_low();
15
}

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.