Nqc guia em português/Dave Baum

Transcrição

Nqc guia em português/Dave Baum
Guia do programador de NQC
Versão 2.5 a4
Por Dave Baum
Traduzido por Ricardo Ortins
ÍNDICE
1. Introdução..............................................................................................................1
2. Linguagem NQC....................................................................................................2
2.1 Regra léxica............................................................................................................2
2.1.1. Comentários......................................................................................................2
2.1.2. Espaço em branco.............................................................................................2
2.1.3. Constantes numéricas.......................................................................................3
2.1.4. Identificadores e palavras reservadas...............................................................3
2.2. Estrutura do programa...........................................................................................4
2.2.1. Tarefas..............................................................................................................4
2.2.2. Funções.............................................................................................................4
2.2.3. Sub-rotinas........................................................................................................7
2.2.4. Variáveis...........................................................................................................8
2.2.5. Matrizes..........................................................................................................10
2.3. Instruções............................................................................................................10
2.3.1. Declaração de variáveis..................................................................................10
2.3.2. Atribuição.......................................................................................................11
2.3.3. Estruturas de controle.....................................................................................11
2.3.4. Controle de acesso e eventos..........................................................................15
2.3.5. Outras instruções............................................................................................17
2.4. Expressões...........................................................................................................17
2.4.1. Condicionais...................................................................................................19
2.5. O pré-processador................................................................................................20
2.5.1. # include.........................................................................................................20
2.5.2. # define...........................................................................................................20
2.5.3. Compilação condicional.................................................................................21
2.5.4. Iniciação do programa....................................................................................21
2.5.5. Armazenamento de reserva............................................................................21
3. NQC API..............................................................................................................22
3.1. Sensores...............................................................................................................22
3.1.1. Tipos e modos RCX, CyberMaster................................................................22
3.1.2. Informação do sensor.....................................................................................25
3.1.3. Sensor de luz do Scout ..................................................................................26
3.1.4. Sensor de luz do Spybotics Spy.....................................................................27
3.2. Saídas.................................................................................................................27
3.2.1. Funções básicas.............................................................................................27
3.2.2. Outras funções...............................................................................................28
3.2.3. Controle Global RCX2, Scout.......................................................................30
3.2.4. Saídas de Spybotics.......................................................................................31
3.3. Som.....................................................................................................................31
3.4. Display LCD RCX............................................................................................32
3.5. Comunicação......................................................................................................33
3.5.1. Mensagens RCX, Scout.................................................................................33
3.5.2. Série RCX2, Scout.........................................................................................34
3.5.3. VLL (Link de luz visível), Scout...................................................................36
3.6. Temporizadores..................................................................................................36
3.7. Contadores RCX2, Scout, Spy...........................................................................37
3.8. Controle de acesso RCX2, Scout, Spy...............................................................38
3.9. Evento RCX2, Scout..........................................................................................38
3.9.1. Eventos do RCX, spy....................................................................................39
3.9.2. Eventos do Scout...........................................................................................43
3.10. Registro de dados RCX....................................................................................45
3.11. Características gerais........................................................................................46
3.12. Características específicas do RCX..................................................................47
3.13. Características específicas do Scout.................................................................48
3.14. Características específicas do Cybermaster......................................................48
4. Detalhes técnicos.................................................................................................50
4.1. A instrução ASN.................................................................................................50
4.2. Fontes de dados..................................................................................................52
5. Nota do tradutor...................................................................................................54
____________________Guia do programador de NQC________________________
1. Introdução
NQC significa Not Quite C (Não completamente C) e, é uma linguagem simples
para programar vários produtos LEGO MINDSTORMS. Algumas das características de
NQC dependem do produto MINDSTORMS que se utilize. NQC se refere aos
diferentes blocos inteligentes como os modelos. Atualmente NQC suporta quatro
diferentes modelos: RCX, RCX2 (Um RCX que executa um firmware versão 2.0),
CyberMaster, Scout e Spybotics.
Todos os modelos têm um intérprete de bytes (proporcionado por Lego) que pode
ser utilizado para executar programas. O compilador NQC converte um programa fonte
em LEGO Bytecode, o qual pode ser executado no próprio modelo. Ainda que as
estruturas de processamento e de controle sejam muito similares a C, NQC não é uma
linguagem de propósito geral
há muitas restrições que são produtos das limitações do
intérprete de código de bytes Lego.
Logicamente o NQC se define em duas partes diferentes.
A linguagem NQC descreve a sintaxe utilizada ao escrever programas. O NQC
API descreve as funções dos sistemas, constantes e macros que se podem usar nos
programas. Esta API define-se em um arquivo especial incluído no compilador.
Por definição, esse arquivo sempre se processa antes de compilar o programa.
Esse documento descreve ambos: a linguagem NQC e o NQC API. Em resumo,
proporciona a informação necessária para escrever programas em NQC. Dado que há
várias interfaces para NQC, esse documento não descreve como usar nenhuma
implementação de NQC específica. Consulte a documentação oferecida com a
ferramenta NQC, assim como o manual de usuário de NQC para informações
específicas para essa implementação.
Se deseja informação em documentação atualizada de NQC, visite o site web de
NQC na seguinte direção:
http:// www.baumfamily.org/nqc
1
____________________Guia do programador de NQC________________________
2. Linguagem NQC
Esta sessão descreve a própria linguagem NQC. Inclui as regras léxicas usadas
pelo compilador, a estrutura dos programas, sentenças e expressões e, o funcionamento
do processador.
2.1. Regras Léxicas
As regras léxicas descrevem como o NQC divide um arquivo fonte em sinais
individuais. Inclui como estão escritos os comentários, o manejo dos espaços em branco
e os caracteres válidos para identificadores.
2.1.1. Comentários
NQC suporta dois tipos de comentários. O primeiro tipo (comentário C
tradicional) começa com /* e termina com */ . Podem conter várias linhas, mas não
podem adicionar.
/* isto é um comentário*/
/* isto é um comentário
de duas linhas */
/* outro comentário...
/* tentando adicionar...
finalizando o comentário de dentro... */
Esse texto já não é um comentário! */
A segunda classe de comentário começa com // e termina com uma nova linha
(conhecido às vezes como comentário estilo c++).
// um comentário de uma linha simples
O compilador ignora os comentários. Seu único propósito é permitir ao
programador documentar o código fonte.
2.1.2. Espaços em branco
O espaço em branco (espaços, tabulações e saltos de linha) usa-se para separar
sinais e para fazer os programas mais legíveis. Contanto que se distingam, os sinais, não
tem efeito no programa adicionar ou suprimir espaços em branco. Por exemplo, as
seguintes linhas de código têm o mesmo significado:
2
____________________Guia do programador de NQC________________________
x=2;
x = 2 ;
Alguns dos operadores de c++ estão formados por múltiplos caracteres. Para
preservar esses sinais não se deve inserir espaços em branco entre eles. No exemplo
seguinte, a primeira linha usa um operador de avanço para direita ( >> ), mas na
segunda linha o espaço adicionado faz com que os símbolos > se interpretem como
dois elementos separados e em conseqüência se gera um erro.
x = 1 >> 4;
// dá a X o valor de 1 deslocado para direita 4 bits
x = 1 > > 4;
// errado
2.1.3. Constantes Numéricas
As constantes numéricas podem se escrever tanto em formato decimal como
hexadecimal. As constantes decimais consistem em um ou mais dígitos decimais. As
constantes hexadecimais começam com 0x ou 0X seguidos de um ou mais dígitos
hexadecimais.
x = 10; // dá a x o valor 10
x = 0x10; // dá a x o valor 16 (10 hex)
2.1.4. Identificadores e palavras reservadas
Usam-se os identificadores para nomes de tarefas, variáveis e funções. O primeiro
caractere de um identificador deve ser uma letra maiúscula ou minúscula ou sublinhado
( _ ). Os caracteres restantes podem ser letras números e sublinhado.
Se reserva um número potencial de identificadores para uso da própria linguagem
de NQC. Essas palavras se denominam palavras reservadas e não se podem usar como
identificadores. Abaixo se oferece uma lista completa de palavras reservadas:
__event_src
__res
__tasked
abs
__nolist
__sensor
__type
acquire
asm
do
int
sub
break
else
monitor
switch
case
false
repeat
task
catch
for
return
true
const
goto
sign
void
continue
if
start
while
default
inline
stop
3
____________________Guia do programador de NQC________________________
2.2. Estrutura do programa
Um programa NQC compõe-se de blocos de código e variáveis globais. Existem
três tipos de blocos de código: tarefas, funções em linha e sub-rotinas. Cada tipo de
blocos de código tem suas próprias características e restrições particulares, mas todos
compartilham uma estrutura comum.
2.2.1.Tarefas
O RCX suporta multitarefas, de modo que uma tarefa em NQC corresponde a uma
tarefa RCX. As tarefas definem-se por meios da palavra reservada task utilizando a
seguinte sintaxe:
task nome()
{
// o código da tarefa se escreve aqui
}
O nome da tarefa pode ser qualquer identificador legal. Um programa deve ter ao
menos uma tarefa chamada main
que se inicia cada vez que se executa o programa. O
número máximo de tarefas depende do modelo
RCX suporta 10 tarefas, cybermaster 4
e, scout 6.
O corpo de uma tarefa consiste em uma lista de instruções. As tarefas podem
iniciar-se e parar utilizando as instruções start e stop (descritas na sessão intitulada
instruções). Existe também um comando RCX API stopalltasks, que pára todas as
tarefas em funcionamento neste momento.
2.2.2. Funções
Às vezes é útil agrupar um conjunto de instruções em uma só função, que pode ser
chamada quando seja necessário. NQC suporta funções com argumentos, mas não
valores de retorno. As funções se definem com as seguintes sintaxes:
void nome(lista_de_argumentos)
{
// corpo da função
}
A palavra reservada void é conseqüência da herança de NQC
em C as funções
se especificam com o tipo de dados que devolvem. As funções que não devolvem dados
devolvem void. A devolução de dados não tem suporte em NQC, de modo que todas as
funções se declaram usando a palavra reservada void.
4
____________________Guia do programador de NQC________________________
A lista de argumentos pode estar vazia ou pode conter uma ou mais definições de
argumento. Um argumento define-se por seu tipo, seguido de seu nome. Os argumentos
múltiplos separam-se por vírgulas. Todos os valores em RCX representam-se como
inteiros de 16 bits consígnos. No entanto, NQC suporta quatro tipos diferentes de
argumentos que correspondem a diferentes restrições e modos de passo do argumento.
Tipo
Significado
Restrição
Int
Passa Valor
Nenhuma
Const Int
Passa Valor
Só se pode usar constantes
Int&
Passa Referência
Só se pode usar variáveis
Const Int&
Passa Referência
A função não pode modificar argumentos
Os argumentos de tipo int passam-se por valores da função que chama para
função iniciada. Isto quer dizer, que o compilador deve definir uma variável temporal
que armazene o argumento. Não existem restrições para o tipo de valor que se pode
usar. No entanto, já que a função trabalha com a cópia do argumento em si, qualquer
mudança que sofra o valor não se vê na função que se chama. No exemplo abaixo a
função foo tenta estabelecer o valor de seu argumento em 2. Isto é perfeitamente legal,
mas dado que foo funciona como uma cópia do argumento original a variável Y da
tarefa principal não sofrerá mudanças.
void foo(int x)
{
x = 2;
}
task main()
{
int y = 1; // y é agora igual a 1
foo (y); // y continua igual a 1!
}
O segundo tipo de argumento, const int, também passa-se por valor, mas com a
restrição de só poder usar valores constantes (quer dizer, números). Isso é muito
importante já que existem várias funções RCX que só funcionam com argumentos
constantes:
void foo(const int x)
{
PlaySound (x); // ok
5
____________________Guia do programador de NQC________________________
x = 1; // erro
não pode modificar um argumento
}
task main()
{
foo (2);
// ok
foo (4*5 );
// ok
foo (x);
// erro - x não é uma constante
expressão constante
}
O terceiro tipo, int &, passa argumentos ou referências em vez de passar valor.
Isto permite que a função chamada modifique o valor e, faça visíveis as mudanças na
função que chama. No entanto, só se podem usar variáveis quando se chama uma
função usando argumento int &:
void foo(int &x)
{
x = 2;
}
task main()
{
int y = 1;
// y é igual a 1
foo (y);
// y é agora igual a 2
foo (2);
// erro
só se permitem variáveis
}
O último tipo, const int &, é muito pouco freqüente. Também se passa por
referência, mas com a restrição de que a função chamada não lhe permite modificar o
valor. A causa dessa restrição: o compilador pode passar qualquer coisa (não só
variáveis) à função usando esse tipo de argumento. Em geral esta é a forma mais
eficiente de passar argumentos em NQC.
Existe uma diferença importante entre argumentos int e argumentos const int & .
Um argumento int passa-se por valor, de modo que no caso de uma expressão dinâmica
(como a leitura de um sensor) o valor lê uma vez e se armazena. Com os argumentos
const int & , a expressão lê cada vez que é usada na função:
void foo(int x)
{
if (x == x)
// isto sempre é verdade
PlaySound(SOUND_CLICK);
}
void bar(const int &x)
{
6
____________________Guia do programador de NQC________________________
if (x == x)
//pode não ser verdade..
// o valor poderia mudar
PlaySound(SOUND_CLICK);
}
task main()
{
foo(SENSOR_1); // reproduz som
bar(2); // reproduz som
bar(SENSOR_1); // poderia não reproduzir som
}
As funções devem chamar com o número (e tipo) correto de argumentos.
O exemplo seguinte mostra diferentes chamadas legais e ilegais da função foo:
void foo(int bar, const int baz)
{
// faz algo aqui...
}
task main()
{
int x; // declarar variável x
foo (1,2); // ok
foo (x,2); // ok
foo (2,x); // erro
foo (2); // erro
segundo argumento não constante!
número equivocado de argumentos!
}
As funções NQC sempre se expandem como funções em linha. Isto significa que
cada chamada a uma função faz com que se inclua no programa outra cópia do código
da função. Se não se usar com sensatez, as funções em linha fazem com que o tamanho
do código seja excessivo.
2.2.3. Sub-rotinas
A diferença das funções em linha é que as sub-rotinas permitem que se
compartilhem uma única cópia do fragmento de código entre diferentes funções
chamadoras. Isto faz com que seja muito mais eficaz no uso do espaço que nas funções
em linha, mas devido a algumas limitações do interpretador de códigos de bytes Lego as
sub-rotinas possuem algumas restrições significativas. Em primeiro lugar, as sub-rotinas
não podem utilizar nenhum argumento. Segundo, uma sub-rotina não pode chamar a
outra sub-rotina. Por último, o número máximo de sub-rotinas se limita a 8 para o RCX,
7
____________________Guia do programador de NQC________________________
4 para cybermaster, 3 para scout e 32 para spybotics. No mais, quando se utiliza o RCX
1.0 ou o cybermaster, se uma sub-rotina é chamada, desde múltiplas tarefas, não podem
ter variáveis locais ou realizar cálculos que necessitem variáveis temporárias. Estas
importantes restrições fazem com que as sub-rotinas sejam menos atrativas que as
funções. Portanto, seu uso deveria limitar-se a situações onde seja absolutamente
necessário economizar no tamanho do código. Segue abaixo a sintaxe de uma subrotina:
Nome_da_sub-rotina()
{
// corpo da sub-rotina
}
2.2.4. Variáveis
Todas as variáveis em NQC são do mesmo tipo
inteiros consígnos de 16 bits.
As variáveis se declaram usando a palavra reservada int seguido de uma lista de
nomes de variáveis separadas por vírgulas e terminadas por um ponto e vírgula ( ; ).
Opcionalmente pode-se especificar um valor inicial para cada variável usando o
símbolo ( = ) depois do nome da variável. Seguem abaixo alguns exemplos:
int x; // declara x
int t,z; // declara t e z
int a=1,b; // declara a e b, inicia a com o valor 1
As variáveis globais se declaram no âmbito do programa (fora de qualquer bloco
de código). Uma vez declaradas podem ser utilizadas dentro de qualquer tarefa, função
e sub-rotina. Seu âmbito começa com a declaração e termina ao final do programa.
As variáveis locais podem declarar-se dentro das tarefas, funções e às vezes dentro
das sub-rotinas. Essas variáveis somente são acessíveis dentro do bloco de código em
que são definidas. Concretamente, seu âmbito começa com a declaração, e termina ao
final do bloco de código. No caso das variáveis locais se considera um bloco uma
instrução composta (um grupo de instruções incluídas entre duas chaves { y } ):
int x; // x é global
task main()
{
int y; // y é local da tarefa main
x = y; // ok
{ // começa a instrução composta
int z; // z declaração local
8
____________________Guia do programador de NQC________________________
y = z; // ok
}
y = z; // erro
aqui não está definida z
}
task foo ()
{
x = 1; // ok
y = 2; // erro - y não é global
}
Em muitos casos, NQC deve reservar uma ou mais variáveis temporais para o seu
próprio uso. Em alguns casos utiliza-se uma variável temporal para alocar um valor
intermediário durante um cálculo. Em outros casos utiliza-se para guardar um valor a
ser passado à função. Estas variáveis temporais reduzem a reserva de variáveis
disponíveis para o resto do programa. O NQC tentará ser o mais eficiente possível com
as variáveis temporais (inclusive reutilizando-as sempre que seja possível).
O RCX (e outros modelos) proporcionam várias posições de armazenamento e
podem ser usadas para alocar variáveis em um programa NQC. Existem dois tipos de
posições de armazenamento: globais e locais. Quando compilam um programa, NQC
reserva cada variável a um lugar específico de armazenamento. Os programadores
geralmente podem ignorar os detalhes desta reserva seguindo duas regras básicas:
Se uma variável necessitar estar em posição global declara-se como
variável global.
Se uma variável não necessitar ser global, faz com que seja o mais local
possível.
Isto dá ao compilador a máxima flexibilidade ao reservar uma posição de
armazenamento correta.
O número de variáveis locais e globais variam segundo o modelo:
Modelo
Global
Local
RCX
32
0
Cyber Master
32
0
Scout
10
8
RCX2
32
16
Spybotics
32
4
9
____________________Guia do programador de NQC________________________
2.2.5. Matrizes
Os modelos RCX2 e spybotics suportam matrizes (os outros modelos não têm
suporte apropriado para matrizes em seu firmware). As matrizes declaram-se da mesma
forma que as variáveis normais, mas com o tamanho das matrizes fechadas entre
colchetes. O tamanho deve ser uma constante.
int minha_matriz[3]; // declarar uma matriz de três elementos
Os elementos de uma matriz identificam-se por sua posição dentro da matriz
(chamada índice). O primeiro elemento possui um índice 0, o segundo 1, etc. Por
exemplo:
minha_matriz[0]=123; // estabelece o primeiro elemento em 123
minha_matriz[1]= minha_matriz[2];// cópia o terceiro no segundo
Atualmente existe uma série de limitações no uso de matrizes. É provável que
essas limitações acabem nas futuras versões de NQC:
Uma matriz não pode ser argumento de uma função. Todavia pode-se
passar a uma função um elemento individual da matriz.
Nem as matrizes nem seus elementos podem utilizar-se com os operadores
de aumento (++) ou diminuição (--).
Somente é permitida a utilização normal (=) para os elementos da matriz.
Não se permitem as utilizações matemáticas (+=).
Os valores iniciais dos elementos de uma matriz não se podem especificar.
Necessita de uma reserva explícita dentro do mesmo programa que
estabeleça o valor de um elemento.
2.3. Instruções
O corpo de um bloco de código (tarefa, função ou sub-rotina) compõe-se de
instruções. As instruções terminam com um ponto e vírgula ( ; ).
2.3.1. Declaração de variáveis
A declaração de variáveis, como descrito na sessão anterior, é um tipo de
instrução. Uma variável declara-se como local (com indicação opcional) quando
utilizada dentro de um bloco de código. A sintaxe para uma declaração de variável
é:
10
____________________Guia do programador de NQC________________________
Int variáveis;
Onde variáveis é uma lista de nomes separados por vírgulas com valores iniciais
opcionais.
Nome [=expressão]
As matrizes de variáveis também podem declarar (só para RCX2):
Int matriz [tamanho]
2.3.2. Atribuição
Uma vez declaradas as variáveis pode-se reservar o valor de uma expressão:
Variável operador_de_atribuição expressão
Há novos operadores de reserva. O operador mais básico, = , simplesmente
reserva o valor da expressão para a variável. Os outros operadores modificam de
alguma forma o valor da variável em um dos modos como se mostra na tabela
seguinte:
Operador
Ação
=
Atribui a uma variável uma expressão
+=
-=
Adiciona a uma variável uma expressão
*=
Multiplica uma variável por uma expressão
/=
Divide uma variável por uma expressão
&=
AND bit a bit da expressão e da variável
|=
OR bit a bit da expressão e da variável
||=
Atribui a uma variável o valor absoluto de uma expressão
+-=
Atribui uma variável o sinal (-1, +1, 0) de uma expressão
>>=
Desloca para a direita a variável em uma quantidade constante
<<=
Desloca para a esquerda a variável em uma quantidade constante
Resta a uma variável uma expressão
Alguns exemplos:
x = 2; // atribui a x o valor 2
y = 7; // atribui a y o valor 7
x += y; // x é 9, y é todavia 7
2.3.3. Estrutura de controle
A estrutura de controle mas simples é uma instrução composta. Esta é uma lista de
instrução colocada entre chaves ( { y } ).
11
____________________Guia do programador de NQC________________________
{
x = 1;
y = 2;
}
Ainda pode não parecer muito significativo, exerce um papel crucial ao construir
estruturas de controle mais complicadas. Muitas estruturas de controle requerem uma
instrução simples como corpo. Usando uma instrução composta, a mesma estrutura de
controle pode ser usada para controlar múltiplas instruções.
A instrução if demonstra uma condição. Se a condição é verdadeira, executa uma
instrução (a conseqüência). Uma segunda instrução opcional (a alternativa) é executada
se a condição é falsa. Continuando, mostram-se as sintaxes possíveis para uma instrução
if.
if (condição) conseqüência
if (condição) conseqüência else alternativa
Observe que a condição vai ser colocada entre parênteses. Veja os exemplos a
seguir. No último exemplo utiliza-se uma instrução composta para permitir que se
executem duas instruções como conseqüências da condição.
if (x==1) y = 2;
if (x==1) y = 3; else y = 4;
if (x==1) {y = 1; z = 2;}
A instrução while é usada para construir um laço condicional. Avalia-se a
condição. Se, é verdadeira, executa-se o corpo do laço, a continuação comprova de novo
a condição. O processo continua até que a condição se torne falsa (o que executa a
instrução break). A continuação aparece na sintaxe para o laço while:
while (condição) corpo
É normal utilizar uma instrução composta como corpo do laço.
while (x < 10)
{
x = x+1;
y = y*2;
}
Uma variante do laço while é o laço do-while. Sua sintaxe é :
do corpo while (condição)
12
____________________Guia do programador de NQC________________________
A diferença entre o laço while e o do-while é que o do-while sempre executa ao
menos uma vez, enquanto que o laço while pode não executá-lo nunca.
Outro tipo de laço é o laço for:
for(instr1 ; condição ; instr2 ;) corpo
Um laço for sempre executa instr1 logo, checa repetidamente a condição e,
enquanto é verdadeira, executa o corpo seguido de instr2. O laço for é equivalente a:
instr1;
while (condição)
{
corpo
instr2;
}
A instrução repeat executa um laço de um número determinado de vezes:
repeat (expressão) corpo
A expressão determina quantas vezes se executará o corpo. Observa-se que a
expressão só se avalia uma vez e o corpo se repete esse número de vezes. Isto é
diferente nas estruturas while e do-while que avalia a condição em cada laço.
Uma instrução switch pode utilizar-se para executar um de vários blocos de
código dependendo do valor de uma expressão. Cada bloco de código vem precedido
por uma ou mais etiquetas case. Cada case deve ser uma constante única dentro da
instrução switch. A instrução switch avalia a expressão e a continuação busca uma
etiqueta case que cumpra a condição. Então executará qualquer instrução que diga case
até que se encontre uma instrução break ou até que chegue ao final do switch. Também
pode-se usar uma única etiqueta default que se associará a qualquer valor que não
apareça na etiqueta case. Tecnicamente uma instrução switch tem a seguinte sintaxe:
switch (expressão) corpo
As etiquetas case e default não são instruções em si mesmas, sendo que são
etiquetas que precedem as instruções. Múltiplas etiquetas podem preceder à mesma
instrução. Estas etiquetas têm a seguinte sintaxe:
case expressão_constante:
default :
13
____________________Guia do programador de NQC________________________
Uma típica instrução switch teria este aspecto:
switch (x)
{
case 1 : // faz algo quando x é 1
break;
case 2 :
case 3 : // faz outra coisa quando x é 2 ou 3
break;
default : // faz isto quando x não é 1,2 nem 3
break;
}
A instrução goto força o programa a saltar a uma posição determinada. As
instruções de um programa podem ser marcadas precedendo-as de um identificador e
dos pontos. Uma instrução goto especifica a etiqueta a que o programa há de saltar. Por
exemplo, vejamos como implementar um laço que incrementa o valor de uma variável
utilizando goto:
Meu_ laço:
x++;
goto Meu_ laço;
A instrução goto deve ser usada com moderação e precaução. Na maioria dos
casos as estruturas de controle tales como if , while e switch fazem os programas mais
fáceis de ler e modificar. Têm a precaução de não utilizar nunca uma instrução goto
para saltar a uma instrução monitor ou acquire ou para sair dela. Isto é assim porque
monitor e acquire têm um código especial que normalmente é executado na entrada e
saída, e um goto evitaria este código
provavelmente provocando um comportamento
não desejado.
NQC também define o macro until que supõe uma alternativa prática ao laço
while. A definição real de until é:
#define until (c ) while (!( c ))
Em outras palavras, until continuará fazendo laços até que a condição seja
verdadeira. Com muita freqüência utiliza-se como uma instrução de corpo vazio:
until (SENSOR_1 = 1); // espera que se pressione o sensor
14
____________________Guia do programador de NQC________________________
2.3.4. Controle de acesso e eventos
O Scout, RCX2 e Spybotics suportam a monitoração de eventos e o controle de
acesso. O controle de acesso permite que uma tarefa solicite a posição de um ou mais
recursos. No NQC o controle de acesso proporciona a instrução acquire, que pode
apresentar duas formas:
acquire (recursos) corpo
acquire (recursos) corpo catch handler
onde recursos é uma constante que especifica os recursos que há que obter e corpo e
handler, são instruções. O NQC API define as constantes para os recursos individuais
que se podem somar para solicitar múltiplos recursos por vez. O comportamento da
instrução acquire é o seguinte: solicitará a posição dos recursos especificados. Se outra
tarefa de prioridade superior já possui os recursos, então a solução não será aceita e a
execução saltará a handler (se existir). Caso contrário o pedido será aceito e o corpo
começará a ser executado. Enquanto executa-se o corpo, se uma tarefa de prioridade
igual ou superior solicita alguns dos recursos, a tarefa original perderá sua posição.
Quando se perde a posição, a execução salta para handler (se existir). Uma vez que o
corpo está completo devolvem-se os recursos ao sistema (para que possam adquirir
tarefas de prioridade mais baixa). Se não se especifica um handler, tanto o pedido não é
aceito como pode sê-lo (com sua conseqüência perdida uma vez executado o corpo) o
controle passa a instrução que segue a instrução acquire. Por exemplo, o seguinte
código adquire um recurso 10 segundos, fazendo soar um som não se completa com
êxito:
acquire (ACQUIRE_OUT_A)
{
Wait (1000);
}
catch
{
PlaySound (SOUND_UP);
}
A monitoração de eventos implementa-se com a instrução monitor, que tem uma
sintaxe muito similar a instrução acquire:
monitor (eventos) corpo
monitor (eventos) corpo handler_list
15
____________________Guia do programador de NQC________________________
onde handler_list é um ou mais handlers do tipo:
catch (catch_events) handler
O último handler em uma lista de handlers pode omitir a especificação do
evento:
catch handler
Evento é uma constante que determina que eventos devem ser monitorados. Para
o Scout os eventos estão pré-definidos, de modo que há constantes como
EVENT_1_PRESSED que podem ser utilizadas como especificação de evento. Com
RCX2 o significado de cada evento é configurado pelo programador. Existem 16
eventos (números do 0 ao 15). Para especificar um evento em uma instrução de monitor,
o número de evento deve converter-se em uma máscara de evento, utilizando a macro
EVENT_MASK(). As constantes de evento do Scout, as máscaras de evento podem
somar-se para especificar múltiplos eventos. Podem combinar-se múltiplas máscaras
por meio do OR bit a bit.
A instrução monitor executa o corpo enquanto monitoriza os eventos
especificados. Sucede-se qualquer dos eventos, a execução salta o primeiro handler para
esse evento (um handler sem nenhuma especificação maneja qualquer evento). Se não
existe nenhum handler de evento para esse evento, então o controle continua na
instrução que segue a instrução monitor. O seguinte exemplo espera 10 segundos
enquanto monitora os eventos 2, 3 e 4 para RCX2:
monitor (EVENT_MASK (2)| EVENT_MASK (3)| EVENT_MASK (4))
{
Wait (1000);
}
catch (EVENT_MASK (4))
{
PlaySound (SOUND_DOWN); // sucede o evento 4
}
catch
{
PlaySound (SOUND_UP); // sucede o evento 2 ou 3
}
Observe que as instruções acquire e monitor só são suportadas por modelos que
implementem o controle de acesso e a monitoração de eventos, quer dizer, o Scout e o
RCX2.
16
____________________Guia do programador de NQC________________________
2.3.5. Outras instruções
Uma chamada da função (ou sub-rotina) é uma instrução como a seguinte:
Nome(argumentos);
A lista de argumentos é uma lista de expressões separadas por vírgulas. O
número e o tipo de argumento que se proporciona, deve coincidir com a definição da
mesma função.
As tarefas devem iniciar-se e terminar com as seguintes instruções:
start nome_de_tarefa;
stop nome_de_tarefa;
Dentro dos laços (por exemplo, em um laço while) a instrução break pode ser
utilizada para sair do laço e a instrução continue pode ser utilizada para saltar a parte
superior da seguinte interação do laço. A instrução break também pode ser utilizada
para sair da instrução switch.
break;
continue;
É possível fazer com que uma função finalize antes de chegar ao fimde seu
código usando a instrução return.
return;
Qualquer expressão é também uma instrução legal quando termina em ponto e
vírgula. É pouco freqüente usar esse tipo de instrução já que então se descartaria no
valor da expressão. A única exceção mencionada refere-se às expressões que implicam
nos operadores de acréscimo (++) ou decréscimo (--).
X++;
A instrução vazia (só o ponto e vírgula) é também uma instrução legal.
2.4. Expressões
As primeiras versões de NQC faziam uma distinção entre expressões e
condições. Esta diferença elimina-se a partir da versão 2.3: tudo é uma expressão e
agora operadores condicionais para as expressões. Isto é parecido como utiliza-se em
C/C++, as operações condicionais.
Os valores são do tipo mais primitivo de expressões. Formam-se expressões
mais complicadas a partir de valores, usando vários operadores. A linguagem NQC só
17
____________________Guia do programador de NQC________________________
tem incorporado duas classes de valores: constantes numéricas e variáveis. O RCX API
define outros valores que correspondem a várias características do RCX tais como
sensores e temporizadores (timers).
As constantes numéricas no RCX representam-se como inteiros com signo de 16
bits. Internamente NQC usa matemáticas com signo de 32 bits para a avaliação de
expressões constantes, logo o reduz a 16 bits quando generaliza o código RCX. As
constantes numéricas podem ser escritas como decimais (123) ou hexadecimais
(0xABC). Atualmente, existe muito pouco controle da faixa de valor das constantes, de
modo que usar um valor maior do que esperado pode ter efeitos não usuais.
Se predefinem dos valores especiais: true e false. O valor de false é zero,
enquanto que só se garante que o valor de true não é zero. São válidos os mesmos
valores para operadores relacionais (<): quando a relação é falsa, o valor é zero, em
qualquer outro caso o valor não é zero.
Pode-se combinar os valores, utilizando operadores. Vários dos operadores
somente podem usar valores ao avaliar expressões constantes, o que significa que seus
operandos devem ser ou constantes, ou expressões que não impliquem nada exceto
constantes. Apresenta-se abaixo uma lista de operadores em ordem de prioridade (da
mais alta a mais baixa).
18
____________________Guia do programador de NQC________________________
Operador
Descrição
Associação
Restrição
Exemplo
abs ()
sing ()
Valor absoluto
Sinal de operando
n/a
++ , --
Incremento, Diminuição
Esquerda
Só variáveis
~
!
unário menor
negação bitwise (unário)
negação lógica
Direita
Direita
Direita
-x
Só constantes ~123
!x
*
/
%
Multiplicação
Divisão
Modulo
Esquerda
Esquerda
Esquerda
x*y
+
-
Soma
Subtração
Esquerda
Esquerda
x+y
x-y
>>
<<
Deslocamento a direita
Deslocamento a esquerda
Esquerda
<,>
<= , >=
Operadores Relacionais
Esquerda
x<y
x>y
==
!=
Igual a
Diferente de
Esquerda
x == 1
x != 1
&
AND bit a bit
Esquerda
x&y
^
XOR bit a bit
Esquerda
x^y
|
OR bit a bit
Esquerda
x|y
&&
AND lógico
Esquerda
x && y
||
OR lógico
Esquerda
x || y
?:
Valor condicional
n/a
x ==1 ?Y : z
abs (x)
sing (x)
x++ ou ++x
A medida do
deslocamento x << 4
tem que ser
x >> 4
constante
Pode se usar parêntesis onde seja necessário, para mudar a ordem da avaliação.
x = 2+3; 4 // atribuir a x o valor 14
y = (2+3)*4 // atribuir a y o valor 20
2.4.1. Condicionais
As condições formam-se, geralmente, comparando duas expressões. Existem
também duas expressões constantes
true e false
que sempre dão como valor de
avaliação verdadeiro ou falso, respectivamente. Pode-se negar uma condição com o
operador de negação, ou combinar duas condições com os operadores AND ou OR. A
tabela seguinte resume os diferentes tipos de condicionais:
19
____________________Guia do programador de NQC________________________
Condição
Significado
True
Sempre verdadeiro
False
Sempre falso
Expr
Verdade se a expressão não é igual a 0
Expr1 == Expre2
Verdade se expr1 for igual a expr2
Expr1 != Expre2
Verdade se expr1 for diferente a expr2
Expr1 < Expre2
Verdade se expr1 for menor a expr2
Expr1 <= Expre2
Verdade se expr1 for menor ou igual a expr2
Expr1 > Expre2
Verdade se expr1 for maior a expr2
Expr1 >= Expre2
Verdade se expr1 for maior ou igual a expr2
! Condição
Negação lógica de uma condição
Cond1 &&
Cond2
AND lógico de duas condições - Verdadeiro se e somente se ambas as condições
forem verdadeiras
Cond1 || Cond2
OR lógico de duas condições - Verdadeiro se e somente se uma condição for
verdadeira
Verdadeiro se a condição for falsa
2.5. O pré-processador
O pré-processador implementa as seguintes diretrizes: #include, #define,
#ifdef, #ifndef, #if, #elif, #else, #endif, #undef. Sua implementação
está muito próxima a um pré-processador C standar, de modo que a maior parte das
coisas que funcionam em um pré-processador C genérico, deveriam ter o efeito
esperado em NQC. Abaixo aparece uma lista de desvios significativos.
2.5.1. #include
O comando #include funciona como era de se esperar, com a ressalva de que o
nome do arquivo deve ir colocado entre aspas. Não existe noção no sistema de uma rota
de inclusão, de modo que, fechar um arquivo entre parêntesis angular, está proibido.
#include
foo.nqh
// ok
#include <foo.nqh> // erro!
2.5.2. #define
O comando #define usa-se para uma substituição de macro simples. A
redefinição de macro é um erro (ao invés que em C onde é um aviso). Os macros
terminam normalmente pelo final da linha, porém pode escrever a nova linha com ( \ )
para permitir macros multilinhas.
#define foo (x) do { bar (x);\
baz (x); } while (false)
A diretiva #undef pode-se usar para retirar uma definição de macro.
20
____________________Guia do programador de NQC________________________
2.5.3. Compilação condicional
A compilação condicional funciona de forma parecida ao pré-processador C.
Podem-se usar as seguintes diretivas de pré-processador.
#if condição
#ifdef symbol
#ifndef symbol
#else
#elif condição
#endif
As condições nas diretivas #if usam os mesmos operadores e prioridades que em
C. Suporta-se no operador defined( ).
2.5.4. Iniciação do programa
Ao início do programa o pré-processador insere uma chamada a uma função de
inicialização especial, _init. Esta função, de fato, é parte do RCX API e põe as três
saídas a plena potência em direção para frente (todavia desconectadas). A função de
iniciação pode desativar-se usando a diretiva #pragma noinit:
#pragma noinit //não faz nenhuma iniciação do programa
A função de iniciação, de fato, pode-se substituir por uma função diferente
usando a diretiva #pragma init:
#pragma init função //usar iniciação de usuário
2.5.5. Armazenamento de reserva
O compilador NQC atribui, automaticamente, variáveis a posições de
armazenamento. No entanto, às vezes é necessário impedir que o compilador use certas
posições de armazenamento. Isto pode-se fazer por meio da diretiva:
#pragma reserve:
#pragma reserve início
#pragma reserve início fim
Esta diretiva faz com que o compilador ignore uma ou mais posições de
armazenamento, durante a atribuição de variáveis. Início e fim devem ser números que
se refiram as posições de armazenamento válidas. Só se proporciona um início, então se
reserva uma única posição. Se especificam ambos, início e fim então, se reserva a escala
de posições de principio a fim (inclusive). O uso mais comum desta diretiva é para
reservar as posições 0, 1 e/ou 2 ao usar contadores para RCX2. Isto é porque os
21
____________________Guia do programador de NQC________________________
contadores de RCX2 se sobrepõem com as posições de armazenamento 0, 1 e 2. Por
exemplo, se fossem utilizar os três contadores:
#pragma reserve 0 1 2
3. NQC API
O NQC API define um grupo de constantes, funções, valores e macros que
proporcionam acesso a várias capacidades do modelo como sensores, saídas,
temporizadores e comunicações. Algumas características só se encontram em certos
modelos. Sempre que seja necessário, o título da seção indica a que o modelo se aplica.
O RCX2 reúne todas as características do RCX, de modo que se faz referência ao RCX,
então essa característica funciona com o firmware original e com o firmware 2.0. Se faz
referência ao RCX2 a característica que só se aplica ao firmware 2.0. CyberMaster,
Scout e Spybotics se indicam como CM, Scout e Spy, respectivamente.
O API consiste em funções, valores e constantes. Uma função é algo que pode
ser também denominada instrução. Tipicamente, empreende alguma ação ou configura
algum argumento. Os valores representam algum argumento ou quantidade, e podem ser
usados em expressões. As constantes são nomes simbólicos para valores que têm
significado especial para o modelo. A princípio, usa-se um grupo de constantes junto
com uma função. Por exemplo, a função PlaySound utiliza um simples argumento que
determina que som tocará. As constantes, tais como SOUND_UP, definem-se para cada
som.
3.1. Sensores
Existem três sensores, que se enumeram internamente 0, 1 e 2. Isto poderia dar
confusão já que no RCX está etiquetado externamente como 1, 2 e 3. Para minimizar
esta confusão, foi definido os nomes dos sensores SENSOR_1, SENSOR_2 e
SENSOR_3. Esses nomes de sensores podem ser usados em qualquer função, que
requeira um sensor como argumento. No mais, podem-se usar os nomes sempre que um
programa deseje ler o valor atual de um sensor:
x = SENSOR_1; // lê o sensor y armazena o valor em x
3.1.1. Tipos e modos RCX, CyberMaster
As portas de sensores no RCX têm capacidade de suportar uma grande variedade
de sensores (outros modelos não suportam tipos de sensor configuráveis). É função do
programa dizer ao RCX que classe de sensor está conectada em cada porta. Se pode
22
____________________Guia do programador de NQC________________________
configurar o tipo do sensor por meio de SetSensorType. Existem quatro tipos de
sensores e cada qual corresponde a um sensor específico de LEGO. Pode-se usar um
quinto tipo (SENSOR_TYPE_NONE) para ler os valores puros dos sensores genéricos
passivos. Em geral, um programa deveria configurar o tipo para que encaixe com o
sensor real. Se uma porta de sensor se configurar com o tipo incorreto, o RCX pode não
ser capaz de ler corretamente.
Tipo de Sensor
Significado
SENSOR_TYPE_NONE
Sensor passivo - Genérico
SENSOR_TYPE_TOUCH
Sensor de toque
SENSOR_TYPE_TEMPERATURE
Sensor de temperatura
SENSOR_TYPE_LIGHT
Sensor de luz
SENSOR_TYPE_ROTATION
Sensor de rotação
O RCX, CyberMaster e Spybotics permitem que se configure um sensor em
modos diferentes. O modo de sensor determina como se processa o valor puro de um
sensor. Alguns modos só têm sentido para certos tipos de sensores, por exemplo
SENSOR_MODE_ROTATION só é útil com sensores de rotação. O modo de sensor
pode estabelecer por meio de SetSensorMode. A continuação mostra os possíveis
modos. Adverte-se que, já que CyberMaster não suporta sensores de rotação ou de
temperatura, os últimos três modos se restringem somente ao RCX. Spybotics é todavia
mais restrito e, somente permite os modos raw, booleano e porcentual.
Modo do Sensor
Significado
SENSOR_MODE_RAW
Valor puro de 0 a 1023
SENSOR_MODE_BOOL
Valor booleano (0 ou 1)
SENSOR_MODE_EDGE
Conta números de transições booleanas
SENSOR_MODE_PULSE
Conta números de períodos booleanos
SENSOR_MODE_PERCENT
Valor de 0 a 100
SENSOR_MODE_FAHRENHEIT
Graus F - Somente RCX
SENSOR_MODE_CELSIUS
Graus C - Somente RCX
SENSOR_MODE_ROTATION
Rotação (16 ticks por revolução) - Somente RCX
Ao usar o RCX é normal pôr o tipo e o modo ao mesmo tempo. A função
SetSensor faz este processo um pouco mais fácil ao proporcionar uma única função
que chama e estabelece um conjunto de combinações tipo/modo padrão.
23
____________________Guia do programador de NQC________________________
Configuração do Sensor
Tipo
Modo
SENSOR_TOUCH
SENSOR_TYPE_TOUCH
SENSOR_MODE_BOOL
SENSOR_LIGHT
SENSOR_TYPE_LIGHT
SENSOR_MODE_PERCENT
SENSOR_ROTATION
SENSOR_TYPE_ROTATION
SENSOR_MODE_ROTATION
SENSOR_CELSIUS
SENSOR_TYPE_TEMPERATURE
SENSOR_MODE_CELSIUS
SENSOR_FAHRENHEIT
SENSOR_TYPE_TEMPERATURE
SENSOR_MODE_FAHRENHEIT
SENSOR_PULSE
SENSOR_TYPE_TOUCH
SENSOR_MODE_PULSE
SENSOR_EDGE
SENSOR_TYPE_TOUCH
SENSOR_MODE_EDGE
O RCX proporciona uma conversão booleana para todos os sensores
não só
para os sensores de contato. Esta conversão booleana baseia-se, normalmente, em
entradas préestablecidos para o valor puro. Um valor baixo (menor de 460) é um
valor booleano de 1. Um valor alto (maior de 562) é um valor booleano de 0. Esta
conversão pode modificar: ao chamar SetSensorMode pode adicionar um valor de
entrada entre 0 e 31. Se o valor do sensor mudar mais que o valor de entrada durante
certo tempo (3 milisegundos), então troca o estado booleano do sensor. Isto permite que
o estado booleano reflita mudanças rápidas no valor puro. Um aumento rápido ocasiona
um valor booleano de 0, uma descida rápida em um valor booleano de 1. Inclusive
quando
um
sensor
se
configura
para
outro
modo
(por
exemplo
SENSOR_MODE_PERCENT), se leva a cabo a conversão booleana.
SetSensor (sensor, configuração)
Função RCX
Estabelece o tipo e modo de um sensor dado em uma configuração específica,
que deve ser uma constante especial contendo o tipo e o modo da informação.
SetSensor (SENSOR_1, SENSOR_TOUCH);
SetSensorType (sensor, tipo)
Função RCX
Estabelece um tipo de sensor, que deve ser uma das constantes de tipo de sensor
pré-definidos.
SetSensorType(SENSOR_1, SENSOR_TYPE_TOUCH);
SetSensorMode (sensor, modo)
Função - RCX, CM, Spy
Estabelece um modo de sensor, que deve ser uma das constantes de modo de
sensor pré-definidos. Pode-se adicionar, se desejar, (somente ao modo RCX ) um
argumento de entrada para converção booleana.
SetSensorMode(SENSOR_1, SENSOR_MODE_RAW); // modo puro
SetSensorMode(SENSOR_1, SENSOR_MODE_RAW+10); // entrada 10
24
____________________Guia do programador de NQC________________________
ClearSensor(sensor)
Apaga o valor de um sensor
Função Todas
só afeta aos sensores que se configuram para
medir uma quantidade acumulativa, tal como a rotação uma recontagem de pulso.
ClearSensor(SENSOR_1);
3.1.2. Informação do sensor
Existe um número de valores que se pode inspecionar para cada sensor. Para
todos esses valores deve-se especificar o sensor por seu número de sensor (0, 1 ou 2), e
não com a constante correspondente (ex. SENSOR_1).
SensorValue(n)
Valor Todos
Devolve a leitura processada do sensor para o sensor n, onde n é 0, 1 ou 2. Este é
o mesmo valor que devolve os nomes de sensor (ex. SENSOR_1).
x = SensorValue(0); // lê o sensor 1
SensorType(n)
Valor
RCX, CM, Scout
Devolve o tipo configurado do sensor n, que deve ser 0, 1 ou 2. Somente tem
tipos configuráveis de sensor RCX, outros suportes devolvem o tipo pré-configurado de
sensor.
x = SensorType(0);
SensorMode(n)
Valor RCX, CyberMaster, Spy
Devolve o modo de sensor em uso para o sensor n, que deve ser 0, 1 ou 2.
x = SensorMode(0);
SensorValueBool(n)
Valor RCX
Devolve o valor booleano do sensor n, que deve ser 0, 1 ou 2. A conversão
booleano se faz baseando-se, ou bem em limites preestabelecidos, ou em um argumento
slope especificado por meio de SetSensorMode.
x = SensorValueBool(0);
SensorValueRaw(n)
Valor RCX, Scout, Spy
Devolve o valor puro do sensor n, que deve ser 0, 1 ou 2. Os valores puros
variam entre 0 e 1023.
x = SensorValueRaw(0);
25
____________________Guia do programador de NQC________________________
3.1.3. Sensor de luz do Scout
Scout
No Scout, SENSOR_3 refere-se ao sensor de luz que vem incorporado. A leitura
do valor do sensor de luz (com SENSOR_3) devolve um dos três níveis: 0 (escuro), 1
(normal)
o
2
(brilhante).
Pode-se
ler
o
valor
puro
do
sensor
com
SensorValueRaw(SENSOR_3), mas tem que levar em conta que uma luz mais brilhante,
ocasiona um valor puro mais baixo. A conversão do valor puro do sensor (entre 0 e
1023) a um dos três níveis depende de três argumentos: limite superior, limite inferior e
delay. O limite inferior é o valor puro menor (mais brilhante) que ainda se considera
normal. Os valores abaixo do limite mais baixo se consideram brilhantes. O limite
superior é o maior valor puro (mais escuro) que se considera normal. Os valores perto
deste limite se consideram escuros.
Pode-se usar o delay para impedir que mude o nível, quando o valor puro chegue
perto de um dos limites. Isto se consegue, fazendo com que seja um pouco mais difícil
abandonar os estados escuros e brilhantes que entram neles. Especificamente, o limite
para mover-se de normal a brilhante é um pouco mais baixo que o limite para voltar de
brilhante a normal. A diferença entre estes dois limites é a margem do delay. O mesmo
acontece para a transição entre normal e escuro.
SetSensorLowerLimit (valor)
Função Scout
Estabelece o valor inferior da luz do sensor. O valor pode ser uma expressão.
SetSensorLowerLimit (100);
SetSensorUpperLimit (valor)
Função Scout
Estabelece o valor superior da luz do sensor. O valor pode ser uma expressão.
SetSensorUpperLimit (900);
SetSensorHysteresis (valor)
Função Scout
Estabelece o delay do sensor de luz. O valor pode ser uma expressão.
SetSensorHysteresis (20);
CalibrateSensor ( )
Função - Scout
Lê o valor do sensor de luz, depois estabelece os limites superior e inferior em
12,5% por cima o por baixo da leitura atual, e estabelece um delay de 3.12% do valor da
leitura.
CalibrateSensor();
26
____________________Guia do programador de NQC________________________
3.1.4. Sensores do Spybotics
Spy
Spybotics utiliza sensores embutidos, em lugar de sensores conectados
externamente. O sensor de contato que se encontra na parte frontal do tijolo Spybotics é
o SENSOR_1. Está normalmente configurado no modo percentual, seu valor é 0 quando
não está pressionado e 100 quando está. SENSOR_2 é o sensor de luz (o conector na
parte traseira do tijolo que se usa para se comunicar com o computador). Está
normalmente configurado no modo percentual, valores altos indicam luz brilhante.
3.2. Saídas
3.2.1. Funções básicas
Todas as funções que utilizam saídas, estabelecem, como primeiro argumento
um conjunto de saídas. Este valor tem que ser uma constante. Os nomes OUT_A,
OUT_B, e OUT_C usam-se para identificar as três saídas. Várias saídas podem ser
combinadas encadeando saídas individuais. Por exemplo, usa-se OUT_A+OUT_B para
especificar as saídas A e B, em uma só instrução. O valor das saídas tem que ser sempre
uma constante (não pode ser uma variável).
Cada saída tem três atributos: modo, direção e potência. Modo, pode ser
configurado por meio de SetOutput(saída, modo). O argumento modo deve ser uma das
seguintes constantes:
Modo
Significado
OUT_OFF
A saída está desligada (O motor não pode girar)
OUT_ON
A saída está ligada (O motor pode girar)
OUT_FLOAT
O motor seguirá girando até parar por si só
Os outros dois atributos, direção e potência, podem ser configurados em
qualquer momento, mas só têm efeito se a saída está ligada. A direção é configurada
mediante o comando SetDirection(saída,direção). O argumento direção deve ser uma
das seguintes constantes:
Direção
Significado
OUT_FWD
O motor gira para frente
OUT_REV
O motor gira para trás
OUT_TOGGLE
O motor inverte o sentido de giro
A potência pode ser configurada entre 0 (mínima) e 7 (máxima). Os valores
OUT_LOW, OUT_HALF e OUT_FULL estão definidos para serem utilizados na
configuração do argumento potência. A potência pode ser estabelecida mediante a
27
____________________Guia do programador de NQC________________________
função SetPower(saída,potencia). Por padrão, os três motores estão configurados a
máxima potência e giro para frente quando o programa inicia. Contudo, o RCX neste
momento está parado.
SetOutput(saídas, modo)
Função Todos
Estabelece a saída no modo especificado. Saída é um ou mais dos valores
OUT_A, OUT_B, e OUT_C. O modo tem que ser OUT_ON, OUT_OFF, ou OUT_FLOAT.
SetOutput(OUT_A + OUT_B, OUT_ON); // Estabelece A e B ligado
SetDirection(saídas, direção)
Função - Todos
Estabelece a saída na direção especificada. Saída é um ou mais dos valores
OUT_A, OUT_B, e OUT_C. A direção tem que ser OUT_FWD, OUT_REV, ou
OUT_TOGGLE.
SetDirection(OUT_A, OUT_REV); // Faz girar A para trás
SetPower(saídas , potência)
Função - Todos
Estabelece a potência do motor especificado. A potência pode ser uma
expressão, cujo resultado deve ser um valor entre 0 e 7. As constantes OUT_LOW,
OUT_HALF, ou OUT_FULL também podem ser usadas.
SetPower(OUT_A, OUT_FULL); // A a máxima potência
SetPower(OUT_B, x);
OutputStatus(n)
Valor - Todos
Devolve o estado do motor n. Tendo em conta que n deve ser 0, 1 ou 2
não
OUT_A, OUT_B, ou OUT_C.
x = OutputStatus(0); // Estado de OUT_A
3.2.2. Outras funções
Dado que o controle das saídas é uma característica de uso freqüênte dentro do
programa, se dispõe de outras funções que fazem com que se trabalhar com saídas seja
mais fácil. Deve levar-se em conta que estes comandos não adicionam nenhuma nova
funcionalidade aos comandos SetOutput e SetDirection. Só são interessantes para fazer
o programa mais conciso.
28
____________________Guia do programador de NQC________________________
On(saídas)
Função - Todos
Estabelece as saídas especificadas como ligadas. Saída é um ou mais dos valores
OUT_A, OUT_B, e OUT_C.
On(OUT_A + OUT_C); // Liga as saídas A e C
Off(saídas)
Função - Todos
Estabelece as saídas especificadas como apagadas. Saída é um ou mais dos
valores OUT_A, OUT_B, e OUT_C.
Off(OUT_A); // Apaga a saída A
Float(saídas)
Função - Todos
Estabelece as saídas especificadas como float. Saída é um ou mais dos valores
OUT_A, OUT_B, e OUT_C.
Float(OUT_A); // Detém a saída A sem freá-la
Fwd(saídas)
Função
Todos
Estabelece o sentido de giro das saídas especificadas como para frente. Saída é
um ou mais dos valores OUT_A, OUT_B, e OUT_C.
Fwd(OUT_A);
Rev(saídas)
Função - Todos
Estabelece o sentido de giro das saídas especificadas como retrocesso. Saída é
um ou mais dos valores OUT_A, OUT_B, e OUT_C.
Rev(OUT_A);
Toggle(saídas)
Função - Todos
Inverte o sentido de giro das saídas especificadas. Saída é um ou mais dos
valores OUT_A, OUT_B, e OUT_C.
Toggle(OUT_A);
OnFwd(saídas)
Função - Todos
Estabelece o sentido de giro das saídas especificadas como para frente e as põe
em marcha. Saída é um ou mais dos valores OUT_A, OUT_B, e OUT_C.
OnFwd(OUT_A);
29
____________________Guia do programador de NQC________________________
OnRev(saídas)
Função - Todos
Estabelece o sentido de giro das saídas especificadas como retrocesso e as põe
em marcha. Saída é um ou mais dos valores OUT_A, OUT_B, e OUT_C.
OnRev(OUT_A);
OnFor(saídas, tempo)
Função - Todos
Põe em marcha as saídas especificadas por um determinado tempo e a
continuação as detém. Saída é um ou mais dos valores OUT_A, OUT_B, e OUT_C.
Tempo se mede em incrementos de 10ms (one second = 100) e pode ser uma
expressão.
OnFor(OUT_A, x);
3.2.3. Controle Global
SetGlobalOutput(saídas, modo)
RCX2, Scout
Função - RCX2, Scout, Spy
Desativa ou volta a ativar as saídas dependendo do argumento modo. Se modo é
OUT_OFF, então as saídas se apagaram e se desativaram. Enquanto estão desativadas
qualquer chamada SetOutput() (incluindo funções tais como On()) será ignorada. Se, se
utiliza o modo OUT_FLOAT, às saídas estarão estabelecidas no modo float antes de
desativá-las. As saídas podem voltar a ser ativadas chamando SetGlobalOutput() e
modo OUT_ON. Deve levar-se em conta que ativar uma saída não a liga
imediatamente, só permite posteriores chamadas a SetOutput().
SetGlobalOutput(OUT_A, OUT_OFF); //desativa a saída A
SetGlobalOutput(OUT_A, OUT_ON); //ativa a saída A
SetGlobalDirection(saídas, direção)
Função - RCX2, Scout, Spy
Inverte ou reestabelece a direção das saídas. O argumento direção deve ser
OUT_FWD, OUT_REV ou OUT_TOGGLE. Se SetGlobalDirection é OUT_FWD o
comportamento da saída é o normal. Se SetGlobalDirection é OUT_REV o
valor de saída de direção será o oposto do que se atribui pelas funções básicas. Se
SetGlobalDirection é OUT_TOGGLE este mudará entre o comportamento
normal e o comportamento oposto.
SetGlobalDirection(OUT_A, OUT_REV); //direção oposta
SetGlobalDirection(OUT_A, OUT_FWD); //direção normal
30
____________________Guia do programador de NQC________________________
SetMaxPower(saídas, potência)
Função - RCX2, Scout, Spy
Estabelece o valor máximo de potência permitida para as saídas. Potência pode
ser uma variável, mas deve ter um valor entre OUT_LOW e OUT_FULL.
SetMaxPower(OUT_A, OUT_HALF);
GlobalOutputStatus(n)
Função RCX2, Scout, Spy
Devolve a configuração global da saída do motor n. Deve levar-se em conta que
n tem que ser 0, 1 ou 2
não OUT_A, OUT_B ou OUT_C.
X = GlobalOutputStatus(0); //Estado global de OUT_A
3.2.4. Saídas de Spybotics
Spybotics tem dois motores internos. OUT_A refere-se ao motor direito e
OUT_B ao esquerdo. OUT_C enviará ordens VLL por meio do LED traseiro (o que se
utiliza para as comunicações com o ordenador). Isto permite a um dispositivo VLL,
como o Micro_Scout, ser utilizado como terceiro motor do Spybotics. O mesmo LED
pode ser controlado utilizando as funções SendVLL() e SetLight().
3.3. Som
PlaySound(som)
Função - Todos
Executa um dos 6 sons pré-determinados do RCX. O argumento som tem que
ser uma constante (exceto em Spybotics, que permite utilizar uma variável).
As seguintes constantes estão pré-definidas para serem usadas com a função
PlaySound(): SOUND_CLICK, SOUND_DOUBLE_BEEP, SOUND_DOWN, SOUND_UP,
SOUND_LOW_BEEP, SOUND_FAST_UP.
PlaySound(SOUND_CLICK);
PlayTone(freqüência, duração)
Função - Todos
Executa um só tom da freqüência e duração específica. O valor de freqüência é
em hertz e pode ser uma variável para RCX2, Scout e Spybotics, mas tem que ser uma
constante para RCX e CyberMaster. O valor duração é em centésimos de segundo e tem
que ser constante.
PlayTone(440,50); //Executa um La de meio segundo
31
____________________Guia do programador de NQC________________________
MuteSound()
Função - RCX2, Scout, Spy
Faz com que se deixe de executar todos os sons e tons.
MuteSound();
UnmuteSound()
Função - RCX2, Scout, Spy
Devolve o comportamento normal de sons e tons.
UnmuteSound();
ClearSound()
Função - RCX2, Spy
Elimina todos os sons pendentes de serem executados no buffer.
ClearSound();
SelectSounds(grupo)
Função - Scout
Seleciona que grupo de sons do sistema devem ser usados. O grupo deve ser
uma constante.
SelectSound();
3.4. Display LCD
RCX
O RCX tem 7 modos diferentes de display como se mostra abaixo. O prédeterminado do RCX é DYSPLAY_WATCH.
Modo
Conteúdo do LCD
DISPLAY_WATCH
Mostra o relógio do sistema
DISPLAY_SENSOR_1
Mostra o valor do sensor 1
DISPLAY_SENSOR_2
Mostra o valor do sensor 2
DISPLAY_SENSOR_3
Mostra o valor do sensor 3
DISPLAY_OUT_A
Mostra a configuração da saída A
DISPLAY_OUT_B
Mostra a configuração da saída B
DISPLAY_OUT_C
Mostra a configuração da saída C
O RCX2 atribui um oitavo modo: DISPLAY_USER. Este modo lê,
continuamente, um valor fonte e o mostra no visor. Pode mostrar um ponto decimal em
qualquer posição entre os valores. Isto permite emular o trabalho, com frações, ainda
que todos os valores estejam armazenados como inteiros. Por exemplo, a seguinte
função irá mostrar o valor 1234, mostrando duas cifras depois do ponto decimal,
fazendo com que apareça 12.34 no LCD.
SetUserDisplay(1234,2);
O seguinte programa ilustra a atualização do display:
32
____________________Guia do programador de NQC________________________
task main()
{
ClearTimer(0);
SetUserDisplay(Timer(0),0);
Until(false);
}
Dado que o modo SetUserDysplay atualiza constantemente o LCD, existem
algumas restrições no código fonte. Se, se usa uma variável, esta deve ser atribuída a
uma variável global. A melhor maneira para assegurar que é assim, é declará-la como
variável global. Também podem produzir-se outros efeitos estranhos. Por exemplo, se
está mostrando uma variável e se executa um cálculo em que o resultado é a variável, é
possível que o display mostre alguns resultados intermediários:
int x;
task main()
{
SetUserDisplay(x,0);
while(true)
{
// O display pode mostrar durante um instante 1
x = 1 + Timer(0);
}
}
SelectDisplay(modo)
Função - RCX
Seleciona um modo de display.
SelectDisplay(DISPLAY_SENSOR_1); // mostra o sensor 1
SetUserDisplay(valor,precisão)
Função - RCX2
Estabelece que o display LCD mostre continuamente um valor especificado.
Precisão específica o número de dígitos à direita do ponto decimal. Uma
precisão de 0 não mostra ponto decimal.
SetUserDisplay(Timer(0),0); // mostra o temporizador 0
3.5. Comunicações
3.5.1. Mensagens RCX, Scout
O RCX e o Scout podem enviar e receber mensagens, simples, utilizando os
infravermelhos. Uma mensagem pode ter um valor desde 0 até 255, mas não se
33
____________________Guia do programador de NQC________________________
recomenda utilizar a mensagem 0. A última mensagem recebida é guardada e, pode-se
acessá-la mediante Message(). Se não foi recebida nenhuma mensagem, Message()
devolverá o valor 0. Deve-se levar em conta, que devido a natureza da comunicação
mediante infravermelhos, não poderão receber mensagens enquanto uma mensagem
estiver sendo transmitida.
ClearMessage(valor, precisão)
Função - RCX, Scout
Apaga o buffer de mensagens. Isto facilita a detecção de uma mensagem
recebida, já que então o programa pode esperar que a Message() não seja zero:
ClearMessage(); // Apaga as mensagens recebidas
until(Message() > 0); //Espera a mensagem seguinte
SendMessage(mensagem)
Função - RCX, Scout
Envia uma mensagem por infravermelhos. Mensagem pode ser uma expressão,
mas o RCX só pode enviar mensagens com um valor entre 0 e 255, portanto só os 8 bits
menores do argumento serão usados.
SendMessage(3); // envia mensagem 3
SendMessage(259); // outra maneira de enviar a mensagem 3
SetTxPower(potência)
Função RCX, Scout
Estabelece a potência para a transmissão por infravermelhos. Potência tem que
ser uma destas constantes: TX_POWER_LO ou TX_POWER_HI
3.5.2. Série
RCX2, Scout
O RCX2 pode transmitir dados série, pela porta de infravermelhos. Antes de
enviar dados, a configuração da comunicação e dos pacotes têm que ser especificada.
Então, para cada transmissão, os dados devem ser colocados no buffer de transmissão e
então, usar a função SendSerial() para enviá-los. A configuração da comunicação é
establecida mediante SetSerialComm()e, determina como são enviados os bits mediante
a porta de infravermelho. Os valores possíveis se mostram abaixo:
Opção
Efeito
SERIAL_COMM_DEFAULT
Configuração prédeterminada
SERIAL_COMM_4800
4800 Baud
SERIAL_COMM_DUTY25
25% Duty cycle
SERIAL_COMM_76KHZ
76 khz carrier
34
____________________Guia do programador de NQC________________________
Por definição, está configurado enviar dados a 2400 baud usando um duty cycle
de 50% em um carrier de 38 kHz. Para especificar opções múltiplas (como a 4800 baud
com um duty cycle de 25%), combinam-se as opções individuais utilizando OR bit a bit
(SERIAL_COMM_4800 | SERIAL_COMM_DUTY25). A configuração dos pacotes
estabelecem-se com SetSerialPacket e controla a maneira como se monta os bytes em
pacotes. Os valores possíveis se mostram abaixo:
Opção
Efeito
SERIAL_PACKET_DEFAULT
Sem formato de pacotes , somente os bits de dados
SERIAL_PACKET_PREAMBLE
Envia um preâmbulo do pacote
SERIAL_PACKET_NEGATED
Envia cada byte com seu complemento
SERIAL_PACKET_CHECKSUM
Inclui um checksum para cada pacote
SERIAL_PACKET_RCX
Formato padrão do RCX (preâmbulo dados negados e checksum)
Deve-se levar em conta que os pacotes negados sempre incluem um checksum,
por tanto a opção SERIAL_PACKET_CHECKSUM só tem significado quando
SERIAL_PACKET_NEGATED não tiver sido especificado. Igualmente, o preâmbulo
negados e checksum estão implícitos no SERIAL_PACKET_RCX. O buffer de
transmissão pode guardar até 16 bytes de dados. Por exemplo, o seguinte código envia
dois bytes (0x12 y 0x34) à porta série:
SetSerialComm(SERIAL_COMM_DEFAULT);
SetSerialPacket(SERIAL_PACKET_DEFAULT);
SetSerialData(0,0x12);
SetSerialData(1,0x34);
SendSerial(0,2);
SetSerialComm(configuração)
Função - RCX2
Estabelece a configuração da comunicação que determinando que modos são
enviados os bits pelos infravermelhos.
SetSerialComm(SERIAL_COMM_DEFAULT);
SetSerialPacket(configuração)
Funcão - RCX2
Estabelece a configuração dos pacotes que determinando de que modos os bytes
são montados em pacotes.
SetSerialPacket(SERIAL_PACKET_DEFAULT);
SetSerialData(n,valor)
Função - RCX2
35
____________________Guia do programador de NQC________________________
Coloca um byte de dados no buffer de transmissão. n é o índice do byte a
establecer (0-15), e o valor pode ser uma expressão
SetSerialData(3,x); // estabelece o byte 3 em x
SerialData(n)
Funcão - RCX2
Devolve o valor do byte do buffer de transmissão (não os dados recebidos). N
tem que ser uma constante entre 0 e 15.
X = SerialData(7);// lê o byte #7
SendSerial(começo, contador)
Função - RCX2
Utiliza conteúdo do buffer de transmissão para construir um pacote e enviá-lo
por infravermelhos (de acordo com a configuração atual de pacotes e comunicação).
Começo e contador são constantes que especificam o primeiro byte e o número de bytes
dentro do buffer que se devem enviar
SendSerial(0,2);// envia os dois primeiros bytes do buffer
3.5.3. VLL (Link de luz visível)
SendVLL(valor)
Scout
Função Scout, Spy
Envia um comando VLL que pode ser usado para comunicar-se com o
MicroScout ou o CodePilot. Os comandos específicos VLL estão descritos no SDK do
Scout.
SendVLL(4); // Envia o comando VLL #4
3.6. Temporizadores
Os diferentes modelos oferecem temporizadores, independentes, com uma
resolução de 100 ms (10 ticks por segundo). O Scout dispõe de 3 temporizadores,
enquanto o RCX, o Cybermaster e o Spybotics de 4. Os temporizadores vão desde o tic
0 até o tic 32767 (ao redor de 55 minutos). O valor do temporizador pode ser lido
usando Timer(n), onde n é uma constante que determina que temporizador usar (0-2
para o Scout, 0-3 para o resto). O RCX2 e o Spybotics possuem a característica de ler o
mesmo temporizador com maior resolução usando FastTimer(n), que devolve o valor
do temporizador com uma resolução de 10 ms (100 ticks por segundo).
36
____________________Guia do programador de NQC________________________
ClearTimer(n)
Função - Todos
Põe a zero o temporizador especificado.
ClearTimer(0);
Timer(n)
Função - Todos
Devolve o valor atual do temporizador especificado (com uma resolução de 100
ms).
x = Timer(0);
SetTimer(n,valor)
Função RCX2, Spy
Configura o temporizador com um valor especificado (que pode ser uma
expressão).
SetTimer(0,x);
FastTimer(n)
Função - RCX2, Spy
Devolve o valor atual do temporizador especificado com uma resolução de 10
ms.
x = FastTimer(0);
3.7. Contadores RCX2, Scout, Spy
Os contadores são como variáveis simples que podem ser incrementadas,
decrescidas, e apagadas. O Scout dispõe de dois contadores (0 e 1), enquanto que o
RCX2 e Spybotics dispõem de três (0, 1, 2). No caso do RCX2, estes contadores se
sobrepõem com direções de armazenagem global 0-2, portanto, vão ser usadas como
contadores haverá de ser reservadas com #pragma para evitar que NQC as utilize como
uma variável comum. Por exemplo, se se deseja utilizar o contador 1:
#pragma reserve 1
ClearCounter(n)
Função - RCX2, Scout, Spy
Põe a zero o contador n. n tem que ser 0 ou 1 para o Scout, 0-2 para o RCX2 e
Spybotics.
Clear Counter(1);
IncCounter(n)
Função - RCX2, Scout, Spy
Incrementa o contador n em 1. n tem que ser 0 ou 1 para o Scout, 0-2 para o
RCX2 e Spybotics.
37
____________________Guia do programador de NQC________________________
IncCounter(1);
DecCounter(n)
Função - RCX2, Scout, Spy
Decrescida o contador n em 1. n tem que ser 0 ou 1 para o Scout, 0-2 para o
RCX2 e Spybotics.
DecCounter(1);
Counter(n)
Função - RCX2, Scout, Spy
Devolve o valor do contador n. n tem que ser 0 ou 1 para o Scout, 0-2 para o
RCX2 e Spybotics.
x = Counter(1);
3.8. Controle de Acesso
RCX2, Scout, Spy
O controle de acesso é implementado, principalmente, por meio das declarações
acquire. A função SetPriority pode ser usada para estabelecer a prioridade de uma
função, e as seguintes constantes podem ser usadas para especificar os recursos em uma
declaração acquire. Deve levar-se em conta que a definição dos recursos só está
disponível no RCX2.
Constante
Recurso
ACQUIRE_OUT_A
Saídas
ACQUIRE_OUT_B
ACQUIRE_OUT_C
ACQUIRE_OUT_SOUND
ACQUIRE_LED
Som
LEDs (Somente spybotics)
ACQUIRE_USER_1
ACQUIRE_USER_2
Definidas pelo usuário ( Somente em RCX2)
ACQUIRE_USER_3
ACQUIRE_USER_4
SetPriority(p)
Função - RCX2, Scout, Spy
Estabelece a prioridade de uma função a p, que deve ser constante. RCX2
suporta prioridades 0-255, enquanto o Scout suporta prioridades 0-7. Deve levar-se em
conta que para números menores, prioridade maior.
SetPriority(1);
3.9. Eventos
RCX2, Scout
Ainda que o RCX2, Scout e Spybotics comportem um mecanismo comum de
eventos, o RCX2 e Spybotics dispõem de 16 eventos, completamente, configuráveis,
38
____________________Guia do programador de NQC________________________
enquanto que o Scout dispõe de 15 eventos pré-definidos. As únicas funções comuns
nestes modelos são os comandos para inspecionar e forçar eventos.
ActiveEvents(tarefa)
Valor - RCX2, Scout, Spy
Devolve os eventos que foram produzidos em uma tarefa dada.
x = ActiveEvents(0);
CurrentEvents()
Valor - RCX2, Scout, Spy
Devolve os eventos que foram produzidos na tarefa atual.
x = CurrentEvents();
Event(eventos)
Valor - RCX2, Scout, Spy
Ativa, manualmente, um evento. Isto pode ser útil para provar o tratamento de
eventos de um programa ou, em outros casos, para simular um evento baseado em
outros argumentos. Deve-se levar em conta que a especificação de eventos difere, um
pouco, entre o RCX2 e o Scout. RCX2 usa a macro EVENT_MASK para computar uma
máscara de evento, enquanto que o Scout as tem pré-definidas.
Event(EVENT_MASK(3)); // Ativa um evento no RCX2
Event(EVENT_1_PRESSED)); // Ativa um evento no Scout
3.9.1. Eventos do RCX2
RCX2, Spy
Nota: Spybotics events appear to be very similar to RCX2 events, although very
little testing has been done for the NQC API y Spybotics. A informação seguinte foi
escrita sobre a perspectiva do RCX2, e não tem sido todavia atualizada para Spybotics.
O RCX2 e Spybotics oferecem um sistema de eventos, extremamente, flexível.
Existem 16 eventos, cada um deles se relaciona com uma das diferentes fontes de
eventos (o estímulo que pode fazer disparar o evento) e o tipo de evento (o critério para
que se dispare).
Outros argumentos podem ser especificados dependendo do tipo de evento. Para
todas as chamadas desde uma função, um evento se identifica por seu número de evento
uma constante entre 0 e 15. Fontes de eventos são os sensores, temporizadores,
contadores ou o buffer de mensagens. Um evento é configurado chamando a
SetEvent(evento, fonte, tipo), onde evento é um número constante (0-15), fonte é a fonte
do evento, e tipo é um dos tipos que se mostram a continuação (algumas combinações
de fontes e tipos não são possíveis).
39
____________________Guia do programador de NQC________________________
Tipo de Evento
Condição
Fonte do Evento
EVENT_TYPE_PRESSED
Valor muda para on
Somente Sensores
EVENT_TYPE_RELESASED
Valor muda para off
Somente Sensores
EVENT_TYPE_PULSE
Valor muda de off a on e outra vez a off
Somente Sensores
EVENT_TYPE_EDGE
Valor muda de on para off e vice-versa
Somente Sensores
EVENT_TYPE_FASTCHANGE
Valor varia rapidamente
Somente Sensores
EVENT_TYPE_LOW
Valor muda para low
Todos
EVENT_TYPE_NORMAL
Valor muda para normal
Todos
EVENT_TYPE_HIGHT
Valor muda para hight
Todos
EVENT_TYPE_CLICK
Valor muda de low para hight e outra vez a low Todos
EVENT_TYPE_DUOBLECLICK Dois clicks durante um determinado tempo
Nova mensagem recebida
EVENT_TYPE_MESSAGE
Todos
Somente mensagem
Os primeiros quatro eventos baseiam-se no valor booleano de um sensor, assim
que são mais úteis com sensores de contato. Por exemplo, para configurar o evento #2
que se dispare quando o sensor de contato da porta 1 é pressionado, pode ser assim:
SetEvent(2,SENSOR_1,EVENT_TYPE_PRESSED);
Quando se queria usar EVENT_TYPE_PULSE o EVENT_TYPE_EDGE, o sensor
tem que estar configurado como SENSOR_MODE_PULSE o SENSOR_MODE_EDGE
respectivamente.
EVENT_TYPE_FASTCHANGE deve ser usado com sensores que tenham sido
configurados com um argumento slope. Quando o valor raw troca mais rápido que o
argumento slope um evento EVENT_TYPE_FASTCHANGE se disparará.
Os seguintes três tipos (EVENT_TYPE_LOW, EVENT_TYPE_NORMAL e
EVENT_TYPE_HIGH) convertem o valor fonte do evento em uma das três escalas (low
[baixa], normal ou high[alta]), e o evento disparará quando o valor passar de uma
escala a outra. As escalas são definidas por lower limit (limite inferior) e upper limit
(limite superior) para o evento. Quando o valor fonte é menor que o limite inferior, a
fonte será considerada baixa. Quando o valor fonte seja maior que o limite superior, a
fonte será considerada alta. A fonte será normal, quando estiver entre os dois limites.
O seguinte evento configura o evento #3 para que dispare quando o valor do
sensor na porta 2 estiver na escala alta. O limite superior está estabelecido em 80 e o
limite inferior em 50. Esta configuração é um exemplo de como um evento pode
disparar quando o sensor de luz detecta luz clara.
SetEvent(3,SENSOR_2,EVENT_TYPE_HIGH);
SetLowerLimit(3,50);
40
____________________Guia do programador de NQC________________________
SetUpperLimit(3,80);
O argumento hysteresis pode ser usado para fazer com que as transições sejam
mais estáveis, nos casos em que o valor varia. O delay funciona fazendo com que a
transição de baixa a normal seja um pouco mais ampla que a transição de normal a
baixa.
Assim, faz com que seja mais fácil entrar na escala baixa que sair dela. O
mesmo se aplica a transição de normal a alta.
Uma transição de baixa a alta e outra vez a baixa fará disparar o evento
EVENT_TYPE_CLICK sempre que a seqüência completa seja mais rápida que o tempo
de click do evento. Se, se produzem dois clicks, seguidos, e o tempo entre eles é inferior
ao tempo de click, se disparará um evento EVENT_TYPE_DOUBLECLICK. O sistema
também se mantém ao tanto do número total de clicks para cada evento.
O último tipo de evento, EVENT_TYPE_MESSAGE, só é válido quando
Message() é usada como a fonte de um evento. Este evento disparará quando uma nova
mensagem chegar (incluindo se seu valor é o mesmo que o da mensagem anterior).
Monitorar instruções e algumas funções API (como ActiveEvents() ou Event())
requer manipular múltiplos eventos. Isto se faz, convertendo cada número de evento em
uma máscara de evento, e então combinando as máscaras com uma OR bit a bit. A
macro EVENT_MASK(evento) converte um número de evento em uma máscara. Por
exemplo, para mostrar os eventos 2 e 3 pode utilizar-se a seguinte instrução:
monitor(EVENT_MASK(2) | EVENT_MASK(3))
SetEvent(evento, fonte, tipo)
Função RCX2, Spy
Configura um evento (um número entre 0 e 15) para usar a fonte e o tipo
especificado. Ambos, eventos e tipos têm que ser constantes, e fonte deve ser a
expressão de uma fonte que se quer utilizar.
SetEvent(2, Timer(0), EVENT_TYPE_HIGH);
ClearEvent(evento)
Valor RCX2, Spy
Apaga a configuração do evento especificado. Isto permite que não se dispare
até que seja outra vez configurado.
ClearEvent(2); //apaga evento #2
41
____________________Guia do programador de NQC________________________
ClearAllEvents()
Valor RCX2, Spy
Apaga a configuração de todos os eventos.
ClearAllEvents();
EventState(evento)
Valor RCX2, Spy
Devolve o estado do evento dado. Os estados são 0: Low (baixo), 1: Normal, 2:
High (alto), 3: Undefined (indefinido), 4: Start calibrating (inicio de calibração), 5:
Calibrating process (em processo de calibração).
X = EventState(2);
ClearAllEvents();
CalibrateEvent(evento, inferior, superior, delay)
Função RCX2, Spy
Calibra o evento tomando a leitura atual do sensor e aplicando as relações
especificadas de inferior, superior e delay para determinar os limites e o valor do delay.
As fórmulas para a calibração dependem do tipo de sensor e estão explicadas no LEGO
SDK. A calibração não é instantânea. EventState() pode ser comprovado para
determinar quando está completa a calibração (normalmente 50ms).
CalibrateEvent(2,50,50,20);
Until(EventState(2) != 5); //esperar a calibração
SetUpperLimit(evento, limite)
Valor RCX2, Spy
Estabelece o limite superior para o evento, onde evento é um número de evento
constante e limite pode ser qualquer expressão.
SetUpperLimit(2,x);//estabelece o limite superior para #2 em x
UpperLimit()
Valor RCX2, Spy
Devolve o valor do limite superior do número de evento especificado.
x = UpperLimit(2);// Obtém o limite superior do evento #2
SetLowerLimit(evento, limite)
Valor RCX2, Spy
Estabelece o limite inferior para o evento, onde evento é um número de evento
constante e limite pode ser qualquer expressão.
SetLowerLimit(2,x);//estabelece o limite inferior para #2 em x
42
____________________Guia do programador de NQC________________________
LowerLimit()
Valor RCX2, Spy
Devolve o valor do limite inferior do número de evento especificado.
x = LowerLimit(2);// Obter o limite inferior do evento #2
SetHysteresis(evento, valor)
Valor RCX2, Spy
Estabelece o delay do evento, onde evento é um número de evento constante e
valor pode ser qualquer expressão.
SetHysteresis(2,x);
Hysteresis(evento)
Valor RCX2, Spy
Devolve o valor do delay do número de evento especificado.
x = Hysteresis(2);
SetClickTime(evento, valor)
Função RCX2, Spy
Estabelece o tempo de click para o evento, onde evento é um número de evento
constante e valor pode ser qualquer expressão. O tempo é especificado em incrementos
de 10 ms, então um segundo terá o valor de 100.
SetClickTime(2,x);
ClickTime(evento)
Valor RCX2, Spy
Devolve o valor do tempo de click para o número de evento especificado.
x = ClickTime(2);
SetClickCounter(evento, valor)
Função RCX2, Spy
Estabelece o contador de click para o evento, onde evento é um número de
evento constante e valor pode ser qualquer expressão.
SetClickCounter(2,x);
ClickCounter(evento)
Valor RCX2, Spy
Devolve o valor do contador de click para o número de evento especificado.
x = ClickCounter(2);
3.9.2. Eventos do Scout
Scout
O Scout oferece 15 eventos, cada um dos quais tem um significado pré-definido
como se mostra na tabela abaixo:
43
____________________Guia do programador de NQC________________________
Nome do Evento
Condição
EVENT_1_PRESSED
Sensor 1 passa a estar pressionado
EVENT_1_RELESASED
Sensor 1 passa a não estar pressionado
EVENT_2_PRESSED
Sensor 2 passa a estar pressionado
EVENT_2_RELESASED
Sensor 2 passa a não estar pressionado
EVENT_LIGHT_HIGHT
Sensor de luz alto
EVENT_LIGHT_NORMAL
Sensor de luz normal
EVENT_LIGHT_LOW
Sensor de luz baixo
EVENT_LIGHT_CLICK
De baixo a alto e outra vez a baixo
EVENT_LIGHT_DOUBLECLICK
Dois clicks
EVENT_COUNTER_0
Contador 0 por cima do limite
EVENT_COUNTER_1
Contador 1 por cima do limite
EVENT_TIMER_0
Timer 0 por cima do limite
EVENT_TIMER_1
Timer 1 por cima do limite
EVENT_TIMER_2
Timer 2 por cima do limite
EVENT_MESSAGE
Nova mensagem recebida
Os primeiros quatro eventos disparam mediante sensores de contato, conectados
às duas portas de sensores. EVENT_LIGHT_HIGH, EVENT_LIGHT_NORMAL e
EVENT_LIGHT_LOW disparam quando o valor do sensor de luz muda de uma escala a
outra. As escalas são definidas mediante SetSensorUpperLimit, SetSensorLowerLimit, e
SetSensorHysteresis que têm sido descritos, anteriormente.
EVENT_LIGHT_CLICK e EVENT_LIGHT_DOUBLECLICK também disparam
mediante o sensor de luz. Um click é a transição de baixo a alto e de volta a baixo
dentro de um determinado período de tempo, chamado tempo de click.
Cada contador tem um limite de contador. Quando o contador supera este limite,
EVENT_COUNTER_0 ou EVENT_COUNTER_1 disparam. Os temporizadores também
têm um limite, e geram EVENT_TIMER_0, EVENT_TIMER_1, e EVENT_TIMER_2.
EVENT_MESSAGE dispara quando uma nova mensagem é recebida pelo infravermelho.
SetSensorClickTime(valor)
Função Scout
Estabelece o tempo de click utilizado para gerar eventos do sensor de luz. O
valor deve ser especificado em incrementos de 10 ms, e pode ser uma expressão.
SetSensorClickTime(x);
SetCounterLimit(n,valor)
Função Scout
Estabelece o limite para o contador n. n deve ser 0 ou 1 e valor pode ser uma
expressão.
SetCounterLimit(0,100); //Estabelece o limite do contador 0 em 100
44
____________________Guia do programador de NQC________________________
SetTimerLimit(n,valor)
Função Scout
Estabelece o limite para o temporizador n. N deve ser 0, 1 ou 2, e valor pode ser
uma expressão.
SetTimerLimit(1,100); //Estabelece o temporizador 0 em 100
3.10. Registro de dados
RCX
O RCX contém um registro de dados que pode ser usado para armazenar valores
dos sensores, temporizadores, variáveis e do relógio do sistema. Antes de adicionar
dados, o registro de dados necessita ser criado, por meio do comando,
CreateDatalog(tamanho). O argumento tamanho deve ser uma constante e determina
quantos dados podem ser armazenados:
CreateDatalog(100); // registro de dados de 100 valores
Pode-se adcionar valores ao registro de dados usando AddToDatalog(valor).
Quando o registro de dados transmite ao computador, este mostrará ambos, o valor e a
fonte do valor (temporizador, variável, etc). O registro de dados suporta, diretamente, as
seguintes fontes de dados: temporizadores, valores de sensores, variáveis, e o relógio de
sistema. Outros tipos de dados (como constantes ou números aleatórios) também podem
ser armazenados, mas neste caso NQC primeiro atribuirá o valor a uma variável e logo a
armazenará. Os valores poderão ser, fielmente, armazenados no datalog, mas a origem
do dado ficará desvirtuada.
AddToDatalog(Timer(0)); // Atribuir temporizador 0 ao registro de
// dados
AddToDatalog(x); // Atribuir variável x
AddToDatalog(7); // Atribuir 7
se verá como uma variável
O RCX não pode ler, por si só, os valores do registro de dados. O registro de
dados deve ser transferido a um computador. As especificidades da transferência do
registro de dados depende do meio no qual se está utilizando NQC. Por exemplo, na
linha de comandos de NQC os seguintes comandos transferirão e imprimirão o registro
de dados:
nqc
datalog
nqc
datalog_full
45
____________________Guia do programador de NQC________________________
CreateDatalog(tamanho)
Função RCX
Cria um registro de dados do tamanho especificado (que deve ser constante). Um
tamanho de 0 elimina o anterior sem criar um novo.
CreateDatalog(100); //registro de dados de 100 pontos
AddToDatalog(valor)
Função RCX
Atribui o valor, que pode ser uma expressão, ao registro de dados. Se o registro
de dados está cheio a chamada não tem efeito.
AddToDatalog(x);
UploadDatalog(começo, contador)
Função RCX
Inicia e transmite um número de dados igual ao contador, começando pelo valor
de começo. Este comando não é muito útil, já que o ordenador é o que normalmente
começa a transmissão.
UploadDatalog(0,100); //transmite o registro dos primeiros
// 100 pontos
3.11. Características gerais
Wait(tempo)
Função
Todos
Faz parar uma tarefa durante um tempo especificado (em centésimos de
segundo). O argumento tempo pode ser uma expressão ou uma constante:
Wait(100);
//Espera durante um segundo
Wait(Random(100));
//Espera durante um tempo aleatório de
//até 1 segundo.
StopAllTasks( )
Função
Todos
Detém todas as tarefas que estão se executa. Isto fará parar o programa,
completamente, portanto, qualquer comando que siga, este será ignorado.
StopAllTasks();
Random(n)
//detém o programa
Valor Todos
Devolve um número aleatório entre 0 e n. n deve ser uma constante.
x = Random(10);
SetRandomSeed(n)
Função RCX2, Spy
Pré-seleciona o gerador de números aleatórios com n. n pode ser uma expressão.
SetRandomSeed(x);
//pré-seleciona com o valor de x
46
____________________Guia do programador de NQC________________________
SetSleepTime(minutos)
Função
Todos
Estabelece o número de minutos (que deve ser constante) que estará ativo até
que se durma. Especificando 0 minutos desabilita esta opção.
SetSleepTime(5);
//Dorme ao final de 5 minutos
SetSleepTime(0);
//Desabilita o tempo para que se durma
SleepNow(n)
Função
Todos
Forçar a que se durma. Só funciona se o tempo para que se durma não é zero.
SleepNow(); //Irá dormir
3.12. Características específicas do RCX
Program( )
Valor RCX
Número do programa que está selecionado.
x = Program();
SelectProgram(n)
Função
RCX2
Seleciona o programa, especificado, para que comece a executar-se. Tem que
levar em conta que os programas estão numerados de 0 ao 4 (não de 1 a 5 como se
mostra no LCD).
SelectProgram(3);
BatteryLevel( )
Valor RCX2
Devolve o nível de bateria em milivolts.
x = BatteryLevel();
FirmwareVersion( )
Valor RCX2
Devolve a versão do firmware como um inteiro. Por exemplo, a versão 3.2.6 é
326.
x = FirmwareVersion();
Watch( )
Valor RCX
Devolve o valor do relógio do sistema em minutos.
x = Watch();
47
____________________Guia do programador de NQC________________________
SetWatch(horas, minutos)
Função RCX
Estabelece no relógio de sistema, um número específico de horas e minutos.
Horas deve ser um número constante entre 0 e 23, ambos incluídos. Minutos deve ser
uma constante entre 0 e 59, ambos incluídos.
SetWatch(3,15);
//Estabelece o relógio em 3:15
3.13. Características específicas do Scout
SetScoutRules(movimento, tato, luz, tempo, fx)
Função Scout
Estabelece as regras usadas pelo Scout no modo stand-alone.
ScoutRules(n)
Valor Scout
Devolve o valor da configuração de uma das regras. n deve ser um número
constante entre 0 e 4.
x = ScoutRules(1);
// Obter a regra #1
SetScoutMode(modo)
Função RCX
Põe o Scout no modo stand-alone(0) ou power (1). Dentro de um programa só
tem sentido utilizá-lo para pôr no modo stand-alone, já que para que possa se executar
um programa NQC deve estar no modo power.
SetEventFeedback(eventos)
Função Scout
Estabelece que eventos devem ser acompanhados por um som.
SetEventFeedback(EVENT_1_PRESSED);
EventFeedback()
Valor Scout
Devolve os eventos que estão acompanhados de um som.
x = eventFeedback();
SetLight(modo)
Função Scout
Controla o LED do Scout. Modo deve ser LIGHT_ON ou LIGHT_OFF.
SetLight(LIGHT_ON);
// Acender
LED
3.14. Características específicas do CyberMaster
CyberMaster oferece nomes alternativos para os sensores: SENSOR_L,
SENSOR_M e SENSOR_R. Também oferece nomes alternativos para as saídas:
OUT_L, OUT_R, OUT_X. No mais, os dois motores internos têm tacômetros, que
48
____________________Guia do programador de NQC________________________
medem clicks e velocidade conforme o motor gira. Há uns 50 clicks por revolução. Os
tacômetros podem ser usados, por exemplo, para criar um robô que possa detectar se
houve um choque com um objeto sem usar um sensor externo. Os tacômetros têm um
valor máximo de 32767 e não diferenciam entre ambos sentidos. Também contará se, se
gira o motor com a mão, inclusive se não há nenhum programa funcionando.
Drive(motor0,motor1)
Função
CyberMaster
Liga ambos os motores no nível de potência especificado. Se a potência é
negativa, o motor girará no sentido inverso. Equivale ao seguinte código:
SetPower(OUT_L,abs(power0));
SetPower(OUT_R,abs(power1));
if(power0<0)
{SetDirection(OUT_L,OUT_REV)}
else
{SetDirection(OUT_L,OUT_FWD)}
if(power1<0)
{SetDirection(OUT_R,OUT_REV)}
else
{SetDirection(OUT_R,OUT_FWD)}
SetOutput(OUT_L+OUT_R,OUT_ON);
OnWait(motor,n,tempo)
Função
CyberMaster
Liga os motores especificados, todos com a mesma potência e então espera
durante o tempo dado. O tempo se dá em décimos de segundo, com um máximo de 255
(25.5 segundos). Equivale ao seguinte código:
SetPower(motores,abs(power));
if(power<0)
{SetDirection(motores,OUT_REV)}
else
{SetDirection(motores,OUT_FWD)}
SetOutput(motores,OUT_ON);
Wait(tiempo*10);
OnWaitDifferent(motores,n0,n1,n2,tempo)
Função
CyberMaster
Como OnWait(), exceto que se podem dar diferentes potências para cada motor.
ClearTachoCounter(motores)
Função
CyberMaster
Põe a zero o tacômetro do motor especificado.
49
____________________Guia do programador de NQC________________________
TachoCount(n)
Valor CyberMaster
Devolve o valor do tacômetro do motor especificado.
TachoSpeed(n)
Valor CyberMaster
Devolve o valor da velocidade do tacômetro do motor especificado. A
velocidade é quase constante para um motor em vazio a qualquer velocidade, com um
valor máximo de 90 (este será menor conforme as baterias perdem potência). O valor
diminui conforme a carga no motor aumenta. Um valor de 0 indica que o motor está
bloqueado.
ExternalMotorRunning( )
Valor CyberMaster
Isto é na realidade, a medida do estado atual do motor. Os valores devolvidos
tendem a flutuar um pouco, mas, em geral, são os seguintes para um motor sem carga:
0 o motor está em floating
1 o motor está apagado
<=7 o motor gira ao redor desse nível de potência. Aqui é onde o valor flutua mais. Em
qualquer caso, deve-se saber que nível de potência foi establecido no motor. O
valor incrementa conforme a carga no motor aumenta, e um valor entre 260 e 300
indica que o motor está bloqueado.
AGC( )
Valor CyberMaster
Devolve o valor do AGC (controle automático de ganho) no receptor de rádiofreqüência. Isto pode ser usado para dar uma medida da distância, um pouco inexata,
entre o CyberMaster e o transmissor de rádio-freqüência.
x = AGC();
4. Detalhes técnicos
Esta seção explica algumas das características de baixo nível de NQC. Em geral,
estes mecanismos devem ser usados somente como último recurso, já que podem trocar
em futuras versões. A maioria dos programadores nunca necessitaram utilizar estas
características descritas abaixo
são utilizadas principalmente na criação do arquivo
API de NQC.
4.1. A instrução asm
A instrução asm é utilizada para definir a maioria das chamadas da API de NQC.
A sintaxe desta instrução é:
50
____________________Guia do programador de NQC________________________
asm {ítem1, ítem2, ... ítemN}
onde ítem é um dos seguintes
expressão_constante
&expressão
&expressão: restrição
A instrução emite simplesmente o valor de cada um dos itens como bytecodes
raw (os 8 bits menores do valor constante). Por exemplo, o arquivo API define a
seguinte função:
Void ClearMessage() {asm{0x90};}
Quando ClearMessage() é chamado por um programa, o valor 0x90 é emitido
como um bytecode.
Muitas funções API tomam argumentos, e estes argumentos devem ser
codificados em uma direção apropriada, para o intérprete de bytecode. No caso mais
geral, uma direção constante o código fonte seguido de um valor de dois bytes (de
menor importância o primeiro byte): Os códigos fonte estão explicados na
documentação do SDK disponível de LEGO.
No entanto, é desejável codificar o valor de outro modo
por exemplo, utilizar
só um valor de um só byte depois do código fonte, omitir o código fonte, ou permitir
usar só certas fontes. Uma restrição pode ser usada para controlar como está formada a
direção. Uma restrição é um valor constante de 32 bit. Os 24 bits menores formam uma
máscara indicando que fontes são válidas (o bit 0 deve ser estabelecido para permitir a
fonte 0, etc).
Os 8 bits superiores incluem o formato dos flags para a direção. Deve-se ter em
conta que quando não se especifica uma restrição, é o mesmo que usar o restritor 0
(nenhuma restrição na fonte, e o formato da fonte seguido de um valor de dois bytes). O
arquivo API define as seguintes constantes que podem ser usadas para usar restritores:
#define __ASM_SMALL_VALUE 0x01000000
#define __ASM_NO_TYPE 0x02000000
#define __ASM_NO_LOCAL 0x04000000
#if __RCX==2
// não restrição
#define __ASM_SRC_BASIC 0
#define __ASM_SRC_EXT 0
#else
51
____________________Guia do programador de NQC________________________
#define __ASM_SRC_BASIC 0x000005
#define __ASM_SRC_EXT 0x000015
#endif
O flag __ASM_SMALL_VALUE indica que o valor de um byte deve ser usado
em vez de um valor de dois bytes. O flag __ASM_NO_TYPE indica que o código fonte
deve ser omitido. O flag __ASM_NO_LOCAL especifica que as variáveis locais não são
uma fonte legal para a expressão. Deve-se ter em conta que o firmware de RCX2 é
menos restrito que os outros intérpretes, pela a definição de __ASM_SRC_BASIC e
__ASM_SRC_EXT são menos estritas no caso do RCX2.
O arquivo de definição de API para NQC contém numerosos exemplos do uso
de restritores dentro de uma instrução ASM. Está usando-se uma versão de linha de
comandos de NQC, pode editar o arquivo API teclando o seguinte comando:
nqc
api
4.2. Fontes de dados
Os intérpretes do bytecode usam diferentes fontes de dados para representar
diferentes tipos de dados (constantes, variáveis, números aleatórios, valores de sensores,
etc). As fontes específicas dependem de que tipo de dispositivo se está usando e estam
descritas na documentação do SDK de LEGO.
NQC oferece um operador especial para representar uma fonte de dados:
@ constant
O valor desta expressão é a fonte de dados descrita pela constante. Os 16 bits da
constante representam o valor dos dados, e os seguintes 8 bits são o código fonte. Por
exemplo, o código fonte para um número aleatório é 4, portanto a expressão para um
número aleatório entre 0 e 9 seria:
@ 0x40009
O arquivo API de NQC define um número de macros que usam o operador @
transparente para o programador. Por exemplo, no caso de números aleatórios:
# defineRandom(n) @(0x40000 + (n))
Adverte-se que desde que a fonte 0 é um espaço de variável global, as posições
de memória global podem ser referenciadas de modo numérico: @0 se refere a posição
0. Se por alguma razão necessita ter um controle sobre onde se armazenam as variáveis,
então deve usar #pragma reserve para ordenar a NQC que não use estas, e então acessa,
52
____________________Guia do programador de NQC________________________
manualmente, com o operador @. Por exemplo, o seguinte código reserva o espaço 0 e
cria uma macro para chamar x:
#pragma reserve 0
#define x (@0)
Por causa de como foi implementado o sensor, é necessário converter o
sensor fonte de dados em um índice de sensor, para uso em macros tal como
SensorValueRaw ().
A expressão
de
__sensor
pode
ser
usada
para
fazer
isto:
#define SensorValueRaw(n) @ (0xc0000 + (__sensor(n)))
53
____________________Guia do programador de NQC________________________
5. Nota do tradutor
Meu nome é Ricardo Ortins, sou estudante de Processamento de Dados e Membro
fundador do grupo de robótica RobotecnoTeam em Salvador (Ba).
No intuito de ter ferramentas de aprendizagem e, por ter percebido a carência
existente de conteúdo acerca de NQC em nossa língua, traduzi este documento também
como parte de um projeto de robótica e sistema LEGO.
Na certeza da sua utilidade, fico ao inteiro dispor para posteriores contatos que só
farão aumentar o conhecimento e a troca de experiências acerca deste assunto.
Gostaria de agradecer a DAVE BAUM por ter desenvolvido essa linguagem de
programação, NQC, para LEGO e, ter-me concedido a autorização para realização da
tradução deste documento.
Gostaria de agradecer a Ludmilla e Graça Fonsêca, e todas as pessoas envolvidas
neste projeto, por todo apoio necessário para que a tradução fosse possível e fiel ao
original.
CONTATOS:
E-mail pessoal: [email protected]
Site: http://br.groups.yahoo.com/group/RobotecnoTeam/
54