PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Übergabe von Buchstaben an Unterfunktionen



corone
25.02.2007, 01:00
Hallo allesamt =)
Ich bringe auf meinem RN-Contro aktuell über dieses Programm eine LED zum blinken. *Sagt jetzt nichts wegen der Zählschleifen, die sind nur testweise...*


#include <avr/io.h>



int main(void)
{
//Initialisierungsphase
unsigned long counter;

DDRC = 0x10; //set PORTC for output

while(1)
{
setpout(2,4,1);

//wait
counter = 0;
while(counter < 160000000)
{
counter++;
}

setpout(2,4,0);

//wait
counter = 0;
while(counter < 160000000)
{
counter++;
}
}

return 1;
}



int setpout(int p, int n, int io)
{
if(p == 0) {PORTA = io<<n;} //set PORTA.n high oder low
if(p == 1) {PORTB = io<<n;} //set PORTB.n high oder low
if(p == 2) {PORTC = io<<n;} //set PORTC.n high oder low
if(p == 3) {PORTD = io<<n;} //set PORTC.n high oder low
return 0;
}




Das ganze soll mal mit etlichen Unterroutinen so eine Art "Betriebssystem" für das Board werden, damit ich nich jeden scheiß ständig direkt einstellen muss. Gut, fällt bei den Digitalports nu nich so auf, aber man muss ja klein anfangen... Ich möchte gerne der Funktion "setpout" als erstes ("p") anstelle der Zahl einen Buchstaben übergeben, der den jeweiligen Port kennzeichnet. Also die Form "setpout(port,stelle,wert)". Damit könnte ich mir dann die if-Abfragen sparen.

Frage: wie geht das? Hab fleißig Google, die RN-Wiki und diverse andere Seiten gequält, aber irgendwie wurde ich daraus nicht recht schlau :-/

Wäre für etwas Hilfe dankbar :)


Gruß
Corone

Ach ja, so am Rande... ich habe immer "PORTX = y<<z;" geschrieben. In Beispielen fand ich aber immer anstelle des einfachen =Zeichens |= bzw &= und statt ner "0" für "aus" so was wie "~(1<<z)". Warum? Welchen Sinn hat das? Hat es irgend einen Nachteil, so wie ich gleich mit "bla = 0;" zu arbeiten?

NumberFive
25.02.2007, 07:16
Wie so ein Buchstabe dir die If's er sparen können ?

Auch wenn du den Buchstabe übergibst ändert das nicht's an de Case oder If strucktur. Den PORTB ist ein Synonym einer Adresse und wird nicht Zur lauf zeit sonder zur Compeliert Zeit Interpretiert. Also falls du dir vorgestellt hast das du durch Buchstaben den Port namen zusammen bauen kannst vergiss es dan geht nicht.

PORTA für den AMGEA 16 heist
#define PORTA _SFR_IO8(0x1B)

Ich hoffe das es dir hilft.

Gruß

Javik
25.02.2007, 11:30
Also wenn du PORTX|=(1<<PXY) verwendest setzt du NUR das Y-te Bit im PORTX auf 1 alle anderen Bits in PORTX bleiben wie sie sind.
Bei deiner Methode PORTX=1 (oder was auch immer) werden alle Bits gesetzt und eventuelle andere Pinzustände überschrieben.

Wenn du es dir einfacher machen willst könntest du das mit den Ouptupins auch so machen:

#define OutputA1 DDRA|=(1<<PA1)

dann musst du um PA1 zum output zu machen nur noch OutputA1; schrieben.
Wenn du das jetzt durchziehst und für alle Pins das machst (scheis viel Arbeit und vlt umständlich) dann kannst dus einfacher haben ;)

Ansonsten schließ ich mich NumberFive an, dass du nämlich PORTA nicht aus PORT und A zusammensetzen kannst.

Grüße Javik

SprinterSB
25.02.2007, 17:41
Wenn du das wirklich so machen willst, dann ginge es so:


void set_port (const char port, const uint8_t pad)
{
if ('A' == port)
{
PORTA |= (1 << pad);
}
if ('B' == port)
{
PORTB |= (1 << pad);
}
}


Das würde zunächst dazu führen, daß das Portsetzen recht lange dauert.

Anstatt diese Funktion in ein C-Modul zu schreiben, könntest du sie in den entsprechenden Header tun und die Funktion als


static inline set_port (const char, const uint8_t);

oder


static set_port (const char, const uint8_t) __attribute__((always_inline));

deklarieren. Dann weiß GCC schon zur Compilezeit, wie die Werte in der Funktion sind und optimiert das bis nur noch eine Maschineninstruktion da steht.

Aber was hat es für ein Vorteil, set_port ('A', 7) zu schreiben anstatt PORTA |= (1 << 7) :-k

Ich selbst benutze allerdings auch Port-Support Makros, die Code folgender Gestalt erlauben:


// irgendwo in "ports.h"
#define PORT_LED PORTB_6
#define PORT_TASTER PORTC_1

// irgendwo in "module.c"

// LED anschalten
MAKE_OUT (PORT_LED);
SET (PORT_LED);

// Taster ist IN + PullUp
MAKE_IN (PORT_TASTER);
SET (PORT_TASTER);

if (IS_SET (PORT_TASTER)) ...


Das erlaubt eine zentrale Port-Definition und man muss nicht PORTx, PINx, DDRx anpassen, wenn man was an der Schaltung ändert.

Die gcc-Optimierungen führen dazu, daß die riesigen Makros zu einer einzigen Maschineninstruktion kollabieren -- ganz wichtig, weil das Setzen/Löschen von Ports ATOMAR sein muss!

Zudem muss der Port nicht physikalisch sein, sondern es kann auch ein virtueller Port sein (also zB einer, der im RAM abgebildet ist und über Kommunikation mit einem Portexpander gelesen/geschrieben wird).

Die Makros sind allerding nicht schön. Zudem müssen die Ports zur Compilezeit bekannt sein, in einer lib funktioniert des Ansatz also nicht.

corone
25.02.2007, 19:42
gut, danke :-)

das hilft mir weiter. *jetz dann auch gleich delay-loops einbaut*






ach ja... immer, wenn ich in Programmers Notepad 2 das Program-Tool aufrufe, kommt das hier:


> "make.exe" program
avrdude -p atmega32 -P lpt1 -c stk200 -U flash:w:RNControl-Test.hex -U eeprom:w:RNControl-Test.eep
avrdude: can't open device "giveio"

avrdude: failed to open parallel port "lpt1"

make.exe: *** [program] Error 1

> Process Exit Code: 2
> Time Taken: 00:00


Compilieren funzt hingegen einwandfrei. Ich übertrage die Hex-Files derzeit immer per PonyProg... dort bekomme ich aber auch jedes mal ne Meldung "Falsches oder fehlendes Device (-24)"... wenn ich da dann auf "Ignorieren" klicke flasht er trotzdem ohne Probleme.
Ich verwende das Kabel von robotikhardware.de, im makefile habe ich den stk200 als Programmer eingestellt. Weiss jemand, woran das leigen könnte? *gerne direkt von PN2 aus arbeiten würde*



Gruß
Corone

Javik
25.02.2007, 20:20
Also wenn du von PN2 aus Ponyprog starten willst um den µC zu proggen hab ich da was für dein Makefile ;):



ISPEXE = C:\.....\PonyProg2000\PONYPROG2000.EXE

isp: $(TARGET).hex
echo -e "SELECTDEVICE $(MCU)\nLOAD-PROG $(TARGET).hex\nWRITE-PROG" >isp.e2s
$(ISPEXE) isp.e2s

eeprom: $(TARGET).eep
echo -e "SELECTDEVICE $(MCU)\nLOAD-DATA $(TARGET).hex\nWRITE&VERIFY-DATA" >isp.e2s
$(ISPEXE) eeprom.e2s


Einfach einfügen und noch den Pfad auf deine Installation von Ponyprog2000 einstellen und du kannst durch das Ausführen von "isp" den µC programmieren oder per "eeprom" das Eeprom mit deinen im Eeprom abgelegten Variablen flaschen.

Das Ausführen machst du so wie du es auch für "make all" gemacht hast nur das du eben als Parameter nun "isp" bzw "eeprom" eingibst.
Wenn du "isp" ausführst compiliert der Kompiler automatisch das Programm erstellt die Hex Datei und startet dann Ponyprog2000 welches deinen µC flasht und sich dann wieder automatisch schliest ;) EXTREM nützlich :D
Ich find auch die schnelltastenbelegung sehr nützlich :)
wenn noch Fragen sin stell sie ;)

corone
25.02.2007, 20:47
ok... habe in http://winavr.sourceforge.net/install_config_WinAVR.pdf die antwort gefunden. hatte nich gewusst, dass man das teil erst installieren muss :-)

hab jetzt ne verbesserte version meines blinkprogrammes geschrieben, enthält delay-loops und ne eigene header-datei, die ich schrittweise ausbauen werde. schaut euch das zeug bitte mal an... irgendwo fehler drin? will möglichst standard-gerecht bleiben, damit der kram auch läuft :-)





//rncontrol.h
static inline void setpout(const char p, const uint8_t n, const uint8_t io)
{
if(p == 'A') {PORTA = io<<n;} //set PORTA.n high oder low
if(p == 'B') {PORTB = io<<n;} //set PORTB.n high oder low
if(p == 'C') {PORTC = io<<n;} //set PORTC.n high oder low
if(p == 'D') {PORTD = io<<n;} //set PORTC.n high oder low
}



//RNControl-Test.c
#include <avr/io.h>
#include <avr/delay.h>
#include <rncontrol.h>



int main(void)
{
//Initialisierungsphase
unsigned int counter;

DDRC |= 0x10; //set PORTC.4 for output

while(1)
{
setpout('C',4,1);

//wait 16*65536
counter = 0;
while(counter < 16)
{
counter++;
_delay_loop_2(65535);
}

setpout('C',4,0);

//wait
counter = 0;
while(counter < 16)
{
counter++;
_delay_loop_2(65535);
}

}
return 1;
}




ach ja... aus "static inline set_port" habe ich "static inline void setpout" gemacht, weil der compiler sonst immer meckerte, ich hätte da keine rückgabe, obwohl das ne integerfunktion ist. ist das richtig so, wie ich es gemacht habe?

vielen dank für eure hilfe!

gruß
corone

SprinterSB
26.02.2007, 19:40
Zu den Standards:

Die { und } werden üblicherweise links ausgerichtet:


while (foo)
{
if (bar)
{
bazz();
}
}


Macht dir und anderen das Quelle-Lesen einfacher.


Um die C-Quelle Standard-konform zu halten, gibt es Optionen wie -ansi, -pedantic, -std=c99, -Wmissing-prototypes, -W, -Wall, ...

Mit deinen Makros musst du darauf achten, daß die Werte zur Compilezeit bekannt sind. Ausserdem gibt es Grenzen, ab der eine Funktion als "wert sie zu inlinen" betrachtet wird. Wenn die Funktion (setpout) nicht geinlint wird, fällt sie u.U recht groß aus, weil die o.g. Optimierung nicht gemacht werden kann. Sind die Werte zur Compilezeit nicht bekannt, bläht das den Code auf.
Inlining erzwingen kannst mit dem o.g. Attribut. Anwarnen, wenn Inlining nicht klappt mit -Winline oder so. Musst eben bei dem gcc-Schaltern schauen.

corone
26.02.2007, 22:39
gut, danke dir :-) *das mit der ausrichtung mal ändern wird*

sonst irgendwelche fehler drin oder optimierungsvorschläge vorhanden?

SprinterSB
27.02.2007, 10:41
Ja.

setpout tut nicht das, was du willst.

Eine Port wird HIGH gesetzt durch


PORTx |= (1 << pad);

und low gesetzt durch


PORTx &= ~(1 << pad);


Dein setpout setzt zwar den gewünschten Port wie du willst, aber alle anderen Ports des PORT-Registers auf LOW.

Wie ein BSP (board support package) aussehen könnte wird dir vielleicht klarer, wenn du ein paar kleine, funktionierende Projekte gemacht hast und siehst, was die brauchen und welche Anforderungen sie an ein BSP stellen würden.

Da das BSP in unterschiedlichen Prokejten zum Einsatz kommen soll, musst du schon sehr genau überlegen, wie du es implementierst. weil es nicht so toll ist, das BSP nachträglich zu ändern weil du zB das Interface ändern musst und inkompatibel zu älteren Versionen wirst. Daher der Vorschlag, erst mal ein paar Erfahrungen aus der Praxis zu sammeln.

Zudem musst du dir überlegen, ob du das BSP als Quellarchiv oder als Bibliothek zur Verfügung stellen willst. Mach dir dazu die Unterschiede/Schwächen/Stärken der beiden Lösungen klar.

corone
27.02.2007, 17:26
ahh... Danke :-)

*das sofort mal ändern wird*
Was meinst du mit Bibliothek/Quellarchiv? Ist eine Bibliothek eine Header-Datei und ein Quellarchiv eine C-Datei? Oder meinst du damit doch etwas anderes?


Vielen, vielen Dank für eure Hilfe hier! Das bringt mich wirklich n großes Stück weiter!


Gruß
Corone

SprinterSB
09.03.2007, 18:09
Eine Bibliothek ist eine Binärdatei die Objects enthält, die beim linken dazugebunden werden. Zudem sind Header dabei, welche die Bibliotheksfunktionen bekannt machen (Prototypen) etc.

Ein Quellarchiv ist ein Archiv mit Quellen, hier also C-Dateien und Header, die ein gewissen Grad an Allgemeinheit haben und in verschiedenen Projekten menutzt werden können. Hier liegen die Dateien nicht binär vor sondern sind lesbar (ASCII).

Im optimalen Falle werden beide ergänzt durch eine API-Doku.

Beide ANsätze unterscheiden sich in
- Allgemeingültigkeit
- Portierbarkeit
- Effizienz
- Konfigurierbarkeit/Parametrisierbarkeit
- Verwendung
- etc

corone
10.03.2007, 15:52
ahhh... vielen dank!

dann ist's klar: Quellarchiv mit einer header-datei.
in zwei oder drei wochen könnte die erste beta-version zur verfeinerung fertig sein, wäre cool, wenn dann mal jemand wegen standards etc. drüber gucken könnte.
ach ja, die lange zeit liegt nicht unbedingt am umfang des projekts, sondern an meiner nicht vorhandenen zeit zum weiterschreibseln. nicht, dass ihr euch wundert =)


gruß
corone