Forum: Mikrocontroller und Digitale Elektronik DDR, PORT, PORT_BIT an Libary übergeben


von Stefan S. (sschultewolter)


Lesenswert?

Hallo,

ich würde gerne eine Libary etwas abkapseln. Derzeit habe ich die Sachen 
soweit noch fest in der lib.h drinstehen. Würde aber gerne das nur noch 
so machen, dass aus der main.c der IO nur noch einmal übergeben wird und 
der rest eigenständig abläuft.

Das war was ich dazu gefunden hab, passt aber so wie ich das sehe nicht 
so wirklich.
http://www.nongnu.org/avr-libc/user-manual/group__avr__sfr.html
1
/* main.c */
2
#include <avr/io.h>
3
#include "mylib.h"
4
5
int main()
6
{
7
  mylib_init(DDRB, PORTB3);
8
  while(1)
9
  {
10
    // do something
11
  }
12
  return 0;
13
}
14
15
/* mylib.h */
16
void mylib_init(uint8_t *DDR, uint8_t *PORT, uin8t_t *PORT_BIT);
17
void mylib_do(void);
18
19
/* mylib.c */
20
uint8_t my_lib_ddr = 0;
21
uint8_t my_lib_port = 0;
22
uint8_t my_lib_port_bit = 0;
23
24
void mylib_init(uint8_t *DDR, uint8_t *PORT, uint8_t *PORT_BIT)
25
{
26
  *DDR |= (1 << *PORT_BIT);
27
  
28
  my_lib_ddr = *DDR;
29
  my_lib_port = *PORT;
30
  my_lib_port_bit = *PORT_BIT;
31
}
32
33
void mylib_do(void)
34
{
35
  my_lib_port |= (1 << my_lib_port_bit); // HIGH
36
  // do something
37
  my_lib_port &= ~(1 << my_lib_port_bit); // LOW
38
}

Wäre über eure Hilfe dankbar. Gruß Stefan

von Waldemar Z. (waldemar_z39)


Lesenswert?

Ich verstehe deine Beschreibung nicht.
Geht es auch verständlicher?

von Karl H. (kbuchegg)


Lesenswert?

Stefan S. schrieb:

> Das war was ich dazu gefunden hab, passt aber so wie ich das sehe nicht
> so wirklich.
> http://www.nongnu.org/avr-libc/user-manual/group__avr__sfr.html

korrekt. Da gehts um was ganz anderes

Zum Programm

> /* main.c */
> #include <avr/io.h>
> #include "mylib.h"
>
> int main()
> {
>   mylib_init(DDRB, PORTB3);

Das kann so nicht funktionieren.

Wenn du in C eine Funktion aufrufst
1
void foo( int a )
2
{
3
  ...
4
}
5
6
int main()
7
{
8
  int i = 5;
9
10
  foo( i );
11
}

dann kriegt die Funktion bei ihrem Aufruf den momentan aktuellen Wert 
des Arguments (hier: i. Also den Wert 5).
Änderst du nach dem Funktionsaufruf das i, so wie hier
1
int main()
2
{
3
  int i = 5;
4
5
  foo( i );
6
  i = 8;
7
}
dann beeindruckt das die Funktion nicht weiter. Denn: Die Funktion weiss 
ja gar nichts von deinem i. Die Funktion hat nur einen Wert bekommen.

Wenn die Funktion wissen soll/muss, wo der Wert eigentlich herkommt, 
dann musst du ihr schon die Adresse davon geben.
1
void foo( int * a )
2
{
3
  ...
4
}
5
6
int main()
7
{
8
  int i = 5;
9
10
  foo( &i );
11
}

Jetzt kann sich die Funktion diese Adresse in einer Variablen merken, zb 
in einer globalen, um dann in weiterer Folge diese Adresse wieder zur 
Verfügung zu  haben, wenn siegebraucht wird.

So auch hier bei dir. Deine Lib-Funktionen brauchen die Adresse! Der 
aktuelle Wert vom Port Register ist zwar nett, aber eigentlich sollen 
die Funktionen ja das Port register manipulieren. Daher brauchen sie die 
Adresse, wo diese Manipulation stattfinden soll:
1
    mylib_init(&DDRB, &PORTB, 3);
und da die Bitnummer eine Konstante ist und sich nie ändert, muss die 
Funktion davon auch keine Adresse kriegen. Da braucht sie sich nur die 
Bitnummer merken. Daher kriegt sie die auch einfach nur als Zahl


du übergibst der Funktion hier die aktuellen Werte von DDRB, PORTB und 
ich nehme mal an, du hast da ein Komma zwischen PortB und der 3 
vergessen.
Das ist aber nicht das was du willst!
Du willst der Funktion ja nicht die aktuellen Werte geben, sondern du 
willst der Funktion nmitteilen, wo es die aktuellen Werte herkriegen 
kann!

Daher hast du ja auch hier
1
void mylib_init(uint8_t *DDR, uint8_t *PORT, uin8t_t *PORT_BIT);
ja auch vereinbart, dass die Funktion die Adressen kriegt (wo auch immer 
du das abgeschrieben hast). Aber dann musst du ihr auch Adressen geben!
1
void mylib_init(uint8_t *DDR, uint8_t *PORT, uin8t_t PORT_BIT);

(UNd schreib die Variablen nicht gross. Namen komplett in 
Grossbuchstaben sind weltweit eine Konvention um anzuzeigen, dass es 
sich um ein Makro handelt. Das hier ist aber kein Makro. Das sind 
normale Funktionsargumente.
1
void mylib_init(uint8_t *Ddr, uint8_t *Port, uin8t_t PortBit);

Und der korrekte Datentyp für die Register ist 'volatile uint8_t *' und 
nicht nur 'uint8_t *'

> {
>   *DDR |= (1 << *PORT_BIT);

Dann eben
1
    *Ddr |= ( 1 << PortBit );
Vor das Ddr muss ein *, weil DDr ja eine Adresse ist. Das PortBit 
braucht keinen *, weil da ja schon die Zahl selber in der Variablen 
steht.

>   my_lib_ddr = *DDR;
>   my_lib_port = *PORT;
>   my_lib_port_bit = *PORT_BIT;

Hier speicherst du dir die übergebenen Werte, weil du sie ja für spätger 
brauchst. Aber: Du willst dir wieder nicht speichern, welcher aktuelle 
Wert am Ddr gerade vorliegt, sondern du willst dir die Adresse merken, 
welcher DDrd gemeint ist. Die Adresse hast du ja vom Aufrufer gekriegt. 
Also musst du dir die auch merken, wenn du sie später wieder brauchst.
1
volatile uint8_t* my_lib_ddr = 0;
2
volatile uint8_t* my_lib_port = 0;
3
uint8_t my_lib_port_bit = 0;
4
5
void mylib_init(volatile uint8_t *Ddr, volatile uint8_t *Port, uin8t_t PortBit)
6
{
7
  *Ddr |= ( 1 << PortBit );
8
9
  my_lib_ddr = Ddr;
10
  my_lib_port = Port;
11
  my_lib_port_bit = PortBit;
12
]

> Wäre über eure Hilfe dankbar. Gruß Stefan

Ein gutes C Buch, aufgeschlagen in einem der Kapitel in dem es erstmalig 
um Pointer geht und ein paar Übungssstunden auf dem PC würden Wunder 
wirken.

von Peter D. (peda)


Lesenswert?

Du kannst es nach Arduino Art machen, d.h. jeder Pin hat eine Nummer und 
jeder Zugriff ruft eine Funktion auf, die die Nummer auflöst und die 
Aktion macht.
Das kostet natürlich zusätzlich Flash, SRAM und CPU-Zeit.

Falls es keinen Grund gibt, die Lib nicht als Quelltext zu belassen, ist 
die Definition im h-File um Klassen effektiver.
Dann kann der Compiler alles schon zur Compilezeit auflösen und 
SBI/CBI/SBIS/SBIC benutzen. Also nur ein Befehl statt umständlicher 
Funktionscalls.

von W.S. (Gast)


Lesenswert?

Stefan S. schrieb:
> ich würde gerne eine Libary etwas abkapseln.

Dann mußt du das eben konsequent und vor allem sinnvoll tun.

ich unterscheide hier mal zwischen einer Bibliothek und einem Treiber.

Die Bibliothek stellt Funktionen bereit, wie z.B. irgendwelche 
Berechnungen usw., hat also selbst keine Verbindung zur Hardware und ist 
damit hardwareunabhängig. Beispiel: Datenkonvertierungen wie z.B. 
LongToString oder Adler32 oder GetFontAscent oder LZWExpand usw.

Ein Treiber IST hardwareabhängig, weil er einen Dienst an der Hardware 
verrichtet und seine Leistungen dem aufrufenden System zur Verfügung 
stellt. Sowas muß zumeist initialisiert werden. Beispiel: serielle 
Schnittstellen mit CharAvail oder GetChar oder PutChar oder SetBaudrate 
oder SendBreak usw.

ABER: In beiden Fällen sollte in der zugehörigen .h nur das 
drinstehen, was von aufrufender Seite auch wirklich gebraucht wird. Beim 
Treiber sind das Aufrufe der zur Verfügung gestellten Funktionen und 
ggf. öffentliche Konstanten, aber KEIN Hardwarebezug!! Den stellt der 
Treiber nämlich SELBST her und deswegen hat sowas nicht in der 
Schnittstelle zum Programm hin aufzutauchen.

W.S.

von Achim K. (aks)


Lesenswert?

Bei den FAQ findet man auch die Hinweise, die oben schon gegeben wurden 
und die Erklärung, wann manchmal MACROS auch ein gute Wahl sein können.

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

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.