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