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.
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
BANKSEL 0 je BANK0 ale BANKSEL 1 je taky BANK0 To samé platí i pro řadu 18F, jak jsem zjistil
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.
Překročil jsi PAGE0 a kousek programu ti zasahuje do PAGE1. Už se nebudeš otravovat s BANKSEL, ale ted už i s PAGESEL
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
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
Tady máš INIT v PAGE3, třeba to z toho pochopiíš.
AstroBOX.asm (69.3 KB)