Estou desenvolvendo um programa para posicionar um embolo mecânico, não há necessidade de muita precisão, eu coloquei uma roda dentada em um eixo que gera 20 pulsos por volta e habilitei a seguinte interrupção:

attachInterrupt(digitalPinToInterrupt(2), encoder, CHANGE);

A ISR está assim:

void encoder(){
   if(dir == 1)pulso ++;                    // Se o driver gira em um sentido incrementa variável
   if(dir == 0)pulso --;                      // Se o driver gira em outro sentido decrementa variável
   conta ++;                                   // Esas são instruções apenas
   if(conta == 10){                          // para confimrar
          digitalWrite(led, !digitalRead(led));             // que os pulsos chegam no pino 2
          conta = 0;                                              //
          }
}

a variável dir eu a defino como "0" quando o driver faz o motor girar em um sentido e defino como "1" quando ele gira em outro sentido.

Quando eu faço:

while(pulso < x){
           Serial.println(pulso);
}

O while funciona perfeito, mas se eu tirar o Serial.println() de dentro do while:

while(pulso < x){

}

Ele não funciona mais, eu não preciso ficar enviando nada de dentro do while, apenas quero que o programa espere até o embolo chegar em uma determinada posição de acordo com os pulsos enviados.

Alguém poderia me ajudar?

Exibições: 357

Responder esta

Respostas a este tópico

olá Renato.

      Você pode estar sendo vítima de alguma otimização "agressiva" que o Compilador possa estar fazendo, especificamente quando vc omite o "core" do while. Já vi compiladores fazerem exatamente isso, o que poderíamos chamar de "esperteza" do compilador, ou então uma inteligência artificial "jeca".

      Se possível, post seu código completo, de forma que se possa ver como estão as declarações de variáveis (isto é bastante importante pra se avaliar), e mesmo possamos compilá-lo e espionar o Assembly dele, e ver se sobrou algo no "core" do while, ou se o Compilador mandou aquele "vazio" para o "ether",

      Ah sim, qual Arduino vc está usando?

      Abrçs,

      Elcids

Boa tarde Elcids.

Primeiro grato pela atenção, estava fazendo o código e costumo usar alguns Serial.print para debug, o código já estava bem mais extenso quando resolvi começar a limpar esses comandos de debug, aí apareceu o problema, então comecei um código do zero apenas com esse problema para tentar solucionar.

Segue o código completo que está apresentando a falha, mas antes tem alguns termos que não havia visto ainda, talvez por eu ter aprendido o que eu sei de programação sozinho, sem um curso ou orientador, então o que vem a ser "core" do while  "vazio" para o "ether", mas isso se não for tomar muito seu tempo, o que mais me importa é o não funcionamento do while.

/********************************************************************************
* Area para inclusão de bibliotecas *
****************************************************************************** */

/********************************************************************************
* Area para definições gerais *
****************************************************************************** */

#define rpwm 8 // Pino de PWM direito
#define lpwm 9 // Pino de PWM esquerdo
#define r_en 10 // Habilita ponte da direita
#define l_en 11 // Habilita ponte da esquerda
#define pot A0 // Entrada analógica para testes
#define enc_a 2 // Pino A do ENCODER
#define enc_b 3 // Pino B do ENCODER
#define aberto 6 // Fim de curso embolo aberto
#define fechado 7 // Fim de curso embolo fechado
#define led 13 // Apenas para debug

/********************************************************************************
* Area para criação de variáveis globais *
****************************************************************************** */
int pulso = 0; // Contagem de pulso do motor de embutir
bool dir = 0; // Flag para saber a direção do motor de embutir
int conta = 0; // Apenas para debug

/********************************************************************************
* Protótipos de funções *
****************************************************************************** */
void encoder(); // Coloquei o nome encoder mas é apenas um pino
void avancar();
void recuar();
void parar();

/********************************************************************************
* Função Setup do ARDUINO *
****************************************************************************** */
void setup() {
pinMode(rpwm, OUTPUT);
pinMode(lpwm, OUTPUT);
pinMode(r_en, OUTPUT);
pinMode(l_en, OUTPUT);
pinMode(enc_a, INPUT);
pinMode(enc_b, INPUT);
pinMode(aberto, INPUT);
pinMode(fechado, INPUT);
pinMode(pot, INPUT);
pinMode(led, OUTPUT);
digitalWrite(r_en, HIGH); // Habilita lado direito da ponte H
digitalWrite(l_en, HIGH); // Habilita lado esquerdo da ponte H
Serial.begin(9600);
attachInterrupt(digitalPinToInterrupt(enc_a), encoder, CHANGE);
}

/********************************************************************************
* Função principal do sistema (loop infinito) *
****************************************************************************** */
void loop() {
recuar(); // Habilita driver para recuar embolo
while(digitalRead(aberto) == 1){ // Aguarda recuar embolo
// Aqui o while funciona
}
parar(); // Desliga o motor
Serial.println("Parou"); // Apenas para debug
avancar(); // Habilita driver para avancar embolo
delay(2500); // Espera 2,5s
parar(); // Desliga o motor
Serial.println("Avançou 2,5s"); // Apenas para debug
pulso = 0; // zera a variável por desencargo de conciência
avancar(); // Habilita driver para avancar embolo
while(pulso < 200){ // Aqui é que emperra
// Esse while não funciona
}
parar();
while(true){

}
}

/********************************************************************************
* ISR do encoder para contar pulsos do motor *
****************************************************************************** */
void encoder(){
if(dir == 1)pulso ++; // Se o driver gira em um sentido incrementa variável
if(dir == 0)pulso --; // Se o driver gira em outro sentido decrementa variável
conta ++; // Esas são instruções apenas
if(conta == 10){ // para confimrar
digitalWrite(led, !digitalRead(led)); // que os pulsos chegam no pino 2
conta = 0; //
}
}
/********************************************************************************
* Função para avancar o embolo *
****************************************************************************** */
void avancar(){ // Configura ponte H para avancar embolo
dir = 1;
digitalWrite(rpwm, HIGH);
digitalWrite(lpwm, LOW);
delay(50);
}

/********************************************************************************
* Função para recuar o embolo *
****************************************************************************** */
void recuar(){ // Configura ponte H para recuar embolo
dir = 0;
digitalWrite(lpwm, HIGH);
digitalWrite(rpwm, LOW);
}

/********************************************************************************
* Função para parar o motor totalmente *
****************************************************************************** */
void parar(){ // Configura ponte H para parar embolo
digitalWrite(rpwm, LOW);
digitalWrite(lpwm, LOW);
}

Desculpa, eu tentei colar com a formatação mas não consegui

   Adicione como um arquivo em anexo.

   Assim fica mais fácil para análise e melhora a estética da postagem aqui no fórum.

Estou usando o ARDUINO MEGA 2560

olá novamente Renato.

      Acredito que seu problema não era otimização do Compilador. Ao ver seu código, imediatamente uma coisa me chamou  a atenção, e por uma imensa coincidência, expliquei exatamente sobre isso em um outro tópico hoje. Assim, peço que vc dê uma olhada neste link, onde explico a questão no item 3)  e falo sobre a questão do "volatile":    "questao 3"

      Parece que o problema do seu código é falta de "atomicidade" quando seu "while" acessa a variável "pulso" (além de vc não ter usado o qualificador "volatile"). Se for isso, por uma sorte (ou azar, dependendo de como se vê), quando vc usou o Serial.print dentro do while, ocorreu um lapso na temporização, a qual coincidia sempre com um momento que a ISR não era executada, e com isso a leitura era de forma atomizada. Mas quando vc retirou o Serial.print,  certamente mudou a temporização, mesmo que o mínimo, e já seria o suficiente para a falta de atomicidade ter efeito (fazendo vc sempre ler valores incorretos do "pulso").

      Veja como foi ajustado:   pos_Embolo_02.zip

      Aguardo sua verificação, pois não tive tempo de testar.

     Observe que não afirmei categoricamente que o problema era falta de "atomicidade", pois há um "ingrediente" no seu código, que poderia levar a outras conclusões (mais relacionadas ao "volatile").

 

     Uma sugestão de melhoria:  caso vc teste o código que postei e confirme que o problema foi resolvido, substitua o "int", por "unsigned short", e coloque verificação da contagem minima na ISR, impedindo que a contagem vá além do zero (isto pode acontecer mesmo com "unsigned short").

      Abrçs,

      Elcids

olá Renato.

      Ontem não tive tempo de testar seu Sistema. Mas agora já posso te passar os resultados.

      Testei seu código original, aquele com o Serial.print  dentro do "while",  e realmente funcionou conforme vc descreveu.

      Em seguida testei o mesmo código, porém semSerial.print  no "while", e confirmo que não detectou a contagem chegar a 200.

      Finalmente testei o código que eu "ajustei" e postei ontem. Ele funcionou conforme eu esperava. Isso confirmou minhas suspeitas:  seu problema era o fato de não ter declarado a variável "pulso"  como "volatile". Porém, como eu disse também no post anterior, isso não é suficiente, pois é uma variável de 2 bytes (ou 16 bits), o que exige que vc garanta que a leitura da variável seja feita de forma "atômica" (conforme eu fiz no código que postei ontem).

      Mas ao invés de fazer uma leitura "atômica", é mais simples ler uma Flag sinalizadora, neste caso uma Flag que chamei de "trigger_Contagem", a qual está em uma nova versão do código.

      Normalmente essa técnica da Flag, é que usamos. Mas caso vc precise ler alguma variável que está sendo modificada dentro da ISR, lembre-se que ela deve ser declarada como sendo "volatile", e caso ela seja de 16 bits (ou mais), vc também deve fazer uma leitura "atômica" (veja o exemplo que postei anteriormente). Nem todos os Processadores exigem que se faça a leitura atômica, vai depender da Arquitetura do Processador. No caso do AVR de 8 bits (usado no Arduino UNO, Mega, Nano, Leonardo, etc), é preciso fazer a leitura atômica.

      Também simulei seu Sistema no Proteus. Claro, a parte da roda com furos, tive que fazer algo similarmente próximo, mas funcionou como esperado. Testei na simulação, todas as variantes do código que descrevi acima, para confirmar o resultado de cada uma dessas variantes.

      O resultado da simulação do último código com a Flag sinalizadora, vc pode ver na figura a seguir:

(clique na figura para "zoom")

      O código com a técnica da Flag sinalizadora, está aqui:   pos_Embolo_03.zip

      Neste arquivo zip, vc encontrará também o arquivo para simulação no Proteus.

      Espero ter ajudado,

      Abrçs,

      Elcids

Ajudar? Ajudar é quando carregamos uma sacola para uma senhora idosa na feira, isso foi mais que uma aula.

Não consegui ainda parar para por em prática, mas consegui entender os conceitos, principalmente o que diz respeito ao atendimento da interrupção pelo μC, onde normalmente os registradores que podem ser modificados pela ISR são salvos e depois recuperados ao voltar da ISR.

Parece que é aí que o problema ocorre.

Muito obrigado pela aula.

Quando estiver funcionando eu posto o resultado.

olá Renato.

      Sim, ao entrar na ISR os registrados são salvos, e quando a ISR termina, eles são restaurados. Então veja:  quando eles são restaurados, eles ficam como estavam quando a Interrupção ocorreu. Então isto não é um problema. Ocorre com todos o Processadores (desde um sofisticadíssimo x86 até um "primitivo" PIC de 8 bits).

      A questão é a linguagem de programação, e o contexto que o código está executando. Veja que o qualificador "volatile" faz parte do padrão C ANSI. Então é preciso saber quando o "volatile" deve ser usado (sempre que vc alterar uma variável dentro de uma ISR  que é também acessada fora da ISR). Veja este link, especificamente no ítem 3) onde eu esclareço a questão do "volatile":    questão do "volatile"

      Já sobre a questão da execução "atômica", também não há nada errado. Ela  não é  uma gambiarra do Mundo dos Processadores. É uma questão da Arquitetura do Processador. Geralmente a questão da necessidade da execução "atômica" afeta principalmente Processadores que sejam exclusivamente de 8 bits. Mas há Processadores de 8 bits que não são exclusivamente de 8 bits, como por exemplo o querido Z80, onde uma carga de 16 bits no par "BC" já é automaticamente uma operação atômica, e aí vc não precisa se preocupar em fazer uma operação "atômica explícita". Mas este não é o caso dos Processadores AVR de 8 bits usados nos Arduinos mais populares (UNO, Nano, Mega). O AVR de 8 bits, requer que seja feita uma operação "atômica explícita", pois do contrário, corre o risco do "volatile" não ser suficiente para garantir um acesso adequado em variáveis de 2, 4, ou mais bytes no contexto fora da ISR (claro, quando a variável também é alterada dentro da ISR).

      Veja que uma das primeiras coisas que alguém ouve quando está aprendendo a Linguagem C/C++, é que esta Linguagem além de extremamente poderosa, também tem tanto características de "Alto Nível" (mais próximo do ser humano) como de "Baixo Nível" (mais próximo da Máquina). Então, uma vez programando sobre a plataforma C/C++, é preciso saber como se fazer cada coisa dentro das especificações da Linguagem, para se obter o comportamento desejado.

      Uma curiosidade: quando uma ISR é iniciada, somente os Registradores que são alterados no contexto da ISR, é que são salvos/restaurados, pois o Compilador do C/C++ toma a providência para isso quando o código é compilado.

      Espero ter elucidado um pouco mais a questão.

      Abrçs,

      Elcids

declara apenas 

"while(pulso<x);"

Assim não precisa dar instruções ao while.

Imagino que seu "while (pulso < x) {}" esteja dentro da função loop().
Sendo assim, tente substitui-lo por uma condicional. Algo assim:
if (pulso >= x) {

/*código que estaria sendo executado após seu while original*/

}

Quanto ao while não funcionar... Talvez seja resultado da atuação do otimizador de código do compilador.

Espero ter ajudado.

RSS

© 2024   Criado por Marcelo Rodrigues.   Ativado por

Badges  |  Relatar um incidente  |  Termos de serviço