Błędne działanie po optymalizacji kodu

0

Witam
Aktualnie piszę zegarek na kontroler atmega8, niestety _delay_ms nie działa dokładnie, za pewne z powodu braku włączonej optymalizacji kodu, niestety po jej włączeniu z programem dzieją się dziwne rzeczy, zegar czasami działa za szybko, czasami lcd w ogóle się nie odpala ;/

#define F_CPU 8000000UL

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <stdio.h>
#include <string.h>

#include "lcd.h"
#include "ds1307/ds1307.h"
#include "uart/uart.h"


#define UART_BAUD_RATE 2400

int main(void)
{
    ds1307_init();

    sei();

    uint8_t year = 0;
	uint8_t month = 0;
	uint8_t day = 0;
	uint8_t hour = 0;
	uint8_t minute = 0;
	uint8_t second = 0;

	char *str = "123";
	char datetime[8] = "";

	ds1307_getdate(&year, &month, &day, &hour, &minute, &second);


	DDRD=0xFF;
    lcd_init(LCD_DISP_ON);

    PORTC |= ((1 << 0) | (1 << 1) | (1 << 2) | (1 << 3));

    while(1)
    {
        for(second = second; second < 60; second++)
        {
            sprintf(str, "%d", hour);
            if(hour < 10)
                strcat(datetime, "0");
            strcat(datetime, str);
            strcat(datetime, ":");

            sprintf(str, "%d", minute);
            if(minute < 10)
                strcat(datetime, "0");
            strcat(datetime, str);
            strcat(datetime, ":");

            sprintf(str, "%d", second);
            if(second < 10)
                strcat(datetime, "0");
            strcat(datetime, str);

            lcd_puts(datetime);
            _delay_ms(1000);
            lcd_clrscr();
            memset(datetime, 0, 8 * sizeof(char) );

            if(bit_is_clear(PINC, PC3))
            {
                hour = 0;
                minute = 0;
                second = 0;

                lcd_puts("00:00:00");
                _delay_ms(500);

                while(!(bit_is_clear(PINC, 3)))
                {
                    if((bit_is_clear(PINC, 2)))
                    {
                        if(hour < 23)
                            hour++;
                        else
                            hour = 0;
                    }

                    if((bit_is_clear(PINC, 1)))
                    {

                        if(minute < 59)
                            minute++;
                        else
                            minute = 0;
                   }

                    if((bit_is_clear(PINC, 0)))
                    {
                        if(second < 59)
                            second++;
                        else
                            second = 0;

                    }
                    lcd_clrscr();



                        sprintf(str, "%d", hour);
                        if(hour < 10)
                            strcat(datetime, "0");
                        strcat(datetime, str);
                        strcat(datetime, ":");

                        sprintf(str, "%d", minute);
                        if(minute < 10)
                            strcat(datetime, "0");
                        strcat(datetime, str);
                        strcat(datetime, ":");
                        sprintf(str, "%d", second);
                        if(second < 10)
                            strcat(datetime, "0");
                        strcat(datetime, str);
                        lcd_puts(datetime);
                        memset(datetime, 0, 8 * sizeof(char) );
                        _delay_ms(500);

                }
                ds1307_setdate(year, month, day, hour, minute, second);
            }
        }

        second = 0;
        minute++;

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

        if(hour == 24)
            hour = 0;
    }
}







 
1

Być może chodzi o

char *str = "123";

Jest to zapis legalny w C, ale nie masz prawa zmieniać przypisanego w ten sposób stringa. Być może kompilator w fazie optymalizacji uznaje, że Twój kod nie ma UB i przypisuje wartość 123 na stałe do str.

Poprawnie:

char str[4] = "123";

lub, patrząc po wywołaniach:

char str[3];

Ewentualnie, gdybyś poczytał o sprintf ( http://en.cppreference.com/w/c/io/fprintf ) to byś mógł zamiast

            sprintf(str, "%d", hour);
            if(hour < 10)
                strcat(datetime, "0");
            strcat(datetime, str);
            strcat(datetime, ":");
 
            sprintf(str, "%d", minute);
            if(minute < 10)
                strcat(datetime, "0");
            strcat(datetime, str);
            strcat(datetime, ":");
 
            sprintf(str, "%d", second);
            if(second < 10)
                strcat(datetime, "0");
            strcat(datetime, str);

napisać

// po co komu str
// char str[3];

char datetime[9] = ""; // 9 bo na c-stringi sa null-terminated

// ...

sprintf(datetime, "%02d:%02d:%02d", hour, minute, second);


0

Dzięki wielkie, przynajmniej skróciłem kod, niestety teraz po uruchomieniu optymalizacji, zegar zaczął chodzić za szybko - bardzo szybko, więc myślę, że jest jeszcze jakiś problem ;/ Jeżeli ktoś coś poradzi będę bardzo wdzięczny

1

To zależy czym jest

_delay_ms(1000);
0

powinno być sekundową przerwą, taktowanie rdzenia jest poprawne, więc nie mam pomysłu co jest źle... W dodatku zegar przyśpiesza tylko gdy uruchamiam optymalizację...

2

Jeśli chodzi szybciej niż 1x na sekundę, to na pewno _delay_ms(1000) nie powoduje sekundowej przerwy. Szybkie googlowanie potwierdza: http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=37807

0

Znalazłem taką funkcję, wszystko działa ok, ale zamiast F_CPU wpisałem 35 Mhz co jest absurdalne, gdyż kontroler uważa, że pracuje z tak ogromnym taktowaniem

void delay_ms( int time_ms)
{
    unsigned short delay_count = 35000000 / 4000;

    unsigned short cnt;
    asm volatile ("\n"
    "L_dl1%=:\n\t"
    "mov %A0, %A2\n\t"
    "mov %B0, %B2\n"
    "L_dl2%=:\n\t"
    "sbiw %A0, 1\n\t"
    "brne L_dl2%=\n\t"
    "dec %1\n\t" "brne L_dl1%=\n\t":"=&w" (cnt)
    :"r"(time_ms), "r"((int) (delay_count))
    );
}
1

Jeżeli chcesz takie duże przerwy to użyj timera.

_delay_ms() działa poprawnie tylko kiedy optymalizacje są włączone i nie za bardzo nadaje się do takich długich przerw - to jest napisane w dokumentacji. (http://www.nongnu.org/avr-libc/user-manual/group__util__delay.html)
Z tymi opóźnieniami trzeba uważać - zbyt duża przerwa może spowodować, że opóźnienia nie będzie w ogóle.
Jeżeli używane jest __builtin_avr_delay_cycles to wtedy max to aż 268 sekund dla 16 MHz. (Kompilator musi to obsługiwać)
Można też użyć starej wersji opóźnienia - trzeba zdefiniować sobie __DELAY_BACKWARD_COMPATIBLE__ przed dołączeniem nagłówka <util/delay.h> i wtedy max to 6.5 sekundy. (Chyba, że kompilator nie obsługuje tego wyżej to ta "stara" wersja wybierana jest automatycznie)

Mimo, że "nowa" wersja potrafi generować długie opóźnienia użycie timera i tak jest o wiele lepsze. Po pierwsze - nauczysz się go używać. Po drugie - procesor się tak nie męczy. :-P No i zawsze trzeba pamiętać o tym, że argumenty tych funkcji opóźniających muszą być znane podczas kompilacji - inaczej zostaną użyte operacje zmiennoprzecinkowe i wszystko pójdzie w maliny.

0

I polecam F_CPU dodać do Makefile'a/globalnych ustawień projektu zamiast definiować w samym pliku, to tak na marginesie ;)

0

Ok, dzisiaj spróbuję z timerem, mam taktowanie ustawione w projekcie, tutaj dodałem dla pewności bo liczyłem, że może z tego powodu nie działa poprawnie

0

A tak zapytam jeszcze, czy macie może jakiś inny pomysł na zrealizowanie tego projektu? Czy jednak generowanie tych opóźnień to jedyny sposób.

0

Timer, tłumienie drgań styków, praca w oparciu o przerwania.
Jeżeli DS1307 może działać jak źródło zegara dla AVRa to też warto z tego skorzystać...

0
if (TCNT1 >= 31250)
{
    lcd_puts(datetime);
    TCNT1 = 0;
}

Do czyszczenia wyświetlacza powinienem użyć drugiego timera?

0

Nie było pytania :)

0

Rozwiązałem problem w ten sposób:

if(TCNT1 >= 31250)
{
    second++;

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

    if(minute == 60)
    {
        hour++;
        minute = 0;

        if(hour == 24)
            hour = 0;
    }

    sprintf(datetime, "%02d:%02d:%02d", hour, minute, second);
    
    lcd_clrscr();
    lcd_puts(datetime);
                
    TCNT1 = 0;
}

Pozdrawiam

1 użytkowników online, w tym zalogowanych: 0, gości: 1