19.
Funções
19.1.
Forma Geral
As funções em linguagem C podem ser encaradas como algo similar as sub-rotinas em
programas escritos em Assembly, guardadas as suas devidas proporções. Sua forma geral,
descrita pelo padrão ANSI C, é mostrada a seguir:
{tipo da função} nome_da_função ({parâmetros})
{
comandoA;
comandoB;
....
}
Onde:
• Tipo da função: especifica o tipo de dado (int, char, float, doble, etc.) que a função irá
devolver para o local de onde ela foi chamada.
• Nome_da_função: identificador a ser utilizado para referenciar aquela função. Ela
passará a ser reconhecida pelo resto do programa por este nome.
• Parâmetros: são utilizados para a passagem de valores para que a função possa
utilizá-los e efetuar os procedimentos para o qual foi escrita.
Veja um exemplo de funções no programa (EXEMPLO-14):
//******************************************************************************
//
Exemplo de funções
//
//
Alessandro Ferreira da Cunha
//
Tech Training - Engenharia e Treinamentos
//
Janeiro 2009
//
Extraído do Livro "PIC - Programação em C", do Fábio Pereira
//
Editora Érica - Página 139
//
Built with IAR Embedded Workbench Version: 4.11.2.9
//******************************************************************************
#include <msp430xG46x.h>
#include <stdio.h>
int soma ( int a , int b)
{
return a + b;
}
main ( )
{
printf("1 + 1 = %d \n", soma (1,1));
}
Página 95
19.1.1.
Funções do tipo void
Um dos usos de void é declarar explicitamente funções que não devolvem valores. Isso evita
seu uso em expressões e ajuda a afastar um mau uso acidental. Antes de poder usar
qualquer função como void, você deve declarar seu protótipo. Se isto não for feito, o C
assumirá que ela devolve um valor inteiro e, quando o compilador encontrar de fato a
função, ela declarará um erro de incompatibilidade.
19.1.2.
O comando return
O comando return tem dois importantes usos. Primeiro, ele provoca uma saída imediata da
função que o contém. Isto é, faz com que a execução do programa retorne ao código que o
chamou. Se o comando return for executado na função main, então o programa será
encerrado.
Segundo, ele pode ser utilizado para devolver um valor a função que o chamou.
O primeiro programa (EXEMPLO-1) dá uma demonstração de como as funções podem
trocar valores entre si:
//******************************************************************************
//
Exemplo de variáveis globais e locais
//
Built with IAR Embedded Workbench Version: 4.11.2.9
//******************************************************************************
#include <msp430xG46x.h>
#include <stdio.h>
int somatorio; // VARIÁVEL GLOBAL! SERÁ ACESSADA POR TODAS AS FUNÇÕES
void soma (int valor) //AO ACESSAR ESTA FUNÇÃO, O PARÂMETRO VALOR RECEBE DADOS DE QUEM O CHAMOU
{
int conta; // VARIÁVEL LOCAL! SERÁ ACESSADA APENAS PELA FUNÇÃO SOMA
somatorio = somatorio + valor;
printf("0");
for (conta = 1;(conta<(valor+1));conta++)
{
printf("+%u",conta);
}
printf(" = %u\r\n",somatorio);
}
void main()
{
WDTCTL = WDTPW+WDTHOLD;
// Stop WDT
int conta; // VARIÁVEL LOCAL! SERÁ ACESSADA APENAS PELA FUNÇÃO MAIN
somatorio = 0; // A VARIÁVEL GLOBAL É INICIALIZADA
for (conta=1;conta<20;conta++)
{
soma(conta); // É CHAMADA A FUNÇÃO SOMA, ONDE É PASSADO O VALOR DE CONTA
}
}
Página 96
19.2.
Passagem de parâmetros
19.2.1.
Fixo (por valor)
Exatamente como vem sendo executado até agora nos programas exemplos.
19.2.2.
Variável (por referência)
Utilizando ponteiros. Veja o exemplo abaixo (EXEMPLO-15):
//******************************************************************************
//
Exemplo de funções
//
//
Alessandro Ferreira da Cunha
//
Tech Training - Engenharia e Treinamentos
//
Janeiro 2009
//
Extraído do Livro "PIC - Programação em C", do Fábio Pereira
//
Editora Érica - Página 141
//
Built with IAR Embedded Workbench Version: 4.11.2.9
//******************************************************************************
#include <msp430xG46x.h>
#include <stdio.h>
float divide ( float a , float b)
{
if (!b) return 0;
a /= b;
return a;
}
main ( )
{
float a , b , c ;
a = 7 ;
b = 3 ;
c = divide ( a , b );
printf("a = %f , b = %f , c = %f \n", a , b , c);
}
19.3.
Protótipos de função
Em programas profissionais, muito extensos, com diversas funções, pode acontecer de
determinada função ser chamada e ainda não ter sido definida. Isto irá gerar um erro no
compilador.
Para evitar que isto ocorra, utiliza-se o protótipo da função, que será uma declaração prévia
com o intuito de informar ao compilador que mais adiante no programa esta função será
definida.
Para isto basta colocar a primeira linha da função, que contem os dados do tipo da função,
do nome da função e os parâmetros da função, como pode ser visto abaixo:
{tipo da função} nome_da_função ({parâmetros})
Página 97
20.
Tipos de dados avançados
20.1.
Ponteiros
Ponteiros são um dos recursos mais poderosos da linguagem C. Qualquer programa de
utilidade prática escrito em C dificilmente dispensará o uso de ponteiros. A tentativa de evitálos implicará quase sempre códigos maiores e de execução mais lenta.
Para quem está começando, pode parecer (e algumas vezes é) um tanto difícil. Mas não há
outro caminho senão enfrentar a realidade. São muitas as aplicações de ponteiros. A seguir,
relação das mais comuns.
•
•
•
•
•
•
Acessar endereços de memória que o programa aloca em tempo de execução.
Acessar variáveis que não são visíveis em uma função.
Manipulação de arrays.
Manipulação de strings.
Passar o endereço de uma função para outra.
Retornar mais de um valor para uma função.
Genericamente, um ponteiro é uma variável utilizada para guardar o endereço de outra
variável, ou seja, um ponteiro é um apontador de outra variável. Veja um exemplo de
ponteiros no programa (EXEMPLO-16):
//******************************************************************************
//
Exemplo de ponteiro
//
//
Alessandro Ferreira da Cunha
//
Tech Training - Engenharia e Treinamentos
//
Janeiro 2009
//
Built with IAR Embedded Workbench Version: 4.11.2.9
//******************************************************************************
#include <msp430xG46x.h>
#include <stdio.h>
int main()
{
char letra = 's';
int idade = 35;
char nome[10] = "samuel";
float peso = 87.8;
float altura = 1.82;
printf("Exibindo o
printf("O valor da
printf("O valor da
printf("O valor da
printf("O valor da
printf("O valor da
endereço
variável
variável
variável
variável
variável
de memória de variáveis\n\n");
letra é %c e seu endereço é %x\n",letra,&letra);
idade é %d e seu endereço é %x\n",idade,&idade);
nome é %s e seu endereço é %x\n",nome,&nome);
peso é %2.1f e seu endereço é %x\n",peso,&peso);
altura é %1.2f e seu endereço é %x\n",altura,&altura);
}
Para entender exatamente como os ponteiros podem ser utilizados, execute os programas
exemplos que vão de (EXEMPLO-18) a (EXEMPLO-23).
Página 98
(EXEMPLO-18)
//******************************************************************************
//
Exemplo de ponteiro
//
//
Alessandro Ferreira da Cunha
//
Tech Training - Engenharia e Treinamentos
//
Janeiro 2009
//
Built with IAR Embedded Workbench Version: 4.11.2.9
//******************************************************************************
#include <msp430xG46x.h>
#include <stdio.h>
/* atribuindo valor a uma variável ponteiro */
int main()
{
int idade = 35;
int *ptr_idade;
ptr_idade = &idade; /* foi atribuído o endereço da variável
idade a variável ponteiro ptr_idade.
Observe o uso do operador & que devolve
o endereço de memória da variável
idade. */
printf("O valor da variável idade é %d\n",idade);
printf("O endereço da variável idade é %x\n",&idade);
printf("O valor da variável ponteiro ptr_idade é %x\n",ptr_idade);
return(0);
}
(EXEMPLO-19)
//******************************************************************************
//
Exemplo de ponteiro
//
//
Alessandro Ferreira da Cunha
//
Tech Training - Engenharia e Treinamentos
//
Janeiro 2009
//
Built with IAR Embedded Workbench Version: 4.11.2.9
//******************************************************************************
#include <msp430xG46x.h>
#include <stdio.h>
/* atribuindo valor a uma variável ponteiro */
int main()
{
int idade = 35;
int *ptr_idade;
ptr_idade = &idade; /* foi atribuído o endereço da variável
idade a variável ponteiro ptr_idade.
Observe o uso do operador & que devolve
o endereço de memória da variável
idade. */
printf("O valor da variável idade é %d\n",idade);
printf("O endereço da variável idade é %x\n",&idade);
printf("O valor da variável ponteiro ptr_idade é %x\n",ptr_idade);
printf("O valor apontado por ptr_idade é %d\n",*ptr_idade);
/* observe, na linha acima, o uso do operador de indireção ( * )
* para desreferenciar o ponteiro ptr_idade e, assim, exibir
* o valor armazenado no endereço de memória apontado por ele. */
return(0);
}
Página 99
(EXEMPLO-20)
//******************************************************************************
//
Exemplo de ponteiro
//
Alessandro Ferreira da Cunha
//
Tech Training - Engenharia e Treinamentos
//
Janeiro 2009
//
Built with IAR Embedded Workbench Version: 4.11.2.9
//******************************************************************************
#include <msp430xG46x.h>
#include <stdio.h>
/* Alterando o valor armazenado no endereço apontado
* por um ponteiro
*/
int main()
{
int numero = 35;
int *ptr;
ptr = &numero; /* atribuindo o endereço de numero a ptr */
printf("O ponteiro ptr armazena o endereço %x que,\npor sua vez,\
armazena o valor %d\n",ptr,*ptr);
*ptr = 25; /* alterando o valor armazenado no endereço
* apontado por ptr. Observe que o ponteiro
* deve ser desreferenciado.*/
printf("\nAgora o ponteiro ptr armazena o endereço %x que,\npor sua vez,\
armazena o valor %d\n",ptr,*ptr);
return(0);
}
(EXEMPLO-21)
//******************************************************************************
//
Exemplo de ponteiro
//
Alessandro Ferreira da Cunha
//
Tech Training - Engenharia e Treinamentos
//
Janeiro 2009
//
Built with IAR Embedded Workbench Version: 4.11.2.9
//******************************************************************************
#include <msp430xG46x.h>
#include <stdio.h>
/* ponteiros como parâmteros de função */
/* a função que recebe como argumento o valor
* da variável não consegue alterar o valor
* deste */
int valor(int a)
{
a = 35;/* alterando o valor do argumento passado */
}
/* a função que recebe como argumento um ponteiro
* consegue alterar o valor apontado por este*/
int ponteiro(int *a)
{
*a = 35; /* alterando o valor do argumento passado */
}
int main()
{
int nr = 26;
int *ptr_nr;
printf("O valor inicial de nr é %d\n",nr);
valor(nr); /* função que recebe o valor. Não consegue alterar este */
printf("Valor de nr após a chamada da função valor = %d\n",nr);
ptr_nr = &nr;
ponteiro(ptr_nr); /* função que recebe ponteiro. Consegue alterar valor
* apontado*/
printf("Valor de nr após a chamada da função ponteiro = %d\n",nr);
return(0);
}
Página 100
(EXEMPLO-22)
//******************************************************************************
//
Exemplo de ponteiro
//
//
Alessandro Ferreira da Cunha
//
Tech Training - Engenharia e Treinamentos
//
Janeiro 2009
//
Built with IAR Embedded Workbench Version: 4.11.2.9
//******************************************************************************
#include <msp430xG46x.h>
#include <stdio.h>
/* visualizando como funciona a aritmética de ponteiros */
int main()
{
char letra[6] = {'a','e','i','o','u','\0'};
int contador, nr[5] = {30,12,67,13,41};
char *ptr_letra;
int *ptr_nr;
ptr_letra = letra;
ptr_nr = nr;
printf("Visualizando como funciona a aritmética de ponteiros\n");
printf("\nmatriz letra = a, e, i, o, u\n");
printf("matriz nr = 30,12,67,13,41\n");
printf("\nVerificando o tamanho dos tipos de dados\n");
printf("tamanho do tipo de dado char
= %d\n",sizeof(char));
printf("tamanho do tipo de dado int
= %d\n",sizeof(int));
printf("\nPonteiro para letra aponta para %c no endereço %x\n",*ptr_letra,ptr_letra);
printf("Ponteiro para nr aponta para %d no endereço %x\n",*ptr_nr,ptr_nr);
printf("\nIncrementando os ponteiros\n");
printf("ptr_letra + 3, ptr_nr + 2\n");
ptr_letra += 3;
ptr_nr += 2;
printf("\nPonteiro para letra agora aponta para %c no endereço %x\n",*ptr_letra,ptr_letra);
printf("Ponteiro para nr agora aponta para %d no endereço %x\n",*ptr_nr,ptr_nr);
return(0);
}
Página 101
(EXEMPLO-23)
//******************************************************************************
//
Exemplo de ponteiro
//
//
Alessandro Ferreira da Cunha
//
Tech Training - Engenharia e Treinamentos
//
Janeiro 2009
//
Built with IAR Embedded Workbench Version: 4.11.2.9
//******************************************************************************
#include <msp430xG46x.h>
#include <stdio.h>
/* percorrendo uma matriz de strings com um ponteiro */
int main()
{
char *dia[] = {"Domingo","Segunda","Terça","Quarta","Quinta","Sexta","Sábado",0};
char **ptr_dia;
/* *dia é um ponteiro para uma string e
* **ptr_dia é um ponteiro para um ponteiro para uma string
*/
ptr_dia = dia; /* apontando ptr_dia para o início da matriz dia */
while(*ptr_dia)
{
printf("%s\n",*ptr_dia);
ptr_dia++;
}
return(0);
}
/* Quando você declara uma matriz de strings o compilador
* não acrescenta um caractere NULL para indicar o final
* da matriz como o faz com uma matriz de caracteres (strings).
* Por isso você mesmo tem que inserir o caractere NULL para
* indicar o final da matriz.
*
* Foi isso que foi feito ao inserir 0 no final da matriz dia
*/
20.2.
Matrizes de dados
Uma matriz é uma estrutura de dados que pode armazenar vários valores do mesmo tipo.
A sintaxe para declarar uma matriz é:
TIPO nome_da_matriz [QUANTIDADE];
Onde:
• TIPO: é o tipo dos dados que serão armazenados na matriz. Todos os dados
colocados na matriz devem ser deste tipo.
• NOME: é o nome a ser dado a matriz. Este nome identificará a matriz no código do
programa.
• QUANTIDADE: é a quantidade máxima de itens a ser armazenados.
Página 102
Exemplo:
int nr_de_livros [50];
//esta matriz pode armazenar até 50 valores do tipo int
float nota [30];
//esta matriz pode armazenar até 30 valores do tipo float
Os valores armazenados na matriz são chamados de "elementos da matriz". O primeiro
elemento da matriz é indexado como item zero e o último é indexado como QUANTIDADE
menos 1. Assim, para nossa matriz nota, mostrada no exemplo acima, o primeiro elemento é
nota[0] e o último elemento é nota[29]. Você pode inicializar os elementos de uma matriz na
sua declaração usando a sintaxe:
int notas [5] = {60,70,35,50,68};
// No exemplo acima o elemento zero da matriz notas
receberá o valor 60, o elemento 1 receberá o valor 70, e
assim por diante. Para melhorar o entendimento observe o
código do exemplo (EXEMPLO-24). Esta matriz pode armazenar
até 50 valores do tipo int
//******************************************************************************
//
Exemplo de matriz
//
//
Alessandro Ferreira da Cunha
//
Tech Training - Engenharia e Treinamentos
//
Janeiro 2009
//
Built with IAR Embedded Workbench Version: 4.11.2.9
//******************************************************************************
#include <msp430xG46x.h>
#include <stdio.h>
int main()
{
int notas[5] = {60,70,35,50,68};
printf("Analisando os elementos da matriz notas\n");
printf("O primeiro elemento tem o valor %d\n",notas[0]);
printf("O segundo elemento tem o valor %d\n",notas[1]);
printf("O terceiro elemento tem o valor %d\n",notas[2]);
printf("O quarto elemento tem o valor %d\n",notas[3]);
printf("O quinto e último elemento tem o valor %d\n",notas[4]);
return(0);
}
Página 103
Praticamente o mesmo efeito obtido pelo exemplo anterior pode ser obtido de modo muito
mais eficiente pelo próximo programa (EXEMPLO-25):
//******************************************************************************
//
Exemplo de matriz
//
//
Alessandro Ferreira da Cunha
//
Tech Training - Engenharia e Treinamentos
//
Janeiro 2009
//
Built with IAR Embedded Workbench Version: 4.11.2.9
//******************************************************************************
#include <msp430xG46x.h>
#include <stdio.h>
int main()
{
int notas[5] = {60,70,35,50,68};
int contador;
printf("Analisando os elementos da matriz notas\n");
for(contador = 0;contador < 5;contador++)
printf("O %do elemento tem o valor %d\n",contador+1,notas[contador]);
return(0);
}
Para entender exatamente como as matrizes podem ser utilizadas, execute os programas
(EXEMPLO-26) e (EXEMPLO-27).
(EXEMPLO-26)
//******************************************************************************
//
Exemplo de matriz
//
//
Alessandro Ferreira da Cunha
//
Tech Training - Engenharia e Treinamentos
//
Janeiro 2009
//
Built with IAR Embedded Workbench Version: 4.11.2.9
//******************************************************************************
#include <msp430xG46x.h>
#include <stdio.h>
/* visualizando strings como matrizes de caracteres */
int main()
{
char palavra[7] = "matriz";
int contador;
printf("Em C strings são matrizes de caracteres e podem ser manipuladas como tal.\n");
printf("\nA string é %s\n",palavra);
printf("\nExibindo cada elemento da matriz palavra\n");
for(contador = 0;contador < 7;contador++)
printf("%c\n",palavra[contador]);
return(0);
}
Página 104
(EXEMPLO-27)
//******************************************************************************
//
Exemplo de matriz
//
//
Alessandro Ferreira da Cunha
//
Tech Training - Engenharia e Treinamentos
//
Janeiro 2009
//
Built with IAR Embedded Workbench Version: 4.11.2.9
//******************************************************************************
#include <msp430xG46x.h>
#include <stdio.h>
void exibe(int matriz[],int elementos)
{
int contador;
for(contador = 0;contador < elementos;contador++)
printf("O %do elemento tem o valor %d\n",contador+1,matriz[contador]);
}
int main()
{
int notas[5] = {60,70,35,50,68};
int contador;
printf("Analisando os elementos da matriz notas\n");
exibe(notas,5);
return(0);
}
20.2.1.
Matrizes bi-dimensionais
Imagine uma matriz bidimensional como uma tabela de linhas e colunas. Por exemplo, a
matriz:
pesos [3][5];
Ela pode ser imaginada como o seguinte arranjo de linhas e colunas, formando células:
Observe que o primeiro índice ([3]) indica as linhas da matriz e o segundo ([5]) indica as
colunas. Como sabemos que [3] varia de zero a 2 e [5] varia de zero a 4, fica fácil determinar
os índices de cada posição da matriz:
0,0
0,1
0,2
0,3
0,4
1,0
1,1
1,2
1,3
1,4
2,0
2,1
2,2
2,3
2,4
Página 105
Visto a posição de cada índice vamos preencher nossa matriz pesos com valores:
10
30
45
70
36
86
44
63
82
80
70
61
52
63
74
De tudo que foi exposto acima podemos entender que:
pesos
pesos
pesos
pesos
[1][3]
[0][4]
[0][0]
[2][4]
=
=
=
=
82;
36;
10;
74;
Para preencher nossa matriz com os valores mostrados na tabela acima podemos usar uma
declaração como:
int pesos [3][5] =
{{10,30,45,70,36},{86,44,63,82,80},{70,61,52,63,74}};
Podemos manipular os elementos de nossa matriz bidimensional usando duas variáveis e
um laço for da mesma maneira que fizemos com as matrizes comuns. Observe o código do
programa (EXEMPLO-28):
//******************************************************************************
//
Exemplo de matriz
//
//
Alessandro Ferreira da Cunha
//
Tech Training - Engenharia e Treinamentos
//
Janeiro 2009
//
Built with IAR Embedded Workbench Version: 4.11.2.9
//******************************************************************************
#include <msp430xG46x.h>
#include <stdio.h>
/* manipulando uma matriz bidimensional */
int main()
{
int pesos[3][5] = {{10,30,45,70,36},
{86,44,63,82,80},
{70,61,52,63,74}};
int linha,coluna;
for(linha = 0;linha < 3;linha++)
for(coluna = 0;coluna < 5; coluna++)
printf("elemento[%d][%d] = %d\n",linha,coluna,pesos[linha][coluna]);
return(0);
}
Página 106
20.2.2.
Passando uma matriz bi-dimensional para uma função
Uma função que manipula uma matriz bidimensional deve receber a matriz e o número de
linhas desta matriz. O número de colunas da matriz também deve estar especificado nesta
declaração. Ao chamar a função, deve-se passar a matriz e o número de linhas. Como isto
funciona? Veja o programa (EXEMPLO-29):
//******************************************************************************
//
Exemplo de matriz
//
//
Alessandro Ferreira da Cunha
//
Tech Training - Engenharia e Treinamentos
//
Janeiro 2009
//
Built with IAR Embedded Workbench Version: 4.11.2.9
//******************************************************************************
#include <msp430xG46x.h>
#include <stdio.h>
/* manipulando uma matriz bidimensional */
void exibe(int matriz[][5], int linhas)
{
int linha,coluna;
for(linha = 0;linha < 3;linha++)
for(coluna = 0;coluna < 5; coluna++)
printf("elemento[%d][%d] = %d\n",linha,coluna,matriz[linha][coluna]);
}
int main()
{
int pesos[3][5] = {{10,30,45,70,36},
{86,44,63,82,80},
{70,61,52,63,74}};
exibe(pesos,3);
return(0);
}
20.3.
Estruturas de dados
As estruturas são utilizadas para agrupar informações relacionadas de tipos de dados
diferentes. Digamos que você precisa controlar os seguintes dados relacionados ao estoque
de um pequeno estabelecimento comercial:
•
•
•
•
•
•
•
código
nome do produto
quantidade estocada
valor de compra
valor a ser vendido
lucro
observacões sobre o produto
Página 107
Este seria um caso para o uso de estruturas, pois relacionados a cada produto teremos
dados do tipo int(código,quantidade), char(nome, observações) e float(valor de compra, valor
de venda, lucro).
A sintaxe para a declaração (ou criação) de uma estrutura é:
struct nome_da_estrutura
{
tipo campo1;
tipo campo1;
....
tipo campoN;
}
Para o caso exemplificado no item anterior poderíamos ter algo como:
struct produto
{
int codigo;
char nome[50];
int quantidade;
float valor_compra;
float valor_venda;
float lucro;
char obs[200];
}
É importante observar que a declaração da estrutura não cria, ainda, uma variável. A
declaração da estrutura apenas cria um novo tipo de dado. Após criar a estrutura você pode
declarar variáveis do tipo de estrutura criado.
20.3.1.
Declarando variáveis do tipo de uma estrutura criada
Após a declaração da estrutura você pode declarar variáveis do tipo da estrutura com a
sintaxe:
struct nome_da_estrutura nome_da_variável
struct produto item
Página 108
Observe que esta sintaxe obedece a sintaxe normal para a declaração de variáveis:
tipo nome_da_variável
sendo que o TIPO da variável, a nova estrutura criada. Você também pode declarar a
variável logo após a declaração da estrutura com uma sintaxe do tipo:
struct produto
{
int codigo;
char nome[50];
int quantidade;
float valor_compra;
float valor_venda;
float lucro;
char obs[200];
} item;
20.3.2.
Acessando os campos de uma estrutura
A sintaxe para acessar e manipular campos de estruturas é a seguinte:
nome_da_estrutura.campo
Observe o código do programa (EXEMPLO-30), mostrado a seguir, para um melhor
esclarecimento:
Página 109
//******************************************************************************
//
Exemplo de struct
//
//
Alessandro Ferreira da Cunha
//
Tech Training - Engenharia e Treinamentos
//
Janeiro 2009
//
Built with IAR Embedded Workbench Version: 4.11.2.9
//******************************************************************************
#include <msp430xG46x.h>
#include <stdio.h>
/* acessando os campos de uma estrutura */
/* criando um novo tipo de dado "produto" */
struct produto
{
int codigo;
char nome[50];
int quantidade;
float valor_compra;
float valor_venda;
};
int main()
{
struct produto item; /* declarando uma variável "item" do tipo "struct produto" */
printf("Preenchendo a variável \"item\"\n");
printf("Item............:\n");
fgets(item.nome,50,stdin); // este commando apenas funciona com a FULLDLIB
printf("Código..........:\n");
scanf("%d",&item.codigo);
printf("Quantidade......:\n");
scanf("%d",&item.quantidade);
printf("Valor de compra.:\n");
scanf("%f",&item.valor_compra);
printf("Valor de revenda:\n");
scanf("%f",&item.valor_venda);
printf("\n");
printf("Exibindo os dados\n");
printf("Código..........:%d\n",item.codigo);
printf("Item............:%s\n",item.nome);
printf("Quantidade......:%d\n",item.quantidade);
printf("Valor de compra.:%.2f\n",item.valor_compra);
printf("Valor de revenda:%.2f\n",item.valor_venda);
return(0);
}
20.3.3.
Acessando uma estrutura com ponteiros
Para acessar uma estrutura usando ponteiros você pode usar duas sintaxes:
(*nome_da_estrutura).campo
nome_da_estrutura -> campo
Observe o código do programa (EXEMPLO-31), mostrado a seguir, para um melhor
esclarecimento:
Página 110
//******************************************************************************
//
Exemplo de struct
//
//
Alessandro Ferreira da Cunha
//
Tech Training - Engenharia e Treinamentos
//
Janeiro 2009
//
Built with IAR Embedded Workbench Version: 4.11.2.9
//******************************************************************************
#include <msp430xG46x.h>
#include <stdio.h>
/* acessando uma estrutura com ponteiros */
struct registro
{
char nome[30];
int idade;
};
altera_estrutura1(struct registro *ficha)
{
(*ficha).idade -= 10;
}
altera_estrutura2(struct registro *ficha)
{
ficha->idade += 20;
}
int main()
{
struct registro ficha;
printf("Entre com seu nome:\n");
fgets(ficha.nome,30,stdin); // este commando apenas funciona com a FULLDLIB
printf("Qual sua idade?\n");
scanf("%d",&ficha.idade);
printf("\nExibindo os dados iniciais\n");
printf("Nome: %s \n",ficha.nome);
printf("Idade: %d.\n",ficha.idade);
altera_estrutura1(&ficha);
printf("\nExibindo os dados após a primeira alteração\n");
printf("Nome: %s \n",ficha.nome);
printf("Idade: %d.\n",ficha.idade);
altera_estrutura2(&ficha);
printf("\nExibindo os dados após a segunda alteração\n");
printf("Nome: %s \n",ficha.nome);
printf("Idade: %d.\n",ficha.idade);
return(0);
}
Página 111
20.4.
Uniões
Em C uma union é uma posição de memória que é compartilhada por duas ou mais variáveis
diferentes, geralmente de tipo de diferentes, em momentos diferentes. A definição de uma
union é semelhante à definição de estrutura. Sua forma geral é:
union identificador
{
tipo nome_da_variável;
tipo nome_da_variável;
tipo nome_da_variável;
...
} variáveis_união;
Exemplo:
union teste
{
int i;
char ch;
};
Página 112
21.
EXERCÍCIO: Tipos de dados avançados
21.1.
Armazenamento e tratamento de dados (EXERCÍCIO-13)
Já foi identificado no diagrama elétrico da Experimenter Board, mostrado no item 9, as
seguintes conexões, envolvendo pinos do MSP430FG4618 e hardwares externos:
a) Botão S1 Æ pino P1.0;
b) Botão S2 Æ pino P1.1;
c) LED1 Æ pino P2.1;
d) LED2 Æ pino P2.2;
e) LED4 Æpino P5.1.
Com estas informações escreva um programa que execute o algoritmo mostrado abaixo, de
modo que o programa fique em Low Power Mode e apenas saia deste estado para
executar ações, fazendo economia de energia e realizando as atividades pedidas.
1. Aguardar o usuário pressionar o botão S1 para iniciar as atividades. Enquanto o
botão não for pressionado, a Experimenter Board deve ficar em LPM3.
2. Ao pressionar o botão S1 deve ser aceso o LED1 e o terminal I/O deve mostrar a
mensagem:
“Insira o dado 01:”
E o programa deve ficar aguardando, com o LED1 aceso, o usuário inserir, via
terminal I/O, um número entre 0 e 65535.
3. Ao término da digitação, o programa deve apagar o LED1, acender o LED2 e
aguardar o usuário pressionar o botão S2. Enquanto o usuário não pressionar o
botão S2, a Experimenter Board deve ficar em LPM3.
4. Ao pressionar o botão S2 deve ser aceso o LED1 e o terminal I/O deve mostrar a
mensagem:
“Insira o dado 02:”
E o programa deve ficar aguardando, com o LED1 aceso, o usuário inserir, via
terminal I/O, um número entre 0 e 65535.
5. O ciclo composto pelos passos 2, 3 e 4 deve se repetir até que o usuário entre com o
vigésimo dado.
6. Ao final da entrada do vigésimo dado o LED4 deve ficar aceso e a Experimenter
Board deve ficar em LPM3 enquanto o usuário não pressionar o botão S1.
Página 113
7. Ao pressionar o botão S1, o LED4 deve ser apagado, o LED1 deve ser aceso e o
terminal I/O deve mostrar a mensagem:
“O dado 01 é XX”
Onde XX é exatamente o mesmo valor que foi inserido pelo usuário no passo 2.
8. Um segundo após mostrar a mensagem, o LED1 deve ser apagado, o LED4 aceso e
a Experimenter Board deve ficar em LPM3 enquanto o usuário não pressionar o
botão S1.
9. Ao pressionar o botão S1, o LED4 deve ser apagado, o LED1 deve ser aceso e o
terminal I/O deve mostrar a mensagem:
“O dado 02 é YY”
Onde YY é exatamente o mesmo valor que foi inserido pelo usuário no passo 4.
10. Um segundo após mostrar a mensagem, o LED1 deve ser apagado, o LED4 aceso e
a Experimenter Board deve ficar em LPM3 enquanto o usuário não pressionar o
botão S1.
11. O ciclo composto pelos passos 7, 8, 9 e 10 deve se repetir até que seja mostrado no
terminal I/O o último dado armazenado.
12. Ao final da amostragem do vigésimo dado o programa deve retornar ao passo 1, e
recomeçar tudo novamente.
Página 114
22.
REAL TIME CLOCK
Este periférico presente nos chips da família 4 é composto de registradores de propósito
geral que podem ser configurados como um timer de 32 bits ou como um relógio de tempo
real. Dentre as funcionalidades deste módulo estão:
• Modos de funcionamento como relógio e calendário
• Contador de 32 bits com fonte de clock de contagem selecionável
• Quando configurado no modo calendário é feito o incremento automático dos
segundos, minutos, horas, dias da semana, dia do mês, mês e ano
• Capacidade de gerar interrupções
• Capacidade de trabalhar em BCD
O diagrama deste módulo é mostrado na figura abaixo:
Todos os valores dos registradores que pertencem ao módulo RTC tem valor inicial em
estado X. Isto significa que o programador deve inicializar cada um deles adequadamente.
Página 115
Os modos de funcionamento são ajustáveis pelo registrador RTCCTL (Real Time Clock
Control Register), mostrado a seguir:
Página 116
22.1.
Operação como contador
Os bits RTCMODEx com qualquer valor diferente de 11 fazem o módulo RTC operar como
um contador de 32 bits, totalmente acessível via software. Caso haja uma troca do modo de
operação de contador para RTC todos os registradores com valores serão resetados.
Quatro resitradores de 8 bits são cascateados (RTCNTx), formando um contador de 32 bits.
Com esta arquitetura é possível obter interrupções quando ocorre um estourou em qualquer
um destes quatro registradores (em 8 bits, 16 bits, 24 bits ou 32 bits). Qualquer um destes
registradores são totalmente acessíveis para escrita ou leitura.
22.2.
Operação como calendário
Quando os bits RTCMODEx são ajustados com 11 módulo RTC opera como calendário.
Neste modo de operação o RTC fornecerá segundos, minutos, horas, dia da semana, dia do
mês, mês e ano. Estas informações podem ser fornecidas em formato hexadecimal ou em
BCD. Caso haja uma troca do modo de operação de RTC para contador faz com que os
registradores dos com valores segundos, minutos, horas, dia da semana e ano sejam
restados. Já os registradores com os valores do dia do mês e do mês serão receberão os
valor 1.
O algoritmo interno deste módulo permite ajuste de data em qualquer dia entre os anos de
1901 e 2099, incluindo a contagem dos anos bissextos.
32 bits, totalmente acessível via software. Caso haja uma troca do modo de operação de
contador para RTC ou vice-versa, todos os registradores com valores serão resetados.
22.3.
Interação entre o RTC e o Basic Timer 1
Quando o RTC está ajustado para o modo de calendário, o Basic Timer 1 é
automaticamente configurado como um divisor prévio para o RTC, com os dois registradores
de 8 bits do Basic Timer cascateados e o ACLK selecionado como fonte de clock do Basic
Timer.
Os ajustes que tenham sido feitos nos bits BTSSEL, BTHOLD and BTDIV do Basic Timer
serão ignorados. O bit RTCHOLD do módulo RTC passará a controlar os dois periféricos
simultaneamente (RTC e BT1).
Página 117
22.4.
As interrupções do RTC
O módulo RTC utiliza duas fontes de controle de interrupção:
• BT1IF
• RTCIE
O RTC compartilha as flags com o BT1.
Quando RTCIE = 0, o BT1 passará a controlar a interrupção através do bit BTIPx.
Neste caso, os bits RTCTEVx serão responsáveis por selecionar o intervalo em que será
setado o bit RTCIF, mas este bit, mesmo quando levado para nível lógico 1, não irá gerar um
evento de interrupção. Mesmo assim ainda haverá a necessidade de que o software apague
o bit RTCIF para que haja um novo registro de que aconteceu mais uma interrupção.
Quando RTCIE = 1, o RTC passará a controlar a interrupção e os bits BTIPx serão
ignorados.
Neste caso, os bits RTCFG e BT1FG serão setados a cada intervalo de tempo que foi
previamente ajustado pelos bits RTCEVx. Um evento de interrupção será gerado
automaticamente cada vez que um destes dois bits sejam setados, desde que o bit GIE já
esteja ajustado. Ao entrar na rotina de interrupção estes dois bits serão resetados
automaticamente, não necessitando de intervenção do programador. Mesmo assim ainda
haverá a necessidade de que o software apague o bit RTCIF para que haja um novo registro
de que aconteceu mais uma interrupção.
22.5.
Todos os registradores do RTC
A listagem de todos os registradores utilizados no RTC são mostrados na figura a seguir.
Página 118
Página 119
Observe o código do programa (EXEMPLO-31), mostrado a seguir, para um melhor
esclarecimento:
//******************************************************************************
// MSP430xG461x Demo - Real Time Clock, Toggle P5.1 Inside ISR, 32kHz ACLK
//
// Description: This program toggles P5.1 by xor'ing P5.1 inside of
// a Real Time Clock ISR. The Real Time Clock ISR is called once a minute using
// the Alarm function provided by the RTC. ACLK used to clock basic timer.
// ACLK = LFXT1 = 32768Hz, MCLK = SMCLK = default DCO = 32 x ACLK = 1048576Hz
// //* An external watch crystal between XIN & XOUT is required for ACLK *//
//
//
MSP430FG4619
//
----------------//
/|\|
XIN|//
| |
| 32kHz
//
--|RST
XOUT|//
|
|
//
|
P5.1|-->LED
//
// S.Schauer / A. Dannenberg
// Texas Instruments Inc.
// June 2007
// Built with IAR Embedded Workbench Version: 3.42A
//******************************************************************************
#include <msp430xG46x.h>
//-----------------------------------------------------------------------------void main(void)
{
WDTCTL = WDTPW + WDTHOLD;
// Stop Watchdog Timer
FLL_CTL0 = XCAP14PF;
// Configure load caps
RTCCTL = RTCBCD+RTCHOLD+RTCMODE_3+RTCTEV_0+RTCIE;
// RTC enable, BCD mode,
// alarm every Minute,
// enable RTC interrupt
// Init time
RTCSEC = 0x00;
// Set Seconds
RTCMIN = 0x00;
// Set Minutes
RTCHOUR = 0x08;
// Set Hours
// Init date
RTCDOW = 0x02;
RTCDAY = 0x23;
RTCMON = 0x08;
RTCYEAR = 0x2005;
//
//
//
//
RTCCTL &= ~RTCHOLD;
// Enable RTC
P5DIR |= 0x02;
// Set P5.1 to output direction
__bis_SR_register(LPM3_bits + GIE);
// Enter LPM3 w/ interrupt
Set
Set
Set
Set
DOW
Day
Month
Year
}
// Basic Timer interrupt service routine
#pragma vector=BASICTIMER_VECTOR
__interrupt void basic_timer(void)
{
P5OUT ^= 0x02;
// Toggle P5.1 using exclusive-OR
}
Página 120
23.
EXERCÍCIO: Relógio digital
23.1.
Relógio Digital com RTC (EXERCÍCIO-14)
Já foi identificado no diagrama elétrico da Experimenter Board, mostrado no item 9, as
seguintes conexões, envolvendo pinos do MSP430FG4618 e hardwares externos:
a) Botão S1 Æ pino P1.0;
b) Botão S2 Æ pino P1.1;
c) LED1 Æ pino P2.1;
d) LED2 Æ pino P2.2;
e) LED4 Æpino P5.1.
Com estas informações escreva um programa que execute o algoritmo mostrado abaixo, de
modo que o programa fique em Low Power Mode e apenas saia deste estado para
executar ações, fazendo economia de energia e realizando as atividades pedidas.
1. Aguardar o usuário pressionar o botão S1 para iniciar as atividades. Enquanto o
botão não for pressionado, a Experimenter Board deve ficar em LPM3.
2. Ao pressionar o botão S1 deve ser aceso o LED1 e o terminal I/O deve mostrar a
mensagem:
“Insira o ano:”
E o programa deve ficar aguardando, com o LED1 aceso, o usuário inserir, via
terminal I/O, o ano atual.
3. Ao término da digitação, o programa deve apagar o LED1, acender o LED2 e
aguardar o usuário pressionar o botão S2. Enquanto o usuário não pressionar o
botão S2, a Experimenter Board deve ficar em LPM3.
4. Ao pressionar o botão S2 deve ser aceso o LED1 e o terminal I/O deve mostrar a
mensagem:
“Insira o mês:”
E o programa deve ficar aguardando, com o LED1 aceso, o usuário inserir, via
terminal I/O, o mês atual.
5. O ciclo composto pelos passos 2, 3 e 4 deve se repetir até que o usuário entre com o
o ano, mês, dia da semana, dia do mês, hora, minuto e segundo atual.
6. Ao terminar este processo o LED4 deve ficar aceso e a Experimenter Board deve ficar
em LPM3 enquanto o usuário não pressionar o botão S1.
Página 121
7. Ao pressionar o botão S1, o LED4 deve ser apagado, o LED1 deve ser aceso e o
terminal I/O deve mostrar a mensagem:
“Hora do alarme:”
E o programa deve ficar aguardando, com o LED1 aceso, o usuário inserir, via
terminal I/O, a hora do alarme.
8. Ao término da digitação, o programa deve apagar o LED1, acender o LED4 e
aguardar o usuário pressionar o botão S1. Enquanto o usuário não pressionar o
botão S1, a Experimenter Board deve ficar em LPM3.
9. Ao pressionar o botão S1, o LED4 deve ser apagado, o LED1 deve ser aceso e o
terminal I/O deve mostrar a mensagem:
“Minuto do alarme:”
E o programa deve ficar aguardando, com o LED1 aceso, o usuário inserir, via
terminal I/O, a hora do alarme.
10. Ao término da digitação, o programa deve apagar o LED1, acender o LED4 e
aguardar o usuário pressionar o botão S1. Enquanto o usuário não pressionar o
botão S1, a Experimenter Board deve ficar em LPM3.
11. Ao pressionar o botão S1, o LED4 deve ser apagado, o LED1 deve ser aceso e o
terminal I/O deve mostrar a mensagem:
“Segundo do alarme:”
E o programa deve ficar aguardando, com o LED1 aceso, o usuário inserir, via
terminal I/O, a hora do alarme.
12. Ao término da digitação, o programa deve passar a piscar rapidamente os três LEDs
da placa (LED1, LED2 e LED4) a cada segundo computado pelo RTC. Quando o
RTC atingir o horário estipulado pelo alarme, o buzzer deve soar por um minuto e o
programa deve voltar ao passo inicial. Durante todo este processo a Experimenter
Board deve ficar em LPM3 na maior parte do tempo possível.
Página 122
24.
CONTROLADOR DE DISPLAY DE LCD
O hardware LCD_A tem a capacidade de alimentar diretamente displays de LCD através da
criação de sinais AC por segmento e tensões comuns automaticamente. Suas principais
características são:
•
•
•
•
•
•
•
Uma memória onde são armazenados os dados a serem exibidos no display;
Geração automática dos sinais necessários ao funcionamento do display;
Freqüência de exibição de quadro configurável;
Possibilidade de piscar o display independentemente do sinal de clock;
Fonte de alimentação regulada;
Controle de contraste via software;
Suporte a quatro tipos de displays de LCD:
o Estático;
o 2-mux, ½ bias ou ⅓ bias
o 3-mux, ½ bias ou ⅓ bias
o 4-mux, ½ bias ou ⅓ bias.
O diagrama em blocos deste controlador pode ser visto na figura a seguir:
24.1.
Memória para o LCD
No mapeamento de memória do MSP 430 existe um trecho dedicado ao funcionamento do
controlador de LCD, conforme mostrado na figura a seguir.
Cada um dos bits deste mapeamento de memória corresponde a um segmento do display
de LCD, o que pode ser utilizado ou não, dependendo do modelo do LCD e do dispositivo
utilizado. Acionar um determinado segmento do display LCD corresponde a colocar
um bit em nível lógico 1.
24.2.
Piscando o LCD
O controlador de LCD permite que todos os segmentos ativos pisquem, sem a necessidade
de modificação da taxa de tempo. Isto é feito através do bit LCDSON. Quando LCDSON = 1,
cada segmento que está ativo permanece ligado. Os segmentos que não estão ativos
permanecem desligados. Quando LCDSON = 0, todos os do LCD são desligados,
independentemente de seu estado ativo ou não.
Página 123
Página 124
24.3.
Controle de tensão e de geração de sinal de offset
O módulo LCD permite selecionar qual será a fonte para gerar o sinal de tensão de saída
que é aplicada aos planos de fundo (back planes) do display (chamada de tensão V1), bem
como a fração desta tensão que será utilizada como offset (de V2 a V5). Isto possibilita que
o LCD seja alimentado através do AVCC, de um gerador interno ou de uma fonte externa ao
microcontrolador. Todas as fontes de alimentação internas são desligadas quando o ACLK
está desligado (OSCOFF = 1) ou quando o módulo LCD_A é desligado (LCDON = 0).
24.3.1.
Selecionando as fontes de tensão
Para determinar qual será a fonte de tensão a ser utilizada pelo módulo é necessário atuar
sobre dois registradores de controle vistos a seguir: LCDAVCTL0 e LCDAVCTL0. Eles
atuam sob o hardware de tensão mostrado a seguir.
Página 125
Página 126
24.3.2.
Controle de contraste.
A tensão de saída em conjunto com o modo de operação e o offset determinam qual será o
contraste a ser utilizado no LCD. Assim, um ajuste de contraste via software necessitará que
seja feito um ajuste no gerador de voltagem, atuando sobre os bits VLCDx, presentes no
registrador LCDAVCTL0. A taxa de contraste dependerá display LCD utilizado e do offset
escolhido. Um exemplo desta configuração é mostrada na tabela abaixo.
Página 127
24.4.
Freqüência de operação do LCD_A
O módulo LCD_A utiliza o sinal fLCD (proveniente do Basic Timer), que deve ser previamente
ajustado a partir do ACLK para gerar a base de tempo necessária ao funcionamento dos
displays de LCD. O valor da freqüência a ser ajustada é controlada através dos bits
LCDFREQx, presentes no registrador LCDACTL. O valor correto depende do tipo de LCD
que se está utilizando. Então uma consulta ao manual do fabricante do LCD se faz
necessário.
A taxa de quadros que serão amostrados no display quando este fizer uso de vários back
planes, e tiver a necessidade de ser multiplexado, é calculada pela expressão abaixo:
f LCD = 2 ⋅ mux ⋅ f FRAME
Por exemplo: um LCD com três back planes, que necessita de um mux de três vias, com
uma freqüência de quadro entre 30 Hz e 100 Hz:
f LCD = 2 ⋅ 3 ⋅ f FRAME
f LCDMÍNIMO = 180Hz ⇔ f LCDMÁXIMO = 600Hz
Isto faz com que o microcontrolador seja ajustado para as seguintes opções:
f LCD
32768 Hz
=
= 256 Hz
128
f LCD =
f LCD =
32768 Hz
= 341Hz
96
32768 Hz
= 512 Hz
64
Página 128
24.5.
Saídas do módulo LCD
Alguns dos segmentos do display de LCD são multiplexados com funções de I/O digital.
Então estes pinos podem ter a função de I/O ou de LCD. Em que momento cada pino deste
será ajustado para qual função dependerá exclusivamente do ajuste feito através do
registrador PxSELx aplicável a porta onde o LCD está conectado.
Página 129
24.6.
Modo estático (um único back plane)
Neste modo cada pino de segmento do driver do MSP430 controla apenas um segmento de
LCD e apenas a linha comum COM0 é utilizada. Um exemplo dos sinais gerados seguir e da
conexão a ser feita pode ser vista nas figuras abaixo.
Página 130
24.7.
Modo 2-Mux (dois back planes)
Página 131
24.8.
Modo 3-Mux (três back planes)
Página 132
24.9.
Modo 4-Mux (quatro back planes)
Página 133
24.10. Outros registradores que controlam o LCD
Página 134
25. EXERCÍCIO: Configurar e utilizar o display de LCD da Experimenter
Board.
O Primeiro passo para configurar e utilizar o display de LCD presente na Experimenter
Board é buscar o datasheet do mesmo, o que pode ser feito através do site do fabricante.
www.softbaugh.com
O modelo na placa é o SBLCDA4.
O segundo passo é verificar como os terminais do display estão conectados na placa, o que
é feito no próximo item.
Página 135
25.1.
O display LCD da Experimenter Board
A conexão entre os pinos do Display SoftBaugh SBLCDA4 e o MSP430FG4618 são
mostrados nas duas figuras a seguir. A seguir são mostrados todos os segmentos deste
display e a suas respectivas numerações.
Página 136
Página 137
25.2.
(EXERCÍCIO-15) Æ Mapear a memória do MSP430
Preencha o mapeamento de memória do MSP430 mostrado abaixo adequadamente. Para
isto você deve indicar onde cada bit da memória representa cada um dos segmentos do
display SBLCDA4 da SoftBaugh.
Página 138
25.3.
(EXERCÍCIO-16) Æ Relógio Digital com display de LCD
Aproveite o exercício 5, que foi desenvolvido para o RTC, e faça com que as mensagens
que são enviadas ao Terminal I/O, para entrada e saída de dados, sejam mostradas no
Display de LCD.
Para isto, crie imagens que substituam adequadamente as mensagens e que possibilitem ao
usuário o entendimento do que se está pedindo para fazer.
Não formataremos qual é esta mensagem. Deixaremos que utilizem sua criatividade!
25.4.
(EXERCÍCIO-17) Æ Relógio Digital com display de LCD
Utilize o exercício anterior e acrescente a seguinte funcionalidade a ele:
Para ativar o programa (sair do estado inicial de Low Power Mode), o usuário deve manter
pressionado o botão S1 por 30 segundos. Somente após este tempo é que o software deve
iniciar os procedimentos de entrada para ajuste do relógio e do despertador.
Página 139
Download

19. Funções 19.1. Forma Geral