Archiv verlassen und diese Seite im Standarddesign anzeigen : Vorgehensweise für Programmaufbau?
squelver
27.08.2009, 09:01
Hi ihr \:D/
Wenn ich mal dazu komme, dann probier ich hier und da mal n Programmschnipsel oder eine Funktion aus.
Nu mache ich mir aber schon Gedanken, wie man "richtig" eine Struktur ins Programm bekommt bzw. wie man der Reihe nach "richtig" sein Programm auf- und ausbaut.
Womit gehts los? (ausser mit dem Grundgerüst)
Gruß copi
Eine wirkliche Richtlinie gibt es da glaube ich nicht.
Da hat halt jeder seinen eigenen Stil.
Ich unterteile meine Programme normalerweise in Hardware- und Software-Funktionsgruppen.
Wenn ich z.B. einen externen DAC verwende, kommen alle Funktionen, die diesen Baustein betreffen (Initialisierung, Ansteuerung), in eine eigene C-Datei, die nach dem Baustein benannt wird. Die Prototypen und defines dafür landen in der passenden Header-Datei. Das wäre dann ein Beispiel für eine Hardware-Funktionsgruppe.
Da ein Controllerprojekt in der Regel nicht nur eine Aufgabe erfüllt, unterteile ich auch noch in Softwarefunktionsgruppen. Bei einem Roboter könnte das z.B. eine Gruppe für Fahrfunktionen, eine für Sensorikfunktionen, eine für Schnittstellenfunktionen usw. sein.
Im das Hauptprogramm fängt dann - nach der eventuellen Deklaration von lokalen Variablen - mit den Initialisierungsfunktionen der einzelnen Funktionsgruppen an. Danach kommt dann die Hauptschleife, in der die Verknüpfung der einzelnen Funktionsgruppen stattfindet. In der main.c steht bei mir außer der main() keine weitere Funktion.
Für die Benennung von Variablen halte ich mich zumindest halbwegs an die ungarische Notation (http://de.wikipedia.org/wiki/Ungarische_Notation). Ist zwar Anfangs ein wenig lästig, wird aber schnell zur Gewohnheit und erleichtert ein späteres lesen des Programms - auch für andere - ungemein.
Dokumentation des ganzen Codes - z.B. mit Doxygen (http://de.wikipedia.org/wiki/Doxygen) - halte ich auch für sehr sinnvoll.
Gruß,
askazo
meine (zugegeben eher selten verwendete) optimale herangehensweise ist:
erstmal die initialisierung in eine methoden fassen, dann überlegen welche schnittstellen man brauch und dafür die notwendigen kommandos in methoden fassen, inklusive initialisierung
dann die prozedurprototypen zwischen variablen deklaration und main-methode einsetzen, die methoden kommen ans ende der datei
dann die ISRs zwischen prototypen und mein methode (wenn ich wüsste dass man ISRs auch auber als prototypen deklariern kann würd ich sie auch ans ende der datei verbannen)
und dann in der main das programm schreiben
generell ist das schreiben des programms an sich (bei mir zumindest) problembezogen, ich zerlege mein problem in teilbereiche und schreibe mir kleine programme um die funktionen zu testen und zu optimineren, achte dabei aber immer darauf, dass die programme sich untereinander später nicht behindern
dann füge ich sie stück für stück in einem programm zusammen, optimiere das zusammenspiel und lege eventuell doppelt verwendete funktionen (timer z.B.) zusammen
abschliessend wird noch ein wenig "inline" verteilt wo es hilft und dann nochmal alles aufgeräumt und bei bedarf alphabetisch sortiert und sauber durchkommentiert (obwohl ich letztere 2 aktionen gern auslasse ^^)
ich hoffe das beantwortet deine frage zumindest teilweise
EDIT: ich muss dem vorposter zustimmen, am besten wäre es natürlich einzelne sachen auszulagern und mit headerfile zu arbeiten statt mit prototypen ... ist halt nur so ne gewohnheit von mir beim proggen mitm atmel, das avr studio lädt leider nciht so richtig ein mit zig files gleichzeitig zu hantieren
squelver
27.08.2009, 16:13
Vielen Dank für die ausführlichen Antworten, dass ist eine sehr große Hilfe \:D/
Also generell ist es also besser, Programmteile in eine separate Datei zu packen und die dann über den Header zu laden.
Dabei würde mich interessieren, ob es zeitlich Nachteile gibt, wenn etwas aus einer externen Datei reingeladen wird?
Wird bei Programmstart alles geladen (was im Header angegeben ist) und bleibt solange im Hintergrund, bis es Verwendung hat oder nur auf Anfrage?
Hubert.G
27.08.2009, 16:37
Wenn es nicht nur eine Kleinigkeit ist mach ich mir auf einem Blatt Papier ein Ablaufdiagramm mit den grundsätzlichen Funktionen.
Wenn ein Programmteil eventuell in anderen Programmen wieder verwendet werden kann, ist eine separate Datei mit Header günstig.
Ob du Funktionen in eine eigene Datei packst oder alles in einer Datei lässt, ist vollkommen egal. Nach dem Compilieren packt der Linker ohnehin alles zusammen. Der Programmablauf steht dann im Flash und wird von dort aus abgearbeitet.
@Ceos: Ich habe eigentlich keine Probleme, mit dem AVR-Studio mit vielen Dateien zu hantieren. Im "AVR GCC"-Fenster sehe ich alle Dateien des Projektes auf einen Blick und kann diese per Doppelklick öffnen. Die Tab-Leiste unter dem Editor-Fenster ist allerdings wirklich schlecht gemacht, aber da kann man auch drauf verzichten.
@copious: Nachteile gibt es bei der verwendung externer Dateien nicht. Der Linker bastelt ja alle Dateien zu einem Hex-File zusammen. Der Prozessor bekommt nachher nicht mehr mit, ob eine Funktion in der Main-Datei oder in einer externen Datei stand.
Der große Vorteil dieser Aufteilung ist die modularität. Ich habe mir z.B. mal eine Datei mit Funktionen für die SPI-Schnittstelle programmiert (Initialisierung, Senden, Empfangen). Wenn ich nun SPI brauche, muss ich nur die spi.c und die spi.h in mein Projektverzeichnis aufnehmen und kann die Schnittstelle verwenden.
askazo
squelver
27.08.2009, 16:58
Hi :)
Da fehlte mir wohl etwas Wissen bzw. war mir das nicht klar, dass der Maschinencode aus den einzelnen Dateien was komplettes macht. War immer der Meinung, es würde so aufm Controller abgelegt, wie ich es in meinem Verzeichnis habe 8-[ O:)
Dann werd ich das ebenfalls so machen, dass ich die einzelnen Programmteile extrahiere.
Bei meinem Board brauchte ich bisher nur auf Compilieren klicken und dann hab ich mein Programm aufm Controller \:D/
Besserwessi
27.08.2009, 18:18
Zu sehr sollte man den Code aber nicht auf verschiedene Files aufteilen. Sinnvoll ist das vor allem wenn man so Teile Wiederverwenden kann. Man kann so schon getesteten Code wiederverweden, und wenn man dann später doch noch mal einen Fehler findet, kann man ihn besser in allen betroffenen Projekten korrigieren.
Der Compiler kann etwas besser optimieren wenn der Code aus eine File kommt. So groß ist der Unterschied aber nicht.
Je nach Umfang des Programms sollte man auch vorher auf Papier (oder als text file) planen was man an Daten und Unterprogrammen braucht. Auch bei den doch eher kurzen Programmen für µCs kann das helfen.
squelver
27.08.2009, 18:22
Klingt einleuchtend, gewisse Vorstellungen von den einzelnen Aufgaben und Funktionen hab ich mittlerweile, werd mir diese erstmal notieren \:D/
squelver
28.08.2009, 09:16
Hi ihr O:)
Ich hab hier meine Vorstellungen, was ich für die Programmierung benötige.
- 4x Spannung messen -> Winkelbestimmung der Gelenke
- 1x Zustand messen -> Bodenkontakt
- Routine Datenerfassung und Vergleich -> bestimmte Winkel und Zustand Bodenkontakt / Ist & soll
- Datenauswertung und Anzeige -> Anzeige über LCD
- Richtungsbestimmung bzw. Ausführung von Anweisungen -> Bewegungsabläufe
- Verarbeitung von Ereignissen -> Kollision / nichterreichte Zustände
- allgemeine Spannungsüberwachung -> Betriebsspannung
- ...
Gibt es was, was noch wichtig ist? Gehe ich manchmal falsch an die Sache ran?
- 4x Spannung messen -> Winkelbestimmung der Gelenke
initADC()
getADC()
setChannel()
- 1x Zustand messen -> Bodenkontakt
if (PINx &(1<<Pxn)) {}
- Routine Datenerfassung und Vergleich -> bestimmte Winkel und Zustand Bodenkontakt / Ist & soll
switch case oder verschachtelte if's
- Datenauswertung und Anzeige -> Anzeige über LCD
initLCD();
printLCD(char* text, unsigned char zeile);
clearLCD([unsigned char Zeile]); //Zeile nur optional
- Richtungsbestimmung bzw. Ausführung von Anweisungen -> Bewegungsabläufe
kommt drauf an wie du dich bewegst, aber bei servolenkung UND doppelantrieb brauchste wohl PWM also
initTimer() //altervnativ initSERVO()
initMotor()
setMotorDir() // wie im asuro ^^
setMotorSpeed()
- Verarbeitung von Ereignissen -> Kollision / nichterreichte Zustände
interruptservice-routinen
ISR oder SIGNAL
- allgemeine Spannungsüberwachung -> Betriebsspannung
das geht mit dem ADC
so als anregung für die zusammenstellung deiner methoden ^^
squelver
28.08.2009, 10:08
Ui :shock: danke, das sind ne Menge Informationen mit denen ich was anfangen kann \:D/
Gibts bei der ganzen Zusammenstellung etwas, was da unbedingt noch mit reingehört? Irgend eine Funktion / Methode?
Noch ein Tipp:
Mit ein paar Makros kann man sich das schreiben des Programms sehr erleichtern und erhöht die Lesbarkeit des Codes ungemein.
Ich habe da vor allem diese vier:
#define SetBit(ADDRESS,BIT) ((ADDRESS) |= (1<<(BIT))) //!< Setzt ein bestimmtes Bit eines Registers
#define ClrBit(ADDRESS,BIT) ((ADDRESS) &= ~(1<<(BIT))) //!< Löscht ein bestimmtes Bit eines Registers
#define ToogleBit(ADDRESS,BIT) ((ADDRESS) ^= (1<<(BIT))) //!< Toogelt ein bestimmtes Bit eines Registers
#define IsBitSet(ADDRESS,BIT) (((ADDRESS) & (1<<BIT))?1:0) //!< Fragt ein bestimmtes Bit eines Registers ab
So wird zum Beispiel aus der recht kryptischen Abfrage eines Eingangs auf PB4
if (PINB &(1<<PB4)) {}
ein recht einfaches
if (IsBitSet(PINB,4)) {}
und das ohne dass der Compiler einen anderen Code draus macht.
Gruß,
askazo
squelver
28.08.2009, 10:32
Hallo askazo :)
Das mit den defines hats mir schon länger angetan, find ich ne tolle Sache, hab bei meinen bisherigen Versuchen / Programmtests schon gern genau solch Tasterabfragen vordefiniert \:D/.
Was sollte man besser nicht definieren?
Ich benutze defines meistens zur Umdefinition von Zahlenwerten, z.B. für die Baudrate der UART (#define UART_BAUD_RATE 57600) oder für Berechnungen oder zur Zusammenfassung von Rechenoperationen (z.B #define UART_BAUD_SELECT (F_CPU/(USS_BAUD_RATE*16l)-1) )
Bei diesem Beispiel bräuchte ich z.B. zur Anpassung der Baudrate nur in der uart.h den Wert zu ändern und bin fertig.
Gelegentlich sind auch if-Abfragen auf Präprozessor-Ebene ganz nützlich. Beim ATMega128 habe ich z.B. zwei COM-Schnittstellen. Um meine uart.c für beide Schnittstellen benutzen zu können, habe ich mir folgenden Konstrukt gebastelt:
#define COM_PORT 0
#if COM_PORT
#define UDRX UDR1
#define UCSRXA UCSR1A
#define UCSRXB UCSR1B
#define UCSRXC UCSR1C
#define UBRRXL UBRR1L
#define UBRRXH UBRR1H
#define SIG_UARTX_DATA SIG_UART1_DATA
#define SIG_UARTX_RECV SIG_UART1_RECV
#else
#define UDRX UDR0
#define UCSRXA UCSR0A
#define UCSRXB UCSR0B
#define UCSRXC UCSR0C
#define UBRRXL UBRR0L
#define UBRRXH UBRR0H
#define SIG_UARTX_DATA SIG_UART0_DATA
#define SIG_UARTX_RECV SIG_UART0_RECV
#endif
Um den Code auf die andere Schnittstellen anzupassen, brauche ich nur noch das Makro COM_PORT zu ändern.
Man darf natürlich mit Makros nicht übertreiben. Komplexere Sachen gehören in eine Funktion. Und ganz tabu sind Sachen wie
#define wenn if
#define dann else
Damit fängt man dann an, eine neue Programmiersprache zu basteln.
Generell würde ich keine Makros basteln, die Schlüsselwörter von C enthalten. Da kann man sich besser eine inline-Funktion basteln, wenn man einen kurzen Code hat, den man mehrmals aufrufen möchte.
Gruß,
askazo
squelver
28.08.2009, 11:22
Ok, das kann ich nachvollziehen \:D/
Bisher hab ich Pullups und PIN in define geschrieben, damit ich nur noch Wörter wie "Taster 1" oder "LED1" ecta. in Bedingungen / Schleifen schreiben brauche. Hatte den Vorteil, wenn ich den Port / Pin ändere, das dann in define anpasse. Das ist doch ok, oder?
Möglich, dass sich meine Frage mit einem größeren Programm beantwortet :mrgreen:
copious,
was mir bei deiner aufstellung noch fehlt, ist der programmfluß; wie spielen die dinge zusammen? das ist gerade bei uC-programmierung IMHO ganz wesentlich. wenn du von bewegungsabläufen sprichst, wirst du genaue zeitabläufe brauchen, damit kannst du entweder mit delays arbeiten:
while(1) {
start_bewegung();
_delay_ms(10.0);
stop_bewegung();
read_sensors();
...
}
damit kann dann aber während der delays nix mehr passieren, wenn zb ein hindernis auftritt, kann das erst nachher erkannt werden.
besser, aber auch komplizierter, ist sowas:
while(1) {
if(ausloeser) {
start_bewegung();
set_timer(1, 10);
}
if(timer_elapsed(1))
stop_bewegung();
read_sensors();
...
}
damit läuft dann alles andere weiter, während auf das ende der bewegung gewartet wird, und reaktionen auf zb hindernisse können schnell erfolgen. dafür ist die dauer nicht ganz so exakt wie beim ersten fall, weil das ablaufen des timers erst erkannt wird, wenn die endlosschleife wieder bei "timer_elapsed" vorbeikommt...
Ja, das kannst Du so machen. (Bis auf das Leerzeichen zwischen Taster und 1 - das geht nicht)
Die Namen von Makros sollte übrigens komplett in Großbuchstaben geschrieben werden, damit man sie direkt als Makros erkennen kann.
(Ausnahmen bestätigen die Regel, wie man bei meinen 4 defines weiter oben sieht - da würde sonst die Lesbarkeit ziemlich leiden. Ansonsten halte ich mich aber strikt dran.)
squelver
28.08.2009, 11:31
Die 2. Version find ich klasse, man meinte mal zu mir, dass if-Abfragen nur in bestimmten Fällen angewendet werden sollten, ließe sich gleiche Aufgabe mit Switch verwirklichen?
squelver
28.08.2009, 11:33
@askazo , das Leerzeichen ist bei mir immer ein Unterstrich, hab ich vergessen ^^
Bisher hab ich nur den ersten Buchstaben groß geschrieben, bringt das Probleme mit sich?
Nein, Probleme bringt das nicht. Aber die komplette Großschreibung bei Makros ist halt eine allgemeine Konvention. Ob man sich dran hält, bleibt letztendlich jedem selbst überlassen.
Mitlerweile ist die Verwendung von Makros generell zur Glaubensfrage geworden. Manche Programmierer verpöhnen Makros und setzen stattdessen nur noch Inline-Funktionen ein. Siehe auch http://www.mikrocontroller.net/articles/Makro
squelver
28.08.2009, 13:12
Nein, Probleme bringt das nicht. Aber die komplette Großschreibung bei Makros ist halt eine allgemeine Konvention. Ob man sich dran hält, bleibt letztendlich jedem selbst überlassen.
Mitlerweile ist die Verwendung von Makros generell zur Glaubensfrage geworden. Manche Programmierer verpöhnen Makros und setzen stattdessen nur noch Inline-Funktionen ein. Siehe auch http://www.mikrocontroller.net/articles/Makro
Ich denke, man sollte Makros weiterhin benutzen, find das ne tolle Sache \:D/
Anders sehen das sicher die eingefleischten Programmierer, die sonst nichts anderes machen, kennen sicher auch den Code in und auswendig, dass sie ihre Werte direkt in den Code schreiben und den Zusammenhang sofort erkennen.
Danke für deine Hilfe ;)
oberallgeier
18.09.2009, 18:37
Hei, das ist eine schön-lehrreiche Unterhaltung. Ich habe gleich die BitSet-Definitionen übernommen und um #define IsBitClr(ADDR,BIT) ((!(ADDR) & ~(1<<BIT))) erweitert (das ist halt MEIN Faulheitsfaktor).
Als C(äh)-Newbie seit Sept. 07 liegen mir viele Fragen auf der Zunge, ich schreib mal ne wichtige her. Ich versuche ja einigermassen strukturiert zu programmieren, habe aber immer nur eine eigene Headerdatei.
// ================================================== ===============================
#include <stdlib.h>
#include <avr/io.h> // Includiert u.A. auch iomx328p.h
#include <avr/interrupt.h>
#include "D01-3_40_com_x21.h" // commonvariable
#include "D01-3_40_dme_x21.c" // irDME Routinen
#include "D01-3_40_gpd_x21.c" // ADC-Routinen für GP2D120 und Aufruf Regelung
#include "D01-3_40_inf_x21.c" // Info-Ausgaben
#include "D01-3_40_kal_x21.c" // Kalibrierungen, z.B.: irDME´s
#include "D01-3_40_mot_x21.c" // Motorroutinen incl. PWM, Regelung Motor
#include "D01-3_40_tmr_x21.c" // Timer, PWM
#include "D01-3_40_tst_x21.c" // Testroutinen
#define MCU = AVR_ATmega328pDie ist so ne Art Projektübersicht und enthält mehrere Abschnitte mit defines, Variablendeklarationen die eher nach den Softwarepaketen (Beispiel: "Motordaten: Steuerung, Motortests", "Encoderbedienung und Zeitdaten" und so ähnlich) gruppiert sind. Dahinter kommt die (hoffentlich) aktuelle Modulliste mit allen Funktionsprototypen. Damit habe ich in diesem Header eine Kurzübersicht des Programms und weiß, wo ich ALLE globalen Variablen schnell finde. Könnt ihr das so als passabel durchgehen lassen?
mare_crisium
18.09.2009, 22:44
Copious,
noch ein Vorteil der starken Modularität, die askazo Dir mit guten Gründen empfiehlt, ist, dass Du jedes Modul einzeln austesten kannst. Z.B. wenn Du ein Modul für die RS232 hast, dann testest Du's mit einem eigenen kleinen Testprogramm, bis es läuft. Fehler findest und beseitigst Du so viel schneller, als wenn das Modul schon im Gesamtprogramm eingebunden ist. Auch später, wenn Änderungen an dem Modul erforderlich werden, ist es ratsam, die geänderte Version erst wieder in der einfachen Testumgebung auszuprobieren. Erst, wenn es da zuverlässig läuft, wird es wieder in das Gesamtprogramm eingefügt. - Noch ein Tip: Ändere nie ein funktionierendes Modul! Immer erst unter einem neuen Namen speichern (z.B. mit angehängter fortlaufender Versionsnummer ..._VXX). Dann kannst Du immer auf die alten, erprobten Versionen zurückgreifen ;-). Ich möchte die Stunden nicht zählen, die ich schon mit der Reparatur verschlimmbesserter Programmteile verbracht habe :-(.
Ciao,
mare_crisium
@mare_crisium: Für sowas gibt es Versionsverwaltungssysteme ;) N kleines SVN auf der Platte hat man mit Tortoise & Konsorten ganz schnell und einfach.
@oberallgeier: Du strukturierst nur deinen Code, nicht aber das eigentliche Programm - letztendlich landet alles in einem großen File und wird erst dann kompiliert.
Wenn man richtig modularisiert (das geht auch nicht immer), hat jedes Modul einen oder mehrere Header und du greifst nur über die Deklarationen in diese(n) Header(n) auf das Modul zu.
Die Implementierung des Moduls selbst bleibt komplett verborgen, du könntest es dann jederzeit gegen eine Library austauschen oder die interne Struktur ändern, solange die Schnittstelle (die im Header definiert wird) gleich bleibt.
Leider ist das insbesondere bei hardwarenahen Sachen eine sehr schwierige Sache und führt bei konsequenter Anwendung mal zu sehr hässlichen Schnittstellen und nur suboptimalem Code.
Spätestens wenn man in C versucht, Pseudo-Objekte halbwegs sauber zu kapseln wird es seeeeehr unangenehm (mein momentaner Versuch, einen universalen (und ungenießbaren) Funkstack für die RF(M)-Reihe zu entwicklen)
mfG
Markus
was mir in hinsicht auf kapselung recht gut gefallen hat, war V-USB (vormals AVR-USB) von obdev; das könnt man sich als inspiration anschauen.
wenn man anderer leute code liest, weil man ihn debuggen oder erweitern muß, bekommt man mit der zeit ein gefühl für code-qualität. bei mir haben jedenfalls eineinhalb jahrzehnte basteln an *nix open source code dazu geführt, daß ich nach 5 minuten weiß, ob ich den code mag oder nicht, und auch warum.
wesentlich ist dabei, daß man einfach und ohne umwege das findet, was man im code sucht -- "wo passiert X, und womit hängt es zusammen?". jedenfalls wenn man, wie ich meistens, in "use the source, luke"-manier nur mal kurz was in einem größeren haufen code ändern oder erweitern will. ich denke aber, das gilt genauso für µC-projekte, wo man vielleicht in ein paar monaten oder jahren nur punktuell was ändern will.
und das matcht eigentlich ganz gut mit den üblichen programmier-empfehlungen -- kapselung, prägnante doku, vermeidung von seiteneffekten, wo es geht, und so. aber bitte nicht übertreiben, wenn man einen funktionsaufruf einmal durch 3 layer von wrappern in 3 directories verfolgen darf, wird's mühsam. kommentare wie
i++; // inkrementiere i auch.
daß mein eigener code allerdings meinen ästhetischen ansprüchen genügen würde, ist meistens nicht der fall, ich glaub, dazu bin ich zu ungeduldig, wenn's funktioniert, kann ich mich der nächsten baustelle widmen :-)
ja, und versionskontrolle ist essentiell. wenn man viel codet, sollte man sich einmal einen abend oder ein wochenende mit versionskontrollsystemen auseinandersetzen und sich ein repository anlegen.
ciao,
cm.
oberallgeier
19.09.2009, 09:51
... Du strukturierst nur deinen Code, nicht aber das eigentliche Programm ...Genau das ist für mich in diesen ersten Monaten wichtig zu lernen. Dass ich/man mit der Zeit besser "sprechen" lernt und sich auch kürzer/besser "ausdrücken" kann - na ja, das ist ein zusätzlicher Wunsch, der sich irgendwann für mich erfüllen wird.
... du könntest es dann jederzeit gegen eine Library austauschen oder die interne Struktur ändern, solange die Schnittstelle (die im Header definiert wird) gleich bleibt ...Ansätze dazu + Absichten sind bei mir vorhanden. Aber, danke - es ist manchmal für Anfänger wie mich nach den ersten Schritten ziemlich wichtig die allgemeine Richtung (neu) zu erkennen bzw. darauf gestoßen zu werden.
... wesentlich ist dabei, daß man einfach und ohne umwege das findet, was man im code sucht -- "wo passiert X, und womit hängt es zusammen ...
ja, und versionskontrolle ist essentiell. ... mit versionskontrollsystemen auseinandersetzen ...Ja, das mit dem Sich-zurechtfinden ist ein Teil dessen, warum ich meinen Code so strukturiere und auf verschiedene Pakete aufteile oder zusammenfasse.
Versionskontrollsysteme kenne ich nicht, das ganze Thema wurde mir erst jetzt durch Dich und markusj klar(er). Aber elementare Ansätze sind vorhanden: ich sichere meine komplette Projektdirectory einschließlich zugehöriger hex-Datei teilweise täglich mit einem halbwegs aussagekräftigen Directorynamen in eine subdirectory "alter code". Ausserdem schreibe ich einen "Fortschrittsbericht" in dem ich Hard-, Software- und Testarbeiten, auch Anmerkungen und Wünsche dokumentiere. Und seit vielen Jahren weiß ich: ... wenn ich "alles" aufschreibe, dann habe ich ungefähr die Hälfte von dem, was ich in drei oder sechs Monaten oder später brauche, um bestimmte Dinge wieder zu finden oder zu rekapitulieren.
Jedenfalls danke für Eure Ratschläge.
@oberallgeier: Sieh dir mal TortoiseSVN an (ich nehme an, dass du unter Windows entwickelst)
Es ist relativ einfach möglich, ein lokales Repository einzurichten, die Dokumentation zu dem Thema ist auch sehr gut.
Der Fortschrittsbericht findet dann ganz von alleine statt, weil man zu jedem "Commit" eine "Commit-Message" schreibt, die die Änderungen zusammenfassen sollte.
Eclipse bietet einem nach etwas Fingerbrechen sogar die Möglichkeit, solche "Commit-Messages" automatisch vorzubereiten.
mfG
Markus
PS: Im übrigen fällt es mir, evtl. auch weil ich objektorientiert Programmieren gelernt habe, teilweise verdammt schwer, die saubere Trennung von Modulen in C zu realisieren - vor allem weil man auf µCs dann auf malloc & co. verzichtet / verzichten sollte.
oberallgeier
23.10.2009, 19:47
@oberallgeier: Sieh dir mal TortoiseSVN an ...Schrecklich - so etwas nennt man "einen alten Mann überfordern". Ok, markusj, danke für den Hinweis. Ich kam mit diesem Hinweis in ein Land, das mir völlig unbekannt war und in dem ich noch keinerlei Orientierung habe. SCM, Heap, malloc - alles Bäume, die ich nicht kannte. Und davon ein ganzer Wald - aber es scheint wirklich interessant zu sein. Da ich C - in meiner Variante Cäh - erst seit etwas über einem Jahr nutze und noch jede Menge holpriger Lösungen in meinen Code einbaue, ist dieser Wald interessant, aber für mich derzeit gefährlich bzw. recht verwirrend. Ich werde in Stück für Stück erforschen.
A propos holprige Lösungen. Die beiden Kunstruktionen (das u ist hier gewollt) verstehe ich nicht wirklich:
(((ADDRESS) & (1<<BIT))?1:0) . . . . sowie mein eigenes Oevre
((!(ADDR) & ~(1<<BIT))) . . . . . . . . .// Fragt Bit = 0?
Vielleicht kann ich meinen Kernighan/Ritchie nicht richtig lesen. Was bedeutet im ersten Statement "?1:0 " und was bedeutet das "((!(" - aus dem KR lese ich "..Der Operand des Operators ! muss einen arithmetischen Typ besitzen oder ein Zeiger sein.." Äähhhh ? ? ? Ausserdem kann ich in meiner Erfindung doch das äusserste Klammernpaar weglassen ! ? ! ? Oder nicht?
Danke im Voraus für Eure Hilfe.
Besserwessi
23.10.2009, 20:59
Das Fragezeichen ist in C so eine Art IF ... Then .... Else...., nur hat für Werte. Bei einem Ausdruck A ? B : C wird erst A ausgewertet, wenn A = 0 ist kreigt der Ausdruck den Wert von C, sonst von B.
Wenn der Ausdruck einefach nur einer Variable zugewiesen wird kann man die Klammer außen wohl weglassen, in einem IF , While oder so natürlich nicht. Aber ein paar Klammern mehr als nötig sind ja nicht so schlimm, wie eine zu wenig.
oberallgeier
23.10.2009, 21:54
Danke Bessewessi!
Das bedeutet wohl, dass in diesem Code
(((ADDRESS) & (1<<BIT))?1:0)
die IF ... Then ... Else Lösung ziemlich überflüssig ist, oder verstehe ich das falsch? Wenn ((ADDRESS) & (1<<BIT)) falsch ist - dann ist es doch Null - bekommt aber trotzdem die Null von ?1:0 - und wenn es richtig ist - also 1 - dann bekommt es doch überflüssigerweise die 1 von ?1:0.
Ich fürchte, das verstehe ich noch nicht.
Bleibt noch die Frage offen nach der Bedeutung des "((!(..."
Richtig, das erste der beiden Makros begrenzt den Wert des "herausfallenden" Wahrheitswertes einfach nur auf die Werte 1 bzw. 0.
Wenn beim Zweiten das Ausrufezeichen ein Zeichen weiter links stehen würde, würde das ganze für mich Sinn ergeben, so kann ich nicht viel damit anfangen ...
In diesem Falle würde es übrigens ein bestimmtes Bit ausmaskieren/löschen.
mfG
Markus
squelver
11.11.2009, 13:31
Das ist peinlich, der Thread ist bei mir total untergegangen, danke für die Antworten, wird hilft mir schon ungemein ;)
oberallgeier
05.01.2010, 15:30
Hallo,
die hübschen Makro-Codeschnippsel (https://www.roboternetz.de/phpBB2/zeigebeitrag.php?p=457999&sid=cdee43d78787a6c12aeff0659db971c8#457999) von askazo gibts hier auch (klick). (http://www.mikrocontroller.net/articles/C_Makros) Damit will ich keinesfalls etwas gegen seine Kreativität sagen, im Gegenteil. Mittlerweile verwende ich askazos Makros recht häufig - danke askazo!
schmiereck
07.01.2010, 00:54
Hallo,
eine Software solle (muss aber nicht) von der Struktur her in Schichten unterteilt sein.
Jede dieser Schichten greift auf die darunterliegende zu, "weiss" aber nichts von der darüberliegenden. In C manfistiert sich das daraus, das "übergeordnete" Schichten immer nur Header (.h) Files von untergeordneten oder soche aus der eigenen Schicht inkludieren.
Es hilft ungemein, wenn man die Source-Dateien diese Schichten in eigene Verzeichnisse packt. So sieht man an den Pfadnamen gleich, wenn eine Schicht auf eine andere zugreift.
Ein Vorschlag aus der Business Entwicklung wäre, folgenden Schichtenaufbau zu verwenden (von "unten" nach "oben"):
Access: Zugriff auf die Hardware (und, streng genommen, auch auf den dynamischen Speicher, da dieser die "Datenbank" des Roboters ist). Also alles was Actoren und Sensoren betrifft.
Service: Logik der Steuerung, also Funktionen die "Highlevel" ablaufen sollen, wie "fahre eine x cm geradeaus", "fahre eine Kurve mit einem Radius von x cm und y Grad", "folge x cm einer Wand", usw. Dabei werden Funktionen der Acess-Schicht aufgerufen.
Controller: Darüberliegende "Intelligenz" die, Aufgrund der Service-Schicht, Aktionen ausführt. Diese können sein: "Fahre Aufgrund einen abgelegten Pfades zu dem Punkt XY im Raum", "Weiche einem Hindernis mit den folgenden Aktionen aus", usw.
Dieses, recht einfache Modell, kommt aus der Entwicklung von Dialog geführten Anwendungsprogrammen und geht davon aus, das auf eine bestimmte Eingabe ein bestimmtes Ergebnis berechenbar ist. Wenn die Eingabe fehlerhaft ist, tritt ein "Fehler" auf, auf den die Software entsprechend reagiert und dies zurückmeldet.
In der Robotik treten allerdings zum einen recht komplexe Eingaben auf, da zum einen die Sensoren nie die immer gleichen Egebnisse liefern (Messfehler, Ansteuerungsfehler, ...), zum anderen unvorhergesehende Ereignisse auftreten (Hindernisse, unbekanntes Retrain, ...). Hier hilft es, anlehnen an der Biologie zu nehmen und seinen Roboter mit einer weietren Schicht auszustatten, den "Verhaltensweisen" (Behaviours).
Diese sitzen auf einer weiteren Schicht zwischen den Services und dem Controller und fahren eine bestimmte Verhaltenweise ab, bis diese von einer anderen unterrochen, oder "überlagert" wird. Diese werden in einer bestimmten Priorität abgearbeitet, die sich, gesteuert durch den Controller, ändern kann.
Beispiel: Eine Fliege ist "schwanger" und sucht einen Platz, um ihre Eier abzuladen. Deshalb fiegt diese Fliege zu einem herrlich duftendem Kothaufen. Wenn ich nun zwischendurch mit meiner Hand nach ihr schlagen, wird dem Hinderniss durch bestimmte Verhaltenweisen ausweichen, ohne ihr eigentliches "Meta"-Ziel aus den Rezeptoren zu lassen - den Scheißehaufen zu finden. Der Controller stuert nur, das dem übergeordetem Ziel folgt. Wird sie durch das Ausweichmanöver von den Düften des einen, superfrischen Kothaufens abgelenkt, folgt sie evetuell einem anderen Duft, der meines super abgelagerten Steaks, das ich gerade grillen will.
Na ja, usw., weitere Schichten sind denkbar und evtl. auch sinnvoll. Weiteres in dem Thread zu, Meta-Controller, in dem ich dieses gerade einem Rp6 beizubrigen Versuche ;-) https://www.roboternetz.de/phpBB2/viewtopic.php?p=478404
Ein weiteres Stichwort sind "Zustandsautomaten", wie in dem Examples zum RP6 (Example_05_Move_05) gezeigt, auf denen mein Versuch aufsetzt. Zustandsautomaten sind eine Technik der Softwareentwicklung in der davon ausgegangen wirde, das das Verhalten einer "Schicht" immer von dem Zustand (im Idealfall einer Variablen) abhängt. Eine Änderung des Zustandes hängt immer von dem vorherigen und sonstigen Einflüssen ab.
Damit kommen wir zu den Techniken in der Softwareentwicklung zurück, mit der sich die Schichten trennen lassen. Wenn sich in einer "Unteren" Schicht ein Zustand ändert, an dem eine "obere" unmittelbar interessiert ist, kann sie dies durch ein "Callback"-Interface einem "Listener" mitteilen. Wenn diese nicht unmittelbar davon Erfahren müssen (also zeitgleich), reicht es, wenn sie die Schicht mit einem Getter (getXY()) beobachten.
Realisiert wird das durch eine Programm-Schleife in der oberen Schicht, die regelmässig die run()-Funktionen der unteren Schicht (und so weiter) aufruft. Ideal wäre hier natürlich Multitasking, aber jetzt träume ich zu sehr....
Grüße,
smk
squelver
10.01.2010, 20:03
Hey @ all , hab gar keine Mail bekommen, dass wieder geantwortet wurde 8-[
Vielen Dank für eure ausführliche Beschreibung bzw. Erklärung \:D/
Ich hoffe, dass ich mich beizeiten mal wieder mit der Materie beschäftigen kann, irgendwie kommt es immer anders.
Gruss copi
schmiereck
10.01.2010, 21:13
Hi,
schön wenn is irgendwie hilfreich ist, ich bin mal wieder in's schwafeln geraten, sorry ;-)
Bei mir hat die Benachrichtigung funktioniert,
Gruß, smk
squelver
11.01.2010, 08:01
Hi,
schön wenn is irgendwie hilfreich ist, ich bin mal wieder in's schwafeln geraten, sorry ;-)
Bei mir hat die Benachrichtigung funktioniert,
Gruß, smk
Find ich nicht schlimm, eher von Vorteil, dem kann man jedenfalls noch folgen \:D/
Bei mir / uns steht die nächste Zeit einiges an, dass ich einfach keine Zeit finde, mich intensiv weiter mit Programmierung beschäftigen kann, danach hoffe ich dann aber doch O:)
Powered by vBulletin® Version 4.2.5 Copyright ©2024 Adduco Digital e.K. und vBulletin Solutions, Inc. Alle Rechte vorbehalten.