Jazyk C - začátečnické chyby

Ahoj,
dovolil jsem si založit nové vlákno, jen pro občasné rady v C, ať se to nikde nemotá, snad to projde :slight_smile:.

Může mi někdo, prosím, vysvětlit, jak a zda vůbec se program v C nějak ukončuje? Z assembleru jsem byl zvyklý, že onu nekonečnou programovou smyčku je nutno vytvořit pomocí GOTO - někam zpět na začátek.

V C se však doporučuje příkaz GOTO nepoužívat. Program se však nějak zpět dostat musí. Zajišťuje to ona uzavřená dvojice závorek funkce main()? Program tedy automaticky jde na začátek?

Když tedy potřebuji nejprve jenom jednou (!) vykonat nějakou inicializaci (např. vytvořím funkci init() a teprve za ni napíši main() atd., bude to fungovat tak, že se nejprve jen jednou provede to, co je uvnitř funkce init(), pak se začne provádět hlavní program uvnitř funkce main() a po dosažení konce main() přejde opět na její začátek jako nekonečná smyčka?

Díky za vysvětlení …

Vlasťa

Nekonečný cyklus (teda náhradu GOTO) vieš spraviť jednoducho, napríklad pomocou while (1) { } – to znamená, že podmienka bude splnená VŽDY, a stále sa ti bude vykonávať tá časť programu v tej slučke.
GOTO sa nedoporučuje používať, ale v C není problém ho jednoducho nahradiť. Pre názornú ukážku ti sem dám 1 jednoduchý program, na ktorom to pochopíš.

[code]#include <p18f2320.h>
#include <delays.h>

void main()
{

TRISB=0;
while (1)
{
LATB=0b11111111;
Delay10KTCYx(1);
LATB=0;
Delay10KTCYx(1);

}
}[/code]

V assembleri by ten isty program vyzeral takto:

clrf TRISB
NAVESTIE 
 MOVLW 0b11111111
MOVWF LATB
CALL Delay
clrf LATB
CALL Delay
GOTO Navestie

Každý program v C musí mít nejméně 1 funkci a ta se musí jmenovat “main”. První řádek v mainu je první řádek uživatelského kódu, který se provede po spuštění procesoru. Všechen kód musí být v nějaké funkci (neplést s makry - makra nejsou výkonný kód, pouze předpis pro preprocesing).

Kód, který se má provést 1x po startu se tedy umísťuje na začátek mainu nebo do funkce, která je na žačátku mainu volána.
Na mcu je prakticky bez výjimky následován nekonečkou smyčkou. V této smyčce je tedy kód periodicky se opakující až do vypnutí napájení. Nemusí v ní být také nic (pokud je program řízen přerušeními).

Dobře, znamená to tedy, že mohu napsat:

main()
{
init() //zde bude inicializace portu apod.
while (1)
… pokracovani kodu hlavniho programu
}

a po restartu program nejprve vykona funkci init(), pak projede hlavni program ve funkci main() az do konce (posledni slozena zavorka), vrati se do mista, kde je podminka while (1) a bude pokracovat znovu v teto nekonecne smycce?

Vl.

Miso:
Omlouvám se za poslední zbytečný dotaz, přehlédl jsem, že jsi pro názornost uvedl i příklad kódu v assembleru - z toho je to naprosto jasné.

Díky, hoši, jste zlatí!!

Vl.

Několik nepřesností.
Po zapnutí se spustí funkce main. Její první příkaz je u tebe volání funkce init(). Ta se tedy provede. Následuje kód za initem, zde while(1). To znamená, že se bude donekonečna provádět 1 příkaz umístěný za while. Jelikož se za příkaz považuje i BLOK (úsek ohraničený složenými závorkami), může být prováděn tento blok. Ty tam ovšem nemáš ani příkaz (musí být ukončen středníkem) ani blok, takže to nepůjde přeložit. Kdybys tam měl blok, došel by program na konec bloku, pak znova vyhodnotil podmínku a provedl blok znovu. Příkaz taky může být prázdný - tedy jen “while(podmínka);” (středník na konci). To se používá třeba při čekání na změnu bitu.
Na konec funkce main by se program NEMĚL NIKDY dostat.

Zvykni si kód nižší úrovně odsazovat (o tabelátor) oproti předcházejícímu. Jinak to bude vypadat jak to napsal miso a nedá se v tom vyznat.
Kód nižší úrovně je obsah bloku (je jedno jesli za podmínkou, cyklem nebo funkcí). Přehledně formátovaný kód by tedy měl vypadat takto:[code]#include <p18f2320.h>
#include <delays.h>

void main()
{
TRISB=0; // první příkaz po spuštění procesoru

while (1) 
{
	// příkazy vykonávané opakovaně do vypnutí mcu
	LATB=0b11111111; 
	Delay10KTCYx(1); 
	LATB=0; 
	Delay10KTCYx(1);
} 

}[/code]

Piityy: jo - před chvílí jsem to zkoušel krokovat a opravdu to opakovalo ten první příkaz dokola … :slight_smile:.

A když už jsme u toho krokování - Misa ve své ukázce používá zpoždění. Já mám Hi-Tech compiler, tam je to podobně, např.: __delay_ms(100). Při krokování se mi to vždy na tomto zpoždění pozastaví na velmi dlouhou dobu, pak se to tedy rozmyslí, že jako že jo - půjdeme dále, ale dost mne to čekání otravuje.
Jde to nějak přeskočit (Step Over není v režimu ICD-2 aktivní) nebo budu muset při krokování udělat z tohoto řádku dočasně poznámku?

V l.

Obávám se, že budeš muset komentovat.
Další možnost je obalení těchto při krokování nežádoucích příkazů příkazy preprocesoru a dočasně si je všechny vypnout (úpravou 1 řádku, není třeba procházet všechen kód), ale nevim, jesli tě s tím otravovat teď v začátku. Radši bych to nechal až to budeš mít trochu v ruce :wink:. Není to nic složitýho, jen to trochu znepřehledňuje kód.

OK, dík.

Vl.

Mám taky otázku, přešel jsem z řady 16F na 18F, ale stále používám POTRB = 0: a ne LATB = 0; jaký je v tom rozdíl.

Zrovna včera jsem se na to díval. Podle mého názoru je zapojení portů obou řad principiálně stejné, jenže řada 18Fxx má programově dostupné buffery (které má samozřejmě i řada 16Fxx - to jsou ty klopné obvody v portu - je to hezky vidět na jejich blokovém schématu v datasheetu), ve kterých jsou data uchována do doby, než jim registry TRIS otevřou cestu ven. Asi to má význam hlavně u výstupního směru, protože může někdy nastat situace, že potřebuješ data zapsat do portu a nemáš přitom nastaven registr TRIS do výstupního režimu. Tak je tam zapíšeš, necháš je tam čekat a později, až je na to vhodná chvíle, nastavíš TRIS. Nějak tak jsem to kdysi četl, sám jsem to nikdy nevyužil.

Pokud se mýlím, tak mne, prosím opravte nebo doplňte, sám bych rád věděl, jaké to má výhody nebo jaký je rozdíl mezi zápisem do těchto bufferů namísto zápisu přímo do portů.

Vl.

Kdysi jsem tady na fóru napsal:

…a pořád si ještě myslím, že to je pravda :smiley:
Ale je to ještě trochu složitější.
Pokud provedu MOVWF PORTA nebo MOVWF LATA, tak je to vždy naprosto totéž. Všechny ostatní operace nad registrem PORTA a LATA se mohou lišit, protože se provádějí systémem RMW.
Zapsat do registru PORTA nebo LATA je možné pochopitelně při libovolném nastavení registru TRISA.
Použít LATA má význam v okamžiku, kdy se chci vyvarovat problémům RMW. Například chci udělat běžící světlo. Pak mohu provádět bitové operace na registru LATA a ty se mě přenášejí na výstup bez vedlejších efektů, které by mohly vzniknout použitím PORTA.
Procesory PIC16F18xx mají registr LATCH také.

jankop:

Přečti si ještě jednou, co jsem napsal. V podstatě totéž, možná trošku jinak a možná méně srozumitelně :slight_smile:.

Takže OK, rozumíme si …

Vl.

Hoši, prosím vás, zkouším pro začátek jednoduchoučké běžící světlo. Chtěl jsem to napsat v C, ale podobně, jako jsem to měl v assembleru:
Využívám toho, že v PIC16Fxx je při rotacích bit CARRY napevno propojen s registrem, který rotuji:

MOVLW 0xFF ;zhasnutí LED diod
MOVWF PORTC
BCF STATUS,C ;nulování CARRY
RLF PORTC ;L z CARRY do PORTC, krajní H z PORTC do CARRY
GOTO $-1

Zkoušel jsem totéž v C:

/Běžící světlo 1
LED diody svítí v L
/

#include <htc.h>
__CONFIG (FOSC_XT & WDTE_OFF & PWRTE_OFF & BOREN_OFF &
LVP_OFF & WRT_OFF & DEBUG_ON & CPD_OFF & CP_OFF);

void init (void)
{
PORTC=0xFF;
TRISC = 0;
CARRY = 0;
PORTC = 0xFF;
#define _XTAL_FREQ 4000000
}

main (void)
{
init();

	while (1)
	{
		PORTC=PORTC<<1;
	__delay_ms(150);
	}	

}

Toto se chová tak, že při prvním průchodu smyčkou se hodnota “L” z CARRY správně přenese do krajního bitu portu C, ale při druhém průchodu a dalších už ne. Dá se to takto dělat a já mám někde chybu nebo je to v něčem jiném?

Díky za ochotu!

Vl.

V C není carry běžně přístupný (není k tomu důvod). Bitový posun 1B proměnné ho nevyužívá. C nemá rotaci, poze posun. Přesun bitu z msb na lsb musíš udělat ručně.[code]prom = 1;
while(1)
{
PORTC = ~prom;
prom <<= 1;
if(!prom) prom = 1; // bit byl vysunut z msb, vložit na lsb
delay();

}[/code]

Jasně, dík …

Kolik takových překvapení mne ještě čeká? :slight_smile:

Vl.

Dobrý den,
zkouším v MPLABu v simulačním režimu základy jazyka C a narazil jsem na následující problém:

Deklaroval jsem proměnné a, b, c.
Myslel jsem si, že po překladu se mi objeví v seznamu uživatelských proměnných v okně Watch MPLABu.
Bohužel se mi tam objeví pouze první z nich, ostatní ne.
Můžete mi, prosím, poradit, kde dělám chybu? Nemohu se přes to dostat.

Uvádím zdrojový text a v příloze screen-copy okna Watch:

#include <htc.h>

main ()
{
int a, b;

a = 0;
while (a < 5)
	{ 
	a = a + 1;
	}
b = a + 2;	

}
ScreenShot.jpg

Zkus je deklarovat jako globální a ne jako lokální.
Jinak myslím že by to mělo vypadat nějak takto:

main () { int a, b; while (1) { a = 0; while (a < 5) { a = a + 1; // můžeš použít a += 1; nebo a++; } b = a + 2; } }

Dík, zítra zkusím a dám vědět …

Vl.

Zdravím všechny,
pro informaci zasílám odkaz na připravovanou publikaci od BENU, která by mohla usnadnit začátky S Cečkem.
Pavel

shop.ben.cz/cz/121332-c-pro-mikr … y-pic.aspx