16F877 prestane fungovat pri vetsim ASM (memory limit?)

Resim (pro me zajimavy) problem. Script ASM ma velikost 71337kB (mam tam hodne poznamek) a vysledny HEX je o velikosti 11 813kB. Kdyz naprogramuji PIC, tak se program spusti a vse funguje jak ma. Kdyz do scriptu ASM pridam jeden prikaz (jakykoliv, staci treba i NOP), velikost ASM je 71 342kB, HEX 11 817kB a naprogramuji PIC, program se nespusti. Overuji to treba tak, ze na zacatku mam roznuti LED diody - pokud program v PICu je spusteny, na zacatku rozne LEDku. Kdyz se podivam v MPLABu na View - Memory usage gauge, viz priloha. Pri kompilaci se mi nehlasi zadna chyba. Poradi mi nekdo ? Abych rekl pravdu, docela v tech limitech plavu, 16F877A ma 8kB pro program, HEX mam 11 813kB a MPLAB mi hlasi 2092.
Poradi mi nekdo ? Dekuji.
jen pro info, v te priloze je nekolik NOP-u, to tam je jen z duvodu “testovani” kdy to prestane fungovat, jinak tam nemaji ted zadny smysl.
memory_pic.JPG

Tak uz jsem prisel asi na pricinu problemu, kdyz vynecham INI_LCD (inicializace LCD displeje) tak se mi LED dioda rozsviti pokazde i kdyz pridam dalsich nekolik radku (treba NOP).

INI_LCD	
	call	CEK15m			; cekej 15ms
	bcf		RS				; zapis ridicich instrukci do LCD
;-------------------------------
	movlw	30h				; !!! poslat 03h na PORTD 4-7 (vstupy LCD DB4-DB7) !!!
;	movlw	03h				; !!! poslat 03h na PORTD 0-3 (vstupy LCD DB4-DB7) !!!
	movwf	LCD_PORT
;       ------------------------
	bsf		E
	bcf		E
	call	CEK4m			; cekej 4,1ms
;       ------------------------
	bsf		E
	bcf		E
	call	CEK100			; cekej 100us
;       ------------------------
	bsf		E
	bcf		E
	call	CEK40			; cekej 40us
;-------------------------------
	movlw	20h				; !!! poslat 03h na PORTB 4-7 (vstupy LCD DB4-DB7) !!!
;	movlw	02h				; !!! poslat 03h na PORTB 0-3 (vstupy LCD DB4-DB7) !!!
	movwf	LCD_PORT		; nastavena 4-bitova komunikace
;       ------------------------
	bsf	E
	bcf	E
	call	CEK40			; cekej 40us
;-------------------------------
	movlw	28h				; 00101000 - počet bitů, 2 řádky, 5x7 znaky
	call	WR_CMD
	movlw	0Ch				; 00001100 - display ON, kurzor OFF, blikání OFF
	call	WR_CMD
	movlw	01h				; 00000001 - smaže displej, kurzor na pozici 0
	call	WR_CMD
	movlw	06h				; 00000110 - směr kurzoru, posunu displeje
	call	WR_CMD
;       ------------------------
	return

;**************************************************************************
; Podprogram na obsluhu LCD
;**************************************************************************
CLS_LCD
	movlw	.0
	goto	WR_CMD
LINE1
	movlw	0x80
	goto	WR_CMD
LINE2
	movlw	0xC0
	goto	WR_CMD
LINE3
	movlw	0x94
	goto	WR_CMD
LINE4
	movlw	0xD4
	goto	WR_CMD

WR_CMD
	banksel	0
	bcf		RS				; RS=0, zápis instrukcí do LCD
	goto	$+4
;-------------------------------
WR_LCD   
	banksel	0
	bsf		RS				; RS=1, zápis dat do LCD
;       ------------------------
	movwf	ZNAK			; ulozit W do ZNAK   !!! data v registru W !!!
	bsf		E				; nastav Enable
;       ------------------------
	movf	LCD_PORT,W		; zapamatovat stav PORTuD
;	iorlw	0x0F			; !!! poslat na PORTD 0-3 !!! (vysledek je xxxx1111)
	iorlw	0xF0			; !!! poslat na PORTD 4-7 !!! (vysledek je 1111xxxx)
	movwf	TMP1			; zapise W do TMP1
;-------------------------------
;   swapf	ZNAK,W			; !!! poslat na PORTD 0-3 !!!
;   iorlw	0xF0			; zamaskuje horní bity (vysledek je 1111xxxx)

	movf	ZNAK,W			; !!! poslat na PORTD 4-7 !!!
	iorlw	0x0F			; zamaskuje dolní bity (vysledek je xxxx1111)

	andwf	TMP1,W			; pošle vyšší 4 bity na PORTD
	movwf	LCD_PORT
	bcf		E				; zapíše do LCD
;-------------------------------
	bsf		E				; nastav Enable

;   movf   ZNAK,W			; !!! poslat na PORTD 0-3 !!!
;   iorlw   0xF0			; zamaskuje horní bity (vysledek je 1111xxxx)

	swapf	ZNAK,W			; !!! poslat na PORTD 4-7 !!!
	iorlw	0x0F			; zamaskuje dolní bity (vysledek je xxxx1111)

	andwf	TMP1,W			; pošle nižší 4 bity na PORTD
	movwf	LCD_PORT
	bcf		E				; zapíše do LCD
;-------------------------------
	btfsc	RS
	goto	CEK40			; RS=1, zápis dat - čekej 40 us
;       ------------------------
	movlw	04h				; instrukce 1, 2 a 3 - čekej 1,64 ms
	subwf	ZNAK,W
	btfss	STATUS,C
	goto	CEK1m6			; C=0, instrukce CLEAR - čekej 1,64 ms
	goto	CEK40			; C=1, zápis dat - čekej 40 us

Takze pokud je ma uvaha spravna, pamet je bloku 0x30 pouzita pro inicializaci LCD. Ja pro sve promenne pouzivam

	cblock	0x29
	;* promenne pro teplotni cidlo
	TMP0
	TMP1
	TMP2
	TMP3
	FLAG
	TEMP_MSB
	TEMP_LSB

	;* promenne pro casove smycky
	SM_AD1
	SM_AD2
	SM_AD3

	;* promenne pro LCD
	ZNAK
	T3
	T2
	T1
	T0

	;* promenne pro mereni z AD prevodniku
	xw0
	xw1
	ST
	SE
	f0
	f1
	SH
	SZ
	counter
	Fehler

	;* promenne ktera obrazovka se ma zobrazit
	scr1
	scr_alarm

	;* pracovni promenna
	wrks
	wrk1
	wrk2
	wrk3
	wrk4
	

	;* promenne pro matematicke vypocty
	mat_A1                	; LByte (dolní)
	mat_A2                	; MByte (střední)
	mat_A3                	; HByte (horní)
	mat_B1                	;
	mat_B2                	;
	mat_B3                	;
	mat_C1                	;
	mat_C2                	;
	mat_C3                	;
	mat_D1                	;
	mat_D2                	;
	mat_D3                	;
	mat_X1                	;
	mat_X2                	;
	mat_FL                	; Flag

	;* promenne kde ulozim aktualni namerene hodnoty
	act_u_f0				; aktualni vstupni napeti
	act_u_f1				; aktualni vstupni napeti
	max_u_f0				; maximalni vstupni napeti
	max_u_f1				; maximalni vstupni napeti
	min_u_f0				; minimalni vstupni napeti
	min_u_f1				; minimalni vstupni napeti

	act_i_f0				; aktualni celkovy odebirany proud
	act_i_f1				; aktualni celkovy odebirany proud
	max_i_f0				; maximalni aktualni celkovy odebirany proud
	max_i_f1				; maximalni aktualni celkovy odebirany proud

	act_temperature_f0		; aktualni teplota
	act_temperature_f1		; aktualni teplota
	max_temperature_f0		; maximalni aktualni teplota
	max_temperature_f1		; maximalni aktualni teplota
	min_temperature_f0		; minimalni aktualni teplota
	min_temperature_f1		; minimalni aktualni teplota
	temperature_flag		; zda se jedna o kladnou ci zapornenou hodnotu
;	old_temperature_flag	; zda se jedna o kladnou ci zapornenou hodnotu (hodnota z predchazejiciho mereni)

	vyhr1_procenta_f0		; nastaveny procentualni vykon vyhrivani c.1
	vyhr1_procenta_f1		; nastaveny procentualni vykon vyhrivani c.1
	vyhr1_i_f0				; odebirany proud vyhrivani c.1
	vyhr1_i_f1				; odebirany proud vyhrivani c.1

	vyhr2_procenta_f0		; nastaveny procentualni vykon vyhrivani c.2 
	vyhr2_procenta_f1		; nastaveny procentualni vykon vyhrivani c.2 
	vyhr2_i_f0				; odebirany proud vyhrivani c.2
	vyhr2_i_f1				; odebirany proud vyhrivani c.2

	vyhr3_procenta_f0		; nastaveny procentualni vykon vyhrivani c.3 
	vyhr3_procenta_f1		; nastaveny procentualni vykon vyhrivani c.3 
	vyhr3_i_f0				; odebirany proud vyhrivani c.3
	vyhr3_i_f1				; odebirany proud vyhrivani c.3
	
	;* promenne pro porovnani dvou 16bitovych cisel (if a1 > a2 ... else ... endif)
	xalo
	xahi
	xylo
	xyhi
	compare_work
	compare_status

	endc


	cblock	0x41
	resault_compare_u		; vysledek porovnani 16bitovych cisel - U
	resault_compare_t		; vysledek porovnani 16bitovych cisel - Temperature
	
	bargraph_data			; promenna pro zobrazeni bargrafu 
	bargraph_blink			; promenna pro blikani udaju bargrafu

	view_binary_data

	endc

Zkousel jsem to rozdelit, ale nepomaha to. Jak z toho ven ? Tech promennych mam docela hodne. ja nejdrive merim vsechny hodnoty co potrebuji, ty uzloim do promennych a nasledne na jednotlivych obrazovkach LCD display zobrazuji konkretni data.

Pokud bych chtěl mít jistotu, tak bych si stáhnul IC-prog, nastavil si v něm tu 16F877 a do bufferu nahrál ten HEX (poslední adresa v programové paměti je u 16F877 s 8K paměti 1FFF), a uvidíš hned jestli se vejde do paměti nebo ne. Jinak neznám MPLAB, zatím jsem v něm nepsal, ale předpokládám, že i ten snad umí zobrazit buffer s HEXem… (?)

Tak jsem si ten HEX stahl pres PICKIT2 (pres PICKIT2 Programmer) a posledni obsazene misto je 0828. pak az do 1FF8 jsou samo 3FFF.

Tak v programové paměti teda problém není. Počítal jsem jen tak z plezíru deklarace proměnných (teda v tom co jsi zveřejnil), těch je něco okolo devadesáti, jelikož 16F877 má 368B RAM, tak tady by problém taky být neměl (pokud začínáš od správné adresy, to jsem nekontroloval), chyba asi bude vznikat někde při překladu. Díval jsem se do té inicializace, a zarazil mě tam již zde několikrát probíraný BANKSEL 0… Zkus to opravit; jinak další tipy nemám, nevím.

Kdyby jsi sem hodil celej ASM, bylo by to jednodušší. A jinak, co jsem zběžně koukal,[code] cblock 0x41
resault_compare_u ; vysledek porovnani 16bitovych cisel - U
resault_compare_t ; vysledek porovnani 16bitovych cisel - Temperature

bargraph_data ; promenna pro zobrazeni bargrafu
bargraph_blink ; promenna pro blikani udaju bargrafu

view_binary_data

endc [/code]
Toto se ti překrývá s tímhle.

Ale to by na závadu být nemělo.

Tak zahada je asi rozlustena. Smazal jsem cely projekt v MPLABu (vectne vsech souboru co jsem mel v adresari) a vytvoril ho znovu (co bylo v ASM jsem mel v clipboardu). No a najednou to zacalo fungovat. Neni mi to vubec jasne proc se tak stalo.

to:pokus.omyl
BANKSEL 0 nahradit BANKSEL PORTD ? Hledal jsem informaci zde na foru a snad to je ono.

to: honza3
ze se to prekryva, tim mas na mysli ze se ty promenne navzajem “hadaji” o stejne pametove misto ? A jak si to zjistil ?

Kazdopadne vsem dekuji za rady a jsem rad ze jsou lide kteri radi pomuzou, kezbych i ja mohl byt ve Vasi pozici radcu …

jo hadaji,
But si to spocitas nebo se koukni jakou adresu ma kazdej definovanej registr adresu,
1 prirazeni mas 0x29 - 0x7A
2 prirazeni 0x41 - 0x45

a pouziva se BANKSEL PortA, BANKSEL + jmeno registru , NE cislo banky, je to tu na foru nekde napsany
Prekryvani_registru.jpg

BANKSEL 0 je BANK0 ale BANKSEL 1 je taky BANK0 :smiley: To samé platí i pro řadu 18F, jak jsem zjistil :blush:

Tak zahada je opet tady s nefunkcnosti. Pripadne chyby jsem opravil (BANKSEL 0 jsem prepsal na BANKSEL PORTD) a vse funguje jak ma. Kdyz ale kdekoliv pridam dalsi radky programu, tak se procesor zacne “resetovat” ?
Ve scriptu mam

;********************* Konec inicializace procesoru  **********************
	bsf 	PORTD,2				; LED-ka ted nesmi svitit, az se rozviti, vim ze procesor zpracovava program
	CALL	CEK1s
	bcf		PORTD,2

a tim vim ze procesor aspon neco dela (roznu na chvili LEDku a pak ji zhasnu).
Pak pokracuji vynulovanim promennych a zobrazim na LCD display uvodni text

;*************************************************************************
; Po zapnuti napajeni zobrazim uvodni info
	call	view_screen_start

Nasledne ve smycce MAIN nacitam data a nasledne zobrazuji na LCD. Vse je OK jen do doby, pokud kdekoliv nepridam nekolik prikazu, to se pak chova tak, ze se zobrazi na LCD uvodni text a misto aby se provedlo co je v MAIN, tak se zase zacne se zobrazovat onen uvodni text a to porad dokola.
Prikladam cely ASM,.

Přesně, co jsem čekal.:slight_smile:

Překročil jsi PAGE0 a kousek programu ti zasahuje do PAGE1. Už se nebudeš otravovat s BANKSEL, ale ted už i s PAGESEL :slight_smile:

Jestli ti mužu poradit, hod si osvedčené a funkční podprogramy, jako třeba INIT procesoru, vše s LCD, matiku a CEKxx do PAGEx, a v PAGE 0 si nech místo pro psaní. Já dávám funkční “kousky” programu odzadu, PAGE3, mám pak místo v PAGE0 na psaní a "tvoření funkčních kousků, které pak taky hodím odzadu. MAIN nechávám v BANK0.
Pak stačí napsat třebapagesel INIT call INIT

Aha, takze jsem prekrocil limit. Pointu, co pises, chapu, ale neni mi jaksna ta konstrukce

pagesel INIT
call INIT

Muzes, prosim, mi tady uvest nazorny kod jak to myslis ? Co jsem zkousel ja

PAGE 3
INIT
banksel OPTION_REG
movlw b'00000000'
movwf OPTION_REG
banksel ADCON0
movlw b'01000001'
...
...
PAGE 0

je blbost, snazil jsem se najit nejaky vzrovoy priklad pres google, ale nenasel jsem.

Skus si precist todle tema + 2 odkazy ve 3 prispevku
forum.mcontrollers.com/viewtopic.php?t=1326&postdays=0&postorder=asc&highlight=pagesel&start=0

Tak jsem si to prect a abych rekl pravdu, nejsem z toho moc chytry :frowning:

Z čeho přesně nejsi moc moudrý?
Když si to vezmeš s pohledu HW tak PIC, tak tvůj konkrétní typ má 14b jádro - když se koukneš do datasheetu, tak v části pojednávající o instrukční sadě máš rozepsány jednotlivé OP kódy. Instrukce CALL a GOTO obsahují z celé 14b délky 10b adresu -> můžeš naadresovat jen 2048 adres paměti.

Tvůj problém je, že jsi už přesáhl limit 10b. Já teď nevím jakou šířku má programový čítač. 8b máš přístupných jako registr PC a zbytek jako PCLATH - s tím, že v PCLATH máš překopírovaný zbylý bity PC.

JAk z toho ven:
Právě abys mohl skákat a obsloužit víc jak 2kB programu tak je potřeba před zavoláním podprogramu nebo před skokem naplnit PCLATH.

PAGESEL      SKOC    ;NÁVĚSTÍ KAM SE BUDE SKÁKAT
CALL            SKOC   ;PROVEDENÍ SKOKU

Překladač zná adresy návěstí, PAGESEL dělá to, že se vezme horních 8b co by se pak měly nacházet ve skutečném PC, přesune je do PCLATH a pak se teprve provádí skok. 10b adresa z instrukce skoku + PCLATH pak umožňují naadresovat celou paměť.

Omlouvám se za mírné nepřesnosti.

Stačí to takhle?

Ja to tak nejak teoreticky chapu, jen se mi to nepovedlo uvest do praxe.
Kdyz to vezmu pres ASM tak by to melo vypadat nasledovne

definice promennych
inicializace portu
...
pagesel reg2
call ini_lcd ; inicializace LCD

MAIN
LCDout 'A' ; napisu 'A' na LCD
GOTO MAIN

;* inicializace lcd display
ini_lcd
...
...
return

Kdyz to takhle napisu, tak to nefunguje

definice promennych
inicializace portu
...

ini_lcd ; inicializace LCD

MAIN
LCDout 'A' ; napisu 'A' na LCD
GOTO MAIN

;* inicializace lcd display
ini_lcd
...
...
return

A tady mi to funguje.

Ty musíš napsat:

PAGESEL       INI_LCD
CALL            INI_LCD

Pak ti to půjde. Protože když jsi tam dal reg2, tak ti to do PC natáhne dohnotu odněkať pryč a bude ti to skákat kamsi, kdo ví kam.

Takže, pagesel návěstí
call nebo goto a to návěstí, který jsi napsal do pagesel.

Jo, takze to jsem udelal chybu a je mi to ted jasne. Jak se pak resi kdyz treba v reg0 chci mit “hlavni program” a v reg1 a reg2 “podpurne utility” ?

cblock 0x20
definice promennych
endc

#define xyz1
#define xyz2

aaa macro par1
...
endm

org 0x00

goto init

init ; inicializace portu
...

pagesel reg1
call ini_lcd ; inicializace LCD
call ini_16s20 ; inicializace teplotniho cidla

bsf portd,2 ; roznu si LEDku

MAIN

LCDout 'A' ; napisu 'A' na LCD

pagesel reg2
call cek1s

pagesel reg3
call neco1
call neco2

pagesel reg2
call cek2s
call cek1s

pagesel main

GOTO MAIN


reg1: ; navesti
ini_lcd
...
...
return

ini_16c20
...
...
return


reg2: ; navesti
cek1s
...
...
return

cek2s
...
...
return

reg3: ; navesti
neco1
...
...
return

neco2
...
...
return

end

No a musim jeste definovat na jake adrese zacina reg1, reg2, reg3 ? Ja totiz porad “plavu” v banksel a pagesel v teto zalezitosti prepinani pametovych bank.

Tak abys určitou část programu natlačil do určité banky, na to musíš použít datasheet, konkrétně část “Memory organization”. Máš tam hned na prvním obrázku vlevo PIC16F877 a je tam vidět rozmezí adres pro konkrétní stránky.
Ty když chceš mít část programu na stránce 2, tak jednoduše před ten blok programů napíšeš org 0x0FFF a budeš to mít tam.

Banksel je zase udělátko na přepínání bank paměti RAM. Třeba TRISA je v bance 1. Ty máš 2 možnosti, buď si rušně přepneš banku nasetováním RP0 ve STATUSU nebo použiješ BANKSEL, který si samo pohlídá ve které bance je konkrétní registr a přepne na příslušnou banku.

                              ;VE W MÁŠ ČÍSLO, KTERÝ CHCEŠ ZAPSAT DO TRISA
BANKSEL     TRISA  ;PŘEPNEŠ BANKU
MOVWF       TRISA   ;ZAPÍŠEŠ HODNOTU

Já osobně preferuju způsob, že si banky přepínám sám. Mám na to napsaný makro, který přepíná na zvolenou banku.

Jak to tam máš s tou pamětí, moc jsem to nepročítal a nezkoumal.
Na přiřazení názvu registrům používám CBLOCK, nevím jestli jsi to použil taky nebo používáš EQU.

Kdyžtak CBLOCK se používá takhle:

CBLOCK     0x20      ;OFFSET PAMĚTI

PROMENA1
PROMENA2
PROMENA3
TMP_W
TMP_STATUS

ENDC

CBLOCK - od určité adresy ti přiřadí názvy registrů k fyzickým registrům.

Já používám pouze CBLOCK a využívám registry pouze v BANK 0. Píšu programy velice úsporný na použitou RAM a vystačím si s max 30B :slight_smile:

Tady máš INIT v PAGE3, třeba to z toho pochopiíš.
AstroBOX.asm (69.3 KB)