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
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".
Ü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)
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))
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).
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?
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.
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.
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
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
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.
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.
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.
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").
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":
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
WEAKvoiduart_on_tx_begin(void){}
9
10
WEAKvoiduart_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: