Hi Leute,
hab heute zum ersten mal einen Mikrocontroller programmiert und brauche
hilfe! Hab mich durch eure Tutorials geschlagen und viel gesucht und
auch viel gefunden, nun zu meinem problem:
Ich will eine Heizungssteurung aufbauen, wo eine Uhr läuft und ich einen
Start und einen Stoppzeitraum einstellen kann. Das ganze soll mit
Hyperterminal zu sehen sein!
Bisher klappt die Kommunikation ZUM PC, aber ich will mit der taste "1"
bzw. "2" in ein Auswahlmenü kommen, wo ich dann die Zeiten
(Abschalt/Einschalt) einstellen kann. Aber irgendwie schaffe ich es
nicht ne richtige Abfrage zu machen.
Hier mein bisheriger Code:
// Hauptinclude Datei für den Atmega32 16PU
#include <mega32.h>
// Standard Input/Output functions
#include <stdio.h>
#include <string.h>
// Globale Variabeln
char text [31] = "Schaltuhr Heizungssteuerung!\r\n";
flash char timetext[] = "Es ist jetzt %02d:%02d:%02d\r\n";
flash char Heiztext1 [] = " Die Heizung wird um %02d:%02d %p.";
flash char Heiztextan [] = "eingeschaltet";
flash char Heiztextaus [] = "ausgeschaltet";
char * message ;
int i;
// Zeiten
int ss_aktuel = 0;
int mm_aktuel = 0;
int hh_aktuel = 0;
int mm_start = 0;
int hh_start = 0;
int mm_stop = 0;
int hh_stop = 0;
int timercount = 0;
// Funktioen zu Seriellen Ausagbe
void sendchar( char c)
{
while (!(UCSRA & ( 1<<UDRE))) {}
UDR = c;
}
// sendnewline Zeilenvorschub an Hyperterm senden
void sendnewline()
{
char c = 13;
sendchar(c);
c = 10;
sendchar(c);
}
// sende eine textzeile an Hyperterm
void sendtextline ( char *txt )
{
int i = 0;
while ( i < strlen(txt) )
sendchar(txt[i]);
}
// Timer ISr - Interrupt
interrupt [TIM0_COMP] void timer0_Compare(void)
{
timercount ++;
if ( timercount > 12500)
{
timercount = 0;
ss_aktuel ++;
mm_aktuel += ss_aktuel / 60;
// if ( ss_aktuel >= 60) ssaktuel = 0;
ss_aktuel = ss_aktuel % 60;
hh_aktuel += mm_aktuel / 60;
mm_aktuel = mm_aktuel %60;
hh_aktuel = hh_aktuel % 24;
sprintf(message, timetext, hh_aktuel , mm_aktuel, ss_aktuel);
sendnewline();
sendtextline(message);
}
}
// Main
void main(void)
{
// Port A initialization
// Func0 - 7=In und = T
PORTA=0x00;
DDRA=0x00;
// Port B initialization
PORTB=0x00;
DDRB=0x00;
// Port C initialization
PORTC=0x00;
DDRC=0x00;
// Port D initialization
PORTD=0x00;
DDRD=0x00;
TCCR0=0x3a;// | (1 << CS01);
TCNT0=0x00;
OCR0=0x7d;// 0x00;
TCCR1A=0x00;
TCCR1B=0x00;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;
ASSR=0x80;
TCCR2=0x00;
TCNT2=0x00;
OCR2=0x00;
MCUCR=0x00;
MCUCSR=0x00;
// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x00 |(1<< OCIE0);
//USART: 9600 Baud, 8 Data, 1 Stop, No Parity, Asynchron, Receiver on,
Transmitter off
UCSRA=0x00;
UCSRB= 0x10 | ( 1 << TXEN); // |(1 << RXEN) ;
UCSRC=0x86;
UBRRH=0x00;
UBRRL=0x067;
ACSR=0x80;
SFIOR=0x00;
#asm("sei")
while ( i < 31) sendchar(text[i++]);
i = 0;
sendnewline();
sprintf (message ,"","");
sprintf(message, timetext, hh_aktuel , mm_aktuel);
sendnewline();
sendtextline(message);
sendnewline();
sprintf(message , Heiztext1 , hh_start,mm_start , Heiztextan);
sendnewline();
sprintf(message , Heiztext1 , hh_stop,mm_stop , Heiztextaus);
while (1)
{
/*
while (!(UCSRA & ( 1 << RXC)))
{
char c = UDR;
sprintf(message, timetext, hh_aktuel , mm_aktuel) ;
sendnewline();
sendtextline(message);
sendnewline();
}
*/
};
}
PJ schrieb:
> Bildschirm löschen geht meist mit ^L (Ctrl-L, ist ein bestimmter Code,> ich glaube 0x0C), aber das hängt vom angeschlossenen Terminal ab.
jau, hat geklappt, danke :)
void clearscreen();
{
char c = 12; // 0x0c
sendchar(c)
}
Die Frage nach dem Löschen eines Bildschirms wurde ja schon beantwortet.
In deinem Programm ist aber noch ein schwerer Fehler:
char * message ;
...
sprintf(message, timetext, hh_aktuel , mm_aktuel);
Das geht so nicht.
message ist zwar ein Pointer. Aber auf welchen Speicher zeigt er denn?
Damit du zb sprintf verwenden kannst, musst du eine Speicherfläche
bereit stellen, in der sprintf den Text ablegen kann. Du hast keine
Speicherfläche. Du hast nur einen Zeiger, der irgendwo hin zeigt. Ok,
stimmt nicht ganz, er zeigt so wie du es geschrieben hast schon auf eine
definierte Speicherzelle. Der springende Punkt ist aber: du darfst dort
nicht einfach hinschreiben lassen. Der Speicher steht nicht unter deiner
Kontrolle.
Was du brauchst ist ein char-Array, das du an sprintf übergeben kannst
und welches groß genug ist, um den Text aufzunehmen
char message[80];
Das wäre zb so was. Da passen jetzt Texte aus maximal 79 Zeichen rein.
http://www.mikrocontroller.net/articles/FAQ#Wie_funktioniert_String-Verarbeitung_in_C.3F
Zu deinem Problem mit dem Empfang:
1
while(!(UCSRA&(1<<RXC)))
2
{
3
charc=UDR;
4
sprintf(message,timetext,hh_aktuel,mm_aktuel);
5
sendnewline();
6
sendtextline(message);
7
sendnewline();
8
}
Nope. Das RXC Bit wird gesetzt nachdem ein Zeichen empfangen wurde. Wenn
du also schon warten musst, dann musst du darauf warten, dass ein
Zeichen empfangen wird.
Es muss also lauten
1
while(!(UCSRA&(1<<RXC)))// warten bis Zeichen verfuegbar
Er will vermutlich nicht NUR auf das Zeichen warten, sondern
währenddessen weiter seinen Zustand ausgeben.
Nur hatte er noch nicht für uns klar programmiert, was dann mit dem
empfangenen Zeichen gemacht wird.
"Besser" wäre es vielleicht, das Zeichen in einer ISR zu empfangen,
anstatt durch eine ständige Belastung der Übertragungskette bis zum PC
unnötig Strom zu verbraten.
Aber alles zu seiner Zeit, ist ja sein erstes Projekt.
Zur Auswertung des Zeichens kann man geschachtelte "if else"s verwenden,
aber wahrscheinlich ist das "case select" Konstrukt Dir lieber.
Einfach mal danach googeln, da findet man sofort Beispiele.
Ava Ava schrieb:
> PJ schrieb:>> Bildschirm löschen geht meist mit ^L (Ctrl-L, ist ein bestimmter Code,>> ich glaube 0x0C), aber das hängt vom angeschlossenen Terminal ab.>> jau, hat geklappt, danke :)>> void clearscreen();> {> char c = 12; // 0x0c> sendchar(c)> }
Du musst nicht für alles und jedes lokale Variablen erzeugen (gut, der
Compiler wird sie sowieso rausschmeissen). Zuviel ist dann auch nicht
gut, schadet der Übersicht.
1
voidclearscreen();
2
{
3
sendchar(12);
4
}
ist genausogut.
Wenn du es noch besser machen willst, dann machs zb so
1
...
2
3
#define CLS 12
4
5
voidclearscreen();
6
{
7
sendchar(CLS);
8
}
Selbiges kannst du auch mit der sendnewline Funktion machen.
1
...
2
3
#define CLS 12 // ASCII ^L = Clear screen
4
#define CR '\n' // Carriage return
5
#define LF '\r' // Line Feed
6
7
8
voidsendnewline()
9
{
10
sendchar(CR);
11
sendchar(LF);
12
}
13
14
voidclearscreen();
15
{
16
sendchar(CLS);
17
}
Und deine sendtextline können wir bei der Gelegenheit auch gleich noch
pimpen. Es gibt keinen Grund da mit Indizes oder gar strlen rumzuwerfen.
Ist alles nur Laufzeit für nichts.
jap,
Bin gerade dabei im sekundentakt an Hyperterm die Zeit + Text zu senden.
Es soll ungefähr so ausschauen am ende:
Schaltuhr Heizungssteurung
--------------------------
Es ist jetzt 22:32
Die Heizung wird um 6:00 aktiviert
Die Heizung wird um 12:00 deaktiviert
Druecken sie "1" um die Startzeit zu ändern
Druecken sie "2" um die Endzeit zu ändern
Heizung deaktiviert
Wie man das ganze weitertreibt um eine komplette Eingabezeile
zusammenzusetzen, möcht ich jetzt noch nicht ansprechen. Ich denke im
Moment gibt es genug zu verdauen.
Wenn das kaum mehr machen soll, könnte man einfach ein paar Tasten
definieren, mit denen die Stunden und Minuten von Start und Ende erhöht
und verringert werden können. Bei größeren Änderungen einfach die
Tastenwiederholfunktion des PCs nutzen (oder noch 10er-Änderungstasten
hinzufügen).
Ein umfassenderes Menüsystem braucht dann halt 'ne kleine State machine.
hört sich gut an :)
Wow, so schnelle und viele Antworten! Bin grad am Umsetzen
hab jetzt im Sekundentakt eine aktualisierte Seite, die Funktionen sind
nun auch besser sotiert, überflüssiges gelöscht und mehr
einheitlichkeit, war halt noch viel testerei zwischendrin :)
Versuche gerade den Vorschlag von "kbuchegg" umzusetzen :)
Ava Ava schrieb:
> hört sich gut an :)>> Wow, so schnelle und viele Antworten! Bin grad am Umsetzen> hab jetzt im Sekundentakt eine aktualisierte Seite, die Funktionen sind> nun auch besser sotiert, überflüssiges gelöscht und mehr> einheitlichkeit, war halt noch viel testerei zwischendrin :)>> Versuche gerade den Vorschlag von "kbuchegg" umzusetzen :)
Dein 'Sekundentakt' macht mir noch etwas Sorgen.
Du hast hoffentlich nicht vor, den sekündlichen Abstand mittels eines
_delay_ms( 1000 ) zu erreichen?
Wieso nicht? Verboten ist's bestimmt nicht.
Natürlich kann man damit auch Timer und ISRs und Semaphoren üben...
Und sowie gilt ja, wer "Timer" oder "Uhr" sagt, muss auch "Uhr stellen"
sagen.
PJ schrieb:
> Wieso nicht? Verboten ist's bestimmt nicht.
Das gibt dann allerdings Schwierigkeiten mit der UART, wenn der µC immer
wieder mal 1 Sekunde Däumchen dreht und einfach nicht hinhört, wenn der
PC schreit :-)
Ach so, ja da hast Du natürlich recht.
Dann kann er ja den delay auf 100 ms verkürzen.
Natürlich ist das halb so schlimm, wenn er immer nur ab und zu 1 Taste
drückt.
Aber vielleicht hat er eh einen delay von 0, weil das noch in der
UART-Rx-Warteschleife steht. :-)
Karl heinz Buchegger schrieb:
> Ava Ava schrieb:>> hört sich gut an :)>>>> Wow, so schnelle und viele Antworten! Bin grad am Umsetzen>> hab jetzt im Sekundentakt eine aktualisierte Seite, die Funktionen sind>> nun auch besser sotiert, überflüssiges gelöscht und mehr>> einheitlichkeit, war halt noch viel testerei zwischendrin :)>>>> Versuche gerade den Vorschlag von "kbuchegg" umzusetzen :)>> Dein 'Sekundentakt' macht mir noch etwas Sorgen.> Du hast hoffentlich nicht vor, den sekündlichen Abstand mittels eines> _delay_ms( 1000 ) zu erreichen?
Nein!
interrupt [TIM0_COMP] void timer0_Compare(void)
{
timercount ++;
if ( timercount > 12500)
{
timercount = 0;
ss_aktuel ++;
mm_aktuel += ss_aktuel / 60;
showMaintext(); // <- HIER 1 Sekunde
ss_aktuel = ss_aktuel % 60;
Ava Ava schrieb:
>> Dein 'Sekundentakt' macht mir noch etwas Sorgen.>> Du hast hoffentlich nicht vor, den sekündlichen Abstand mittels eines>> _delay_ms( 1000 ) zu erreichen?>> Nein!
Ah, jetzt wo du es sagst, kann ich mich erinnern das gelesen zu haben.
Na dann ist ja alles gut.
Und, klappt der Empfang?
(kleiner Hinweis: es ist am Anfang immer gut, wenn der µC gleich mal ein
emmpfangenes Zeichen zurückschickt. So kann man gleich mal kontrollieren
ob und wenn ja was empfangen wurde.)
Aber ich glaube nicht, dass es so eine gute Idee ist, in der ISR ein
paar Zeilen Text über den UART auszugeben, vor allem, wenn die ISR 12500
mal pro Sekunde aufgerufen wird.
Da gehen dann wohl jede Sekunde ein paar Aufrufe verloren, und schon
geht die Uhr immer fälscher.
Du rechnest da aber in deiner Uhr sehr viel herum :-)
Auch wenn man mit dem µC keine Gnade haben sollte, so muss man ihn aber
auch nicht unbedingt quälen (Obwohl mir dein Code zeigt, dass du mit /
und % umgehen kannst, was nicht so selbstverständlich ist)
Das erweitert sich dann schon fast von alleine auf Wochentage, was ja
interesant sein könnte, da man am Wochenende ein anderes Heizprofil
fahren wird als unter der Woche. Zumindest wenn alle im Haushalt
berufstätig sind.
Alledings solltest du dich an die Grundregel halten, die da lautet: in
einer ISR haben lang andauernde Prozesse nichts verloren. Man möchte so
schnell wie möglich aus der ISR wieder raus.
Karl heinz Buchegger schrieb:
> Ah, jetzt wo du es sagst, kann ich mich erinnern das gelesen zu haben.> Na dann ist ja alles gut.>> Und, klappt der Empfang?> (kleiner Hinweis: es ist am Anfang immer gut, wenn der µC gleich mal ein> emmpfangenes Zeichen zurückschickt. So kann man gleich mal kontrollieren> ob und wenn ja was empfangen wurde.)
Bevor deinen tread gelesen zu haben, hab ich es genauso gemacht :)
Also eine Abfrage auf 1 bzw. 2 ... wenn 1 Gedrückt wird, soll Einfach
ein kleiner text ausgegeben werden, genauso wenn man 2 drückt.
es klappt, der text wird angezeigt, nur nach 1 sekunde halt
logischerweise wieder wegaktualisiert :p ... Nun, muss ich also bei
Tastendruck da raus :)
Ava Ava schrieb:
> es klappt, der text wird angezeigt, nur nach 1 sekunde halt> logischerweise wieder wegaktualisiert :p ... Nun, muss ich also bei> Tastendruck da raus :)
Auch deshalb ist die Lösung mit den Jobflags (zb updateTime) die bessere
Lösung. Dann verteilt sich die Logik des Programms nicht über so viele
Codestellen, sondern bleibt mehr beisammen und bleibt besser
überschaubar.
Das Jobflag updateTime, welches von der Timer ISR gesetzt wird, meldet
lediglich, dass 1 Sekunde vergangen ist. Was die Konsequenz daraus ist,
geht den Timer an sich nichts an. Die Konsequenz kann sein, dass das
Terminal mit der aktuellen Zeit aktualisiert wird. Sie könnte aber auch
sein, dass überprüft wird, ob die Heizung ein oder ausgeschaltet werden
soll. Es könnte auch sein, dass eine LED umgeschaltet wird (damit man
auch bei ausgeschaltetem Terminal sieht, dass die Steuerung noch läuft)
etc. All das ist aber nicht das Bier der Timer-ISR. Die meldet nur, dass
1 Sekunde vergangen ist.
So mit dem untermenue das klappt nun auch. Wenn ich 1 drücke poppt, das
Einstellmenü auf, wenn ich 2 drücke, das Austellmenue, nun muss ich mal
gucken, wegen der Uhrzeitabfrage, was könnt ihr mir da raten?
Ich würds so machen, wie weiter oben schon mal vorgeschlagen:
4 Tasten: Stunden rauf, Stunden runter, Minuten rauf, Minuten runter
Mit dem Autorepeat, den Windows automatisch auf die Tasten legt, geht
das noch komfortabel. Zusätzlicher Bonus: Fehleingaben werden
zuverlässig ausgeschlossen.
Da haste recht, ging sehr schnell und klappt gut :)
Muss jetzt noch 23std ++ und 59min ++ abfangen :)
while (1)
{
// BIS HIER WAR NOCH ALLES RICHTIG
if( UCSRA & ( 1 << RXC ) )
{
char c = UDR; //Zeichen empfangen
if( c == 'q' )
hh_start ++;
else if( c == 'w' )
hh_start --;
else if( c == 'e' )
mm_start ++;
else if( c == 'r' )
mm_start --;
}
};
Ava Ava schrieb:
> Da haste recht, ging sehr schnell und klappt gut :)>> Muss jetzt noch 23std ++ und 59min ++ abfangen :)
Die andere Richtung auch nicht vergessen :-)
Na, geht doch voran. So wies aussieht, hast du Spass daran, das Werk
entstehen zu sehen. Das ist das Allerwichtigste.
Ich glaub spass ist etwas untertrieben :D
Das ist der Wansinn, wie das wächst, und vor allem, durch die hilfe
macht es noch mehr laune, weil man nicht so heftig stecken bleibt :))
Andere Richtung funktioniert nun auch und die überläufe von 23std auf 00
sowie 59 auf 00 funtkioniert ebenso. is zwar viel code nun, aber
trotzdem verständlich!
Muss jetzt ja nur noch eine Bereichsabfrage machen, für die Steurung
"an" bzw. "aus" ... wie man die einzelenen Ports schaltet habe ich mir
noch nicht angeschaut...
>Muss jetzt ja nur noch eine Bereichsabfrage machen, für die Steurung>"an" bzw. "aus" ... wie man die einzelenen Ports schaltet habe ich mir>noch nicht angeschaut...
Bis zum nächsten Winter ist ja auch noch etwas Zeit :-)
Oliver