TODOPIC

Mecatrónica => UAV => Mensaje iniciado por: elgarbe en 30 de Enero de 2014, 20:00:29

Título: Comandando un motor BLDC a travez de un ESC y un uC
Publicado por: elgarbe en 30 de Enero de 2014, 20:00:29
Si bien el que lea estos post no se dará cuenta de la cronología de los mismo (voy a tener que hacer un índice), todos los post que voy escribiendo van siguiendo el camino más o menos lógico en el diseño de una controladora de vuelo para un UAV. Hemos visto los sensores (acelerómetro, giróscopos, magnetómetros, altímetro), estamos hablando algo de la matriz de cosenos directores para la orientacion del UAV, hemos visto como leer la señal PPM del Rx del RC para saber conocer las órdenes del usuario.
Ahora nos toca ver como hacer para controlar un motor brushless a travez de un ESC (Electronic Speed Controller) y nuestro radiocontrol.
No descarto en un futuro no muy lejano tratar de diseñar nuestros propios ESC, Texas Instruments tiene unas muestras grátis de sus integrados específicos para control de BLDC que son muy tentadores, pero por ahora no es ese el objetivo.

Bueno, como muchos sabrán, hoy por hoy la mayoría de los modelos RC (aviones, quad, hexa, etc.) que son eléctricos funcionan con motores Brushless sensorless. No vamos a entrar demasiado en la teoría de funcionamiento, pero muy básicamente el motor es de iman permanente, trifásico, similar a un Paso a Paso, pero con la necesidad de controlar el momento preciso en el que debemos aplicar pulsos a cada bobina para her girar el motor. Los BLDC suelen ser de muy altas revoluciones, por ejemplo un motor de 1000kv (no son 1000 kilo volts!!!) nos da 1000 RPM por cada volt aplicado. Si funciona con una lipo de 3s (11.1V) nos dará más de 10.000RPM!!!! Para determinar en que momento se debe cambiar de bobina bajo alimentacion tenemos los BLDC con sensores y los sensorless. Estos miden la fuerza contra electro motriz que se crea por el paso de un imán frente a una de las bobinas.... en fin, el ESC posee la electrónica adecuada para comandar al motor a partir de una señal de control en forma de PWM.
Es muy importante saber que cada ESC debe ser configurado con el equipo RC que vallamos a usar. Como vimos las señales de cada canal de control del RC es un PWM con período 20mseg y con el tiempo en on de 1mseg para el stick del RC en el extremo inferior, 1.5mseg para el stick centrado y 2mseg para el stick con máximo superior. El tema es que esto no está tan normalizado. Por lo tanto debemos decirle al ESC cual es el tiempo ON de cada posicion del Stick. Para ello el ESC tiene un modo "configuracion" que se activa al energizarlo y con la señal de Stick al máximo. El procedimiento sería, desconectar la alimentacion del ESC. Encender el Tx. Llevar el stick del acelerador al máximo y tenerlo ahí. Alimentar el ESC. Este emitirá un sonido (un pitido, 2 pitidos o un tono, de acuerdo al modelo). Cuando esto sucede el ESC ha grabado el máximo valor del PWM del Rx. Luego debemos poner el stick en la posicion mínimo. Al cabo de unos segundos el ESC emitirá otro sonido avisándonos que ha terminado la configuracion. A partir de alli ya podemos utilizar el ESC sin problemas y con "escalado" perfecto. Debido a que la configuracion se graba en Flash, no será necesario volver a configurarlo cuando encendamos denuevo el sistema.

Aquí dejo un manual de un ESC de RCTimer (http://www.sarkanyellato.hu/wp-content/uploads/2011/10/RC-Timer-10.18.30.40A-ESC-Instruction.pdf) para que vean la programacion del mismo.

Una vez que la inicializacion está completa ya solo queda el comando del motor, el cual se realiza con un simple PWM.

Bueno, por ahora solo eso, la próxima veremos como controlar el ESC con nuestro uC.

Saludos
Título: Re: Comandando un motor BLDC a travez de un ESC y un uC
Publicado por: elgarbe en 05 de Febrero de 2014, 23:14:37
Controlar un ESC es prácticamente igual a comandar un servo. La señal del PWM es la misma. Debe tener un período de 20mseg y un ON TIME de entre 1mseg y 2mseg. Para un servo, la posicion centrado es 1.5mseg. Para un ESC el 1.5 no dice nada, solo el minimo para motor parado y los valores intermedios hasta el máximo.

Mi uC posee 6 canales PWM por hardware, tiene su propio timer de 32bits y todos los registros necesarios para funcionar de forma independiente. Una vez que lo configuramos solo debemos modificar un registro y activar un latch para que el duty cicle cambie.

Vamos directo al código.
Mi pwm.h

Código: [Seleccionar]
#ifndef PWM_H_
#define PWM_H_

void pwm_init();
void pwm_set_duty(char canal, int on_time);

#endif /* PWM_H_ */

Solo dos funciones usaremos para el PWM.

mi pwm.c
Código: [Seleccionar]
#include "LPC17xx.h"
#include "pwm.h"

void pwm_init(){
LPC_SC->PCONP |= (1 << 6); // Enciendo el PWM
// Elijo la funcion PWM en el Puerto 2 pin 0
LPC_PINCON->PINSEL4 &=~0x00000FFF; // Borro el contenido de PINSEL Bit 0 a 11
LPC_PINCON->PINSEL4 |= 0x00000555;      // PINSEL4 queda con 010101010101 y habilita PWM1.1 a 6 en P2.0
// Configuro el PWM
LPC_PWM1->TCR = 2; // Reseteo el contador del PWM por las dudas
LPC_PWM1->PR = SystemCoreClock / (4 * 1000000) - 1; // CCLK /4 / prescaler (= PR +1) = 1 µs
LPC_PWM1->MCR = (1<<1);            // Configuro Reset del contador cuando MR0 alcanza el conteo
LPC_PWM1->MR0 = 20000;              // El conteo del PWM son 20000 -> 20mseg -> 50Hz
LPC_PWM1->MR1 = 1500;               // Pongo el duty del canal 1 en 1.5mseg
LPC_PWM1->MR2 = 1500;               // Pongo el duty en 0
LPC_PWM1->MR3 = 1500;               // Pongo el duty en 0
LPC_PWM1->MR4 = 1500;               // Pongo el duty en 0
LPC_PWM1->MR5 = 1500;               // Pongo el duty en 0
LPC_PWM1->MR6 = 1500;               // Pongo el duty en 0
LPC_PWM1->LER = 0x007F;        // Activo el Latch para tomar en cuenta MR0 a MR6
LPC_PWM1->PCR = 0x7E00;            // Habilito la salida del PWM1 al PWM6
LPC_PWM1->TCR = (1<<0)|(1<<3); // Habilito el Contador y el PWM para que empieze a funcionar
}

void pwm_set_duty(char canal, int on_time){
switch (canal){
case 1: LPC_PWM1->MR1 = on_time; // Actualizo el tiempo que va a estar en ON
break;
case 2: LPC_PWM1->MR2 = on_time;
break;
case 3: LPC_PWM1->MR3 = on_time;
break;
case 4: LPC_PWM1->MR4 = on_time;
break;
case 5: LPC_PWM1->MR5 = on_time;
break;
case 6: LPC_PWM1->MR6 = on_time;
break;
}
LPC_PWM1->LER = 0x7F; // Activo el Latch para tomar en cuenta MR0 a MR6
}

Bien, la funcion pwm_init lo que hace es configurar los pines del puerto como PWM, configura el prescaler para obtener un tick 1useg. Luego habilito el registro MR0 como los ticks que resetean el timer y dan la frecuencia del PWM. Luego fijo el On Time de los 6 canales en 1.5mseg. Cuando activo los latch's se graban los valores de los MR's y en el proximo reinicio se tomarán en cuenta. Finalmente habilito las salidas del PWM para que comience a funcionar.

La funcion pwm_set_duty recive como parámetros el Canal y el tiempo en ON. Lo que hace es grabar el Match Register correspondiente al canal con el On Time. Finalmente activa todos los latch's.

Mi main.c
Código: [Seleccionar]
/*
===============================================================================
 Name        : main.c
 Author      :
 Version     :
 Copyright   : Copyright (C)
 Description : main definition
===============================================================================
*/

#ifdef __USE_CMSIS
#include "LPC17xx.h"
#endif

#include <cr_section_macros.h>
#include <NXP/crp.h>
#include <math.h>
#include "PWM.h"

__CRP const unsigned int CRP_WORD = CRP_NO_CRP ;

volatile uint32_t msTicks; // counter for 1ms SysTicks

// ****************
//  SysTick_Handler - just increment SysTick counter
void SysTick_Handler(void) {
  msTicks++;
}

// ****************
// systick_delay - creates a delay of the appropriate number of Systicks (happens every 1 ms)
__INLINE static void delay_ms (uint32_t delayTicks) {
  uint32_t currentTicks;
  currentTicks = msTicks; // read current tick counter
  // Now loop until required number of ticks passes.
  while ((msTicks - currentTicks) < delayTicks);
}

int duty[16]={1000, 1125, 1250, 1375, 1500, 1625, 1750, 1875, 2000, 1875, 1750, 1625, 1500, 1375, 1250, 1125};

int main(void) {

int n=0;
//Configuro el SysTick para que interrumpa cada 1mseg
if (SysTick_Config(SystemCoreClock / 1000)) {
while (1);  // Capture error
}

pwm_init(); //Inicializo los 6 canales del PWM

while(1) {
    // write duty cycle value and set Latch-enable register
pwm_set_duty(1, duty[n]); // Cambio el duty del canal segun la matriz de duty's
pwm_set_duty(2, duty[n]);
pwm_set_duty(3, duty[n]);
pwm_set_duty(4, duty[n]);

    n++; //incremento el índice de la matriz de dutys
    if(n == 16) //si llegamos al último elemento iniciamos de 0
        n = 0;
    delay_ms(250); // Tiempo muerto entre cambio de PWM
}
    return 0 ;
}

Lo que hago es crear una matriz con valores de on time para hacer un ida y vuelta de los servos.
Luego inicializo el PWM y en el bucle infinito voy incrementando el índice y modificando el On time.

Aquí dejo un video de como funciona:


La próxima, pruebas con ESC y motor.

Saludos!