• Revista PROGRAMAR: Já está disponível a edição #53 da revista programar. Faz já o download aqui!

RJSC

C embedded - PIC - Variaveis Globais, Flags, Interrupts...

4 mensagens neste tópico

Ora bem, sendo um "Hardware Guy" venho aqui fazer umas perguntas aos "Software Guys".

Supostamente é má prática usar variáveis globais. Mas...

Quando se precisa duma FLAG para nos facilitar a vida o que é que se deve fazer?

As flags só funcionam como variáveis globais mas depois a reutilização de código fica complicada  ;)

Sugestões? Passar flags como parâmetros ás funções?

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Se a utilização de uma variável global for conveniente e facilitar o código, então na minha opinião não é uma má prática.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Pois é, por vezes é mesmo necessário usar variáveis globais.... Deve é ter-se em mente que não é boa prática, e isto porque, se perde a noção de "como é que as coisas acontecem", por exemplo:

void main(void)
{
int a, b;

a = 3;
b = 5;
soma (a, b);
mostra_soma();
}
 

neste caso teria que ser usada uma variável global para que os dados passem de dentro da função soma para dentro da função mostra_soma :

int result;

void soma (int par1, int par2)
{
result = par1 + par2;
}

void mostra_soma (void)
{
printf("resultado = %d\n", result);
}
 

este pequeno programa fica muito melhor se não se usarem variáveis globais, e se o dados forem passados por parâmetros:

void soma (int par1, int par2)
{
return par1 + par2;
}

void mostra_soma (int reslut)
{
printf("resultado = %d\n", result);
}

void main(void)
{
int a, b, c;

a = 3;
b = 5;
c = soma (a, b);
mostra_soma(c);
}
 

Neste último caso, percebe-se perfeitamente que na primeira função é calculado o valor que é mostrado pela segunda função!

Quando se programa em "baixo nível" as coisas são diferentes, e por vezes, é necessário perder alguma dessa legibilidade do código de forma a melhorar, por exemplo, a velocidade de processamento ou a ocupação da memória de dados.

Outros exemplos do não cumprimento desta regra são a utilização de flags (como é dito do título deste post) e buffers.

A utilização destas variáveis globais explica-se facilmente por ser o resultado do processamento de funções especais (as funções de interrupção). Estas funções são especais, porque não são chamadas pelo "fluxo" do programa. Por exemplo uma função que atenda a interrupção de uma porta série tem que ser executada imediatamente após se verificar a recepção de um byte por essa porta série. Como o processamento desta informação tem que ser rápido (antes que chegue mais dados que apaguem os primeiros) quem chama esta função é o próprio Hardware. O programa tem que agir rapidamente, deve guardar o byte recebido num buffer e deve levantar uma flag. Quando a execução da interrupção termina, o programa volta ao seu "fluxo" normal, e este deve ter informação de que foi recebido um byte (flag activa) e ainda qual foi este byte (é aquele que está guardado no buffer).

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

"embedded" é uma palavra que ainda abrange bastantes sistemas, desde máquinas com 1K a máquinas com 256MB ou mais. Em máquinas pequeninas às vezes temos mesmo que "fazer truques"... mas ainda assim há sempre formas de tornar o código legível ou melhor organizado. No caso das flags, podes por exemplo ter à mesma uma variável global mas depois usar funções para aceder à variável, e assim "isolar" num pequeno bloco todo o código que acede à variável global. A penalidade de tempo de execução por utilização destas funções quando comparado com acesso directo à variável é na verdade ZERO, desde que uses macros ou funções inline para as implementar. Não perdes nada em eficiência nem espaço e manténs a organização.

Convém conhecer bem o compilador e algumas funcionalidades mais "recondidas" do C como o inline.

Deixo-te um exemplo de uma nano-biblioteca (e não libraria ou livraria como já vi por aí) para lidar com flags:

static int  gFlags = 0;

inline void flag_set (int flagid)
{
    gFlags |= flagid;
}

inline void flag_reset (int flagid)
{
    gFlags &= ~flagid;
}

inline int flag_isset (int flagid)
{
    return (gFlags & flagid) != 0;
}

A variável global é declarada e usada apenas neste bloco, e o resto do código utiliza apenas as funções, não lhe interessando sequer saber como é que as flags são implementadas.

Tal como está acima é para ser usado no mesmo ficheiro .c. Para poderem ser usadas (partilhadas) em vários ficheiros é necessário ter as funções e um protótipo da variável global num .h e a declaração e inicialização da variável num .c (num ficheiro que pode ser usado exclusivamente para esta nano-biblioteca). O static tem que ser removido.

Um aspecto importante na legibilidade do código é teres regras para os nomes dos objectos do programa. Uma das regras que uso é que todas as variáveis globais (e apenas estas) são prefixadas com "g". Ao olhar para um pedaço de código sei logo se uma certa variável é global ou outra coisa. Mais abaixo vais ver que prefixo sempre uma constante enumerada com "e".

E já agora, o código acima é re-utilizável!

Para o código ser rápido, o "flagid" já é uma máscara de bits. Podes declarar as tuas flags por exemplo usando um enumerado, o que torna a sua utilização bastante legível:

enum {
    eSerialWait  = 1 << 0,
    eSerialRx    = 1 << 1,
    eSerialTx    = 1 << 2,
} SerialFlags_e;
...

flag_set(eSerialWait);
...

if (flag_isset(eSerialTx))
{
    ...

Resumindo, usar variáveis globais não implica perda de estrutura, abstracção nem legibilidade. É uma questão de escolher uma implementação adequada.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Crie uma conta ou ligue-se para comentar

Só membros podem comentar

Criar nova conta

Registe para ter uma conta na nossa comunidade. É fácil!


Registar nova conta

Entra

Já tem conta? Inicie sessão aqui.


Entrar Agora