Mestrado Integrado em Engenharia Electrotécnica e de Computadores U.C. Automação Ramos de Automação e Energia Guião de trabalho prático: Projecto de sistema de controlo de parque de estacionamento baseado em MicroControlador Armando Jorge Sousa asousa@fe.up.pt Luís Almeida lda@fe.up.pt Versão 06 de 05-03-2009
Apresentação do Trabalho Prático Neste trabalho prático utilizar-se-á um microcontrolador (µc ou uc) para controlar uma versão simplificada do problema do parque automóvel da FEUP, com as duas cancelas. Este trabalho demorará 2 aulas e deverá ser demonstrado no final da segunda aula TP. Os uc são dispositivos complexos que serão alvo de estudo aprofundado mais tarde no curso. Neste trabalho pretende-se apenas ilustrar conceitos básicos de automação. No início da aula será fornecida uma placa com o uc ATMega8 já montado tal como na figura 1. Este microcontrolador pode ser programado em linguagem C, idêntica à que já deve ter aprendido anteriormente no curso. Para programar este uc são necessários os programas livres WinAVR e o AVR Studio. Para transferir o programa para o uc, utiliza-se o MegaLoad que também é livre. Estes programas já estarão instalados no laboratório. Poderá ainda ser utilizado o site http://jarkonkotisivu.org/mvtheme/avrcoder.phtml para produzir código de inicialização para o uc. Será ainda utilizada a biblioteca bit_tools.h, disponível nos conteúdos da disciplina. Figura 1 Placa de montagem tal como fornecida inicialmente aos alunos Os pinos livres podem ser utilizados tanto como entradas como saídas. Entradas são os pinos de onde o uc pode ler informação e saídas são os pinos onde o uc pode escrever informação. A respectiva leitura e escrita são efectuadas através de instruções de um programa que é executado no uc. Os pinos do uc pertencem a um porto, por exemplo o pino 14 é o PB0, bit 0 (o menos significativo) do porto B (que tem 8 bits). Refira-se que, desses 8 pinos, apenas os pinos PB0 a PB5 estão livres para utilização como entradas e saídas. Automação 2007/8 2 / 11
Parte 1: Caderno de encargos Projecte e implemente o sistema de comando de um parque de estacionamento simplificado. Deve haver uma cancela à entrada e outra à saída e deve haver sensores para detectar a presença de carros e abrir as cancelas respectivas. A capacidade do parque é de 8 lugares. Existe um semáforo que deverá estar verde sempre que o nº de carros no parque seja inferior a 8 e mudar para vermelho sempre que o parque estiver cheio. Quando um carro se aproxima da cancela de entrada e se o parque não estiver cheio, esta abre durante 2 segundos aproximadamente. Sempre que um carro se aproximar da cancela de saída, esta abre durante 4 segundos aproximadamente. Naturalmente, podem entrar e sair carros ao mesmo tempo. Considere a simplificação de que as cancelas sobem (e descem) instantaneamente e considere que os sensores são botões de pressão montados na placa de testes ( breadboard ). Automação 2007/8 3 / 11
Parte 2: Conceitos e problema sob análise Qualquer sistema de controlo/comando visa comandar um determinado sistema real (sistema a ser controlado). O sistema de comando recebe informação através de entradas onde ligam sensores e altera o sistema a ser controlado através de saídas que ligam a actuadores. Sistema de Comando (Neste Caso: µc) Entradas Saídas Neste caso: Carro_Entr Carro_Sai Sensores Neste Caso: Canc_Entr Canc_Sai Sem_Verd Sem_Verm Actuadores Sistema a ser controlado (Neste caso: Parque de Estacionamento) Para resolver o problema proposto temos: Um Sensor de Carro à Entrada = uma entrada do uc que indica que chegou um carro à entrada do parque o É 1 quando o carro está presente (um carro quer entrar) o É 0 quando não está nenhum carro presente Um Sensor de Carro à Saída = uma entrada do uc que indica que um carro quer sair o É 1 quando o carro está presente (um carro quer sair) o É 0 quando não está nenhum carro presente Um Actuador da Cancela de Entrada = uma saída do uc que levanta a cancela de entrada o Quando este actuador é ligado, isto é, posto a 1, a cancela de entrada levanta o Quando está a 0, a cancela desce e não permite entrada de carros o A cancela permanece levantada ou descida enquanto este actuador estiver a 1 ou a 0 respectivamente Um Actuador da Cancela de Saída = uma saída do uc que levanta a cancela de saída o Quando este actuador é ligado, isto é, posto a 1, a cancela de saída levanta o Quando está a 0, a cancela desce e não permite a saída de carros o A cancela permanece levantada ou descida enquanto este actuador estiver a 1 ou a 0 respectivamente Nota: O valor lógico 1 é normalmente representado na realidade pela tensão eléctrica de alimentação positiva do uc, neste caso +5V. O valor lógico 0 é normalmente representado na realidade pela tensão eléctrica de referência ou massa, isto é, 0V. Automação 2007/8 4 / 11
Parte 3: Código Exemplo Para efeitos de preparação da aula, analise o seguinte código: #include <avr/io.h> #include "bit_tools.h" // Todas as Definições do uc ATMEGA8 // Todas as operações ao bit /* Queima Tempo */ void delay(long time) volatile long t; for(t=0;t<time;t++); /* Programa Principal */ int main() DDRC = 0x0F; // PC0..PC3 saídas PORTC = 0; // saídas inicialmente a 0 DDRB = 0; // porto B todo com entradas PORTB = 0x03; // activa pull-ups de PB0 e PB1 while(1) if (bit_read(portc,1)) bit_clear(portc,1); else bit_set(portc,1); // Comuta PC1 // de uma forma // complicada // de proposito // Cuidado: quando se carrega no botão, ligo pino à massa e PB0 fica a 0 // Com o código abaixo, o LED está ligado e quando se carrega no botão o LED apaga! if (bit_read(pinb,0)) bit_set(portc,0); // Se a entrada PB0 está activa então liga saída PC0 else bit_clear(portc,0); // Senão desliga saída PC0 delay(500000); // "Queima" tempo antes de continuar Automação 2007/8 5 / 11
Parte 4: Procedimento de programação e transferência 1. Verifique se a placa de montagem está em bom estado e se as ligações dos fios estão de acordo com a Fig. 1. Tenha particular atenção aos fios de alimentação, isto é, de +5V e de 0V. Utilize fios vermelhos para todas as ligações aos +5V e brancos para as ligações aos 0V (ligações à massa) 2. Confirme que os fios provenientes do PC estão tal como na Fig.1 fio castanho do PC liga ao pino 7 do integrado MAX232; verde ao pino 8; cinzento à massa 3. Abra o AVRStudio 4. Abra o projecto Teste_Auto 5. Compile o código certifique-se que tem 0 erros e 0 warnings 6. Ligue a alimentação do circuito 7. Abra o MegaLoad (bootloader permite carregar programas no uc) 8. Configure a porta série adequada (confirme que para o seu PC se trata da COM1) 9. Escolha o ficheiro default\teste_auto.hex 10. Prima o botão de reset (o programa é transferido do PC para o uc) 11. Se tiver recebido a mensagem Flash prog Done, então a transferência do programa teve sucesso e o uc está programado, isto é, tem na sua memória interna o programa Teste_Auto e esse programa já deverá estar em execução! 12. Desligue a alimentação 13. Ligue um LED amarelo com uma resistência em série ao PC0 (pino 23 na Fig.1) 14. Ligue um LED amarelo com uma resistência em série ao PC1 (pino 24 na Fig.1) 15. Ligue um LED verde com uma resistência em série ao PC2 (pino 25 na Fig.1) 16. Ligue um LED vermelho com uma resistência em série ao PC3 (pino 26 na Fig.1) 17. Ligue a alimentação. O programa deverá iniciar execução e os LEDs devem acender. Se ligar o pino PB0 com um fio à massa (0V), o LED ligado a PC0 deverá apagar. O outro LED deve piscar. Automação 2007/8 6 / 11
Parte 5: Projectar a solução Construa a sua solução utilizando o projecto Teste_Auto como inspiração e siga estes passos: 1. Comece por guardar o ficheiro Teste_Auto.c com outro nome, por exemplo, Trabalho1.c. Crie um novo projecto, por exemplo chamado Trabalho1, e associe-lhe o ficheiro de código Trabalho1.c. 2. Note que a primeira tarefa a executar num uc é configurar as funcionalidades dos pinos de I/O, isto é, definir quais serão entradas e quais serão saídas. Sugerimos que aproveite a configuração do projecto Teste_Auto, assim como os LEDS já montados. Apenas necessita de adicionar mais uma entrada, que sugerimos que seja através do bit 1 do porto B (PB1). Note que também deverá activar o respectivo pull-up. Adicione à placa de montagem dois botões de pressão para simular os sensores e ligue-os a PB0 e PB1. Assim, teremos como saídas Canc_Ent (PC0), Canc_Sai (PC1), Sem_Verd (PC2) e Sem_Verm (PC3), e como entradas Carr_Ent (PB0), Carr_Sai (PB1). 3. Associe os nomes referidos atrás aos respectivos pinos de I/O usando a directiva #define. Defina o porto e o bit respectivo, por exemplo #define Port_Canc_Sem PORTC #define Canc_Ent 0 #define Port_Carros PINB // porto das saídas das cancelas e semáforo // cancela de entrada PC0 // porto das entradas dos sensores de carros (note que a leitura de portos de entrada se faz pelos registos PINx) 4. Conforme se viu no Teste_Auto, é possível ler dos pinos de entrada directamente e escrever nos bits de saída também forçando-os a 1 ou 0. Contudo, vamos usar funções que facilitam o manuseamento das entradas e saídas e que estão definidas no ficheiro bit_tools.h que está localizado na mesma pasta do programa que está a editar. Adicione ao projecto este ficheiro do tipo header e acrescente no programa a directiva #include adequada (ver código exemplo). 5. Utilize o botão de pressão ligado a PB0, o qual vai simular o sensor de carros à entrada. Construa um programa que acenda o LED ligado a PC0 que simula a cancela de entrada, enquanto o botão é premido. Use as funções bit_read(port, bit), bit_set(port, bit) e bit_clear(port, bit) usando para port e bit as definições que fez no ponto 3. 6. Altere o programa anterior para o LED ficar aceso (cancela levantada) durante cerca de 2 segundos após premir o botão. Note que o tempo de espera conseguido com a função delay(time) é aproximadamente proporcional ao valor do argumento que lhe é passado. Verifique o que acontece se premir o botão antes de expirar esse intervalo de tempo. Nesta fase o sistema deverá ser insensível ao botão durante esse tempo. 7. Adicione um contador de carros, isto é, uma variável que incrementa cada vez que a cancela levanta e que está limitado a CAPAC_PARQ. Defina esta constante com o valor de 8. Enquanto o valor do contador for menor do que CAPAC_PARQ mantenha aceso o LED que representa o verde do semáforo (PC2). Quando chegar a CAPAC_PARQ, pare a contagem, apague o LED verde e acenda o outro LED correspondente ao vermelho do semáforo (PC3). 8. Faça as alterações necessárias para que a cancela de entrada não abra (o LED não deverá acender) quando o semáforo estiver vermelho, mesmo que se carregue no botão. Automação 2007/8 7 / 11
9. Acrescente agora o código relativo ao controlo da cancela de saída (PC1) usando o sensor de carros à saída (PB1). O comportamento obtido deverá ser semelhante ao da cancela da entrada mas faça o tempo de abertura de aproximadamente 4 segundos. Pode começar por copiar o bloco de código da outra cancela. 10. Ajuste o código de gestão do semáforo, de tal forma que quando sair um carro, isto é, cada vez que a cancela de saída abrir, o contador de carros deve ser decrementado e as luzes do semáforo devem ser controladas adequadamente. Se estava vermelho este deve apagar e acender-se o verde. Neste caso, também a cancela de entrada deve recomeçar a permitir a entrada de carros. 11. Verifique que, com a solução actual, o sistema é insensível aos botões enquanto uma cancela está levantada, o que não permite a entrada e saída de carros ao mesmo tempo. Como poderia resolver esta questão? Faça as alterações necessárias a conseguir essa funcionalidade. a. Sugestão: em vez de fazer a contagem de tempo de espera de forma bloqueante, isto é, com o processador dedicado a essa tarefa, faça antes uma contagem de tempo não-bloqueante, por contagem de repetições do ciclo do programa, usando variáveis adequadas. Automação 2007/8 8 / 11
Relatório Final No FINAL da segunda aula do trabalho, o grupo deve demonstrar o projecto a funcionar tal como estiver. Deve ser enviado por EMail, ao respectivo professor das aulas práticas as impressões para PDF geradas pelo AVRStudio. O EMail a enviar deve ter o assunto [Auto] TP1 Tx Gy - Nomes1+Nomes2 e deve ter anexo o documento PDF. O nome do documento PDF é obrigatório ser TP1 Tx Gy - Nomes1+Nomes2. O texto do EMail deve conter a turma, o grupo, nome COMPLETO e números de aluno dos autores do trabalho. Obs: x representa o número da turma, y o número do grupo e Nomes representa o Primeiro e Último nome dos autores do trabalho exemplo: [Auto] TP1 T2 G3 ManuelFagundes+AmbrósioFelisberto Bom Trabalho! Automação 2007/8 9 / 11
Tópico Avançado É possível utilizar comunicações série para ajudar a tirar erros ao programa. Utilizar a Baud Rate de 38400, 8 bits, No parity, 1 stop bit. Incluir printf_tools.h e inicializar no programa principal, tal como no seguinte exemplo: #include <avr/io.h> #include "bit_tools.h" #include "printf_tools.h" // Todas as Definições do uc ATMEGA8 // Todas as operações ao bit // Permite utilização de printf /* Queima Tempo */ void delay(long time) volatile long t; for(t=0;t<time;t++); /* Programa Principal */ int main() long cycle_count=0; // uma variável exemplo init_printf_tools(); printf("hello World!\n"); DDRC = 0x0F; // PC0..PC3 saídas PORTC = 0; // saídas inicialmente a 0 DDRB = 0; // porto B todo com entradas PORTB = 0x03; // activa pull-ups de PB0 e PB1 while(1) if (bit_read(portc,1)) bit_clear(portc,1); else bit_set(portc,1); // Comuta PC1 // de uma forma // complicada // de proposito // Cuidado: quando se carrega no botão, ligo pino à massa e PB0 fica a 0 // Com o código abaixo, o LED está ligado e quando se carrega no botão, o LED apaga if (bit_read(pinb,0)) bit_set(portc,0); // Se a entrada PB0 está activa então liga saída PC0 else bit_clear(portc,0); // Senão desliga saída PC0 delay(50000); // "Queima" tempo antes de continuar printf("#=%05ld\n",++cycle_count); Outros Recursos A utilização completa de ucs é complexa e não é o objectivo central da cadeira de Automação. Para mais informação acerca do AVR ATMEGA8, consultar: Datasheet: http://www.atmel.com/dyn/resources/prod_documents/doc2486.pdf Linguagem C (libc): C:\WinAVR-(versãoxxx)\doc\avr-libc\avr-libc-user-manual Automação 2007/8 10 / 11
Anexo: Listagem de todas as operações ao bit De seguida transcreve-se o ficheiro bit_tools.h onde se pode ver todas as operações ao bit existentes. O nome da função é auto-explicativo e não é necessário entender a implementação associada. As funções mais úteis encontram-se a negrito. /* Copyright (c) 2005, 2006, 2008 Paulo Costa, Paulo Marques All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holders nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define bits_set_8(var, mask) ((var) = (uint8_t)(mask)) #define bits_clear_8(var, mask) ((var) &= (uint8_t)~(mask)) #define bits_toggle_8(var, mask) ((var) ^= (uint8_t)(mask)) #define bits_read_8(var, mask) ((var) & (uint8_t)(mask)) #define bits_write_8(var, mask) ((var) = (uint8_t)(mask)) #define bits_set_16(var, mask) ((var) = (uint16_t)(mask)) #define bits_clear_16(var, mask) ((var) &= (uint16_t)~(mask)) #define bits_toggle_16(var, mask) ((var) ^= (uint16_t)(mask)) #define bits_read_16(var, mask) ((var) & (uint16_t)(mask)) #define bits_write_16(var, mask) ((var) = (uint16_t)(mask)) // "Public" versions for the common operation. #define bits_set(var, mask) bits_set_8(var, mask) #define bits_clear(var, mask) bits_clear_8(var, mask) #define bits_toggle(var, mask) bits_toggle_8(var, mask) #define bits_read(var, mask) bits_read_8(var, mask) #define bits_write(var, mask) bits_write_8(var, mask) #define bit(x) ((uint8_t)1 << (x)) #define bit_long(x) ((uint32_t)1 << (x)) #define bit_set(var, x) bits_set_8(var, bit(x)) #define bit_clear(var, x) bits_clear_8(var, bit(x)) #define bit_toggle(var, x) bits_toggle_8(var, bit(x)) #define bit_read(var, x) bits_read_8(var, bit(x)) Automação 2007/8 11 / 11