¿Conteo hacia atrás se puede hacer de otra manera?

Hola amigos, tengo la siguiente pregunta que hacer ya que no se como mejorar una parte de codigo, si es que se puede mejorar.
Tengo hecho un programa sencillo con un contador hacia atras, vasado en uno que me enseño D@rbytes, pues bien lo que quiero es que cuando llegue a cierto tiempo se active una salida que conecta a un ventilador y transcurrido 2 minutos se apaga.
PHP:
 if(min==50||min==44||min==38||min==32||min==26||min==20||min==14||min==8)    
               {
       output_high(pin_D6);   //VENTILADOR==1 
             }             
 if(min==48||min==42||min==36||min==30||min==24||min==18||min==12||min==6)         
               {
         output_low(pin_D6);  //VENTILADOR==0     
             }
Yo lo hecho de esta manera y funciona pero lo que quiero saber es si se puede mejor este codigo, gracias de antemano
 
Última edición:
Código:
if((min<=50)&&!((min+2)%6)) output_high(pin_D6);
if((min<50)&&!((min+4)%6)) output_low(pin_D6);

Si min sabes que nunca va a valer 56, entonces puedes hacer:
Código:
if(!((min+2)%6)) output_high(pin_D6);
if(!((min+4)%6)) output_low(pin_D6);

PD: Me he dado cuenta que uno de los intervalos no es de 6min sino de 4min, si es así no vale el codigo de arriba y vale la pena hacerlo como tienes, o con sentencias case que hacen lo mismo.
 
Última edición:
Gracias palurdo, al intervalo que te refieres es el de 32 minutos, me equibocado al ponerlo, es 34 minutos, y si los intervalos son todos de 6 minutos.
Si no te importa puedes explicarme, para que lo pueda comprender, las condiciones que has puesto?.

Perdon el error, ahora que lo estoy mirando esta en 16 minutos y no 32, que deve ser 14 minutos y por consiguiente los otros se deven de modificar tambien.
Y en la condicion de apagado igualmente, a partir de 18 minutos se deve modificar tambien, no son 14 si no 12 minutos, con lo que la ultima instruccion no la necesito.

Bueno te cuento, termino de probarlo y hace lo que quiero, es perfecto, solo me gustaria saber la explicacion de la condicion que no la entiendo, gracias de nuevo.
 
Última edición:
A ver, miglo, !((min+2)%6) es la forma optimizada de poner la condición (min+2)%6==0 (si el resultado de los parentesis es 0, que es lo mismo que falso, el ! lo convierte al opuesto, es decir verdadero, y se cumple el if).

Ahora bien, cuando pasa que (min+2)%6 vale cero?, El operador % computa el modulo, en este caso del número 6, y el modulo 6 es el "resto" de dividir un numero por el 6. Esto significa que el resultado de la operacion solo puede ir del 0 al 5, ya que el resto de una division nunca puede se igual o mayor al divisor.

Ahora pongamos que hacemos min%6, ¿cuando vale eso 0?, Pues siendo min en cada multiplo de 6, es decir 0, 6, 12, 18 ... (6*0, 6*1, 6*2, 6*3...). Si pongamos que queremos que valga 0 cuando min sea 4 y no 0, lo logico es hacer la operación restando a min el número 4, de esa forma cuando min sea 4, 4-4=0 y se cumple la condicion (4,10,16,22...), Ahora bien, por qué he puesto (min+2)%6 en lugar de !(min-4)%6? Pues porque cuando min valga menos de 4, min-4 va a ser un numero negativo, y si la variable min es unsigned, puede haber desbordamiento y dar un resultado incorrecto. Para evitar eso, darse cuenta que el modulo se repite cada 6, por lo que en lugar de -4, se puede hacer -4+6=+2, de esa manera el resultado es el mismo y solo se usan positivos.

El 2, en aritmética modular, se dice que es la congruencia de -4 mod 6, o 2 ← -4mod 6
 
Última edición:
Gracias palurdo, me va tocar leerlo un par de veces mas para comprender bien lo que has escrito, lo entiendo bastante, de nuevo gracias.
 
Última edición por un moderador:
... si es que se puede mejorar.
...
PHP:
 if(min==50||min==44||min==38||min==32||min==26||min==20||min==14||min==8)    
               {
       output_high(pin_D6);   //VENTILADOR==1 
             }             
 if(min==48||min==42||min==36||min==30||min==24||min==18||min==12||min==6)         
               {
         output_low(pin_D6);  //VENTILADOR==0     
             }

Todos son multiples de 6 con offset de 8 en el true del if, y offset de 0 o 6 en el segundo... por ahí está la vuelta.

De todas maneras, opino que para que sea un buen código habría que ver el por qué de esos valores (pines de entrada?, otro motivo?), y luego lo más correcto sería hacer el switch-case, que no es otra cosa que una tabla de lookup. Eso para contemplar que a futuro esos "valores mágicos" puedan cambiar y se puede adaptar el código fácilmente.

Pero para hacerlo sucio y rápido :):

PHP:
for(i= valor; i>=6; i-=6) {
    //no hago nada
}

if(i > 0) {
    //alto
} else {
    //bajo
}
 
Última edición:
Pero para hacerlo sucio y rápido :):

PHP:
for(i= valor; i>=6; i-=6) {
    //no hago nada
}

if(i > 0) {
    //alto
} else {
    //bajo
}

No se si te has dado cuenta o lo has hecho a propósito, pero has hecho una implementación sucia y rapida del operador % (mod, o rem en otros lenguajes). Eso si, con valores menores a sesenta esa implementacion no esta mal, pero si tiene un long tal que 192837465 y quieres hacer mod 6, ese bucle va a tardar una eternidad. La forma estandar es con el resto de la division entera, y la no estandar, es decir, la mejor, es aplicar congruencias y criterios de divisibilidad.
 
No se si te has dado cuenta o lo has hecho a propósito, pero has hecho una implementación sucia y rapida del operador % (mod, o rem en otros lenguajes). Eso si, con valores menores a sesenta esa implementacion no esta mal, pero si tiene un long tal que 192837465 y quieres hacer mod 6, ese bucle va a tardar una eternidad. La forma estandar es con el resto de la division entera, y la no estandar, es decir, la mejor, es aplicar congruencias y criterios de divisibilidad.

Sí señor, y también sirve para dividir (hay que poner un contador dentro del for), así con eso tenemos un divmod :) (se llaman así dentro de la implementación - parcial - de la librería estandar de C para los micros).
Eso... si el micro esta pelado de hardware y tiene una ALU que solo hace suma y resta (sin multiplicador, mucho menos divisor). De lo contrario le pegamos con hardware.
Pero sí, lo que puse es apenas mejor que
i = valor % 6;
en fin... no hay que reinventar la rueda, coincido.
 
Última edición:
Eso... si el micro esta pelado de hardware y tiene una ALU que solo hace suma y resta (sin multiplicador, mucho menos divisor). De lo contrario le pegamos con hardware....

en fin... no hay que reinventar la rueda, coincido.

Pero ¿y lo divertido que es a veces reinventarla? aquí te dejo mi implementación sin multiplicaciones ni divisiones de "mod 6" y de regalo la de "mod 3", usando criterios de divisibilidad y propiedades de las congruencias...

Código:
#include <stdio.h>

unsigned mod3(unsigned long x){
unsigned long aux;
do{
aux=0;
while(x) {
aux+=(x&0x00000003);
x>>=2;
}
x=aux;
}while(x>3);
if(x==3) x=0;
return(x);
}

unsigned mod6(unsigned long x){
unsigned long aux=x&0x00000001;

return((mod3(x>>1)<<1)+aux);
}

void main(int argc,char *argv[]){

unsigned long x;
if (argc<1) printf("Uso: %s [numero positivo]\n",argv[0]);
else {
x=atoi(argv[1]);
printf("\nResto de %ld dividido 6 forma estandar: %ld\n",x ,x%6);
printf("Resto de %ld dividido 6 forma no estandar: %ld\n",x ,mod6(x));
}
}

Y aquí una captura funcionando:

Screenshot_2016-08-28-21-28-24.jpg
 
;"un contador hacia atras, pues bien lo que quiero es que cuando llegue a cierto tiempo
; se active una salida que conecta a un ventilador
; y transcurrido 2 minutos se apaga.

Sencillo

; Declara 3 tareas
; Tarea uno " Compara cuenta contra valor determinado,
;---------------- Si no es igual, regresa al kernel a ser otra cosa.
;---------------- Si es igual señala al kernel que tarea 2 esta lista para correr. y regresa al kernel"
; Tarea 2 " Activa salida ventilador, Arranca timer de 2 min y
;---------------- señala al kernel que tarea 3 esta lista para correr y
;---------------- señala al kernel que tarea 2 no esta lista para correr y
;---------------- regresa al kernel"
; Tarea 3 " Revisa Timer_2 min, si se sobrepasa:
;--------------------------------------- apaga ventilador,
;--------------------------------------- pon a zero el timer_2,
;--------------------------------------- regresa al kernel
;---------------------------------si no se sobrepasa, regresa al kernel
; Esto es facil de implementar en cualquier micro yen cualquier lenguaje que uses.
; Yo uso ansembler y es muy facil de hacer, pero eso es tu chamba.
 
Última edición:
Hola, continuando con la implementación de módulos sin usar divisiones ni multiplicaciones (al menos explicitas), tristemente, o afortunadamente, la función mod3 se puede generalizar a todos los numeros de mersene (los de la forma 2^s-1) hasta S=32, en una función de un par de lineas que he llamado modm que admite como parametro el exponente que genera el numero de mersene (2->3, 3->15, 4->31, 5->63....). A partir de ahí, he creado unas macros y otras funciones que dependen de modm, para calcular desde mod 2 hasta mod 16 (aunque se puede usar los mismos metodos para numeros mayores, sobre todo si no son primos). Para mod 5, mod 9, mod 11 y mod 13 he usado reducción modular a un número de mersene multiplo de ellos y luego implementar una pseudodivision mucho mas pequeña a base de restas para llegar al numero final. Derivo mod 5 a partir de mod 15 (5*3), mod 9 a partir de mod 63 (9*7), mod 11 a partir de mod 1023 (11*93) y mod13 a partir de mod 4095 (13*315). Para mod 5 y mod 7 no hay mucho incremento de calculo para la division ya que en caso de mod 5 solo son como mucho dos restas y para mod 9 solo son 6. Sin embargo para mod 11 son 22 restas y para mod3 son 27 restas máximas, asi que si alguien tiene alguna ide para reducir el coste computacional de mod 11 y mod 13, sea bienvenido a aportar ideas.

Aquí el código:

Código:
#define mod2(x) (x&0x1)
#define mod3(x) modm(x,2)
#define mod4(x) (x&0x3)
#define mod6(x) ((modm(x>>1,2)<<1)+(x&0x1))
#define mod7(x) modm(x,3)
#define mod8(x) (x&0x7)
#define mod10(x) ((mod5(x>>1)<<1)+(x&0x1))
#define mod12(x) ((modm(x>>2,2)<<2)+(x&0x3))
#define mod14(x) ((modm(x>>1,3)<<1)+(x&0x1))
#define mod15(x) modm(x,4)
#define mod16(x) (x&0x0f)


unsigned modm(unsigned long n, unsigned s);

unsigned mod5(unsigned long x){
x=mod15(x);
while(x>=5) x-=5;
return(x);
}

unsigned mod9(unsigned long x){
x=modm(x,6);
while(x>=9) x-=9;
return(x);
}

unsigned mod11(unsigned long x){
x=modm(x,10);
while(x>=121) x-=121;
while(x>=11) x-=11;
return(x);
}

unsigned mod13(unsigned long x){
x=modm(x,12);
if(x>=2197) x-=2197;
while(x>=169) x-=169;
while(x>=13) x-=13;
return(x);
}

unsigned modm(unsigned long n, unsigned s){
unsigned int d = (1 << s) - 1; 
unsigned int m;    
for (m = n; n > d; n = m) for (m = 0; n; n >>= s) m += n & d;
return(m = m == d ? 0 : m);
}

Saludos.
 
Todos son multiples de 6 con offset de 8 en el true del if, y offset de 0 o 6 en el segundo... por ahí está la vuelta.

De todas maneras, opino que para que sea un buen código habría que ver el por qué de esos valores (pines de entrada?, otro motivo?), y luego lo más correcto sería hacer el switch-case, que no es otra cosa que una tabla de lookup. Eso para contemplar que a futuro esos "valores mágicos" puedan cambiar y se puede adaptar el código fácilmente.

Siento no haber contestado antes, respondiendo a ciertas preguntas, a tu pregunta de ¿el por que de esos valores?, pues te explico, hago un proceso biodiesel, quiero automatizarlo todo, el tiempo que uso de batir, en la reaccion, son 50 minutos.

La tarjeta que diseñado para alimentar los reles, que activan motores y calentadores, he comprobado que el regulador que los alimenta pasados unos 5 o 6 minutos, aunque tiene radiador se calienta, lo que visto con pruebas a ojo es que, si coloco un simple ventilador de los de ordenadores, que van a 12v, con 1 minuto que este activado se enfria completamente el regulalador.

Lo de switch-case lo habia pensado pero como va ser unos valores fijos, pues no hace falta, de hay que mi pregunta era "si se podia hacer de otra manera", con tu codigo lo he comprobado y va de lujo, a mi no se me hubiese ocurrido.

Yo se lo justo, tirando a poco, en programacion, pero bueno me defiendo, sobretodo gracias a vuestra ayuda inestimable. Espero haberte contestado
 
Siento no haber contestado antes, respondiendo a ciertas preguntas, a tu pregunta de ¿el por que de esos valores?, pues te explico, hago un proceso biodiesel, quiero automatizarlo todo, el tiempo que uso de batir, en la reaccion, son 50 minutos.

Que chulada!!! ¿Que proceso sigues? Reaccion basica en una fase? Reacción de dos fases Base-Base? Reaccion de dos fases Acido-Base? Que aceite usas de origen, nuevo o reciclado? Como separas el metoxido? Limpias el bio? Filtrado y/o lavado? Lo usas en el coche o en caldera? Como es tu reactor?

Saludos.
 
Que chulada!!! ¿Que proceso sigues? Reaccion basica en una fase? Reacción de dos fases Base-Base? Reaccion de dos fases Acido-Base? Que aceite usas de origen, nuevo o reciclado? Como separas el metoxido? Limpias el bio? Filtrado y/o lavado? Lo usas en el coche o en caldera? Como es tu reactor?

Saludos.

Ala ala ala alaaaaa, te queda alguna pregunta que hacer jejejeje, no se puede preguntar mas en menos palabras jejeje, te explico y de paso si le sirve a alguien pues que tome nota.

A la primera pregunta, el proceso es reaccion en dos fases base-base, he comprobado, con el tiempo, que la calidad del biodiesel mejora muchisimo "es de mas alta calidad", la respuesta es mejor.

El aceite que uso de origen, siempre es usado, hay veces que no tengo pero otras tengo vastante, sale mucho mas varato, hay que mirar la pelaaaaa jejeje.

"Como separas el metoxido?" quedras decir la glicerina no?, el metoxido se usa para la reaccion, la glicerina se separa por decantacion en un tanque terminado en cono de fibra de vidrio, de esta manera al dia siguiente solo con abrir la llave saco la glicerina y la recojo en un recipiente. Despues con el biodiesel que queda realizo la segunda fase.

A "limpias el bio" supongo que quieres decir si lo lavo?, si es esto te digo que si, lo lavo en 4 fases y luego lo filtro. Normalmente salvo alguna vez me sale muy limpio y de mucha pureza.

Pues si, lo uso en mis 2 coches y hasta la fecha no tengo ningun problema como dicen los escepticos.

Mi reactor es un tanque de 200L, siempre hago las tandas de 150L.
 
Atrás
Arriba