PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : LED an und aus schalten



Lightstorm
10.03.2010, 20:58
Hallo Zusammen,

es geht um folgenden Code auf einem Olimex AVR-P28 mit einem ATMega8:



/****************************
Ein-/Ausgänge
Zeitverzögerung
Variablenlängen
*****************************/
#include <avr/io.h>
#include <util/delay.h>
#include <stdint.h>


/****************************
Anlegen der Funktionen
*****************************/
void initialize();
void LEDschalten(int status);


/****************************
Globale Variable für Zähler der An-Aus-Schleife
*****************************/
uint8_t zaehler=0; //bis max 255 ansonsten uint16_t


int main(void)
{

initialize();

while(1)
{
if (PIND & (1<<2))
{
LEDschalten(1); //LED ein
zaehler=0;
}


if (zaehler >= 1999)
{
LEDschalten(0); //LED aus nach 2sec
zaehler=0;
}
else
{
zaehler++;
_delay_ms(1);
}
}


return 0;
}


void initialize()
{
DDRB = 0x00; //B als Eingang
DDRC = (1<<5); //C5 als Ausgang
DDRD = 0x00; //D als Eingang

PORTB = 0x00; //B 00000000
PORTC = (1<<5); //C 00100000
PORTD = 0x00; //D 00000000

LEDschalten(1); //LED ein
_delay_ms(500); //0.5sec Leuchten
LEDschalten(0); //LED aus

}


void LEDschalten(int status)
{
switch (status)
{
case 1: //LED ein??
PORTC &= ~(1<<5);
break;
case 0: //LED aus??
PORTC |= (1<<5);
break;
}
}


Leider geht der Code auf dem Bord nicht. Die LED geht an und es gibt keine weitere Reaktion auf den Schalter.
Bei der Simulation in AVRStudio werden außerdem die _delay_ms übersprungen, ohne dass die Stoppuhr entsprechend weiter zählt.

Könnt ihr mir sagen, wo ich meine(n) Fehler habe...

Danke für die Hilfe...Grüße

PS: Bei mir im Programm habe ich die einzelnen Blöcke eingerückt. Hier kommt das leider nicht an :-(

Lightstorm
10.03.2010, 21:17
Noch ein paar Worte zum Sinn und Zweck des Programms:

Zuerst sollen die Ein-/Ausgänge gesetzt werden. Als Bestätigung dafür soll die LED für 0,5sec leuchten.

Danach folgt eine Abfrage: Ist der Schalter gedrückt oder nicht.
Wenn ja, wir die LED eingeschaltet durch LEDschalten(1). Danach folgt eine Schleife die entweder nur zaehler++ macht oder bei einer Dauer von 2sec die LED wieder aus schaltet und den zaehler wieder auf 0 setzt.

Die Schleife mit dem zaehler habe ich eingefügt um so ne Art Multitasking zu realisieren (hier ohne effekt, es geht mehr ums angewöhnen).

Ich hoffe das ist so verständlich... ;-)

Jaecko
11.03.2010, 06:46
Das mit dem Einrücken haut deshalb nicht hin, da du das als "Zitat" und nicht als "Code" angegeben hast.
Warum das delay nicht geht: Hast du ne Optimierung eingeschaltet? Ohne die arbeitet _delay_ms() nicht richtig.

Lightstorm
11.03.2010, 07:22
Hi Jaecko,

ich hab bei mir die Optimierung auf -Os stehen. Das ist doch so der allgemeine Standard, oder?

Jaecko
11.03.2010, 07:32
Einen Fehler seh ich grad auch noch (der sogar im Code richtig kommentiert ist): Die Zählervariable ist nur eine uint8_t. Die wird den Wert 1999 niemals erreichen.

Und das mit dem Problem im Simulator: Es könnte sein, dass das Delay garnicht übersprungen wird sondern einfach so lange braucht. Der Simulator ist leider alles andere als Echtzeit.

Und Os ist eigentlich das am häufigsten verwendete.
Kenn eigentlich kaum was, wo man komplett ohne Optimierung arbeitet.

oberallgeier
11.03.2010, 08:27
Hi Lightstorm,

mir ist unklar, warum Du auf den Ausgang für die LED den PullUp setzt: ... //C5 als Ausgang ... PORTC = (1<<5); //C 00100000 . Das ist doch eine Aktion die (eher) für Eingänge Sinn macht. Als nächstes schreibst Du nix dazu, wie der Eingang beschaltet ist: (http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Tasten_und_Schalter) wird durch den Taster ein low oder ein high auf PIND2 gelegt? Bei (Taster legt gedrückt low auf PD2) sollte auf PD2 der PullUp aktiviert werden. Aus Deiner Abfrage if (PIND & (1<<2)) lese ich, dass Du ein high auf dem Taster abfragst. (http://www.mikrocontroller.net/articles/Bitmanipulation#Standard_C_4) Hast Du dann einen Pulldown für den Eingang PD2 vorgesehen?

Dein Dilemma mit uint8_t zaehler hat Jaecko ja schon erwähnt.

Lightstorm
11.03.2010, 09:52
Hi zusammen und lieben Dank für eure Hilfe.

@Jaecko: Oops, ja das ist mir durch die Finger gelaufen, danke für den Hinweis.

@oberallgeier: Also irgendwo steige ich bei deinem Kommentar aus, bin noch blutiger Anfänger. Die Initialisierung (außer dem Blinken) hab ich aus dem Programm Button auf der Olimex-Seite entnommen (http://olimex.com/dev/avr-p28.html). Wieso der PORTC gesetzt wird weiß ich ehrlich gesagt nicht. Ich selbst hätte nur die DDR gesetzt und die PORT erstmal unbeachtet gelassen. Was du aber jetzt mit "PullUp setzen" meinst, und wieso man das eher für Eingänge benutzt versteh ich nicht.

Woran erkenne ich, ob ein Schalter ein low (eine null) oder ein High (eine 1) erzeugt?


Bei (Taster legt gedrückt low auf PD2) sollte auf PD2 der PullUp aktiviert werden. Aus Deiner Abfrage if (PIND & (1<<2)) lese ich, dass Du ein high auf dem Taster abfragst. Hast Du dann einen Pulldown für den Eingang PD2 vorgesehen?
Ja, und hier steige ich dann komplett aus. Da weiß ich gar nicht mehr was du von mir willst ;-)

Wäre über Vokabeltrainung und Hilfestellung froh...Danke für eure Mühen.

Georg

Lightstorm
11.03.2010, 10:13
Hab mir das mit dem PullUp-Wiederständen nochmal angeschaut (http://www.mikrocontroller.net/articles/AVR-Tutorial:_IO-Grundlagen#Pullup-Widerstand) und natürlich macht damit das setzen des PORT (und damit des PullUp-Wiederstands) nur beim Eingang Sinn.

Hier nochmal die aktualisierte Version des Codes:

/****************************
Ein-/Ausgänge
Zeitverzögerung
Variablenlängen
*****************************/
#include <avr/io.h>
#include <util/delay.h>
#include <stdint.h>


/****************************
Anlegen der Funktionen
*****************************/
void initialize();
void LEDschalten(int status);


/****************************
Globale Variable für Zähler der An-Aus-Schleife
*****************************/
uint16_t zaehler=0; //bis max 65536


int main(void)
{

initialize();

while(1)
{
if (PIND & (1<<2))
{
LEDschalten(1); //LED ein
zaehler=0;
}


if (zaehler >= 1999)
{
LEDschalten(0); //LED aus nach 2sec
zaehler=0;
}
else
{
zaehler++;
_delay_ms(1);
}
}


return 0;
}


void initialize()
{
DDRB = 0x00; //B als Eingang
DDRD = 0x00; //D als Eingang
DDRC = (1<<5); //C5 als Ausgang

PORTB = 0x00; //B 00000000
PORTD = 0x00; //D 00000000

LEDschalten(1); //LED ein
_delay_ms(500); //0.5sec Leuchten
LEDschalten(0); //LED aus

}


void LEDschalten(int status)
{
switch (status)
{
case 1: //LED ein??
PORTC &= ~(1<<5);
break;
case 0: //LED aus??
PORTC |= (1<<5);
break;
}
}

askazo
11.03.2010, 10:21
@oberallgeier: Wenn ein Port als Ausgang definiert ist, wird er mit dem PORTx-Register an- oder ausgeschaltet. Mit PullUp hat das nix zu tun. Und das PORTC = (1<<5) macht an der Stelle durchaus Sinn, denn dadurch wird die LED, die offensichtlich (wie es sich gehört) Low-aktiv am Controller angeschlossen ist, erst mal ausgeschaltet.

Zu den Eingängen: Da hast Du Recht.
Zur Erklärung für Lightstorm: Du kannst einen Taster auf zwei Arten an den Controller anschließen.
1) Du verbindest den einen Pin des Tasters mit dem Controller, den anderen mit GND. Das ist die übliche vorgehensweise. Damit der Controller bei offenem Taster einen definierten Zustand hat, benötigst Du einen Pull-Up Widerstand (~10k), der einfach zwischen den Eingang des Controllers und VCC geschaltet wird. Alternativ kannst Du auch den internen Pull-Up Widerstand des AVR anschalten, indem Du das entsprechende Bit im PORTx-Register setzt (was Sinn macht, so spart man ein Bauteil)
2) Du verbindest den einen Pin des Tasters mit dem Controller, den anderen mit VCC. Hier gilt das gleiche wie bei 1), nur das Du nun keinen Pull-Up sondern einen Pull-Down brauchst - also einen Widerstand vom Controller-Pin nach Masse. Den musst Du extern beschalten, der AVR hat keinen internen Pull-Down.

Gruß,
askazo

oberallgeier
11.03.2010, 10:27
... steige ich bei deinem Kommentar aus ...Tut mir leid. Ich dachte, dass Du Dein Board wenigstens in den Grundzügen kennst - und ich mir diesen Schaltplan nicht erst von irgendwo herholen muss. Also - nach dem Schaltplan zieht der Taster den PD2 auf GND (sprich : auf etwa 0V bzw. low ). Und deshalb müsste Deine Abfrage entsprechend geändert werden.


@oberallgeier: Wenn ein Port als Ausgang definiert ist, wird er mit dem PORTx-Register an- oder ausgeschaltet ...Na prima, dann weiß ich das jetzt auch. Hatte den Kommentar dummerweise gegeben, ohne das Board zu kennen - werde ich zukünftig bleiben lassen.

askazo
11.03.2010, 10:36
@oberallgeier: Wenn ein Port als Ausgang definiert ist, wird er mit dem PORTx-Register an- oder ausgeschaltet ...Na prima, dann weiß ich das jetzt auch. Hatte den Kommentar dummerweise gegeben, ohne das Board zu kennen - werde ich zukünftig bleiben lassen.
Hm, das hat aber nix mit dem Board zu tun, sondern ist grundsätzlich bei allen AVR-Controllern so - bei Deiner Erfahrung solltest Du das eigentlich wissen... ;)

Gruß,
askazo

Lightstorm
11.03.2010, 11:20
Hallo oberallgeier und askazo,

herzlichen Dank für eure Hilfe. Wie schon erwähnt - Anfänger. Ich hab mein erstes Board seit einer Woche und kenne es quasi noch gar nicht und vor allem - ich weiß auch noch gar nicht was wichtig ist/was man wissen muss.

Daher herzlichen Dank für die Erklärung. Ich werde entsprechend den Code nochmal überprüfen und meine Logik überdenken. Mal schauen ob es dann klappt.

Bis dahin...
Georg

Lightstorm
11.03.2010, 13:28
so, hier Version 2.0
Die Schaltung ist diese: http://olimex.com/dev/images/avr-p28-sch.gif

Und dieses hier der Code. Ich hatte aber noch keine Gelegenheit zu prüfen, ob er auf dem Board funktioniert. Für das Ein-/Ausschalten habe ich mich dieses Mal über #define entschieden, weil es weniger Code benötigt und die Übergabe einer Variable spart.


#include <avr/io.h>
#include <util/delay.h>
#include <stdint.h>

#define LEDon PORTC &= ~(1<<5); //LED ein mit PORTC5 als 0 da LED auf VCC
#define LEDoff PORTC |= (1<<5); //LED aus mit PORTC5 als 1 da LED auf VCC

void initialize();

int main(void)
{
initialize();

while(1)
{
static uint16_t i=0;

if(PIND & (1<<2)) //Wenn der Button gedrückt ist
{
LEDon; //LED ein und Zähler i auf 0
i=0;
}
else //Ist der Button nicht gedrückt
{ //i++ und 1ms Pause
i++;
_delay_ms(1);
}

if(i >= 1999)) //Ist i größer 2sec
{
Ledoff; //LED aus und Zähler i auf 0
i=0;
}
}
return 0;
}

void initialize()
{
DDRB = 0x00; //B als Eingang
DDRC = (1<<5); //C5 als Ausgang für LED
DDRD = 0x00; //D als Eingang

PORTD = (1<<2); //PORTD2 mit PullUp-Widerstand (Doppelt zur Schaltung)

LEDon; //zwei Mal blinken als Bestätigung
_delay_ms(200);
LEDoff;
_delay_ms(500);
LEDon;
_delay_ms(200);
LEDoff;
}

ex535
11.03.2010, 18:16
Hi,

mein Bord funktioniert mit diesem Code:
/* Sample program for Olimex AVR-P28 with ATMega8 processor
* Blinks the led with a speed ~2Hz using a simple delay loop.
* Compile with AVRStudio+WinAVR (gcc version 3.4.6)
*/

#define __AVR_ATmega8__ 1

#include <avr/io.h>
#include <util/delay.h>
#include <stdint.h>




void Initialize(void)
{
PORTB = 0x0;
PORTC = 1<<5; /* turn the LED off */
PORTD = 0x0;

DDRB = 0x0;
DDRC = 1<<5; /* PD5 as output - the LED is there */
DDRD = 0x0;

}

/* state = 0 -> Led Off
* state = 1 -> Led On
* state !=[0,1] -> Led Toggle
*/
void LedSet(unsigned char state)
{
switch (state)
{
case 0:
PORTC &= ~(1<<5);
break;
case 1:
PORTC |= 1<<5;
break;
default:
if (PORTC & 1<<5)
PORTC &= ~(1<<5);
else
PORTC |= 1<<5;
}

}


int main(void)
{
int i;

Initialize();

while (1)
{
LedSet(0);
_delay_ms(200);
//for (i=60000;i;i--);
LedSet(1);
_delay_ms(200);
//for (i=60000;i;i--);
}
return 0;
}

Gruß
Kurt

Lightstorm
11.03.2010, 19:10
Also es zeigt sich, dass bei mir die _delay_ms() Funktion nicht das tut, was sie soll. Das oben genannte Beispiel (welches das vom Anbieter mitgelieferte Beispiel ist, nur mit delay_ms) funktioniert auch nicht.

Optimierung ist auf -0s eingestellt.
#include <util/delay.h> am Anfang eingefügt.
Außerdem werde ich nach der Frequency gefragt, hier habe ich für den ATMega8-16PU ......


TADAAAA, es Klappt liebe Leute. Das Problem lag in der Angabe der Frequenz F_CPU. Mit 1000000 klappt jetzt alles ganz gut ;-)

Jaecko
11.03.2010, 21:43
ohne F_CPU aber mit delay hätte der (Pre-)Compiler aber eigentlich ne Warnung ausspucken müssen...

Lightstorm
11.03.2010, 21:52
Nicht wenn man als Frequence 1000 drin stehen hat und nicht bedenkt dass ein Unterschied zwischen Hz und kHz besteht (zu meiner Verteidigung - ich hab einfach nicht drauf geachtet oder daran gedacht).

Und so versinkt die Hilfe netter Leute in einem schwarzen Loch aus Dummheit von einem. Trotzdem herzlichen Dank nochmal. Bin begeistert wie schnell und ausführlich hier im Forum geholfen wird...

Grüße
Georg

oberallgeier
11.03.2010, 22:51
... Und so versinkt ... in einem schwarzen Loch aus Dummheit ...Einstein würde Dich wegen dieses Ausdrucks beneiden, wenn er die schwarzen Löcher schon gekannt / noch erlebt hätte. Ich dagegen wär froh, wenn meine Dummheit ein schwarzes Loch wäre - weil schwarze Löcher ausserhalb ihres Ereignishorizontes nix rauslassen. Bei denen bleibt die Dummheit drin - bei mir nicht *ggg* - und es ist mit der Dummheit noch seltsamer - die Massenbilanz stimmt nicht - dauernd strömt was raus, aber es wird nicht weniger. Das alles ist aber nicht wirklich ernst gemeint.


ohne F_CPU aber mit delay hätte der (Pre-)Compiler aber eigentlich ne Warnung ...Na ich weiß nicht, bei meinem AVRStudio meckerte er nicht. Ich hab das gerade ausprobiert. ABER ich habe auch brav unter [Configuration Options] die Frequency: [1000000] hz stehen . . . .

Lightstorm
12.03.2010, 07:36
@oberallgeier: Sehr geiler Spruch!!!

Ich hab die Frequenz auch nur in der Konfiguration in AVRStudio eingetragen. Mehr braucht es nicht.

Jaecko
12.03.2010, 08:36
Wenn die Frequenz bei der Config drinsteht, wird F_CPU automatisch definiert; es taucht automatisch die Compileroption -DF_CPU=xxx auf.
Lässt sich auch prüfen, wenn da was drinsteht und dann im Programm nochmal #define F_CPU xxx versucht wird.

Lightstorm
12.03.2010, 08:45
Ich kann doch die Frequenz des Quartz (?) auch erhöhen. Bspw. von 1MHz auf 8MHz. Und natürlich entsprechend das F_CPU anpassen. In wie fern beeinflusst das mein Code den ich geschrieben habe?

Eine höhere Rechenleistung wird es ja nicht sein. Maximal eine höhere Präzision im Timer oder?

Jaecko
12.03.2010, 08:49
Wenn der Code "sauber" geschrieben ist, läuft der mit 8 MHz genau so wie mit 1 MHz. Nur halt 8x schneller, und damit gibts auch eine höhere Rechenleistung, weil eben 8x so viel in der gleichen Zeit geschafft wird.
Da der Timer damit auch schneller läuft, kann man damit noch kleinere Zeitabstände messen bzw. noch genauer.

Wichtig ist halt, dass die Frequenz vom Quarz (Hardware) und F_CPU (Software) immer zusammenpassen.

Sind die Werte unterschiedlich, gehen zwar Berechnungen etc. auch noch (also 3+3 wird immer 6 sein), aber alles, wo es aufs Timing ankommt, geht in die Hose (Wartezeiten, Displaykommunikation, Serielle Schnittstelle etc.)

oberallgeier
12.03.2010, 10:12
... eine höhere Präzision im Timer oder?Die Ti mer können bei höheren Taktfrequenzen des Controllers natürlich besser auflösen, aber sind sie dann genauer oder präziser? Dazu sollten wir uns den Unterschied zwischen Präzision und Genauigkeit ansehen. (http://www.kowoma.de/gps/zusatzerklaerungen/Praezision.htm)

Ein anschauliches Beispiel ist das Schiessen z.B. mit einem Gewehr. Wenn 10 Schuss (bei eingespannter Waffe) sehr eng beisammen liegen, dann schießt das Gewehr sehr präzise. Dabei ist es völlig egal, ob dieses Trefferbild genau um den Zielpunkt liegt oder völlig daneben. Liegt das Trefferbild daneben, so kann man dies durch Visierverstellung korrigieren. Ebenso ist es mit Messwerten. WENN (beispielsweise) der Quarz des Controllers 1 % Abweichung vom aufgedruckten Wert hat, aber seine Frequenz völlig unverändert einhält, dann sind sehr präzise Ergebnisse möglich - aber genau sind die erstmal nicht - dazu müsste gemessen, kalibriert und justiert werden. Hard- oder softwareseitig.

Es ging weiter oben ja erstmal weniger um Ti mer, sondern eher um das delay. In der delay.h steht

double __tmp = ((F_CPU) / 4e3) * __ms;... also sind wir schon wieder bei der eingegebenen CPU-Frequenz und deren möglichen Abweichungen.

Da wir hier ja ein bisschen zu Grundlagendiskussionen gekommen sind, habe ich mir diesen Abstecher erlaubt.

Richard
12.03.2010, 10:49
Ein anschauliches Beispiel ist das Schiessen z.B. mit einem Gewehr. Wenn 10 Schuss (bei eingespannter Waffe) sehr eng beisammen liegen, dann schießt das Gewehr sehr präzise. Dabei ist es völlig egal, ob dieses Trefferbild genau um den Zielpunkt liegt oder völlig daneben.


G***** Das ist beim Bogenschießen genauso, kleiner Streukreis
egal wo, guter Schütze (Körperbeherrschung). Das gesamte
Treffer Bild wird dann mittels Schußgewicht Einstellung ins Ziehl
gebracht. :-)

Gruß Richard