Escolar Documentos
Profissional Documentos
Cultura Documentos
Department of Computer Science Utrecht University P.O. Box 80.089, 3508 TB Utrecht the Netherlands
Traduzido para a lngua portuguesa por Anderson Grandi Pires em 23 de maro de 2008 Professor do Centro Federal de Educao Tecnolgica de Minas Gerais (CEFET-MG) Campus III Leopoldina Minas Gerais Brasil
Prefcio
Os kits de robs Lego Mindstorms e CyberMaster so novos maravilhosos brinquedos em que uma ampla variedade de robs podem ser construdos e programados para fazer todo tipo de tarefa complicada. Infelizmente, o software distribudo juntamente com tais kits , apesar de visualmente atrativo, bastante limitado no que diz respeito funcionalidade. Desse modo, tal software pode ser utilizado somente para tarefas simples. Para explorar todo o potencial dos robs, faz-se necessrio um ambiente de programao diferente. NQC uma linguagem de programao, escrita por Dave Baum, projetada especialmente para os robs Lego. Caso voc nunca tenha escrito um programa antes, no se preocupe. NQC realmente muito fcil de usar e este tutorial o ensinar tudo a este respeito. Atualmente, programar robs com NQC muito mais fcil do que programar um computador normal, sendo essa uma maneira fcil de se tornar um programador. Para tornar a tarefa de programar realmente fcil, existe o RCX Command Center. Este utilitrio o ajuda a escrever seus programas, envi-los aos robs, iniciar e parar os robs. RCX Command Center funciona quase como um processador de textos, porm com algumas funcionalidades extras. Este tutorial usar o RCX Command Center (verso 3.0 ou superior) como ambiente de programao. Voc pode efetuar o download desse utilitrio, livremente, a partir do seguinte endereo eletrnico: http://www.cs.uu.nl/people/markov/lego/ RCX Command Center roda sobre a plataforma Windows (verso 95, 98 e NT). (Voc deve executar o software que vem com o kit Lego Mindstorms pelo menos uma vez, antes de executar o RCX Command Center. O software que acompanha o kit instala certos componentes que o RCX Command Center utiliza.) A linguagem NQC pode tambm ser usada sobre outras plataformas. Voc pode baix-lo na internet no endereo: http://www.enteract.com/~dbaum/lego/nqc/ A maior parte deste tutorial tambm se aplica a outras plataformas (assumindo que se esteja usando o NQC verso 2.0 ou superior), exceto pelo fato de que algumas ferramentas so perdidas, alm da perda da funcionalidade de realce de cor no cdigo-fonte. Neste tutorial eu assumo que voc tenha o kit de robs Lego Mindstorms. A maior parte dos contedos tambm se aplica ao kit de robs CyberMaster, contudo algumas funcionalidades no se encontram disponveis para este ltimo. Alm disso, para o kit de robs CyberMaster, os nomes dos motores (por exemplo) so diferentes, o que torna necessrio modificar os exemplos um pouco para faz-los funcionar.
Agradecimentos
Eu gostaria de agradecer a Dave Braum por ter desenvolvido o NQC. Gostaria de agradecer tambm a Kevin Saddi por ter escrito a primeira verso da primeira parte deste tutorial.
-2-
Sumrio
Prefcio ___________________________________________________________________ 2
Agradecimentos ______________________________________________________________________ 2
V. Sensores _______________________________________________________________ 16
Esperando por um sensor ______________________________________________________________ Respondendo a um sensor de toque ______________________________________________________ Sensores de luz ______________________________________________________________________ Resumo ____________________________________________________________________________ Tarefas_____________________________________________________________________________ Sub-rotinas _________________________________________________________________________ Funes inline _______________________________________________________________________ Definindo macros ____________________________________________________________________ Resumo ____________________________________________________________________________ 16 16 17 18 19 20 20 21 22
-3-
-4-
Construindo um rob
O rob que utilizaremos por todo este tutorial uma verso simplificada do rob top-secret que descrito nas pginas 39 a 46 do material contructopedia que acompanha o kit. Utilizaremos somente a base (chassi). Remova toda a frente, incluindo os dois braos e os sensores de toque. Alm disso, conecte os motores ligeiramente diferentes, de modo que os cabos conectados no RCX fiquem dispostos lateralmente. Isto importante para que o rob mova na direo correta. O rob parecer tal como a figura abaixo:
Tenha certeza de que a torre de transmisso de dados (Infrared Tower) esteja conectada corretamente ao computador e que esteja configurada para longo alcance (long range). (Voc pode querer checar com o software Robotics Invention System RIS se o rob est funcionando adequadamente)
-5-
A interface parecida com aquelas existentes em editores de texto, com os menus usuais e botes para abrir, salvar, imprimir, editar arquivos, etc. Alm disso, existem menus especiais para compilar e descarregar os programas nos robs, alm daqueles disponveis para se obter informaes a respeito do rob. Voc pode ignorar os ltimos menus por enquanto. Iremos escrever um novo programa. Pressione o boto New File para criar uma nova janela vazia.
Escrevendo o programa
Digite as instrues abaixo no novo programa:
task main() { OnFwd(OUT_A); OnFwd(OUT_C); Wait(400); OnRev(OUT_A+OUT_C); Wait(400); Off(OUT_A+OUT_C); }
Pode parecer um pouco complicado a princpio, ento vamos analis-lo. Programas em NQC consistem de tarefas (task). Nosso programa tem somente uma tarefa de nome main (tarefa principal). Cada programa necessita de uma tarefa nomeada main, que ser automaticamente executada pelo rob. Voc aprender mais a respeito de tarefas no Captulo VI. Uma tarefa consiste de um conjunto de comandos, tambm referenciados como instrues (statements). Existem chaves em torno das instrues, o que deixa claro que aquelas instrues pertencem a uma determinada tarefa (Um par de chaves define um bloco de comandos). Cada instruo finaliza com um ponto-e-vrgula. Desse modo, torna-se claro onde uma instruo finaliza e onde a prxima comea. De maneira geral, uma tarefa se parece com a seguinte estrutura:
-6-
Nosso programa (pgina anterior) tem seis comandos. Daremos uma olhada em cada um destes:
OnFwd(OUT_A);
Este comando diz ao rob para habilitar (energizar) a sada A, isto , o motor conectado sada A do RCX girar para frente. Ele mover com a velocidade mxima, a menos que a velocidade seja configurada antes da execuo deste comando. Posteriormente veremos como fazer isso.
OnFwd(OUT_C);
Semelhante instruo anterior, porm agora girar o motor C. Aps a execuo desses dois comandos, ambos os motores estaro girando e o rob se move para frente.
Wait(400);
Agora hora de esperar por um perodo de tempo. Esta instruo nos diz para aguardar pelo perodo de 4 segundos. O argumento, isto , o nmero entre parnteses, define o nmero de ticks. Cada tick corresponde a 1/100 segundos. Ento voc pode, de maneira bastante precisa, dizer ao programa quanto tempo aguardar. Assim sendo, pelo perodo de 4 segundos o programa no faz nada, embora o rob continue se movendo para frente.
OnRev(OUT_A+OUT_C);
Tendo o rob se movimentado o suficiente para frente, diremos a ele para se mover no sentido reverso, ou seja, para trs. Observe que ns podemos configurar ambos os motores de uma nica vez, utilizando OUT_A+OUT_C como argumento. Ns poderamos ter combinado as duas primeiras instrues desta maneira.
Wait(400);
E, finalmente, desligamos ambos os motores. Este todo o programa. Ele move os motores para frente por 4 segundos, move os motores para trs outros 4 segundos e finalmente os desliga. Voc provavelmente observou as cores quando estava digitando o programa. Elas aparecem automaticamente. Tudo que est em azul so comandos para o rob, ou uma indicao de um motor ou outra coisa que o rob conhece. A palavra task est em negrito por ser uma importante palavra-chave (reservada) em NQC. Outras importantes palavras aparecem em negrito, conforme veremos a seguir. As cores so teis para mostrar que voc no cometeu algum erro enquanto estava digitando o programa.
Executando o programa
Uma vez que um programa tenha sido escrito, ele precisa ser compilado (isto , transformado em um cdigo que o rob possa entender e executar) e enviado para o rob usando a torre de transmisso de dados (comumente denominado de processo de descarregar o programa). Existe um boto que efetua as duas tarefas (veja a figura acima). Pressione este boto e, assumindo que no foram inseridos erros na digitao, o programa ir compilar corretamente e ser descarregado no rob. (Caso existam erros no programa, tais erros sero notificados; veja abaixo.) Agora voc pode executar o programa. Para isso, pressione o boto verde (run) do RCX ou, de maneira mais fcil, pressione o boto run da janela do RCX Command Center (ver figura acima). O rob faz aquilo que esperado? Caso no, provavelmente os cabos foram conectados de maneira errada.
Erros no programa
Quando estamos digitando um cdigo de um programa existe a possibilidade de cometermos erros de digitao. O compilador verifica os erros e os relata na parte inferior da janela do RCX Command Center, conforme apresentado abaixo:
-7-
Ele automaticamente seleciona o erro (cometemos um equvoco ao digitar o nome do motor). Quando existem mais erros, voc pode clicar nas mensagens de erros para localiz-los. Observe que erros existentes no incio de um programa podem acarretar em erros em outros locais do cdigo. Ento, o melhor a fazer corrigir alguns erros e compilar o programa novamente. Observe, tambm, que a cor associada s instrues de cdigo ajuda bastante a evitar erros. Por exemplo, na ltima linha foi digitado Of ao invs de Off. Devido ao fato deste comando ser desconhecido, o mesmo no se apresenta na cor azul. Existem alguns erros que no so identificados pelo compilador. Caso tenhamos digitado OUT_B isso teria passado despercebido devido ao fato de que tal sada existe (mesmo sabendo que ns no a usamos no rob). Ento, caso o rob exiba um comportamento inesperado, existe ainda algum erro no seu programa.
Modificando a velocidade
Como observado, o rob se move de maneira bastante rpida. Por padro, o rob se move to rpido quanto ele pode. Para modificar esta velocidade voc pode utilizar o comando SetPower(). A potncia um nmero entre 0 e 7. O nmero 7 representa a velocidade mais rpida, enquanto que 0 representa a mais devagar (mas o rob ainda se move). Aqui est uma nova verso do nosso programa nos quais o rob se move lentamente:
task main() { SetPower(OUT_A+OUT_C,2); OnFwd(OUT_A+OUT_C); Wait(400); OnRev(OUT_A+OUT_C); Wait(400); Off(OUT_A+OUT_C); }
Resumo
Neste captulo voc escreveu seu primeiro programa em NQC, usando o RCX Command Center. At este ponto, voc aprendeu a escrever um programa, descarreg-lo no rob e coloc-lo em execuo. O software RCX Command Center pode fazer outras coisas mais. Para descobrir, leia a documentao que acompanha o software. Este tutorial referente, principalmente, linguagem NQC e somente menciona caractersticas do RCX Command Center quando realmente forem necessrias. Voc tambm aprendeu alguns importantes aspectos da linguagem NQC. Inicialmente, voc aprendeu que cada programa tem uma tarefa denominada main, que sempre executada pelo rob. Depois voc aprendeu os quatro comandos referentes a motores mais importantes: OnFwd(), OnRev(), SetPower() e Off(). Por fim, voc aprendeu a respeito do comando Wait().
-8-
Fazendo curvas
Voc pode instruir o rob a fazer curvas por meio da paralisao ou da rotao reversa de um dos dois motores. Abaixo temos um exemplo. Digite e compile o cdigo, transfira-o para o rob e ento execute. Ele deve se mover para frente um pouco e ento girar em um ngulo de 90 graus.
task main() { OnFwd(OUT_A+OUT_C); Wait(100); OnRev(OUT_C); Wait(85); Off(OUT_A+OUT_C); }
Pode acontecer de voc ter que tentar alguns nmeros prximos de 85 na segunda chamada ao comando Wait() para fazer com que o rob gire precisamente em 90 graus. Isto depende do tipo de superfcie nos quais o rob est sendo utilizado. Ao invs de modificar isto no corpo da tarefa main, mais fcil atribuir um nome para este nmero. Em NQC voc pode definir valores constantes como mostrado no seguinte programa:
#define TEMPO_DE_MOVIMENTACAO #define TEMPO_DE_CURVATURA task main() { OnFwd(OUT_A+OUT_C); Wait(TEMPO_DE_MOVIMENTACAO); OnRev(OUT_C); Wait(TEMPO_DE_CURVATURA); Off(OUT_A+OUT_C); } 100 85
As duas primeiras linhas definem duas constantes. A partir da definio, as constantes podero ser utilizadas por todo o programa. Definir constantes bom por duas razes: torna o programa mais legvel e facilita as mudanas de valores. Observe que o RCX Command Center estipula uma cor prpria para as instrues define. Como ser visto no Captulo VI, voc poder definir outras coisas alm de constantes.
Repetindo comandos
Vamos agora escrever um programa que faz o rob se mover em um caminho em forma de um quadrado. Mover-se em forma de um quadrado significa: mover para frente, girar 90 graus, mover para frente de novo, girar 90 graus, etc. Ns poderamos repetir o pedao de cdigo acima quatro vezes, mas isto pode ser feito de maneira mais fcil com a estrutura de repetio repeat.
-9-
#define TEMPO_DE_MOVIMENTACAO #define TEMPO_DE_CURVATURA task main() { repeat(4) { OnFwd(OUT_A+OUT_C); Wait(TEMPO_DE_MOVIMENTACAO); OnRev(OUT_C); Wait(TEMPO_DE_CURVATURA); } Off(OUT_A+OUT_C); }
100 85
O nmero entre parnteses na instruo repeat indica com que freqncia as coisas devero ser repetidas. As instrues que devem ser repetidas so colocadas entre chaves (definindo o bloco de comandos associado instruo repeat), semelhantemente ao que se faz em uma tarefa. Observe no programa acima que tambm endentamos as instrues (insero de espaos na margem esquerda). Isso no necessrio, mas torna o programa mais legvel. Como um exemplo final, faremos o rob se movimentar 10 vezes em um caminho em forma de um quadrado. Aqui est o programa:
#define TEMPO_DE_MOVIMENTACAO #define TEMPO_DE_CURVATURA task main() { repeat(10) { repeat(4) { OnFwd(OUT_A+OUT_C); Wait(TEMPO_DE_MOVIMENTACAO); OnRev(OUT_C); Wait(TEMPO_DE_CURVATURA); } } Off(OUT_A+OUT_C); } 100 85
Existem agora duas instrues repeat, uma dentro da outra. Ns chamamos isso de instrues repeat aninhadas. Voc pode aninhar quantas instrues repeat voc desejar. D uma olhada criteriosa nas chaves e na endentao utilizada no programa. A tarefa principal inicia na primeira abertura de chaves e finaliza na ltima chave. A primeira instruo repeat inicia na segunda abertura de chaves e finalize na quinta chave. A segunda instruo repeat inicia na terceira chave e finalize na quarta. Como se pode ver, as chaves sempre aparecem em pares e as instrues existentes entre as chaves so endentadas.
Incluindo comentrios
Uma boa maneira para tornar seu programa mais legvel por meio da incluso de comentrios. Caso voc coloque // (duas barras) em uma linha, todo cdigo inserido aps estes smbolos (na mesma linha) sero ignorados e podero ser utilizados como comentrios. Um comentrio mais longo poder ser includo entre os smbolos /* e */ (comentrio de mltiplas linhas). No RCX Command Center os comentrios se apresentam na cor verde. O programa completo se parece conforme apresentado a seguir:
- 10 -
/*
Este programa faz o rob se movimentar por 10 quadras */ #define TEMPO_DE_MOVIMENTACAO #define TEMPO_DE_CURVATURA 100 //Tempo de movimentao para frente 85 //Tempo de curvatura em 90 graus
task main() { repeat(10) // Percorre as 10 quadras { repeat(4) { OnFwd(OUT_A+OUT_C); Wait(TEMPO_DE_MOVIMENTACAO); OnRev(OUT_C); Wait(TEMPO_DE_CURVATURA); } } Off(OUT_A+OUT_C); // Agora, desliga os motores }
Resumo
Neste captulo voc aprendeu a usar a instruo repeat e inserir comentrios. Alm disso, voc viu o propsito de se utilizar chaves aninhadas e o uso de endentao. Com tudo o que foi aprendido, voc pode fazer o rob se mover por todo tipo de caminho. Um bom exerccio seria tentar escrever algumas variaes dos programas deste captulo antes de avanar para o prximo.
- 11 -
// inicializa a varivel
// incrementa a varivel
As linhas interessantes esto indicadas com comentrios. Primeiro definimos uma varivel por intermdio da utilizao da palavra-chave int, seguida por um nome que ns mesmos escolhemos. (Normalmente usamos letras minsculas para nomes de variveis e letras maisculas para nomes de constantes, mas no necessrio.) O nome deve iniciar com uma letra, mas pode conter nmeros e o caractere especial sublinhado ( _ ). Nenhum outro smbolo permitido. (O mesmo se aplica a constantes, nomes de tarefas, etc.) A palavra-chave em negrito int significa inteiro. Somente nmeros inteiros podem ser armazenados nesse tipo de varivel. Na segunda linha interessante ns inicializamos a varivel com o valor 20 (atribumos o valor 20 varivel). A partir deste ponto, caso voc use a varivel ela valer 20. Agora, no corpo de repeat, observe que usamos a varivel tempo_de_movimentacao para indicar o tempo de espera e no final do loop incrementamos o valor da varivel em 5 unidades. Assim sendo, na primeira vez o rob espera por 20 ticks, na segunda vez 25, na terceira 30, etc. Alm de adicionar valores para uma varivel, podemos multiplicar uma varivel por um nmero utilizando *=, subtrair utilizando -= e dividir usando /=. (Observe que para a diviso o resultado arredondado para o nmero inteiro mais prximo.) Voc pode tambm adicionar uma varivel a outra e desenvolver expresses mais complicadas. Temos aqui alguns exemplos:
- 12 -
int aaa; int bbb, ccc; task main() { aaa = 10; bbb = 20 * 5; ccc = bbb; ccc /= aaa; ccc -= 5; aaa = 10 * (ccc + 3); }
Observe nas duas primeiras linhas que podemos definir vrias variveis em uma nica instruo. Poderamos, tambm, ter combinado as trs variveis em uma nica linha.
Nmeros randmicos
Em todos os programas acima definimos exatamente o que seria suposto que o rob fizesse. Mas as coisas se tornam mais interessantes quando o rob est apto a fazer coisas que ns no sabemos, ou seja, de maneira imprevisvel. Queremos alguma aleatoriedade nas movimentaes do rob. Em NQC voc pode criar nmeros randmicos, ou seja, aleatrios. O seguinte programa usa isso para deixar o rob se movimentar de maneira aleatria. O rob constantemente se movimenta para frente uma quantidade de tempo aleatria e ento efetua um giro tambm aleatrio.
int tempo_de_movimentacao, tempo_de_curvatura; task main() { while(true) { tempo_de_movimentacao = Random(60); tempo_de_curvatura = Random(40); OnFwd(OUT_A+OUT_C); Wait(tempo_de_movimentacao); OnRev(OUT_A); Wait(tempo_de_curvatura); } }
O programa define duas variveis e ento atribui a elas valores aleatrios. Random(60) significa que um nmero entre 0 e 60 (inclusives) ser gerado (0 ou 60 so valores possveis). A cada chamada a Random() os nmeros sero diferentes. (Observe que poderamos evitar o uso de variveis ao escrever, por exemplo, Wait(Random(60)).) Voc pode observar um novo tipo de loop aqui. Ao invs de utilizar a instruo repeat, escrevemos while(true). A estrutura de repetio while repete as instrues associadas a ela (seu bloco de comandos) enquanto o resultado da condio entre parnteses for verdadeiro. A palavra especial true sempre verdadeiro, assim as instrues entre chaves executaro indefinidamente, tanto quanto queiramos. Voc aprender mais a respeito da instruo while no Captulo IV.
Resumo
Neste captulo voc aprendeu a respeito de variveis. Variveis so muito teis, mas, devido s restries do rob, elas so um pouco limitadas. Voc pode definir somente 32 variveis e elas podem armazenar somente inteiros. Mas para muitas tarefas realizadas por robs isso bom o suficiente. Voc tambm aprendeu a criar nmeros randmicos, tais que se pode dar ao rob um comportamento imprevisvel. Finalmente, vimos o uso da estrutura de repetio while para criar um loop infinito que executa indefinidamente.
- 13 -
A instruo if
s vezes voc deseja que uma parte especfica do seu programa seja executada somente em certas situaes. Em casos semelhantes a esse que a instruo if utilizada. Deixe-me mostrar um exemplo. Iremos modificar novamente o programa que temos utilizado, mas com uma abordagem diferente. Ns queremos que o rob se mova ao longo de uma linha reta e ento efetue um giro para a esquerda ou para a direita. Para fazer isso, ns utilizaremos novamente os nmeros randmicos. Ns pegaremos um nmero randmico entre 0 e 1 (inclusives), isto , o nmero 0 ou o nmero 1. Se o nmero for 0 efetuaremos um giro para a direita; caso contrrio, efetuaremos um giro para a esquerda. Veja o programa:
#define #define TEMPO_DE_MOVIMENTACAO TEMPO_DE_CURVATURA 100 85
task main() { while(true) { OnFwd(OUT_A+OUT_C); Wait(TEMPO_DE_MOVIMENTACAO); if (Random(1) == 0) { OnRev(OUT_C); } else { OnRev(OUT_A); } Wait(TEMPO_DE_CURVATURA); } }
A instruo if se parece um pouco com a instruo while. Caso a condio entre parnteses seja verdadeira, a parte entre chaves ser executada. Caso falso, a parte entre as chaves aps a palavra-chave else ser executada. Vamos olhar um pouco melhor a condio que utilizamos: Random(1) == 0. Isso significa que Random(1) deve ser igual a 0 para fazer a condio verdadeira. Voc poderia pensar por que utilizamos = = ao invs de =. A razo para diferenci-lo daquelas instrues que atribuem valores a uma varivel. Voc pode comparar valores de diferentes maneiras. A seguir so apresentadas as mais importantes: == < <= > >= != igual a menor do que menor ou igual a maior do que maior ou igual a diferente (no igual a)
Voc pode combinar condies usando &&, que significa e ou ||, que significa ou. Aqui temos alguns exemplos de condies:
true sempre verdadeiro false nunca verdadeiro (falso) ttt != 3 verdadeiro quanto ttt for diferente de 3 (ttt >= 5) && (ttt <= 10) verdadeiro quando ttt estiver entre 5 e 10 (inclusives) (aaa == 10) || (bbb == 10) verdadeiro tanto se aaa ou bbb (ou ambos) forem iguais a 10
- 14 -
Observe que a instruo if tem duas partes. A parte imediatamente aps a condio, que executada quando a condio verdadeira, e a parte aps else, que executada quando a condio falsa. A palavra-chave else e a parte aps a mesma so opcionais. Ento, pode-se fazer nada caso a condio seja falsa.
A instruo do
Existe outra estrutura de controle, a instruo do. Ela tem a seguinte forma geral:
do { instrues; } while (condio);
As instrues entre as chaves aps o do so executadas enquanto a condio for verdadeira. A condio tem a mesma forma daquela utilizada nas instrues if, conforme descrito acima. Aqui temos um exemplo de um programa. O rob se movimenta de maneira aleatria por 20 segundos e ento pra.
int tempo_de_movimentacao, tempo_de_curvatura, tempo_total; task main() { tempo_total = 0; do { tempo_de_movimentacao = Random(100); tempo_de_curvatura = Random(100); OnFwd(OUT_A+OUT_C); Wait(tempo_de_movimentacao); OnRev(OUT_C); Wait(tempo_de_curvatura); tempo_total += tempo_de_movimentacao; tempo_total += tempo_de_curvatura; } while (tempo_total < 2000); Off(OUT_A+OUT_C); }
Podemos notar que neste exemplo existem duas instrues em uma nica linha. Isto permitido. Voc pode colocar tantas instrues em uma nica linha quantas voc quiser (desde que separe cada instruo por um ponto-e-vrgula). Mas por questes de legibilidade do programa, isso freqentemente no uma boa idia. Observe, tambm, que a instruo do se comporta de maneira semelhante instruo while. A diferena que na estrutura while a condio testada antes de executar suas instrues, enquanto que na estrutura do a condio testada no fim. Em uma instruo while, pode acontecer do bloco de comandos nunca ser executado, mas na instruo do o bloco de comandos executado pelo menos uma vez.
Resumo
Neste captulo vimos duas estruturas de controle novas: a instruo if e a instruo do. Juntamente com as instrues repeat e while, elas so as instrues que controlam a maneira com que o programa executado. muito importante que voc entenda o que elas fazem. Sendo assim, seria bom que voc tentasse mais alguns exemplos desenvolvidos por voc mesmo antes de continuar. Vimos, tambm, que podemos colocar vrias instrues em uma mesma linha.
- 15 -
V. Sensores
Um dos aspectos interessantes dos robs Lego que voc pode conectar a eles sensores e fazer o rob reagir a esses sensores. Antes de mostrar como fazer isto, devemos modificar o rob que temos utilizado um pouco, por intermdio da instalao de um sensor. Para isso, monte o sensor conforme apresentado na figura 4 da pgina 28 do construpedia. Voc deve fazer isto de maneira ligeiramente diferente, de tal modo que o rob se parea com a figura abaixo:
Existem duas linhas importantes aqui. A primeira delas nos diz o tipo de sensor que ns estamos usando. SENSOR_1 o nmero da entrada em que o sensor est conectado. As outras duas entradas so denominadas SENSOR_2 e SENSOR_3. SENSOR_TOUCH indica que se trata de um sensor de toque. Para o sensor de luz ns utilizaramos SENSOR_LIGHT. Aps termos especificado o tipo de sensor, o programa liga os dois motores e o rob se movimenta para frente. A prxima instruo (until) uma construo muito til. Ela aguarda at que a condio definida entre parnteses seja verdadeira. Essa condio diz que o valor do SENSOR_1 deve ser 1, o que significa que o sensor est pressionado. To logo o sensor seja liberado (deixe de estar pressionado), o valor se torna 0. Ento, esta instruo aguarda at que o sensor seja pressionado (a tarefa fica parada neste ponto aguardando a condio ser satisfeita). Quando isso ocorre os motores so desligados e a tarefa finalizada.
- 16 -
task main() { SetSensor(SENSOR_1,SENSOR_TOUCH); OnFwd(OUT_A+OUT_C); while (true) { if (SENSOR_1 == 1) { OnRev(OUT_A+OUT_C); Wait(30); OnFwd(OUT_A); Wait(30); OnFwd(OUT_A+OUT_C); } } }
Tal como apresentado no exemplo anterior, primeiro indicaremos o tipo de sensor. Em seguida o rob inicia seu movimento para frente. No loop infinito while, verificaremos continuamente se o sensor foi acionado e, em caso verdadeiro, o rob se mover para trs por um tempo de 1/3 de segundo, mover para a direita pelo mesmo perodo de tempo (1/3 de segundo) e ento continuar se movimentando para frente.
Sensores de luz
Alm dos sensores de toque, o kit Lego Mindstorms possui sensores de luz. O sensor de luz mede a quantidade de luz em uma determinada direo. Alm disso, esse sensor emite luz. Sendo assim, possvel apontar o sensor para uma determinada direo e fazer a distino entre a intensidade de luz refletida por um objeto existente naquela direo. Em particular, isso til quando se tenta fazer um rob seguir uma linha existente no cho. Isso o que faremos no prximo exemplo. Primeiramente precisamos encaixar o sensor de luz no rob de tal modo que o sensor fique posicionado no meio do rob, apontando para baixo. Conecte-o na entrada 2. Por exemplo, faa uma construo conforme ilustrado abaixo:
Precisaremos tambm da pista que vem com o kit. (Aquele pedao grande de papel com uma trilha preta nele.) A idia agora que o rob mantenha o sensor de luz sobre a trilha. Caso a intensidade do sensor de luz aumentar, significa que o sensor saiu da trilha e precisaremos ajustar a direo. A seguir apresentado um exemplo simples de como fazer isso, porm somente funciona se o rob seguir a trilha no sentido horrio.
- 17 -
#define VALOR_LIMITE 40 task main() { SetSensor(SENSOR_2,SENSOR_LIGHT); OnFwd(OUT_A+OUT_C); while (true) { if (SENSOR_2 > VALOR_LIMITE) { OnRev(OUT_C); until (SENSOR_2 <= VALOR_LIMITE); OnFwd(OUT_A+OUT_C); } } }
O programa indica, inicialmente, que o sensor 2 um sensor de luz. Em seguida, ele configura o rob para se mover para frente e entra em um loop infinito. Caso o valor lido pelo sensor seja maior do que 40 (utilizamos uma constante aqui de modo que seja fcil modificar o programa no caso de exposio do rob a ambientes com grande variedade de luminosidade) revertemos o sentido de rotao de um dos motores (OUT_C) e aguardamos at que o rob retorne trilha. Como poder ser observado quando o programa for executado, a movimentao no muito regular. Tente adicionar um comando Wait(10) antes da instruo until de modo a fazer o rob se mover de maneira melhor. Observe que o programa no funciona para a movimentao no sentido anti-horrio. Para permitir uma movimentao em caminhos arbitrrios, faz-se necessrio um programa mais complicado.
Resumo
Neste captulo foi visto como trabalhar com sensores de toque e de luz. Tambm vimos o comando until que til quando se usa sensores. Eu sugiro que voc escreva uma variedade de programas com os conceitos vistos at este momento. Voc tem agora todos os ingredientes necessrios para dar ao rob comportamentos bastante complexos. Por exemplo, tente colocar dois sensores de toque no rob, um na parte frontal esquerda e o outro na parte frontal direita, e o faa se afastar dos obstculos que porventura ele se choque. Alm disso, tente manter o rob em uma rea delimitada por uma linha preta no cho.
- 18 -
Tarefas
Um programa NQC pode possuir no mximo 10 tarefas. Cada tarefa tem um nome. Uma das tarefas deve ter o nome main (tarefa principal), e ser automaticamente executada. As outras tarefas sero executes somente quando uma tarefa em execuo pedir para elas executem, por intermdio do comando start. A partir deste ponto, as duas tarefas estaro sendo executadas simultaneamente (devido ao fato da primeira tarefa continuar em execuo). Uma tarefa em execuo pode finalizar a execuo de outra tarefa por meio do comando stop. Posteriormente, essa tarefa poder ser reiniciada novamente, mas iniciar a execuo do incio e no a partir do ponto em que foi finalizada. Deixe-me demonstrar o uso de tarefas. Coloque novamente o sensor de toque no rob. Desejamos fazer um programa em que o rob se movimenta em um caminho com a forma de um quadrado, como anteriormente. No entanto, quando o rob chocar com um obstculo ele dever reagir ao obstculo. difcil fazer isto em uma nica tarefa, devido ao fato do rob necessitar fazer duas coisas ao mesmo tempo: movimentar-se (isto , ligar e desligar os motores na hora certa) e responder aos sensores. Ento, o melhor a fazer utilizar duas estratgias para isso, onde uma delas controla o movimento em forma de quadrado e a outra reage aos sensores. Segue um exemplo.
task main() { SetSensor(SENSOR_1,SENSOR_TOUCH); start verificarSensores; start movimentarEmQuadrados; } task movimentarEmQuadrados() { while (true) { OnFwd(OUT_A+OUT_C); Wait(100); OnRev(OUT_C); Wait(85); } } task verificarSensores() { while (true) { if (SENSOR_1 == 1) { stop movimentarEmQuadrados; OnRev(OUT_A+OUT_C); Wait(50); OnFwd(OUT_A); Wait(85); start movimentarEmQuadrados; } } }
A tarefa main simplesmente define o tipo de sensor e ento inicializa as outras tarefas. Aps isso, a tarefa main finalizada. A tarefa movimentarEmQuadrados move o rob continuamente em um circuito com a forma de um quadrado. A tarefa verificarSensores verifica se o sensor de toque pressionado. Caso tenha sido, ele responde com as seguintes aes: antes de tudo ele pra a tarefa movimentarEmQuadrados. Isso muito importante. verificarSensores agora possui o controle sobre as movimentaes do rob. Em seguida, ela move o rob um pouco para trs e o faz girar. Agora ela pode iniciar novamente a tarefa
- 19 -
quadrado. muito importante lembrar que todas as tarefas que voc inicia esto sendo executadas ao mesmo tempo. Isso pode levar a resultados inesperados. O Captulo X explica esses problemas em detalhes e oferece solues para eles.
Sub-rotinas
s vezes voc precisa que um mesmo pedao de cdigo seja utilizado em vrios lugares em um programa. Nesse caso, voc pode colocar o pedao de cdigo em uma sub-rotina e lhe dar um nome. Agora voc pode executar esse pedao de cdigo simplesmente efetuando uma chamada ao seu nome dentro de uma tarefa. NQC (ou melhor, o RCX) permite at 8 sub-rotinas. Vamos ver um exemplo.
sub darVoltas() { OnRev(OUT_C); Wait(340); OnFwd(OUT_A+OUT_C); } task main() { OnFwd(OUT_A+OUT_C); Wait(100); darVoltas(); Wait(200); darVoltas(); Wait(100); darVoltas(); Off(OUT_A+OUT_C); }
Neste programa definimos uma sub-rotina que faz o rob dar voltar em torno de si mesmo. A tarefa main efetua uma chamada a essa rotina trs vezes. Observe que efetuamos uma chamada sub-rotina por meio da escrita de seu nome seguido de um par de parnteses. Isso se parece bastante com os demais comandos que temos visto. A diferena uma sub-rotina no pode definir parmetros, o que implica em nunca termos algo entre os parnteses de uma sub-rotina. So necessrios alguns esclarecimentos antes de continuarmos. Sub-rotinas so um pouco estranhas. Por exemplo, sub-rotinas no podem ser chamadas a partir de outras sub-rotinas. Por outro lado, sub-rotinas podem ser chamadas a partir de diferentes tarefas, entretanto isto no indicado. muito fcil ocorrerem problemas devido a duas tarefas distintas tentarem, ao mesmo tempo, executar uma mesma sub-rotina. Isso leva a efeitos indesejveis. Alm disso, quando se efetua chamadas a uma mesma sub-rotina a partir de tarefas distintas, devido a uma limitao do firmware do RCX, voc fica impedido de utilizar expresses complicadas (nesse caso, no se pode ter variveis locais ou efetuar clculos que requerem variveis temporrias). Ento, a menos que voc tenha certeza do que est fazendo, no efetue chamadas a uma sub-rotina a partir de diferentes tarefas!
Funes inline
Conforme citado acima, sub-rotinas causam certos problemas. A parte boa que elas so armazenadas somente uma vez no RCX. Isso gasta menos memria e, pelo fato do RCX ter pouca memria livre, isso til. Mas quando as sub-rotinas so pequenas, o melhor a fazer utilizar funes inline. Elas no so armazenadas separadamente, mas copiadas para cada lugar em que so utilizadas. Isso gasta mais memria, mas os problemas citados anteriormente relacionados a expresses complicadas deixam de existir. Alm disso, no existe um limite para a quantidade de funes inline. Definir e chamar funes inline ocorre exatamente da mesma maneira que sub-rotinas. A nica diferena a utilizao da palavra-chave void ao invs de sub. (A palavra void utilizada devido ao fato dessa mesma palavra ser usada em outras linguagens, tal como C, quando uma funo no retorna um valor. As funes em NQC no retornam valor.) Desse modo, o programa acima, com o uso de funes inline, seria assim:
- 20 -
void darVoltas() { OnRev(OUT_C); Wait(340); OnFwd(OUT_A+OUT_C); } task main() { OnFwd(OUT_A+OUT_C); Wait(100); darVoltas(); Wait(200); darVoltas(); Wait(100); darVoltas(); Off(OUT_A+OUT_C); }
Funes inline tm outra vantagem sobre sub-rotinas. Elas podem ter parmetros. Argumentos podem ser usados para passar um valor para certas variveis definidas na funo inline. Por exemplo, assuma, no exemplo acima, que podemos tornar o tempo de giro um parmetro para a funo, conforme apresentado abaixo:
void darVoltas(int tempoDeGiro) { OnRev(OUT_C); Wait(tempoDeGiro); OnFwd(OUT_A+OUT_C); } task main() { OnFwd(OUT_A+OUT_C); Wait(100); darVoltas(200); Wait(200); darVoltas(50); Wait(100); darVoltas(300); Off(OUT_A+OUT_C); }
Observe que foi especificado entre os parnteses associados funo inline o parmetro da funo. Neste caso ns indicamos que o argumento dever ser um inteiro (existem outras opes) e o nome do parmetro tempoDeGiro. Quando existem mais parmetros, os mesmos devem vir separados por vrgulas.
Definindo macros
Existe ainda outra maneira de associar um nome a pequenos pedaos de cdigo. Voc pode definir macros em NQC (no confunda com os macros do RCX Command Center). Foi visto que podemos definir constantes usando a instruo #define, dando-as um nome. Mas na verdade ns podemos definir qualquer pedao de cdigo. A seguir apresentado o mesmo programa anterior com a definio de uma macro para executar as aes existentes em darVoltas.
- 21 -
#define darVoltas OnRev(OUT_C);Wait(340);OnFwd(OUT_A+OUT_C); task main() { OnFwd(OUT_A+OUT_C); Wait(100); darVoltas; Wait(200); darVoltas; Wait(100); darVoltas; Off(OUT_A+OUT_C); }
Aps a instruo #define a palavra darVoltas representa o texto que a segue. Agora caso voc digite darVoltas, essa palavra ser substituda por este texto. Observe que o texto deve estar em uma nica linha. (Na verdade existem maneiras de colocar uma instruo #define em mltiplas linhas, mas no recomendado.) Instrues #define so realmente muito mais poderosas. Elas tambm podem ter parmetros. Por exemplo, podemos colocar o tempo de giro como um parmetro na instruo. Aqui ser apresentado um exemplo em que definimos quatro macros: uma para mover o rob para frente, uma para mover para trs, uma para virar esquerda e outra para virar direita.
#define #define #define #define virarDireita(s,t) SetPower(OUT_A+OUT_C,s);OnFwd(OUT_A);OnRev(OUT_C);Wait(t); virarEsquerda(s,t) SetPower(OUT_A+OUT_C,s);OnRev(OUT_A);OnFwd(OUT_C);Wait(t); frente(s,t) SetPower(OUT_A+OUT_C,s);OnFwd(OUT_A+OUT_C);Wait(t); tras(s,t) SetPower(OUT_A+OUT_C,s);OnRev(OUT_A+OUT_C);Wait(t);
task main() { frente(3,200); virarEsquerda(7,85); frente(7,100); tras(7,200); frente(7,100); virarDireita(7,85); frente(3,200); Off(OUT_A+OUT_C); }
muito til definir macros. Isso faz o seu cdigo mais compacto e legvel. Alm do mais, voc pode modificar seu cdigo facilmente quando, por exemplo, voc modificar as conexes dos motores.
Resumo
Neste captulo vimos o uso de tarefas, sub-rotinas, funes inline e macros. Elas tm utilidades distintas. As tarefas executam simultaneamente e tratam coisas distintas que tm que ser feitas ao mesmo tempo. Sub-rotinas so teis quando grandes pedaos de cdigo devem estar em diferentes lugares em uma mesma tarefa. Funes inline so teis quando pedaos de cdigo devem ser usados em vrios locais em diferentes tarefas, porm com um consumo adicional de memria. Finalmente, macros so muito teis para pequenos pedaos de cdigo que devem ser usados em diferentes locais. Eles tambm podem conter parmetros, o que os torna mais teis. Agora que voc chegou at aqui, voc possui todo tipo de conhecimento para fazer seu rob executar tarefas complexas. Os demais captulos deste tutorial o ensinaro a respeito de outras coisas que so importantes somente em certas aplicaes.
- 22 -
Sons pr-programados
Existem seis sons pr-programados no RCX, numerados de 0 a 5, conforme apresentado a seguir: 0 1 2 3 4 5 Pressionamento de tecla Beep beep Mudana de freqncia decrescente Mudana de freqncia crescente Som de erro (Buhhh) Mudana crescente rpida
Voc pode tocar estes sons por meio do comando PlaySound(). Aqui temos um pequeno programa que toca todos eles.
task main() { PlaySound(0); PlaySound(1); PlaySound(2); PlaySound(3); PlaySound(4); PlaySound(5); }
Voc pode se perguntar por que existem estes comandos Wait. A razo que o comando que toca o som no espera pelo seu trmino. Ele imediatamente executa o prximo comando. O RCX tem um pequeno buffer em que ele armazena sons, mas aps um tempo este buffer enche e os sons so perdidos. Isso no faz muita diferena para sons, mas muito importante para msicas, como veremos a seguir. Observe que o argumento para PlaySound() deve ser uma constante. Voc no pode utilizar uma varivel aqui!
Tocando msica
Para msicas mais interessantes, o NQC tem o comando PlayTone(). Ele tem dois parmetros. O primeiro deles a freqncia e o segundo a durao (em ticks de 1/100 segundos, tal como no comando Wait). Abaixo temos uma tabela de freqncias teis: Sound G# G F# F E D# D C# C B A# A 1 52 49 46 44 41 39 37 35 33 31 29 28 2 104 98 92 87 82 78 73 69 65 62 58 55 3 208 196 185 175 165 156 147 139 131 123 117 110 4 415 392 370 349 330 311 294 277 262 247 233 220 5 831 784 740 698 659 622 587 554 523 494 466 440 6 1661 1568 1480 1397 1319 1245 1175 1109 1047 988 932 880 7 3322 3136 2960 2794 2637 2489 2349 2217 2093 1976 1865 1760 8
Conforme citado anteriormente com relao aos sons, aqui tambm o RCX no espera uma nota terminar. Assim sendo, caso voc use vrias notas, o melhor a fazer adicionar comandos Wait entre elas (fica um pouco maior). Eis um exemplo:
- 23 -
Voc pode criar pedaos de msicas muito facilmente utilizando o RCX Piano que parte do RCX Command Center. Caso voc queira que o RCX toque uma msica enquanto se movimenta, o melhor a fazer separar uma tarefa para isso. A seguir apresentado um exemplo de um programa bastante simples onde o rob se move constantemente para frente e para trs, tocando uma msica.
task tocarMusica() { while (true) { PlayTone(262,40); PlayTone(294,40); PlayTone(330,40); PlayTone(294,40); } }
Resumo
Neste captulo voc aprendeu como fazer o RCX produzir sons e msicas. Alm disso, foi mostrado como usar uma tarefa especfica para tocar msica.
- 24 -
Parando suavemente
Quando voc utiliza o comando Off(), o motor para imediatamente, usando freios. Em NQC tambm possvel para os motores de maneira suave, sem utilizar freios. Para esse propsito utilizamos o comando Float(). s vezes isso melhor para a tarefa que o rob est executando. Utilizemos o seguinte exemplo. Primeiramente o rob pra usando freios; depois faz o mesmo sem usar os freios. Observe a diferena. (Provavelmente a diferena seja bastante sutil para o rob que estamos usando. Entretanto pode fazer uma grande diferena para outros tipos de robs.)
task main() { OnFwd(OUT_A+OUT_C); Wait(200); Off(OUT_A+OUT_C); Wait(100); OnFwd(OUT_A+OUT_C); Wait(200); Float(OUT_A+OUT_C); }
Comandos avanados
Na verdade, o comando OnFwd() faz duas coisas: ele liga o motor e configura sua direo para frente. O comando OnRev() tambm faz duas coisas: ele liga o motor e configure a direo no sentido reverso. O NQC tambm tem comandos para fazer essas coisas separadamente. Se voc quiser mudar somente uma de duas coisas, mais eficiente utilizar esses comandos em separado; isso faz uso de menos memria no RCX, mais rpido e pode resultar em movimentos mais suaves. Os dois comandos separados so SetDirection() que configura a direo (OUT_FWD, OUT_REV ou OUT_TOGGLE nos quais troca a direo atual) e SetOutput() que define o modo (OUT_ON, OUT_OFF ou OUT_FLOAT). A seguir apresentado um programa simples que faz o rob se mover para frente, para trs e para frente de novo.
task main() { SetPower(OUT_A+OUT_C,7); SetDirection(OUT_A+OUT_C,OUT_FWD); SetOutput(OUT_A+OUT_C,OUT_ON); Wait(200); SetDirection(OUT_A+OUT_C,OUT_REV); Wait(200); SetDirection(OUT_A+OUT_C,OUT_TOGGLE); Wait(200); SetOutput(OUT_A+OUT_C,OUT_FLOAT); }
Cabe observar que no incio de todos os programas, todos os motores so configurados automaticamente para se moverem para frente e a velocidade definida em 7. Sendo assim, no exemplo acima os dois primeiros comandos so desnecessrios. Existem vrios outros comandos para controlar os motores, nos quais consistem de uma abreviao da combinao dos comandos apresentados anteriormente. A seguir apresentada uma lista completa:
On(motores) Off(motores) Float(motores) Fwd(motores) Rev(motores) Toggle(motores)
Liga o motor Desliga o motor Desliga os motores sem frenagem Configura o motor na direo frente (no o coloca em movimento) Configura o motor na direo trs (no o coloca em movimento) Muda a direo do motor (frente para trs e vice-versa)
- 25 -
Configura o motor na direo frente e o coloca em movimento Configura o motor na direo trs e o coloca em movimento Liga os motores por um perodo de tempo em ticks (100 ticks = 1s) Configura a sada (OUT_ON, OUT_OFF ou OUT_FLOAT) Configura a direo da sada (OUT_FWD, OUT_REV ou OUT_TOGGLE) Configura a potncia da sada (0-7)
Este programa pode se tornar muito mais poderoso, permitindo rotaes, e tambm possivelmente incorporando um tempo de espera antes do comando Off(). Experimente voc mesmo.
Resumo
Neste captulo voc aprendeu a respeito de comandos extras disponveis relacionados a motores: Float() que para o motor de maneira suave, SetDirection() que define a direo (OUT_FWD, OUT_REV ou OUT_TOGGLE nos quais inverte a direo atual) e SetOutput() que define o modo (OUT_ON, OUT_OFF ou OUT_FLOAT). Voc viu a lista completa dos comandos relacionados a motores disponveis. Voc tambm aprendeu um truque para controlar a velocidade dos motores de uma maneira melhor.
- 26 -
- 27 -
task main() { SetSensorType(SENSOR_1,SENSOR_TYPE_TOUCH); SetSensorMode(SENSOR_1,SENSOR_MODE_PULSE); while(true) { ClearSensor(SENSOR_1); until (SENSOR_1 > 0); Wait(100); if (SENSOR_1 == 1) {Off(OUT_A+OUT_C);} if (SENSOR_1 == 2) {OnFwd(OUT_A+OUT_C);} } }
Observe que primeiramente configuramos o tipo e o modo do sensor. Parece que isso essencial, pois ao mudar o tipo tambm afetamos o modo de operao.
O sensor de rotao
O sensor de rotao um tipo de sensor muito til, porm infelizmente no faz parte do kit padro. Ele pode ser comprado separadamente diretamente na Lego. O sensor de rotao tem um orifcio onde se pode colocar um eixo. O sensor de rotao mede a quantidade de vezes que aquele eixo gira. Uma rotao complete do eixo contm 16 passos (ou -16 caso voc rode no sentido contrrio). Sensores de rotao so mais teis para controlar os movimentos do rob de maneira mais precisa. Voc pode fazer um eixo girar a quantia exata que voc quiser. Se voc quiser um controle com preciso maior do que 16 passos, voc pode utilizar engrenagens, conect-las a um eixo que se mova mais rpido e us-la para contar os passos. Uma aplicao bsica utiliza dois sensores de rotao conectados a duas rodas de um rob que voc controla por intermdio de dois motores. Para um movimento em linha reta voc deseja que ambas as rodas girem na mesma velocidade. Infelizmente, os motores no giram exatamente na mesma velocidade. Usando sensores de rotao voc pode constatar isso. Voc pode, temporariamente, parar o motor que gira mais rpido (melhor se usar Float()) at que ambos os sensores possuam o mesmo valor. O programa abaixo faz isso. Ele simplesmente faz o rob se movimentar em linha reta. Para utiliz-lo, modifique seu rob, conectando dois sensores de rotao s duas rodas. Conecte os sensores nas entras 1 e 3.
task main() { SetSensor(SENSOR_1,SENSOR_ROTATION); ClearSensor(SENSOR_1); SetSensor(SENSOR_3,SENSOR_ROTATION); ClearSensor(SENSOR_3); while (true) { if (SENSOR_1 < SENSOR_3) {OnFwd(OUT_A); Float(OUT_C);} else if (SENSOR_1 > SENSOR_3) {OnFwd(OUT_C); Float(OUT_A);} else {OnFwd(OUT_A+OUT_C);} } }
O programa inicialmente indica que ambos os sensores so de rotao e estabelece 0 como seus valores de inicializao (por meio do comando ClearSensor). Posteriormente, inicia um loop infinito. No loop verificamos se as leituras dos sensores so iguais. Caso sejam, ele simplesmente move o rob em linha reta. Caso alguma leitura apresente um valor maior, o motor adequado parado at que as leituras apresentem valores iguais. Pode-se observar que o programa bastante simples. Voc pode estend-lo para fazer o rob se movimentar em distncias precisas, ou possibilit-los fazer curvas com preciso.
- 28 -
O mais fcil conectar dois sensores de toque em uma nica entrada. Se um deles (ou ambos) pressionado, o valor ser 1, caso contrrio ser 0. Voc no tem como distinguir qual foi pressionado, mas s vezes isso no necessrio. Por exemplo, quando voc coloca um sensor de toque na frente do rob e outro atrs, voc sabe qual foi tocado com base na direo que o rob est se movendo. Mas voc pode configurar o modo para entrada bruta (veja anteriormente). Agora, voc pode obter muito mais informaes. Se voc estiver com sorte, o valor de leitura do sensor quando pressionado no ser o mesmo para ambos os sensores. Sendo este o caso, voc pode realmente distinguir entre os dois sensores. E, quando ambos so pressionados, voc obtm um valor de leitura bem pequeno (em torno de 30), o que o possibilita detectar essa situao. Voc pode conectar um sensor de toque e um sensor de luz a uma mesma entrada. Defina o tipo para luz (de outro modo o sensor de luz no funcionar). Defina o modo de operao para bruto. Nesse caso, quando o sensor de toque for pressionado voc obter um valor bruto menor que 100. Caso no seja pressionado o valor de leitura do sensor nunca ser menor do que 100. O programa abaixo ilustra essa idia. O rob deve ser equipado com um sensor de luz apontado para baixo, e um anteparo na frente (para identificar quando o rob bateu em algo) conectado ao sensor de toque. Conecte ambos na entrada 1. O rob se mover de maneira aleatria dentro de uma rea de cor clara. Quando o sensor identificar uma linha preta (valor bruto > 750) ele recuar um pouco. Quando o sensor de toque bater em algo (valor bruto menor do que 100) ele far o mesmo. Veja um programa exemplo:
int ttt,tt2; task moverAleatoriamente() { while (true) { ttt = Random(50) + 40; tt2 = Random(1); if (tt2 > 0) { OnRev(OUT_A); OnFwd(OUT_C); Wait(ttt); } else { OnRev(OUT_C); OnFwd(OUT_A);Wait(ttt); } ttt = Random(150) + 50; OnFwd(OUT_A+OUT_C);Wait(ttt); } } task main() { start moverAleatoriamente; SetSensorType(SENSOR_1,SENSOR_TYPE_LIGHT); SetSensorMode(SENSOR_1,SENSOR_MODE_RAW); while (true) { if ((SENSOR_1 < 100) || (SENSOR_1 > 750)) { stop moverAleatoriamente; OnRev(OUT_A+OUT_C);Wait(30); start moverAleatoriamente; } } }
Eu espero que o programa esteja simples. Existem duas tarefas. A tarefa moverAleatoriamente faz o rob se mover de maneira aleatria. A tarefa principal inicialmente inicia moverAleatoriamente, define o sensor e aguarda algo acontecer. Caso a leitura do sensor se torne muito pequeno (sendo tocado) ou muito grande (fora da rea clara), ele pra de se mover aleatoriamente, recua um pouco e inicia a movimentao aleatria novamente. Tambm possvel conectar dois sensores de luz na mesma entrada. O valor bruto estar, de alguma maneira, relacionado com a combinao da quantidade de luz recebida pelos sensores. Mas isso bastante confuso e pode parecer difcil de usar. Conectar outros sensores, tal como rotao e temperatura, parece no ser til.
- 29 -
task enviarSinal() { while(true) {SendMessage(0); Wait(10);} } task verificarSinal() { while(true) { nivelAnterior = SENSOR_2; if(SENSOR_2 > nivelAnterior + 200) {OnRev(OUT_C); Wait(85); OnFwd(OUT_A+OUT_C);} } } task main() { SetSensorType(SENSOR_2, SENSOR_TYPE_LIGHT); SetSensorMode(SENSOR_2, SENSOR_MODE_RAW); OnFwd(OUT_A+OUT_C); start enviarSinal; start verificarSinal; }
A tarefa enviarSinal envia 10 sinais de infra-vermelho a cada segundo, usando o comando SendMessage(0). A tarefa verificarSinal armazena o valor lido pelo sensor de luz, repetidamente. Ento, ela verifica se um desses valores maior do que o valor anterior em pelo menos 200 unidades, o que indicaria uma grande flutuao. Caso isso tenha ocorrido, ela faz o rob efetuar um giro de 90 graus para a direita. O valor 200 arbitrrio. Se voc o fizer menor, o rob se desviar mais rapidamente do obstculo, ou seja, notar o objeto a uma distncia maior. Caso seja um valor maior, o rob chegar mais perto antes de notar o objeto. Mas isso tambm depende do tipo de material e da quantidade de luminosidade disponvel no ambiente. Voc deveria experimentar ou utilizar algum mecanismo mais fcil e adequado para aprender a determinar o valor correto. Uma desvantagem da tcnica o fato da mesma funcionar somente quando o rob est se movimentando em uma direo. Voc provavelmente precisar de sensores de toque localizados nas laterais para evitar colises nesses locais. Mas a tcnica muito til para robs que necessitam se locomover em labirintos. Outra desvantagem o fato de no ser possvel estabelecer uma comunicao do computador com o rob porque a mesma ir interferir nas mensagens de infra-vermelho enviadas pelo rob. (O controle remoto da sua televiso provavelmente tambm no funcionar.)
Resumo
Neste captulo foram vistas algumas questes adicionais relacionadas a sensores. Vimos como configurar o tipo e o modo de um sensor de maneira separada, alm de como utilizar para se obter informaes adicionais.
- 30 -
Aprendemos como utilizar um sensor de rotao. Vimos, tambm, como fazer para conectar vrios sensores em uma mesma entrada do RCX. Finalmente, vimos um truque para utilizar a conexo de infra-vermelho e o sensor de luz do rob para criar um sensor de proximidade. Todos esses truques so extremamente teis quando se est construindo robs complexos. Sensores tm sempre um papel crucial nessas construes.
- 31 -
X. Tarefas paralelas
Como citado anteriormente, no NQC as tarefas so executadas simultaneamente, ou em paralelo como as pessoas normalmente dizem. Isso extremamente til, possibilitando-o efetuar a leitura de sensores, enquanto outra tarefa controla a movimentao do rob e, ainda, uma outra tarefa toca alguma msica. Mas tarefas paralelas tambm podem causar problemas. Uma tarefa pode interferir em outra.
Um programa errado
Considere o seguinte programa. Nesse programa uma tarefa controla a movimentao do rob em caminhos com a forma de um quadrado (semelhante ao que foi feito em programas anteriores) e uma segunda tarefa verifica um sensor de toque. Quando o sensor for pressionado, ele mover o rob um pouco para trs e far um giro de 90 graus.
task main() { SetSensor(SENSOR_1,SENSOR_TOUCH); start verificarSensores; while (true) { OnFwd(OUT_A+OUT_C); Wait(100); OnRev(OUT_C); Wait(85); } } task verificarSensores() { while (true) { if (SENSOR_1 == 1) { OnRev(OUT_A+OUT_C); Wait(50); OnFwd(OUT_A); Wait(85); OnFwd(OUT_C); } } }
Provavelmente esse programa se parea perfeito. Mas se voc execut-lo, voc provavelmente observar um comportamento inesperado. Tente o seguinte: faa o rob tocar em algo enquanto ele est girando. Ele comear a se mover para trs, mas imediatamente ele se mover para frente novamente, batendo no obstculo. A razo disso que as tarefas esto interferindo umas nas outras. Olha o que acontece. O rob est girando direita, isto , a primeira tarefa est na sua segunda instruo Wait. Agora, o rob bate com o sensor em algo. Ele comea a se movimentar para trs, mas, ao mesmo tempo, a tarefa main finaliza a espera e move o rob para frente novamente; batendo no obstculo. A segunda tarefa est em espera (instruo Wait) neste momento e, devido a isso, no percebe a coliso. Claramente, isto no o comportamento que gostaramos de ver. O problema que enquanto a segunda tarefa est em espera, no percebemos que a primeira tarefa ainda estava em execuo e que suas aes interferem com as aes da segunda tarefa.
- 32 -
task main() { SetSensor(SENSOR_1,SENSOR_TOUCH); start verificarSensores; start movimentarEmQuadrados; } task movimentarEmQuadrados () { while (true) { OnFwd(OUT_A+OUT_C); Wait(100); OnRev(OUT_C); Wait(85); } } task verificarSensores() { while (true) { if (SENSOR_1 == 1) { stop movimentarEmQuadrados; OnRev(OUT_A+OUT_C); Wait(50); OnFwd(OUT_A); Wait(85); start movimentarEmQuadrados; } } }
A questo aqui que a tarefa verificarSensores somente move o rob aps ter parado a tarefa movimentarEmQuadrados. Ento, esta tarefa no pode interferir na movimentao de desvio de obstculo. Uma vez que o procedimento de se afastar do objeto termina, ela inicia a tarefa movimentarEmQuadrados de novo. Mesmo acreditando que esta seja uma boa soluo para o problema descrito anteriormente, ainda existe um problema. Quando ns reiniciamos a tarefa movimentarEmQuadrados, ela inicia novamente executando a sua primeira instruo. Isso no traz problemas para o problema em questo, mas freqentemente no o comportamento desejado. Ns preferiramos parar a tarefa em algum ponto e, posteriormente, continuar a sua execuo daquele ponto. Infelizmente isso no pode ser feito de maneira muito fcil.
Usando semforos
Uma tcnica padro utilizada para solucionar este problema utilizar uma varivel para indicar qual tarefa est controlando os motores em um determinado momento. As demais tarefas no conseguem obter o controle dos motores at que a primeira tarefa indique, por intermdio da varivel, que est liberada. Tal varivel freqentemente denominada semforo. Abaixo, sem um semforo. Assumimos que o valor 0 em sem indica que nenhuma tarefa est controlando os motores. Agora, caso alguma tarefa queira fazer algo com os motores ela executa os seguintes comandos:
until (sem == 0); sem = 1; // Faa algo com os motores sem = 0;
Ento, inicialmente, esperamos at que ningum precise dos motores. Quando isso ocorre, obtemos o controle por meio da definio da varivel sem como 1. Agora, podemos controlar os motores. Quando tivermos terminado, definimos sem para 0. Neste ponto, chegamos ao programa acima, implementado com o uso de um semforo. Quando o sensor de toque se choca em algo, o semforo definido em 1 e o procedimento de afastamento do obstculo executado. Durante esse procedimento a tarefa movimentarEmQuadrados deve esperar. No momento que o procedimento termina, o semforo definido em 0 e a tarefa movimentarEmQuadrados pode continuar.
- 33 -
int sem; task main() { sem = 0; start movimentarEmQuadrados; SetSensor(SENSOR_1,SENSOR_TOUCH); while (true) { if (SENSOR_1 == 1) { until (sem == 0); sem = 1; OnRev(OUT_A+OUT_C); Wait(50); OnFwd(OUT_A); Wait(85); sem = 0; } } } task movimentarEmQuadrados () { while (true) { until (sem == 0); sem = 1; OnFwd(OUT_A+OUT_C); sem = 0; Wait(100); until (sem == 0); sem = 1; OnRev(OUT_C); sem = 0; Wait(85); } }
Voc poderia dizer que no necessrio definir o semforo para 1 e de volta para 0 na movimentarEmQuadrados. Todavia, isso til. A razo que o comando OnFwd() de fato dois comandos (veja o Captulo VIII). Voc no quer que esta seqncia de comandos seja interrompida por outra tarefa. Semforos so muito teis e quando se est escrevendo programas complicados com tarefas paralelas eles so quase sempre necessrios. (Existe ainda uma chance mnima de eles falharem. Tente descobrir por que.)
Resumo
Neste captulo estudamos alguns dos problemas que podem ocorrer quando utilizamos vrias tarefas. Esteja sempre atento aos efeitos colaterais. Muitos dos comportamentos inesperados so provenientes disso. Vimos duas maneiras diferentes de solucionar tais problemas. A primeira soluo pra e reinicia tarefas para garantir que somente uma tarefa crtica esteja sendo executada a cada momento. A segunda abordagem utiliza semforos para controlar a execuo das tarefas. Isso garante que a cada momento somente a parte crtica de uma tarefa est em execuo.
- 34 -
Dando ordens
Freqentemente, quando voc tem dois ou mais robs, um deles o lder. Ns o chamamos de mestre (master). Os demais robs so os escravos (slaves). O rob mestre envia ordens para os robs escravos e esses as executam. s vezes, os robs escravos podem enviar de volta informaes para o mestre como, por exemplo, o valor lido em um sensor. Deste modo, voc precisa escrever dois programas, um para o rob mestre e outro para o(s) escravo(s). Aqui, iremos assumir que temos somente um escravo. Deixe-nos iniciar com um exemplo bastante simples. Nesse exemplo o rob escravo pode executar trs diferentes ordens: mover para a frente, mover para trs e parar. Seu programa consiste de um loop simples. Neste loop o valor da mensagem atual defina em 0, por meio do comando ClearMessage(). Depois ele aguarda at que a mensagem se torne diferente de 0. Baseado no valor da mensagem, o rob escravo executa uma de trs diferentes ordens. Eis o programa:
task main() { while (true) { ClearMessage(); until (Message() if (Message() == if (Message() == if (Message() == } } // ESCRAVO
!= 1) 2) 3)
O rob mestre tem tambm um programa bastante simples. Ele simplesmente envia mensagens referentes s ordens e aguarda por um tempo. No programa abaixo, ele ordena aos escravos se moverem para frente. Ento, aps dois segundos, ele os ordena a se moverem para trs. Aps novos dois segundos, ele os ordena a parar.
task main() // MESTRE { SendMessage(1); Wait(200); SendMessage(2); Wait(200); SendMessage(3); }
Aps ter escrito esses dois programas, voc precisa efetuar o download dos mesmos para os robs. Cada programa de um dos robs. Esteja certo de que voc tenha desligado o outro rob no momento em que estiver efetuando o download (veja tambm as precaues apresentadas abaixo). Agora, ligue ambos os robs e execute os programas: execute primeiro aquele que est no rob escravo e depois o mestre. Caso voc tenha vrios escravos, voc dever efetuar o download para cada um deles em separado (no o faa simultaneamente; veja abaixo). Assim, todos os robs escravos executaro as mesmas aes. Para propiciar a comunicao dos robs entre si, ns definimos o que chamado de um protocolo: decidimos que um 1 significa mover para a frente, um 2 mover para trs e um 3 parar. muito importante definir tais protocolos de maneira cuidadosa, em particular quando se est lidando com um volume grande de troca de informaes. Por exemplo, quando existem muitos escravos, voc poderia definir um protocolo em que dois
- 35 -
nmeros so enviados (com um pequeno tempo de atraso entre eles): o primeiro o nmero do rob escravo e o segundo a ordem a ser executada. Os robs escravos primeiramente verificam o nmero e somente executam a ao caso aquele seja o seu nmero. (Isso requer que cada rob escravo tenha seu prprio nmero, o que pode ser feito fazendo com que cada escravo tenha um programa ligeiramente diferente em que, por exemplo, uma constante seja diferente.)
Elegendo um lder
Conforme visto acima, quando lidamos com vrios robs, cada um deve ter seu prprio programa. Seria muito mais fcil se pudssemos efetuar o download do mesmo programa para todos os robs. Mas a questo que se apresenta : quem o lder? A resposta fcil: deixe os robs decidirem por si mesmos. Deixe que eles elejam um lder e os demais o seguiro. Mas como fazer isso? A idia bastante simples. Deixaremos cada rob aguardar por um perodo de tempo aleatrio e ento enviar uma mensagem. O primeiro a enviar a mensagem ser o lder. Esse esquema pode falhar caso dois robs esperem por exatamente a mesma quantidade de tempo, mas isso bastante improvvel. (Voc pode construir esquemas mais complicados que detectam isso e tentam uma segunda eleio.) Segue um programa que faz o que foi descrito:
task main() { ClearMessage(); Wait(200); Wait(Random(400)); if (Message() > 0) { start escravo; } else { SendMessage(1); Wait(400); start mestre; } }
// garante que todos os robs estejam ligados // aguarda um tempo entre 0 e 4 segundos // algum outro rob foi o primeiro
task mestre() { SendMessage(1); Wait(200); SendMessage(2); Wait(200); SendMessage(3); } task escravo() { while (true) { ClearMessage(); until (Message() if (Message() == if (Message() == if (Message() == } }
!= 1) 2) 3)
Efetue o download deste programa para todos os robs (um por um, no o faa simultaneamente; veja abaixo). Inicie os robs ao mesmo tempo e veja o que acontece. Um deles dever tomar o comando e os outros seguiro as ordens. Em situaes bastante raras, nenhum deles se tornar o lder. Como indicado anteriormente, isso requer um protocolo mais elaborado para solucionar tais situaes.
Precaues
Voc deve ser bastante cuidadoso quando estiver lidando com vrios robs. Existem dois problemas: se dois robs (ou um rob e um computador) enviarem informao ao mesmo tempo, as mesmas podero ser perdidas; o segundo problema que, quando o computador envia um programa para vrios robs ao mesmo tempo, isso causa problemas.
- 36 -
Vamos iniciar com o segundo problema. Quando voc efetua o download de um programa para um rob, o rob diz ao computador se ele est recebendo corretamente (as partes do) o programa. O computador reage a isso enviando novos pedaos ou reenviando partes do programa. Quando dois robs esto ligados, ambos iro iniciar dizendo ao computador se ele receberam corretamente o programa. O computador no entende isso (o computador no sabe que existem dois robs!). Como resultado, coisas podem dar errado e o programa corrompido. Os robs no faro as coisas certas. Sempre garanta que quando voc estiver fazendo o download de programas, somente um rob esteja ligado! O outro problema que somente um rob pode enviar uma mensagem em um determinado momento. Caso duas mensagens sejam enviadas exatamente ao mesmo tempo, elas sero perdidas. Alm disso, um rob no pode enviar e receber mensagens no mesmo instante. Isso no um problema quando somente um rob envia mensagens (existe somente um mestre) mas, por outro lado, pode se tornar um problema srio. Por exemplo, voc pode imaginar a escrita de um programa em que o rob escravo envia uma mensagem quando ele bate em algo, de modo que o rob mestre tome uma deciso. Mas, caso o mestre envie uma ordem ao mesmo tempo, a mensagem ser perdida. Para resolver isso, importante definir seu protocolo de comunicao de tal modo que, no caso de uma comunicao falhar, ela seja corrigida. Por exemplo, quando o mestre envia um comando, ele deve receber de volta uma resposta do escravo. Caso ele no receba uma resposta em um intervalo de tempo esperado, ele reenvia o comando. Isso poderia resultar em um pedao de cdigo que se parece com isso:
do { SendMessage(1); ClearMessage(); Wait(10); } while (Message() != 255);
Aqui, 255 usado para informar que o comando foi recebido pelo escravo. s vezes, quando voc est lidando com vrios robs, voc deseja que somente aquele rob que est mais perto receba o sinal. Isso pode ser feito por meio da insero do comando SetTxPower(TX_POWER_LO) ao programa do rob mestre. Neste caso, o sinal de infra-vermelho enviado muito baixo e somente um rob prximo e de frente para o mestre poder escut-lo. Isso em particular til quando estiver construindo um rob maior que utiliza mais de dois RCX. Use SetTxPower(TX_POWER_HI) para configurar o rob novamente com uma taxa de transmisso longa.
Resumo
Neste captulo estudamos alguns aspectos bsicos da comunicao entre robs. A comunicao utiliza comandos para enviar, limpar e verificar mensagens. Vimos que importante definir um protocolo referente maneira que a comunicao acontecer. Tais protocolos desempenham um papel crucial em toda forma de comunicao entre computadores. Vimos, tambm, que existem restries na comunicao entre robs, nos quais evidencia a importncia de se definir bons protocolos.
- 37 -
Temporizadores
O RCX possui quarto temporizadores internos. Esses temporizadores avanam em incrementos de 1/10 segundos. Os temporizadores so numerados de 0 a 3. Voc pode tornar zero o valor de um temporizador (zerar o temporizador) com o comando ClearTimer()e obter o valor corrente com Timer(). Abaixo, tem-se um exemplo do uso de temporizadores. O programa deixa o rob se movimentar de forma meio aleatria por 20 segundos.
task main() { ClearTimer(0); do { OnFwd(OUT_A+OUT_C); Wait(Random(100)); OnRev(OUT_C); Wait(Random(100)); } while (Timer(0)<200); Off(OUT_A+OUT_C); }
Voc pode querer comparar este programa com aquele apresentado no Captulo IV que fazia exatamente a mesma coisa. Entretanto, este programa com o uso de temporizadores se torna definitivamente mais simples. Temporizadores so muito teis em substituio ao comando Wait(). Voc pode aguardar (sleep) por uma determinada quantidade de tempo zerando o temporizador e esperando at que seu valor alcance um valor determinado. Mas voc tambm pode reagir a outros eventos (como por exemplo, gerados por sensores) enquanto o temporizador estiver executando. O seguinte programa um exemplo disso. Ele deixa o rob se movimentar at que uma de duas coisas acontea: 10 segundos sejam passados; ou o sensor de toque perceba algo.
task main() { SetSensor(SENSOR_1,SENSOR_TOUCH); ClearTimer(3); OnFwd(OUT_A+OUT_C); until ((SENSOR_1 == 1) || (Timer(3) >100)); Off(OUT_A+OUT_C); }
No se esquea que os temporizadores trabalham em intervalos de 1/10 segundos, enquanto que o comando Wait utiliza intervalos de 1/100 segundos.
O display
possvel controlar o display do RCX de duas maneiras diferentes. Na primeira delas voc pode indicar o que ser mostrado no display: o clock do sistema, um dos sensores ou um dos motores. Isso equivalente a utilizar o boto view do RCX. Para definir o tipo de informao a ser apresentada, utilize o comando SelectDisplay(). A seguir so mostradas todas as sete possibilidades, uma aps a outra.
- 38 -
// // // // // // //
Observe que voc no deve utilizar SelectDisplay(SENSOR_1). A segunda maneira de controlar o display por intermdio do valor do clock do sistema (relgio do sistema). Voc pode us-lo, por exemplo, para apresentar informaes de diagnstico. Para isso, use o comando SetWatch(), conforme mostrado abaixo.
task main() { SetWatch(1,1); Wait(100); SetWatch(2,4); Wait(100); SetWatch(3,9); Wait(100); SetWatch(4,16); Wait(100); SetWatch(5,25); Wait(100); }
Datalogging
O RCX pode armazenar valores de variveis, leituras de sensores e o valor de temporizadores em uma pequena rea da memria denominada datalog. Os valores do datalog no podem ser utilizados pelo RCX, mas podem ser lidos pelo seu computador. Isso til, por exemplo, para verificar o que est acontecendo com o seu rob. O RCX Command Center tem uma janela especial em que voc pode ver o contedo atual do datalog. Usar o datalog consiste de trs passos. Primeiro, o programa em NQC define o tamanho do datalog por meio do comando CreateDatalog(). Isso tambm limpa o valor corrente do datalog. Depois, os valores podem ser escritos no datalog usando o comando AddToDatalog(). Os comandos sero escritos um aps o outro. (Se voc observar o display do RCX ver que aparecem quatro partes de um disco, uma aps a outra. Quando o disco estiver completo o datalog estar cheio.) Caso o final do datalog estiver sido alcanado, nada acontece. Novos valores no sero armazenados. O terceiro passo transferir o contedo do datalog para o computador. Para isso, escolha no RCX Command Center o comando Datalog no menu Tools. Depois, pressione o boto Upload Datalog, e todos os valores aparecero. Voc pode v-los ou salv-los em um arquivo para fazer algo a mais com eles. Essa caracterstica tem sido freqentemente utilizada, por exemplo, para fazer uma varredura com o RCX. No prximo exemplo temos um rob com um sensor de luz. O rob se move por 10 segundos e a cada segundo so feitas 5 escritas no datalog do valor lido no sensor.
task main() { SetSensor(SENSOR_2,SENSOR_LIGHT); OnFwd(OUT_A+OUT_C); CreateDatalog(50); repeat (50) { AddToDatalog(SENSOR_2); Wait(20); } Off(OUT_A+OUT_C); }
- 39 -
Instrues
Instrues
while (cond) body do body while (cond) until (cond) body break continue repeat (expression) body if (cond) stmt1 if (cond) stmt1 else stmt2 start task_name stop task_name function(args) var = expression var += expression var -= expression var *= expression var /= expression var |= expression var &= expression return expression
Descrio Executa o corpo zero ou mais vezes enquanto a condio for verdadeira Executa o corpo uma ou mais vezes enquanto a condio for verdadeira Executa o corpo zero ou mais vezes at que a condio se torne verdadeira Sai de uma estrutura do tipo while/do/until Pula a prxima iterao de uma estrutura do tipo while/do/until Repete o corpo um nmero de vezes especfico Executa stmt1 se a condio for verdadeira. Executa stmt2 (caso exista) se a condio for falsa Inicia a tarefa especificada Pra a tarefa especificada Chama uma funo utilizando os argumentos passados Calcula a expresso e atribui o valor resultante para a varivel Calcula a expresso e adiciona o valor resultante ao valor da varivel Calcula a expresso e subtrai o valor resultante do valor da varivel Calcula a expresso e multiplica o valor resultante pelo valor da varivel Calcula a expresso e divide o valor resultante pelo valor da varivel Calcula a expresso e executa a operao OR sobre a varivel Calcula a expresso e executa a operao AND sobre a varivel Retorna de uma funo para o ponto que a chamou Calcula a expresso
Condies
Condies so usadas dentro de estruturas de controle para a tomada de decises. Na maioria dos casos, a condio envolver uma comparao entre as expresses. Condio Significado true sempre verdadeiro false sempre falso expr1 == expr2 Testa se as expresses so iguais expr1 != expr2 Testa se as expresses so diferentes expr1 < expr2 Testa se uma expreso menor do que a outra expr1 <= expr2 Testa se uma expreso menor ou igual do que a outra expr1 > expr2 Testa se uma expreso maior do que a outra expr1 >= expr2 Testa se uma expreso maior ou igual do que a outra ! condition Negao lgica de uma expresso cond1 && cond2 Lgica AND de duas condies (verdadeiro se, e somente se, ambas as condies forem verdadeiras) cond1 || cond2 Lgica OR de duas condies (verdadeiro se, e somente se, pelo menos uma das condies for verdadeira)
Expresses
Existem diferentes valores que podem ser usados dentro de expresses, incluindo constantes, variveis e valores de sensores. Observe que SENSOR_1, SENSOR_2 e SENSOR_3 so macros que se expandem para SensorValue(0), SensorValue(1) e SensorValue(2), respectivamente. Valor nmero varivel Timer(n) Descrio Um valor constante (por exemplo "123") Uma varivel nomeada (por exemplo "x") Valor do temporizador n, onde n pode ser 0, 1, 2 ou 3
- 40 -
Nmero randmico definido entre 0 e n (inclusives) Valor corrente do sensor n, onde n pode ser 0, 1 ou 2 Valor do relgio do sistema Valor da ltima mensagem de infra-vermelho recebida
Valores podem ser combinados utilizando operadores. Muitos dos operadores somente podem ser utilizados no clculo de expresses constantes, o que significa que seus operandos devem ser constantes ou expresses envolvendo nada mais do que constantes. Os operadores so listados abaixo em ordem de precedncia (da maior para a menor). Operador
abs() sign() ++ -~ * / % + << >> & ^ | && ||
Descrio Valor absoluto Sinal do operando Incremento Decremento Menos unrio Negao Bitwise (unria) Multiplicao Diviso Mdulo Adio Subtrao Deslocamento para a esquerda Deslocamento para a direita Bitwise AND Bitwise XOR Bitwise OR Operador lgico AND Operador lgico OR
Associatividade Restrio n/a n/a esquerda Somente variveis esquerda Somente variveis direita Somente constantes direita esquerda esquerda esquerda Somente constantes esquerda esquerda esquerda Somente constantes esquerda Somente constantes esquerda esquerda Somente constantes esquerda esquerda Somente constantes esquerda Somente constantes
Exemplo
abs(x) sign(x) x++ ou ++x x-- ou --x -x ~123 x * y x / y 123 % 4 x + y x - y 123 << 4 123 >> 4 x & 123 x | 123 123 y ^ 4 y && 4 || 4
Funes do RCX
A maioria das funes requer que todos os argumentos sejam expresses constantes (nmeros ou operaes envolvendo outras expresses constantes). As excees so as funes que usam um sensor como argumento e aquelas que no usam qualquer expresso. No caso de sensores, o argumento deve ser um nome de um sensor: SENSOR_1, SENSOR_2 ou SENSOR_3. Em alguns casos existem nomes predefinidos (por exemplo, SENSOR_TOUCH) para constantes apropriadas. Funo
SetSensor(sensor, config) SetSensorMode(sensor, mode) SetSensorType(sensor, type) ClearSensor(sensor) On(outputs) Off(outputs) Float(outputs) Fwd(outputs) Rev(outputs) Toggle(outputs) OnFwd(outputs) OnRev(outputs) OnFor(outputs, time)
Exemplo
SetSensor(SENSOR_1, SENSOR_TOUCH) SetSensor(SENSOR_2, SENSOR_MODE_PERCENT) SetSensor(SENSOR_2, SENSOR_TYPE_LIGHT)
ClearSensor(SENSOR_3) Limpa o valor do sensor. On(OUT_A + OUT_B) Liga uma ou mais sadas. Desliga uma ou mais sadas. Off(OUT_C) Float(OUT_B) Deixa que a sada flutue. Fwd(OUT_A) Define a direo da sada para frente. Rev(OUT_B) Define a direo da sada para trs. Toggle(OUT_C) Troca a direo da sada (Frente para trs e vice-versa) Liga em movimentao para OnFwd(OUT_A) frente. OnRev(OUT_B) Liga no sentido reverso. OnFor(OUT_A, 200) Liga por um perodo de tempo especificado por um nmero igual a 1/100 de
- 41 -
segundo. O tempo (time) pode ser uma expresso. SetOutput(outputs, mode) Define o modo de sada. SetOutput(OUT_A, OUT_ON) SetDirection(outputs, dir) Define a direo da sada. SetDirection(OUT_A, OUT_FWD) SetPower(outputs, power) Define o nvel de potncia da SetPower(OUT_A, 6) sada (0-7). power pode ser uma expresso. Wait(time) Aguarda por uma quantidade Wait(x) de tempo especificada em 1/100 segundos. time pode ser uma expresso. PlaySound(sound) PlaySound(SOUND_CLICK) Toca um som especfico (0-5). PlayTone(freq, duration) Toca uma nota de uma PlayTone(440, 5) freqncia especificada por um determinado perodo de tempo (em 1/10 segundos). ClearTimer(timer) Define o temporizador (0-3) ClearTimer(0) em 0. StopAllTasks() StopAllTasks() Pra todas as tarefas atualmente em execuo. SelectDisplay(mode) Seleciona um de 7 modos do SelectDisplay(1) display: 0: relgio do display, 1-3: valores de sensor, 4-6: configurao de sada. mode pode ser uma expresso. SendMessage(message) SendMessage(x) Envia uma mensagem em infra-vermelho (1-255). message pode ser uma expresso. ClearMessage() Limpa o buffer de mensagens ClearMessage() de infra-vermelho. CreateDatalog(size) Cria um novo datalog de um CreateDatalog(100) tamanho (size) especificado. AddToDatalog(value) AddToDatalog(Timer(0)) Adiciona um valor ao datalog. value pode ser uma exepresso. SetWatch(hours, minutes) Define o valor do relgio do SetWatch(1,30) sistema. SetTxPower(hi_lo) Define o nvel de potncia do SetTxPower(TX_POWER_LO) transmissor de infra-vermelho para um nvel baixo ou alto.
Constantes do RCX
Existem vrias constantes nomeadas (constantes simblicas) que podem ser passadas para as funes de modo a ajudar a tornar o cdigo mais legvel. Quando possvel, utilize uma constante nomeada ao invs de um valor bruto. Configuraes do sensor para SetSensor() Modos para SetSensorMode()
SENSOR_TOUCH, SENSOR_LIGHT, SENSOR_ROTATION, SENSOR_CELSIUS, SENSOR_FAHRENHEIT, SENSOR_PULSE, SENSOR_EDGE SENSOR_MODE_RAW, SENSOR_MODE_BOOL, SENSOR_MODE_EDGE, SENSOR_MODE_PULSE, SENSOR_MODE_PERCENT, SENSOR_MODE_CELSIUS, SENSOR_MODE_FAHRENHEIT, SENSOR_MODE_ROTATION SENSOR_TYPE_TOUCH, SENSOR_TYPE_TEMPERATURE, SENSOR_TYPE_LIGHT, SENSOR_TYPE_ROTATION OUT_A, OUT_B, OUT_C OUT_ON, OUT_OFF, OUT_FLOAT OUT_FWD, OUT_REV, OUT_TOGGLE
Tipos para SetSensorType() Sadas para On(), Off(), etc. Modos para SetOutput() Direes para SetDirection()
- 42 -
Potncia de sada para SetPower() OUT_LOW, OUT_HALF, OUT_FULL SOUND_CLICK, SOUND_DOUBLE_BEEP, SOUND_DOWN, SOUND_UP, Sons para PlaySound() Modos para SelectDisplay() Nvel da potncia da taxa de transmisso para SetTxPower()
SOUND_LOW_BEEP, SOUND_FAST_UP DISPLAY_WATCH, DISPLAY_SENSOR_1, DISPLAY_SENSOR_2, DISPLAY_SENSOR_3, DISPLAY_OUT_A, DISPLAY_OUT_B, DISPLAY_OUT_C TX_POWER_LO, TX_POWER_HI
Palavras-chave
Palavras-chave so aquelas palavras reservadas pelo compilador do NQC. um erro utilizar essas palavras para nomes de funes, tarefas ou variveis. A seguir, tem-se uma lista das palavras-chave usadas: __sensor, abs,
asm, break, const, continue, do, else, false, if, inline, int, repeat, return, sign, start, stop, sub, task, true, void, while.
- 43 -
- 44 -