Pro ostřílené Cčkaře asi nebudu psát nic nového, ale jsou mezi námi i takoví, kteří s Cčkem teprve začínají a těm by tohle povídání mohlo usnadnit práci. Při použití timeru nebo přerušení, případně i při jiných činnostech prováděných mikrokontrolerem je někdy šikovné používat flagy. Vzhledem k tomu, že flagy (neboli příznaky) jsou jednobitové informace, může být použití celého bytu pro jeden příznak celkem plýtvání RAMkou. Obzvlášť u menších procesorů. Ale konec s teorií, ukažme si, jak na ně.
První možnost je tato :
unsigned char ProgramStatus;
#define Bit0 0
#define Bit1 1
#define Bit2 2
#define Bit3 3
#define Bit4 4
#define Bit5 5
#define Bit6 6
#define Bit7 7
Použití je pak následující :
ProgramStatus |= (1<<Bit6); // Nastavení bitu 6
ProgramStatus &= ~(1<<Bit4); // Vynulování bitu 4
if ((ProgramStatus & (1<<Bit6)) != 0) ; // Pokud bit 6 = 1
if ((ProgramStatus & (1<<Bit6)) == (1<<Bit6)) ; // Pokud bit 6 = 1
if ((ProgramStatus & (1<<Bit2)) == 0) ; // Pokud bit 2 = 0
Jak vidíte, použití je trošku komplikované, ale naštěstí se dá celkem výrazně zjednodušit.
Bohužel se tento způsob nedá použít pro PORTx, DDRx apod. - tedy alespoň ne přímo (viz. dále) - tuto informaci beru zpět - viz následující příspěvek od Radiuse.
struct
{
unsigned char Bit0 : 1;
unsigned char Bit1 : 1;
unsigned char Bit2 : 1;
unsigned char Bit3 : 1;
unsigned char Bit4 : 1;
unsigned char Bit5 : 1;
unsigned char Bit6 : 1;
unsigned char Bit7 : 1;
}ProgramStatus;
nebo varianta s definicí typu:
typedef struct
{
unsigned char Bit0 : 1;
unsigned char Bit1 : 1;
unsigned char Bit2 : 1;
unsigned char Bit3 : 1;
unsigned char Bit4 : 1;
unsigned char Bit5 : 1;
unsigned char Bit6 : 1;
unsigned char Bit7 : 1;
}ProgramStatusTyp; // 1 byte
typedef struct
{
unsigned char Bit0 : 1;
unsigned char Bit1 : 1;
unsigned char Bit2 : 1;
unsigned char Bit3 : 1;
unsigned char Bit4 : 1;
unsigned char Bit5 : 1;
unsigned char Bit6 : 1;
unsigned char Bit7 : 1;
unsigned char Bit8 : 1;
unsigned char Bit9 : 1;
unsigned char Bit10 : 1;
}ProgramStatusTyp; // 2 byty
ProgramStatusTyp ProgramStatus;
Takto nadefinované flagy se pak používají takto :
ProgramStatus.Bit6 = 1; // Nastavení bitu 6
ProgramStatus.Bit4 = 0; // Vynulování bitu 4
if (ProgramStatus.Bit6 == 1) ; // Pokud bit 6 = 1
if (ProgramStatus.Bit2 == 0) ; // Pokud bit 2 = 0
Výsledný překlad je pro unsigned char i použití struktury shodný.
Výhodou je, že program pracuje pouze s tím bytem, ve kterém se nalézá bit, se kterým se pracuje.
Nulování celého statusu najednou se dělá následnovně :
memset((void*)&ProgramStatus, 0, sizeof(ProgramStatus));
Tohle byl nejčistší způsob a funguje na jakékoliv množství flagů ve statusu. Překladač totiž alokuje pro status proměnou tolik bytů, aby se vešly všechny bity. Lze jej však vynulovat i takto :
*(unsigned char *)&BitStatus = 0; // pro 1 byte
*(unsigned int *)&BitStatus = 0; // pro 2 byty
Nevýhodou je, že je potřeba znát, kolik bytů status vlastně zabírá (i když nepředpokládám, že by programátor potřeboval více, než 2 byty).
Status se dá i vyslat na nějaký port. Nevýhodou je ale to, že na port se vyšle vždy jenom prvních 8/16 bitů.
PORTA = *(unsigned char *)&BitStatus;
TCNT1 = *(unsigned int *)&BitStatus;
Použití flagů jako pojmenování LED nebo signálů na portu je tedy možné, jen je to naopak vyváženo trošku menším komfortem zápisu.
Lze nadefinovat ale i čítač (v tomto případě 2-bitový) :
typedef struct
{
unsigned char Bit0 : 1;
unsigned char Bit1 : 1;
unsigned char Bit2 : 1;
unsigned char Bit3 : 1;
unsigned char Bit4 : 1;
unsigned char Bit5 : 1;
unsigned char Citac : 2;
}ProgramStatusTyp; // 1 byte
ProgramStatusTyp ProgramStatus;
Když by se pak provádělo :
ProgramStatus.Citac++; // čítá 0-1-2-3-0-1-...
V případě zápisu :
ProgramStatus.Citac = 5; // nepřeteče, ale zapíše jen hodnotu%4.
V případě definování vícebitových částí ale doporučuju je umístit tak, aby celá vícebitová proměnná byla obsažena jen v jednom bytu, ideálně od bitu 0.
Veškeré zde uvedené střípky kódu jsou vyzkoušené na překlad v AVR Studiu 4.19 + WinAVR a zkontrolované pomocí disassembleru.