Go Down

Topic: Problema con interrupción y timers millis/micros (Read 331 times) previous topic - next topic

fernando1828

Mar 07, 2019, 11:50 pm Last Edit: Mar 07, 2019, 11:56 pm by fernando1828
Hola a todos después de buscar por la web los molesto con el siguiente problema: estoy armando un aparato para derretir cera. Consta de una resistencia de 220 v / 1000 watts, un sensor de temperatura ds18b20 y un display lcd 16x2.

Básicamente el programa lo que hace es leer las interrupciones (de un optoacoplador, cruce por cero) y luego disparar un triac para manejar los 220v de la resistencia a través de un PID. El problema surge cuando enchufo los 220v: se disparan la interrupciones y sucede que la pantalla corre muy lenta. Cuando hago lecturas de millis o micros veo que no corren como corresponde y todo anda muy lento.

Code: [Select]


#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);

#include <OneWire.h>
#include <DallasTemperature.h>
#define ONE_WIRE_BUS 11
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
DeviceAddress Termometro;

float temp = 0.0;

const byte rojo = 7;
const byte verde = 8;
bool estadoledv = true;
bool estadoledr = true;

const byte buzzer = 12;
bool estadobuzzer = false;

const byte interruptPin = 2;
const byte dimmer = 6;
int valor = 0;

byte setpoint = 38;
unsigned long elapsedTime = 0;
unsigned long Time = 0;
unsigned long timePrev = 0;

unsigned long tiempoactual = 0;
unsigned long tiempoanterior = 0;
unsigned long tiempoanterior2 = 0;
unsigned long tiempoanterior3 = 0;
unsigned long tiempoanterior4 = 0;

const byte boton =  9;
bool pressed_1 = false;
volatile bool encender = true;
byte a = 1;

byte n = 0;

float PID_error = 0.0;
float previous_error = 0.0;
volatile int PID_value = 0;
// ORIGINAL!!! : byte kp = 203;   byte ki = 7.2;   byte kd = 1.04;
int kp = 1000;   byte ki = 0;   byte kd = 25;
byte PID_p = 0;    byte PID_i = 0;   byte PID_d = 0;


void setup() {
  lcd.begin();
  Wire.setClock(100000L);
  lcd.backlight();
  pinMode(rojo, OUTPUT);
  pinMode(verde, OUTPUT);
  inicio();
  pinMode(interruptPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(interruptPin), resistencia, RISING);
  pinMode(dimmer, OUTPUT);
  digitalWrite(dimmer, LOW);
  pinMode (boton, INPUT_PULLUP);
  pinMode (buzzer, OUTPUT);


  PCIFR |= (1 << PCIF0);
  PCICR |= (1 << PCIE0);
  PCMSK0 |= (1 << PCINT1);


  Serial.begin(9600);

  sensors.begin();
  sensors.getAddress(Termometro, 0);
  sensors.setResolution(Termometro, 9);

}

void loop() {

  tiempoactual = micros() / 1000L;

  if (tiempoactual - tiempoanterior2 >= 800 && a == 0) {
    a = 1;
  }

  if (tiempoactual - tiempoanterior3 >= 3000) {
    tiempoanterior3 = tiempoactual;
    n++;
    lcd.clear();
    if (n == 2) {
      n = 0;
    }
  }

  switch (n) {
    case 0:
      estado();
      break;

    case 1:
      temperatura();
      break;
  }


  if (tiempoactual - tiempoanterior >= 500) {
    tiempoanterior = tiempoactual;
    // sensors.requestTemperatures();
    // temp = sensors.getTempC(Termometro);
    temp = 20.5;

    if (encender) {
      previous_error = PID_error;
      PID_error = setpoint - temp;

      if (PID_error > 30)                             //integral constant will only affect errors below 30ºC
      {
        PID_i = 0;
      }

      PID_p = kp * PID_error;                         //Calculate the P value
      PID_i = PID_i + (ki * PID_error);               //Calculate the I value
      PID_d = kd * ((PID_error - previous_error) / 0.5); //Calculate the D value
      PID_value = 9200.0 - (PID_p + PID_i + PID_d);                      //Calculate total PID value

      //We define firing delay range between 0 and 7400. Read above why 7400!!!!!!!
      if (PID_value < 0)      {
        PID_value = 0;
      }

      if (PID_value > 9200)      {
        PID_value = 9200;
      }
      Serial.println(tiempoactual);
      // PID_value = 3000;
      // LOW VERDE
      PORTB &= B11111110;

      if (!estadoledr) {
        PORTD |= B10000000;
        //ROJO HIGH
      }
      else {
        //LOW ROJO
        PORTD &= B01111111;
      }
      estadoledr = !estadoledr;

    }

    else {
      // triac LOW
      PORTD &= B10111111;

      //LOW ROJO
      PORTD &= B01111111;

      if (!estadoledv) {
        PORTB |= B11000001;
        //VERDE HIGH
      }
      else {
        // LOW VERDE
        PORTB &= B11111110;
      }
      estadoledv = !estadoledv;

    }

    if (temp >= 30) {
      if (!estadobuzzer) {

        PORTB |= B11010000;
        //BUZZER HIGH
      }
      else {
        //BUZZER LOW
        PORTB &= B11101111;
      }
    }
    else {
      //BUZZER LOW
      PORTB &= B11101111;
    }
    estadobuzzer = !estadobuzzer;

  }
}


void resistencia() {
 // if (encender) {
    delayMicroseconds(PID_value);
    // triac high
    PORTD |= B01000000;
    delayMicroseconds(100);
    // triac LOW
    PORTD &= B10111111;
 // }
}


 ISR(PCINT0_vect) {
  if (PINB & B00000010) {
    if (a == 1)    {
      encender = !encender;
      //Serial.println(encender);
      tiempoanterior2 = tiempoactual;
      a = 0;
    }
  }
}



void estado() {
  lcd.setCursor(4, 0);
  lcd.print("Estado:");

  if (!encender) {
    lcd.setCursor(3, 1);
    lcd.print(" Detenido ");
  }
  else {
    lcd.setCursor(3, 1);
    lcd.print("Calentando");
  }

}

void temperatura() {

  lcd.setCursor(0, 0);
  lcd.print("Temp.:");
  lcd.setCursor(7, 0);
  lcd.print(temp);
  lcd.setCursor(13, 0);
  lcd.print((char)223);
  lcd.setCursor(14, 0);
  lcd.print("C");

  lcd.setCursor(3, 1);
  lcd.print("Set:");
  lcd.setCursor(8, 1);
  lcd.print(valor);
  lcd.setCursor(11, 1);
  lcd.print((char)223);
  lcd.setCursor(12, 1);
  lcd.print("C");

  if (tiempoactual - tiempoanterior4 >= 150) {
    tiempoanterior4 = tiempoactual;
    valor =  analogRead(A0);
    valor = map(valor, 0, 1023, 27 , 31);
  }
}


void inicio() {

  PORTD |= B10000000;
  //ROJO HIGH
  PORTB |= B11000001;
  //VERDE HIGH
  lcd.setCursor(4, 0);
  lcd.print("INTRONIC");
  delay(1000);
  lcd.setCursor(0, 1);
  lcd.print("Iniciando...");
  delay(2000);
  lcd.clear();

  //LOW ROJO
  PORTD &= B01111111;
  // LOW VERDE
  PORTB &= B11111110;

}



El arranque y parada del sistema es con un pulsador. La verdad no me doy cuenta donde meto la pata...

gracias!!!

PeterKantTropus

Dentro de la función de interrupción no funciona nada que dependa de otras interrupciones o timers, por eso no funciona los delays
Saludos
"Si no entra como tornillo, entra como clavo"

fernando1828

Entiendo pero necesito entrar con una frecuencia de 100Hz a la rutina de interrupción y medir el tiempo de disparo del triac e ir mostrando las pantallas. como puedo medir el tiempo del loop?

Gracias!!

PeterKantTropus

No leí tu programa,  pero si quieres medir tiempo entre lapsos de ocurrencia de interrupciones se hace levantando banderas dentro de la interrupción y contando fuera de ella.
"Si no entra como tornillo, entra como clavo"

fernando1828

Lo que se me ocurrió es:

Code: [Select]

void interrupcion() {

  resistencia();

}

void resistencia() {

  delayMicroseconds(PID_value);

  // triac high
  PORTD |= B01000000;
  delayMicroseconds(100);
  // triac LOW
  PORTD &= B10111111;

}


De esa manera me libero de usar delay dentro de la interrupción y no ejecuto la orden de la interrupción en el loop. Anda muchísimo mejor pero aun está un poquito lento...  :(

gracias por la ayuda. Si se puede mejorar escucho opiniones.


fernando1828

No leí tu programa,  pero si quieres medir tiempo entre lapsos de ocurrencia de interrupciones se hace levantando banderas dentro de la interrupción y contando fuera de ella.
Ya lo intenté pero a veces en el loop entra a leer el sensor y se alenta el programa. Existe otra solución??

surbyte


PeterKantTropus

#7
Mar 10, 2019, 11:42 am Last Edit: Mar 10, 2019, 11:44 am by PeterKantTropus
Lo que se me ocurrió es:

Code: [Select]

void interrupcion() {

  resistencia();

}

void resistencia() {

  delayMicroseconds(PID_value);

  // triac high
  PORTD |= B01000000;
  delayMicroseconds(100);
  // triac LOW
  PORTD &= B10111111;

}


De esa manera me libero de usar delay dentro de la interrupción y no ejecuto la orden de la interrupción en el loop. Anda muchísimo mejor pero aun está un poquito lento...  :(

gracias por la ayuda. Si se puede mejorar escucho opiniones.


Igualmente no funcionaría, mientras la interrupción no devuelva el control al loop, los delays no funcionan.
La función de las interrupciones es no perder un evento, no ejecutar código. Deberías levantar una bandera dentro de la interrupción y en loop, mediante un if , llamar a función que contiene el código y al terminar de la misma, bajar la bandera.u
"Si no entra como tornillo, entra como clavo"

surbyte

Busca : Arduino ACPhaseControl elige la opcion del playground.

Estudia Timers.


Go Up