Conteo de pulsos con Arduino

Típico de muchas librerías de Arduino. Almuchas de ellas son una auténtica basura.

Sin ir mas lejos ¿Alguien sabe cuanto mas lento es hacer digitalWrite a un pin frente a un port?
Yo lo he medido y..
No es una ni dos ni tres ni diez ni vente veces mas lento, es unas CINCUENTA veces mas lento. No sé muy bien que fumaban los que hicieron las librerías de arduino pero algunas son para llorar.
Para ese caso, estoy usando digitalWriteFast para parar el motor lo mas rapido posible al llegar a los pulsos de destino.
Segun el autor:
The regular digitalWrite() in Arduino Uno core (16MHz) takes about 6280nS while digitalWriteFast() port manipulation takes 125nS.
Veo que tienen experiencia con estas cosas, aprovecho para consultar lo siguiente
Mi logica de codigo es :
opcion1:
recibo comando mover motor->activo conteo de pulsos->muevo->llego a destino->paro->espero 1 segundo (sin bloquear el codigo)->paro conteo de pulsos.

opcion2:
recibo comando mover motor->activo conteo de pulsos->muevo->llego a destino->paro->paro conteo de pulsos.

La diferencia es que con la opcion 2 siempre cuenta de 1 en 1, pero con la opcion 1 hay veces que cuenta 1 pulso de mas.
Pienso que puede ser porque al parar el motor con la misma inercia puede avanzar un poquito mas
Ya hice muchas pruebas y no logro decidirme por una de ellas ya que la parabolica se posiciona bien con cualquiera de las 2.
 
Gracias por la información, no conocía esa librería.
Es que hice una rutina para manejar una tira de leds inteligentes y le faltaba velocidad a mares. Hay que generar trenes de pulsos a 400kHz y no llega ni de lejos. Accediendo al puerto le sobraba velocidad de tal forma que tuve que poner retardos para cuadrar las velocidades. Eso para no usar código máquina que es lo que usan las librerías de estas tiras de led; se pueden usar sin código máquina y sobra tiempo.

¡¡¡Que burrería, eso que indicas significa que con el soft original puedes generar una onda cuadrada de 70kHz o así, mientras que con ese sistema llegas a 4MHz!!!
Desde luego hay algún patán al timón del Arduino. Patán patán.

Respecto a tu problema, es probable que sea eso, que se pasa un pulso y que no pase siempre.
Podrías usar pwm y hacer un frenado progresivo o al menos de un escalón.

Cuando falten unos pocos pulsos bajas el pwm al 50% habría que hacer pruebas, si lo bajas mucho mucho tiempo antes es probable que se quede sin fuerza y no llegue.
 
Última edición:
Cuando falten unos pocos pulsos bajas el pwm al 50% habría que hacer pruebas, si lo bajas mucho mucho tiempo antes es probable que se quede sin fuerza y no llegue.
Es mas facil diseñar una suerte de control proporcional o un PI para llevar el PAP a la posicion correcta.
De esa forma siempre se esta leyendo la posicion y no hay que inventar cosas raras.
 
Por ahora lo voy a dejar asi, para hacer un PWM tendria que cambiar el diodo que tengo en el mosfet y no se si algo mas.
El actuador es asi:
qarl-satellite-heavy-duty-actuator.jpg


Tiene un motor CC de 36v y un reedswitch.
 
Estuve haciendo mas pruebas para ver porque a veces contaba un pulso de mas dejando la interrupcion activa y llegue a la conclusion de que el problema es la comunicacion con la LCD.
Estoy usando una lcd 20x4 con modulo i2c y esta conectada en los pines 20 y 21, segun esta tabla estos son las 2 primeras interrupciones en el micro:
Pin Port INT Arduino Pin
2 PE4 INT4 6
3 PE5 INT5 7
21 PD0 INT0 43
20 PD1 INT1 44
19 PD2 INT2 45
18 PD3 INT3 46
n/c PE6 INT6 8 (fake 75)
n/c PE7 INT7 9 (fake 76)
Y como son las 2 primeras tendrian mayor prioridad que las del conteo de pulsos.
Yo refresco la LCD dentro del loop cada 1 segundo para mostrar el conteo de pulsos, pienso que justo cuando esta por llegar al pulso de destino la interrupcion no se ejecuta porque en ese momento dibuje la pantalla y el motor esta moviendose.
Ahora quite toda referencia a la lcd y puedo mover de 1 en 1 sin problema.
Esta es la libreria de la LCD
Creen que pueda ser ese el problema?.

Entiendo que las interrupciones cuando ocurren paran lo que el micro esta haciendo, ejecuta el codigo de la interrupcion y despues sigue donde estaba.
Supongamos que en arduino dentro del loop se esta leyendo el puerto serie con Serial.read() si justo en ese momento una interrupcion se dispara, arduino la ejecuta o sigue con el Serial.read?
Consulto esto porque los comandos enviados por el receptor satelital los recibe un PIC y luego por serie los envia al arduino a una velocidad de 19200 baudios (estaba en 9600, lo cambie). aca pueden ver ese proyecto
Lei por ahi como que la comunicacion serial no es interrumpida por las interrupciones externas

Saludos.
 
La respuesta es "depende" una interrupción interrumpe un proceso de menor prioridad y luego vuelve. Es decir que una interrupción puede ser interrumpida por otra de mayor prioridad ... Si se ha configurado así.

Como no tengo ni idea de cómo funciona serial del Arduino pues no lo sé.
Que yo sepa va por interrupciones y tiene dos buffers fifo de 32 caracteres
 
busque mas informacion y encontre esta tabla, la prioridad la tendrian las interrupciones externas, entiendo que no deberia de tener problemas con el Serial.read().

INT_Priority.png
 
Lo que no tiene sentido es que una cosa disparé otra.
Las interrupciones están vectorizadas y cada una va a su código.
Salvo que se haga un desastre de programación eso no puede pasar. Puede que pierdas una pero no que una active otra.
 
Lo que no tiene sentido es que una cosa disparé otra.
Las interrupciones están vectorizadas y cada una va a su código.
Salvo que se haga un desastre de programación eso no puede pasar. Puede que pierdas una pero no que una active otra.

Voy a seguir haciendo pruebas con y sin la LCD a ver que sucede, pero por ahora funciona sin la LCD asi que seguramente hice algun desastre en el codigo
 
Definitivamente si uso la LCD al usar la interrupcion en modo CHANGE aleatoriamente cuenta 2 o 3 pulsos de mas. No sucede si uso la interrupcion en modo FALLING o RISING y tampoco sucede si desactivo la LCD y uso las interrupciones CHANGE.
Dentro de la ISR hago lo siguiente:

Código:
void sumarPulsosM1() {
        motor1PulsosActuales = motor1PulsosActuales + 1;
        if ((motor1PulsosActuales >= motor1PulsosDestino)) {
            digitalWriteFast(motor1,LOW);
            moviendoMotor1=true;
        }
}

Incluso usando la interrupcion en modo CHANGE con la LCD hay veces que no ejecuta lo que esta dentro del IF, si lo cambio para que la condicion sea motor1PulsosActuales == motor1PulsosDestino el motor sigue de largo.
No me habia dado cuenta de este problema antes y quizas era porque usaba el anti-rebote dentro de la interrupcion.

Voy a probar con la LCD sin el modulo i2c
Hice la nueva pcb con el optoacoplador incluido y le puse un led para ver cuando el reedswitch esta cerrado/abierto.
Para que entren en contexto lo que estoy construyendo es algo similar a esto:
titanium-asc1.jpg


Es un posicionador que generalmente se usa con estas parabolicas:
41piW4s7QCL.jpg


Obviamente cuando tenga todo funcionando voy a postear todo lo necesario para el que quiera armarlo o mejorarlo.
 

Adjuntos

  • IMG_20190714_211553.jpg
    IMG_20190714_211553.jpg
    239.5 KB · Visitas: 8
No le veo mucha utilidad al trigger.
Se suele usar en osciladores donde la señal es analógica pero en este caso ...
Prueba, nunca se sabe. El filtrado de ruidos es todo un arte.
 
Ok.
Aun no probé el schmitt trigger, funciona bastante bien el conteo pero lo quiero dejar lo mas confiable posible.
Creo que aun tengo algún tipo de inducción.
En el vídeo lo que hago es presionar los pulsadores para mover el motor 1 pulso hacia el este u oeste, tengo conectado solo 1 hilo del motor y 1 hilo del reed switch asi que no hay movimiento del motor y tampoco en el reed switch.
El ruido que se escucha es el relee que invierte la polaridad. Lo que sucede es que al invertir la polaridad, el led conectado en paralelo al optoacoplador enciende y posiblemente también lo haga el opto, pero aparentemente no es suficiente como para que cuente un pulso erróneo (el conteo se mantiene en 0).
El relee también esta opto acoplado del arduino. Lo activo con un 2n2222a con diodo y un "filtro" con un capacitor de 100nf + resistencia de 100ohm en paralelo al diodo.
Esto solo sucede cuando por lo menos un hilo del reed swicht y el motor están conectados. si se desconecta alguno de ellos como se ve, no pasa nada. El cable azul es el reed switch.
Probé en la escala de mega ohms continuidad entre los hilos del reed switch y los del motor y no marca nada.
Si agrego un capacitor de 100 nf en paralelo al diodo D1, el led no parpadea al accionar el relee
 

Adjuntos

  • Captura.PNG
    Captura.PNG
    5.8 KB · Visitas: 3
Ahora descubrí otro problema. No siempre el motor se para cuando debe.
Esto esta dentro de la interrupción:
Código:
#define motor1PWR 7
void sumarPulsosM1() {
        motor1PulsosActuales = motor1PulsosActuales + 1;
        if ((motor1PulsosActuales >= motor1PulsosDestino)) {
            digitalWriteFast(motor1,LOW);
            moviendoMotor1=false;
        }
}

Por algún extraño motivo las instrucciones se ejecutan pero el pin no cambia de estado y por eso el motor se mueve 2 o 3 pulsos de mas, volviendo a llamar a la interrupción X cantidad de veces hasta que para, al conectar la LCD pasa practicamente siempre.

Reduci la interrupción a esto:
Código:
#define motor1PWR 7
void sumarPulsosM1() {
    motor1PulsosActuales = motor1PulsosActuales + 1;
    digitalWriteFast(motor1,LOW); //esto no funciona algunas veces
    //digitalWrite(motor1,LOW); //esto tampoco funciona algunas veces
}
Por las dudas medí con el tester la salida del arduino y no cambia de estado.

Aparentemente no es problema del codigo completo, hice esto como para probar y sucede lo mismo

Código:
void setup() {
 pinMode(7,OUTPUT); //motor
 pinMode(26,OUTPUT); // sentido giro
 pinMode(20,INPUT); //reed switch
 pinMode(14,INPUT_PULLUP); //boton
 Serial.begin(115200);
 attachInterrupt(digitalPinToInterrupt(20), sumarPulsosM1, CHANGE);
}
int boton = 0;

void sumarPulsosM1() {
  digitalWrite(7, LOW);
  detachInterrupt(digitalPinToInterrupt(20)); //aca para la interrupcion
}

void loop() {
  boton = digitalRead(14);
 if (boton == 0){
    Serial.println("pulso");
    delay(100);
    digitalWrite(7,HIGH);
    delay(1000);
  }
}
Para ver mas claro el problema, paro la interrupción después de parar el motor y como no vuelve a entrar en la misma, el motor nunca para.
Si las interrupciones quedan activas se va a mover un poco mas hasta que para.
 
Última edición:
En lugar de usar delay haz algo útil; escribe un cero un millón de veces en el pin.

Es raro que no funcione. Me imagino que en algún otro lugar del código se activa el pin por algún motivo no aparente pero real.
Por si las dudas en el bucle principal te puedes dedicar a reactivar o redesactivar el pin mientras esperas según sea el caso.


Estás teniendo demasiados problemas con esto, me da la sensación de que el sensor que estás usando es una M como un avión.
Si usas un reed, eliminarlo y en su lugar pon un sensor hall digital dejando el mismo imán en el eje.
O prueba con otro modelo de pulsador/relé reed.

Eso tienes problemas de ruido en la alimentación, prueba a cambiarla o a alimentar con baterías para probar. Cables cortos, buenos contactos...
 
En lugar de usar delay haz algo útil; escribe un cero un millón de veces en el pin.

Es raro que no funcione. Me imagino que en algún otro lugar del código se activa el pin por algún motivo no aparente pero real.
Por si las dudas en el bucle principal te puedes dedicar a reactivar o redesactivar el pin mientras esperas según sea el caso.


Estás teniendo demasiados problemas con esto, me da la sensación de que el sensor que estás usando es una M como un avión.
Si usas un reed, eliminarlo y en su lugar pon un sensor hall digital dejando el mismo imán en el eje.
O prueba con otro modelo de pulsador/relé reed.

Eso tienes problemas de ruido en la alimentación, prueba a cambiarla o a alimentar con baterías para probar. Cables cortos, buenos contactos...

Con un posicionador comercial, funciona bien el sensor y la parabólica. Incluso con este que estoy haciendo funciona, pero tiene ese detalle que quiero solucionar.

Probé quitar el delay que puse en el código de prueba pero pasa lo mismo.
Aparentemente es algún tipo de problema al ejecutar el digitalWrite con la interrupción.
Si hago todo dentro del loop funciona bien.
Existe la posibilidad de que al momento de estar escribiendo el puerto, se vuelva a disparar la interrupción entrando en un bucle infinito?.
No encontré ningún problema similar

Edit: me respondo yo mismo, no debería de estar entrando nuevamente a la interrupción, porque si paro el motor y quito la interrupción, el motor nunca para.
Así que el problema debe de ser otro.



Edit: probé poner un booleano dentro de la interrupción y parar el motor dentro del loop, igual sucede lo mismo, incluso a veces es peor porque dentro del loop estoy haciendo otras cosas.
 
Última edición:
Coloca una bandera dentro de la interrupción, supongo que a eso te refieres con un booleano, y desde el LOOP coloca un if que funcione con esa bandera, y para saber si tú interrupción está funcionando correctamente utiliza el serial como herramienta de depuración, ese bandera que vas a crear imprimela en el serial desde el LOOP a ver si de verdad está cambiando de estado o no
 
Coloca una bandera dentro de la interrupción, supongo que a eso te refieres con un booleano, y desde el LOOP coloca un if que funcione con esa bandera, y para saber si tú interrupción está funcionando correctamente utiliza el serial como herramienta de depuración, ese bandera que vas a crear imprimela en el serial desde el LOOP a ver si de verdad está cambiando de estado o no

Eso mismo hice, puse un flag dentro de la interrupción:

Código:
#define motor1PWR 7
void sumarPulsosM1() {
        motor1PulsosActuales = motor1PulsosActuales + 1;
        if ((motor1PulsosActuales >= motor1PulsosDestino)) {
            digitalWriteFast(motor1,LOW);
            moviendoMotor1=false; //este flag
          detachInterrupt(digitalPinToInterrupt(pin))  //SOLO PARA PROBAR, DE ESTA FORMA ES MAS EVIDENTE CUANDO FALLA
        }
}

En el loop lo que hago es mostrar el valor de moviendoMotor1, da 1 cuando se esta moviendo y 0 cuando para.
De esa forma veo que la interrupcion ejecuta todo, pero el motor se sigue moviendo, incluso se puede ver por el monitor serial el valor de la variable en 0 y en el arduino el pin esta con 5v, por algún motivo no le hace caso solamente a la instrucción digitalWrite tampoco si uso digitalWriteFast.

La ultima linea detachInterrupt esta para probar, si la quito el motor siempre se para, pero lo hace unos pulsos de mas, porque no desactiva el pin en la primer interrupción, quizás lo hace en la segunda o tercera y ahí se para.
Probé ya en varios pines diferentes.
Hay algo que me estoy saltando, no creo que sea problema de código porque con algo tan sencillo como el sketch que probé mas arriba también falla.
Quizás algo relacionado con los timers? o no tiene nada que ver?.
Arduino fallado?

PD: Todavía no pude recrear el mismo error usando la interrupción en modo RISING
 
Atrás
Arriba