PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : atmega16 Füllstand



Jimmy83
20.03.2010, 17:48
Hallo:-)

also ich beschäftige mich gerade mit dem atmega16. Bin auf dem Gebiet noch ein blutiger Anfänger und wäre überglücklich wenn mir jemand bei meinem Problem helfen könnte:-)
Ich habe einen Schwimmer in einem Behälter und 2 Sensoren.(Hoher und tiefer Füllstand) Ist es möglich mit dem Atmega die Zeit zu "stoppen" die mein Schwimmer braucht um vom hohem Füllstand auf den niedrigen zu gelangen!? Wenn das geht würde ich mich über jeden Quelltext freuen:-)

schonmal vielen Dank

021aet04
20.03.2010, 18:44
Wilkommen im RN Netz.
Wie willst du die gestoppte Zeit anzeigen?

Wenn Endschalter oben gedrückt
Starte Timer
Wenn Endschalter unten gedrückt
Stoppe Timer
Timer auslesen und umrechnen (je nach Zeitformat)
Zeit anzeigen
Timer zurücksetzen

Den Timer musst du vorher noch einstellen.

Vielleicht hilft es weiter.

MfG Hannes

Jimmy83
20.03.2010, 19:12
Hallo Hannes vielen Dank für deine antwort!

also prinzipiell hab ich das verstanden, wollte mir die Zeit irgendwie am PC ausgeben lassen und bräucht vielleicht nen Quellcode an dem ich mich orientieren kann. Wie gesagt noch bin ich ein Anfänger :-)
Vielen Dank nochmal

021aet04
20.03.2010, 20:41
Willst du nur dieses Projekt machen oder willst du generell in die Programmierung einsteigen? Hast du schon Vorkentnisse mit µC? Ich würde mich nacheinander in die einzelnen Themen einarbeiten (Eingabe, Interrupt, Timer, Datenübertragung,...).

Ein fertiges Beipsiel habe ich leider nicht. Wenn du nur dieses Projekt machen willst würde ich auf einen anderen, kleineren AVR umsteigen, da der Atmega viele IOs hat, die frei bleiben. Wie willst du die Verbindung mit dem PC herstellen? Wenn du mit Funk arbeiten willst würde ich SPI als Verbindung mit dem Funksender empfehlen. Dann könntest du mit dem Attiny 25, 45 bzw 85 arbeiten. Der hat 8 Anschlüsse.

MfG Hannes

the_muck
20.03.2010, 20:44
Hallo,
ich glaube du brauchst dann auch einen Timerquarz damit es genau wird... eine der besten anlaufstellen ist für Anfänger wohl das AVR-GCC-Tutorial hier der Link zum Timer.

http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Die_Timer_und_Z%C3%A4hler_des_AVR

Und die Kommunikation zum PC

http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Der_UART

021aet04
20.03.2010, 20:48
@teh_muck
Du hast Recht, wenn es aber nicht so genau sein muss geht auch der interne Oszillator.

MfG Hannes

Andun
20.03.2010, 20:55
Ich bin auch der Meinung, dass der interne Oscilator reichen sollte, da es ja hier wohl eher um Sekunden und nicht um nanosekunden geht, oder?

Generell musst du deine Frage aber präziser stellen, Jimmy, da ich keine Ahnung hätte, was du haben willst.

Wie hast du die Sensoren angeschlossen? Welches Board nutzt du? Wie willst du die Daten ausgeben?

Generell solltest du dir mal das AVR-GCC-Tutorial ansehen, wie oben auch schon empfohlen.

Jimmy83
20.03.2010, 21:42
Hallo!
Vielen Dank für die Antworten!
Also als Board hab ich das STK 500. Da evtl. noch eine Regelung über eine Pumpe geplant ist. Meine Vorkenntnisse sind eher bescheiden, aber was nicht ist, kann ja noch werden:-)
Bei dieser Kombi wirds wohl auch bleiben.

Zu dem Prescaler: Kann ich da für jeden Timer nen andren Takt vorgeben...oder ist der wenn ich einmal einen bestimmten Wert vorgeben hab gleich!?

Die Verbindung zum PC ist Uart und da hätte ich gern ein Fenster in dem meine benötigte Zeit steht, die der Schwimmer vom oberen Sensor zum unteren benötigt. Ich les mir erstmal dieses Tutorial durch und werd dann bei Fragen nochmal schreiben. Aber schonmal vielen vielen Dank für eure Antworten.

Gruß jimmy

Jimmy83
26.03.2010, 14:04
Hallo nochmal!

Also wie es jetzt laufen soll weiss ich. Wenn ein Endschalter gedrueckt ist, starte Timer. Bei jedem Ueberlauf loese ISR aus. Setze hier Zaehlvariable eins hoeher. Gib Wert der Variable aus wenn Schalter 2 gedrueckt ist. Zeit berechenbar.

Stimmt das vom Ablauf so? Und wenn ja wie schaffe ich es den Timer mit einem externen Signal zu starten???Finde dazu nirgends was :-(

Wuerde mich ueber eine Antwort sehr freuen:

021aet04
27.03.2010, 11:51
Der Ablauf stimmt so. Mit "sei();" kannst du die Interrupts freigeben (alle). Mit "cli();" kannst du die Interrupts sperren (alle). Da du nur den Timerinterrupt brauchst kannst du es so machen. Die Interrupt.h musst du includen.
Hier ist etwas zum Interrupt für den Mega32, du kannst es aber auch beim Mega16 anwenden http://www.tschallener.net/AVR/InterrupttechnikATmega32.pdf

MfG Hannes

Jimmy83
27.03.2010, 12:24
Hey Hannes,

vielen Dank für den guten Link. Leider werd ich vor Montag nicht dazu kommen es auszutesten :-) Aber dann gehts los :-)
Schaun mer mal :-)

Viele Grüße,

Jimmy

Jimmy83
27.03.2010, 18:28
Hallo Hannes :-)

Also die Sachen die du mir da geschickt hast, passen ziemlich gut zu dem was ich auch machen will. Nur eines ist mir noch unklar...
Mein Timer muss ja irgendwie starten..mache ich das über ne if Anweisung? Oder wie krieg ich den Timer dazu genau dann hochzulaufen wenn der obere Endschalter betätigt ist und dann zu stoppen wenn der untere betätigt ist?

Viele Grüße

Jimmy

021aet04
27.03.2010, 18:46
Zuerst stellst du den Timer ein (Prescaler,...). Danach gibst du den Timer frei, damit er auf Interrupts des Timers reagiert.
Ich weiß zwar nicht wo du den Taster anschließt, aber hier ein kleines Beispiel.

if ( PINC & (1<<PINC1) ) sei();
damit du den Interrupt stoppst musst du sei mit cli austauschen. Den Port und Pin musst du anpassen.
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial

MfG Hannes

Jimmy83
27.03.2010, 18:59
Also mein erster Taster liegt am Port A Pin 7. Wenn der auf Masse gezogen wird, soll der Timer starten.

Den stelle ich vorher entsprechend dem Datenblatt ein (Prescaler etc.)


In meiner ISR soll nur eine Variable hochgezählt werden...entsprechend dem oberen Link, den du mir geschickt hast.

Heisst das ich muss den Pin so anpassen wie dus da geschrieben hast und wenn er aufhören soll zu zählen einfach ein cli() mit entsprechendem PIN hin ? Und die Stoppuhr läuft dann schon!? Oder kommt in die if-Anweisung sonst noch etwas rein?

Sorry, dass es bei mir so lange dauert:-) Bin manchmal etwas schwer von Begriff :-)

Gruss

021aet04
28.03.2010, 09:08
Du musst oben das anpassen (Port und Pin). Bei deinem ersten Kontakt wäre das dann

if (PINA & (1<<PA7)) sei();
Beim anderen Kontakt ersetzt du "PINA" mit dem Port, den du hast. PIN bleibt immer gleich. der letzte Buchstabe gibt den Port an. PINA wäre Port A, PINB wäre Port B,...
"PA7" musst du mit dem Pin ersetzten. "P" bleibt immer gleich. Das "A" ist der Port und die Ziffer ist der Pin.

Wenn du die Interrupts stoppst musst du "sei();" mit "cli();" austauschen. Die Pins und Ports musst du deinen Bedürfnissen anpassen.

Ich würde den Timer einfach laufen lassen (mittels Kontakt starten). Bei jedem Overflow eine Variable um 1 erhöhen. Wenn der Timer gestoppt wird, ließt du die Variable und das Timerregister mit dem aktuellen Zählerstand. Entweder berechnest du die Zeit direkt und sendest es an den PC oder du sendest die Überläufe und den Zählerstand an den PC und berechnest es am PC.

MfG Hannes

Jimmy83
28.03.2010, 15:58
Also ich hab mir da jetzt mal was überlegt...bzw mit deinem Link da zusammengesucht und wollt fragen ob das prinzipiell laufen könnt.


TIMER2_interrupt_init(void)
{
TCCR2 = (1<<WGM21) (0<<WGM20) (1<<CS22) (1<<CS21) (1<<CS20);
TCNT2=0;
OCR2=249;
TIMSK = (1<<OCIE2);
}

int main(void)

{
if(PINA & (1<<PA7))
{sei()
TIMER2_interrupt_init
}
if(PINA & (1<<PA2)
{cli();
}
while(1);
}

ISR(TIMER2_COMP_vect)
{
z++
if(z==125){
sekunde++;
z=0;}
if(sekunde==60){
minute++;
sekunde=0;
if(minute==60){
stunde++;
minute = 0;
}
}


Nun ist es ja so, dass ich mit dem oberen Sensor den Timer starten und den mit dem unteren den Timer stoppen und auslesen will...für die Übertragung an den PC hab ich mir noch wenig gedanken gemacht...zuerst sollte das hier funktionieren!?
wenn ich mein Programm so laufen lass: Was passiert in dem Bereich in dem kein Sensor betätigt wird?Oder kann man das irgendwie machen, dass der nur auf steigende oder fallende Flanken reagiert?
Ist mein Programm prinzipiell richtig?Oder hab ich cli() und sei() falsch eingesetzt?

Vielen Dank und viele Grüße,

Jimmy

Jimmy83
29.03.2010, 23:05
Hallo nochmal :-)

also einen fehler hab ich schon gefunden. da wo das while(1) muss ein return(0) hin und vor die if Anweisung in main das while(1). Aber prinzipiell müssts doch dann stimmen ???
Und die Variablen sekunde, minute, stunde müssen doch dann als volatile deklariert werden. Würde mich über jede Antwort freuen.

Viele Grüße,
Jimmy

021aet04
03.04.2010, 11:02
Bin erst jetzt wieder online. Du hast einige Fehler drinnen.
1.) Das "ISR(Timer2_COMP_vect){...} kommt vor main (wie timer init)
2.) Den Timer nur einmal initialisieren
Als Beispiel:

TIMER2_interrupt_init(void)
{
TCCR2 = (1<<WGM21) (0<<WGM20) (1<<CS22) (1<<CS21) (1<<CS20);
TCNT2=0;
OCR2=249;
TIMSK = (1<<OCIE2);
}

ISR(TIMER2_COMP_vect)
{
z++;
if(z==125)
{
sekunde++;
z=0;
}

if(sekunde==60)
{
minute++;
sekunde=0;
}

if(minute==60)
{
stunde++;
minute = 0;
}

int main(void)
{
TIMER2_interrupt_init;

if(PINA & (1<<PA7))
{
sei();
}

if(PINA & (1<<PA2)
{
cli();
}

while(1);
}


Statt
if(PINA & (1<<PA2)
{
cli();
}
kannst du auch

if(PINA & (1<<PA2) cli();
schreiben.

Den Code solltest du kommentieren, damit du später auch noch weißt, warum was geschrieben wurde. (Andere finden sich im Code besser zurecht)

MfG Hannes

Jimmy83
03.04.2010, 12:44
Hallo Hannes!

Vielen Dank für die vielen Antworten! Ach so ja stimmt, solange mein Pin gesetzt ist, wird der Timer immer neu initialisiert. Das werd ich so übernehmen und dann weiterprobieren. Und der Timer startet dann immer wenn der obere Pin gesetzt wird bei 0?

Viele Grüße, Jimmy

021aet04
03.04.2010, 13:33
Habe gerade einige Fehler gesehen, die mir vorher entgangen sind.
1.) Je nachdem was du verwendest musst du if(PINA & (1<<PA2)) cli(); bzw if ( !(PINB & (1<<PINB2))) cli(); schreiben. Das Gleiche gilt für PA7. Wenn du den internen Pullupwiderstand nimmst musst du dieses Ändern.

2.) Die Endschalterabfrage musst du in die While(1) Schleife setzten, sonnst wird das Programm nur einmal ausgeführt.

Das Programm schaut so aus, wenn du den internen (oder externen) Pullupwiderstand nimmst.

int main(void)
{
TIMER2_interrupt_init;

while(1)
{
if(!(PINA & (1<<PA7))) sei();

if(!(PINA & (1<<PA2))) cli();
}
}

Habe nur main geschrieben. die ISR und Timer Init musst du natürlich auch schreiben.
Damit du das Programm testen kannst würde ich noch eine Led anschließen. Bei jedem Timerüberlauf (in der ISR) änderst du den Zustand der Led (ein/aus). Bei der Led darfst du den Vorwiderstand nicht vergessen.

Beim Timer init musst du noch die einzelnen Variablen mit | verknüpfen, sonst funktioniert es nicht. Außerdem musst du vor "TIMER2_interrupt_init(void)" noch ein void geben.

Diesen Code habe ich gerade mit einem Atmega8 getestet, deswegen habe ich andere Pins.

void TIMER2_interrupt_init(void)
{
TCCR2 = (1<<WGM21)|(1<<CS22)| (1<<CS21) |(1<<CS20);
TCNT2=0;
OCR2=249;
TIMSK = (1<<OCIE2);
}

ISR(TIMER2_COMP_vect)
{
PORTB ^= (1<<PB0);
}

int main(void)
{

DDRB |= (1<<PB0) | (1<<PB1) | (1<<PB2); //Led und Summer als Ausgang
PORTD |= (1<<PD0) | (1<<PD1) | (1<<PD2) | (1<<PD3); //Pullup für Taster ein
PORTB |= (1<<PB2); //Summer ausschalten

TIMER2_interrupt_init();

while(1)
{
if(!(PIND & (1<<PD0))) sei();

if(!(PIND & (1<<PD1))) cli();
}
}
Mit Taste1 aktiviert man den Timer, Taste 2 deaktiviert ihn. In der ISR wird eine Led invertiert. Ist der Timer aktiviert, blinkt die Led.

Willst du den Timer jede Sekunde um 1 erhöhen oder soll der Timer so schnell wie möglich laufen und du errechnest dann die Zeit?

MfG Hannes

Jimmy83
03.04.2010, 18:12
Hallo Hannes,

also ich hab die internen Pullup Widerstände verwendet. Deshalb brauch ich ja das ! wegen dem Invertieren. Ich dacht mir ich lass den Timer hochzählen und die ISR gibt mir ja dann die Anzahl der Überläufe an und lässt die Variablen entsprechend hochzählen. (sekunde, minute, stunde) Muss ich denn da noch rückrechnen? Habs ja so eingestellt, dass alle 125 Überläufe eine sekunde vergangen ist.

Viele Grüße,

Jimmy

021aet04
05.04.2010, 18:49
Das ! musst du schreiben, es fehlt aber in deinem Programm. Wie kommst du auf 125 Überläufe/s?
Der Wert im OCR wird immer geändert. du musst den immer neu einstellen, nicht nur einmal.

MfG Hannes

Jimmy83
05.04.2010, 22:00
Hey Hannes,

ich dachte mit dem Prescaler und dem OCR-Wert bestimme ich wie oft in der Sekunde eine ISR ausgeführt wird!? TCNT bestimmt ab wo gezählt wird und so ergibt sich bei den eingestellten Werten eine "Frequenz" für die ISR von 125Hz. Deshalb zählt man doch Sekunde immer hoch wenn z == 125 usw...oder bin ich jetzt völlig falsch :-)

MFG Jimmy

Jimmy83
07.04.2010, 13:31
Hallo Hannes,

ich hab mal versucht ein LCD einfach an meinen uC anzuschliessen. Ist ein Seiko LCD-Display 16X2 (L1672 series). Hat natuerlich nicht funktioniert :-)
Hierrueber hab ich Port B fuer mein LCD benutzt und diese Datei als Header-Datei in mein Programm eingebunden

void lcd_data(unsigned char temp1);
void lcd_string(char *data);
void lcd_command(unsigned char temp1);
void lcd_enable(void);
void lcd_init(void);
void lcd_home(void);
void lcd_clear(void);
void set_cursor(uint8_t x, uint8_t y);

// Hier die verwendete Taktfrequenz in Hz eintragen, wichtig!

#define F_CPU 8000000

// LCD Befehle

#define CLEAR_DISPLAY 0x01
#define CURSOR_HOME 0x02

// Pinbelegung für das LCD, an verwendete Pins anpassen

#define LCD_PORT PORTB
#define LCD_DDR DDRB
#define LCD_RS PDB
#define LCD_EN PDB
// DB4 bis DB7 des LCD sind mit PB0 bis PB3 des AVR verbunden


.
Mein Controller is der KS0066 also muesste das doch laut Tutorial funktionieren wenn ich das Programm einbinde. Pins sind entsprechend verbunden...

Das Programm hab ich einfach kopiert...aber nichts passiert!?

#include <avr/io.h>
#include "lcd-routines.h"

int main(void)
{
lcd_init();

lcd_data('T');
lcd_data('e');
lcd_data('s');
lcd_data('t');

set_cursor(0,2);

lcd_string("Hello World!");

while(1)
{
}

return 0;
}



Wie gehe ich mit der lcd-routines.c denn um ?? Wird die auch einfach eingebunden ? So hab ichs nämlich versucht...(Source-Files reinkopiert) Oder was mache ich mit der ?


// Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus
// http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/LCD-Ansteuerung
//
// Die Pinbelegung ist über defines in lcd-routines.h einstellbar

#include <avr/io.h>
#include "lcd-routines.h"
#include <util/delay.h>

// sendet ein Datenbyte an das LCD

void lcd_data(unsigned char temp1)
{
unsigned char temp2 = temp1;

LCD_PORT |= (1<<LCD_RS); // RS auf 1 setzen

temp1 = temp1 >> 4;
temp1 = temp1 & 0x0F;
LCD_PORT &= 0xF0;
LCD_PORT |= temp1; // setzen
lcd_enable();

temp2 = temp2 & 0x0F;
LCD_PORT &= 0xF0;
LCD_PORT |= temp2; // setzen
lcd_enable();

_delay_us(42);
}

// sendet einen Befehl an das LCD

void lcd_command(unsigned char temp1)
{
unsigned char temp2 = temp1;

LCD_PORT &= ~(1<<LCD_RS); // RS auf 0 setzen

temp1 = temp1 >> 4; // oberes Nibble holen
temp1 = temp1 & 0x0F; // maskieren
LCD_PORT &= 0xF0;
LCD_PORT |= temp1; // setzen
lcd_enable();

temp2 = temp2 & 0x0F; // unteres Nibble holen und maskieren
LCD_PORT &= 0xF0;
LCD_PORT |= temp2; // setzen
lcd_enable();

_delay_us(42);
}

// erzeugt den Enable-Puls
void lcd_enable(void)
{
// Bei Problemen ggf. Pause gemäß Datenblatt des LCD Controllers einfügen
// http://www.mikrocontroller.net/topic/81974#685882
LCD_PORT |= (1<<LCD_EN);
_delay_us(1); // kurze Pause
// Bei Problemen ggf. Pause gemäß Datenblatt des LCD Controllers verlängern
// http://www.mikrocontroller.net/topic/80900
LCD_PORT &= ~(1<<LCD_EN);
}

// Initialisierung:
// Muss ganz am Anfang des Programms aufgerufen werden.

void lcd_init(void)
{
LCD_DDR = LCD_DDR | 0x0F | (1<<LCD_RS) | (1<<LCD_EN); // Port auf Ausgang schalten

// muss 3mal hintereinander gesendet werden zur Initialisierung

_delay_ms(15);
LCD_PORT &= 0xF0;
LCD_PORT |= 0x03;
LCD_PORT &= ~(1<<LCD_RS); // RS auf 0
lcd_enable();

_delay_ms(5);
lcd_enable();

_delay_ms(1);
lcd_enable();
_delay_ms(1);

// 4 Bit Modus aktivieren
LCD_PORT &= 0xF0;
LCD_PORT |= 0x02;
lcd_enable();
_delay_ms(1);

// 4Bit / 2 Zeilen / 5x7
lcd_command(0x28);

// Display ein / Cursor aus / kein Blinken
lcd_command(0x0C);

// inkrement / kein Scrollen
lcd_command(0x06);

lcd_clear();
}

// Sendet den Befehl zur Löschung des Displays

void lcd_clear(void)
{
lcd_command(CLEAR_DISPLAY);
_delay_ms(5);
}

// Sendet den Befehl: Cursor Home

void lcd_home(void)
{
lcd_command(CURSOR_HOME);
_delay_ms(5);
}

// setzt den Cursor in Zeile y (1..4) Spalte x (0..15)

void set_cursor(uint8_t x, uint8_t y)
{
uint8_t tmp;

switch (y) {
case 1: tmp=0x80+0x00+x; break; // 1. Zeile
case 2: tmp=0x80+0x40+x; break; // 2. Zeile
case 3: tmp=0x80+0x10+x; break; // 3. Zeile
case 4: tmp=0x80+0x50+x; break; // 4. Zeile
default: return; // für den Fall einer falschen Zeile
}
lcd_command(tmp);
}

// Schreibt einen String auf das LCD

void lcd_string(char *data)
{
while(*data) {
lcd_data(*data);
data++;
}
}


Wäre toll wenn du mir helfen könntest :-) Zunächst wäre ich froh es wuerde wenigstens leuchten :-)

Vielen Dank,

Jimmy

Jimmy83
07.04.2010, 14:22
Hab auch schon versucht die datei lcd-routines.c als lcd-routines2.h einzubinden. Aber das LCD leuchtet nicht einmal!?
MFG

021aet04
07.04.2010, 18:26
Habe gerade im DB nachgeschaut. Dort steht auf S119 unter "Output Compare Unit":

The 8-bit comparator continuously compares TCNT2 with the Output Compare Register
(OCR2). Whenever TCNT2 equals OCR2, the comparator signals a match.
Das heißt soviel wie er zählt solange bis er den Wert erreicht (nicht ab diesen Wert).
Wie das mit dem LCD funktioniert weiß ich nicht, hab noch nicht mit LCDs gearbeitet. Du musst vermutlich die Makefile anpassen. Hier findest du einen Link zur AVRLIB. Bei der kannst du schauen, wie das geht. Du musst die *.c und die *.h Datei einbinden.
Das ist aber nur eine Vermutung.

MfG Hannes

Jimmy83
07.04.2010, 20:02
Hallo Hannes,
das mit dem LCD kann ich vergessen, da es sowieso hinüber ist :-(
Habs erst vorhin entdeckt. Weisst du zufällig wie die Ausgabe über UART funktioniert ? Brauch ich da auf dem Rechner ein Programm oder gibt der mir das bei ner Verbindung mit meinem STK die Variable direkt aus?

/* UART-Init Bsp. ATmega16 */

void uart_init(void)
{
UCSRB |= (1<<TXEN); // UART TX einschalten
UCSRC |= (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0); // Asynchron 8N1

UBRRH = UBRR_VAL >> 8;
UBRRL = UBRR_VAL & 0xFF;
}

Ausgabe dann so:

// bei neueren AVRs steht der Status in UCSRA/UCSR0A/UCSR1A, hier z.B. fuer ATmega16:
while (!(UCSRA & (1<<UDRE))) /* warten bis Senden moeglich */
{
}

UDR = 'x'; /* schreibt das Zeichen x auf die Schnittstelle */


Reicht das schon !? Um mir ne Variable am Rechner auszugeben ?
Oder brauch ich da sonst noch was ?

MFG

021aet04
08.04.2010, 17:49
Mit UART habe ich mich noch nicht beschäftigt. Ich glaube, dass im Artikelbereich ein Beitrag über die Ansteuerung des UART gibt. Am PC brauchst du ein Terminalprogramm.

Mfg Hannes