Table of Contents

Curso básico de C

Estrutura de um programa

Um programa na linguagem C pode apresentar as mais diversas formas, até mesmo ser composto por uma única linha, pois o compilador de código atenta apenas ao cumprimento das regras de sintaxe e não exige a implementação de uma estrutura específica. Contudo, é aconselhável aos programadores, adotarem um estilo de programação que apresente clareza e simplicidade para qualquer tipo de pessoa que leia o código. Esta é a estrutura típica de um programa em linguagem C:

/* Include header files */
#include <avr/io.h>
#include <stdio.h>
 
/* Macro declarations */
#define PI 3.141
 
/* Data type definitions */
typedef struct
{
	int a, b;
}
element;
 
/* Global variables */
element e;
 
/* Functions */
int main(void)
{	
	// Local variables
	int x;
 
	// Program code
	printf("Hello World!\n");
}

Comentários

Ao programador é dada a liberdade de escrever qualquer tipo de texto num ficheiro de código. Este texto é geralmente utilizado para escrever anotações ou descrição do funcionamento de um excerto de código, neste caso este texto é ignorado pelo compilador. Os comentários podem também ser utilizados para por exemplo excluir temporalmente bocados de código da compilação. Os dois tipos de métodos de comentários em linguagem C são:

// Line comment is on one line.
// Text after two slash signs is considered as comment.
 
/*
 Block comment can be used to include more than one line.
 The beginning and the end of a comment is assigned with slash and asterisk signs.
*/

Dados

Tipos de dados

Os tipos básicos de dados do C:

Tipo Valor Minimo Valor Máximo Bits Bytes
(signed) char -128 127 8 1
unsigned char 0 255 8 1
(signed) short -32768 32767 16 2
unsigned short 0 65535 16 2
(signed) long -2147483648 2147483647 32 4
unsigned long 0 4294967295 32 4
float -3.438 3.438 32 4
double -1.7308 1.7308 64 8

A palavra “signed” em parêntesis é considerada opcional, pois por omissão um qualquer tipo de dados numérico é considerado “signed” (bipolar).

Ns microcontroladores AVR int = short
No PC int = long

A linguagem C não tem um tipo de dados que represente uma string (conjunto de carateres). Em vez disso são utilizados vetores de carateres (assunto que será abordado adiante), e são utilizados carateres ASCII onde a cada caracter alfabético é representado um valor numérico.

Variáveis

Um programa pode utilizar um tipo de espações de memória pré-definidos, a isto chamamos variáveis. Os nomes das variáveis podem incluir qualquer caratere do alfabeto latino, números ou underscores, desde que o primeiro caractere não seja um caractere numérico. Quando se declara uma variável num programa, é necessário preceder o nome da variável com o tipo de dados desejado. Por forma a atribuir-se um valor a uma variável é utilizado o sinal de igualdade (=). A seguir apresenta-se um exemplo que ilustra a utilização de variáveis:

// char type variable c declaration
char c;
 
// Value is given to variable c.
c = 65;
c = 'A'; // A has in ASCII character map also value 65
 
// int type variable i20 declaration and initialization
int i20 = 55;
 
// Declaration of several unsigned short type variables
unsigned short x, y, test_variable;

Constantes

As constantes são declaradas da mesma maneira que as variáveis, apenas precedendo a palavra const com o tipo de dados respetivo. Uma constante nunca altera o seu valor durante a execução de um programa. Um exemplo de utilização de constantes:

// int type constant declaration
const int x_factor = 100;

Estruturas

Os tipos básicos de dados podem ser re-agrupados em estruturas por forma a criarem-se tipos de dados mais complexos, recorrendo à palavra struct. Uma estrutura representa um tipo de dados combinado, este tipo é declarado com a palavra typedef. Um exemplo sobre estruturas, sua criação e utilização de tipos de dados complexos:

// Declaration of a new data type "point"
typedef struct
{
	// x and y coordinates and color code
	int x, y;
	char color;
}
point;
 
// declaration of a variable as data type of point
point p;
 
// Assigning values for point variable
p.x = 3;
p.y = 14;

Vetores

Os tipos básicos de dados podem ser organizados em vetores. Os vetores podem ter várias dimensões (tabelas, cubos, etc.). Seguem-se exemplos da utilização de vetores de uma e duas dimensões respetivamente:

// Declaration of one- and two-dimensional arrays
char text[3];
int table[10][10];
 
// Creating a string from char array
text[0] = 'H';  // Char
text[1] = 'i';  // Char
text[2] = 0;    // Text terminator (0 B)
 
// Assigning new value for one element.
table[4][3] = 1;

 

Operadores

As variáveis, constantes e valores retornados de funções podem ser utilizados na composição de operações. O resultado de uma operação pode ser atribuído a uma variável, pode ser utilizado como parâmetro para outras funções ou como valor em diversas estruturas de controlo.

Operadores aritméticos

A linguagem C suporta a generalidade dos operadores aritméticos, como a soma (+), subtração (-), multiplicação (*), divisão (/) e o módulo (%). Seguem-se alguns exemplos ilustrativos da utilização destes operadores:

int x, y;
 
// Modulo, multiplication and assigning value
// x gets value of 9
x = (13 % 5) * 3;
 
// Adding-assigning operator
// x gets value of 14
x += 5;
 
// Quick style method for subtracting 1
// x gets value of 13
x--;

Operadores lógicos

Os operadores lógicos na linguagem C são compostos pela negação NOT (!), multiplicação lógica AND (&&) e a adição lógica (||). Seguem-se exemplos da utilização destes operadores:

bool a, b, c;
 
// Initialization
a = true;
b = false;
 
// Negation
// c will get a value of false because a is true
c = !a;
 
// Logic multiplication
// c will get a value of false because one of the operators is false
c = a && b;
 
// Logic addition
// c will get a value of true because one of the operators is true
c = a || b;

NOTA! O tipo de dados bool na linguagem C, na realidade não existe, pelo que em alternativa geralmente são utilizados valores inteiros para a representação destes valores, em que o valor 0 representa Falso, e qualquer outro valor diferente de 0 representa Verdadeiro. Por exemplo, na biblioteca do HomeLab, o tipo de dados bool é representado pelo tipo de dados unsigned char. A constante Verdadeiro é então representada pelo valor 1 e Falso representado pelo valor 0.

Operadores relacionais e de igualdade

Os valores lógicos são geralmente resultado de uma comparação entre variáveis. Os operadores de igualdade são: (==) igual a, (!=) diferente, (>) maior que, (>=) maior ou igual que, (<) menor que, (⇐) menor ou igual que. Segue-se um exemplo de utilização destes operadores:

int x = 10, y = 1;
 
// greater than operation which is true
// brackets around the operation are only for clarity
bool b = (5 > 4);
 
// Not equal to operation
// The result is false
b = (4 != 4);
 
// Arithmetic, equality and logic operations altogether
// b is false because the first operator of logic multiplication is false
b = (x + 4 > 15) && (y < 4);

Operações de bit

As operações de bit são utilizadas para manipulação de dados em formato binário. Estas apenas podem ser aplicadas a tipos de dados inteiros. As operações de bit são bastante semelhantes às operações lógicas, com a única diferença que porque estas são aplicadas diretamente a cada um dos bits que compõem o valor, e não apenas ao conjunto que compõem o número. As operações de bit que existem na Linguagem C são a inversão (~), conjunção (&), disjunção (|), anti-valência (^), rotação à esquerda («) e a rotação à direita (»).

// Declaration of unsigned 8 bit char type variable
// Variable value is 5 in decimal system, 101 in binary system
unsigned char c = 5;
 
// Disjunction of c with a digit 2 (010 in binary)
// c value will become 7 (111 in binary)
c = c | 2;
 
// Bit left shift by 2
// c value will become 28 (11100 in binary)
c = c << 2;

As operações de bit são essenciais na manipulação de registos de um microcontrolador. Estes registos serão descritos adiante no capítulo de registos do AVR.

 

Funções

As funções definem partes de um programa que pode ser denominadas e invocadas pelo nome. As funções podem incluir parâmetros de entrada e permitem retornar um valor de saída. Se a função não retorna nenhum valor, esta função é definida com o tipo void. Se a função não possui parâmetros de entrada, em versões mais antigas da Linguagem C, o tipo void também deve ser incluído dentro dos parêntesis da sua declaração. Segue-se um exemplo de uma função que adiciona dois números inteiros e outro de uma função que não retorna nenhum valor.

// Declaration of 2 int type parameter function
// The function returns int type value
int sum(int a, int b)
{
	// Addition of 2 variables and returning of their sum
	return a + b;
}
 
// Function without parameters and no return output
void power_off(void)
{
}

Para utilizar-se uma função, esta deve ser invocada. Para isso, é necessário que a função seja declarada antes de ser invocada. Um exemplo com a invocação de uma função:

int x;
int y = 3;
 
// Calling an addition function
// Parameetriteks on muutuja ja konstandi väärtus
// The parameters are variable and constant
x = sum(y, 5);
 
// The call of a power off function
// No parameters
power_off();

A execução de um programa em Linguagem C é sempre iniciada na função main.

 

Declarações

Declarações If/Else

As declarações condicionais, permitem a execução condicional do código de um programa com base em operações lógicas e/ou relacionais. Uma declaração condicional utiliza a palavra if. Exemplo:

// Statement is true and operation x = 5 will be executed
// because 2 + 1 is higher than 2
if ((2 + 1) > 2) x = 5;
 
// If x equals 5 and y equals 3 then the following code will be executed
if ((x == 5) && (y == 3))
{
	// Random action
	y = 4;
	my_function();
}

Um if permite também adicionar uma cláusula else que só é executada em caso do valor Falso. Exemplo:

// Is x equal with 5 ?
if (x == 5)
{
	// Random action
	z = 3;
}
// If this is false then x might be equal with 6
else if (x == 6)
{
	// Random action
	q = 3;
}
// If x was not 5 nor 6 ...
else
{
	// Random action
	y = 0;
}

 

Declaração Switch

Quando é necessário comparar operações e variáveis com várias combinações de diferentes valores, é geralmente recomendável que se use em alternativa uma comparação com base na declaração switch. Exemplo da sua utilização:

int y;
 
// Switch statement for comparing y
switch (y)
{
	// is y equal to 1 ?
	case 1:
		// Random action
		function1();
		break;
 
	// is y equal to 2 ?
	case 2:
		// Random action
		function2();
		break;
 
	// All other cases
	default:
		// Random action
		functionX();
		// break operation not needed,
		// because the comparison ends anyway
}

Loops

Os loops permitem-nos executar código várias vezes.

while loop

O código encapsulado por um while é executado até que a condição entre parêntesis obtenha o valor Falso. Exemplo:

int x = 0;
 
// Loop will execute until x is smaller than 5
while (x < 5)
{
	// x incrementation
	x++;
}

 

for loop

O loop for assemelha-se ao loop while, excepto que as operações que este engloba são executadas antes da condição de e a verificação da condição é sempre efetuada no fim de cada ciclo. Exemplo:

int i, x = 0;
 
// i is equal to 1 at the beginning of the loop.
// Loop will be executed until i is smaller than 5.
// i will be incremented in the end of every loop cycle.
for (i = 0; i < 5; i++)
{
	// x addition by 2
	x += 2;
}
 
// here x value is 10

Saída de loops

A iteração de ciclos while ou for pode ser terminada com a instrução break. Para retomar a próxima iteração de um ciclo evitanto a execução do código que se segue pode ser feita com um continue. Por exemplo:

int x = 0, y = 0;
 
// Infinite loop because 1 is logic true
while (1)
{    
	// Exit the the loop cycle if x becomes 100
	if (x >= 100) break;
 
	// x incrementation to end loop at some time
	x++;
 
	// If x is 10 or less then the next cycle is started
	if (x <= 10) continue;
 
	// y incrementation
	y++;
}
 
// Here y value is 90

Operações de texto

As operações de texto são geralmente utilizadas em microcontroladores para mostrar caracteres e texto num ecrã LCD, por exemplo.

sprintf

A função sprintf é bastante simular à função printf mais comumente utilizada na Linguagem C. A diferença entre estas é que o resultado desta função é carregado numa variável em vez do standard output.

return = sprintf(variable, parameter_text, parameters);

Exemplo:

int r = sprintf(buffer, "%d pluss %d on %d", a, b, a+b);

Esta função carrega a variável passada como primeiro argumento com texto formatado, texto este que é passado como segundo argumento da função, ao qual podem ser atribuídos valores com os seus restantes argumentos. O uso da função sprintf permitirá compôr texto mais complexo de maneira mais simples. Como retorno, esta função devolve o tamanho do texto que foi carregado na variável e no caso de erro, um valor negativo é retornado.

Exemplo:

  sprintf(x, "%d. is first", 1);
  // the same result can be achieved also:
  x = "1. is first";
 
  sprintf(x, "%s is %d years old", "Juku", 10);
  // the same result can be achieved also:
  x = "Juku is 10 years old";

%s e %d neste exemplosão parâmetros que irão ser substituídos pelos valores dos dois últimos parâmetros da função, respetivamente. O número de parâmetros deve igualar ao número de variáveis utilizadas na expressão. No primeiro exemplo, o parâmetro %d toma o valor 1. No segundo exemplo, os parâmetros %s e %d são substituídos pelos valores das variáveis, “Juku” e 10 respetivamente. Esta ordem de substituição dos parâmetros é respetiva à ordem em que se apresentam os valores. Existem vários descrições para os diferentes tipos de dados:

Parameter Description Example
%c Char a
%i or %d Integer 123
%f Real number 3,14
%s Text example
%X Hexadecimal number 3F
#include <stdio.h>
 
int main ()
{
  char buffer [50];
  int n, a=5, b=3;
  n=sprintf (buffer, "%d plus %d is %d", a, b, a+b);
  printf ("\"%s\" is %d digits long\n",buffer,n);
  return 0;
}

Funções genéricas

A biblioteca standard da Linguagem C (stdlib.h) inclui funções que permitem a simplificação de várias operações e conversões mais comuns

Função aleatória

Não é fácil gerar um número aleatório num microcontrolador.

Primeiro, é necessário dar como parâmetro de entrada um gerador que servirá como base para a geração do conjunto de números aleatórios. Este conjunto, se for sempre baseado no mesmo número gerador, será sempre igual. Por forma a tornar o resultado desta função mais aleatório, geralmente utiliza-se como valor gerador, um dos valores recolhidos de um dos pinos do ADC do microcontrolador.

Exemplo:

srand(100);
rand();

Exemplo de geração de um número aleatório na gama de 1-16:

#include <stdlib.h>
 
int x;
x=rand() % 16;

Para uma descrição mais detalhada de todas as funções incluídas na Linguagem C:

http://www.cplusplus.com/reference/clibrary/