Problema con función millis()

no me funciona....si al menos puedes agregar para dos led me ayudaria mucho...
Hasta dentro de unas horas no podré hacer pruebas.

Publica el código que tengas para dos led, por ejemplo. No te olvides de ponerle las marcas de código como te dice @DJ T3:
 
No recomendaría bucle con delay jamás y más si vas a meter secuencias de teclado luego, tu código tendría que llegar a ensamblador para compensar correctamente el tiempo por que no será constante luego, la idea de interrupciones es mejor pero al menos millis() es menos dañina qué delay() incontrolado.

Recuerda que esa función NO sirve para ejecutar tareas en segundo plano, en realidad es una interrupción por temporizador qué incrementa un contador cada ms y la función devuelve el valor de dicho contador, si planeas usarla para disparar eventos seria algo como
C:
unsigned long EVENTxTIC //intervalo de retrazo
unsigned long EVENTxTRIGGER //comparador para disparar 

void Event() {
 volatile unsigned long m = millis();
 if(m > EVENTxTRIGGER) //esto debe corregirse para considerar el desborde
 {
  EVENTxTRIGGER = m + EVENTxTIC;
  //lo que tengas que hacer aquí
 }
}

No es ideal por que se pierde sincronía en las tareas y debes tener cuidado de como manejar el desborde de contador, la rutina la debes insertar y leer constantemente sin meter delays o puedes afectar todo de manera grave. La ventaja de usar temporizador es que este tiende a 0 y se reinicia a su máximo por lo que es más fácil mantenerlo en seguimiento y sincronía, es más rápido detectar valor nulo a cualquier otra cosa.
 
hola amigos.....aun sigo con mi problema....por favor si me ayudan.....este es el código....funciona pero los delay me complican...!!!!!una ayudita por favor!!!....para eliminar los delay y actuar instantaneamente con el teclado.mil gracias.....saludos amigos


Código:
#include <Keypad.h>
#include <LiquidCrystal_I2C.h>

int LED1 = A0;
int LED2 = 10;
int LED3 = 11;

int t1=0;
int t2=0;
int t3=0;

const byte ROWS = 4;  //FILAS
const byte COLS = 4;  //COLUMNAS
byte rowPins[ROWS] = {9, 8, 7, 6}; //FILAS
byte colPins[COLS] = {5, 4, 3, 2};   //COLUMNAS

char hexaKeys[ROWS][COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'},
};



Keypad customKeypad = Keypad(makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);
LiquidCrystal_I2C lcd(0x27, 20, 4);


void setup()
{
  Serial.begin(9600);
  lcd.init();
  lcd.backlight();
  lcd.setCursor(0, 0);
  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);
  pinMode(LED3, OUTPUT);
}

void loop()
{
  lcd.setCursor(1, 0);
  lcd.print("   PRESIONE   ");
  lcd.setCursor(2, 1);
  lcd.print("(1), (2), (3) ");

 
  char Key = customKeypad.getKey();

  if(Key=='1')
  {
  t1=500;
  lcd.setCursor(1, 0);
  lcd.print("TIEMPO    UNO ");
  lcd.setCursor(2, 1);
  lcd.print("      OK      ");
  }

  if(Key=='2')
  {
  t2=1000;
  lcd.setCursor(1, 0);
  lcd.print("TIEMPO    DOS ");
  lcd.setCursor(2, 1);
  lcd.print("      OK      ");
  } 

  if(Key=='3')
  {
   t3=2000;
  lcd.setCursor(1, 0);
  lcd.print("TIEMPO   TRES ");
  lcd.setCursor(2, 1);
  lcd.print("      OK      "); 
  }

    if(Key=='#')
  {
    t1=0;
    t2=0;
    t3=0;
  lcd.setCursor(1, 0);
  lcd.print("    APAGADO   ");
  lcd.setCursor(2, 1);
  lcd.print("              "); 
  }




      analogWrite(LED1, 255);
      delay(t1);
      analogWrite(LED1, 0);
      delay(t1);

      analogWrite(LED2, 255);
      delay(t2);
      analogWrite(LED2, 0);
      delay(t2);

      analogWrite(LED3, 255);
      delay(t3);
      analogWrite(LED3, 0);
      delay(t3);
      
}
hola amigos....aun con mi problema....no he podido con la solucion....les dejo el codigo para ver si me ayudan, por favor. como hacerlo

Código:
#include <Keypad.h>
#include <LiquidCrystal_I2C.h>

int LED1 = A0;
int LED2 = 10;
int LED3 = 11;

int t1=0;
int t2=0;
int t3=0;

const byte ROWS = 4;  //FILAS
const byte COLS = 4;  //COLUMNAS
byte rowPins[ROWS] = {9, 8, 7, 6}; //FILAS
byte colPins[COLS] = {5, 4, 3, 2};   //COLUMNAS

char hexaKeys[ROWS][COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'},
};



Keypad customKeypad = Keypad(makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);
LiquidCrystal_I2C lcd(0x27, 20, 4);


void setup()
{
  Serial.begin(9600);
  lcd.init();
  lcd.backlight();
  lcd.setCursor(0, 0);
  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);
  pinMode(LED3, OUTPUT);
}

void loop()
{
  lcd.setCursor(1, 0);
  lcd.print("   PRESIONE   ");
  lcd.setCursor(2, 1);
  lcd.print("(1), (2), (3) ");

 
  char Key = customKeypad.getKey();

  if(Key=='1')
  {
  t1=500;
  lcd.setCursor(1, 0);
  lcd.print("TIEMPO    UNO ");
  lcd.setCursor(2, 1);
  lcd.print("      OK      ");
  }

  if(Key=='2')
  {
  t2=1000;
  lcd.setCursor(1, 0);
  lcd.print("TIEMPO    DOS ");
  lcd.setCursor(2, 1);
  lcd.print("      OK      ");
  } 

  if(Key=='3')
  {
   t3=2000;
  lcd.setCursor(1, 0);
  lcd.print("TIEMPO   TRES ");
  lcd.setCursor(2, 1);
  lcd.print("      OK      "); 
  }

    if(Key=='#')
  {
    t1=0;
    t2=0;
    t3=0;
  lcd.setCursor(1, 0);
  lcd.print("    APAGADO   ");
  lcd.setCursor(2, 1);
  lcd.print("              "); 
  }




      analogWrite(LED1, 255);
      delay(t1);
      analogWrite(LED1, 0);
      delay(t1);

      analogWrite(LED2, 255);
      delay(t2);
      analogWrite(LED2, 0);
      delay(t2);

      analogWrite(LED3, 255);
      delay(t3);
      analogWrite(LED3, 0);
      delay(t3);
      
}
 
Última edición:
Por aquí hay una frase hecha que es:
"Cada tonto con su tema"

Vamos, yo sigo en lo mío, hablad, hablad que yo a lo mío... Pero sigo preguntando.


Por preguntar las cosas no cambian solas.
Si sigues con los mismos horrores en la programación el resultado va a ser el mismo.

Mira que en tu código sigues sin leer el teclado más que una vez por ciclo, tendrías que mantener la tecla pulsada todo el rato hasta que pase por ahí.

Habría que ver el código de lectura del teclado que lo mismo también lleva una buena siembra de delays. Me apuesto un café a que lleva bien de delays.

Si el lector del teclado está delayzado entonces no te queda otra que interrupciones si o si. Además por timers, no tienes la opción de interrupciones por el teclado porque eso descuadraria los tiempos por culpa de los delays del lector del teclado.
 
Ya lo he probado en el Arduino. Había un error en mi código. Las variables de estado deben ser signed, no unsigned, ya que tienen que almacenar valores negativos.

Aquí el código funcionando:
C++:
/*
* Ejemplo de multitarea usando "Tiempo compartido".
* https://es.wikipedia.org/wiki/Tiempo_compartido_(inform%C3%A1tica)#Tiempo_compartido
*
* Joaquín Ferrero. 20200416
*
*
*/


// Conexiones de los LED
#define LED1 2
#define LED2 3
#define LED3 4
#define LED4 5

unsigned int lapso = 100;    // mínimo tiempo de espera entre vueltas del bucle

signed int estado1 = 0;        // si estado1 > 0, LED encendido, si estado1 < 0, LED apagado
signed int estado2 = 0;
signed int estado3 = 0;
signed int estado4 = 0;

unsigned int t1 = 0;        // tiempo inicial
unsigned int t2 = 0;
unsigned int t3 = 0;
unsigned int t4 = 0;


void setup() {
    t1 = 10 * lapso;
    t2 = 5  * lapso;
    t3 = 2  * lapso;
    t4 = 1  * lapso;
}

void loop() {

    // LED1
    if (estado1 > 0) {                // ¿Sigue encendido?
        estado1--;                // Sí, decrementar
       
        if (estado1 <= 0)  {            // ver si hay que apagarlo
            digitalWrite(LED1, LOW);    // lo apagamos
            estado1 = -int(t1/lapso);    // inicio de cuenta en modo apagado
        }
    }
    // Aquí entramos también en el caso estado1 == 0 (primera vuelta)
    else {                        // Si no está encendido, estará apagado
        estado1++;                // Sí, incrementar
       
        if (estado1 >= 0)  {            // ver si hay que encenderlo
            digitalWrite(LED1, HIGH);    // lo encendemos
            estado1 = int(t1/lapso);    // inicio de cuenta en modo encendido
        }
    }
   
    // LED2
    if (estado2 > 0) {
        estado2--;
       
        if (estado2 <= 0)  {
            digitalWrite(LED2, LOW);
            estado2 = -int(t2/lapso);
        }
    }
    else {
        estado2++;
       
        if (estado2 >= 0)  {
            digitalWrite(LED2, HIGH);
            estado2 = int(t2/lapso);
        }
    }
   
    // LED3
    if (estado3 > 0) {
        estado3--;
       
        if (estado3 <= 0)  {
            digitalWrite(LED3, LOW);
            estado3 = -int(t3/lapso);
        }
    }
    else {
        estado3++;
       
        if (estado3 >= 0)  {
            digitalWrite(LED3, HIGH);
            estado3 = int(t3/lapso);
        }
    }
   
    // LED4
    if (estado4 > 0) {
        estado4--;
       
        if (estado4 <= 0)  {
            digitalWrite(LED4, LOW);
            estado4 = -int(t4/lapso);
        }
    }
    else {
        estado4++;
       
        if (estado4 >= 0)  {
            digitalWrite(LED4, HIGH);
            estado4 = int(t4/lapso);
        }
    }
   
   
  delay(lapso);
}
Esta es otra versión, que facilita poner un número determinado de LED sin cambiar mucho el código.
C++:
/*
 * Ejemplo de multitarea usando "Tiempo compartido".
 * https://es.wikipedia.org/wiki/Tiempo_compartido_(inform%C3%A1tica)#Tiempo_compartido
 *
 * Joaquín Ferrero. 20200416
 *
 * Versión para n LED. Solo es necesario indicar los pines donde se encuentran.
 */


// Conexiones de los LED
#define NLEDS 4

unsigned int lapso = 100;    // mínimo tiempo de espera entre vueltas del bucle (milisegundos)
signed int estado[NLEDS];    // si estado[x] > 0, LED encendido, si estado[x] < 0, LED apagado
unsigned int t[NLEDS];        // tiempo de cada LED encendido y apagado
unsigned int leds[NLEDS]    // pines donde están los LED
        = { 2, 3, 4, 5 };

void setup() {
    for (unsigned char i = 0; i < NLEDS; i++) {
        t[i] = 10/(1+i) * lapso;
    }
}

void loop() {

    cambia_estados();

    delay(lapso);
}

// Cambia los estados de todos los LED
void cambia_estados() {
    for (unsigned char i = 0; i < NLEDS; i++) {
        if (estado[i] > 0) {                    // ¿Sigue encendido?
            estado[i]--;                        // Sí, decrementar
        
            if (estado[i] <= 0)  {                // ver si hay que apagarlo
                digitalWrite(leds[i], LOW);        // lo apagamos
                estado[i] = -int(t[i]/lapso);    // inicio de cuenta en modo apagado
            }
        }
        // Aquí entramos también en el caso estado1 == 0 (primera vuelta)
        else {                                    // Si no está encendido, estará apagado
            estado[i]++;                        // Sí, incrementar
            
            if (estado[i] >= 0)  {                // ver si hay que encenderlo
                digitalWrite(leds[i], HIGH);    // lo encendemos
                estado[i] = int(t[i]/lapso);    // inicio de cuenta en modo encendido
            }
        }
    }
}

Esta es otra versión, con millis().

En este caso, se comprueba el tiempo transcurrido y se compara con los tiempos de cambio de cada led.

C++:
/*
* Ejemplo de multitarea usando "Tiempo compartido".
* https://es.wikipedia.org/wiki/Tiempo_compartido_(inform%C3%A1tica)#Tiempo_compartido
*
* Joaquín Ferrero. 20200416
*
* Versión con la función millis().
*     estado[x] almacena el estado del LED
*     ultimo[x] almacena el siguiente momento de cambio del LED
*
* Solo es necesario indicar los pines donde se encuentran los LED.
*/

#define NLEDS 4

unsigned int estado[NLEDS];     // si estado[x] == 1, encendido, si estado[x] == 0, apagado
unsigned long tiempo[NLEDS];    // tiempo de cada LED encendido y apagado
unsigned long ultimo[NLEDS];    // momento del ultimo cambio
unsigned int pinleds[NLEDS]     // pines donde están los LED
                = { 2, 3, 4, 5 };

void setup() {
    unsigned long ahora = millis();
 
    for (unsigned char i = 0; i < NLEDS; i++) {
        pinMode(pinleds[i], OUTPUT);
        tiempo[i] = 1000/(1+i);
        ultimo[i] = ahora;
    }
}

void loop() {
    cambia_estados();
 
    delay(100);                      // simulamos algo de carga
}

void cambia_estados() {              // Cambia los estados de todos los LED
    unsigned long ahora = millis();
 
    for (unsigned char i = 0; i < NLEDS; i++) {

        if (ahora >= ultimo[i]) {                   // ¿hay que hacer un cambio?
            estado[i] ^= 1;                         // sí, cambio de estado
            digitalWrite(pinleds[i], estado[i]);    // lo mostramos
            ultimo[i] += tiempo[i];                 // siguiente lapso
        }
    }
}

A propósito, falta poner la línea

pinMode(pinleds, OUTPUT);

en los dos códigos anteriores, en el setup(). No he podido cambiarlo porque estos foros permiten hacer cambios en los mensajes sólo durante una hora.
 
Última edición por un moderador:
Muchas gracias Joaquin Ferrero por su muy buena voluntad en tomarse su tiempo para mostrar opciones....le aseguro estudiare muy bien sus codigos propuestos para finalmente la logica y asi utilizarla.

de verdad le agradezco.

saludos.
 
Esta es otra solución, usando un temporizador que interrumpe cada pocos µs.

C++:
/*
 * Ejemplo de multitarea usando "Tiempo compartido".
 * https://es.wikipedia.org/wiki/Tiempo_compartido_(inform%C3%A1tica)#Tiempo_compartido
 *
 * Joaquín Ferrero. 20200416
 *
 * Versión con interrupciones con el temporizador 1 de la biblioteca TimerOne.
 *
 * Cada led tiene asociado un lapso y un contador.
 * Cada TICK µs, se decrementa el contador. Si llega a 0, cambiamos el estado del led y
 * reiniciamos el valor del contador.
 *
 * El problema de usar Timer1 es que quedan afectadas algunas funciones:
 * delay(), millis(), micros(), analogWrite() pines 5 y 6...
 */

#include <TimerOne.h>

#define NLEDS 4

uint8_t pin_led      [NLEDS] = { 2, 3, 4, 5 };  // pines donde están los LED
uint8_t estado_led   [NLEDS];                   // estados de cada led
uint8_t contador_led [NLEDS];                   // contadores de tiempo de cada led
uint8_t lapso_led    [NLEDS];                   // intervalos de tiempo para cada led

uint32_t TICK = 100000;                         // intervalo en microsegundos del temporizador 1


void setup() {
    for (unsigned char i = 0; i < NLEDS; i++) {
        pinMode(pin_led[i], OUTPUT);

        estado_led   [i] = 0;                   // inicialmente apagado
        contador_led [i] =                      // contador del intervalo (igual a la duración)
        lapso_led    [i] = 10 / (1+i);          // duración del lapso (múltiplos de TICK) (> 0)
    }

    // Arranque del temporizador
    Timer1.stop();                              // parada preventiva
    Timer1.initialize(TICK);                    // asignamos el periodo
    Timer1.attachInterrupt(cambia_estados);     // asignamos la rutina a ejecutar
    Timer1.start();                             // arrancamos el temporizador
}

void loop() {
    _NOP();                                     // cualquier cosa
}

// Cambia los estados de todos los LED
// Esta función se llama cada TICK µs
void cambia_estados() {

    for (unsigned char i = 0; i < NLEDS; i++) {

        if (contador_led[i] == 0) {                     // ¿fin del conteo?
            estado_led   [i] ^= 1;                      // sí, cambio de estado
            contador_led [i]  = lapso_led[i];           // siguiente lapso

            digitalWrite(pin_led[i], estado_led[i]);    // lo mostramos
        }

        contador_led[i]--;
    }
}
 
perfecto...gracias Joaquin Ferrero..

este codigo anda muy bien con mi programa, solo una ultima cosa....
Código:
C++:
/*
* Ejemplo de multitarea usando "Tiempo compartido".
* https://es.wikipedia.org/wiki/Tiempo_compartido_(inform%C3%A1tica)#Tiempo_compartido
*
* Joaquín Ferrero. 20200416
*
*
*/


// Conexiones de los LED
#define LED1 2
#define LED2 3
#define LED3 4
#define LED4 5

unsigned int lapso = 100;    // mínimo tiempo de espera entre vueltas del bucle

signed int estado1 = 0;        // si estado1 > 0, LED encendido, si estado1 < 0, LED apagado
signed int estado2 = 0;
signed int estado3 = 0;
signed int estado4 = 0;

unsigned int t1 = 0;        // tiempo inicial
unsigned int t2 = 0;
unsigned int t3 = 0;
unsigned int t4 = 0;


void setup() {
    t1 = 10 * lapso;
    t2 = 5  * lapso;
    t3 = 2  * lapso;
    t4 = 1  * lapso;
}

void loop() {

    // LED1
    if (estado1 > 0) {                // ¿Sigue encendido?
        estado1--;                // Sí, decrementar
      
        if (estado1 <= 0)  {            // ver si hay que apagarlo
            digitalWrite(LED1, LOW);    // lo apagamos
            estado1 = -int(t1/lapso);    // inicio de cuenta en modo apagado
        }
    }
    // Aquí entramos también en el caso estado1 == 0 (primera vuelta)
    else {                        // Si no está encendido, estará apagado
        estado1++;                // Sí, incrementar
      
        if (estado1 >= 0)  {            // ver si hay que encenderlo
            digitalWrite(LED1, HIGH);    // lo encendemos
            estado1 = int(t1/lapso);    // inicio de cuenta en modo encendido
        }
    }
  
    // LED2
    if (estado2 > 0) {
        estado2--;
      
        if (estado2 <= 0)  {
            digitalWrite(LED2, LOW);
            estado2 = -int(t2/lapso);
        }
    }
    else {
        estado2++;
      
        if (estado2 >= 0)  {
            digitalWrite(LED2, HIGH);
            estado2 = int(t2/lapso);
        }
    }
  
    // LED3
    if (estado3 > 0) {
        estado3--;
      
        if (estado3 <= 0)  {
            digitalWrite(LED3, LOW);
            estado3 = -int(t3/lapso);
        }
    }
    else {
        estado3++;
      
        if (estado3 >= 0)  {
            digitalWrite(LED3, HIGH);
            estado3 = int(t3/lapso);
        }
    }
  
    // LED4
    if (estado4 > 0) {
        estado4--;
      
        if (estado4 <= 0)  {
            digitalWrite(LED4, LOW);
            estado4 = -int(t4/lapso);
        }
    }
    else {
        estado4++;
      
        if (estado4 >= 0)  {
            digitalWrite(LED4, HIGH);
            estado4 = int(t4/lapso);
        }
    }
  
  
  delay(lapso);
}

lo que ocurre es que cometi un error en mi secuencia de encendido de los LEDs y debio ser asi


analogWrite(LED1, 255);
delay(t1);
analogWrite(LED1, 0);


analogWrite(LED2, 255);
delay(t2);
analogWrite(LED2, 0);


analogWrite(LED3, 255);
delay(t3);
analogWrite(LED3, 0);

es decir, simplemete enciende el primer led LED1, y pasado el tiempo de encendido t1, se apaga de inmediato y luego lo mismo con el LED2 y el LED3, como muestro en el codigo final.....como digo, elcodigo enviado funciona muy bien, solo que me equivoque en la forma de encenderlo y es como lo explico.....podra revisarlo Joaquin Ferrero....muchas gracias. saludos
 
Solo tienes que modificar los tiempos iniciales y en el setup() indicar los tiempos de encendido de cada led.

Si un led depende del estado de otro... pues tienes que tratar a los dos como una única tarea, no dos separadas, con una única variable de estado, que además de contador, debe indicar cuál de los dos led debe estar encendido. Puedes usar if() para preguntar en qué tramo de la cuenta se encuentra estado para saber el estado de los dos led.

Pero si los tiempos t son múltiplos entre sí, vale con lo primero que te he dicho.
 
......
lo que ocurre es que cometi un error en mi secuencia de encendido de los LEDs y debio ser asi

analogWrite(LED1, 255);
delay(t1);
analogWrite(LED1, 0);

analogWrite(LED2, 255);
delay(t2);
analogWrite(LED2, 0);

analogWrite(LED3, 255);
delay(t3);
analogWrite(LED3, 0);
...

Y dale con analogWrite(LED1, 255) y analogWrite(LED1, 0) . Ahí no usa PWM (que no te funcionaría en pin_A0) y es lo mismo que escribir digitalWrite(LED1,HIGH) , con la diferencia que queda mas claro lo que hace con la salida (digital, no PWM)

Otra manera de hacerlo y simple de ampliar y modificar es usar una tabla con pin y tiempo.

C:
#define LED1  A0
#define LED2  10
#define LED3  11
#define LED4  12
#define LED5  13

#define LEN(x) sizeof(x)/sizeof(x[0])

//  Array con el pin para cada led y su duración
struct  LEDS { byte pin ; word t ; }
        led[]= { LED1 , 1500 ,
                 LED2 , 1000 ,
                 LED3 , 2000 ,
                 LED4 , 1000 ,
                 LED5 ,  200  };

unsigned long T ;
byte k ;

void setup()
{
    pinMode(LED1, OUTPUT);
    pinMode(LED2, OUTPUT);
    pinMode(LED3, OUTPUT);
    pinMode(LED4, OUTPUT);
    pinMode(LED5, OUTPUT);

    k = 0 ;
    digitalWrite(led[0].pin,HIGH);
    T  = millis() + led[0].t ;
}

void loop()
{
//-------------------------------------------------
//
//  Acá va el escaneo de teclado y modificacion de
//  los tiempos
//
//  led[0].t = t1  ;
//  led[1].t = t2  ;
//  led[2].t = t3  ;
//  ........ = ...
//
//-------------------------------------------------

//-------------------------------------------------
//  Cambio de led
//-------------------------------------------------
    if(millis()>T){            // Fallaría después de
                               // 50 días encendido
        digitalWrite(led[k].pin,LOW);
        if(++k==LEN(led)) k = 0 ;
        digitalWrite(led[k].pin,HIGH);
        T = millis() + led[k].t   ;
    }
//-------------------------------------------------
}
 
Si, lo del analogWrite me tiene intrigado.
Si lo que quieres es usar los pines analógicos como digitales se pueden usar.
Incluso las entradas analógicas se pueden usar como salidas digitales. Lee la documentación de Arduino que lo explica bien claro.
 
Me quedaba por poner un ejemplo más, ejemplo de tiempo compartido usando el temporizador 1 y accediendo a los registros de forma directa (no depender de bibliotecas externas). Pero enhorabuena a @Eduardo porque ha sabido encontrar la forma de sacar la secuencia de una forma muy elegante.

C++:
/*
 * Ejemplo de multitarea usando "Tiempo compartido".
 * https://es.wikipedia.org/wiki/Tiempo_compartido_(inform%C3%A1tica)#Tiempo_compartido
 *
 * Joaquín Ferrero. 20200417
 *
 * Versión con interrupciones con el temporizador 1 y acceso a los registros de forma directa.
 *
 * Solo es necesario indicar los pines donde se encuentran los LED.
 */

/* Configuración */
#define NLEDS 4
uint8_t pinleds[NLEDS] = { PD2, PD3, PD4, PD5 };    // pines donde están los led
/* Fin configuración */

uint8_t mascara_led  [NLEDS];                // máscara de bits de la posición de cada led
uint8_t contador_led [NLEDS];                // contadores de tiempo de cada led
uint8_t lapso_led    [NLEDS];                // intervalos de tiempo para cada led

void setup() {
    for (uint8_t i = 0; i < NLEDS; i++) {
        mascara_led[i] = (1 << pinleds[i]);

        DDRD |= mascara_led[i];                    // pin en modo salida

        contador_led[i] =                         // contador del intervalo (igual a la duración)
        lapso_led[i] = 40 / (1 << i);            // duración del lapso (múltiplos de TICK) (> 0)
    }

    setup_timer1();
}

void setup_timer1(void) {                       // Configuración del temporizador

    /*
    El funcionamiento del temporizador es así:

        El temporizador se alimenta desde el reloj del sistema (16 MHz).
        Ese reloj pasa por un preescalador, que divide la cuenta por 1, 8, 64, 256 o 1024
        Por cada golpe de reloj que resulta, se incrementa el contador del temporizador (TCNT1).
        Si la cuenta llega al valor almacenado en (OCR1A, 16 bit), se dispara la interrupción.
        Trabajando en modo CTC (Clear Timer on Compare) se inicializa el valor del contador.

    Calcular el valor del preescalador y de la cuenta:

        La fórmula matemática está en el manual del AVR 328P.

            frecuencia de salida = <frecuencia reloj sistema> / (2 * N * (1 + OCR1A))

        Ejemplo, para 100 ms (10 Hz), a 16 Mhz, con un preescalador de 256:

            10 = 16E06 / (2 * 256 * (1 + OCR1A))

        Despejando OCR1A:

            OCR1A = (16E06 / 10 / 2 / 256) - 1 = 3124
    */

    TCCR1A = 0;                // Reiniciar registros de control del temporizador 1
    TCCR1B = 0;

    TCNT1 = 0;                // Valor inicial del contador

    TCCR1B |= 1 << WGM12;        // Modo de trabajo: CTC

    // Código de preescalado
    //        CS12    CS11    CS10
    // 0    0        0        0    temporizador parado
    // 1    0        0        1    /1 (no preescalado)
    // 2    0        1        0    /8
    // 3    0        1        1    /64
    // 4    1        0        0    /256
    // 5    1        0        1    /1024
    // 6    1        1        0    Reloj en T1, activo en flanco descendente
    // 7    1        1        1    Reloj en T1, activo en flanco ascendente

    TCCR1B |= 4 << CS10;        // Poner el preescalador

    OCR1A = 3124;                // Valor final de la cuenta

    TIMSK1 |= 1 << OCIE1A;        // Activar temporizador en modo comparación

    sei();                        // Activar las interrupciones
}


void loop() {
    _NOP();                        // simulamos algo de carga
}

ISR(TIMER1_COMPA_vect) {
    for (uint8_t i = 0; i < NLEDS; i++) {

        if (0 == --contador_led[i]) {            // ¿fin del conteo?

            PORTD ^= mascara_led[i];            // sí, cambio de estado

            contador_led[i] = lapso_led[i];        // reiniciar siguiente lapso
        }
    }
}
 
Con permiso de @Eduardo he tomado su ejemplo y he hecho la siguiente versión, que usa una struct con métodos y nombres más descriptivos de las variables.

C++:
/*
 * Ejemplo de multitarea usando "Tiempo compartido".
 * https://es.wikipedia.org/wiki/Tiempo_compartido_(inform%C3%A1tica)#Tiempo_compartido
 *
 * Joaquín Ferrero. 20200417
 *
 * Varios led se encienden en secuencia mientras el resto del programa sigue funcionando.
 * Se usa
 *
 * Versión para n led. Solo es necesario indicar los pines donde se encuentran.
 */

// configuración //////////////////////////////////////////////////////////////

// número de led
#define NLEDS 4

// declaración de la secuencia
struct SEQ_T {
    uint8_t  pin;
    uint32_t periodo;
    uint32_t fin_periodo { 0L };

    SEQ_T(uint8_t pinled, uint32_t periodoled)               // inicializador
    : pin(pinled), periodo(periodoled)
    {
        pinMode(pinled, OUTPUT);
    }

    void arranca(uint32_t tiempo = millis()) {               // arranca secuencia
        digitalWrite(pin, HIGH);
        fin_periodo = tiempo + periodo;
    }

    void termina(void) {                                     // detiene secuencia
    digitalWrite(pin, LOW);
    }
};

// definición de la secuencia: números de led y periodo de tiempo en ms que está encendido
SEQ_T secuencia[NLEDS] = {
    { 2, 500L },  // 0
    { 3, 400L },  // 1
    { 4, 300L },  // 2
    { 5, 200L }   // 3
};
// fin de configuración ///////////////////////////////////////////////////////


// aquí resto de variables
// ...
uint8_t SEQ_led_activo;


// inicialización
void setup(void) {
    // empezar por el tercer led
    secuencia[SEQ_led_activo = 2].arranca();
}

void loop(void) {
    // resto del programa

    _NOP();

    // ...

    // Fin de bucle
    SEQ_actualiza();
    delay(100);
}

void SEQ_actualiza(void) {
    uint32_t ahora = millis();

    if (ahora >= secuencia[SEQ_led_activo].fin_periodo) {    // si ha pasado el periodo
        secuencia[SEQ_led_activo].termina();                 // termina la secuencia
        ++SEQ_led_activo;                                    // pasamos al siguiente led
        SEQ_led_activo = SEQ_led_activo % NLEDS;             // ver si hay que volver al primero
        secuencia[SEQ_led_activo].arranca(ahora);            // inicia secuencia del led
    }
}
 
Me gusta ese ultimo codigo @JoaquinFerrero , ademas si lo entiende y lo sabe implementar, no solo le puede servir para lo que quiere, sino algo mas grande.
Trata de leer y entender el codigo, @huesc , y entender cada parte del mismo, y no en general
 
El problema es que no sabes lo que tarda el resto del código y cada iteración es 100ms del delay más un tiempo indeterminado del resto del código. Puede ser 100 y un poquito o 100 y a saber que .
Para eso prefiero llamar a la función por un timer cada 100ms y olvidarme para siempre.
Me gusta infinitamente más la que usa un timer.
 
El problema está en que este pibe practica la costumbre de malgastar recursos. Muy común en millenials e informáticos ;)

delay() usa el Timer0 y AnalogWrite(pin, 1...254) los otros , con lo que al atmega328P no le queda nada para una interrupción periódica.

Claro que eso sería así si se quisieran dimmerizar los leds, porque AnalogWrite(pin, 0 o 255) se compila como un DigitalWrite() , o sea , no usa los timers.

Por otro lado, cuando se usa delay() y/o millis() el propio Arduino te activa una interrupción con timer0 cada 1.024ms . A menos que se necesite un intervalo muy diferente o nos sobren timers es mas negocio editar la librería wiring.c (donde se definen millis() y delay() ) y agregar unas líneas propias.
 
El problema es que no sabes lo que tarda el resto del código y cada iteración es 100ms del delay más un tiempo indeterminado del resto del código. Puede ser 100 y un poquito o 100 y a saber que .
Para eso prefiero llamar a la función por un timer cada 100ms y olvidarme para siempre.
Me gusta infinitamente más la que usa un timer.

Pero se podria eliminar el delay, si la comparacion la hace en la funcion "SEQ_actualiza", ya no es necesario el delay... Se pierde algun tiempo, pero es muy minimo.

PD: Me salté el delay en el loop, pero sin el no influye en nada..
 
Atrás
Arriba