PWM variable en Arduino UNO

Buenos dias, estoy haciendo un proyecto de un VDF y me tope con el problema de la lentitud de trabajar con analogwrite, ya que necesito una señal PWM que pase de 0% a 100% cada 10ms (lo que seria una frecuencia base de 60Hz con dos ciclos de 10ms), y con las simples lineas de codigo que tiré no logra convencerme, adicional a eso cada vez que la señal llege al 100% un bit de salida debe togglear, que en este caso es el pin10, el cual estoy viendo en un osciloscopio y genera una señal cuadrada bastante convincente, entre 13Hz y 78Hz, lo cual esta perfecto para mi, como esta señal esta tomada desde la condición de reseteo del PWM, se supone que cada vez que el PWM vuelve a cero el bit cambia, sin embargo la forma de generar este PWM no me agrada del todo y como es un PWM variable en el osciloscopio que tengo en este momento esta difícil de visualizar con precisión. Al momento no se me ocurre como podria generar de otra forma un PWM variable dentro de estos tiempos que he comentado.

int PWM;
int vel;
byte direccion;

void setup() {
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
  Serial.begin(9600);
  PWM = 0;
  direccion = 0;
}

void loop() {
  
  vel = map(analogRead(A0), 0, 1023, 900, 6000);
  analogWrite(9, PWM);
  delayMicroseconds(vel);
  PWM = PWM + 50;
  if (PWM >= 255) {
  PWM = 0;
  direccion = !direccion;
  }
  digitalWrite(10, direccion);
}

Desde ya es un problema para Timers.
Recuerda o considera algunos errores que introduces en tu código.
Nunca uses delay del tipo que sea en rutinas de tiempo como en tu caso. Cuando el micro encuentra delay no hace nada. Ahora la ejecución permite que los 100useg que toma leer el potenciometro mas lo que lleva ejecutar analogWrite tengan mas tiempo.
En su lugar usa micros variante de millis().

// define a startPwm como global de este modo
unsigned long startPwm;

/// resto del código

void loop() {
  
  vel = map(analogRead(A0), 0, 1023, 900, 6000);
  analogWrite(9, PWM);
  if (micros() - startPwm > vel) {
      PWM = PWM + 50;
      if (PWM >= 255) {
          PWM = 0;
          direccion = !direccion;
      }
      digitalWrite(10, direccion);
      startPwm = micros();
   }
}

Si esto no funciona hay que trabajar con TIMERS

Hola, use delay porque la funcion PWM no se detiene cuando se ejecuta el delay, por eso decidi usarla, primero habia hecho pruebas con millis() pero al final tardaba mucho mas en ejecurse el codigo y me iba peor, creo que con micros pasara lo mismo, ahora estoy haciendo pruebas y reordene el código para dejar todo el tiempo muerto lo mas junto posible y mejoro bastante, pero se me vino otro problema, como el PWM trabaja de forma paralela el codigo, cuando se cumple la condicion de PWM >= el PWM cae a cero, pero me di cuenta de que esto pasa en cualquier parte del ciclo de trabajo, a veces lo pilla justo en LOW pero a veces lo pilla en HIGH y lo bota a medio pulso, estoy tratando de leer el estado de la salida analoga con (bitRead(PINB,1)) que seria el pin 9 del arduino.

int PWM;
int vel;
byte direccion;


void setup() {
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
  Serial.begin(9600);
  PWM = 0;
  direccion = 0;
}

void loop() {
  

  PWM = PWM + 51;
  if (PWM >= 255 && (!bitRead(PINB,1))) {
  PWM = 0;
  direccion = !direccion;
  }
  delayMicroseconds(vel);
  digitalWrite(10, direccion);
  analogWrite(9, PWM);
  vel = map(analogRead(A0), 0, 1023, 1200, 12000);
}

@Surbyte he usado el codigo con micros y se mantiene mas estable, sin embargo la señal PWM no es convincente, creo que hay que meterse con los TIMERS, asi que no me quedara otra que leer bastante, en la web hay varios pseudo VDF con arduino pero nadie hace la señal PWM tipo acordeon que es como realmente funcionan, si gustas trasladas este post a proyectos o lo eliminas y creo uno nuevo, para que asi pueda ir documentando todo el avance, la idea es hacer un VDF modular paso a paso, para poder presentarlo a algunos estudiantes y pueden conocer las etapas de la forma mas didáctica posible. Aunque lo realmente difícil de la señal acordeón que técnicamente se llama SPWM es posible generarla con Operacionales, yo en lo personal tengo ganas de hacerlo en arduino, si veo que se complica mucho no quedara de otra que hacerlo con OPAMPS.

señal

Mira, yo hice en algun momento un intento de una máquina generadora de H2 usando un modulador con dos timers. Uno era la portadora y el otro de diferente frecuencia (no me importaba el PWM xq estaba al 50%).
Voy a buscarlo porque te serviría de guia.
Te lo explico a grandes pasos: 1 timer va a su frecuencia la que tu decides con el potenciometro, es la portadora.
El otro timer en tu caso con pwm variable funciona cuando el primero se lo indica o mejor dicho cuando esta en 1. Muy fácil cuando lo veas con las dos interrupciones que controlan cada uno de los casos.
Eso no usa al uC y puedes actuar de manera limpia.

Mira, por alguna razón nunca busque por SPWM y he dado con un código bastante bueno, el cual tengo que estudiarlo para entenderlo bien, con esto ya es posible hacer un inversor y un VDF monofásico, así que lo trabajare mientras comprendo todo el código para después ver triplicar la señal y desplazar 120º para un VDF trifásico.

link aca

Es el camino. No he encontrado el código pero ya lo haré.

El código es este y creo que estaba basado en un código de alguien o de varios tutos pero no me acuerdo.

#include <Arduino.h>

// Modulador frecuenca 1 sobre una frecuencia 2 con ancho de pulso variable.
// Fecha: 19 Julio del 2020

const byte POT = A0;
const byte LED = 9;  // Timer 1 A output: OC1A

// La frecuencia de reloj surge de dividir por la f deseada (para el prescaler utilizado)
// ojo no cualquier valor puede usarse como frecuencia portadora.
const long FREQ = 500L;
const long timer2_OCR2A_Setting = F_CPU / FREQ / 128L;  // X ticks

ISR (PCINT2_vect)   {
  // if pin 3 esta en HIGH, poner en 1 OC1A on compare
  if (PIND & bit (3))  {
      TCCR1A |= bit (COM1A0) ;    // Toggle OC1A on Compare Match
  }
  else  {
      TCCR1A &= ~bit (COM1A0) ;   // DO NOT Toggle OC1A on Compare Match
      digitalWrite (LED, LOW);    // ensure off
  }
}

void setup() {
  pinMode (LED, OUTPUT);
  pinMode (  3, OUTPUT);  // OC2B
  
  // fijo Timer 1 - gives us 38.095 kHz
  TCCR1A = 0; 
  TCCR1B = bit(WGM12) | bit (CS10);   // CTC, No prescaler
  OCR1A =  (F_CPU / 38000L / 2) - 1;  // zero relative
  
  // Timer 2 - me da 1 ms de intervalo de conteo
  // 16 MHz clock (62.5 ns por tick) prescaled x 128
  // incremento del contador cada8 µs. 
  // entonces contamos X ticks. 
  // Si freq fuera 500 Hz entonces da 250, 
  // timer2_OCR2A_Setting = F_CPU / FREQ / 128L;
  // lo que da 2000 µs exactos (período de 2 ms o 500 Hz frecuencia)

  TCCR2A = bit (WGM20) | bit (WGM21) | bit (COM2B1);    // Fast PWM mode
  TCCR2B = bit (WGM22) | bit (CS20) | bit (CS22) ;      // prescaler a 128
  OCR2A  = timer2_OCR2A_Setting - 1;                    // cuento hasta X = 250 p.ej
  
  // cambio de pin de interrupción
  PCMSK2 |= bit (PCINT19);  // quiero el pin3
  PCIFR  |= bit (PCIF2);    // borro cualquier interrupción outstanding
  PCICR  |= bit (PCIE2);    // Habilito el cambio de pin para D0 a D7
}

void loop()  {
  // modifico el ancho de pulso del Timer de acuerdo al potenciometro.
  OCR2B = (((long) (analogRead(POT) + 1) * timer2_OCR2A_Setting) / 1024L) - 1;
}

Claro que tu quieres cambiar la frecuencia con el pote y el PWM que sea solo.
Es fácilmente modificable dentro de ciertos parámetros dados por el prescaler del Timer1.

Este es mi primera prueba no la que usé en el generador de HHO.

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