Witam
Od kilku dni staram się uruchomić program z książki Mikrokontrolery AVR w praktyce. Po dodaniu makr sbi i cbi program się skompilował, ale kompilator zwracał ostrzeżenia:
A w czasie wykonywania takie:
W pewnym momencie kodu generowanego przez kompilator pojawia się komenda assemblerowa:
a w tym momencie zawartość rejestru R28 jest xxxxxxxx W kodzie asemblera umieściłem komentarze, co zapętla program.
W rezultacie wskaźnik stosu przyjmuje wartość xxxxxxxx, dzięki czemu po instrukcji ret program nie może się połapać i się zapętla, gdyż nie jest w stanie wyjść z żadnej procedury.
Byłbym bardzo wdzięczny za pomoc, gdzie leży błąd - program jest ok, wklejony ze strony www wydawnictwa BTC.
Program wyglada tak:
A oto ten program w disassemblerze:
Od kilku dni staram się uruchomić program z książki Mikrokontrolery AVR w praktyce. Po dodaniu makr sbi i cbi program się skompilował, ale kompilator zwracał ostrzeżenia:
Repeated variable name __c
Repeated variable name __c within block. Rename it
typedef debugging not supported
COFF file contains inconsistencies or unsupported features. Debug info could be wrong or incomplete
A w czasie wykonywania takie:
[PC = $0156, Time = 3.62 ms, {MEM}]: Attempt to read in a forbidden/reserved position
[PC = $0166, Time = 3.64 ms, {UND}]: Load indirect. Y index is undetermined
[PC = $0067, Time = 3.76 ms, {UND}]: Stack pointer contains unknown data (X)
W pewnym momencie kodu generowanego przez kompilator pojawia się komenda assemblerowa:
out $3D, R28
a w tym momencie zawartość rejestru R28 jest xxxxxxxx W kodzie asemblera umieściłem komentarze, co zapętla program.
W rezultacie wskaźnik stosu przyjmuje wartość xxxxxxxx, dzięki czemu po instrukcji ret program nie może się połapać i się zapętla, gdyż nie jest w stanie wyjść z żadnej procedury.
Byłbym bardzo wdzięczny za pomoc, gdzie leży błąd - program jest ok, wklejony ze strony www wydawnictwa BTC.
Program wyglada tak:
//#include <c:\winavr\avr\include\avr\io.h>
//#include <c:\winavr\avr\include\avr\pr.h>
//#include <c:\winavr\avr\include\avr\signal.h>
#include <c:\winavr\avr\include\avr\io.h>
#include <c:\winavr\avr\include\avr\pgmspace.h>
#include <c:\winavr\avr\include\stdlib.h>
#include <c:\winavr\avr\include\avr\sfr_defs.h>
#define PRG_RDB(addr) pgm_read_byte(addr)
#define lcd_rs 2 //definicja bitu portu dla linii RS
#define lcd_e 3 //definicja bitu portu dla linii E
#define CR 0x0a //definicja znaku CR (przejście do nowej linii)
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) //definicja cbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
unsigned char wiersz=0;
unsigned char kolumna=0;
void czekaj(unsigned long pt) //procedura wytracania czasu
{
#define tau 10.38 //przybliżony przelicznik argumentu na ms
unsigned char tp1;
for(;pt>0;pt--)
{
for(tp1=255;tp1!=0;tp1--);
}
}
void piszilcd(unsigned char instr) //zapisz instrukcję sterującą do LCD
{
cbi(PORTB,lcd_rs);
sbi(PORTB,lcd_e);
PORTB=(PORTB&0x0f)|(instr&0xf0); //przygotuj starszy półbajt do LCD
asm("nop"); //wymagane wydłużenie impulsu
asm("nop");
asm("nop");
cbi(PORTB,lcd_e); //impuls strobujący
czekaj(10L); //czekaj na gotowość LCD ok. 100us
sbi(PORTB,lcd_e);
PORTB=(PORTB&0x0f)|((instr&0x0f)<<4); //przygotuj młodszy półbajt do LCD
asm("nop");
asm("nop");
asm("nop");
cbi(PORTB,lcd_e); //impuls strobujący
czekaj(10L); //czekaj na gotowość LCD ok. 100us
}
void piszdlcd(char dana) //zapisz daną do LCD
{
sbi(PORTB,lcd_rs);
sbi(PORTB,lcd_e);
PORTB=(PORTB&0x0f)|(dana&0xf0); //przygotuj starszy półbajt do LCD
asm("nop"); //wymagane wydłużenie impulsu
asm("nop");
asm("nop");
cbi(PORTB,lcd_e); //impuls strobujący
czekaj(10L); //czekaj na gotowość LCD
sbi(PORTB,lcd_e);
PORTB=(PORTB&0x0f)|((dana&0x0f)<<4); //przygotuj młodszy półbajt do LCD
asm("nop");
asm("nop");
asm("nop");
cbi(PORTB,lcd_e); //impuls strobujący
czekaj(10L); //czekaj na gotowość LCD
}
void czysclcd(void) //czyść ekran
{
piszilcd(0x01); //polecenie czyszczenia ekranu dla kontrolera LCD
czekaj(1.64*tau); //rozkaz 0x01 wykonuje się 1.64ms
wiersz=0;
kolumna=0;
}
void piszznak(char znak) //procedura umieszcza znak na wyświetlaczu
{
piszdlcd(znak); //wyświetl znak na LCD
if(++kolumna==16) //czy bieżąca kolumna mieści się na wyświetlaczu?
{
kolumna=0; //jeśli nie, to ustaw początkową...
if(++wiersz==2) //i przejdź do nowego wiersza
{
wiersz=0; //jeśli nowy wiersz jest poza wyświetlaczem, ustaw początkowy
}
}
}
void lcdxy(unsigned char w, unsigned char k) //ustaw współrzędne kursora
{
piszilcd((w*0x40+k)|0x80); //standardowy rozkaz sterownika LCD
//ustawiający kursor w określonych współrzędnych
}
void pisztekst(char *tekst) //pisz tekst na LCD wskazywany pointerem *tekst
{ // ZAPĘTLENIE
char zn;
char nr=0;
while(1)
{
zn=PRG_RDB(&tekst[nr++]); //pobranie znaku z pamięci programu
if(zn!=0) //czy nie ma końca tekstu?
{
if(zn==CR) //czy znak nowej linii
{
wiersz==1?wiersz=0:++wiersz; //przejdź do nowej linii
kolumna=0;
lcdxy(wiersz,kolumna); //ustaw obowiązujące po zmianie współrzędne na LCD
}
else
{
piszdlcd(zn); //umieść pojedynczy znak tekstu na LCD
}
}
else
{
break; //zakończ pętlę, jeśli koniec tekstu
}
}
}
void klawisz(unsigned char pozkl) //czekaj na naciśnięcie określonego klawisza
//klawisz jest wybierany poprzez argument pozkl
{
while(PIND&pozkl); //czekaj na naciśnięcie klawisza
czekaj(30*tau); //odczekaj aż ustaną zakłócenia
while(!(PIND&pozkl)); //czekaj na zwolnienie klawisza
}
int main(void) //program główny
{
unsigned char i;
//>>>>>>>>>>>>> definicje tekstów wyświetlanych na LCD <<<<<<<<<<<<<<
char *tekst1=PSTR("kursor, mruga");
char *tekst2=PSTR("bez kursora\nbez mrugania");
// \n oznacza znak CR (przejście do nowej linii)
//>>>>>>>>>>>>>>>>>>>> konfigurowanie portów <<<<<<<<<<<<<<<<<<<<<<<<<<
PORTD=0xff; //port z podciąganiem
PORTB=0x03; //port z podciąganiem
DDRD=0x00; //PORTD - we
DDRB=0xff; //PORTB - wy
//>>>>>>>>>>>>>>>>>> inicjacja wyświetlacza LCD <<<<<<<<<<<<<<<<<<<<<<
czekaj(45*tau); //opóźnienie ok. 45ms dla ustabilizowania się napięcia
//zasilania LCD (katalogowo min. 15 ms)
//lcd_rs już wcześniej było ustawione w stanie "0"
for(i=0;i<3;i++) //3-krotne wysłanie 3-
{
sbi(PORTB,lcd_e);
PORTB=(PORTB&0x0f)|0x30; //wyślij 3- do LCD
asm("nop");
asm("nop");
asm("nop");
cbi(PORTB,lcd_e);
czekaj(5*tau); //ok. 5ms
}
sbi(PORTB,lcd_e);
PORTB=(PORTB&0x0f)|0x20; //wyślij 2- do LCD
asm("nop"); //wymagane wydłużenie impulsu
asm("nop");
asm("nop");
cbi(PORTB,lcd_e);
czekaj(10L); //od tego momentu można sprawdzać gotowość LCD
//w tym programie nie będzie sprawdzania gotowości
piszilcd(0x28); //interfejs 4-bitowy, 2 linie, znak 5x7
piszilcd(0x08); //wyłącz LCD, wyłącz kursor, wyłącz mruganie
piszilcd(0x01); //czyść LCD
czekaj(1.64*tau); //wymagane dla instrukcji czyszczenia ekranu opóźnienie
piszilcd(0x06); //bez przesuwania w prawo
while(1)
{
//>>>>>>>>>>>> EFEKT 1 <<<<<<<<<<<<<<<
piszilcd(0x0f); //włącz LCD, włącz kursor, włącz mruganie
pisztekst(tekst1);
klawisz(2);
czysclcd();
//>>>>>>>>>>>> EFEKT 2 <<<<<<<<<<<<<<<
piszilcd(0x0c); //włącz LCD, bez kursora, bez mrugania
pisztekst(tekst2);
klawisz(2);
czysclcd();
}
}
A oto ten program w disassemblerze:
; Generated by VMLAB AVR disassembler
; ===================================
;
.org $00
l_0000:
rjmp l_001f
rjmp l_0039
rjmp l_0039
rjmp l_0039
rjmp l_0039
rjmp l_0039
rjmp l_0039
rjmp l_0039
rjmp l_0039
rjmp l_0039
rjmp l_0039
ori R22, $52
and R7, R10
andi R22, $5B
andi R23, $32
andi R22, $2F
sbc R6, R17
ori R22, $52
and R7, R10
andi R22, $2D
ori R23, $75
ori R22, $E1
ori R22, $19
ori R16, $B0
andi R23, $25
ori R23, $F3
mov R7, R2
ori R18, $D0
andi R23, $52
ori R22, $17
nop
l_001f:
clr R1
out $3F, R1
ldi R28, $DF
out $3D, R28
ldi R17, $00
ldi R26, $60
ldi R27, $00
ldi R30, $B6
ldi R31, $04
rjmp l_002c
l_0029:
lpm
adiw R30, $01
st X+, R0
l_002c:
cpi R26, $60
cpc R27, R17
brne l_0029
ldi R17, $00
ldi R26, $60
ldi R27, $00
rjmp l_0034
l_0033:
st X+, R1
l_0034:
cpi R26, $62
cpc R27, R17
brne l_0033
rcall main
rjmp l_0259
l_0039:
rjmp l_0000
czekaj:
push R29
push R28
rcall l_003d
l_003d:
rcall l_003e
l_003e:
push R0
in R28, $3D
in R29, $3E
std Y+2, R22
std Y+3, R23
std Y+4, R24
std Y+5, R25
rjmp l_005a
l_0046:
ldi R24, $FF
std Y+1, R24
rjmp l_004c
l_0049:
ldd R24, Y+1
subi R24, $01
std Y+1, R24
l_004c:
ldd R24, Y+1
tst R24
brne l_0049
ldd R24, Y+2
ldd R25, Y+3
ldd R26, Y+4
ldd R27, Y+5
sbiw R24, $01
sbc R26, R1
sbc R27, R1
std Y+2, R24
std Y+3, R25
std Y+4, R26
std Y+5, R27
l_005a:
ldd R24, Y+2
ldd R25, Y+3
ldd R26, Y+4
ldd R27, Y+5
sbiw R24, $00
cpc R26, R1
cpc R27, R1
brne l_0046
pop R0
pop R0
pop R0
pop R0
pop R0
pop R28
pop R29
ret Z POWODU WSKAŹNIKA STOSU BRAK REAKCJI NA RET
piszilcd:
push R29
push R28
push R0
in R28, $3D
...
....
st X, R24
ldi R22, $0A
ldi R23, $00
ldi R24, $00
ldi R25, $00
rcall czekaj I TU POWRÓT DO PROCEDURY CZEKAJ W KÓŁKO
ldi R26, $38
ldi R27, $00
ldi R30, $38
ldi R31, $00
ld R24, Z
ori R24, $08
st X, R24
ret
pisztekst:
push R29
push R28
in R28, $3D
in R29, $3E
sbiw R28, $07
in R0, $3F
cli
out $3E, R29
out $3F, R0
out $3D, R28 POLECENIE, KTÓRE ZAPĘTLA
std Y+7, R25
std Y+6, R24
std Y+4, R1
l_017c:
ldd R24, Y+4
mov R18, R24
ldi R19, $00
ldd R24, Y+6
ldd R25, Y+7
add R24, R18
adc R25, R19
std Y+3, R25
std Y+2, R24
ldd R24, Y+4
subi R24, $FF
std Y+4, R24
ldd R30, Y+2
ldd R31, Y+3
lpm
mov R24, R0
std Y+1, R24
ldd R24, Y+1
std Y+5, R24
ldd R24, Y+5
tst R24
breq l_01ae
ldd R24, Y+5
cpi R24, $0A
brne l_01ab
lds R24, $0060
cpi R24, $01
brne l_019c
sts $0060, R1
rjmp l_01a1
l_019c:
lds R24, $0060
subi R24, $FF
sts $0060, R24
l_01a1:
sts $0061, R1
lds R24, $0061
lds R25, $0060
mov R22, R24
mov R24, R25
rcall lcdxy
rjmp l_017c
l_01ab:
ldd R24, Y+5
rcall piszdlcd
rjmp l_017c
...