[SOLUCIONADO] Como hago para detener una PWM con Timers

Hola a todos, os explico un poco el problema.
Tengo este Codigo que genera 3 PWM con diferentes Timer y controlando la variación de frecuencia con una trama CAN BUS, Funciona bien, pero tengo dos problemas:
1.- Nada mas iniciar Arduino sin inyectar ninguna trama, ya tengo una PWM,
2.- Cuando lanzo la trama can para variar la frecuencia funciona perfecto, pero en cuanto quito la trama CAN, se queda la PWM hay estática con el ultimo valor sesteado por el CAN

Necesito que al iniciar Arduio no tenga ninguna PWM y al desconectar el CAN esta PWM desaparezca.

#include <TimerOne.h>
#include <TimerThree.h>
#include <TimerFour.h>
#include <SPI.h>
#include "mcp_can.h"

MCP_CAN CAN(10);  // Inicializar MCP2515 CAN bus (CS pin 10)


// Variables para la lectura de CAN
unsigned char len = 0;
unsigned char buf[8];
unsigned char canId = 0;



// Configuracion de las PWM
int freq1 = 0;        // Variable para almacenar la frecuencia del pin 11
int freq2 = 0;        // Variable para almacenar la frecuencia del pin 3
int freq3 = 0;        // Variable para almacenar la frecuencia del nuevo pin
int dutyCycle1 = 50;  // Duty cycle fijo para el pin 11
int dutyCycle2 = 50;  // Duty cycle fijo para el pin 3
int dutyCycle3 = 50;  // Duty cycle fijo para el nuevo pin


void setup() {
  pinMode(11, OUTPUT);  // Configurar el pin 11 como salida para la PWM
  pinMode(3, OUTPUT);   // Configurar el pin 3 como salida para la PWM
  pinMode(6, OUTPUT);   // Configurar el pin 6 como salida para la PWM

  Timer1.initialize();                // Inicializar Timer1 para la PWM del pin 11
  Timer1.attachInterrupt(timerISR1);  // Adjuntar la función de interrupción del temporizador 1
  Timer1.pwm(11, 512);                // Configurar el pin 11 para la PWM con un duty cycle del 50%

  Timer3.initialize();                // Inicializar Timer3 para la PWM del pin 3
  Timer3.attachInterrupt(timerISR3);  // Adjuntar la función de interrupción del temporizador 3
  Timer3.pwm(3, 512);                 // Configurar el pin 3 para la PWM con un duty cycle del 50%

  Timer4.initialize();                // Inicializar TimerFour para la tercera PWM
  Timer4.attachInterrupt(timerISR4);  // Adjuntar la función de interrupción del temporizador 4
  Timer4.pwm(6, 512);                 // Configurar el nuevo pin para la PWM con un duty cycle del 50%


  CAN.begin(CAN_500KBPS) == CAN_OK;  // Inicializar el bus CAN a una velocidad de 500 kbps
}

void loop() {

  // Comprobar si hay un mensaje recibido
  if (CAN_MSGAVAIL == CAN.checkReceive()) {
    CAN.readMsgBuf(&len, buf);  // Leer el mensaje CAN
    canId = CAN.getCanId();

    switch (canId) {
      case 0x001:
        // Obtener el valor del primer byte del buffer y mapearlo al rango deseado para la frecuencia del pin 11
        freq1 = map(buf[0], 0, 255, 20, 3200);  // Mapea la can de 0 a 255 (00 a FF) para variar la frecuencia de la PWM
        periodMicros1 = 1000000 / freq1;
        Timer1.setPeriod(periodMicros1 - 1);
        Timer1.setPwmDuty(11, periodMicros1 * dutyCycle1 / 100);
        break;

      case 0x002:
        // Obtener el valor del primer byte del buffer y mapearlo al rango deseado para la frecuencia del pin 3
        freq2 = map(buf[0], 0, 255, 5, 4000);
        periodMicros2 = 1000000 / freq2;
        Timer3.setPeriod(periodMicros2 - 1);
        Timer3.setPwmDuty(3, periodMicros2 * dutyCycle2 / 100);

        break;

      case 0x003:
        // Obtener el valor del primer byte del buffer y mapearlo al rango deseado para la frecuencia del pin 6
        freq3 = map(buf[0], 0, 255, 5, 4000);
        periodMicros3 = 1000000 / freq3;
        Timer4.setPeriod(periodMicros3 - 1);
        Timer4.setPwmDuty(6, periodMicros3 * dutyCycle3 / 100);
        
        break;
      default:
        break;
    }
  }
}

// Función de interrupción del temporizador 1 (pin 11)
void timerISR1() {
  static boolean state1 = HIGH;
  digitalWrite(11, state1);
  state1 = !state1;
}

// Función de interrupción del temporizador 3 (pin 3)
void timerISR3() {
  static boolean state2 = HIGH;
  digitalWrite(3, state2);
  state2 = !state2;
}

// Función de interrupción del temporizador 4 (nuevo pin)
void timerISR4() {
  static boolean state3 = HIGH;
  digitalWrite(6, state3);
  state3 = !state3;
}

A ver como va esto

void loop() {
  // Comprobar si hay un mensaje recibido
  if (CAN_MSGAVAIL == CAN.checkReceive()) {
    CAN.readMsgBuf(&len, buf);  // Leer el mensaje CAN
    canId = CAN.getCanId();

    switch (canId) {
      case 0x001:
        // Obtener el valor del primer byte del buffer y mapearlo al rango deseado para la frecuencia del pin 11
        freq1 = map(buf[0], 0, 255, 20, 3200);  // Mapea la can de 0 a 255 (00 a FF) para variar la frecuencia de la PWM
        periodMicros1 = 1000000 / freq1;
        Timer1.setPeriod(periodMicros1 - 1);
        Timer1.setPwmDuty(11, periodMicros1 * dutyCycle1 / 100);
        break;

      case 0x002:
        // Obtener el valor del primer byte del buffer y mapearlo al rango deseado para la frecuencia del pin 3
        freq2 = map(buf[0], 0, 255, 5, 4000);
        periodMicros2 = 1000000 / freq2;
        Timer3.setPeriod(periodMicros2 - 1);
        Timer3.setPwmDuty(3, periodMicros2 * dutyCycle2 / 100);

        break;

      case 0x003:
        // Obtener el valor del primer byte del buffer y mapearlo al rango deseado para la frecuencia del pin 6
        freq3 = map(buf[0], 0, 255, 5, 4000);
        periodMicros3 = 1000000 / freq3;
        Timer4.setPeriod(periodMicros3 - 1);
        Timer4.setPwmDuty(6, periodMicros3 * dutyCycle3 / 100);
        
        break;
      default:
        break;
    }
  } else {
    // Detener las salidas PWM si no hay msgs CAN
    Timer1.stop();
    Timer3.stop();
    Timer4.stop();
    digitalWrite(11, LOW);
    digitalWrite(3, LOW);
    digitalWrite(6, LOW);
  }
}

Gracias Surbyte por la sugerencia.

No funciona, no genera ninguna PWM,

He probado con una variable tipo boleana, de esta manera la PWM se para, pero no desaparece se queda estática y es lo miso que si para el CAN, con lo cual estoy en las mismas

#include <TimerOne.h>
#include <TimerThree.h>
#include <TimerFour.h>
#include <SPI.h>
#include "mcp_can.h"

MCP_CAN CAN(10);       // Inicializar MCP2515 CAN bus (CS pin 10)

// Variables para la lectura de CAN
unsigned char len = 0;
unsigned char buf[8];
unsigned char canId = 0;

bool canReceived = false; // Bandera para indicar si se ha recibido una señal CAN

// Configuracion de las PWM
int freq1 = 0;                    // Variable para almacenar la frecuencia del pin 11
int freq2 = 0;                    // Variable para almacenar la frecuencia del pin 3
int freq3 = 0;                    // Variable para almacenar la frecuencia del nuevo pin
int dutyCycle1 = 50;              // Duty cycle fijo para el pin 11
int dutyCycle2 = 50;              // Duty cycle fijo para el pin 3
int dutyCycle3 = 50;              // Duty cycle fijo para el nuevo pin
unsigned long periodMicros1 = 0;  // Variable para almacenar el período en microsegundos del pin 11
unsigned long periodMicros2 = 0;  // Variable para almacenar el período en microsegundos del pin 3
unsigned long periodMicros3 = 0;  // Variable para almacenar el período en microsegundos del nuevo pin

void setup() {
  pinMode(11, OUTPUT);  // Configurar el pin 11 como salida para la PWM
  pinMode(3, OUTPUT);   // Configurar el pin 3 como salida para la PWM
  pinMode(6, OUTPUT);   // Configurar el pin 6 como salida para la PWM
                        
  Timer1.initialize();                // Inicializar Timer1 para la PWM del pin 11
  Timer1.attachInterrupt(timerISR1);  // Adjuntar la función de interrupción del temporizador 1

  Timer3.initialize();                // Inicializar Timer3 para la PWM del pin 3
  Timer3.attachInterrupt(timerISR3);  // Adjuntar la función de interrupción del temporizador 3

  Timer4.initialize();                // Inicializar TimerFour para la tercera PWM
  Timer4.attachInterrupt(timerISR4);  // Adjuntar la función de interrupción del temporizador 4

  CAN.begin(CAN_500KBPS) == CAN_OK;  // Inicializar el bus CAN a una velocidad de 500 kbps
}

void loop() {
  // Comprobar si hay un mensaje recibido
  if (CAN_MSGAVAIL == CAN.checkReceive()) {
    CAN.readMsgBuf(&len, buf);  // Leer el mensaje CAN
    canId = CAN.getCanId();
    canReceived = true; // Indicar que se ha recibido una señal CAN

    switch (canId) {
      case 0x001:
        freq1 = map(buf[0], 0, 255, 20, 3200);                    
        periodMicros1 = 1000000 / freq1;                          
        Timer1.setPeriod(periodMicros1 - 1);                      
        Timer1.setPwmDuty(11, periodMicros1 * dutyCycle1 / 100);  
        break;

      case 0x002:
        freq2 = map(buf[0], 0, 255, 5, 4000);
        periodMicros2 = 1000000 / freq2;
        Timer3.setPeriod(periodMicros2 - 1);
        Timer3.setPwmDuty(3, periodMicros2 * dutyCycle2 / 100);
        break;

      case 0x003:
        freq3 = map(buf[0], 0, 255, 5, 4000);
        periodMicros3 = 1000000 / freq3;
        Timer4.setPeriod(periodMicros3 - 1);
        Timer4.setPwmDuty(6, periodMicros3 * dutyCycle3 / 100);
        break;
      
      default:
        break;
    }
  } else {
    canReceived = false; // Indicar que no se ha recibido una señal CAN
  }
}

// Función de interrupción del temporizador 1 (pin 11)
void timerISR1() {
  if(canReceived) {
    static boolean state1 = HIGH;
    digitalWrite(11, state1);
    state1 = !state1;
  }
}

// Función de interrupción del temporizador 3 (pin 3)
void timerISR3() {
  if(canReceived) {
    static boolean state2 = HIGH;
    digitalWrite(3, state2);
    state2 = !state2;
  }
}

// Función de interrupción del temporizador 4 (nuevo pin)
void timerISR4() {
  if(canReceived) {
    static boolean state3 = HIGH;
    digitalWrite(6, state3);
    state3 = !state3;
  }
}

¿Por qué haces con interrupciones lo mismo que ya hace la función pwm()?
Revisa el ejemplo FanSpeed de la librería y verás que no usa rutinas de manejo de interrupciones "personalizadas".

Sobre tu pregunta inicial, tienes señal ni bien alimentas la placa porque en setup() llamas a la función pwm() con lo cual inicias la generación de la señal PWM.

Para detener un timer y, obviamente, la generación de la PWM está la función stop(). Por otro lado lo inicias o reinicias con start() o resume().

Respecto a que las señales continúen aunque no se reciban nuevas tramas, podrías definir un tiempo luego del cual, si no hay nuevas tramas, se detenga el timer correspondiente.

1 Like

Gracias por Contestar MaximoEsfuerzo:

Pues la verdad lo hice con timer por que no conocía esa librería, Gracias por el consejo.

He vuelto a probar el ejemplo de mi Surbyte y si anulo esta parte del código, funciona y al PWM se para, no entiendo porque.

    digitalWrite(11, LOW);
    digitalWrite(3, LOW);
    digitalWrite(6, LOW);

Y con este consejo tuyo, probé hacer lo que me aconsejas y funciona perfecto. Aqui dejo el Codigo. Gracias

#include <TimerOne.h>
#include <TimerThree.h>
#include <TimerFour.h>
#include <SPI.h>
#include "mcp_can.h"

MCP_CAN CAN(10);       // Inicializar MCP2515 CAN bus (CS pin 10)

// Variables para la lectura de CAN
unsigned char len = 0;
unsigned char buf[8];
unsigned char canId = 0;

// Bandera para indicar si se ha recibido una señal CAN para cada PWM
bool canReceivedPWM1 = false;
bool canReceivedPWM2 = false;
bool canReceivedPWM3 = false;

// Variables para almacenar el tiempo de la última recepción CAN para cada PWM
unsigned long lastCanReceivedTimePWM1 = 0;
unsigned long lastCanReceivedTimePWM2 = 0;
unsigned long lastCanReceivedTimePWM3 = 0;

// Tiempo de espera en milisegundos antes de detener las PWM para cada canal
const unsigned long canTimeout = 1000;

// Configuracion de las PWM
int freq1 = 0;                    // Variable para almacenar la frecuencia del pin 11
int freq2 = 0;                    // Variable para almacenar la frecuencia del pin 3
int freq3 = 0;                    // Variable para almacenar la frecuencia del pin 6
int dutyCycle1 = 50;              // Duty cycle fijo para el pin 11
int dutyCycle2 = 50;              // Duty cycle fijo para el pin 3
int dutyCycle3 = 50;              // Duty cycle fijo para el nuevo pin
unsigned long periodMicros1 = 0;  // Variable para almacenar el período en microsegundos del pin 11
unsigned long periodMicros2 = 0;  // Variable para almacenar el período en microsegundos del pin 3
unsigned long periodMicros3 = 0;  // Variable para almacenar el período en microsegundos del pin 6

void setup() {
  pinMode(11, OUTPUT);  
  pinMode(3, OUTPUT);   
  pinMode(6, OUTPUT);   

  Timer1.initialize();                // Inicializar Timer1 para la PWM del pin 11
  Timer1.attachInterrupt(timerISR1);  // Adjuntar la función de interrupción del temporizador 1

  Timer3.initialize();                // Inicializar Timer3 para la PWM del pin 3
  Timer3.attachInterrupt(timerISR3);  // Adjuntar la función de interrupción del temporizador 3

  Timer4.initialize();                // Inicializar TimerFour para la tercera PWM
  Timer4.attachInterrupt(timerISR4);  // Adjuntar la función de interrupción del temporizador 4

  CAN.begin(CAN_500KBPS) == CAN_OK;  // Inicializar el bus CAN a una velocidad de 500 kbps
}

void loop() {
  // Comprobar si hay un mensaje recibido
  if (CAN_MSGAVAIL == CAN.checkReceive()) {
    CAN.readMsgBuf(&len, buf);  // Leer el mensaje CAN
    canId = CAN.getCanId();

    switch (canId) {
      case 0x001:
        canReceivedPWM1 = true;
        lastCanReceivedTimePWM1 = millis();
        freq1 = map(buf[0], 0, 255, 20, 3200);
        periodMicros1 = 1000000 / freq1;
        Timer1.setPeriod(periodMicros1 - 1);
        Timer1.setPwmDuty(11, periodMicros1 * dutyCycle1 / 100);
        break;

      case 0x002:
        canReceivedPWM2 = true;
        lastCanReceivedTimePWM2 = millis();
        freq2 = map(buf[0], 0, 255, 5, 4000);
        periodMicros2 = 1000000 / freq2;
        Timer3.setPeriod(periodMicros2 - 1);
        Timer3.setPwmDuty(3, periodMicros2 * dutyCycle2 / 100);
        break;

      case 0x003:
        canReceivedPWM3 = true;
        lastCanReceivedTimePWM3 = millis();
        freq3 = map(buf[0], 0, 255, 5, 4000);
        periodMicros3 = 1000000 / freq3;
        Timer4.setPeriod(periodMicros3 - 1);
        Timer4.setPwmDuty(6, periodMicros3 * dutyCycle3 / 100);
        break;
      default:
        break;
    }
  }

  // Verificar si ha pasado el tiempo de espera desde la última recepción CAN para cada PWM
  unsigned long currentTime = millis();
  if (currentTime - lastCanReceivedTimePWM1 > canTimeout) {
    canReceivedPWM1 = false;
  }
  if (currentTime - lastCanReceivedTimePWM2 > canTimeout) {
    canReceivedPWM2 = false;
  }
  if (currentTime - lastCanReceivedTimePWM3 > canTimeout) {
    canReceivedPWM3 = false;
  }
}

// Función de interrupción del temporizador 1 (pin 11)
void timerISR1() {
  if (canReceivedPWM1) {
    static boolean state1 = HIGH;
    digitalWrite(11, state1);
    state1 = !state1;
  }
}

// Función de interrupción del temporizador 3 (pin 3)
void timerISR3() {
  if (canReceivedPWM2) {
    static boolean state2 = HIGH;
    digitalWrite(3, state2);
    state2 = !state2;
  }
}

// Función de interrupción del temporizador 4 (nuevo pin)
void timerISR4() {
  if (canReceivedPWM3) {
    static boolean state3 = HIGH;
    digitalWrite(6, state3);
    state3 = !state3;
  }
}

Pero dijiste que al comenzar sin tener tramas CAN no debian funcionar los timers. Los comandos

   Timer1.stop();
    Timer3.stop();
    Timer4.stop();

los detiene y justamente si no hay ningun canId debe ir ahi.

Eso es Surbyte, mientras que no hay CAN no deben de funcionar los timers, pero la duda es por que si ponemos estas líneas, no funciona nada. No entiendo el porque, debería de funcionar.

digitalWrite(11, LOW);
    digitalWrite(3, LOW);
    digitalWrite(6, LOW);

Después ocurre otra cosa con Timer.stop()

En ambos GIF estoy enviando una trama CAN en bucle para que genere la PWM (la PWM esta a 12v por que la tengo amplificada)

Fijate que en el que esta sin Timer.stop la PWM se va a 0V y hay CAN en bucle, en la que no hay Timer.Stop la PWM siempre esta a 12V.

Supongo que Timer.Stop detecta el paso de CAN por 0, no le encuentro otra logica.

Muchas Gracias a ambos por ayudarme a aprender.

Vídeo_Sin timers Stop

Vídeo_Con timers Stop

No fue lo que escribí en el código.
Yo puse que si no tenés ninguna TRAMA CAN entonces que apague los timers. Eso como primera cosa. Luego CAN requiere tal vez que pongas el bit de salida en determinado estado. Eso te lo dejo a vos. Supuse que

digitalWrite(11, LOW);
    digitalWrite(3, LOW);
    digitalWrite(6, LOW);

lo haría, si quieres que tenga 12 ponelos en HIGH.

Entonces TimerX.Stop no detecta nada. Detiene el timer 1,2 o 3. Punto.
Llegamos ahi porque no hubo tramas CAN.
Antes estas lineas las habilitaste.
Asi que si no funcionan y requieres CAN en una linea independiente hablilita con la contraparte

digitalWrite(pin, HIGH);

Perfecto Surbyte, Gracias por la aclaración.

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.