Problem s displayom - chyba po volani instrukcii

Mam problem. Vyzeram tu ako totalny amater, ale nakolko mi vsetko pod assemblerom bezalo ako ma a pod C-ckom sa mi vsetko babre, tak sa proste musim opytat, co z tym zase je. MPLABX kompilator XC8 1.20. Rozmyslam uz o inom kompilatore. Proste si spustim assemblerovsku aplikaciu, kde pouzivam display a nemam s nim absolutne ziadne problemy. Akonahle ale presne to iste urobim v C-cku, tak display zrazu nevie, co ma robit. Mam dvojriadkovy univerzalny display. Ked mu poslem instrukciu “0xC0”, tak v assembleri posunie kurzor do druheho riadku, v C-cku sa mi zacne spravat ako po restarte (pri vyssom kontraste mi svieti prvy riadok, druhy nereaguje). Inicializacia je spravna, lebo kym nezavolam tuto instrukciu, display bezi, tak to neriesim. Tu je kus kodu:

void sendCommand(unsigned sendByte) {
    RS = 0;
    sendAByte(sendByte);
}

void sendAByte(char sendBites) {
    E = 1;
    LCD1 = 1 & (sendBites >> 7);
    LCD2 = 1 & (sendBites >> 6);
    LCD3 = 1 & (sendBites >> 5);
    LCD4 = 1 & (sendBites >> 4);
    E = 0;
    E = 1;
    LCD1 = 1 & (sendBites >> 3);
    LCD2 = 1 & (sendBites >> 2);
    LCD3 = 1 & (sendBites >> 1);
    LCD4 = 1 & sendBites;
    E = 0;
    __delay_ms(2);
}

void secondLine() {
    sendCommand(0xC0);
}

Aby som este doplnil zoznam zvlastnych spravani, tak presne to iste sa mi stane, ak zavolam instrukciu 0x01, 0x40, 0x02, 0x80 atd… Ak si chcem napriklad definovat vlastny znak, tak tam hodim instrukciu 0x48 a prvy znak sa mi zapise na adresu 0x01. Znova upozornujem, tieto iste instrukcie v assembleri funguju normalne, v C-cku robia problemy. Debugoval som to cele a nevidim v tom problem. Text a inicializacia ficia normalne.

A mimochodom, ak zavolam instrukciu 0xC1, hodi mi to kurzor na druhy znak v druhom riadku.

Tenhle kompilátor moc neznám ale zeptám se - mohl by zoptimalizovat ten kód tak že E=0; a E=1; se neprovede ? Nejrychleji se dozvíš co je blbě když to proženeš simulátorem a koukneš se do disasm toho tvého programu.

Presne toto som uz skusal. Kedze to je iba nejaka funkcia, tak nieje mozne, ze sa ta ista funkcia spusti 2x s roznymi instrukciami. A kedze inicializacia prebehne stale v poriadku a takisto aj odosielanie textu, tak predpokladam spravne poradie instrukcii. Inak, zistil som, ze ak chcem odoslat instrukciu na vymazanie displaya (0x01), tak predtym musim do isteho casu display znova inicializovat, aby bezal spravne. Inak display nevymazem (ak vypisem viac, ako 5 medzier v druhom riadku, zblbne mi to).

Pořádně si přečti datashet k tomu LCD. Pokud nepoužíváš signál BUSY k testování stavu, tak musíš programově zajistit časování signálů. Pro instrukce HOME a CLR je nutné počkat cca 1,64ms pro ostatní cca 40us(záleží na typu IO v LCD). Po nastavení dat na port je vhodné určitý čas počkat(ustálení napětí). Pro funkci signálu E je potřeba určitý čas (cca >500ns),což při sledu příkazů E=0; E=1 a vysokém Fcpu nemusí být vždy splněno.
Když tak dej na forum kompletní schéma a program(pokud to není tajné), třeba bude chyba ještě jinde.

1,64ms dodrziavam urcite, nakolko tam mam po odoslani bajtu delay 2ms. Zvecsenim delayu sa problem neodstranil, takisto pridanim delayu pred zmenu E pinu, alebo medzi E=0; E=1. Dokonca ak medzi tuto zmenu dam delay dlhsi, ako 1us, tak sa mi display ani nezinicializuje. Takt procesora som z 8MHz znizil na 4MHz a problem pretrvava. Referencne som skusal odoslat 0xC0.

Co sa tyka schemy a programu, tak vytvaram vlastne kniznice pre display. Schema pozostava z procaku a displaya, ktory je pripojeny na RB0 - RB6. Predstav si tieto 3 funkcie a ku nim main, kde odosielam po inicializacii command 0xC0. 0xC1 ako som spominal funguje normalne => prehodi mi kurzor na druhu poziciu druheho riadku. 0xC0 by ma mala hodit na prvu poziciu. Zistil som, ze sa na prvu poziciu dostanem odoslanim commandu 0xBF :smiley:

Jeste jednou - odkrokoval jsi ten “C” program na urovni ASM a zkontroloval hodnoty na portu RB0-RB ? Protože pokud máš časování v pořádku, tak chyba je v tom co tomu lcd posíláš.

Chyba by mohla být v použité proměnné typu char - kompilátor toto může brát jako proměnnou v rozsahu (127… -128). Takže (x = 128) může převést jako x = 128 nebo x = 1 nebo x = 0 (záleží na překladači). Pro rozsah 0…255 používej definici unsigned char.

Pravdu může mít radius to přepnutí pinu Enable do nuly a hned zas do jedničky může překladač při optimalizaci vyhodnotit, jako že jsi se zbláznil a vyhodí ti to :slight_smile: .

Naopak, co píše standa33 je nesmysl - 0xC0 je binárně 0b11000000 ať je char signed, nebo unsigned - to získává smysl až při nějakých výpočtech - ne v pouhém bitovém posunu jako v tomto případě - mimochodem ve většině překladaču se dá v nějakém nastavení najít jestli zápis char znamená unsigned char, nebo signed char, případně si to nastavit.
Největší jistotu ale budeš mít, když to tam budeš psát celé - unsigned char - nebo si definovat vlastní typ - třeba:

typedef unsigned char byte; typedef unsigned char u_char8; atd, atd....

A jinak tvoje funkce se docela liší od mé - spolehlivě fungující - úprava té tvojí

void sendAByte(char sendBites) { 
    //E = 1; 
    LCD1 = 1 & (sendBites >> 7); 
    LCD2 = 1 & (sendBites >> 6); 
    LCD3 = 1 & (sendBites >> 5); 
    LCD4 = 1 & (sendBites >> 4); 
    E=1; // E = 0; 
     __delay_ms(1); 
    E=0;// E = 1; 
    LCD1 = 1 & (sendBites >> 3); 
    LCD2 = 1 & (sendBites >> 2); 
    LCD3 = 1 & (sendBites >> 1); 
    LCD4 = 1 & sendBites; 
    E=1; // E = 0; 
     __delay_ms(1); 
    E=0;// E = 1;  
    __delay_ms(2); 
    RS=0;
}

tedy zápis do LCD na náběžnou hranu na E - předpokládám, že tvůj displej má klasický hd44780 řadič.

Taky ta vložená funkce **__delay_ms(1); ** by měla zajistit, aby nedošlo k té optimalizaci při překladu.

Taky by bylo dobré, dát k dispozici celý kód - a hlavně definice těch výstupů (LCD1…4,E,RS atd).

LCD1 máš na displeji na D4, nebo D7? Neposíláš to tam obráceně?

Jak jsem psal záleží na překladači. Pokud na LCD posíláš znaky ASCII, tak 0…127 jsou standartní znaky, takže definice char funguje pro text bez problémů.
Ovšem při zaslání příkazu může být hodnota větší než 127 a pak nastane problém.
S tímto problémem jsem se setkal při zápisu a čtení dat z I2C EEPROM, měl jsem funkce definované jako char read_EEPROM(…) a write_EEPROM(char data,…) a pořád jsem se divil proč mi to vrací proměnnou jen v rozsahu 0…127.

Nemohl by to byt treba “rwm” problem … teda aspon myslim, ze se to tak jmenuje . E = 1, E = 0. Nemela by treba mezi tim byt aspon jedna instrukce NOP. Tezko soudit nevidel jsem jaky pouzivas MCU a na jakym taktu. Obcas se to stava. :smiley:

Standa:
I přesto si myslím, že displeji je jedno, jestli 0xC1je dekadicky +193 nebo -63 :slight_smile: .

Nejlepsi kdyz das cely kod vcetne definic tech portu jak uz byla zminka … nikdo nevi jestli zapisujes do PORTu nebo do LATu.

Hele borci, neni lepsi treba:

LATD &= 0xf0;
LATD |= (unsigned char)((sendBites & 0xf0)>>4);
E = 1;
NOP();
E = 0;
__delay_us(50);        // treba, uz nevim jestli potrebuje nejaky cas
LATD &= 0xf0;
LATD |= (unsigned char)(sendBites & 0x0f);
E = 1;
NOP();
E = 0;

akorad si zrovna nejsem jisty zda jdou prvni vyssi a pak nizsi nebo naopak (dlouho jsem s temahle LCD nedelal) tak me nekamenujte.

jj souhlasim … Takle jsem to nikdy nedelal ja pouzivam porad unsigned char. signed char pouzivam jen pri vypoctech[/code]

Pro kolemjdoucího :
Mezi jednotlivýma částma dat není delay potřeba.
První se posílá vyšší polovina bytu.

Souhlasím s tím (pokud nemáš jednotlivé bity od LCDčka připojené na různé bity a porty MCU) udělat zápis celé poloviny bytu najednou.

Mohl by být rozdíl v případě rotací vpravo, kdy u neznaménkového čísla se doplňuje do nejvyššího bitu 0, ale u znaménkového se opakuje nejvyšší bit (tedy podle překladače, některé rotují signed také s 0). Ale vzhledem k tomu že tady se výsledek maskuje & 1, tak by rozdíl neměl být.

Párkrát jsem se už s unsigned napálil, když jsem např. udělal konstrukci typu “for(u8 i=9; i>=0; i–)” a divil se proč mi program mrzne. :slight_smile:

Jestli je “E” nadefinovaný jako volatile, tak překladač pro něj zdánlivě nadbytečné zápisy nevyhazuje.

Panda38:
E je nějaké makro - definice bitu SFR ( nebo jak se u picu jmenují) registru - buď PORT, nebo LAT - (to ví jen autor) - něco jako:

#define E	PORTCbits.RC3

to je v C18 - ten XC8 neznám

nevím jak by se toto dalo definovat jako volatile

Navíc v takovém Codevisionu pro AVRka bych si v tomto případě s volatile moc nepomohl - tam to funguje tak, že prefix volatile jenom řekne překladači, aby nepoužil pro proměnnou jeden ze 32 pracovních registrů - aby nedošlo k jeho přepsání během přerušení.Obsahuje ale direktivy, opt+ a opt-, kterými vyloučím z optimalizace části kódu, které potřebuju. Přiznám, že u piců jsem to až tak detailně zatím nezkoumal.C18 nic takového neumí - snad jen vypnout optimalizaci úplně - proto ta nutnost vkládat do “nelogické” posloupnosti příkazů alespoň ten nop .

Stejně bych ale viděl zádrhel v tom, že pokud by měl “logicky” propojené
piny portu a LCD nějak takto:

PB0(LCD1) - D4
PB1(LCD2) - D5
PB2(LCD3) - D6
PB3(LCD4) - D7

tak bude posílat ta jeho funkce ten půlbyte zrcadlově obrácený.

samozřejmě že by to tak šlo - dokonce by to bylo mnohem rychlejší - myslím tím princip maskování - toto konkrétně asi funkční nebude

tvoje

LATD &= 0xf0; LATD |= (unsigned char)(sendBites & 0x0f);

budou nějaké 3 - 4 asm instrukce, zatímco jeho

LCD1 = 1 & (sendBites >> 7); LCD2 = 1 & (sendBites >> 6); LCD3 = 1 & (sendBites >> 5); LCD4 = 1 & (sendBites >> 4);

je minimálně 26 instrukcí ale spíš víc

teď budu trochu OT, ale už jsem viděl, že hlavně u větších datových typů (long atd) používá autor místo bitového posunu třeba(1>>15) radši dělení
/32768 - ale nezkoumal jsem jaký to má efekt

Díval jsem se do příkladu >> definice <<, PORTCbits je struktura s bitovým polem (RC0…) a celá struktura je označená jako volatile, takže to by mělo být v pořádku.

Rozumný překladač to má umět rozpoznat že to má nahradit optimálnějším bitovým posunem - alespoň GCC a MSVC to dělají (tj. místo word/32768 by udělal ((word rol 1) and 1) ).