Victor Mirkhan / Mbed 2 deprecated PROJETO_MECATRONICO

Dependencies:   Servo TCS3200 TextLCD2 X_NUCLEO_IHM01A1 mbed

Fork of PROJETO by Victor Mirkhan

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 #include "TextLCD.h"
00002 #include "L6474.h"
00003 #include "TCS3200.h"
00004 #include "Servo.h"
00005 
00006 
00007 // Set do display LCD 20x4 com o módulo I2C
00008 
00009 I2C i2c_lcd(D14,D15);
00010 TextLCD_I2C lcd(&i2c_lcd, 0x7E,TextLCD::LCD20x4);
00011 
00012 /*-----Declaração dos pinos e suas funções-----*/
00013 
00014 //PINOS DE INTERRUPÇÃO.
00015 
00016 InterruptIn jog_button_pos(PC_12); // Botão para jog+ e navegação no menu
00017 InterruptIn jog_button_neg(PA_15); // Botão para jog- e navegação no menu
00018 
00019 InterruptIn btnX(PC_3); // Botão para selecionar o eixo X
00020 InterruptIn btnY(PC_2); // Botão para selecionar o eixo Y
00021 InterruptIn btnZ(PC_0); // Botão para selecionar o eixo Z
00022 
00023 InterruptIn confirma(PC_13); // Botão OK do menu (pino do botão do usuario)
00024 InterruptIn back_btn(PB_7); // Botão para a função "voltar" do menu
00025 
00026 InterruptIn FDC(PC_5); // Fim de curso para o eixo Z
00027 InterruptIn FDC2(PC_8); // Fim de curso para o eixo X
00028 InterruptIn FDC3(PC_6); // Fim de curso para o eixo Y
00029 
00030 /*-----Declaração de variaveis globais do código-----*/
00031 
00032 bool referZ = false; // Parametro para o referenciamento do motor 1
00033 bool referX = false; // Parametro para o referenciamento do motor 2
00034 bool referY = false; // Parametro para o referenciamento do motor 3
00035 
00036 bool save_pos = false; // Parametro para definir o fim da rotina de save pick\drop
00037 
00038 bool dirx = false; // Indicador que o motor ira se movimentar no eixo x
00039 bool diry = false; // Indicador que o motor ira se movimentar no eixo y
00040 bool dirz = false; // Indicador que o motor ira se movimentar no eixo z
00041 
00042 bool jog_pos = false; // Indicador para a movimentação do motor no jog (+)
00043 bool jog_neg = false; // Indicador para a movimentação do motor no jog (-)
00044 bool ref_cycle = true; // Indicador para a realização do ciclo Pick/Place
00045 
00046 bool enable = false; // Variavel auxiliar para navegação no motor
00047 bool flag = true; // Variavel auxiliar para utilizar mais de uma vez as funções disponiveis no menu
00048 
00049 signed char change = 0;  // Numero que varia de 0 a 2, indicando qual set de velocidade será usado
00050 signed char ref_cursor = 0; // Numero de referência para a posição do cursor
00051 unsigned char ref_menu = 0; // Numero para indicar o menu atual
00052 
00053 /*Criação de uma struct (basicamente, uma classe) para sets de velocidades e acelerações
00054   diferentes. Cada objeto dessa struct possui os argumentos de velocidades max e min e
00055   aceleração e desaceleração.
00056 */
00057 
00058 struct set_velocidades {
00059     unsigned int maxspeed;
00060     unsigned int minspeed;
00061     unsigned int ac;
00062     unsigned int dc;
00063 };
00064 
00065 struct set_velocidades set[3]; /* Cria um objeto da struct set_velocidades com três
00066                                objetos dentro dele (basicamente, um objeto triplo).
00067                                */
00068 
00069 //Struct para sets de coordenadas, com argumentos de posição em x,y e z.
00070 
00071 struct Coordenadas {
00072     int posx;
00073     int posy;
00074     int posz;
00075 };
00076 
00077 struct Coordenadas PickPos,DropPos[3]; //Cria objeto unico para posição de pick, e objeto triplo para posição de place.
00078 
00079 // Perfil de incialização do motor
00080 
00081 L6474_init_t init = {
00082     160,                              /* Acceleration rate in pps^2. Range: (0..+inf). */
00083     160,                              /* Deceleration rate in pps^2. Range: (0..+inf). */
00084     1600,                             /* Maximum speed in pps. Range: (30..10000]. */
00085     800,                              /* Minimum speed in pps. Range: [30..10000). */
00086     1000,                              /* Torque regulation current in mA. Range: 31.25mA to 4000mA. */
00087     L6474_OCD_TH_1500mA,               /* Overcurrent threshold (OCD_TH register). */
00088     L6474_CONFIG_OC_SD_ENABLE,        /* Overcurrent shutwdown (OC_SD field of CONFIG register). */
00089     L6474_CONFIG_EN_TQREG_TVAL_USED,  /* Torque regulation method (EN_TQREG field of CONFIG register). */
00090     L6474_STEP_SEL_1_8,               /* Step selection (STEP_SEL field of STEP_MODE register). */
00091     L6474_SYNC_SEL_1_2,               /* Sync selection (SYNC_SEL field of STEP_MODE register). */
00092     L6474_FAST_STEP_12us,             /* Fall time value (T_FAST field of T_FAST register). Range: 2us to 32us. */
00093     L6474_TOFF_FAST_8us,              /* Maximum fast decay time (T_OFF field of T_FAST register). Range: 2us to 32us. */
00094     3,                                /* Minimum ON time in us (TON_MIN register). Range: 0.5us to 64us. */
00095     21,                               /* Minimum OFF time in us (TOFF_MIN register). Range: 0.5us to 64us. */
00096     L6474_CONFIG_TOFF_044us,          /* Target Swicthing Period (field TOFF of CONFIG register). */
00097     L6474_CONFIG_SR_320V_us,          /* Slew rate (POW_SR field of CONFIG register). */
00098     L6474_CONFIG_INT_16MHZ,           /* Clock setting (OSC_CLK_SEL field of CONFIG register). */
00099     L6474_ALARM_EN_OVERCURRENT |
00100     L6474_ALARM_EN_THERMAL_SHUTDOWN |
00101     L6474_ALARM_EN_THERMAL_WARNING |
00102     L6474_ALARM_EN_UNDERVOLTAGE |
00103     L6474_ALARM_EN_SW_TURN_ON |
00104     L6474_ALARM_EN_WRONG_NPERF_CMD    /* Alarm (ALARM_EN register). */
00105 };
00106 
00107 // Declaração dos motores utilizados
00108 
00109 L6474 *motorZ;
00110 L6474 *motorX;
00111 L6474 *motorY;
00112 
00113 /*-----Declaração de funções auxiliares----*/
00114 
00115 /*  FUNÇÕES "sobe_cursor(void)" E "desce_cursor(void)":
00116     1-) Adiciona ou subtrai 1 da variavel ref_cursor;
00117     2-) Atribui o valor de linha máx ou mín se o ref_cursor atravessar algum
00118         extremo da tela LCD;
00119     3-) O cursor se posiciona de acordo com a posição designada;
00120 */
00121 void sobe_cursor(void)
00122 {
00123     ref_cursor += 1;
00124     if (ref_cursor > 3) {
00125         ref_cursor = 0;
00126     }
00127 }
00128 
00129 void desce_cursor(void)
00130 {
00131     ref_cursor -= 1;
00132     if (ref_cursor < 0) {
00133         ref_cursor = 3;
00134     }
00135 }
00136 
00137 /* FUNÇÃO "conclusao(char n)":
00138     1-)Desliga o cursor;
00139     2-)Apresenta uma mensagem de conclusão do salvamento das posições:
00140     -> Se n = 0, apresenta a mensagem de conclusão do PICK;
00141     -> Se n != 0, apresenta a mensagem de alguma das posições de DROP (depende do valor de n);
00142 */
00143 
00144 void conclusao(char n)
00145 {
00146     lcd.setCursor(TextLCD::CurOff_BlkOff);
00147     if (n == 0) {
00148         lcd.cls();
00149         lcd.locate(4,0);
00150         lcd.printf("PICK POSITION");
00151         lcd.locate(7,1);
00152         lcd.printf("SALVA!");
00153         wait(2);
00154         lcd.cls();
00155     }
00156     if (n > 0) {
00157         lcd.cls();
00158         lcd.locate(3,0);
00159         lcd.printf("DROP POSITION %d", n);
00160         lcd.locate(7,1);
00161         lcd.printf("SALVA!");
00162         wait(2);
00163         lcd.cls();
00164     }
00165 }
00166 
00167 /*FUNÇÃO "menu_jog(void)":
00168     -> Prepara a estrutura para printar no LCD as posições atuais de cada eixo.
00169 */
00170 
00171 void menu_jog(void)
00172 {
00173     lcd.cls();
00174     lcd.locate(0,1);
00175     lcd.printf("X: ");
00176     lcd.locate(0,2);
00177     lcd.printf("Y: ");
00178     lcd.locate(0,3);
00179     lcd.printf("Z: ");
00180 }
00181 
00182 /*FUNÇÃO "menu_passivo(char n)":
00183     -> A função apresenta um menu estático diferente de acordo com o valor n recebido.
00184        Os menus aqui disponiveis são estáticos pois são temporários e não possibilitam
00185        seleção de funções ou movimento do cursor. 
00186     -> Os menus estáticos são chamados para avisar ao usuário da realização de alguma 
00187        função, da inicialização da maquina e seu estado de operação.
00188 */
00189  
00190 void menu_passivo(char n)
00191 {
00192     switch(n) {
00193         
00194         // Menu estático 1 é o menu de abertura (ao ligar a máquina)
00195         case 1: {
00196             lcd.cls();
00197             lcd.locate(6,0);
00198             lcd.printf("Maquina");
00199             lcd.locate(3,1);
00200             lcd.printf("Pick-and-Place");
00201             lcd.locate(5,2);
00202             lcd.printf("RAWCAMBOLE");
00203             wait(3);
00204             lcd.cls();
00205             break;
00206         }
00207         // Menu estático 2 é o menu chamado durante a ação de referenciamento da máquina
00208         case 2: {
00209             lcd.cls();
00210             lcd.printf("Setando ponto de");
00211             lcd.locate(5,1);
00212             lcd.printf("origem!");
00213             lcd.locate(5,3);
00214             lcd.printf("AGUARDE...");
00215             break;
00216         }
00217         // Menu estático 3 é chamado durante o ciclo infinito
00218         case 3: {
00219             lcd.cls();
00220             lcd.printf("Maquina operando");
00221             lcd.locate(2,1);
00222             lcd.printf("    em ciclo");
00223             lcd.locate(2,3);
00224             lcd.printf("STOP-> Press Back");
00225             break;
00226         }
00227         // Menu estático 4 é chamado durante o ciclo único
00228         case 4: {
00229             lcd.cls();
00230             lcd.printf("Maquina operando");
00231             lcd.locate(2,1);
00232             lcd.printf("apenas um ciclo");
00233             break;
00234         }
00235             
00236     }
00237 }
00238 
00239 /* FUNÇÕES "jog_(...)":
00240     -> As funções de jog vem em pares de "on" e "off", um par para cada direção.
00241     1-) A função "on" apenas seta uma variavel em TRUE, possibilitando a movimentação
00242         em determinada direção;
00243     2-) A função "off" seta o valor mesma variavel em FALSE e para a movimentação do motor,
00244         terminando com a operação de jog em determinada direção;
00245 */
00246 
00247 void jog_pos_on(void)
00248 {
00249     jog_pos = true;
00250 }
00251 
00252 void jog_pos_off(void)
00253 {
00254     jog_pos = false;
00255     motorZ->hard_stop();
00256     motorX->hard_stop();
00257     motorY->hard_stop();
00258 }
00259 
00260 void jog_neg_on(void)
00261 {
00262     jog_neg = true;
00263 }
00264 
00265 void jog_neg_off(void)
00266 {
00267     jog_neg = false;
00268     motorZ->hard_stop();
00269     motorX->hard_stop();
00270     motorY->hard_stop();
00271 }
00272 
00273 /* FUNÇÕES "motorN_off(void)":
00274     1-) Para a movimentação do motor;
00275     2-) Seta uma variavel booleana em TRUE para indicar que o referenciamento de tal
00276         eixo está pronto;
00277 */
00278 
00279 void motorZ_off(void)
00280 {
00281     motorZ->hard_stop();
00282     referZ = true;
00283 }
00284 
00285 void motorX_off(void)
00286 {
00287     motorX->hard_stop();
00288     referX = true;
00289 }
00290 
00291 void motorY_off(void)
00292 {
00293     motorY->hard_stop();
00294     referY = true;
00295 }
00296 
00297 /*FUNÇÕES "set_aceleracoesN(...)":
00298     -> Função recebe os seguintes parâmetros:
00299     1-) Velocidade máxima;
00300     2-) Velocidade mínima;
00301     3-) Aceleração;
00302     4-) Desaceleração;
00303     -> Função seta esses valores no motor N
00304 */
00305 void set_aceleracoesZ(int maxspeed,int minspeed,int ac,int dc)
00306 {
00307     motorZ->set_max_speed(maxspeed)/2;
00308     motorZ->set_min_speed(minspeed)/2;
00309     motorZ->set_acceleration(ac)/2;
00310     motorZ->set_deceleration(dc)/2;
00311 }
00312 
00313 void set_aceleracoesX(int maxspeed,int minspeed,int ac,int dc)
00314 {
00315     motorX->set_max_speed(maxspeed);
00316     motorX->set_min_speed(minspeed);
00317     motorX->set_acceleration(ac);
00318     motorX->set_deceleration(dc);
00319 }
00320 
00321 void set_aceleracoesY(int maxspeed,int minspeed,int ac,int dc)
00322 {
00323     motorY->set_max_speed(maxspeed);
00324     motorY->set_min_speed(minspeed);
00325     motorY->set_acceleration(ac);
00326     motorY->set_deceleration(dc);
00327 }
00328 
00329 /*FUNÇÕES "save_pick_pos()" E "save_dropN":
00330     1-) Função captura as posições atuais dos 3 motores e armazenam seus valores
00331         nos atributos respectivos de seu objeto respectivo.
00332     2-) Altera o valor de uma variavel booleana para TRUE, indicando o fim da 
00333         operação de jog.
00334 */
00335 
00336 void save_pick_pos(void)
00337 {
00338     PickPos.posx = motorX->get_position();
00339     PickPos.posy = motorY->get_position();
00340     PickPos.posz = motorZ->get_position();
00341     save_pos = true;
00342 }
00343 
00344 void save_drop1()
00345 {
00346     DropPos[0].posx = motorX->get_position();
00347     DropPos[0].posy = motorY->get_position();
00348     DropPos[0].posz = motorZ->get_position();
00349     save_pos = true;
00350 }
00351 
00352 void save_drop2()
00353 {
00354     DropPos[1].posx = motorX->get_position();
00355     DropPos[1].posy = motorY->get_position();
00356     DropPos[1].posz = motorZ->get_position();
00357     save_pos = true;
00358 }
00359 
00360 void save_drop3()
00361 {
00362     DropPos[2].posx = motorX->get_position();
00363     DropPos[2].posy = motorY->get_position();
00364     DropPos[2].posz = motorZ->get_position();
00365     save_pos = true;
00366 }
00367 
00368 /* FUNÇÃO "reconhecimento_peca()":
00369     1-) Função primeiro usa o sensor de cor para identificar a cor predominante:
00370         verde, vermelho ou azul e também o indutivo para identificar o material;
00371         -> De acordo com a leitura do sensor, definiram-se apenas duas opções: ver-
00372            de ou não verde (se não verde, ele retorna vermelho);
00373     2-) Um reconhecimento é feito a cada 0.1 segundos por 1 segundo, e cada um dos três resultados
00374         possiveis recebe um tag:
00375         -> Tag 0 = etiqueta verde e metal;
00376         -> Tag 1 = etiqueta verde e polimero;
00377         -> Tag 2 = etiqueta vermelha, qualquer material;
00378         O valor do tag de cada medição é armazenado em uma array.
00379     3-) Em um par de loops, avalia-se qual foi o tag que mais apareceu. O que for
00380         a maioria, será considerado como o tag (ou cor) verdadeiro, que é o valor
00381         retornado pela função;
00382     4-) Esse tag servirá como o indice da struct de structs "DropPos", indicando 
00383         para qual posição de DROP ele deveria ir
00384 */
00385    
00386 int reconhecimento_peca(void)
00387 {   
00388     TCS3200 color(PC_4, PB_14, PB_1, PB_13, PB_15); // Declara os pinos para o sensor RGB
00389     DigitalIn sensor(PB_2); // Declara pino para o sensor indutivo
00390     long red, green, blue, clear; // Valores de leitura para cada cor
00391     int tagy, i = 0, j = 0, tag[10]; 
00392     int contador = 0, contador_max = 0, elemento;
00393     
00394     // Modo de operação do sensor
00395     color.SetMode(TCS3200::SCALE_20);
00396 
00397     while(i <= 9) {
00398 
00399         red = color.ReadRed();
00400         green = color.ReadGreen();
00401         blue = color.ReadBlue();
00402         clear = color.ReadClear();
00403         
00404         if(green > 30) {
00405             tagy = 2;
00406         }
00407         if((green <= 30)&&(sensor)) {
00408             tagy = 0;
00409         }
00410         if((green <= 30) && (!sensor)) {
00411             
00412             tagy = 1;
00413         }
00414 
00415         tag[i] = tagy;
00416         i = i + 1;
00417         wait(0.1);
00418         
00419     }
00420     
00421     // Loops para avaliar qual tag aparece mais
00422     
00423     for (i = 0; i <= 9; i++) {
00424         for (j = 0; j <= 9; j++) {
00425             if (tag[i] == tag[j]) {
00426                 contador += 1;
00427             }
00428         }
00429         if (contador > contador_max) {
00430             contador_max = contador;
00431             elemento = tag[i];
00432         }
00433         contador = 0;
00434     }
00435    
00436     return elemento;
00437 }
00438 
00439 /* FUNÇÃO "sefta_origem()":
00440     -> A função realiza o referenciamento completo para os 3 eixos, um de cada vez;
00441     -> Por ser uma função mais longa, o processo é detalhado dentro da própria função;
00442 */
00443 
00444 void seta_origem()
00445 {
00446     menu_passivo(2);
00447     
00448     //Seta uma alta velocidade para o referenciamento
00449     
00450     set_aceleracoesZ(3500/2,1200,100/2,100/2); 
00451     set_aceleracoesX(4000,3000,100,100); 
00452     set_aceleracoesY(4000,3000,100,100); 
00453     
00454     InterruptIn confirma(PC_13);
00455     confirma.mode(PullUp);
00456     
00457     while(1) {
00458         
00459         
00460         InterruptIn FDC(PC_5);
00461         FDC.fall(&motorZ_off); 
00462         FDC.mode(PullUp);
00463         
00464         confirma.fall(&motorZ_off);
00465         
00466         //Chamada do fim de curso para a função de interrupção
00467         
00468         
00469         if (referZ == false) {
00470             motorZ->run(StepperMotor::BWD);
00471             motorZ->wait_while_active();
00472         }
00473 
00474         // Se a interrupção for chamada, a variavel referZ se torna TRUE, acionando
00475         // os comandos a seguir.
00476 
00477         else {
00478             motorZ->move(StepperMotor::FWD,1000); //Leve recuo
00479             motorZ->wait_while_active();
00480             motorZ->set_home(); // Seta posição de Home
00481             int HomePosition = motorZ->get_position();
00482             printf("Posicao Home = %d\r\n",HomePosition); //Verificar que HomePosition = 0
00483             referZ = false;
00484             break;   //Quebra do loop while, pois referenciamento do motor foi feito
00485         }
00486 
00487     }
00488     
00489     while(1) {
00490             
00491         // Motor continua andando em uma só direção enquanto variavel referX estiver
00492         // em FALSE
00493         
00494         InterruptIn FDC(PC_5);
00495         FDC.fall(&motorX_off); 
00496         FDC.mode(PullUp);
00497         
00498         if (referX == false) {
00499             motorX->run(StepperMotor::BWD);
00500             motorX->wait_while_active();
00501         }
00502         
00503 
00504         // Se a interrupção for chamada, a variavel referX se torna TRUE, acionando
00505         // os comandos a seguir.
00506 
00507         else {
00508             motorX->move(StepperMotor::FWD,1000); //Leve recuo
00509             motorX->wait_while_active();
00510             motorX->set_home(); // Seta posição de Home
00511             int HomePosition = motorX->get_position();
00512             referX = false;
00513             break;   //Quebra do loop while, pois referenciamento do motor foi feito
00514         }
00515 
00516     }
00517     
00518     referY = false; //Caso os botôes de fim de curso sejam apertados sem querer antes de seus zeramentos, tem certeza que vai entrar no loop, ja que são interrupçõs
00519     
00520     while(1) {
00521         
00522         
00523         InterruptIn FDC(PC_5);
00524         FDC.fall(&motorY_off); 
00525         FDC.mode(PullUp);
00526         //Motor continua andando em uma só direção enquanto variavel referX estiver
00527         // em FALSE
00528 
00529         if (referY == false) {
00530             motorY->run(StepperMotor::BWD);
00531             motorY->wait_while_active();
00532         }
00533 
00534         // Se a interrupção for chamada, a variavel referX se torna TRUE, acionando
00535         // os comandos a seguir.
00536 
00537         else {
00538             motorY->move(StepperMotor::FWD,1000); //Leve recuo
00539             motorY->wait_while_active();
00540             motorY->set_home(); // Seta posição de Home
00541             int HomePosition = motorY->get_position();
00542             printf("Posicao Home = %d\r\n", HomePosition); //Verificar que HomePosition = 0
00543             referY = false;
00544             break;   //Quebra do loop while, pois referenciamento do motor foi feito
00545         }
00546 
00547     }
00548 }
00549 
00550 /* FUNÇÃO "muda_velocidade(void)":
00551     1-) Acrescenta-se 1 à variavel "change", que indica qual dos sets será sele-
00552         cionado.
00553     2-) De acordo com o valor da variável "change", um dos sets é ativado para 
00554         todos os motores. 
00555     3-) De acordo com o valor da variáve l "change", um dos três leds é aceso
00556         para indicar a velocidade atual.
00557 */
00558 
00559 void muda_velocidade(void)
00560 { 
00561     change += 1;
00562     
00563     // O valor máximo de "change" é 2, então ao chegar em 3, é zerado novamente 
00564     if (change == 3) {
00565         change = 0;
00566     }
00567     set_aceleracoesZ(set[change].maxspeed/2,set[change].minspeed/2,set[change].ac/2,set[change].dc/2);
00568     set_aceleracoesX(set[change].maxspeed,set[change].minspeed,set[change].ac,set[change].dc);
00569     set_aceleracoesY(set[change].maxspeed,set[change].minspeed,set[change].ac,set[change].dc);
00570     
00571     switch(change) {
00572         case 0: {
00573             DigitalOut LED1(PC_1);
00574             LED1 = 1;
00575             DigitalOut LED2(PA_1);
00576             LED2 = 0;
00577             DigitalOut LED3(PA_4);
00578             LED3 = 0; 
00579             break; 
00580         }
00581         case 1: {
00582             DigitalOut LED1(PC_1);
00583             LED1 = 0;
00584             DigitalOut LED2(PA_1);
00585             LED2 = 1;
00586             DigitalOut LED3(PA_4);
00587             LED3 = 0;     
00588             break;
00589         }
00590         case 2: {
00591             DigitalOut LED1(PC_1);
00592             LED1 = 0;
00593             DigitalOut LED2(PA_1);
00594             LED2 = 0; 
00595             DigitalOut LED3(PA_4);
00596             LED3 = 1;  
00597             break;
00598         }
00599     }
00600 
00601 }
00602 
00603 /* FUNÇÃO "ativa_eixoN(void)":
00604     -> Desativa a variável referente aos outros eixos, setando-as como "false", 
00605        e ativa a do eixo N, setando-a como TRUE
00606 */
00607 
00608 void ativa_eixoX(void)
00609 {
00610     dirx = true;
00611     dirz = false;
00612     diry = false;
00613     muda_velocidade();
00614 }
00615 
00616 void ativa_eixoY(void)
00617 {   
00618     dirx = false;
00619     dirz = false;
00620     diry = true;
00621     muda_velocidade();
00622 }
00623 
00624 void ativa_eixoZ(void)
00625 {
00626     dirx = false;
00627     dirz = true;
00628     diry = false;
00629     muda_velocidade();
00630 }
00631 
00632 /* FUNÇÃO "selecao_funcao(void)":
00633     -> A função apenas coloca o valor de "enable" em TRUE. No loop principal do
00634        programa, o enable em TRUE permite a seleção de alguma das funções do menu
00635        por parte do usuário.  
00636 */
00637 
00638 void selecao_funcao(void)
00639 {
00640     enable = true;
00641 }
00642 
00643 /* FUNÇÃO "disco_disco(void)":
00644     -> Função completamente inútil, feita apenas para divertir o usuário;
00645 */
00646 
00647 void disco_disco(void) {
00648     unsigned char i = 0;
00649     while (i < 50) {
00650         lcd.cls();
00651         lcd.setBacklight(TextLCD::LightOn);
00652         lcd.printf(" DI$CO DI$CO DI$CO");
00653         lcd.locate(0,1);
00654         lcd.printf(" DI$CO DI$CO DI$CO");
00655         lcd.locate(0,2);
00656         lcd.printf(" DI$CO DI$CO DI$CO");
00657         lcd.locate(0,3);
00658         lcd.printf(" DI$CO DI$CO DI$CO");
00659         wait(0.05);
00660         lcd.setBacklight(TextLCD::LightOff);
00661         wait(0.05);
00662         i += 1;
00663     }
00664     lcd.setBacklight(TextLCD::LightOn);
00665 }
00666 
00667 /* FUNÇÃO "menu_dinamico(char n)":
00668     -> Um "menu_dinamico" é considerado, neste programa, como um menu em que o 
00669        usuario consegue movimentar o cursor e selecionar funções (ou seja, é a
00670        parte interativa do menu). 
00671     1-) Os botões "Confirma","Jog_button_pos" e "Jog_button_neg" são setados para 
00672         chamar certas funções relacionadas à navegação no menu ao detectar uma 
00673         borda de subida (comando "rise");
00674     2-) Há dois menus dinâmicos no projeto:
00675         A-) A tela inicial, contendo as opções:
00676             ->Cycle Start (inicia a operação ciclica de Pick/Place);
00677             ->Set positions (transição para o próximo menu dinâmico);
00678             ->Referenciamento (realiza operação de zeramento dos eixos novamente);
00679             ->Disco Disco! (executa a função "disco_disco"); 
00680         B-) A tela referente às posições à serem salvas:
00681             ->Set Pick (inicia o jog para salvar a posição de Pick);
00682             ->Set Drop 1 (inicia o jog para salvar a posição de Drop 1);
00683             ->Set Drop 2 (inicia o jog para salvar a posição de Drop 2);
00684             ->Set Drop 3 (inicia o jog para salvar a posição de Drop 3);
00685     3-) A variável "n" que entra como input da função define qual dos menus é 
00686         chamado.
00687 */
00688     
00689 void menu_dinamico(char n)
00690 {
00691 
00692     confirma.fall(&selecao_funcao);
00693     jog_button_pos.fall(&sobe_cursor);
00694     jog_button_neg.fall(&desce_cursor);
00695 
00696     switch(n) {
00697         case 0: {
00698             lcd.setCursor(TextLCD::CurOn_BlkOn);
00699             lcd.cls();
00700             lcd.locate(1,0);
00701             lcd.printf("->Cycle start");
00702             lcd.locate(1,1);
00703             lcd.printf("->Set positions");
00704             lcd.locate(1,2);
00705             lcd.printf("->Referenciamento");
00706             lcd.setAddress(0,ref_cursor);
00707             lcd.locate(1,3);
00708             lcd.printf("->DISCO DISCO!");
00709             lcd.setAddress(0,ref_cursor);
00710             break;
00711         }
00712         case 1: {
00713             lcd.setCursor(TextLCD::CurOn_BlkOn);
00714             lcd.cls();
00715             lcd.locate(1,0);
00716             lcd.printf("->Set Pick");
00717             lcd.locate(1,1);
00718             lcd.printf("->Set Drop 1");
00719             lcd.locate(1,2);
00720             lcd.printf("->Set Drop 2");
00721             lcd.locate(1,3);
00722             lcd.printf("->Set Drop 3");
00723             lcd.setAddress(0,ref_cursor);
00724             break;
00725         }
00726         case 2: {
00727             lcd.setCursor(TextLCD::CurOn_BlkOn);
00728             lcd.cls();
00729             lcd.locate(1,0);
00730             lcd.printf("->Ciclo infinito");
00731             lcd.locate(1,1);
00732             lcd.printf("->Ciclo unico");
00733             lcd.setAddress(0,ref_cursor);
00734             break;
00735         }
00736     }
00737 }
00738 
00739 /*FUNÇÕES "cycle_stop(void)" E "cycle(char n)":
00740     A-) "cycle_stop(void)": função seta a variável "ref_cycle" como FALSE, parâ-
00741          metro que irá finalizar o ciclo ao final da rotina;
00742     B-) "cycle":
00743         -> Seta o botão "back" para chamar a função "cycle_stop" e finalizar a 
00744            operação em ciclo;
00745         1-) Leva o motor para a posição de HOME (apenas uma vez);
00746         2-) Motor vai até a posição de PICK;
00747         3-) Ativa a função "reconhecimento_peca()" para determinar aonde a peça
00748             será levada (posição de DROP);
00749         4-) Vai para a posição de DROP e solta a peça;
00750         5-) Retorna para o PICK, reiniciando o ciclo;
00751         -> OBS: A função "cycle" recebe um parâmetro n. Se esse parâmetro for 1, o
00752            ciclo é realizado apenas uma vez. Se for 0, o ciclo se repetirá até o u-
00753            suário pressionar o botão de "back".
00754 */
00755 
00756 void cycle_stop(void)
00757 {   
00758     ref_cycle = false;
00759 }
00760     
00761 void cycle(char n)
00762 {
00763     Servo garra(PA_11); // Declaração do servo
00764     garra.calibrate(0.001,90); // Calibração de sua abertura
00765     
00766     back_btn.fall(&cycle_stop);
00767     
00768     if (n == 0) {
00769         menu_passivo(3); // Ciclo infinito
00770     }
00771     
00772     if (n == 1) {
00773         menu_passivo(4); // Ciclo único
00774     }
00775     
00776     int tag; // Tag que sairá como resultado da função de reconhecimento da peça
00777     
00778     printf("Comeco do ciclo!\r\n");
00779     
00780     //Alta velocidade para retornar para a posição de home
00781     
00782     set_aceleracoesZ(set[2].maxspeed/2,set[2].minspeed/2,set[2].ac/2,set[2].dc/2);
00783     set_aceleracoesX(set[2].maxspeed,set[2].minspeed,set[2].ac,set[2].dc);
00784     set_aceleracoesY(set[2].maxspeed,set[2].minspeed,set[2].ac,set[2].dc);
00785     
00786     motorZ->go_home();
00787     motorZ->wait_while_active();
00788     
00789     motorX->go_home();
00790     motorX->wait_while_active();
00791     
00792     motorY->go_home();
00793     motorY->wait_while_active();
00794     
00795     
00796     // Seta velocidaes/acelerações para o ciclo
00797     
00798     set_aceleracoesZ(set[0].maxspeed,set[0].minspeed,set[0].ac,set[0].dc);
00799     set_aceleracoesX(set[1].maxspeed,set[1].minspeed,set[1].ac,set[1].dc);
00800     set_aceleracoesY(set[1].maxspeed,set[1].minspeed,set[1].ac,set[1].dc);
00801 
00802     ref_cycle = true;
00803     
00804     garra = 1;
00805     
00806     while(1) {
00807         
00808         // Vai para a posição de PICK
00809         motorX->go_to(PickPos.posx);
00810         motorX->wait_while_active();
00811         motorY->go_to(PickPos.posy);
00812         motorY->wait_while_active();
00813         motorZ->go_to(PickPos.posz - 1000);
00814         motorZ->wait_while_active();
00815         
00816         tag = reconhecimento_peca(); // Reconhece a peça e qual posição de Drop irá
00817         garra = 0.7; // Fecha a garra
00818         wait(1);
00819         
00820         // Vai para a posição de DROP
00821         motorZ->move(StepperMotor::BWD,3000);
00822         motorZ->wait_while_active();
00823         motorY->go_to(DropPos[tag].posy);
00824         motorY->wait_while_active();
00825         motorX->go_to(DropPos[tag].posx);
00826         motorX->wait_while_active();
00827         motorZ->go_to(DropPos[tag].posz);
00828         motorZ->wait_while_active();
00829         wait(1);
00830         garra = 1; // Garra abre e deixa o objeto
00831         
00832         /*Se a chamada para finalizar o ciclo foi chamada, volta-se para o menu 
00833           inicial e quebra o loop while; */
00834           
00835         if ((ref_cycle == false) || (n == 1)) {
00836             enable = false;
00837             menu_dinamico(ref_menu);
00838             break;
00839         }
00840         
00841     } 
00842 }
00843 
00844 /* FUNÇÕES DE "jog_(...)":
00845     -> Chama o layout para o menu do jog;
00846     -> O botão "Confirma" é setado para salvar a posição;
00847     -> Basicamente, cada botão de jog, ao ser segurado (borda de descida) faz o motor
00848        seguir em movimento. Ao ser solto (borda de subida) faz o motor parar;
00849     -> Habilitam-se os botões dos eixos para que o usuário possa mover um eixo de
00850        cada vez. Quando o usuario apertar um desses botões, a velocidade é alterada
00851        também;
00852 */
00853 
00854 void paradaZ(void) 
00855 {
00856     motorZ->hard_stop();
00857     wait(1);
00858     int posicao = motorZ->get_position();
00859     if (posicao < 0) {
00860         motorZ->move(StepperMotor::FWD,2000);
00861     }
00862     else {
00863         motorZ->move(StepperMotor::BWD,2000);
00864     }
00865 }
00866 
00867 void paradaX(void) 
00868 {
00869     motorX->hard_stop();
00870     wait(1);
00871     int posicao = motorX->get_position();
00872     if (posicao < 0) {
00873         motorX->move(StepperMotor::FWD,3000);
00874     }
00875     else {
00876         motorX->move(StepperMotor::BWD,3000);
00877     }
00878 }
00879 void paradaY(void) 
00880 {
00881     motorY->hard_stop();
00882     wait(1);
00883     int posicao = motorY->get_position();
00884     if (posicao < 0) {
00885         motorY->move(StepperMotor::FWD,3000);
00886     }
00887     else {
00888         motorY->move(StepperMotor::BWD,3000);
00889     }
00890 }
00891 
00892 void jog(char n) {
00893         menu_jog();
00894         lcd.locate(3,0);
00895     /* 
00896        De acordo com o valor de entrada "n", seta-se o botão "Confirma" para
00897        salvar a posição de Save,Drop1, Drop2 ou Drop3;
00898     */
00899     InterruptIn confirma(PC_13); 
00900     confirma.mode(PullUp);
00901     
00902     switch(n) {
00903         case 0: {
00904             printf("Posicao de Save\r\n");
00905             lcd.printf("PICK POSITION");
00906             confirma.fall(&save_pick_pos);
00907             break;
00908         }
00909         case 1: {
00910             lcd.locate(3,0);
00911             lcd.printf("DROP POSITION %d",n);
00912             confirma.fall(&save_drop1);
00913             break;
00914         }
00915         case 2: {
00916             lcd.locate(3,0);
00917             lcd.printf("DROP POSITION %d",n);
00918             confirma.fall(&save_drop2);
00919             break;
00920         }
00921         case 3: {
00922             lcd.locate(3,0);
00923             lcd.printf("DROP POSITION %d",n);
00924             confirma.fall(&save_drop3);
00925             break;
00926         }
00927     }
00928     
00929     InterruptIn jog_button_pos(PC_12);
00930     jog_button_pos.mode(PullUp);
00931     InterruptIn jog_button_neg(PA_15);
00932     jog_button_neg.mode(PullUp);
00933     
00934     jog_button_pos.fall(&jog_pos_on);
00935     jog_button_neg.fall(&jog_neg_on);
00936 
00937     jog_button_pos.rise(&jog_pos_off);
00938     jog_button_neg.rise(&jog_neg_off);
00939 
00940     btnX.fall(&ativa_eixoX);
00941     btnY.fall(&ativa_eixoY);
00942     btnZ.fall(&ativa_eixoZ);
00943 
00944     while(save_pos == false) {
00945 
00946         float posz = motorZ->get_position();
00947         float posx = motorX->get_position();
00948         float posy = motorY->get_position();
00949         lcd.locate(3,1);
00950         lcd.printf("%.2f mm",posx*3/800);
00951         lcd.locate(3,2);
00952         lcd.printf("%.2f mm",posy*3/800);
00953         lcd.locate(3,3);
00954         lcd.printf("%.2f mm",posz*5/800);
00955 
00956         if (jog_pos == true) {
00957             if (dirx == true) {
00958                 InterruptIn FDC(PC_5);
00959                 FDC.mode(PullUp);
00960                 FDC.fall(&paradaX);
00961                 motorX->run(StepperMotor::FWD);
00962                 motorX->wait_while_active();
00963             }
00964             if (diry == true) {
00965                 InterruptIn FDC(PC_5);
00966                 FDC.mode(PullUp);
00967                 FDC.fall(&paradaY);
00968                 motorY->run(StepperMotor::FWD);
00969                 motorY->wait_while_active();
00970             }
00971             if (dirz == true) {
00972                 InterruptIn FDC(PC_5);
00973                 FDC.mode(PullUp);
00974                 FDC.fall(&paradaZ);
00975                 motorZ->run(StepperMotor::FWD);
00976                 motorZ->wait_while_active();
00977             }
00978         }
00979 
00980         if (jog_neg == true) {
00981             if (dirx == true) {
00982                 InterruptIn FDC(PC_5);
00983                 FDC.mode(PullUp);
00984                 FDC.fall(&paradaX);
00985                 motorX->run(StepperMotor::BWD);
00986                 motorX->wait_while_active();
00987             }
00988             if (diry == true) {
00989                 InterruptIn FDC(PC_5);
00990                 FDC.mode(PullUp);
00991                 FDC.fall(&paradaY);
00992                 motorY->run(StepperMotor::BWD);
00993                 motorY->wait_while_active();
00994             }
00995             if (dirz == true) {
00996                 InterruptIn FDC(PC_5);
00997                 FDC.mode(PullUp);
00998                 FDC.fall(&paradaZ);
00999                 motorZ->run(StepperMotor::BWD);
01000                 motorZ->wait_while_active();
01001             }
01002         }
01003     }
01004     save_pos = false;
01005     ref_menu = 1;
01006     flag = true;
01007     conclusao(n);
01008     menu_dinamico(ref_menu);
01009 }
01010 
01011 /* FUNÇÃO "back_op(void)":
01012     -> Se a tela for referente a um sub-menu, a função irá fazer retornar para o
01013        menu principal. Caso contrário, nada ocorre;
01014 */
01015   
01016 void back_op(void)
01017 {
01018     ref_cursor = 4; // O valor de ref_cursor em 4 chama um case que chama a primeira tela dinâmica;
01019     enable = true;
01020     printf("BACK\r\n");
01021 }
01022 
01023 int main()
01024 {
01025     //Prepara os 3 sets de velocidade, com velocidaes e acelerações variadas.
01026 
01027     set[0].maxspeed = 2000;
01028     set[0].minspeed = 1000;
01029     set[0].ac = 100;
01030     set[0].dc = 100;
01031 
01032     set[1].maxspeed = 4000;
01033     set[1].minspeed = 3000;
01034     set[1].ac = 100;
01035     set[1].dc = 100;
01036 
01037     set[2].maxspeed = 4200;
01038     set[2].minspeed = 3500;
01039     set[2].ac = 100;
01040     set[2].dc = 100;
01041 
01042     
01043     //Seta comunicação SPI
01044     DevSPI dev_spi(D11, D12, D13);
01045 
01046     //Inicialização dos componentes dos motores
01047     motorZ = new L6474(D2, D8, D7, D9, D10, dev_spi);
01048     motorX = new L6474(D2, D8, D4, D3, D10, dev_spi);
01049     motorY = new L6474(D2, D8, D5, D6, D10, dev_spi);
01050 
01051     if (motorZ->init(&init) != COMPONENT_OK) {
01052         exit(EXIT_FAILURE);
01053     }
01054     if (motorX->init(&init) != COMPONENT_OK) {
01055         exit(EXIT_FAILURE);
01056     }
01057     if (motorY->init(&init) != COMPONENT_OK) {
01058         exit(EXIT_FAILURE);
01059     }
01060 
01061     // Seta todos os motores para que trabalhem com microstep de 8
01062     motorZ->set_step_mode(StepperMotor::STEP_MODE_1_4);
01063     motorX->set_step_mode(StepperMotor::STEP_MODE_1_4);
01064     motorY->set_step_mode(StepperMotor::STEP_MODE_1_4);
01065     
01066     //Seta velocidades e acelerações inciciais
01067     set_aceleracoesZ(set[0].maxspeed,set[0].minspeed,set[0].ac,set[0].dc);
01068     set_aceleracoesX(set[0].maxspeed,set[0].minspeed,set[0].ac,set[0].dc);
01069     set_aceleracoesY(set[0].maxspeed,set[0].minspeed,set[0].ac,set[0].dc);
01070 
01071     lcd.setCursor(TextLCD::CurOn_BlkOn); // Liga o cursor
01072     
01073     lcd.setBacklight(TextLCD::LightOn); // Liga o backlight do LCD
01074     lcd.setAddress(0,0);
01075     lcd.setCursor(TextLCD::CurOff_BlkOff); // Desliga o cursor para o menu estático
01076     menu_passivo(1);
01077 
01078     lcd.setCursor(TextLCD::CurOn_BlkOn); // Liga o cursor novamente pro usuario poder mexe-lo
01079     menu_dinamico(0);
01080 
01081 
01082     /* Loops principais do funcionamento do motor:
01083     
01084     -> Os dois loops aninhados (while e do_while) definem o funcionamento conti-
01085        nuo da máquina.
01086     -> O funcionamento geral é o seguinte: 
01087         1-) O do_while depende de um parametro booleano chamado "flag". O flag
01088             permanece em TRUE até alguma função do menu ser chamada. Qualquer fun-
01089             ção chamada no Menu que resulte em uma ação seta o flag como FALSE.
01090             Por exemplo, cliquar em alguma opção do menu que leve para outro sub
01091             menu não setaria o "flag" como FALSE, pois nenhuma ação real foi feita.
01092             Agora, entrar em qualquer operação de jog ou de ciclo, setaria a variavel
01093             em FALSE, finalizando o do_while após o término da ação.
01094         2-) Para evitar que a máquina pare de funcionar depois do término de uma ação,
01095             aninha-se o do_while em um loop infinito while(1). Ao sair do do_while, o
01096             flag retorna para TRUE, permitindo que o usuario possa escolher alguma ação
01097             novamente. 
01098     -> Variaveis importantes:
01099         a) "ref_menu": Seu valor indica em qual dos menus o usuário está no momento;
01100         b) "ref_cursor": Seu valor indica em qual das linhas o cursor está localizado;
01101         c) "enable": A variavel "enable" possui FALSE como estado padrão. Quando o botão
01102             de "confirma" for pressionado, ele comuta para TRUE. Quando isso acontece, 
01103             avalia-se o valor de "ref_menu" e "ref_cursor" para indicar que ação deve
01104             ocorrer naquele momento (ir para outro submenu, referenciar, jog, etc.).
01105             Seu valor sempre retorna para FALSE após qualquer operação ou ao fim do 
01106             do_while;
01107     -> Cada menu dinâmico disponivel tem seu aninhamento com switch-case para avaliar
01108        as funções que podem ser chamadas dentro dele; 
01109     -> OBS: O ref_cursor só vai de 0 a 3 (linha 1 a 4 do LCD). Entretanto, coloca-se nos 
01110        dois sub-menus um case em que ref_cursor vale 4. No uso normal do cursor, isso nunca
01111        vai ocorrer. Esse valor é SETADO para o ref_cursor forçadamente quando o usuario apertar
01112        o botão de BACK. Isso faz com que acione-se uma operação unica para este botão, retornando
01113        para o menu principal. 
01114         */
01115     while(1) {
01116         
01117         /* Redeclaração da maioria dos pinos. Isso é feito para evitar qualquer
01118            confito entre as chamadas de cada pino. Muitos são desativados quando
01119            o sensor RGB é declarado, então esse processo faz com que nenhuma função
01120            seja perdida no meio do uso da máquina */
01121            
01122         InterruptIn confirma(PC_13);
01123         confirma.mode(PullUp);
01124         InterruptIn jog_button_pos(PC_12);
01125         jog_button_pos.mode(PullUp);
01126         InterruptIn jog_button_neg(PA_15);
01127         jog_button_neg.mode(PullUp);
01128         InterruptIn btnX(PC_3); // Botão para selecionar o eixo X
01129         btnX.mode(PullUp);
01130         InterruptIn btnY(PC_2); // Botão para selecionar o eixo Y
01131         btnY.mode(PullUp);
01132         InterruptIn btnZ(PC_0); // Botão para selecionar o eixo Z
01133         btnZ.mode(PullUp);
01134         
01135         InterruptIn back_btn(PB_7);
01136         back_btn.mode(PullUp);
01137         
01138         confirma.fall(&selecao_funcao);
01139         
01140         jog_button_pos.fall(&sobe_cursor);
01141         jog_button_neg.fall(&desce_cursor);
01142         
01143         btnX.fall(&ativa_eixoX);
01144         btnY.fall(&ativa_eixoY);
01145         btnZ.fall(&ativa_eixoZ);
01146         
01147         do {
01148             wait(0.1);
01149             lcd.locate(0,ref_cursor); // Movimentação do cursor
01150             
01151             // Menu prinicipal
01152             if (ref_menu == 0) {
01153                 if (enable == true) {
01154                     switch(ref_cursor) {
01155                         case 0: {
01156                             // Mudança para o submenu com as opções de ciclo
01157                             ref_cursor = 0;
01158                             lcd.locate(0,ref_cursor);
01159                             ref_menu = 2;
01160                             menu_dinamico(ref_menu);
01161                             enable = false;
01162                             break;
01163                         }
01164                         case 1: {
01165                             // Mudança para o submenu com as 4 posições a serem salvas
01166                             ref_cursor = 0;
01167                             lcd.locate(0,ref_cursor);
01168                             ref_menu = 1;
01169                             menu_dinamico(ref_menu);
01170                             enable = false;
01171                             break;
01172                         }
01173                         case 2: {
01174                             // Operação de referenciamento
01175                             //int tag = reconhecimento_peca();
01176                             seta_origem();
01177                             flag = false;
01178                             break;
01179                         }
01180                         case 3: {
01181                             // Operação disco-disco
01182                             disco_disco();
01183                             flag = false;
01184                             break;
01185                         }
01186                     }
01187                 }
01188             }
01189     
01190             // Sub-menu para as posições a serem salvas
01191             
01192             if(ref_menu == 1) {
01193                 back_btn.mode(PullUp);
01194                 back_btn.fall(&back_op);
01195                 if (enable == true) {
01196                     switch(ref_cursor) {
01197                         case 0: {
01198                             // Posição de Pick
01199                             jog(0);
01200                             flag = false;
01201                             InterruptIn back_btn(PB_7);
01202                             back_btn.mode(PullUp);
01203                             back_btn.fall(&back_op);
01204                             break;
01205                         }
01206                         case 1: {
01207                             // Posição de Drop 1
01208                             jog(1);
01209                             flag = false;
01210                             InterruptIn back_btn(PB_7);
01211                             back_btn.mode(PullUp);
01212                             back_btn.fall(&back_op);
01213                             break;
01214                         }
01215                         case 2: {
01216                             // Posição de Drop 2
01217                             jog(2);
01218                             flag = false;
01219                             InterruptIn back_btn(PB_7);
01220                             back_btn.mode(PullUp);
01221                             back_btn.fall(&back_op);
01222                             break;
01223                         }
01224                         case 3: {
01225                             // Posição de Drop 3
01226                             jog(3);
01227                             flag = false;
01228                             InterruptIn back_btn(PB_7);
01229                             back_btn.mode(PullUp);
01230                             back_btn.fall(&back_op);
01231                             break;
01232                         }
01233                         case 4: {
01234                             // Case para quando botão BACK for pressionado. Retorna para o menu inicial
01235                             ref_cursor = 0;
01236                             lcd.locate(0,ref_cursor);
01237                             ref_menu = 0;
01238                             menu_dinamico(ref_menu);
01239                             enable = false;
01240                             break;
01241                         }
01242                     }
01243                 }
01244             }
01245             
01246             if (ref_menu == 2) {
01247                 back_btn.fall(&back_op);
01248                 back_btn.mode(PullUp);
01249                 if (enable == true) {
01250                     switch(ref_cursor) {
01251                         case 0: {
01252                             // Opção para ciclo infinito
01253                             cycle(0);
01254                             flag = false;
01255                             break;
01256                         }
01257                         case 1: {
01258                             // Opção para ciclo único
01259                             cycle(1);
01260                             flag = false;
01261                             break;
01262                         }
01263                         case 4: {
01264                             // Case para quando botão BACK for pressionado. Retorna para o menu inicial
01265                             ref_cursor = 0;
01266                             lcd.locate(0,ref_cursor);
01267                             ref_menu = 0;
01268                             menu_dinamico(ref_menu);
01269                             enable = false;
01270                             break;
01271                         }
01272                     }
01273                 }
01274             }
01275             
01276         } while(flag == true);
01277         
01278         flag = true;
01279         enable = false;
01280         menu_dinamico(ref_menu);
01281     }
01282 }