Nachdem mir vom einen oder anderen etwas der Kopf gewaschen wurde (meist
natürlich berechtigterweise) möchte ich mun brav angewöhnen, wie mans
richtig und Standardgerecht macht.
In der Mainschleife möchte ich der Übersicht halber möglichst nur
schöne, Namentlich eindeutige Funktionen stehen haben, kein
Bit-geschiebe etc.
Ich hänge jetzt an dem Punkt, wo ich an einem Port einige Ausgänge
setzen will. Ich habe ein byte in dem die Soll-Zustände gespeichert
werden (kommen an anderer Stelle über den uart) und in einem Setup File
werden zusätzlich Masken definiert um auch wirklich nur die Pins zu
lesen/schreiben die auch für diesen Gebrauch vorgesehen sind.
Nur wie geht es jetzt richtig?
result|=(port|~(mask));//die nicht maskierten bits einfach annehmen
5
returnresult;
6
}
7
8
Aufruf:
9
PORTA=write_io(PORTA,0b00110100,0b00100100);
1. Entspräche ja zb bei Delphi einer Procedure, 2. eine Function.
1. Hätte den charme daß es in der main "schöner" aussieht
2. Bei sagt mir mein Gefühl daß die C-Ianer das lieber sehen
Also ich kann damit garnichts anfangen.
Und bezüglich Effektivität ist ein extra Funktionsaufruf mit 3
Argumenten auch nicht der Brüller.
Ganz abgesehen, daß es Atomicity-Probleme geben wird, wenn auch
Interrupts Portpins setzen.
Ich gebe immer der Lesbarkeit den absoluten Vorrang.
D.h. ich kann mit der Pinnummer überhaupt nichts anfangen, ich will die
Funktion des Pins wissen.
Daher nehme ich dafür Bitvariablen, die lassen sich bequem mit einem
Bitmacro erzeugen.
Und daß das besonders effektiv in atomare Bitbefehle compiliert wird,
ist doch auch ein hübscher Nebeneffekt.
Hier mal ein Beispiel für Bitvariablen, die nach ihrer Funktion heißen:
http://www.mikrocontroller.net/attachment/30300/lcd_drv.zip
Welcher Pin das dann ist, ist völlig wurscht. Man kann bequem erst das
Layout machen, wie es am besten paßt und dann die Pins zuweisen.
Peter
Also da finde ich aber das klassische Bitgeschiebe oder mit _BV
übersichtlicher. Da kann man dann auch hübsche Bitnamen benutzen.
Ich habs bei mir mal so gelöst:
Danke für die Hilfen, aber nichts davon beantwortet meine Frage.
Ich wollte eignetlich nur wissen ob ich die Ports innerhalb einer
Funktion setzen kann(ob "man" das so macht) oder ob ich deren Wert
zurück gebe und dann zuweise.
Das 0b-präfix war einfach nur dazu da, hier in meinem Codebeispiel
irgendeine Maske und Irgendeinen Wert zu setzen ohne daß sich diese
wiedersprechen, vollkommen egal für die Fragestellung...
> Nur wie geht es jetzt richtig?
Keines deiner beiden Beispiele funktioniert. Die Übergabe des Ports
funktioniert so ganz grundsätzlich nicht, und auch das Maskieren hat
nicht die gewünschte Wirkung.
Wenn ich die FUnktion mit PORTA etc aufrufe, dann müßte es doch gehen
oder? (also zumindest TUT es das hier) da PORTA doch sicher eh als
Zeiger auf den port definiert ist oder?
Das Maskieren tut auch das was es soll.
Ich möchte damit im ersten Teil sicherstellen, daß nur Werte aus dem
"value" Byte berücksichtigt werden, die ich auch setzen möchte. (könnte
ja sein daß da aus irgendwelchen Gründen an der falschen Stelle eine 1
auftaucht.)
Im zweiten Teil möchte ich daß die Pins, die außerhalb meines zu
schreibenden bereichs stehen, erhalten bleiben.
Ich möchte gesetzte Pins damit ja auch wieder löschen können ohne dabei
einen Taktzyklus lang alle gelöscht zu haben und dann erst wieder die zu
setzenden (und die die an bleiben sollen) zu setzen.
Wenn da weitere Elektronik dran hängt würde diese u.U. eine Flanke
erkennen.
result|=(port|~(mask));//die nicht maskierten bits einfach annehmen
5
returnresult;
6
}
7
8
Aufruf:
9
PORTA=write_io(PORTA,0b00110100,0b00100100);
Und dies auch nur weil du den Rückgabewert dem Port zuweist.
Dass du hier den Port übergibst ist 1. überflüssig und 2. hat es keinen
Effekt.
Überleg dir mal was diese write Funktion jedes mal macht:
3 Variablen auf den Stack pushen, IP ablegen und dann einen JUMP Befehl
zu deiner Funktion. Wirklich effektiv ist das nicht.
1
#define LED_PIN 3 // Pin 3 an Port x
2
3
#define SET_BIT( BIT_ , PORT_) \
4
PORT_ = (PORT_ | (1 << BIT_))
5
6
voidmain(void)
7
{
8
SET_BIT(LED_PIN,PORTB);
9
}
Und jetzt die Quizfrage: Wieviel Code wird wohl hier erzeugt? ;-)
die VARIABLE PORTA hat mit Sicherheit die Adresse im RAM, genauer gesagt
vom Special Function Register, dass dem Datenregister von PORTA
entspricht..
Wenn PORTA jetzt eine Variable wäre, so wie du vermutest, dann würde ja
bei jedem schreibenden Zugriff die Adresse des SFR überschrieben werden.
Nicht so toll.
Also wird es wohl eine Variable vom Typ unsigned char sein, deren
Adresse &(PORTA) wohl dem SFR entspricht.
Deshalb musst du wohl die Adresse von PORTA übergeben und diesen Zeiger
dann dereferenzieren.
Wieso kann ich denn in der Main-Schleife einfach den Port per PORTA =
...
ansprechen aber in einer Unterfunktion nicht. Dann müßte doch auch beim
Aufruf in main die Adresse überschrieben werden.
Nicht als besserwissen missverstehn, ich kapiers einfach noch nicht...
Was macht "var = PORTA;"?
Es ließt den Inhalt des Port-Registers aus und speichert diesen in var
ab.
Was macht dann also "func(PORTA);"?
Es übergibt den Inhalt des Port-Registers an die Funktion, nicht den
Port selber (also seine Adresse).
Phillip Hommel schrieb:
> Wieso kann ich denn in der Main-Schleife einfach den Port per PORTA => ...> ansprechen aber in einer Unterfunktion nicht. Dann müßte doch auch beim> Aufruf in main die Adresse überschrieben werden.
Guck mal:
1
voideine_funktion(charp){
2
p=10;
3
}
4
5
voidandere_funktion(char*p){
6
*p=10;
7
}
8
9
intmain(){
10
chareine_variable=5;
11
12
eine_funktion(eine_variable);
13
/* eine_variable ist immer noch 5 */
14
15
eine_funktion(PORTA);
16
/* PORTA bleibt trotzdem unverändert */
17
18
19
andere_funktion(&eine_variable);
20
/* eine_variable ist jetzt 10 */
21
22
eine_funktion(&PORTA);
23
/* PORTA ist jetzt 10 */
24
}
PORTA ist für dich nichts anderes als eine globale Variable. Wo und wie
und warum du die zuweist, ist vollkommen egal. Wenn du die aber z.B. an
'eine_funktion' übergibst, wird -- wie bei jeder anderen Variable und
vereinfacht gesagt -- der Wert kopiert und mit diesem weitergerechnet.
die VARIABLE PORTA hat mit Sicherheit die Adresse im RAM, genauer gesagt
vom Special Function Register, dass dem Datenregister von PORTA
entspricht..
Wenn PORTA jetzt eine Variable (FALSCHE) -> ZEIGER wäre, so wie du
vermutest, dann würde ja
bei jedem schreibenden Zugriff die Adresse des SFR überschrieben werden.
Nicht so toll.
Also wird es wohl eine Variable vom Typ unsigned char sein, deren
Adresse &(PORTA) wohl dem SFR entspricht.
Deshalb musst du wohl die Adresse von PORTA übergeben und diesen Zeiger
dann dereferenzieren.
Im 2. Absatz, sollte es Zeiger und nicht Variable heißen, vielleicht
wird es jetzt klar?
Klar in Main und sonst überall wo PORTA bekannt ist kannst du PORTA
einen Wert zuweißen.
Aber deine Funktion soll ja universell, für jeden Port sein? Deshalb
musst du deiner Funktion ja irgendwie sagen, welcher Port gemeint ist.
So wie ich deine Schnittstelle verstehe willst du dann direkt darauf
zugreifen.
Aber wenn du nur PORTA übergibst, dann ist dann in der Funktion eine
lokale Variable, die ihren Speicherplatz auf dem Stack hat und rein gar
nix mit dem SFR von Port A zu tun hat. Wenn du das erreichen willst
musst du die Schnittstelle so anpassen, dass du die ADRESSE zu deinem
Port übergibst. Z.b. so
und von wegen C konform...
Wenn du es den C Compilern recht machen willst, dann solltest du casten
um Typunsicherheiten und Warnungen zu vermeiden.
z.B. was denkst was für ein datentyp kommt hier heraus?
1
unsignedcharregister_8bit=0x10;
2
unsignedshortregister_16bit=0x00;
3
4
register_16bit=~(register_8bit);
???
Bei Cosmic C Compiler ist es z.B. so, dass der 8 Bit wert auf einen 16
bit Wert gecastet wird und dann negiert!
Also: 0x10 -- 16bit --> 0x0010 -- ~ --> 0xFFEF
Deshalb haut einem ein C Compiler zig Warnungen um die Ohren, wenn man
unsauber mit Bitoperatoren arbeitet.
Nur so by the way...
Aaaah, ok jetzt hab ichs verstanden, autsch das hätte mir sogar mit
meinem bisherigen Wissen klar sein müssen *AUF_DEM_SCHLAUCH!!!*
Noch eine weitere Frage zur Sinnhaftigkeit:
Ich bekomme alle möglichen Werte auf meinem Board, eben Pin-Zustände,
evtl mehrere ADC-Messungen etc.
Diese Werte sollen jeweils mit den bereits gesendeten Werten verglichen
werden und bei Ungleichheit per uart als string verschickt werden
(string besteht dann aus dem Namen des Wertes und dem Wert selbst).
Macht es Eurer Meinung nach mehr Sinn, nach jeder einzelnen Messung je
einen Wert zu vergleichen und evtl zu Senden oder am Ende, wenn alle
Werte einmal eingelesen sind einmal alles zu vergleichen und dort zu
senden?
Mhm, du meinst einen Soll-Ist Wert Vergleich?
Hängen die einzelnen Werte logisch miteinander zusammen?
Willst du die Fehler einzeln ausgeben, also wenn nur ein Bit falsch ist,
oder wenn innerhalb eines Moduls z.B. "Analogausgang" ein Fehler
vorliegt`?
Aber da sich das ganze doch recht universell anhört, würde ich es so
machen
<1: WERT ERFASSEN X_NEU>
<2: X_NEU UNGLEICH X_ALT?> --> Nein --> Gehe zu 4
|
Ja
|
<3: SENDE INFO PER UART: X_NEU UNGLEICH X_ALT>
<4: Erfasse naechsten Wert>
Ich verstehe zwar nicht den logischen Zusammenhang und warum du die
Soll/Ist Werte vergleichst, aber es spricht eigentlich nix gegen ein
lineares Vorgehen..
> write_io(...);
Solche Funktionen habe ich echt gefressen.... :-/
Welche IO denn? Ein Timer? Eine SIO? PWM? SPI? I²C? ... oder gar ein
Port?
Bekomme ich nach dem Aufruf die Pins zurück oder das Port-Register?
write_io...
glas klar!
"Schreibe auf Eingang/AUsgang"... hoppla, Schreibe auf Eingang... nee,
stimmt ja gar nicht ;-) sorry, kleiner Scherz
joa, hab aber auch schon sinnvollere Namen gesehen ;)
Man jetzt zerhackt mir doch nicht jeden einzelnen Namen, ich wollte doch
nur was prinzipielles Fragen.
Die ganze Anwendung ist dafür gedacht, in einem Flugsimulator als
Interface zwischen Hardware und Software zu fungieren.
Der Simulator ist in Module unterteilt die jeweils unterschiedliche
Anzahlen von Schaltern, Drehgebern, Potis, 7seg-Displays und Lampen
(LEDs) haben.
Da ich nicht für jedes einzelne Modul eine andere Platine anfertigen
lassen möchte soll das ganze eben so universell wie möglich ausgelegt
sein.
Es werden vom Rechner Befehle in der Art "Lampe_irgendwas=AN",
"7Seg_wasanderes=5690" "PWM_nocheinanderer=245" (nicht genau in diesem
Wortlaut sondern vom Prinzip her) über den uart gesendet und dann die
entsprechenden Ausgaben an den Beinchen geschalten. Auf der anderen
Seite sollen die Schalter, Potis etc abgetastet werden und bei änderung
des Zustandes oder Wertes der neue Wert im gleichen Stil
"Schalter_bla=Aus", "Poti_blubb=167" an den Rechner zurückschicken.
Wird ein neues Modul in Betrieb genommen wird in einer GUI ausgewählt
was dieses Modul alles an Ein- und Ausgaben (also Schalter, LEDs...)
besitzt und jeweils einer Systemvariablen (bzw ihrer internen Nummer <-
Speicherplatz) zugeordnet. (Dh in Wirklichkeit wird nicht
"Schalter_bla=An" sondern "0x9ad3=1" gesendet und dann auf dem Rechner
interprätiert).
Diese GUI erzeugt dann ein Setup-File das in den Code eingebunden wird
und in dem ua die Zuweisung steht, also im Prinzip an welchem Pin was
dranhängt.
Dazu wird eine Pinbelegung für den Benutzer ausgegeben anhand derer
dieser die entsprechenden Kabel an die vorgesehenen Stecker/Klemmen
einschließen muß.
Das merke ich jetzt schon, aber so ist nunmal die Vorgabe.
Und genau dafür versuche ich ja auch hier einige Fragen zu stellen.
Wie ich grundsätzlich einen Port lese oder schreibe, oder einzelne Pins,
das weiß ich auch, so weit am Anfang stehe ich mit der Materie jetzt
auch nicht auch wenn ich natürlich in C noch jede Menge zu lernen habe.
Im Simulator stecken schätzungsweise 50-60 module, da werde ich nicht
für jede nen eigenen Code schreiben, zumal ja auch Leute, die von
Programmieren gar keine Ahnung haben ein neues Modul in Betrieb nehmen
sollen.
Phillip Hommel schrieb:
> Das merke ich jetzt schon, aber so ist nunmal die Vorgabe.> Und genau dafür versuche ich ja auch hier einige Fragen zu stellen.> Wie ich grundsätzlich einen Port lese oder schreibe, oder einzelne Pins,> das weiß ich auch, so weit am Anfang stehe ich mit der Materie jetzt> auch nicht auch wenn ich natürlich in C noch jede Menge zu lernen habe.> Im Simulator stecken schätzungsweise 50-60 module, da werde ich nicht> für jede nen eigenen Code schreiben, zumal ja auch Leute, die von> Programmieren gar keine Ahnung haben ein neues Modul in Betrieb nehmen> sollen.
Da du sowieso vor hast, die Konfiguration im C Code zu machen, würde ich
das so angehen, dass ich einzelne funktionale Baugruppen zur Verfügung
stelle, die von einem Wissenden zusammengestellt werden, ehe das
Programm durch den Compiler läuft.
Nur so als Idee:
1
// Output Types
2
#define LAMP_TYPE 0
3
#define SEG_7_TYPE 1
4
5
// Input Types
6
#define POTI_TYPE 1
7
8
structLampData
9
{
10
volatileuint8_t*port;
11
uint8_tpinMask;
12
};
13
14
structPotiData
15
{
16
uint8_tchannel;
17
int16_tkNom;// Value = kNom * ADC / kDenom + d
18
int16_tkDenom;
19
int16_td;
20
int16_tPrevValue;
21
};
22
23
structSeg7Data
24
{
25
uint8_tport;
26
};
27
28
structDevice
29
{
30
charname[20];
31
uint8_ttype;// LAMP_TYPE, POTI_TYPE, etc...
32
void*data;
33
};
34
35
//
36
// ab hier kommt die Konfiguration
37
// welche Geräte gist es, welche Daten haben sie
38
//
39
structLampDataErrorLamp={&PORTB,1<<PA5};// es gibt eine Led an PA5
40
structLampDataReadyLemp={&PORTB,1<<PB1};
41
structPotiDataThrottle={0,1,2,0,0};// Poti am ADC 0
Das klingt schonmal ziemlich gut, ich verstehe nur ein paar Stellen noch
nicht.
- Was hat es mit dem Knom und Kdenom und warum gibst Du die hälfte des
ADC Wertes zurück?
- Die Art wie Du die Structs ansprichst ist mir noch nicht ganz klar,
sehe ich es richtig daß mit
1
structLampDataErrorLamp={&PORTB,1<<PA5};
ein neues Struct mit dem Namen ErrorLamp gebildet wird, daß vom Struct
LampData abgeleitet wird, und dessen beide Eigenschaften *port und
pinmask mit dem Zeiger auf PORTB und "00100000" gefüllt werden?
- Wozu dient das struct "device"? Das wird doch im Code nirgends mehr
verwendet oder?
- Was macht dieser Ausdruck?
1
LampData*pLamp;
2
pLamp=(LampData*)OutDevices[i].data;
3
*(pLamp->port)|=pLamp->pinMask;
Da verstehe ich absolut nur Bahnhof, allein den Operator -> habe ich
nicht gefunden, was bedeutet das?
Vielen Dank für deine Mühe mich hier auf den richtigen Weg zu bringen...
Phillip Hommel schrieb:
> - Wozu dient das struct "device"? Das wird doch im Code nirgends mehr> verwendet oder?
Das ist der Typ von InDevices und OutDevices. Karl Heinz war da wohl
etwas tippfaul. ;-)
Korrekt muss es heißen:
1
structDeviceInDevices[]=
2
...
3
structDeviceOutDevices[]=
> - Was macht dieser Ausdruck?
1
LampData*pLamp;
2
pLamp=(LampData*)OutDevices[i].data;
3
*(pLamp->port)|=pLamp->pinMask;
> Da verstehe ich absolut nur Bahnhof, allein den Operator -> habe ich> nicht gefunden, was bedeutet das?
Das ist die Dereferenzierung eines Struct-Members über einen Pointer.
1
SomeStructa;
2
a.var=0;
3
4
SomeStruct*b;
5
b->var=0;
Was genau verstehst du sonst noch nicht an dem Code-Schnipsel? Den Cast?
Phillip Hommel schrieb:
> - Was hat es mit dem Knom und Kdenom und warum gibst Du die hälfte des> ADC Wertes zurück?
Das ist doch nur ein Beispiel.
Wenn dein Benutzer damit glücklich ist, dass der ADC immer Werte
zwischen 0 und 1024 liefert, dann brauchst du den Klimbim nicht.
Es ist allerdings manchmal nett, wenn man den ADC gleich so
'konfigurieren' kann, dass er zb Werte von 0 bis 100 (0% .. Maschine
aus, 100% Maschine läuft Vollgas) liefert und sich dein Benutzer nicht
mehr darum kämmern muss, wie man die Umrechnung von 0..1024 auf 0..100
hinbekommt (*)
Daher hätte ich in mein Poti-Device gleich eine Möglichkeit eingebaut,
dass das Device selber diese Umrechnung macht. Ist ja nur eine lineare
Gleichung
Wert = k * x + d
x ... Messwert vom ADC
k und d ... Koeffizienten der linearen Gleichung
Nun ist k aber gerne mal eine Kommazahl und Fliesskomma möchte ich vom
AVR fernhalten, wenns geht. Also ersetze ich k durch einen Bruch
kNom
Wert = -------- * x + d
kDenom
( Nom wie engl. Nominator. Der Zähler eines Bruchs
Denom wie engl. Denominator. Der Nenner eines Bruchs)
Will ich also haben, dass mein Poti in Prozent arbeitet, dann brauch ich
die Umrechnung
100
Wert = ------ * ADC_Wert + 0
1024
Will ich haben, dass mein Poti am linken Anschlag den Wert -100 und am
rechten Anschlag den Wert +100 liefert, dann brauch ich die Umrechnung
200
Wert = ------ * ADC_Wert - 100
1024
> - Die Art wie Du die Structs ansprichst ist mir noch nicht ganz klar,> sehe ich es richtig daß mit>
1
>structLampDataErrorLamp={&PORTB,1<<PA5};
2
>
> ein neues Struct mit dem Namen ErrorLamp gebildet wird, daß vom Struct> LampData abgeleitet wird
Da wird nichts abgeleitet. struct LampData ist ein Datentp wie jeder
andere auch, nur dass es eine struct ist.
Du schreibst ja auch
int xy = 5;
> , und dessen beide Eigenschaften *port und> pinmask mit dem Zeiger auf PORTB und "00100000" gefüllt werden?
Ja.
Jedes noch so grindige C-Buch weiß noch vieles mehr über Strukturen.
> - Wozu dient das struct "device"? Das wird doch im Code nirgends mehr> verwendet oder?
Schreibfehler. War schon spät in der Nacht.
> Da verstehe ich absolut nur Bahnhof, allein den Operator -> habe ich> nicht gefunden, was bedeutet das?
Das du schleunigst ein C-Buch brauchst.
Das sind Grundlagen.
struct LampData* pLamp;
definiere eine Pointer Variable vom Typ "Zeiger auf LampData"
pLamp = (LampData*)OutDevices[i].data;
nimm den data Pointer aus Outdevices[i] her (welcher ja ein void Pointer
ist) und tu so als ob er ein Pointer auf eine LampData ist. Dieser
Pointer Wert wird in pLamp (welches ja ebenfalls ein Pointer auf eine
LampData ist) abgespeichert.
*(pLamp->port) |= pLamp->pinMask;
pLamp ist ein Zeiger auf eine struct LampData. MIt -> kommt man an
dieses LampData Objekt heran und holt sich von dort die Member port bzw.
pinMask.
(*)
Edit:
Das hat dann den wunderschönen Nebeneffekt, dass dein Benutzer gar nicht
wissen muss, welche Auflösung der ADC eigentlich hat. Du kannst den
10-Bit ADC gegen einen 8-Bit austauschen, in deinem Platinen-Programm
die Umrechnung entsprechend anpassen und dein Benutzer bekommt wieder
die gewohnten Werte, so wie eh und je.
>> so weit am Anfang stehe ich mit der Materie jetzt auch nicht ...
:
> allein den Operator -> habe ich nicht gefunden
Holla :-o
Stichwort: Pointer
Ich hoffe nur, dass ich so ein universalhochgezüchtetes Programm nicht
(nochmal) in die Finger bekomme, und das (zehn Jahre später) verstehen
und anpassen muß.
>> Da verstehe ich absolut nur Bahnhof, allein den Operator -> habe ich>> nicht gefunden, was bedeutet das?> Das du schleunigst ein C-Buch brauchst.> Das sind Grundlagen.
Dem muss ich ausdrücklich zustimmen!
hs-bremen == http://www.hs-bremen.de/ ? Gibt es da kein C im ersten
Semester?
Vielleicht hilft folgendes Beispiel:
1
structstruktur{
2
chartext[64];
3
}direkt={"Hier kommt die Maus."};
4
5
structstruktur*indirekt=&direkt;// (1)
6
7
printf("direkt : %s\n",direkt.text);// (2)
8
printf("indirekt: %s\n",indirekt->text);// (3)
(1) Es wird ein Zeiger auf ein struct vom Typ struktur mit dem Namen
direkt gesetzt.
(2) und (3) Greifen auf den gleichen String in text zu. Einmal direkt
und einmal indirekt via Zeiger.
Um den ersten Zeilen noch mehr Gewicht zu geben:
C ohne Zeiger ist wie Bier ohne Alkohol.
Das mit dem C-Buch sollte ich wirklich mal angehn. Ich habe bisher
einfach "irgendwie" (eben so wie ichs zb von Delphi und VB gewohnt war)
drauflos geschreiben (ja schlagt ruhig die Hände überm Kopf zusammen)
und es hat eben trotzdem immer funktioniert, auch wenn ich globale
Variablen verwendet habe und noch nie einen Pointer. Mir ist auch die
Funktionsweise der Microcontroller klar und denke daß ich da ganz gut
durchsteige. Nur C ist so eine Sache. Ich mag einfach Quelltexte in
denen schöne Namen vergeben werden und alles in übersichtliche
Funktionen verpackt ist (eben -> Delphi, VB).
C sieht für mich immernoch sehr Kryptisch aus, daß Ihr das gewohnt seit
ist mir klar, ich habe daran noch ganz schön zu knapsen.
Ob Lothar das ganze jetzt zu universell ist oder nicht kann ich nicht
ändern, ich habe nunmal die Vorgabe was das ganze können soll, wenn Du
einen guten Vorschlag hast wie man das umgehen kann dann gerne her
damit. Das ganze wird natürlich umfassend kommentiert und dokumentiert,
daher sollte auch in 10 Jahren das ganze durchschaubar sein (zumal ich
doch die aussagekräftigen Namen so mag).
Ich versuche seit über einer Woche so etwa einen Programmablauf
zusammen zu kriegen der mir hier jedes mal wieder zerrissen wird. Habt
Ihr vielleicht einen Vorschlag wie ich das ganze angehn soll? Langsam
bin ich nämlich gründlich frustiert...
Phillip Hommel schrieb:
> Das mit dem C-Buch sollte ich wirklich mal angehn. Ich habe bisher> einfach "irgendwie" (eben so wie ichs zb von Delphi und VB gewohnt war)> drauflos geschreiben (ja schlagt ruhig die Hände überm Kopf zusammen)> und es hat eben trotzdem immer funktioniert,
Darum gehts nicht.
Irgendwie kriegt man immer alles zum Laufen.
Die Frage ist, ob du mit den Händen ein Loch im Boden gräbst, weil du
nicht weißt, dass im Schuppen ein Spaten steht.
> Ich versuche seit über einer Woche so etwa einen Programmablauf> zusammen zu kriegen der mir hier jedes mal wieder zerrissen wird. Habt> Ihr vielleicht einen Vorschlag wie ich das ganze angehn soll? Langsam> bin ich nämlich gründlich frustiert...
Das ist auch kein Wunder, wenn du mehr als die Hälfte deines
Handwerkszeugs nicht benutzt, weil du ganz einfach nicht weißt, dass es
da ist und wie man es benutzt.
Deine Aufgabenstellung ist alles andere als trivial. Möglichst
universell konfigurierbare Progamme zu schreiben ist sogar verdammt
schwer. Vor allen Dingen dann, wenn das alles auch noch performant sein
soll. Das Mindeste ist aber sein Handwerkszeug zu kennen. Und zwar nicht
nur so lala. Wenn du nicht weißt, wie dir die Sprache unter die Arme
greifen kann und wie sie dir helfen kann, kannst du diese Dinge auch
nicht einsetzen. Das ist so, wei wenn du nicht weißt, dass es in C eine
Multiplikation gibt und du daher alles auf Additionen zurückführst. Klar
funktioniert das auch. Aber du machst dir nur selbst unnötig Arbeit.
>Das ist auch kein Wunder, wenn du mehr als die Hälfte deines>Handwerkszeugs nicht benutzt, weil du ganz einfach nicht weißt, dass es>da ist und wie man es benutzt.
Da muss ich dir absolut recht geben, mir war einfach nicht klar, wie
sehr sich C von den bisher verwendeten Sprachen unerscheidet. Lernen
Lernen Lernen ;)
>Gibt es da kein C im ersten Semester?
Bestimmt bei E-Technikern und Informatikern. Ich bin keines von beiden
sondern hab mir das ganze selbst in Form eines Projektes aufgehalst über
das ich nun meine Thesis schreibe. Da es mit Programmieransätzen
eigentlich gut geklappt hat und ich mir um die besondernheiten von C
noch keinerlei Kopf gemacht habe war ich da recht zuversichtlich. Ich
bins auch jetzt eigentlich noch, ich werd mich halt erstmal etwas mehr
in C einfuchsen müssen. Ich verstehe zwar was Pointer sind, und
(abgesehen von manchen "aufm schlauch steh momenten" s.o.) ist mir auch
der Unterschied klar ob ich einer Funktion nun eine Varibale oder deren
Zeiger übergebe. Nur wie man das ganze Anwendet, da hängts eben noch
sehr.
Daher möchte ich nochmal eine etwas allgemeinere Frage zu meinem Projekt
stellen, damit ich mal einen Einstieg kriege:
Ohne Frage muß es dabei irgendwo einen Speicher geben, in dem jede
verwendete Variable auftaucht, ihr Typ (also Poti, Schalter...),deren
Wert, evtl ein vorangegangener Wert. So wie Karl Heinz das oben ja
gezeigt hat.
Jetzt sehe ich im Grunde drei Möglichkeiten wie das Programm arbeitet:
1. Variable für Variable aufrufen und jeweils entsprechend ihres Typs
die passende Abfrage starten.
2. Gerät für Gerät (Port, ADC, Timer[PWM]) ansprechen und in einem weg
alle jeweils zugewiesenen Variablen bearbeiten.
3. Interrupt-Gesteuert den ADC arbeiten lassen, und seine Werte im
entsprechenden Speicher ablegen. Ebenso stetig per Timer
interrupt-gesteuert einen PWM ausgeben (für Servos, also nicht der
eingebaute PWM).
UART Empfang ist sowieso interrupt-gesteuert. Auch in dieser Routine
einfach den empfangenen Wert entsprechend ablegen.
Jetzt soll es ja nicht ganz unkritisch sein, wenn mehrere Interrupts
paralel betrieben werden, das ist mir bewusst und würde ich aber gerne
an anderer Stelle nochmal explizit klären.
Das Programm würde in diesem 3. Fall einfach reihum die Werte im
Speicher mit Werte_alt vergleichen und bei ungleichheit einmal senden.
Welche dieser drei Varianten klingt für Euch am sinnvollsten, evtl
warum?
Phillip Hommel schrieb:
> C sieht für mich immernoch sehr Kryptisch aus, daß Ihr das gewohnt seit> ist mir klar, ich habe daran noch ganz schön zu knapsen.
C ist ganz einfach. Kryptisch wird es nur, wenn man damit kryptischen
Code schreibt. Was leider weit verbreitet ist. Scheint irgendwie cool zu
sein.
> (zumal ich doch die aussagekräftigen Namen so mag).
Dann verwende aussagekraeftige Namen. Kryptisch und nicht
aussagekraeftig ist z.b., wenn eine Funktion write_io() heisst und dann
kein io schreibt. Das gilt eigentlich fuer deine beiden Varianten (von
den Fehlern mal abgesehen) - denn was du beschreiben willst ist ein
Port. Also sollte das Ding schonmal WritePort() heissen (wie du siehst
verwende ich auch die Gross-/Kleinschreibung). Und dann sollte es das
auch tun und nicht einen neuen Portwert ausrechnen - denn wenn es das
tut, sollte es z.B. CalculatePortValue() heissen.
P.S: Auch wenn das leider auch in der Industrie weit verbreitet ist,
halte ich Trial & Error fuer die falsche Herangehensweise fuer einen
Hochschulabgaenger.
Wie gesagt, das war nur ein Beispiel womit ich etwas grundsätzliches
fragen wollte. Ich hatte mit io Input/Output (also das was man mit dem
Port machen kann) verstanden. Sorry wenn io eigentlich was anderes
bedeutet.
Zum Thema trial and error: Das möchte ich ja vermeiden, darum stell ich
hier ja so viele dusselige Fragen. Mir war vorher einfach nicht bewusst,
wieviel ich über C noch nicht weiß...Und jetzt wo mir langsam klar wird
wieviel da noch fehlt werde ich mich natürlich noch mal tiefergehend
darin einlesen und mir mal ein paar tutorials zu gemüte führen etc.
Darum hatte ich ja am Ende nochmal ganz allgemein gefragt auf welche
Ansatz ich denn am besten anfangen soll...
Ich habe den Eindruck, Du willst die eierlegende Wollmilchsau
erschaffen.
Bisher ist aber jeder gescheitert, der das versucht hat.
Versuche die Aufgabe Schritt für Schritt zu lösen.
Mache also erstmal alle Funktionen mit fester Pinzuweisung per Define.
Und wenn das wirklich läuft, kann man als nächsten Schritt die
Universalität hinzufügen.
Es ist wesentlich einfacher, kleine Schritte zu machen, anstatt die
gesamte Aufgabe als großen monolithischen Block zu sehen und dann davon
erschlagen zu werden.
Es ist vielleicht auch garnicht nötig allen möglichen Schrunz variabel
zu halten.
Mit Defines kann man bequem und übersichtlich die Zuordnung zur
Compilezeit machen.
Und anstelle haufenweise Variablen zu verbraten, lädt man einfach die
compilierten Programme per Bootloader in die einzelnen Module.
So ist man genauso flexibel.
Peter
Phillip Hommel schrieb:
> Wie gesagt, das war nur ein Beispiel womit ich etwas grundsätzliches> fragen wollte.
Ich habe dich so verstanden, dass deine grundsaetzliche Frage ist, ob
die Funktion den Port setzen sollte, oder ob sie ihn zurueckgeben sollte
und er dann von aussen gesetzt wird. Die Antwort ist ein eindeutiges:
"Kommt darauf an."
> Ich hatte mit io Input/Output (also das was man mit dem> Port machen kann) verstanden. Sorry wenn io eigentlich was anderes> bedeutet.
Es heisst input/output - aber ein Port ist nur eine Moeglichkeit der
Ein-/Ausgabe. RS232, TWI, SPI, etc. ist alles auch I/O.
> Zum Thema trial and error: Das möchte ich ja vermeiden, darum stell ich> hier ja so viele dusselige Fragen. Mir war vorher einfach nicht bewusst,> wieviel ich über C noch nicht weiß...Und jetzt wo mir langsam klar wird> wieviel da noch fehlt werde ich mich natürlich noch mal tiefergehend> darin einlesen und mir mal ein paar tutorials zu gemüte führen etc.
Vor dem Web gab es Buecher. Ich halte das heute noch fuer eine gute
Methode. Oft besser als Tutorials, die wieder sehr zur fluechtigen
Bearbeitung verleiten:
http://www.amazon.de/Programmieren-C-ANSI-2-C-Reference/dp/3446154973/ref=sr_1_2?ie=UTF8&s=books&qid=1248261705&sr=8-2> Darum hatte ich ja am Ende nochmal ganz allgemein gefragt auf welche> Ansatz ich denn am besten anfangen soll...
Sorry, aber diese Frage (die 3 Ansaetze) ist so allgemein, dass man sie
nicht beantworten kann.
Peter Dannegger schrieb:
> Es ist wesentlich einfacher, kleine Schritte zu machen, anstatt die> gesamte Aufgabe als großen monolithischen Block zu sehen und dann davon> erschlagen zu werden.
Ich denke, dass ist einer der wichtigsten Punkte, die man in der
Progrmmierung lernen muss: Das Arbeiten in kleinen Schritten. Auch
einmal einen kleinen Unweg in Kauf zu nehmen (besonders Anfängern
schrecken gerne davor zurück) um dann später auf geradem Weg ins Ziel zu
kommen.
Was auch immer dazu kommt: Das man während der eigentlichen Arbeit noch
immer einen Lernprozess hat. Dadurch dass man in kleinen Schritten
arbeitet, kann man darauf reagieren. Dadurch dass man einzelne Details
ev. in einem eigenen Testprogramm abklärt, erhält man zusätzliches
Wissen welches in die Gesamtkonzeption einfliessen kann.
Viele Menschen stellen sich programmieren so vor:
Da sitzt sich einer hin, denkt 2 Minuten über das Problem nach und dann
drischt er stundenlang auf seine Tastatur ein um irgendwann nach
Mitternacht ein Programm zu haben, welches auf Anhieb perfekt
funktioniert. Die Wahrheit könnte nicht weiter davon entfernt sein.
Zumindest ist sie mindestens ebensoweit davon entfernt, wie der Glaube,
dass es in der Chemie darum geht bunte Flüssigkeiten zusammenzuschütten
und zu hoffen dass es nur qualmt und nicht explodiert.
Karl heinz Buchegger schrieb:
> Viele Menschen stellen sich programmieren so vor:> Da sitzt sich einer hin, denkt 2 Minuten über das Problem nach und dann
[...]
> funktioniert. Die Wahrheit könnte nicht weiter davon entfernt sein.
Stimmt, meistens wird bis Mitternacht programmiert und dann versucht
über das Problem nachzudenken. ;)
Hehe, kommt mir bekannt vor.
Ich möchte mal kurz Feedback geben:
Als erstes möchte ich mich nochmal bei allen bedanken für Eure Geduld
und die vielen Ratschläge, Ihr habt mir heute echt sehr weiter geholfen!
Ich habe mir heute mal zwei Tutorials zu Gemüte geführt (die Bib is so
weit weg und es regnet und und und weitere Ausreden ;) )
Ich habe jetzt zur Motivation mal ein kleines Programmchen geschrieben
um einige Sachen zu testen und es tut auch (freude!)
Allerdings mit einer Einschränkung:
Hier mal der Code:
1
#include<avr/io.h>
2
#include<AVR/interrupt.h>
3
#include<AVR/wdt.h>
4
#include<util/delay.h>
5
#include<rs232.h>
6
#include<string.h>
7
#include<bit_ops.h>
8
9
#define BAUD 38400
10
11
//Hier wäre später eine ellenlange liste (headerfile) mit allen -zigtausend
12
//systemvariablen die es so in der Software des Simulators gibt
13
14
#define TEST_LED_1 1
15
#define TEST_LED_2 2
16
#define TEST_SWITCH_0 3
17
#define TEST_SWITCH_1 4
18
#define TEST_SWITCH_2 5
19
#define TEST_SWITCH_3 6
20
#define TEST_SWITCH_4 7
21
#define TEST_SWITCH_5 8
22
#define TEST_SWITCH_6 9
23
#define TEST_SWITCH_7 10
24
25
26
structinput_mem
27
{
28
unsignedchar*port;
29
unsignedcharpin;
30
unsignedcharvalue;
31
unsignedcharvar_number;
32
};
33
34
structinput_memswitch_0={
35
&PINA,
36
PA0,
37
0,
38
TEST_SWITCH_0
39
};
40
41
voidread_port(structinput_mem*mem)
42
{
43
unsignedchariport=*(mem->port);
44
//fand ich für mich erstmal leichter nachzuvollziehen
45
//wenn ich die werte "umspeichere", nur hier zur Übung
46
unsignedcharipin=(mem->pin);
47
unsignedcharivalue=(mem->value);
48
unsignedcharivar_number=(mem->var_number);
49
unsignedcharbuffer=0;
50
charstr[20];//19 Stellen + '\0'
51
str[0]='\0';//"echten" string daraus machen
52
buffer=get_bit(iport,ipin);
53
if(!(buffer==ivalue))
54
{itoa(str,ivar_number,10);//so richtig?
55
if(buffer==0)
56
strcat(str,"on");
57
else
58
strcat(str,"off");
59
strcat(str,"\n");
60
uart_puts(str);
61
mem->value=buffer;
62
}
63
}
64
65
66
intmain()
67
{DDRB=0xff;
68
PORTB|=(1<<PB0);//zum testen ob das prog läuft
69
USART_Init(MYUBRR);
70
uart_puts("Initialized\n");
71
for(;;)
72
73
{
74
read_port(&switch_0);
75
}
76
}
An PINA0 hängt ein DIP-Codierer mit externem Pull Up.
Das Program läuft jetzt, die LED an PINB0 leuchtet. "inizialized" wird
gesendet, wenn schalter aus dann auch "off".
Jetzt kommts wo ich hänge:
Er sendet anstatt "3on"/"3off" nur "on"/"off". Dabei kopiere ich doch
vorher die var_number (mit 3 definiert) mit Itoa schon in den String und
setze mit strcat den Zustand dahinter.
Phillip Hommel schrieb:
> Er sendet anstatt "3on"/"3off" nur "on"/"off". Dabei kopiere ich doch> vorher die var_number (mit 3 definiert) mit Itoa schon in den String und> setze mit strcat den Zustand dahinter.
Wenn du schon kein Buch hast, dann solltest du zumindest die Funktionen
die du benutzt nachschlagen. Das geht heutzutage mit google einfacher
als je zuvor.
char * itoa ( int value, char * str, int base );
Hatte denn dein Compiler gar nichts dazu zu sagen?