Inversor trifásico microcontrolado

Buenas a todos,

Me presento soy un estudiante de Ingeniería electrónica que esta terminando la carrera, y estoy realizando como trabajo fin de titulo un Inversor trifásico microcontrolado con una placa Arduino.
He trabajado bastante con el proyecto aunque todavía tengo algunos fallos que espero que ustedes me puedan ayudar con ellos. Y una vez terminado el proyecto colgar aquí la memoria para que otras personas puedan nutrirse de ella y mejorarla.

Bueno el objetivo de mi proyecto consiste en tomar una tensión continua de una batería, fuente de alimentación, etc. Y transformarla en una señal trifásica de unos 100 V de tensión y 50Hz de frecuencia. Se usara una tensión de 100V debido a que voy a fabricar un prototipo (ya he pedido todos los componentes) que tendrá una función didáctica en mi universidad, y para cumplir con la normativa y evitar el riesgo de accidente se ha elegido esa tensión.

La placa que voy a utilizar para este proyecto es una placa Arduino uno R3. Utilizaré las 6 salidas PWM de la placa para controlar la activación/desactivación de 3 pares de transistores MOSFET, los cuales generarán la alternancia de la señal continua.

En mi código la principal premisa que sigo es la de la velocidad, ya que configuraré las PWM para que oscilen a 31250 Hz; lo que me obliga a realizar las operaciones lo más rápidamente posible. Lo que he hecho ha sido crear una tabla con 625 valores con los distintos duty cycle que tomarían las PWM para generar una senoidal de 50 Hz. Y me movería a lo largo de la tabla con punteros (1 por cada PWM ). La tabla contiene todos los duty cycles necesarios a lo largo de un período de una señal de 50Hz, podrían ser menos valores pero he decidido hacerlo así a priori para conseguir una senoidal lo más perfecta posible.

¿Por qué son 625 valores? Bueno, el período de una señal de 50 Hz es 20 ms (20 000us). Y el período de las PWM de 31250Hz es 32 us. Si dividimos: 20 000 us / 32 us = 625 valores, de esta forma sé que cada 32 us tengo que modificar cada duty cycle y me aseguro de que cuando se haya recorrido todos los valores de la tabla habré completado 20 ms y por tanto la señal de salida será de 50 Hz.

Bueno, voy a explicar un poco por encima como he realizado el código aunque esta bastante bien comentado.

He realizado el código usando los registros de control del micro Atmega ya que así modifico los timers y actúo de manera más rápida sobre los registros de comparación para evitar sumar tiempos en cada operación.

He creado unos retardos para evitar cortocircuitos entre pares de transistores de la misma rama usando ensamblador ya que no puedo usar la función delay () porque he modificado el timer0 del cual depende este retardo.

El código lo pongo en el siguiente post porque excedo las palabras por post, I'm sorry.

¿Qué problemas tengo ?

  • Bueno el principal problema que tengo consiste en que cuando simulo este código en el programa Proteus 8, la salida que me obtengo en los piner que corresponden al timer 1 se me genera un 0 durante un largo período (63 ms) de tiempo pero no encuentro el problema ni la posible causa. En un principio pensé que podría ser la configuración del timer por ser de 16 bits pero no creo que esté mal configurado.

Efecto del 0:

Efecto del 0 ampliado:

link a la página:

http://subefotos.com/ver/?951e00da44d4992bf942a90cc0e1396bo.png

  • El segundo problema que he tenido ha sido cuando he realizado las pruebas de los retardos en ensamblador, los cuales me generan un retardo correcto dos veces y una tercera vez me realizan el retardo el doble de grande y así de manera continuada. Este hecho solo ocurre cuando los timers son modificados pero no ocurre cuando se mantiene la configuración normal.

Este es el código de prueba del retardo:

void setup() {
  
  pinMode (13, OUTPUT);
  
  TCCR0A = 0xA1; // Habilitamos las dos salidas en modo Fast PWM - 8 bits. Pre-escaler = 1.
  TCCR0B = 0xC1; 
  TCCR1A = 0xA1; // Habilitamos las dos salidas en modo Phase-correct - 8 bits. Pre-escaler = 1.
  TCCR1B = 0xC1;
  TCCR2A = 0xA1; // Habilitamos las dos salidas en modo Phase-correct - 8 bits. Pre-escaler = 1.
  TCCR2B = 0xC1; 
}

void loop() {


  PORTB = 0x20;

   asm volatile ( // Este es un retardo de 6.5 us
        "clr r16 \n" // 1 ciclo de reloj.
        "ldi r16, 0x1A \n" // 1 ciclo de reloj. 80 vueltas cargadas.
        "1: dec r16 \n" // 1 ciclo de reloj
        "brne 1b  \n" // 2 ciclos de reloj para retornar y 1 para salir 
        );      

  PORTB = 0x00;
  asm volatile (
        "clr r16 \n"
        "ldi r16, 0x1A \n"
        "2: dec r16 \n"
        "brne 2b  \n"

}

Efecto doble del retardo:

link a la página por si se ve muy pequeña la imagen:

http://subefotos.com/ver/?90c8eb8024752dcf2396598f58df0585o.png

Imagen con configuración normal (mismo codigo pero sin poner los TCCxR):

Ahora si cuelgo el código principal:

int dutycycle_156 [625] = {128, 129, 130, 130, 131, 132, 133, 134, 135, 135, 136, 137, 138, 139, 139, 140, 141, 142, 143, 144, 144, 145, 146, 147, 148, 148, 149, 150, 151, 
                           152, 153, 153, 154, 155, 156, 157, 157, 158, 159, 160, 161, 162, 162, 163, 164, 165, 166, 167, 167, 168, 169, 170, 171, 171, 172, 173, 174, 175, 
                           176, 176, 177, 178, 179, 180, 180, 181, 182, 183, 184, 185, 185, 186, 187, 188, 189, 189, 190, 191, 192, 193, 194, 194, 195, 196, 197, 198, 198, 
                           199, 200, 201, 202, 203, 203, 204, 205, 206, 207, 207, 208, 209, 210, 211, 212, 212, 213, 214, 215, 216, 216, 217, 218, 219, 220, 221, 221, 222, 
                           223, 224, 225, 225, 226, 227, 228, 229, 230, 230, 231, 232, 233, 234, 234, 235, 236, 237, 238, 239, 239, 240, 241, 242, 243, 244, 244, 245, 246, 
                           247, 248, 248, 249, 250, 251, 252, 253, 253, 254, 255, 255, 255, 254, 253, 253, 252, 251, 250, 249, 248, 248, 247, 246, 245, 244, 244, 243, 242, 
                           241, 240, 239, 239, 238, 237, 236, 235, 234, 234, 233, 232, 231, 230, 230, 229, 228, 227, 226, 225, 225, 224, 223, 222, 221, 221, 220, 219, 218, 
                           217, 216, 216, 215, 214, 213, 212, 212, 211, 210, 209, 208, 207, 207, 206, 205, 204, 203, 203, 202, 201, 200, 199, 198, 198, 197, 196, 195, 194, 
                           194, 193, 192, 191, 190, 189, 189, 188, 187, 186, 185, 185, 184, 183, 182, 181, 180, 180, 179, 178, 177, 176, 176, 175, 174, 173, 172, 171, 171, 
                           170, 169, 168, 167, 167, 166, 165, 164, 163, 162, 162, 161, 160, 159, 158, 157, 157, 156, 155, 154, 153, 153, 152, 151, 150, 149, 148, 148, 147, 
                           146, 145, 144, 144, 143, 142, 141, 140, 139, 139, 138, 137, 136, 135, 135, 134, 133, 132, 131, 130, 130, 129, 128, 127, 126, 126, 125, 124, 123, 
                           122, 121, 121, 120, 119, 118, 117, 117, 116, 115, 114, 113, 112, 112, 111, 110, 109, 108, 108, 107, 106, 105, 104, 103, 103, 102, 101, 100, 99, 
                            99, 98, 97, 96, 95, 94, 94, 93, 92, 91, 90, 89, 89, 88, 87, 86, 85, 85, 84, 83, 82, 81, 80, 80, 79, 78, 77, 76, 76, 75, 74, 73, 72, 71, 71, 70, 
                            69, 68, 67, 67, 66, 65, 64, 63, 62, 62, 61, 60, 59, 58, 58, 57, 56, 55, 54, 53, 53, 52, 51, 50, 49, 49, 48, 47, 46, 45, 44, 44, 43, 42, 41, 40, 
                            40, 39, 38, 37, 36, 35, 35, 34, 33, 32, 31, 31, 30, 29, 28, 27, 26, 26, 25, 24, 23, 22, 22, 21, 20, 19, 18, 17, 17, 16, 15, 14, 13, 12, 12, 11, 
                            10, 9, 8, 8, 7, 6, 5, 4, 3, 3, 2, 1, 1, 1, 2, 3, 3, 4, 5, 6, 7, 8, 8, 9, 10, 11, 12, 12, 13, 14, 15, 16, 17, 17, 18, 19, 20, 21, 22, 22, 23, 24, 
                            25, 26, 26, 27, 28, 29, 30, 31, 31, 32, 33, 34, 35, 35, 36, 37, 38, 39, 40, 40, 41, 42, 43, 44, 44, 45, 46, 47, 48, 49, 49, 50, 51, 52, 53, 53, 
                            54, 55, 56, 57, 58, 58, 59, 60, 61, 62, 62, 63, 64, 65, 66, 67, 67, 68, 69, 70, 71, 71, 72, 73, 74, 75, 76, 76, 77, 78, 79, 80, 80, 81, 82, 83, 
                            84, 85, 85, 86, 87, 88, 89, 89, 90, 91, 92, 93, 94, 94, 95, 96, 97, 98, 99, 99, 100, 101, 102, 103, 103, 104, 105, 106, 107, 108, 108, 109, 110, 
                           111, 112, 112, 113, 114, 115, 116, 117, 117, 118, 119, 120, 121, 121, 122, 123, 124, 125, 126, 126, 127, 128};

           
int puntero_sin0   = 157;    // Usada para moverse por los valores de la tabla. Su máximo será 625. Representa la señal senoidal sin desfase. Apunta a DC = 255.
int puntero_sin120 = 547;    // Usada para moverse por los valores de la tabla. Su máximo será 625. Representa la señal senoidal con desfase = 120. Apunta a DC = 64.
int puntero_sin240 = 391;    // Usada para moverse por los valores de la tabla. Su máximo será 625. Representa la señal senoidal con desfase = 240. Apunta a DC = 64.

  /* Ahora se llevará acabo la configuración de los timers de la placa arduino, ya que se requiere la modificación de estos
     para la correcta generación de las señales PWM. Los registros de control serán modificados mediante hexadecimal. */

void setup() {

  // Configuramos los pines de PWM como salidas

  pinMode (3, OUTPUT);  // Conectado a transistor Q6. Par transistores para senoidal con fase 240 
  pinMode (5, OUTPUT);  // Conectado a transistor Q2. Par transistores para senoidal con fase 0
  pinMode (6, OUTPUT);  // Conectado a transistor Q1. Par transistores para senoidal con fase 0
  pinMode (9, OUTPUT);  // Conectado a transistor Q3. Par transistores para senoidal con fase 120
  pinMode (10, OUTPUT); // Conectado a transistor Q4. Par transistores para senoidal con fase 120
  pinMode (11, OUTPUT); // Conectado a transistor Q5. Par transistores para senoidal con fase 240
  
  // Configuramos los timers mediante los registros de control.
  
  TCCR0A = 0xA1; // Habilitamos las dos salidas en modo Phase-correct - 8 bits. Pre-escaler = 1. Salidas no invertidas.
  TCCR0B = 0xC1; 
  TCCR1A = 0xA1; // Habilitamos las dos salidas en modo Phase-correct - 8 bits. Pre-escaler = 1. Salidas no invertidas.
  TCCR1B = 0xC1;
  TCCR2A = 0xA1; // Habilitamos las dos salidas en modo Phase-correct - 8 bits. Pre-escaler = 1. Salidas no invertidas.
  TCCR2B = 0xC1; 
}

void loop() {

  // Programa principal, se usarán directamente los registros de comparación porque es más rápido que usar las funciones propias de arduino.
  
  OCR0A = puntero_sin0;    // Registro de comparación para pin A del timer 0. Genera la PWM para Q1.

  /* Este es un retardo de 4.875 us utilizado para crear un tiempo muerto entre pares de transistores y evitar cortocircuitos en los mismos.
     Demostración: (1+1+(1+2)*25+1)/16000000 = 4.875 us*/

  asm volatile ( 
        "clr r16 \n"       // 1 ciclo de reloj.
        "ldi r16, 0x14 \n" // 1 ciclo de reloj. 26 vueltas cargadas.
        "1: dec r16 \n"    // 1 ciclo de reloj
        "brne 1b  \n"      // 2 ciclos de reloj para retornar y 1 para salir 
      ); 
      
   OCR0B = puntero_sin0;    // Registro de comparación para pin B del timer 0. Genera la PWM para Q2.
   OCR1A = puntero_sin120;  // Registro de comparación para pin A del timer 1. Genera la PWM para Q3.
  
  // Retardo 4.875 us.
  
   asm volatile ( 
      "clr r16 \n"       // 1 ciclo de reloj.
      "ldi r16, 0x14 \n" // 1 ciclo de reloj. 26 vueltas cargadas.
      "1: dec r16 \n"    // 1 ciclo de reloj
      "brne 1b  \n"      // 2 ciclos de reloj para retornar y 1 para salir 
    ); 
    
   OCR1B = puntero_sin120;  // Pin B del timer 1. Genera la PWM para Q4
   OCR2A = puntero_sin240;  // Pin A del timer 2. Genera la PWM para Q5
   
   // Retardo 4.875 us.
    
   asm volatile ( 
      "clr r16 \n"       // 1 ciclo de reloj
      "ldi r16, 0x14 \n" // 1 ciclo de reloj. 26 vueltas cargadas.
      "1: dec r16 \n"    // 1 ciclo de reloj
      "brne 1b  \n"      // 2 ciclos de reloj para retornar y 1 para salir 
    ); 
    
    OCR2B = puntero_sin240;  // Registro de comparación para pin B del timer 2. Genera la PWM para Q6
  
  /////-------------------Falta el retardo de 32 us---------------------------------------/////

  // Ahora establecemos las condiciones para moverse por la tabla de valores y volver al comienzo una vez completado un período de señal.
  
    if ( puntero_sin0++ < 625 )   { puntero_sin0++; }   else { puntero_sin0 = 0; }   // Si no se ha llegado al final de la tabla, avanza. Si se ha llegado, vuelve a 0.
    if ( puntero_sin120++ < 625 ) { puntero_sin120++; } else { puntero_sin120 = 0; } // Si no se ha llegado al final de la tabla, avanza. Si se ha llegado, vuelve a 0.
    if ( puntero_sin240++ < 625 ) { puntero_sin240++; } else { puntero_sin240 = 0; } // Si no se ha llegado al final de la tabla, avanza. Si se ha llegado, vuelve a 0.
     
}

Sube el archivo proteus. zipeado claro.

Me faltan conocimientos para ayudarte ,pero si me parece que en el array no guardas valores superiores a 255 por lo que substituyendo int por byte ahorras 625 bytes de sram y alomejor hasta ahorras tiempo de lectura.

jose:
Me faltan conocimientos para ayudarte ,pero si me parece que en el array no guardas valores superiores a 255 por lo que substituyendo int por byte ahorras 625 bytes de sram y alomejor hasta ahorras tiempo de lectura.

Buena observación.
Ademas definelos como const byte dutycycle_156 [625] = {.......};

sigo a la espera del archivo proteus para analizarlo.

Buenas, yo no tengo el nivel en arduino como para ayudarte en el proyecto, pero sí que puedo hacer una cosa que creo que te puede ayudar.

He cargado el sketch en un Arduino Uno físico, y luego, lo he conectado a un analizador lógico, obteniendo el siguiente resultado:

Creo que los cortes que se ven en tus fotos, son algún problema de la simulación, ya que en el montaje real, no se aprecian, Pero lo que sí que he podido apreciar, es que parece que hay veces, que no se acaba del todo el ciclo de PWM. No se si tiene que ser así o no.

He añadido la captura con el analizador lógico, comprimida en zip. Podéis descargaros gratis el software Saleae Logic para ver la captura.

Link de descarga de Saleae Logic 1.1.34

Trifasico.zip (663 KB)

Un consejo, para este tipo de cosas olvida Proteus, le pides que haga demasiado. Se que sueno contradictorio, pero era para probarlo en la simulación, yo tmb he encontrado problemas cuando trabajo asi.

Debes pasar al terreno real directamente con algunos cuidados.

Yo no estoy tan de acuerdo con que trabajes en 100V.
Todo lo que esta por encima de 70VDC no se considera seguro.
Asi que prueba con 24V y genera una trifásica 24VAC que será mas que cómoda para tus pruebas o 12, y usas un par de lamparas de auto para probar la carga o Resistores, claro, algo que sabes por cierto.

Puedes publicar la etapa de potencia?
Usas los IR2101 como drivers para los MOSFET que estarán a HV? Ya se que no estas a High Voltage todavía por la fase de pruebas.

Perdonad que no haya contestado antes, pero he estado muy ocupado.

Le he dedicado unas cuantas horas a solucionar el problema y he encontrado el fallo.... merezco que me devuelvan a primero de carrera xD (pero espero que no lo hagan ).

El problema estaba en que iguala el registro de comparación con el puntero y no con el valor de la tabla al que apunta el puntero, error super tonto jajaja. Bueno una vez solucionado, subiré en el siguiente post el código corregido aunque no esta terminado todavía.

También creo que Proteus no es lo más recomendable para este tipo de proyectos porque he cargado el mismo código con el mismo circuito el mismo día, y me ha dado unos resultados totalmente diferentes. Pero como vivo a 60 Km de los laboratorios de la universidad no me queda otra que tirar de el Proteus :S.

Gracias surbyte y jose por el aporte y tomaré nota de ello, mañana modificaré el array y lo optimizaré.

Es cierto que toda tensión en continua que supere los 60-70 V es muy peligrosa, pero esa tensión se encontrará dentro del prototipo por lo que no será accesible a las personas. Lo que hará el prototipo será tomar la tensión de la red y transformarla a continua para "simular" una batería en su interior.
El problema estaría en la salida del prototipo, ya que se conectarían distintos tipos de carga para que los futuros alumnos aprecien el funcionamiento del circuito, y la tensión de 100VAC si sería accesible.

Los drivers que usaré no lo tengo muy seguro porque los pide la universidad (ya que pagan ellos) y aunque yo les de una lista de componentes ellos me darán lo más parecido que tengan disponible o que puedan conseguir a un coste aceptable, en cuanto los tenga en mi mano te diré cuales son. Por ahora solo tengo los Mosfet que son los IRFP4568. Mañana publico la etapa de potencia diseñada en Proteus, y si de casualidad mi tutor ya ha conseguido los drivers pues incluiré el circuito completo.

Y una vez mas disculpen los desfases horarios pero no tengo una gran disponibilidad hasta la proxima semana porque estoy en prácticas y con las últimas clases. Gracias por la colaboración.

Aquí dejo el código con las correcciones que han ofrecido los compañeros. Faltaría incluir un retardo al final cuya duración será los microsegundos que faltan para que la duración completa de cada vuelta de programa (incluido el retardo mencionado) sea de 32us. Solo hay que volver a incluir un retardo en ensanblador y cargarle el número de vueltas que se obtengan del cálculo.

const byte dutycycle_625 [625] = {128, 129, 130, 130, 131, 132, 133, 134, 135, 135, 136, 137, 138, 139, 139, 140, 141, 142, 143, 144, 144, 145, 146, 147, 148, 148, 149, 150, 151, 
                           152, 153, 153, 154, 155, 156, 157, 157, 158, 159, 160, 161, 162, 162, 163, 164, 165, 166, 167, 167, 168, 169, 170, 171, 171, 172, 173, 174, 175, 
                           176, 176, 177, 178, 179, 180, 180, 181, 182, 183, 184, 185, 185, 186, 187, 188, 189, 189, 190, 191, 192, 193, 194, 194, 195, 196, 197, 198, 198, 
                           199, 200, 201, 202, 203, 203, 204, 205, 206, 207, 207, 208, 209, 210, 211, 212, 212, 213, 214, 215, 216, 216, 217, 218, 219, 220, 221, 221, 222, 
                           223, 224, 225, 225, 226, 227, 228, 229, 230, 230, 231, 232, 233, 234, 234, 235, 236, 237, 238, 239, 239, 240, 241, 242, 243, 244, 244, 245, 246, 
                           247, 248, 248, 249, 250, 251, 252, 253, 253, 254, 255, 255, 255, 254, 253, 253, 252, 251, 250, 249, 248, 248, 247, 246, 245, 244, 244, 243, 242, 
                           241, 240, 239, 239, 238, 237, 236, 235, 234, 234, 233, 232, 231, 230, 230, 229, 228, 227, 226, 225, 225, 224, 223, 222, 221, 221, 220, 219, 218, 
                           217, 216, 216, 215, 214, 213, 212, 212, 211, 210, 209, 208, 207, 207, 206, 205, 204, 203, 203, 202, 201, 200, 199, 198, 198, 197, 196, 195, 194, 
                           194, 193, 192, 191, 190, 189, 189, 188, 187, 186, 185, 185, 184, 183, 182, 181, 180, 180, 179, 178, 177, 176, 176, 175, 174, 173, 172, 171, 171, 
                           170, 169, 168, 167, 167, 166, 165, 164, 163, 162, 162, 161, 160, 159, 158, 157, 157, 156, 155, 154, 153, 153, 152, 151, 150, 149, 148, 148, 147, 
                           146, 145, 144, 144, 143, 142, 141, 140, 139, 139, 138, 137, 136, 135, 135, 134, 133, 132, 131, 130, 130, 129, 128, 127, 126, 126, 125, 124, 123, 
                           122, 121, 121, 120, 119, 118, 117, 117, 116, 115, 114, 113, 112, 112, 111, 110, 109, 108, 108, 107, 106, 105, 104, 103, 103, 102, 101, 100, 99, 
                            99, 98, 97, 96, 95, 94, 94, 93, 92, 91, 90, 89, 89, 88, 87, 86, 85, 85, 84, 83, 82, 81, 80, 80, 79, 78, 77, 76, 76, 75, 74, 73, 72, 71, 71, 70, 
                            69, 68, 67, 67, 66, 65, 64, 63, 62, 62, 61, 60, 59, 58, 58, 57, 56, 55, 54, 53, 53, 52, 51, 50, 49, 49, 48, 47, 46, 45, 44, 44, 43, 42, 41, 40, 
                            40, 39, 38, 37, 36, 35, 35, 34, 33, 32, 31, 31, 30, 29, 28, 27, 26, 26, 25, 24, 23, 22, 22, 21, 20, 19, 18, 17, 17, 16, 15, 14, 13, 12, 12, 11, 
                            10, 9, 8, 8, 7, 6, 5, 4, 3, 3, 2, 1, 1, 1, 2, 3, 3, 4, 5, 6, 7, 8, 8, 9, 10, 11, 12, 12, 13, 14, 15, 16, 17, 17, 18, 19, 20, 21, 22, 22, 23, 24, 
                            25, 26, 26, 27, 28, 29, 30, 31, 31, 32, 33, 34, 35, 35, 36, 37, 38, 39, 40, 40, 41, 42, 43, 44, 44, 45, 46, 47, 48, 49, 49, 50, 51, 52, 53, 53, 
                            54, 55, 56, 57, 58, 58, 59, 60, 61, 62, 62, 63, 64, 65, 66, 67, 67, 68, 69, 70, 71, 71, 72, 73, 74, 75, 76, 76, 77, 78, 79, 80, 80, 81, 82, 83, 
                            84, 85, 85, 86, 87, 88, 89, 89, 90, 91, 92, 93, 94, 94, 95, 96, 97, 98, 99, 99, 100, 101, 102, 103, 103, 104, 105, 106, 107, 108, 108, 109, 110, 
                           111, 112, 112, 113, 114, 115, 116, 117, 117, 118, 119, 120, 121, 121, 122, 123, 124, 125, 126, 126, 127, 128};

           
int puntero_sin0   = 157;    // Usada para moverse por los valores de la tabla. Su máximo será 625. Representa la señal senoidal sin desfase. Apunta a DC = 255.
int puntero_sin120 = 547;    // Usada para moverse por los valores de la tabla. Su máximo será 625. Representa la señal senoidal con desfase = 120. Apunta a DC = 64.
int puntero_sin240 = 391;    // Usada para moverse por los valores de la tabla. Su máximo será 625. Representa la señal senoidal con desfase = 240. Apunta a DC = 64.

  /* Ahora se llevará acabo la configuración de los timers de la placa arduino, ya que se requiere la modificación de estos
     para la correcta generación de las señales PWM. Los registros de control serán modificados mediante hexadecimal. */

void setup() {

  // Configuramos los pines de PWM como salidas

  pinMode (3, OUTPUT);  // Conectado a transistor Q6. Par transistores para senoidal con fase 240 
  pinMode (5, OUTPUT);  // Conectado a transistor Q2. Par transistores para senoidal con fase 0
  pinMode (6, OUTPUT);  // Conectado a transistor Q1. Par transistores para senoidal con fase 0
  pinMode (9, OUTPUT);  // Conectado a transistor Q3. Par transistores para senoidal con fase 120
  pinMode (10, OUTPUT); // Conectado a transistor Q4. Par transistores para senoidal con fase 120
  pinMode (11, OUTPUT); // Conectado a transistor Q5. Par transistores para senoidal con fase 240
  
  // Configuramos los timers mediante los registros de control.
  
  TCCR0A = 0xA1; // Habilitamos las dos salidas en modo Phase-correct - 8 bits. Pre-escaler = 1. Salidas no invertidas.
  TCCR0B = 0xC1; 
  TCCR1A = 0xA1; // Habilitamos las dos salidas en modo Phase-correct - 8 bits. Pre-escaler = 1. Salidas no invertidas.
  TCCR1B = 0xC1;
  TCCR2A = 0xA1; // Habilitamos las dos salidas en modo Phase-correct - 8 bits. Pre-escaler = 1. Salidas no invertidas.
  TCCR2B = 0xC1; 
}

void loop() {

  // Programa principal, se usarán directamente los registros de comparación porque es más rápido que usar las funciones propias de arduino.
  
  OCR0A = dutycycle_625 [puntero_sin0];    // Registro de comparación para pin A del timer 0. Genera la PWM para Q1.

  /* Este es un retardo de 4.875 us utilizado para crear un tiempo muerto entre pares de transistores y evitar cortocircuitos en los mismos.
     Demostración: (1+1+(1+2)*25+1)/16000000 = 4.875 us*/

  asm volatile ( 
        "clr r16 \n"       // 1 ciclo de reloj.
        "ldi r16, 0x14 \n" // 1 ciclo de reloj. 26 vueltas cargadas.
        "1: dec r16 \n"    // 1 ciclo de reloj
        "brne 1b  \n"      // 2 ciclos de reloj para retornar y 1 para salir 
      ); 
      
   OCR0B = dutycycle_625 [puntero_sin0];    // Registro de comparación para pin B del timer 0. Genera la PWM para Q2.
   OCR1A = dutycycle_625 [puntero_sin120];  // Registro de comparación para pin A del timer 1. Genera la PWM para Q3.
  
  // Retardo 4.875 us.
  
   asm volatile ( 
      "clr r16 \n"       // 1 ciclo de reloj.
      "ldi r16, 0x14 \n" // 1 ciclo de reloj. 26 vueltas cargadas.
      "1: dec r16 \n"    // 1 ciclo de reloj
      "brne 1b  \n"      // 2 ciclos de reloj para retornar y 1 para salir 
    ); 
    
   OCR1B = dutycycle_625 [puntero_sin120];  // Pin B del timer 1. Genera la PWM para Q4
   OCR2A = dutycycle_625 [puntero_sin240];  // Pin A del timer 2. Genera la PWM para Q5
   
   // Retardo 4.875 us.
    
   asm volatile ( 
      "clr r16 \n"       // 1 ciclo de reloj
      "ldi r16, 0x14 \n" // 1 ciclo de reloj. 26 vueltas cargadas.
      "1: dec r16 \n"    // 1 ciclo de reloj
      "brne 1b  \n"      // 2 ciclos de reloj para retornar y 1 para salir 
    ); 
    
    OCR2B = dutycycle_625 [puntero_sin240];  // Registro de comparación para pin B del timer 2. Genera la PWM para Q6
  
  /////-------------------Falta el retardo de 32 us---------------------------------------/////

  // Ahora establecemos las condiciones para moverse por la tabla de valores y volver al comienzo una vez completado un período de señal.
  
    if ( puntero_sin0++ < 625 )   { puntero_sin0++; }   else { puntero_sin0 = 0; }   // Si no se ha llegado al final de la tabla, avanza. Si se ha llegado, vuelve a 0.
    if ( puntero_sin120++ < 625 ) { puntero_sin120++; } else { puntero_sin120 = 0; } // Si no se ha llegado al final de la tabla, avanza. Si se ha llegado, vuelve a 0.
    if ( puntero_sin240++ < 625 ) { puntero_sin240++; } else { puntero_sin240 = 0; } // Si no se ha llegado al final de la tabla, avanza. Si se ha llegado, vuelve a 0.
     
}

Los IR2101 son bastante apropiados pero de ahi surge una familia de drivers mosfet ideales para manejar HV, en realidad los usas desde menos tensión tambien.
La selección de tu tabla que criterio siguió? Evaluaste los armónicos?
Hay estudios muy buenos al respecto que permiten generar supuestamente senoides mágicas como las llamas con casi pocos armónicos.
Claro que basta con cambiar la secuencia de duty cycles.

Es un tema muy apasionante. Yo me quedé en el intento de hacerlo porque el proyecto se pinchó, pero siempre pica la curiosidad.

Os paso el resultado con el analizador. Mejor, pero diría que todavía se ve alguna cosa rara.

Trifasico-2.zip (573 KB)

Hola.
Sinceramente no tengo nada claro cómo es el timing de tu programa. Sólo por no partir de ideas incorrectas ¿Entiendo que vas a efectuar un sólo ciclo pwm por cada elemento de la tabla?
Si es así, ¿Estás intentando que cada loop de tu programa dure el tiempo justo para cambiar el duty en el momento exacto? Ten en cuenta que tus cálculos de números de ciclos se pueden ver alterados por las interrupciones.
Por otro lado, si no me equivoco, los incrementos del final del programa:

if ( puntero_sin0++ < 625 )   { puntero_sin0++; }   else { puntero_sin0 = 0; }

¿No estás incrementando dos veces (una en el if y otra en la sentencia) el puntero_sin?
Tal vez sea más correcto

if ( puntero_sin0++ == 625 ) { puntero_sin0 = 0; }

O bien (me has dado la idea :slight_smile: )

if ( puntero_sin0 < 624 )   { puntero_sin0++; }   else { puntero_sin0 = 0; }

Buenas a todos, ya estoy de vuelta por aaqui.

Veamos, primero les adjunto el archivo del circuito de potencia para Proteus. La carga que he creado ha sido una carga aleatoria con la que haré las pruebas ya que como la carga variará al antojo de los futuros estudiantes pues mejor hacer una prueba con una carga random.

Respondiendo a superbyte: los drivers que usaré son los IRS2101. No sé si son los que tu me comentastes porque o a ti se falto una S o a mi tutor le sobro una S xD jaja no estoy seguro porque he buscado el datasheet de IRS2101 y he encontrado el componente :S.

El criterio de que usé para escoger los duty cycle ha sido: "a pelo" basicamente, al principio intente mediante la ecuación de una senoide de amplitud 1, obtener los 625 valores que me daba y luego multiplicarlos por 255 durante el semiciclo positivo y hacer una especie de conversión para el semiciclo negativo. Es decir, si se dan cuenta durante el semiciclo positivo de la senoidal de salida los valores del duty cycle deberán ir desde 128 hasta 255 (siendo este el valor pico) y en el semiciclo negativo deberán ir desde 128 hasta el valor 1 (siendo este el valor pico para este semiciclo) descartanto el valor 0 ya qu eno es de utilidad. Por tanto se me complicaba un poco el cálculo de los valores.

Por tanto opte por el siguiente metodo, usando una hoja exel hice lo siguiente: 31250Hz / 50Hz = 625 valores. 625 valores / 4 tramos = 156.25 valores por tramo (cada tramo consiste en dividir un ciclo de la senoidal entre 4, haciendo diferencia entre pendiente positivas y negativas). Como cada tramo debe tener unos 128 valores (1º tramo va de 128 - 255; 2º va de 255 - 128; 3º va de 128 - 1; 4º va de 1 - 128; y repetimos ) entonces divido 128 / 156.25 = 0.8192 aumento/pulso de reloj.

Es decir, que cada vez que quiera modificar el duty cycle me deberá aumentar 0.8192. Luego metí este incremento en una tabla exel y elimine los decimales (redondeando claro) y obtuve la secuencia de numeros.

Como pueden ver no he tenido en cuenta la cantidad de armónicos que me generarán las PWM pero me dijo el tutor que no tuviera en cuenta esto por ahora, que si hay tiempo lo filtaríamos y si no pues nada. Ya que hay poco tiempo para realizar el proyecto.

Espero que se me haya entendido, si no es así buscaré la forma de explicarme mejor. Esto no quiere decir, que me de el venazo y vuelva a intentar calcular los valores haciendo uso de la ecuación de la senoidal pero la verdad que es una lavor tediosa ya que un semiciclo se mueve entre unos valores y otro entre otros valores.

Respondiendo a noter:

Cierto, voy a efectuar un solo ciclo de PWM por cada elemento de la tabla. Esto como mínimo, si veo que luego necesito más tiempo de ejecución pues reduciría los valores de la tabla y aumentaría el tiempo de modificación: 64 us, 96us, etc. Generando de la forma más sencilla posible los 50 Hz a la salida.

El puntero no se incrementa dos veces (corregidme si me equivoco), ya que en el if() es la condición en sí y en las llaves {} es la acción que incrementa. En la condición solo se "mira" que el siguiente valor no sea 625, y en caso de que no lo sea, pues lo incrementas.

Y la condición debe ser 625 y no 624. Por lo que veo sabes que los arrays van desde el valor 0 al valor n-1 (siendo "n" la cantidad de números en el interior del array. Pero en mi condición en particular quiero que el duty cycle llege hasta el valor de array 624, ya que en esa posición también hay un valor de duty que debe efectuarse.

Bueno, desde mañana hasta la proxima semana no estaré en casa por un viaje. Por lo que tardaré en dar señales de vida en unos días. Quiero daros las gracias porque las dudas que ustedes plantean me hacen pensar, mejorando así mi proyecto y los consejos que me aportan me nutren de mucha información. Muchas gracias.

La proxima semana ya colgaré los resultados obtenidos y las dificultades que posiblemente se me planteen, si les surge alguna duda más estaré encantado de responderles.

Un saludo y que tengan un buen finde. :smiley:

Circuito_inversor.zip (31.9 KB)

Bueno, abhsn; creo que no conoces una de las "frases del argot C". Como yo también he metido la pata, te explico tu error y mi error.

En C, cuando ponemos variable++ o ++variable, equivale a decir variable=variable+1, aunque lo pongamos en una condición. Por ello la línea tuya que cité va a incrementar dos veces en cada iteración.

Ahora mi error:
Cuando ponemos en una condición variable++, primero se verificará la condición y después se incrementará la variable; sin embargo si ponemos ++variable, primero se incrementará la variable y después se evaluará la condición, así que mi línea debía decir:

if ( ++puntero_sin0 == 625 ) { puntero_sin0 = 0; }

Mi segunda solución creo que sí era correcta, aunque rara:

if ( puntero_sin0 < 624 )   { puntero_sin0++; }   else { puntero_sin0 = 0; }

Ya que cuando puntero_sin0 sea igual a 624, debe pasar a cero, en lugar de incrementar a 625.

buenas noches antes que nada muy buen post, yo estoy comenzando a realizar un proyecto similar y desconosco de mucho ya que soy de Ing. Eléctrica, pero note que estas considerando tus tres timers como si fueran de 31250Hz, y tengo entendido que el timer 0 tiene una frecuencia de 62500Hz, y los dos restantes si tienen una frecuencia de 31250 Hz. mas adelante espero me puedan solucionar algunas dudas.
Gracias.

Mas adelante lo dudo porque salvo noter y yo el autor no responde mas desde 2015.
No viste la advertencia de que el hilo tenia mas de 120 días sin movimiento?
Te invito a:
1. Lee las normas del foro a conciencia
2. Crea un nuevo hilo y has una referencia a este para tomarlo como base.
3. No vuelvas a responder en hilos viejos de mas de 4 meses sin cambios. Si tienes la advertencia en letras rojas simplemente crea un nuevo hilo.