Posts Tagged "programação"

Combinando o Arduino com o AVR Studio 5

avr_logoQuase sempre que faço algo com a IDE do Arduino fico com saudades do auto completar ou algum recurso mais aprimorado de verificação de mapa de memória. Quando se trabalha com códigos pequenos até que vai, mas ao utilizar os códigos muito extensos fica meio cansativo ter que lembrar e copiar cada uma das palavras reservadas do sistema. Também por causa de meu aluno José Carlos, que queria utilizar o AVR para seus experimentos.

Como missão dada é missão cumprida fui em frente. Por isso depois de fazer umas “googadas” por aí descobri um site bem legal, disponível nesse link. Tem alguns tutoriais bem interessantes sobre microcontroladores e IDE´s. E decidi seguir um dos tutoriais de integração do AVR Studio 6 – ferramenta bem legal da atmel que é bem semelhante ao Visual Studio da Microsoft e com alguns recursos de desenvolvimento bem legais (se bem para mim o que valeu mais é não ter mais aquela tela de terminal feia de erro do Arduino GUI). Ele pode ser baixado diretamente nesse link, que após um breve cadastro você está apto a utilizar o software. Também no site vocês vão encontrar uns video aulas bem interessantes nesse aqui http://www.atmel.com/Microsite/atmel_studio6/videos.aspx que de certa forma ajuda a se familiarizar com o ambiente.

Logo de cara tive um problema. A versão do AVR Studio que tenho na minha máquina é a 5, mas lendo o tutorial não muda muita coisa da versão 6 para a anterior. Na verdade não muda nada. Porém na outra máquina que tenho em casa a versão da AVR Studio é a 4. E por isso muita coisa mudou… uma atualização vai bem. Lembrando que estou utilizado o SO Windows 7 – 64 bits, porém creio que funcionará bem no Linux. Ainda não testei mas quem testar me manda um feedback para saber se funcionou. E estou usando uma Arduino MEGA 2560P.

Depois de baixar o AVR Studio, aconselho atualizar o software do Arduino no site:

http://arduino.cc/en/Main/Software

Até o fechamento deste post, verifiquei que já tem a versão 1.0.3 do software da Arduino, porém o utilizado na minha máquina é a versão 1.0.2. A versão é muito importante para utilizar a IDE mais adiante. De certa forma versões anteriores podem ser utilizadas em softwares mais atualizados, mas é bom manter a compatibilidade.

Ao baixar o software da Arduino é interessante deixá-lo na raiz do SO. Renomeie a pasta para “Arduino” então a pastinha irá ficar como a seguir:

 

arduino1

 

 

1º passo – Agora é após baixar o AVR Studio. Execute pela primeira vez e escolha a opção “Tools -> Extension Manager…” Na tela que irá aparecer escolha “Online Galery” e mande instalar o Terminal Windows, como na imagem a seguir:

 

arduino2


 

Durante a instalação pode aparecer alguns erros dizendo que o usuário não tem permissão… não foi possível instalar… etc…. para resolver, feche o AVR Studio e abra novamente o programa só que dessa vez mande executar como administrador e repita o passo anterior.

Agora vamos configurar o ambiente:

2º Passo – Para permitir que o AVR copie a imagem .hex para a placa escolha o menu “Tools -> External tools…” A tela que irá aparecer pede para configurar a ferramenta externa coloque:

Title: Arduino UP

Command: C:\Arduino\hardware\tools\avr\bin\avrdude.exe

Arguments: -CC:\Arduino\hardware/tools/avr/etc/avrdude.conf -v -v -v -v -patmega2560 -cwiring -P\\.\COM2 -b115200 -D -V -Uflash:w:”$(ProjectDir)Debug\$(ItemFileName).hex”:i

Marque a opção “Use Output window

Lembrando que essas opções são para a Arduino Mega. Caso queira saber a versão da sua arduino, aconselho verificar a placa impressa no chip conforme a figura abaixo:

 

 

arduino3


 

Notem que a área de azul tem a versão do chip e a frequencia logo abaixo. Nesse caso o chip é um ATMEGA2560 e a frequência 16MHz.

Caso queira saber o que escrever na tela uma dica bem simples é abrir a IDE original da Arduino e escolher “Arquivo -> Preferências…” e na tela que aparecer deixe conforme a imagem abaixo:

 

arduino4


 

Deixem marcada apenas a opção “carregar”, a de compilação vamos utilizar só mais adiante.

Depois escolha as opções “Arquivo –> Exemplos –> 01.Basics –> Blink“. Tenha certeza que sua placa esteja conectada no computador e as configurações de Placa e Porta Serial estejam corretamente ligadas com a Arduino.

Mande Carregar o projeto na placa. Suba a tela de status e observe as primeiras linhas de carregamento, conforme a imagem abaixo:

 

arduino5


 

As primeiras linhas em branco dizem respeitos aos comandos que devem ser digitados no ambiente do AVR Studio. É só digitar no local informado anteriormente no passo 1 e a única alteração pertinente é ao invés de colocar o caminho do .hex conforme a tela da arduino coloque logo depois do w: a seguinte informação: “$(ProjectDir)Debug\$(ItemFileName).hex”

Não esqueça das aspas. Não feche a IDE da Arduino ainda. Vamos utilizar a mesma no passo seguinte novamente. Feito isso o próximo passo é configurar o ambiente para utilizar a bibliotecas da Arduino.

3º Passo – Dentro da pasta do usuário, no explorer, geralmente o AVR studio cria uma pasta, como: C:\Users\<user>\Documents\AVRStudio 5.1. Dentro dela crie uma pasta chamada ArduinoAVR. Com isso a estrutura no explorer vai ficar: C:\Users\<user>\Documents\AVRStudio 5.1\ArduinoAVR. Neste local que irão ficar todas as referências da IDE Arduino, agora é copiar as bibliotecas para esta pasta. Para saber isso vamos refazer algo bem semelhante ao que fizemos no passo 2. Abra a IDE da Arduino e abra as Preferências. Agora ao invés de marcar Carregar, marque Compilação, conforme imagem abaixo:

 

arduino6


 

Isso é importante pois irá mostrar o caminho no computador das bibliotecas. Mande verificar o mesmo código Blink utilizado no passo 2 e vejas as linhas da tela de status, conforme imagem abaixo:

 

arduino7


 

Notem que essa pasta contem o core da Arduino já configurada para a sua máquina! A uns tempos atrás eu fiz um tutorial de Arduino com Eclipse e tive de gerar esta biblioteca na “mão” para depois utilizá-la. Copiem esse caminho, abra no explorer e copie a biblioteca que tem o nome “core.a” para pasta ArduinoAVR com o nome de “libcore.a“. Isso por causa da IDE que retira o nome lib e a extensão do arquivo em suas compilações.

Agora abra no explorer também a pasta de bibliotecas da Arduino. O caminho é C:\Arduino\hardware\arduino\cores\arduino . Copie todos os arquivos com extensão .h para a pasta ArduinoAVR. Também aproveite e copie o arquivo de pinagem (pins_arduino.h) da sua arduino para a mesma pasta. O caminho C:\Arduino\hardware\arduino\variants . Dentro desta pasta possui várias outras pastas, coloque a pinagem de acordo com sua controladora. Como no meu caso é a ATMEGA, copiei a pinagem da pasta “mega“, se fosse uma Arduino UNO, a pasta seria a “standard“. Copiada as bibliotecas principais, copie agora as secundárias no caminho C:\Arduino\libraries. Pode copiar todas as pastas para a pasta ArduinoAVR. Depois esta pasta irá ficar conforme a imagem abaixo:

 

arduino8


 

Pronto! O próximo passo é criar o projeto.

4º Passo – Abra o AVR Studio 5. Escolha “File –> New –> Project…” Escolha “AVRGCC C++ Executable Project“. O nome coloque qualquer um. No meu caso coloquei TesteArduino. Clique em OK. Na tela da solução, aparece um código que é o padrão do AVR. Pode apagar o mesmo, pois não iremos utilizar. Agora aperte ALT+F7, ou escolha “Project –> Properties…“. Na tela que segue escolha:

Toolchain –> AVR/GNU C Compiler –> Directories. Inclua o caminho da biblioteca C:\Users\<user>\Documents\AVRStudio 5.1\ArduinoAVR e também marque a opção “Relative path

Toolchain –> AVR/GNU C Compiler –> Optimization. Escolha em Optimization Level (Optimization for Size). Marque as 3 primeiras opções (Prepare functions for garbage collection, Pack Structure members, Allocate only as many bytes…) e desmarque a última (Use rjmp/rcall…).

Faça a mesma coisa para a opção Toolchain –> AVR/GNU C ++ Compiler, os passos são os mesmos do anterior. O mesmo ocorre na opção Toolchain –> AVR/GNU C ++ Linker. Inclua em “Libraries” logo abaixo da biblioteca “m” a “libcore”, sem a extensão. e em “Library search path” o caminho C:\Users\<user>\Documents\AVRStudio 5.1\ArduinoAVR e marque a opção “Relative path” e em Toolchain –> AVR/GNU C ++ Linker –> Optimization só deixe marcada a opção “Garbage collect unused sections“. Pronto. Agora salve as alterações. Lembrando que nos caminhos digitados nesse passo onde tem a opção <user> coloque o nome do seu usuário do computador. No meu caso ficaria C:\Users\anderson\… Altere conforme o seu computador.

5º Passo – Digite o seguinte código:

*
 * TesteArduino.cpp
 *
 * Created: 26/12/2012 13:34:08
 *  Author: anderson
 */ 

#define F_CPU 16000000
#define ARDUINO 102
#include "Arduino.h"

int Led = 13;

void setup(){
	pinMode(Led, OUTPUT);
	Serial.begin(9600);
}
void loop(){
	digitalWrite(Led, HIGH);
	Serial.println("LIGADO");
	delay(1000);
	digitalWrite(Led, LOW);
	Serial.println("APAGADO");
	delay(1000);
}

Salve o arquivo. Lembrando que em todo projeto que fizer deve incluir as seguintes linhas:

#define F_CPU 16000000
#define ARDUINO 102
#include "Arduino.h"

A opção F_CPU deve ser de acordo com sua placa, se é 4, 6, 8, 16, etc… A linha do #define ARDUINO 102, diz respeito a versão da arduino baixada. Como anteriormente disse que baixei a versão 1.02, então essa opção deve ser 102, se for a versão 1.03, coloque 103 e assim por diante.  Agora está pronto! Salve o projeto e mande compilar. Aperte F7 ou em “Build –> Build Solution“. Se tudo ocorrer sem erros, vai aparecer a mensagem de 1 solução compilada com sucesso. Depois escolha a opção “Tools” e logo acima da opção External Tools… terá a opção “Arduino UP“. Clique nela e aguarde a mensagem avrdude.exe  done.  Thank you. Pronto! Você verá o blink funcionando na sua Arduino e terá um ambiente de testes extremamente completo (auto completar, debug, terminal, biblioteca de processadores, etc…). Caso queira utilizar um Terminal Serial escolha a opção “View –> Terminal Window” Escolha a porta que está conectada a Arduino e Aperte o botão “Connect“, que está logo ao lado da porta. Qualquer dúvida mande um comentário ou uma mail. Os arquivos utilizados nesse tutorial estão logo abaixo:

ArduinoAVR

ModeloEclipse

 

 

Pilha – Estrutura de Dados usando C

Sempre que falo sobre pilhas em minhas aulas os alunos ficam confusos e as vezes não entendem que essa é uma parte fundamental do entendimento de estrutura de dados. Não falo de pilha como a da figura ao lado mas sim uma das várias estruturas de dados que admitem remoção de elementos e inserção de novos elementos.

Mais especificamente, uma  pilha (também chamado de stack)  é uma estrutura sujeita à seguinte regra de operação: sempre que houver uma remoção, o elemento removido é o que está na estrutura há menos tempo. Em outras palavras, o primeiro objeto a ser inserido na pilha é o último a ser removido. Essa política é conhecida pela sigla LIFO (Last-In First-Out).

O uso de pilha está diretamente ligado ao uso de vetores. Suponha que nossa pilha está armazenada em um vetor  pilha[0..n-1] Suponha ainda que os elementos de pilha são números inteiros. (Isto é só um exemplo; os elementos de pilha poderiam ser objetos de qualquer outro tipo.) A parte do vetor ocupada pela pilha será

pilha[0..t-1] .

0 t N-1

 

O índice t indica o topo (top) da pilha.  Esta é a primeira posição vaga da pilha.  A pilha está vazia se t vale 0 e cheia se t vale n. Para remover um elemento da pilha — esta operação é conhecida como desempilhar (to pop) — faça

   x = pilha[--t];

Isso equivale ao par de instruções  “t -= 1; x = pilha[t];”  nesta ordem.  É claro que você só deve desempilhar se tiver certeza de que a pilha não está vazia. Para consultar a pilha sem desempilhar faça x = pilha[t-1].

Para inserir — ou seja, para empilhar (= to push) — um objeto y na pilha faça

   pilha[t++] = y;

Isso equivale ao par de instruções  “pilha[t] = y; t += 1;”  nesta ordem.  Antes de empilhar, verifique se a pilha já está cheia para evitar que ela transborde (ou seja, para evitar um overflow).  Em geral, a tentativa de inserir em uma pilha cheia é uma situação excepcional, que indica um mau planejamento lógico do seu programa.

Ponteiros e Referências em CPP

A coisa que mais me deixa irritado quando ministro aula com as turmas mais avançadas aqui de SI é que os alunos ainda não consegue entender o conceito correto de ponteiro. Muitas vezes por conta da preguiça, que só lembram do assunto quando tem disciplina, ou outras vezes por conta da falta de problemas reais para tentar conseguir visualizar o conceito. Por essa razão segue um pequeno (pequeno mesmo!) sobre o uso de ponteiros. Quem tiver dúvida aconselho a comprar esse livro, ou esse, ou busca algo na Internet.

O desenvolvimento de softwares na linguagem de programação C++, um recurso muito utilizado e poderoso dá aos programadores a possibilidade de manipular contextos de memória utilizando ponteiros e referências.

Tal recurso pode agir como uma espada de dois gumes. Se bem utilizado, trará poder de processamento no uso da memória disponível, mas se mal utilizado, poderá trazer consequências desastrosas para a aplicação, ou pior, trazer consequências desastrosas no pós desenvolvimento, no período de implantação do software.

A manipulação da memória é uma ação que exige atenção e cuidado no desenvolvimento de sistemas, isto faz a diferença entre o sucesso e o fracasso da aplicação, aproveitando ao máximo os recursos disponíveis de hardware e software.

Nesta postagem vamos aprender a criar, manipular e remover da memória os ponteiros e as referências.

Ponteiros

Um ponteiro nada mais é do que uma variável que armazena um endereço de memória.

Mas, o que é a memória ?

A memória do computador ou memória RAM (Random Access Memory), nada mais é do que um espaço físico de armazenamento de dados temporários, de uso do sistemas de computadores. Ela possui setores que armazenam dados, estes que são declarados em uma aplicação como variáveis, ponteiros, referencias e outros recursos do software.

O consumo dos dados na memória, varia de acordo com o seu tipo de dados (int, char, long …), sendo que na declaração de um ponteiro também é declarado o seu tipo, vejamos:

#include ‹iostream›
#include ‹stdio.h› 

using namespace std;

int main(argc, char *argv[]){
    unsigned int address_number = 3560;
    unsigned int *p_value = &address_number;

    cout << *p_value << endl;

    return 0;
}

Repare que no código acima a declaração do ponteiro “p_value” recebe o endereço da variavel “address_number” pelo operador de referência “&”, em seguida o valor apontado por “p_value” é apresentado em tela. Repare ainda que a exibição do valor contido na variavel “address_number” é exibido pelo ponteiro “p_value“, devemos observar que para exibir o valor apontado por um ponteiro é necessário a utilização do operador “*” antes do nome do ponteiro.

A memória do computador possui endereços que podem variar de arquitetura para arquitetura, conhecimento no qual não é necessário no momento, devemos nos ater apenas que um ponteiro irá armazenar este endereço e que ele pode variar de acordo com a disponibilidade da memória livre.

Vejamos abaixo uma demonstração do endereço de memória:

#include ‹iostream›
#include ‹stdio.h›

using namespace std;

int main(argc, char *argv[]){
    unsigned int address_number = 3560;
    unsigned int *p_value = &address_number;

    cout << p_value << endl;

    return 0;
}

Operador new

O operador new, aloca memória para a aplicação de acordo com  a disponibilidade da memória livre e a tipagem de dados.

Utilizando o operador “new” a aplicação irá receber um endereço de memória em caso de sucesso, mas se algo der errado a solicitação de alocação de memória irá obter um valor nulo (NULL). Por isso é crucial que a cada pedido de alocação de memória, o desenvolvedor verifique tal disponibilidade para não causar danos a aplicação.

Abaixo vejamos a utilização do operador:

unsigned int *pvalue;
pvalue = new unsigned int;

ou

unsigned int *pvalue = new unsigned int;

Operador delete

O operador delete, devolve a memória alocada pelo operador “new“, podendo está ser solicitada novamente, de acordo com as necessidades da aplicação.

Abaixo vejamos a utilização do operador:

unsigned int *pvalue = new unsigned int;
*pvalue = 3572;

delete pvalue;

Uma observação muito importante é que após utilizar o operador delete para um ponteiro, apenas estamos dizendo para o compilador devolver a memória para a área disponível, mas isto não remove o endereço de memória armazenado pelo ponteiro.

Então, uma boa prática de desenvolvimento seria, após utilizar o operador delete para devolver a memória requisitada, atribuir a constante NULL para o ponteiro.

Se por engano uma segunda chamada ao operador delete for efetuada para desalocar o ponteiro para a memória, está ação resultaria na quebra do sistema. Logo atribuir a constante NULL para o ponteiro desalocado, remove a referência ao endereço de memória existente como seu valor, o que evita o compilador efetuar uma tentativa de desalocação de memória para uma área já removida da aplicação.

Referências

Uma referência é um nome alternativo(Alias) para uma variável declarada no sistema. Recurso no qual é muito utilizado na passagem de parâmetros para funções.

A utilização da referência, se dá, com a colocação do operador “&” na declaração da variável, após o tipo de dados e antes da declaração do nome, vejamos:

#include ‹iostream›
#include ‹stdio.h›

using namespace std;

int main(int argc, char *argv[]){
    unsigned int x = 20;
    unsigned int &ref = x;

    cout << "valor: " << ref << endl;
    cout << "Endereco de memoria da variavel x: " << &x << endl;
    cout << "Endereco de memoria da variavel p: " << &ref << endl;

    return 0;
}

Reparem que no código acima, a declaração da variável ref possui o “&” (e comercial) antes do seu nome. Podemos ainda verificar que pref possui o mesmo endereço da variável referenciada.

Nas postagens seguintes desta série veremos mais sobre os ponteiros e as referências, utilizando-as em funções, objetos e métodos de classes. Recurso imprescindível para uma aplicação em C++.

Aprendendo a programar de forma fácil e divertida

Rapaz me admiro com a criatividade do povo. Tem certas coisas que não valem a pena, como esse vídeo aqui. Mas outras valem a pena. Hoje um amigo me mandou um link bem legal de um site que ensina de forma divertida e bem intuitiva como programar. Além disso você pode criar a sua aula e compartilhar com outras pessoas. Interessados cliquem no link abaixo:

http://www.codecademy.com/

Variáveis e valores em C (parte 1)

É difícil você entrar em sala de aula e ver alunos das disciplinas de Sistemas Operacionais e Arquitetura não saberem trabalhar direito com variáveis em C/C++. Necessária durante a época da graduação, a linguagem de programação, além de ajudar o alunado em suas questões do dia-a-dia, também auxilia na parte didática e lógica. Alunos devem entender  que não existem só valores inteiro não, podem existir os valores contendo parte fracionária que são, impropriamente,  chamados em C e em outras linguagens de programação de valores reais. O impropriamente é porque qualquer que seja o número que representemos dentro de uma variável, sua precisão será sempre finita. Sempre terá um número finito de dígitos, o que não é exatamente o caso dos números reais em matemática. Melhor os chamarmos de valores com parte fracionária, ou mesmo valores em ponto flutuante, aludindo ao fato que os mesmos podem ser representados na forma M*10E , isto é, mantissa M multiplicada por 10 elevado a um expoente E. O ponto decimal flutua, dependendo do expoente considerado.

Exemplos:

1.2  = 1.2 * 100 ,ou 0.12 * 101 , ou 12 * 10-1, ou ainda 0.0012 * 103

Constantes em ponto flutuante em C

Podem ser representadas com ponto decimal ou em notação exponencial.

Exemplos:

  1. 1.2
  2. 0.12e1 ( o mesmo que 0.12 * 101)
  3. –54.897
  4. –467.8907e12 ( o mesmo que –467.8907 * 1012)
  5. 2.45e0 ( o mesmo que 2.4 * 100)
  6. 23.0

Variáveis em ponto flutuante

São declaradas como float ou double

Float (de ponto flutuante) – contém valores no intervalo –3.4 * 1038 a 3.4 * 1038

O valor positivo mais próximo de zero é 3.4 * 10-38

Double (dupla) – contém valores no intervalo –1.7 * 10308 a 1.7 * 10308

O valor positivo mais próximo de zero é 1.7 * 10-308

Cuidado: nem todos os valores destes intervalos podem ser representados. Existem infinitos valores no intervalo e só são representados aqueles cuja precisão (quantidade de dígitos significativos) cabem dentro de uma variável do tipo correspondente.

Exemplos:

float x,y;
double a,b,c;

Fazendo um resumo de todos os tipos básicos da linguagem C:

Tipos básicos

 

tipo tamanho Description
char

1

Tipo caracter. Pode ser com ou sem sinal (unsigned).
short

2

Inteiro armazenado em 16 bits. Pode ser com ou sem sinal.
int

4

Inteiro aramazenado em 32 bits. Pode ser com ou sem sinal.
long

4

Idêntico ao int.
long long

8

Inteiro armazenado em 64 bits. Com ou sem sinal.
float

4

Ponto flutuante com precisão simples.
double

8

Ponto flutuante em precisão dupla

 

Exemplos:

char b;
unsigned char c;
short c;
unsigned short d;
int e;
unsigned int f;
long g;
unsigned long h;
long long i;
unsigned long long j;
long int k;
unsingned long int l;
float m;
double m;

Funções intrínsecas

Existem no C várias funções pré-definidas. Tais funções podem ser usadas nas expressões aritméticas de um programa. Para isso basta incluir (#include) o arquivo de definições adequado.

Abaixo algumas delas. Para ver uma lista completa, consulte o help do compilador.

 

função e tipo Resultado #include
int abs(int i) | x | – valor absoluto de i <stdlib.h>
int rand (void) um número aleatório <stdlib.h>
double cos (double x) cos x <math.h>
double sin (double x) sen x <math.h>
double tan (double x) tan x <math.h>
double fabs (double x) | x | – módulo de x <math.h>
double exp (double x) ex <math.h>
double log  (double x) Ln x <math.h>
double log10 (double x) Log x (base 10) <math.h>
double acos (double x) Arc cos x <math.h>
double asin (double x) Arc sen x <math.h>
double atan (double x) Arc tan x <math.h>
double sqrt (double x) raiz quadrada de x <math.h>
double cbrt (double x) raiz cúbica de x <math.h>
double pow (double x, double y) xy <math.h>

 

Exemplos:

int a,b;
double x, y, z;
a = abs(i);
a = rand();
z = sqrt(x) + cbrt (x);
z = sin(x) * cos(y) + tan (x+y);
if (exp(x) > 3.1416) printf(“logaritmo de x = %12.5f”, log10(x));
else printf(“logaritmo neperiano de x = %12.5f”, log(x));
z = atan(x+y) + acos(x)/asin(y)
z = pow(x, fabs(y));

O formato f para valores float

 O formato f é usado para imprimoir valores reais. Pode-se especificar o número de casas com que os valores são impressos. Exemplos:

%f – imprime o valor com o comprimento necessário

%12f – imprime o valor com 12 posições. Nestas 12 posições estarão o ponto decimal e 6 casas decimais.

%12.5f – imprime o valor com 12 casas sendo 5 decimais

#include <stdio.h>
#include <stdio.h>

// exemplos do formato f

int main() {
float x;
// entre com x
printf("digite o valor de x:");
scanf("%f", &x);
// imprime x com vários formatos
printf("\n%f", x);
printf("\n%12f", x);
printf("\n%10f", x);
printf("\n%12.5f", x);
printf("\n%10.5f", x);
system(“pause”); return 0;
}

Será impresso:

digite o valor de x: 3.1415

3.1415

    3.141600

  3.141600

     3.14160    

   3.14160

O formato %lf para valores double

Idem ao formato f. Exemplo:

#include <stdio.h>
#include <stdio.h>

// exemplos do formato lf
int main() {
double x;
// entre com x
printf("digite o valor de x:");
scanf("%lf", &x);
// imprime x com vários formatos
printf("\n%lf", x);
printf("\n%12lf", x);
printf("\n%10lf", x);
printf("\n%12.5lf", x);
printf("\n%10.5lf", x);
system(“pause”); return 0;
}

Será impresso:

digite o valor de x:-12.34

-12.340000

-12.340000

-12.34000

-12.34000

-12.34000

No próximo post vamos ver misturas de tipos de expressões.

Alteração na data da prova de Arquitetura

Atenção alunos de Arquitetura!


A prova de vocês que estava inicialmente marcada para o dia 02/05/2011 remarquei para o dia 09/05/2011. E também aproveito para informar que nesse sábado, 30/04/2011 teremos aula de reposição às 8:00.

Até

BrickOS, uma solução para trabalho com Lego Mindstorms

Amplamente difundidos e utilizados tanto para pesquisas como para ensino, os kits de robótica da Lego em sua linha Mindstorms, possibilitam em um baixo investimento a implementação de projetos diversos. Aqui no IFPE utilizamos o NXT em conjunto com as placas da Arduino, e vem demonstrando bons resultados.

Apesar do lançamento, relativamente recente, por parte da Lego da nova linha Mindstorms com a unidade controladora NXT, temos popularmente um grande número de kits no mercado que possuem como unidade central o Robotic Control Explorer (RCX), micro controlador Hitachi H8/3292 de 16 Mhz. Utilizando uma arquitetura RISC (Reduced Instruction Set Computer), com 32 registradores, palavra com 8 bits de tamanho e memória de 16K de ROM e 32K de RAM.

Como sistema operacional que acompanha os kits com controlador RCX temos as versões RCX1 e RCX2, ambas proprietárias, de fácil uso, mas com algumas limitações de desempenho e funcinalidade. Para trabalho com estes sistemas operacionais utilizamos com maior frequência os compiladores Robolab, RIS ou NQC.

Como solução para as limitações impostas pelo software que acompanha os kits temos a solução open source BrickOs que se apresenta como um sistema operacional onde o usuário possui maior controle sobre o RCX, assim como o uso completo de sua memória. Para a programação do RCX é utilizado o gcc e g++ (GNU C e C++ cross compilation tool chain) e mais algumas ferramentas para envio dos programas ao RCX.

Quem não se deparou quando da criação de um projeto, em sua fase de programação, ao utilizar por exemplo o compilador NQC com problemas como número de variáveis, ausência de determinados recursos ou a falta de uma faixa maior de precisão? Pois bem, apesar de ser pouco documentado se comparado ao NQC, e ter uma instalação pouco mais alongada, o BrickOS compensa pela usabilidade.

Para os que estão dispostos a pelo menos experimentar esta solução segue abaixo passo a passo para instalação.

Os passos a seguir foram testados no Debian e em distribuições derivadas como o Kurumin.

Instalar o BrickOS via apt-get.

apt-get install brickos
Plugar a torre de transmissão infra-vermelho na porta USB. E confirir seu reconhecimento.
tail -f /var/log/messages

Você deverá obter uma resposta semelhante a esta:

kernel: usb 4-2: new low speed USB device using uhci_hcd and address 2
kernel: usb 4-2: configuration #1 chosen from 4 choices
kernel: drivers/usb/misc/legousbtower.c:

LEGO USB Tower#0 now attached to major 180 minor 160
kernel: drivers/usb/misc/legousbtower.c:

LEGO USB Tower firmware version is 1.0 build 134
kernel: usbcore: registered new driver legousbtower
kernel: drivers/usb/misc/legousbtower.c: LEGO USB Tower Driver v0.96

Transferir o sistema operacional para o RCX

firmdl3 --tty /dev/usb/legousbtower0

/usr/lib/brickos/brickOS.srec

Não se esqueça de ligar o RCX e posicioná-lo para receber a transmissão da torre.

A transferência irá inicializar e você ao final recebrá a seguinte mensagem:

Transferring "/usr/lib/brickos/brickOS.srec" to RCX...
100%

Existe um demo de alguns programas que já são instalados, verifique neste diretório:

root@dell-3300:/usr/share/doc/brickos/examples/demo# ls

Utilize o make para compilar os exemplos.
Para transferir os programas para o RCX utilize:

dll --tty /dev/usb/legousbtower0
/usr/share/doc/brickos/examples/demo/linetrack.lx

Mais informações em http://brickos.sourceforge.net/docs/CommandRef.html

Programação 2 – Listas Duplamente Encadeadas

Ao trabalharmos anteriormente com as listas de encadeamento simples, tivemos a oportunidade de observar neste tipo de estrutura a falta de um mecanismo eficiente para percorrer os elementos da lista em ordem inversa.

Este não é o único problema identificado. A remoção de um elemento da lista, pelo fato de não termos a referência de seu antecessor, torna-se também complicada.

De forma a sanar todas estas dificuldades encontradas no uso das listas de encadeamento simples, temos as chamadas listas duplamente encadeadas, onde cada elemento tem um ponteiro para o próximo dentro da estrutura e outro para seu antecessor, além é claro dos campos responsáveis pelo armazenamento das informações.

struct noh {
int mat;
struct noh *prox;
struct noh *ant;
};

typedef struct noh lista;

Da mesma forma que fizemos nas listas de encadeamento simples, usaremos de um facilitador para marcarmos o início de nossa lista. Nosso marcador de início da lista não receberá dados, servindo apenas como uma referência para o início de nossa lista, que caso encontre-se vazia terá em nosso inicio->prox o valor NULL.

Utilizaremos também um outro marcador o qual atribuiremos o nome de atual, o qual marcará sempre a posição do último elemento de nossa lista. Inicialmente como não possuímos nenhum elemento em nossa lista, o valor de inicio será igual ao de atual. Entretanto, a medida em que formos inserindo novos elementos, o marcador de atual irá sempre se deslocando e apontando para o último elemento da lista. Com isso, ao realizarmos uma pesquisa seqüencial na lista, temos que nos ater ao fato de que nosso marcador deve terminar a operação apontando sempre ao último elemento.

É importante estar atento também ao fato de que neste exemplo, ao realizarmos a tarefa de inserção na lista, esta irá processar a entrada de um novo nó ao final da lista e não mais em seu início.

Enfim, em regras gerais, salvo as modificações acima citadas, usaremos dos mesmos princípios estudados anteriormente, com a facilidade de possuirmos agora sempre o endereço de memória do elemento anterior ao atual da lista, o que nos facilita e muito na navegação pela mesma.

Vejamos então a seguir como fica nosso exemplo de listas duplamente encadeadas.

struct noh {
int mat;
char *nome;
char sexo[1];
struct noh *prox;
struct noh *ant;
};

typedef struct noh lista;

char onome[256];
lista *inicio=NULL;
lista *atual=NULL;

void inicializa(){
inicio =(lista*) malloc (sizeof (lista));
if (!inicio){
printf("\nNao existe espaco na memoria!");
exit(1);
}

inicio->ant = NULL;
inicio->prox = NULL;
atual = inicio;
}

void insere()
{
lista *novo=NULL;
novo = (lista*) malloc (sizeof (lista));
if (!novo){
printf("\nNao existe espaco na memoria!");
exit(1);
}

printf("\nDigite a matricula: ");
scanf("%d",&novo->mat);
printf("\nDigite o nome: ");
scanf("%s",onome);
novo->nome = (char*) malloc (strlen(onome)+1);
strcpy(novo->nome,onome);
fflush(stdin);
printf("\nDigite o sexo: ");
scanf("%s",novo->sexo);
fflush(stdin);
novo->ant = atual;
novo->prox = NULL;
atual->prox = novo;
atual = novo;
}

void imprime(){
if (inicio->prox==NULL)
printf("\nFila vazia!\n");
else{
atual=inicio;
do{
atual=atual->prox;
printf("\n\nMatricula: %d",atual->mat);
printf("\nNome: %s",atual->nome);
printf("\nSexo: %s",atual->sexo);
}

while (atual->prox!=NULL);
}
}

void busca(){
int matbusca,encontrou=0;
printf("\n\nDigite a matricula para busca: ");
scanf("%d",&matbusca);
if (inicio->prox==NULL)
printf("\nFila vazia! Busca nao pode ser processada!\n");
else{
for (atual=inicio->prox; atual!=NULL; atual=atual->prox){
if (matbusca==atual->mat){
printf("\n\nMatricula: %d",atual->mat);
printf("\nNome: %s",atual->nome);
printf("\nSexo: %s",atual->sexo);
encontrou=1;
}

if (atual->prox==NULL && encontrou==0){
printf("\nRegistro nao encontrado!\n");
break;
}

if (atual->prox==NULL)
break;
}
}
}

void exclui(){
int matbusca,encontrou=0;
printf("\n\nDigite a matricula para exclusao: ");
scanf("%d",&matbusca);
if (inicio->prox==NULL)
printf("\nFila vazia! Busca para exclusao nao pode ser processada!\n");
else{
for (atual=inicio->prox; atual!=NULL; atual=atual->prox){
if (matbusca==atual->mat){
encontrou=1;
if (atual->prox==NULL){
atual->ant->prox = NULL;
free(atual);
atual = inicio;
}else{
atual->ant->prox = atual->prox;
atual->prox->ant = atual->ant;
free(atual);
atual=inicio;
}
}

if (atual->prox==NULL && encontrou==0){
printf("\nRegistro para exclusao nao encontrado!\n");
break;
}

if (atual->prox==NULL)
break;
}
}
}

void menu(){
clrscr();
printf("\n\t\t\t Controle de Lista");
printf("\n\n1 - Inserir\t2 - Excluir\t3 - Imprimir\t4 - Busca\t5 - Sair\n\n");
printf("Sua opcao: ");
}

void finaliza()
{
atual=inicio;
while (inicio->prox!=NULL){
if(inicio->prox->prox!=NULL){
inicio->prox = inicio->prox->prox;
free(inicio->prox->ant);
inicio->prox->ant = inicio;
}else{
free(inicio->prox);
inicio->prox = NULL;
}
}

free(inicio);
}

void main(){
char opcao='0';
inicializa();
while (opcao !='5'){
menu();
opcao=getchar();
fflush(stdin);
switch(opcao){
case '1':insere();
break;
case '2':exclui();
break;
case '3':imprime();
getch();
break;
case '4':busca();
getch();
break;
case '5':printf("\nFIM DE EXECUCAO!");
finaliza();
getch();
exit(1);
default:break;
}
}
}

Observações finais sobre listas duplamente encadeadas
Tivemos neste estudo a oportunidade de verificar como o duplo encadeamento nos permite trabalhar de maneira bem mais fácil pela lista.

É importante frisar que em nosso exemplo optamos por trabalhar com apenas duas variáveis para a navegação pela lista: inicio e atual, e com a condição de que após cada chamada de função, esta deve terminar posicionando nosso atual para o endereço do último nó da lista. Esta regra foi aplicada apenas para forçar uma atenção maior ao caminhar pela lista, entretanto não é a mais eficiente, principalmente quando tratamos da função de exclusão. Se nesta optarmos por introduzir mais uma variável, não necessitaríamos realizar o re-trabalho de voltarmos ao início da lista após uma exclusão, poderíamos guardar o endereço no próximo nó e continuar normalmente nossa busca a partir deste.

Com certeza, após a implementação do exemplo anterior, você já deve ter montado em sua cabeça as alterações necessárias a este para que trabalhemos com o conceito de filas e pilhas. Pois bem, este será nosso próximo estudo.

Programação 1 – Lista encadeada simples

Pessoal de ADS. Por esses dias estarei colocando no blog algumas noções de programação, como lista encadeada, alocação dinâmica, verificação de código com GDB, entre outras… Isso porque estou notando uma queda de rendimento de vocês em sala principalmente quando envolve programação. Vamos lá!

Normalmente para realizarmos a representação de um grupo de dados, utilizamos a forma mais simples para tal que é a dada através do uso de um vetor. Ao declararmos um vetor estamos reservando um espaço de memória unido e em seqüência, para armazenarmos seus elementos. Em um vetor, acessamos seus elementos indexando o ponteiro para seu primeiro elemento.

#define TAMANHO 10
int vetor[TAMANHO];

Ao realizarmos a declaração acima, estamos a partir do endereço de memória FF04, inclusive com este, alocando um total de 10 espaços em memória para que dados do tipo inteiro possam ser gravados nestes.
Desta forma, vetor é o endereço da memória onde o primeiro elemento do vetor está armazenado. De forma análoga poderíamos escrever a mesma situação como &vetor[0].
Apesar de extremamente útil em várias situações, o uso de vetores apresenta como grande desvantagem o fato deste não ser uma estrutura flexível, haja vista que necessitamos dimensioná-lo com um número máximo de elementos. Seu redimensionamento em tempo de execução não é simples e pode requer um recurso computacional mais elevado. Com isso, ao utilizarmos um vetor, corremos o risco da necessidade de crescê-lo além do previsto durante a execução do programa ou então termos super dimensionado o problema e ficarmos com espaço alocado para o mesmo sem que estes sejam utilizados.
Ao fazermos uso portanto de estruturas dinâmicas, como estas alocam seus elementos usando alocação dinâmica de memória, temos esta situação resolvida.

Listas Encadeadas.

Listas encadeadas são estruturas flexíveis onde a cada novo elemento inserido na estrutura é alocado um espaço de memória para que seja processado seu armazenamento. Listas encadeadas, devido ao uso do mecanismo de alocação dinâmica, terão sempre o espaço de memória utilizado variando de acordo com seu número de elementos.
Uma lista encadeada é uma coleção de nós, que armazenam dados, e de ligações com outros nós. Os nós podem estar localizados agora em qualquer lugar na memória, e a passagem de um nó para o outro se dará através do armazenamento dos endereços de outros nós.
Na lista encadeada cada nó conterá um ou mais campos responsáveis pelo armazenamento das informações, além de um campo que é um ponteiro para uma próxima estrutura do mesmo tipo.

struct noh {
int mat;
struct noh *prox;
};
typedef struct noh lista;

De forma a fornecer um facilitador na compreensão das listas, trabalharemos com o conceito de lista com cabeça, ou seja, teremos sempre o conteúdo do primeiro nó irrelevante. Este servirá apenas para marcar o início da lista, não se alterará, e quando estiver apontando para null, indicará que a lista está vazia.



struct noh {
int mat;
struct noh *prox;
};
typedef struct noh lista;
lista *inicio;
inicio =(lista*) malloc (sizeof (lista));
inicio->prox = NULL;

Função para inicialização.

A função para inicialização de uma lista deve criar o nó inicial da lista mas também, no momento de sua criação verificar se há espaço livre na memória para a efetivação do processo.
Iniciaremos neste momento através dos exemplos dados a construção de um primeiro programa modelo, para tratamento de listas, o qual fará uso de variáveis globais de forma a eliminar a necessidade no momento de passagem de parâmetros às funções. Um outro exemplo será dado no decorrer de nossas aulas utilizando estes recursos.


struct noh {
int mat;
char *nome;
char sexo[1];
struct noh *prox;
};
typedef struct noh lista;
char onome[256];
lista *inicio=NULL;
lista *atual=NULL;
lista *anterior=NULL;

void inicializa(){
inicio =(lista*) malloc (sizeof (lista));
if (!inicio){
printf(“\nNao existe espaco na memoria!”);
exit(1);
}
inicio->prox = NULL;
}

Função para inserção

A partir do momento que temos nossa lista inicializada, podemos proceder com a inserção de novos elementos nesta. Lembrando que para cada novo elemento adicionado estaremos alocando um novo espaço de memória para este.
Podemos realizar a inserção em qualquer lugar da lista, entretanto, a maneira mais simples de execução desta tarefa é dada com a inserção do novo elemento no início da lista. Sendo assim o novo elemento entrará logo após nosso marcador de início da lista.
O mais importante aqui é realizar sempre sua programação de maneira defensiva e com o máximo de cuidado nos detalhes de modo a não cometer falhas de direcionamento de próximos nós.
Vejamos como ficaria nossa função de inserção.

void insere()
{
lista *novo=NULL;
novo = (lista*) malloc (sizeof (lista));
if (!novo){
printf("\nNao existe espaco na memoria!");
exit(1);
}
printf("\nDigite a matricula: ");
scanf("%d",&novo->mat);
printf("\nDigite o nome: ");
scanf("%s",onome);
novo->nome = (char*) malloc (strlen(onome)+1);
strcpy(novo->nome,onome);
fflush(stdin);
printf("\nDigite o sexo: ");
scanf("%s",novo->sexo);
fflush(stdin);
if (inicio->prox == NULL){
inicio->prox = novo;
novo->prox = NULL;
}else{
novo->prox = inicio->prox;
inicio->prox = novo;
}
}

Função para impressão

Nossa função de impressão terá uma tarefa bem simples a ser realizada. Seu papel será de a partir do marcador de início da lista, percorrer toda esta, e imprimir as informações gravadas em seus campos. Antes de ler a solução proposta abaixo para nosso estudo, tente criar a função de impressão como um primeiro exercício de nossa disciplina.
Dica: Não se esqueça de verificar antes de tudo se alista não está vazia. Ao realizar a impressão a mesma deve ocorrer, pela lógica proposta acima, na ordem inversa a da entrada dos dados.

void imprime(){
if (inicio->prox==NULL)
printf("\nFila vazia!\n");
else{
atual=inicio;
do{
atual=atual->prox;
printf("\n\nMatricula: %d",atual->mat);
printf("\nNome: %s",atual->nome);
printf("\nSexo: %s",atual->sexo);
}
while (atual->prox!=NULL);
}
}

Função para busca seqüencial

Para verificarmos se um determinado elemento encontra-se presente em nossa lista, utilizaremos aqui de uma busca seqüencial. Em nosso exemplo como citado anteriormente estamos trabalhando neste primeiro momento com variáveis globais e com as funções sem passagem de parâmetros. Entretanto, adiante estaremos reescrevendo a mesma recebendo como parâmetros um ponteiro para lista e uma informação que necessitamos pesquisar. Vamos ao nosso exemplo.

void busca(){
int matbusca,encontrou=0;
printf("\n\nDigite a matricula para busca: ");
scanf("%d",&matbusca);
if (inicio->prox==NULL)
printf("\nFila vazia! Busca nao pode ser processada!\n");
else{
for (atual=inicio->prox; atual!=NULL; atual=atual->prox){
if (matbusca==atual->mat){
printf("\n\nMatricula: %d",atual->mat);
printf("\nNome: %s",atual->nome);
printf("\nSexo: %s",atual->sexo);
encontrou=1;
}
if (atual->prox==NULL && encontrou==0)
printf("\nRegistro nao encontrado!\n");
}
}
}

Função para excluir um elemento da lista

Implementaremos agora a função responsável por localizar e excluir um elemento da lista. Novamente é importante estar atento a detalhes como:
– lista está vazia?
– o elemento a ser excluído não é o primeiro da lista?
A função para remover um elemento da lista é um pouco mais complexa já que devemos guardar o elemento anterior ao procurado e fazer com que este aponte após a exclusão do elemento identificado para o posterior a ele, de forma a manter de maneira correta o encadeamento de nossa lista.
Vamos ao nosso código!

void exclui(){
int matbusca,encontrou=0;
printf("\n\nDigite a matricula para exclusao: ");
scanf("%d",&matbusca);
if (inicio->prox==NULL)
printf("\nFila vazia! Busca para exclusao nao pode ser processada!\n");
else{
anterior = inicio;
for (atual=inicio->prox; atual!=NULL; atual=atual->prox){
if (matbusca==atual->mat){
anterior->prox = atual->prox;
free(atual);
encontrou=1;
}
if (encontrou == 1)
break;
anterior=atual;
if (atual->prox==NULL && encontrou==0)
printf("\nRegistro para exclusao nao encontrado!\n");
}
}
}

Observações finais sobre listas encadeadas

Tivemos a oportunidade de iniciar nosso estudo sobre listas através das listas de encadeamento simples. Notamos que pelo fato de não possuirmos o apontador para elementos anteriores, nosso caminhar pela lista fica prejudicado e necessitamos de soluções nada elegantes para a resolução de problemas como uma exclusão de um elemento no meio da lista.
Partiremos no próximo estudo para o tratamento das listas duplamente encadeadas, o que nos permitirá controlar de maneira eficiente este caminhar, nos inserindo nos tópicos de filas e pilhas.
Quando fazemos uso de linguagens de programação que possuem coletores de lixo de memória, não nos é necessário desalocar o espaço de memória utilizado. Entretanto a linguagem C não possui este mecanismo e liberar a memória utilizada é algo essencial. Fazemos isso através do comando free().

Projetos de Arquitetura

Pessoal da Turma de Arquitetura de Computadores.

Os projetos da disciplina já estão disponíveis no endereço http://va.mu/8b

Já procurem fazer os grupos e comecem a estudar. Não quero grupos em cima da hora pedindo para mudar a data de apresentação. Só quem pode mudar a data é o professor e assim mesmo por força de motivos maior.