PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Kleine Uhr



noNeed 4 aNick
21.06.2005, 21:54
Hi zusammen,
Ich wollte ne kleine Uhr programmieren, da ich mich mit den Strings in C nich wirklich auskenne und die nun bissel beherrschen lernen wollte...

Ich hab jetzt folgenden Code:


#define preValue 57724

int volatile gSeconds = 0;

SIGNAL (SIG_OVERFLOW1)
{
gSeconds++;
TCNT1 = preValue;
}


int main (void)
{

lcd_init(LCD_DISP_ON);

TCCR1B = (1<<CS12) | (1<<CS10);
TIMSK |= (1<<TOIE1);
TCNT1 = preValue;
sei ();

int seconds = 0;
int minutes = 0;
int hours = 0;


char *sec = "00";
char *min = "00";
char *h = "00";

char *time = "00:00:00";

lcd_puts("Time: ");
lcd_puts(time);


while (1)
{

if (seconds != gSeconds)
{
seconds = gSeconds;

if (seconds == 60)
{
seconds = 0;
gSeconds = 0;
minutes++;
}
if (minutes == 60)
{
minutes = 0;
hours++;
}
if (hours == 24) hours = 0;

time = "00:00:00"; // Problem hier s.u.
// 01234567

itoa(seconds, sec, 10);
// itoa(minutes, min, 10);
//itoa(hours, h, 10);


if (seconds < 10)
{
time[6] = "0"; // zweites Problem hier s.u.
time[7] = sec[0];
}
else
{
time[6] = sec[0];
time[7] = sec[1];
}

/* if (minutes < 10)
{
time[4] = min[0];
}
else
{
time[3] = min[0];
time[4] = min[1];
}

if (hours < 10)
{
time[1] = h[0];
}
else
{
time[0] = h[0];
time[1] = h[1];
}*/

lcd_gotoxy(6,0);
lcd_puts(time);
lcd_gotoxy(0,1);
lcd_puts(sec);
}
}
}


Naja, das Problem ist, dass zwar der itoa der Seconds klappt, aber wenn ich die itoa's der Stunden und Minuten nicht auskommentiere, resetet der µC konstant :-s




Außerdem bringt folgende Zeile hier gar nix:



time = "00:00:00"; // Problem hier s.u.

Eigentlich sollte wenn die Seconds wieder bei 0 sind dadurch garantiert werden, dass in der Uhrzeit auch :00 anstatt :50 steht (Die 5 wäre noch aus den 50'er Sekunden geblieben). Ich hoffe ihr versteht was ich meine :)
Trotz dieser Zeile, bleibt in den ersten 10 Sekunden jeder neuen Minute die 5 aus den 50'er Sekunden der vorherigen Minute stehen. So als ob die Variable gar nicht refresht wird...



Time: 00:00:58
Time: 00:00:59
Time: 00:00:50
Time: 00:00:51
Time: 00:00:...
Time: 00:00:59
Time: 00:00:10





time[6] = "0"; // zweites Problem hier s.u.

Ich dachte mir daher: Ach egal, packste halt die Null selber dahin.
Seltsamerweise sieh die ausgabe dann so aus:


Time: 00:00:s0
Time: 00:00:s1
Time: 00:00:...
Time: 00:00:s9


Ich hoffe, jmd hat ne Idee für mich...

ThxInAdv
Alex

churchi
21.06.2005, 22:11
in C kann man Strings nicht einfach durch = "text" zuweisen, sondern muss dies über die Funktion strcpy machen
strcpy[stringvariable,"text"]
stringvariable muss dabei ein array sein, dass mindestens um 1 länger als der maximal darin zu speichernde Text ist

M@nni
22.06.2005, 12:38
Hi Alex,

das Arbeiten mit Strings ist, wie churchi schon erläutert hat, für einen Anfänger in der C-Programmierung nicht so ganz einfach. So müsste es aber funktionieren:


#include <stdio.h>

#define preValue 57724

int volatile gSeconds = 0;

SIGNAL (SIG_OVERFLOW1)
{
gSeconds++;
TCNT1 = preValue;
}


int main (void)
{
int seconds = 0;
int minutes = 0;
int hours = 0;
char mytime[] = "00:00:00";


lcd_init (LCD_DISP_ON);

TCCR1B = (1<<CS12) | (1<<CS10);
TIMSK |= (1<<TOIE1);
TCNT1 = preValue;
sei ();

lcd_puts ("Time: ");
lcd_puts (mytime);

while (1)
{
if (seconds != gSeconds)
{
seconds = gSeconds;

if (seconds == 60)
{
seconds = 0;
gSeconds = 0;
minutes++;
}
if (minutes == 60)
{
minutes = 0;
hours++;
}
if (hours == 24)
hours = 0;

/* Uhrzeit formatieren */
sprintf (mytime, "%02d:%02d:%02d", hours, minutes, seconds);

/* und auf dem Display ausgeben */
lcd_gotoxy (6,0);
lcd_puts (mytime);
}
}
}

Die Funktion sprintf() aus der Standard-C-Library wird hier dazu benutzt, den String für die gewünschte Ausgabe zu formatieren.

Ich hoffe, das hilft Dir ein wenig.

Gruß, M@nni

churchi
22.06.2005, 12:59
soweit ich weiß gibt es den sprintf Befehl nur in der avrgcc version und nicht in der winavr?
bin mir da nicht ganz sicher

Kjion
22.06.2005, 15:07
soweit ich weiß gibt es den sprintf Befehl nur in der avrgcc version und nicht in der winavr?

Naja, so groß ist der unterschied zwischen den beiden nicht. WinAVR ist ja im Prinzip nur ein Programmpacket für Windows das avr-gcc benutzt.

MfG Kjion

noNeed 4 aNick
22.06.2005, 21:08
Hi zusammen,
Ich danke euch - klappt alles perfekt :)

Bye
Alex

Rosenthal
24.06.2005, 14:28
Hallo,

Ich habe mal eben das Programm übernommen. Allerdings treten bei mir 2 Fehler auf.

1. Der Sekundenwert ändert sich bei mir immer erst nach genau 2 Sekunden. Er läuft also nur halb so schnell. Ich nehme mal an, das liegt an meinem externen 4Mhz Quarz. Wie kann ich den richtigen Wert dafür berechnen?

2. Die Zeit läuft kurze Zeit richtig(bis auf das obige Problem). Danach bleibt der Wert stehen und es tut sich nichts mehr. Nach langem Warten (einige Minuten) bekomme ich wieder eine Anzeige. Die liegt dann irgendwo bei mehreren 100 Sekunden. Das heißt also, dass er anscheinend intern weiterzählt. Dachte erst, es hätte was mit dem Watchdog zu tun, dass kann dann aber eigentlich nicht sein.
Ebenfalls zählt er die Minuten nicht hoch. if(seconds==60) wird nicht ausgeführt. Nehme mal an, dass liegt daran, dass er den Wert 60 überspringt.

Kann mir einer vielleicht sagen, woran das liegen kann?

M@nni
27.06.2005, 10:26
Hallo,

offensichtlich benutzt der ursprüngliche Poster einen AVR, der mit 8 MHz betrieben wird, während Deiner mit 4 MHz läuft. Da der interne Takt zum Zählen benutzt wird, läuft Deine Uhr nur halb so schnell. Die Lösung wäre: ersetze im Programm die Zeile

#define preValue 57724

durch

#define preValue 28862

Das sollte dann funktionieren.

Zu Deinem 2. Problem: Vielleicht hilft es, wenn Du den Teil des Programms, der in der Endlosschleife ausgeführt wird, etwas anders schreibst, etwa so:

while (1)
{
if (seconds != gSeconds)
{
seconds = gSeconds;

if (seconds >= 60)
{
seconds -= 60;
gSeconds = seconds;
minutes++;
}
if (minutes >= 60)
{
minutes -= 60;
hours++;
}
if (hours >= 24)
hours -= 24;

/* Uhrzeit formatieren */
sprintf (mytime, "%02d:%02d:%02d", hours, minutes, seconds);

/* und auf dem Display ausgeben */
lcd_gotoxy (6,0);
lcd_puts (mytime);
}
}

Sollte das Problem immer noch vorhanden sein, wäre es interessant zu wissen, welchen AVR Du einsetzt.

Gruß, M@nni

Rosenthal
27.06.2005, 14:32
@ M@nni
Danke für die Antwort.
Wollte vor 2 Stunden deinen Vorschlag ausprobieren. Habe meine Kabel in mein Netzteil gesteckt, und siehe da, nichts funktionierte. Habe nämlich die Stecker vertauscht. Das hat mir anscheinend der 7805 für das Display übelgenommen. Der erzeugte danach um die 8 Volt. Habe mir gerade eine neue Spannungsversorgung zusammengelötet, jetzt allerdings mit Verpolungsschutz.

Also nun zu obigen Problem. Wenn ich den Wert auf 28862 ändere, braucht er ca. 9s um den Wert zu erhöhen. Habe jetzt einfach mal 64000 eingegeben, dann ändert sich der Wert sehr viel schneller als 1s. Gibt es eigentlich eine Formel, mit der man das Berechnen kann? Ich benutze einen Mega163L mit 4MHz Quarz an einem STK500. Das 2.Problem hat sich soweit behoben. Er zählt bis 59 und addiert dann die Minute. Allerdings bricht das Programm immer noch einfach ab. Bei meinem letzten Test ist er bei den Werten (Zeit) 00:03:24 stehengeblieben.

Vielen Dank schon mal

M@nni
27.06.2005, 17:47
Hallo,

oh, anscheinend ist der Zähler so konfiguriert, dass er vorwärts zählt und nicht rückwärts, so wie ich dachte. Habe im Moment leider kein lauffähiges AVR-System, sonst hätte ich das vorher ausprobiert.

Es handelt sich um einen 16-Bit-Zähler, der von dem voreingestellten Wert (im Programm definiert durch "preValue") ausgehend vorwärts zählt. Wenn der Zähler den Maximalwert von 65535 überschreitet, erzeugt er einen Interrupt. In der Interrupt-Routine wird der Zähler dann wieder mit dem Wert "preValue" geladen.

Das bedeutet, der Zähler zählt 65536 - preValue = 65536 - 57724 = 7812 Takte bei 8 MHz. In Deinem Fall müsste er bei 4 MHz nur halb so viele Takte, also 7812/2 = 3906 zählen. Du bekommst dann also einen Wert für preValue von 65536 - 3906 = 61630. Probier das mal aus, dann dürfte Deine Uhr in etwa richtig laufen.

Es kann sein, dass die Uhr nach einiger Zeit etwas vor oder nachgeht. Dann kannst Du den Wert von preValue noch etwas tunen, also etwas erhöhen, wenn die Uhr zu langsam läuft und etwa verringern, wenn sie zu schnell läuft.

Das mit dem Programmabbruch sollte eigentlich nicht sein, da die Software in einer Endlosschleife läuft. Bricht das Programm immer bei der gleichen Uhrzeit ab oder ist dies unterschiedlich? Evtl. könnte es sich um ein Hardware-Problem handeln, vielleicht das Netzteil.

Gruß, M@nni

robotfreak
02.07.2005, 11:42
Allgemein auch: preValue = 65536 - f / pre

PreValue: Zahl mit der der Zähler vorgeladen wird
f: Frequenz die Oszis
pre: Prescaler

Da preValue eine natürliche Zahl sein soll, muss pre entsprechend angepasst werden.

jojobh
15.09.2007, 15:18
Hallo


also hab das Programm an meine LCD-Steuerung angepasst bei mir geht es aber nicht! Auf dem Display steht nur " \L L " Also nichts sinnvolles.


#include <stdio.h>

#include "LCD/lcd-routines.h"
#include "LCD/lcd.c"

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/signal.h>



#define preValue 57724

int volatile gSeconds = 0;

SIGNAL (SIG_OVERFLOW1)
{
gSeconds++;
TCNT1 = preValue;
}


int main (void)
{
int seconds = 0;
int minutes = 0;
int hours = 0;
char mytime[] = "00:00:00";


lcd_init ();



TCCR1B = (1<<CS00) | (1<<CS01);
TIMSK |= (1<<TOIE1);
TCNT1 = preValue;
sei ();

lcd_data ("Time: ");
lcd_data (mytime);

while (1)
{
if (seconds != gSeconds)
{
seconds = gSeconds;

if (seconds == 60)
{
seconds = 0;
gSeconds = 0;
minutes++;
}
if (minutes == 60)
{
minutes = 0;
hours++;
}
if (hours == 24)
hours = 0;

/* Uhrzeit formatieren */
sprintf (mytime, "%02d:%02d:%02d", hours, minutes, seconds);

/* und auf dem Display ausgeben */
set_cursor (6,1);
lcd_data (mytime);
}
}
}


Hier meine LDC.c


// Pinbelegung über defines einstellbar

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

// LCD Befehle

#define CLEAR_DISPLAY 0x01
#define CURSOR_HOME 0x02

// Pinbelegung für das LCD

#define LCD_PORT PORTA
#define LCD_DDR DDRA
#define LCD_RS 4
#define LCD_EN 5


// 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(200);
}

// 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(200);
}

// erzeugt den Enable-Puls
void lcd_enable(void)
{
LCD_PORT |= (1<<LCD_EN);
_delay_us(1);
LCD_PORT &= ~(1<<LCD_EN);
}

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

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

// muss 3mal hintereinander gesendet werden zur Initialisierung

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

_delay_ms(200);
lcd_enable();

_delay_ms(200);
lcd_enable();

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

// 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(200);
}

// Sendet den Befehl: Cursor Home

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

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

void set_cursor(uint8_t x, uint8_t y)
{
switch (y) {
case 1: lcd_command(0x80+0x00+x); break; // 1. Zeile
case 2: lcd_command(0x80+0x40+x); break; // 2. Zeile
//case 3: lcd_command(0x80+0x10+x); break; // 3. Zeile
//case 4: lcd_command(0x80+0x50+x); break; // 4. Zeile
}
}

Hier meine lcd-routines.h

void lcd_data(unsigned char temp1);
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);


Vielleicht kann jemand von euch seine Display Ansteuerungsdatein Posten?

Grüße Johannes[/quote]