PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Anfänger Quellcode



Matthias 321
07.04.2012, 10:07
Hallo Leute,
ich bin ja schon länger im Bereich AVR Programmierung unterwegs. Allerdings bisher in Basic. Jetzt wollte ich ein neues Projekt anfangen (bzw. ein altes wieder aufnehmen), nur dass ich es diesmal in C verwirklichen will. Ich will einen Drucker ansteuern, per Parallel-Port versteht sich, und habe dazu das hier (http://www.josephn.net/avr/printing_from_an_avr_atmega8515_to_a_printer) gefunden. Ich hab allerdings ein anderes Board und daher auch eine andere Pinbelegung. Ich als Basicer bin erschlagen von der Komplexität der Sprache C was Port- und Pinzuweisungen angeht, Basic ist eben doch sehr einfach gestaltet. Nun hab ich mich daran gemacht den Quellcode von obigem Link an meine Umgebung anzupassen, und wollte nun von euch wissen, ob ich alles (oder wenigstens etwas...) richtig gemacht hab, bevor ich mir meinen Drucker damit zerschieße. ;)
Hier mein Code:


//----------------------------------------------------------------------
#define F_CPU 16000000 // Taktfrequenz des myAVR-Boards
#include <avr\io.h> // AVR Register und Konstantendefinitionen
#include <util/delay.h>
//----------------------------------------------------------------------

void init_io(void);
void strobe(void);

int main(void)
{
init_io();
unsignedchar string[12] = "Hello World!"; //Text string

PINF = (1<<PINF2); //Setzt Strobe auf high

while(PINK == 255); //Wartet auf einen Tastendruck
for (unsignedchar i = 0; i <= 12; i++) //Liest alle Stellen des String
{
PORTA = string[i]; //Legt die Zeichen an den Datenausgang
strobe(); //Sagt dem Drucker, dass Daten anliegen
while(PORTB == 1); //Wartet auf die Bestätigung des Druckers, dass Daten empfangen wurden
}

}

void init_io(void)
{
DDRE = 0b11111111; //Datenleitungen DATA0-7
DDRF = 0b10100111; //Steuerleitungen vom/zum Drucker
//PF0: nc Out
//PF1: nc Out
//PF2: Strobe Out
//PF3: Busy In
//PF4: ACK In
//PF5: INIT Out
//PF6: ERROR In
//PF7: Autofeed Out
DDRK = 0b00000000; //An alle Pins sind Taster angeschlossen
PORTK = 0; //Pull-Up nicht setzen
PORTF |= (1<<PinF7); //Schaltet Autofeed ein
}

void strobe(void)
{
PINF = ~(1<<PINF2); //Setzt Stobe auf low = Drucker soll empfangen
_delay_ms(1);
PINF = (1<<PINF2); //Setzt Strobe wieder auf high
}

//----------------------------------------------------------------------


Verwendet wird ein ATMega 2560 und das myAVR Workpad. Achja, ich wollte den Code vom ersten Beispiel im Link übersetzen, sollte ich vlt. dazusagen.

MfG
Matthias

BMS
07.04.2012, 10:26
Hallo,
das meiste sollte so passen, allerdings stimmt bei den PORT- und PIN-Registern manches nicht.
Das ist so gedacht:
Das DDRx-Register legt fest, ob ein Anschluss Eingang(0) oder Ausgang(1) sein soll.
Ist der Anschluss als Ausgang eingestellt, kannst du den mit dem PORTx-Register auf High oder Low setzen.
Ist der Anschluss als Eingang eingestellt, kannst du mit dem PORTx-Register die Pullup-Widerstände aktivieren/deaktivieren.
Willst du einen Eingang abfragen, musst du das PINx-Register auslesen.

So müsste das meiner Meinung nach aussehen:

//----------------------------------------------------------------------
#define F_CPU 16000000 // Taktfrequenz des myAVR-Boards
#include <avr\io.h> // AVR Register und Konstantendefinitionen
#include <util/delay.h>
//----------------------------------------------------------------------

void init_io(void);
void strobe(void);

int main(void)
{
init_io();
unsigned char string[12] = "Hello World!"; //Text string

PORTF = (1<<PF2); //Setzt Strobe auf high

while(PINK == 255); //Wartet auf einen Tastendruck
for (unsigned char i = 0; i <= 12; i++) //Liest alle Stellen des String
{
PORTA = string[i]; //Legt die Zeichen an den Datenausgang
strobe(); //Sagt dem Drucker, dass Daten anliegen
while(PINF == 1); //Wartet auf die Bestätigung des Druckers, dass Daten empfangen wurden
}
return 0;
}

void init_io(void)
{
DDRE = 0b11111111; //Datenleitungen DATA0-7
DDRF = 0b10100111; //Steuerleitungen vom/zum Drucker
//PF0: nc Out
//PF1: nc Out
//PF2: Strobe Out
//PF3: Busy In
//PF4: ACK In
//PF5: INIT Out
//PF6: ERROR In
//PF7: Autofeed Out
DDRK = 0b00000000; //An alle Pins sind Taster angeschlossen
PORTK = 0; //Pull-Up nicht setzen
PORTF |= (1<<PF7); //Schaltet Autofeed ein
}

void strobe(void)
{
PORTF = ~(1<<PF2); //Setzt Stobe auf low = Drucker soll empfangen
_delay_ms(1);
PORTF = (1<<PF2); //Setzt Strobe wieder auf high
}

//----------------------------------------------------------------------
Vermutlich wird man noch die while-Schleifen anpassen müssen, in der verlinkten Seite wird bei der zweiten while-Schleife das ACK abgefragt, ist bei dir an PF4 ?

Grüße, Bernhard

Matthias 321
07.04.2012, 10:31
Ok, vielen Dank! Das System der Pin und Port Register hatte ich noch nicht so recht verstanden. ;)

Stimmt, um die While-Schleife hab ich mich noch nicht gekümmert. PORTA hat da garnix zu suchen. Und ACK ist an PF4, das ist richtig.

radbruch
07.04.2012, 11:13
Hallo

Vielleicht funktioniert es auch nur mit Busy:


// Druckeransteuerung mit AVR über Centronics-Schnittstelle mic 7.4.2012

// https://www.roboternetz.de/community/threads/57421-Anf%C3%A4nger-Quellcode

// PORTE ist D0-D7
// PORTF sind die Steuersigale:

// PF0: Selin Out Low = Drucker ausgewählt
// PF1: nc Out
// PF2: Strobe Out fallende Flanke = Datenübernahme
// PF3: Busy In Low = nicht beschäftigt
// PF4: ACK In Low = Übernahmebestätigung
// PF5: INIT Out Low = Drucker initialisieren
// PF6: ERROR In Low = Fehler aufgetreten
// PF7: Autofeed Out Low = Linefeed bei CR

// http://www.hardwareecke.de/berichte/schnittstellen/centronics.php
// http://de.wikipedia.org/wiki/IEEE_1284

#define F_CPU 16000000 // Taktfrequenz des myAVR-Boards
#include <avr\io.h> // AVR Register und Konstantendefinitionen
#include <util/delay.h>

int main(void)
{
unsigned char i;
unsigned char text[14] = "Hello World!\n\r"; //Text string + LFCR

DDRE = 0b11111111; // Datenleitungen DATA0-7
DDRF = 0b10100111; // Steuerleitungen vom/zum Drucker
DDRK = 0b00000000; // An alle Pins sind Taster angeschlossen
PORTK= 0b00000000; // Wie sind die Taster angeschlossen?
//PORTK= 0b11111111; // bei Bedarf Tastenpullups einschalten

PORTF |= (1<<PF7); //Schaltet Autofeed aus
PORTF |= (1<<PF2); //Setzt Strobe auf high


while(PINK == 255); //Wartet auf einen Tastendruck

for (i = 0; i<14; i++) //Liest alle Stellen des String (Index 0 bis 13!)
{
while(PINF & (1<<PF3)); // Warten wenn Drucker beschäftigt (busy=high)
PORTE = text[i]; // Legt die Zeichen an den Datenausgang

PORTF &= ~(1<<PF2); // Setzt Stobe auf low = Drucker Daten übernehmen
_delay_ms(1);
PORTF |= (1<<PF2); // Setzt Strobe wieder auf high
_delay_ms(1);
}

while(1); // Programm darf nie beendet werden!
return(0);
}(ungetestet)

Gruß

mic

P.S.:
Achtung, alle nicht extra gesetzte Steuersignale zum Drucker sind low!

Matthias 321
07.04.2012, 17:38
Ok, ich hab genau deinen Code übernommen (außer dass ich keine SelectIn Leitung hab) und folgendes passiert: Der Drucker meldet Error, vermutlich weil die Tinte leer ist, das zeigt er auch an. Strobe ist auf high, passt. Autofeed ist an, passt auch. Allerdings wird der Chip warm (bin ich nicht gewöhnt, kann aber daran liegen, dass er sich in einer Schleife aufhängt, die recht rechenintensiv ist) und, was noch schlimmer ist, der Drucker meldet ständig "Busy", sobald ich ihn anschließ. Und er initialisiert sich, ist auch nicht geplant. Einen richtigen Rest bekomm ich aber nicht hin, da der Chip über den Drucker mit Strom versorgt wird, am Strobe liegt ja eine gewisse Spannung an, mit der der Drucker low erkennt. Aber an sich macht der Drucker nichts, ACK ist auch immer high, selbst wenn der Drucker nicht mit dem Board verbunden ist.

MfG
Matthias

PS: Ich warte mit dieser Schleife auf einen Tastendruck, ist die so richtig? PINK ist lustigerweise rot im Editor ^^ while(!(PINK & (1<<PINK0))); //Wartet auf einen Tastendruck"

radbruch
07.04.2012, 18:23
Hallo

Bin ja kurz davor 'nen alten Drucker zu reanimieren. ;)

Hier nun eine verbesserte Variante:


// Druckeransteuerung mit AVR über Centronics-Schnittstelle mic 7.4.2012

// https://www.roboternetz.de/community/threads/57421-Anf%C3%A4nger-Quellcode

// PORTE ist D0-D7
// PORTF sind die Steuersignale:

// PF0: nc Out Low = Drucker ausgewählt
// PF1: nc Out
// PF2: Strobe Out fallende Flanke = Datenübernahme
// PF3: Busy In Low = nicht beschäftigt
// PF4: ACK In Low = Übernahmebestätigung
// PF5: INIT Out Low = Drucker initialisieren
// PF6: ERROR In Low = Fehler aufgetreten
// PF7: Autofeed Out Low = Linefeed bei CR

// http://www.hardwareecke.de/berichte/schnittstellen/centronics.php
// http://de.wikipedia.org/wiki/IEEE_1284

#define strobe (1<<PF2)
#define busy (1<<PF3)
#define ack (1<<PF4)
#define init (1<<PF5)
#define error (1<<PF6)
#define autofeed (1<<PF7)

#define F_CPU 16000000 // Taktfrequenz des myAVR-Boards
#include <avr\io.h> // AVR Register und Konstantendefinitionen
#include <util/delay.h>

int main(void)
{
unsigned char i, t;
unsigned char text[14] = "Hello World!\n\r"; //Text string + LFCR

DDRE = 0b11111111; // Datenleitungen DATA0-7

DDRF = strobe | init | autofeed; // Steuerleitungen zum Drucker (Ausgänge)
PORTF = strobe | init | autofeed; // Kein strobe, kein init und kein autofeed
PORTF = busy | ack | error; // PullUps für die Eingangssignale aktivieren

t=PINK; // Tastenstatus einlesen
while(PINK == t); // Hat sich was geändert?

for (i = 0; i<14; i++) //Liest alle Stellen des String (Index 0 bis 13!)
{
while(PINF & busy); // Warten wenn Drucker beschäftigt (busy=high)
PORTE = text[i]; // Legt die Zeichen an den Datenausgang

PORTF &= ~strobe; // Setzt Stobe auf low = Drucker Daten übernehmen
_delay_ms(1);
PORTF |= strobe; // Setzt Strobe wieder auf high
_delay_ms(1);
}

while(1); // Programm darf nie beendet werden!
return(0);
}(ebenfalls ungetestet)

Ich vermute, der Drucker wurde dauernd im Init gehalten. Diese Version setzt nun auch das Init-Bit.

Nach dem Reset ist Port K sowieso Eingang, deshalb braucht man keine zusätzliche Initialisierung. Allerdings weiß ich immer noch nicht, wie die Taster angeschlossen sind. Schalten sie gegen Vcc oder gegen GND? Externe PullUps/PullDowns?

Der String endet mit LFCR, deshalb kann man das Autofeed beim Drucker deaktivieren. Wenn's mal klappt kannst du das ja ändern...

Die PullUps an den Eingängen sind eher ein Versuch, scheint mir vernünftig. Port K wird bei mir (KamAVR) akzeptiert.

Gruß

mic

P.S.: Der Mega2560 hat tatsächlich einen Port K: http://www.atmel.com/Images/doc2549.pdf
(Über 'nen Mega32u4 bin ich noch nicht hinausgekommen...)

Matthias 321
07.04.2012, 18:35
Ahhh, ich ich glaub ich habs! "34: error: initializer-string for array of chars is too long" Dieser Fehler hat mich stutzig gemacht, ich dachte du hättest einen Fehler gemacht, da du das in einer 14 "langen" Stringvariable speichern willst, obwohl es ja 16 Lettern sind. \n und \r werden von meinem Compiler/Interpreter oder was auch immer dafür zuständig ist nicht als eigenständiges ASCII Zeichen bzw. Befehl erkannt! In der ersten Version hab ich's einfach "verbessert". Robotik Visionäre soll man nicht anzweifeln... ;)

Und vielen Dank wegen dem "#define strobe (1<<PF2)", sowas hab ich gebraucht, ist einfach übersichtlicher.

EDIT: Ganz vergessen: Die Taster sind aktiv high, zumindest schließe ich das aus diesem Datenblatt (http://shop.myavr.de/index.php?sp=download.sp.php&suchwort=dl125).

Matthias 321
07.04.2012, 18:58
Gut, ich habe deinen neuen Code aufgespielt (ohne LFCR Anweisung) und ausgeführt. Der Drucker druckt! Zuminsdest tut er so, der Druckkopf ist stark eingetrocknet, hab ihn grad einer Alkohol-Spezialwäsche unterzogen, mal sehen, ob er nach der Einwirkzeit wieder frei ist.

Ich danke euch für die Unterstützung! Lasst bitte den Thread noch abonniert, ich hab bestimmt noch ein paar Fragen in der nächsten Zeit und ihr seid echt fit auf dem Gebiet... ;)

sternst
07.04.2012, 19:01
\n und \r werden von meinem Compiler/Interpreter oder was auch immer dafür zuständig ist nicht als eigenständiges ASCII Zeichen bzw. Befehl erkannt!Jede Wette, dass dem nicht so ist.
14 ist nur einfach zu wenig für "Hello World!\n\r". Die benötigte Array-Größe hierfür ist 15 (14 Zeichen + Null-Terminierung).

radbruch
07.04.2012, 19:03
"Robotik Visionäre soll man nicht anzweifeln" ist natürlich Unsinn. Welchen Compiler verwendest du denn? Ich vermute, der Preprozessor interpretiert die Steuerzeichen im String ...

@sternst:
Das Array "unsigned chartext[14]" besteht erstmal nur aus 14 Elementen vom Typ "unsigned char". Ein String mit Endekennung wird es wohl erst so:
unsigned char text[] = "Hello World!\n\r";

(Ich hoffe sehr, dass dies so stimmt. Robotik Visionäre sind in meinem Falle C-Einsteiger)

Gruß

mic

Matthias 321
07.04.2012, 19:18
Hi,
"unsigned char text[15] = "Hello World!\n\r";" funktioniert jedenfalls (d.h. ich bekomme keinen Fehler beim compilieren, ausprobieren geht ja noch nicht). Und nur so aus Interesse, wie bekomm ich denn ein Backslash in einen String? Per doppeltem Backslash (\\)?

sternst
07.04.2012, 19:32
Das Array "unsigned chartext[14]" besteht erstmal nur aus 14 Elementen vom Typ "unsigned char". Ja eben, und "Hello World!\n\r" ist ein String mit Endekennung, der eben in dieses 14-Elemente-Array nicht komplett hineinpasst. Daher die Warnung (*).

(*) der OP verwendet vermutlich -Werror, daher bei ihm ein Fehler.

radbruch
07.04.2012, 20:10
unsigned char text[14] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!', '\n', '\r'}; // Text string + LFCR

...sind 14 Elemente.

Matthias 321
07.04.2012, 20:17
Es funktioniert so aber nicht, ich denke sternst hat recht. Mit text[15] geht's, obwohl das in meinen Augen am wenigsten Sinn macht. Aber wenn das die "Endekennung" ist, solls mir recht sein, solang es funktioniert! :)

Uh, guckt der Smylie böse, ist nicht so gemeint... ^^

radbruch
07.04.2012, 20:20
Echt spannend. Meine Version:

C:\Users\mic>avr-gcc -dumpversion
4.3.3

"solang es funktioniert!" ist das Wichtigste. ;)