Vistas de página en total

viernes, 27 de marzo de 2015

Ampliacion usb_cdc.h CCS

 Acabo de  necesitar utilizar la librería USB_CDC.h de CCS para comunicacion emulando un puerto serie.
  Al utilizarla me he encontrado con que le flatan funciones muy básicas. Si bién tiene funciones para interpretar los datos recogidos, no tiene funciones para devolver de la misma manera los datos al PC.
  Esto no me hubiera dejaba componer de forma coherente un sistema de comunicaciones por comandos entre el firmware y el software del PC. Así que implementé las funciones que flataban.
  Para no perderlas, pues ya empiezo a tener librerías modificadas, subo aquí las modificaciones, que al tiempo pueden que sirvan al alguien.
   Debido al copyrigth del CCS, no puedo subir las librería completa, pero no tengo problemas en subir la parte de código mío a añadir a la librería.

/////////////////////////////////////////////////////////////////////////////////////
////                                                                                    ////
//// put_float_usb                    ////
//// Send a number float by USB in  ASCII   format   ////
////  ////
////  ////
//// void put_long_usb  ////
//// Send a number long by USB in  ASCII   format    ////
////  ////
////  ////
//// void put_int_usb  ////
//// Send a number int by USB in  ASCII   format  ////
////  ////
////  ////
//// void put_string_usb(char* s)  ////
//// Send a string by USB                                             ////
////  ////
////  ////
//// VERSION HISTORY                                                ////
////  ////
//// March 27th 2015: by  José Angel Moneo Frdz  ////
////  Add functions                                                ////
//// put_float_usb, put_long_usb,   put_int_usb  ////
////    put_int_usb, put_string_usb                                    ////
///////////////////////////////////////////////////////////////////////////////////

//Envia una cadena de texto por USB
void put_string_usb(char* s) {
   unsigned int len;
   int i;
   for(i=0;i<strlen(s);i++)
        usb_cdc_putc(s[i]);
 
   usb_cdc_putc(13);
}





void put_float_usb(float dato)
{char s[20];
int i;
sprintf(s,"%03.2f",dato);
 for(i=0;i<strlen(s);i++)
         usb_cdc_putc(s[i]);
//usb_cdc_putc(10);
 usb_cdc_putc(13);
       
}


void put_long_usb(long dato)
{char s[13];
int i;
 itoa(dato,10,s);
 for(i=0;i<strlen(s);i++)
         usb_cdc_putc(s[i]);
//usb_cdc_putc(10);
 usb_cdc_putc(13);
       
}

void put_int_usb(int dato)
{char s[7];
int i;
 itoa(dato,10,s);
 for(i=0;i<strlen(s);i++)
         usb_cdc_putc(s[i]);
//usb_cdc_putc(10);
 usb_cdc_putc(13);
}        


lunes, 16 de marzo de 2015

CONTROL SERVO DE 0,5 GRADOS CON PIC (1 Timer)



INTRODUCCIÓN

  Como ya dije, voy a exponer la forma de controlar un servo, pero en este caso usando solo un timer.
   par ello usaré el Timer 1, que dispone de una mayor resolución en su contador. Al ser un contador de 16 bits podremos llegar a contar los 20ms aunque coloquemos una resolución del timer pequeña.
  

FUNCIONAMIENTO

    Colocaremos un divisor de Timer 1 de 2, por lo que la frecuencia del tic de timer quedará en 500000 Hz con la frecuencia de PIC a 4Mhz, que es la de su oscilador interno.
   
 


   TARJETA PROTOTIPO


 

PROGRAMA

   El programa no es muy complejo. Lo podemos dividir en tres secciones.
   Se ha creado una interrupción en timer 1 para generar los pulsos del servo en dos pasos. El primero realizar la espera a nivel alto y el segundo la espera a nivel bajo del ciclo de onda de mando del servo.
   Para calcular los tiempos y esperas necesarias he creado unas constantes con los calculos necesarios. Está en constantes en programa para no consumir memoria.
    Constantes importantes para control del servo:
           MAX y MIN que declaran los valores extremos de posición que vamos a permitir mover el servo. En este caso -180 y 180,pues trabajamos en medios grados. 
           PIN_SERVO que declara la salida usada para control del servo.
           RESOLUTION  que declara el número de posiciones a considerar en los 180º de recorrido del servo. En este caso indica que hay 360 posiciones, que nos darán 0,5 grados de precisión.
        SHORT_TIME, CENTER_TIME , LONG_TIME  y WAVE_TIME  definen los tiempos de control para el servo en concreto. En este caso son los de un Parallax Standard (#900-00005)



MAIN.C


/* Main.c 
 * Control Servo Tunel Viento Blanco
 * Autor José Angel Moneo Fdez
 * Created:   lunes 14 de Abril de 2015
 * Processor: PIC16F882
 * Compiler:  CCS for PIC
 * Versión 2.0

lunes, 9 de marzo de 2015

CONTROL SERVO DE 0,5 GRADOS CON PIC (2 Timer)




INTRODUCCIÓN

  A veces necesitamos hacer circuitos simples para tareas simples que antes hubieran sido muy complejos sin uC. En muchos casos para cualquier cosilla ahora vale la pena crear un pequeño circuito, pues los costes son ridículos .
   Es verdad que ahora tenemos placas de prototipado como las de Raspery o Arduino, pero yo soy de la vieja escuela. Me gusta trastear y hacer yo los circuitos.
   Por otro lado, es más divertido, si tienes tiempo, adaptar todo a tu gusto.
   Para mover muchos servos no tiene sentido realizar el circuito en un microcontrolador, ya que ya existen circuitos PWM para ello que liberan al uC. El uC es mejor usalo para controlar el proceso y no para gestionar cosillas que consumen tantos recursos y para las que ya hay otros contoladores.
    Si necesitais controalar más de un servo es mejor que useis como control de PWM un PCA9685. Con él dispondreis de 16 controladores de 12 bits, y lo podreis controlar por SPI.
   Pero si solo queremos controlar sol un servo, sinos interesa hacerlo con el PIC.

FUNCIONAMIENTO

   Hoy voy a poner un circuito para una acción muy simple. Mover un Servocontrol con PIC. Ya puse una entada para hacerlo desde Android, con Arduino a través de internet, pero en este caso voy a hacer algo más realista y más común de utilizar.
   Supongamos que tengo un elemento en el que quiero ajustar la posición.  Para hacerlo autónomo quiero utilizar un display. Para complicarme la vida, voy a usar un display gráfico. Así puedo mostrar los números mas grandes.
   Vamos a colocar dos botones luminosos para mover el servo hacia arriba o abajo, pero como tengo que posicionarlo 180 grados, sería un rollo tener que pulsar 180 veces para pasar de abajo a arriba o viceversa.
  Por ello usaré un truco, que consiste en ir aumentando los incrementos de paso a medida que pasa el tiempo. De esta forma cuando pulsemos un botón el servo irá saltando en pasos cada vez mayores. pero cuando soltemos volverá a ponerse en pasos de 1, para tener precisión. Esto no va a permitir movernos rápidamente en el intervalo y ser precisos al mismo tiempo.
  En cuanto a control del servo lo haré, en este caso, mediante dos interrupciones para ajustar de reforma precisa el pulso al servo que utilizo.
   Aquí voy a utilizar Timer 0 para generar los pulsos de 20ms y Timer 2 para los de control de posición.
   En la prosima entrada utilizaré otro programa con un solo timer.
   Además quiero que se posicione en pasos de  0,5 grados. Es decir que tenga 360 posiciones para 180 grados.
   Por último he usado pulsadores luminosos para indicar los movimiento permitidos. El pulsador se apagará cuando no se pueda realizar el movimiento en ese sentido.



ESQUEMA

   El circuito está hecho con un PIC16F882. Por qué tan pequeño... pues para hacerlo más difícil. Al tener solo 2K tendré que pensar más.
   En este caso utilizaré el 96% de la memoria. Y para conseguir introducir el proceso en este micro, tendré que prescindir de algunas librerías estándar y crearme yo mi propia función. Con cualquier otro PIC16F88x no lo hubiera necesitado y podría haberlas utilizado, pero así hemos como resolver el problema.

   El circuito es este.


    Se podrá alimentar con tensión continua de 6V a 12V.
    Lleva incorporado el conector ICSP para programarlo.
    RV2 controlará el contraste del display.
    Mediante los botones UP y DOWN, podremos mover el servo hacia arriba o abajo. Cuando estemos fuera del margen de movimiento admitido, el led correspondiente a su movimiento se apagará, indicando que no funcionará el pulsador correspondiente.
    En este caso yo he utilizado pulsadores luminosos ,para conseguir ver la validación de la pulsación sobre el propio pulsador. Esto nos permitirá limitar los movimientos por programa a unos determinados grados, y que le usuario vea que ha llegado al tope de movimiento, sin tener que recordar cual es el máximo o mínimo de este.

   TARJETA PROTOTIPO


 

PROGRAMA

   El programa no es muy complejo. Lo podemos dividir en tres secciones.
  

   CONTROL SERVO

   El servo que utilizo es un Parallax standar. Este necesita 750ms para posición 0 y 2,25ms para posición 180. 
   En esta parte devinimos como vamos a mover el servo. En este caso generaremos los pulsos mediante dos interrupciones. TIMER0 nos servirá para generar pulsos de 20ms exactos. Como no uso cristal, sino que he uso el reloj interno del uP., programamos esta interrupción con divisor de 128, preseleccionando el timer a 100.

            setup_timer_0(RTCC_INTERNAL|RTCC_DIV_128);
            set_rtcc(100);

Esto nos da una llamada cada 20ms exactos.

   Cuando se ejecute esta interrupción. Esta lanzará la interrupción TIMER2. la cual pondrá a 750us, de forma que lance el tiempo inicial de posiconamiento 0 del servo.
                    setup_timer_2(T2_DIV_BY_4,124,1); 
                    set_timer2(0);


   Cada vez que se habilite la interrupción desde TIMER0 se producirá una ejecución de Timer2 casi instantánea, exactamente a los 67us. Esta llamada es realizada inmediatamente salga el procesador de la interrupción 0. Por lo tanto no habrán transcurrido los 750us programados.
     Es por eso que para que cumpla los primeros 750us han de suceder realmente dos llamadas a Timer2. 
    Para controlar estas llamadas sucesivas, colocaré en TIMER2 un contador de secuencia, mediante el cual conoceré en que llamada está y podré decidir en consecuencia.
     El Timer 2 es inicializado al principio desde TIMER0 a 124 con divisor 4. Es menos de los 750us deseados, pero el tiempo programado unido al tiempo gastado en la primera llamada más el procesado de los cases 2 y 3 en vacío, consumirá exactamente 750ms de tiempo hasta case3. Es decir desde que suceda TIMER0 hasta que llegue al case3 de TIMER2, y por lo tanto descienda la señal del servo pasarán 750ms en caso de que la posición del servo sea 0.
     Dicho de otra manera, el TIMER2 siempre será llamado 4 veces. con un tiempo mínimo de 750us. Además después según lo que indiquemos en la variable POS_SERVO, se añadirán tiempo.

#int_RTCC
void  RTCC_isr(void)
{disable_interrupts(INT_RTCC);
 disable_interrupts(INT_TIMER2);
 set_rtcc(100);  //100 SON 50HZ- 20MS
 TIMER2=0; //programa La posición del servo mediante la longitud del pulso de Timer1
   setup_timer_2(T2_DIV_BY_4,124,1);   //1er retardo  588us que sumados al los 67uS de la llamada inicial Y A LO QUE CONSUMEN LOS CASES 2 Y 3 dan 750uS de acceso al case 3(punto 0)
  set_timer2(0);                                      //INICIALIZACIÓN DEL TIMER antes de la llamada
 enable_interrupts(INT_TIMER2);       //HABILITA INTERRUPCIÓN EL TIMER2
 output_high(PIN_SERVO);                    //PASA A ALTO LA SEÑAL DEL SERVO
enable_interrupts(INT_RTCC);     
}
#int_TIMER2
void  TIMER2_isr(void)
{
disable_interrupts(INT_TIMER2);

switch (TIMER2)
{case 0:             //PRIMERA LLAMADA HECHA DE FORMA INSTANTANEA EN 67uS AL HABILITAR LA INTERUPCIÓN
      TIMER2=1;     //LA IGNORAMOS
       enable_interrupts(INT_TIMER2);
       break;
 case 1:
       TIMER2=2;
       if(POS_SERVO<220)
           setup_timer_2(T2_DIV_BY_4,POS_SERVO,1);  
       else
           setup_timer_2(T2_DIV_BY_4,220,1);  
      
       set_timer2(0);
       enable_interrupts(INT_TIMER2);
        break;          
case 2:
       TIMER2=3;
       if(POS_SERVO<220)
           setup_timer_2(T2_DIV_BY_4,0,1);  
       else
             setup_timer_2(T2_DIV_BY_4,POS_SERVO-220,1); 
       set_timer2(0);
       enable_interrupts(INT_TIMER2);
        break;       
 case 3:   //este case es ejecutado a los 750us (punto cero) + el tiempo añadido en case 1
       TIMER2=0;
       output_low(PIN_SERVO);                    //PASA A BAJO LA SEÑAL DEL SERVO
                                                                      //en este caso no habilitamos la interrupción
       break;
}
}   

    Como se ve el case 0 es la entrada inicial nada más ser validada la interrupción por TIMER0. Case 1 es la entrada al primera parte de la temporización. Dado que vamos a temporizar 360 valores y el contador del timer solo tiene 255, tendremos que hacerlo en dos partes. 
    Seleccionamos un tiempo de 4us para el Tic del Timer2. Si la posición deseada es < de 220, usamos el case 1 para marcar el tiempo dejando el case 3 en 0.
      En caso de superar 220 la posición deseada, hacemos los 220 primeros en el case 1. El resto desde 220 a 380, cuando se necesite lo realizará el case 2.
   El case 3 será el que descienda la señal del control del servo.


   CONTROL DE PULSADORES

  Esta rutina incluida en el bucle main, es la que controla el proceso que deseamos.
  En este caso simplemente vamos a ir t4esteando los pulsadores viendo si la posición seleccionada está dentro de los margenes Min y Max. Si está dentro nos irá subiendo o bajando el valor de POS, pero de forma acelerada, mientras mantengamos pulsado el botón.

 //****************Por pulsación mantenida************
  // permite mantener pulsado el mando de forma que vaya subiendo progresivamente la 
  // escala de incremento.
  // de esta forma se podrá pasar de n·meros bajos a altos de forma rápida.
     if(POS<Max){cuenta_pulsador=0;
                        while (!input(UP))
                        { if (POS==Max) break;
                          if ((POS+cuenta_pulsador/2+1)<Max)
                                    POS+= cuenta_pulsador/2+1;
 else
                                    POS++;
                          set_servo(POS);
                          cuenta_pulsador+=10;
                          

                        }
                         }
                               
    if (POS>Min) {cuenta_pulsador=0;
                        while (!input(DOWN))
                        { if (POS==Min) break;
                          if (POS>Min+cuenta_pulsador/2+1)
                                    POS-= cuenta_pulsador/2+1;
 else
                                  POS--;

          set_servo(POS);
                           cuenta_pulsador+=10;
                          
                            }      
                            }                        



REFRESCO 

   Para mover el servo simplemente debemos asignar a una variable la nueva posición para que las interrupciones hagan su trabajo. Pero si os habéis fijado, con el tiempo de tics, son 187*2 los tics necesarios para conseguir los 1,5ms que generan los 180 grados. esto produce un desfase entre lo desado y los tics. Exactamente 1.075.
 Debido a que la memoria está tan al límite, no puedo utilizar la multiplicación con coma flotante, pues las rutina me comería la memoria, por lo que para conseguir la corrección lo haré mediante una multiplicación y una división simple. Esto me ahorra memoria de rom.

           if (pos<=360 && pos>=0) POS_SERVO=pos+pos*76/1000;   //*1.076;

 La otra parte del refresco es la del display.
 Igualmente que en el caso anterior, para mostrar el número en pantalla, necesitaré usar la función itoa(). Pero esto me obliga a incluir stdlib. Al hacerlo se me dispara la memoria rom necesaria, pues incluye funciones que no necesito. Por ello, y para ahorrar memoria, creo mi propia función itoa en el código. Esta además la adapto para colocar un decimal.
   Dado que queremos 360 posiciones en 180 grados, tendremos una resolución de 0,5 grados, por lo que debemos mostrar esta resolución en la pantalla. En vez de usar coma flotante lo que veremos es si la división por 2 de la posición es entera y si no lo es colocaremos ".5" al final de la conversión del itoa().


 CÓDIGO COMPLETO    

  Debemos tener en cuenta que hay que modificar el GLCD.c para definir los puertos y señales concretas para las conexiones que hemos hecho en el esquema.




/* Main.c
 * Autor Jose Angel Moneo Fdez
 * Created:   lunes 23 de Febrero de 2015
 * Processor: PIC16F882
 * Compiler:  CCS for PIC
 */
#include <16F882.h>
#FUSES NOWDT                 //No Watch Dog Timer
#FUSES INTRC_IO               //Internal RC Osc, no CLKOUT
#FUSES NOPUT                 //No Power Up Timer
#FUSES MCLR                   //Master Clear pin enabled
#FUSES NOPROTECT             //Code not protected from reading
#FUSES NOCPD                 //No EE protection
#FUSES NOBROWNOUT             //No brownout reset
#FUSES IESO                   //Internal External Switch Over mode enabled
#FUSES FCMEN                 //Fail-safe clock monitor enabled
#FUSES NOLVP                 //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NODEBUG               //No Debug mode for ICD
#FUSES NOWRT                 //Program memory not write protected
#FUSES BORV40                 //Brownout reset at 4.0V
#FUSES RESERVED               //Used to set the reserved FUSE bits
#use delay(clock=4000000)

#define Max 360
#define Min 0



#include "GLCD.C"
//#include <math.h>




#define UP        PIN_A3   // PULSADOR UP
#define DOWN      PIN_A2   // PULSADOR DOWN
#define LED_UP PIN_A5
#define LED_DOWN PIN_A4
#define PIN_SERVO PIN_C7
#define pin_on   output_high
#define pin_off  output_low
int16 POS_SERVO=0;   //POSICIËN SERVO
int  TIMER2=0;   //bandera TIMER2




/*       Como el reloj es interno la frecuencia son 4Mhz
Para mover el servo,vamos a usar el timer0 para marcar los tiempo de 20 ms.
Cada vez que suceda se activarß el timer2, poniendolo a 750us
Después mediante llamadas sucesivas a timer2 se irá componiendo el tiempo de mando hasta los 1,5ms
El servo lo consideramos con 360puntos de resoluci¾n. Es decir 0.5 grados.
*/

#int_RTCC
void  RTCC_isr(void)
{disable_interrupts(INT_RTCC);
 disable_interrupts(INT_TIMER2);
 set_rtcc(100);  //100 SON 50HZ- 20MS
 TIMER2=0; //programa La posici¾n del servo mediante la longitud del pulso de Timer1
   setup_timer_2(T2_DIV_BY_4,124,1);   //1er retardo  588us que sumados al los 67uS de la llamada inicial Y A LO QUE CONSUMEN MOS CASES 2 Y 3 dan 750uS de acceso al case 4(punto 0)
  set_timer2(0);     //INICIALIZACIËN DEL TIMER antes de la llamada
 enable_interrupts(INT_TIMER2);       //HABILITA INTERRUPCIónN EL TIMER2
 output_high(PIN_SERVO);               //PASA A ALTO LA SEñAL DEL SERVO
enable_interrupts(INT_RTCC);
}
#int_TIMER2
void  TIMER2_isr(void)
{
disable_interrupts(INT_TIMER2);

switch (TIMER2)
{case 0:             //PRIMERA LLAMADA HECHA DE FORMA INSTANTANEA EN 67uS AL HABILITAR LA INTERUPCIËN
      TIMER2=1;     //LA IGNORAMOS
       enable_interrupts(INT_TIMER2);
       break;
 case 1: //Este caso se ejecuta a los 750uS de que RTCC habilite la interrupci¾n y consume 80uS
       TIMER2=2;
       if(POS_SERVO<220)
           setup_timer_2(T2_DIV_BY_4,POS_SERVO,1);
       else
           setup_timer_2(T2_DIV_BY_4,220,1);      
       set_timer2(0);
       enable_interrupts(INT_TIMER2);
        break;        
case 2: //Este caso se ejecuta a los 750uS de que RTCC habilite la interrupci¾n y consume 80uS
       TIMER2=3;
       if(POS_SERVO<220)
           setup_timer_2(T2_DIV_BY_4,0,1);
       else
             setup_timer_2(T2_DIV_BY_4,POS_SERVO-220,1);
       set_timer2(0);
       enable_interrupts(INT_TIMER2);
        break;      
 case 3:   //este case es ejecutado a los 750us (punto cero) + el tiempo a±adido en case 1
       TIMER2=0;
       output_low(PIN_SERVO);               //PASA A BAJO LA SEñAL DEL SERVO

       break;
}
}

/*******itoa especifico para esta aplicaci¾n
Convierte un n·mero entero en texto en texto en base, considerando un bit de decimal.
Este bit es enviado por separado para colocar el ".5"
*********************************************/
char* itoa(signed int16 num, char* str, int base,int1 decimal)
{
    int i = 0,h=0,j=0;
    char temp[10];

    if (num == 0)
    {
        str[i++] = '0';
        str[i] = '\0';
        return str;
    }

    // Si es negativo pone el signo y lo convierte en positivo
    if (num<0) { str[i++]="-";
                 num=abs(num);
}

    // Traducimos digito a digito
    while (num != 0)
    {
        int rem = num % base;
        temp[h++] = (rem > 9)? (rem-10) + 'a' : rem + '0';
        num = num/base;
    }


    for (j=h-1;(j+1)>0;j--)
         str[i++]=temp[j];
    if (decimal==0) {
         str[i++]=".";
         str[i++]="5";
         }
    str[i] = '\0'; // a±adimos el final de cadena

  return str;
}
   
// refresca los inicadores de permiso de botonera
void led_rfsh (int16 pos)
{   if(POS<Max) pin_on(LED_UP);
    else pin_off(LED_UP);
    if(POS>Min) pin_on(LED_DOWN);
    else pin_off(LED_DOWN);

}
// envÝa orden de posici¾n al servo y refresca los pulsadores y el display
void set_servo(int16 pos)
{   char Texto[10]="";
     char grado[10]="Grados";

if (pos<=360 && pos>=0) POS_SERVO=pos+pos*76/1000;   //*1.076;
//refresca posici¾n en pantalla

itoa(pos/2, Texto,10,(pos==(pos/2)*2));
   //prepara pantalla
glcd_fillScreen(OFF);
glcd_text57(30,45,grado,2,ON);
glcd_text57(10,10,Texto,4,ON);

led_rfsh(pos);
}




//INICIALIZACIONES
void Setup()
{
   setup_wdt(WDT_OFF);
    setup_timer_1(T1_DISABLED);

   //timer 0 ... 20ms
   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_128); //PREPARA TIMER 0
    set_rtcc(100);    //desde 157 tics = 20ms
    //timer 2
   setup_timer_2(T2_DIV_BY_4,175,1);   //1er retardo  750us
   enable_interrupts(INT_RTCC);
   enable_interrupts(GLOBAL);
   //comparadores
   setup_comparator(NC_NC_NC_NC);
   setup_vref(FALSE);
   set_tris_a(0b00001100);
   set_tris_C(0x00);
   set_tris_b(0x00);
   glcd_init(ON);  
   glcd_fillScreen(OFF);                        // Must initialize the LCD
 
}



void main() {
  INT16 POS=180;
  // int1 upold,downold;
   int8 cuenta_pulsador=0;  //cuenta los avances de pulsador para aumentar su velociad
   Setup();
   set_servo(POS);
  //COMIENZO DE CICLO
  while(TRUE)
  {

  //****************Por pulsacion mantenida************
  // permite mantener pulsado el mando de forma que vaya subiendo progresivamente la
  // escala de incremento.
  // de esta forma se podrß pasar de n·meros bajos a altos de forma rßpida.
     if(POS<Max){cuenta_pulsador=0;
                        while (!input(UP))
                        { if (POS==Max) break;
                          if ((POS+cuenta_pulsador/2+1)<Max)
                                    POS+= cuenta_pulsador/2+1;
 else
                                    POS++;
                          set_servo(POS);
                          cuenta_pulsador+=10;
                       
                        }
                         }
                             
    if (POS>Min) {cuenta_pulsador=0;
                        while (!input(DOWN))
                        { if (POS==Min) break;
                          if (POS>Min+cuenta_pulsador/2+1)
                                    POS-= cuenta_pulsador/2+1;
 else
                                    POS--;
set_servo(POS);
                          cuenta_pulsador+=10;
                       
                            }    
                            }                      



  }
}




VIDEO


 

lunes, 2 de marzo de 2015

Regulador PWM 40A con PIC


   En este caso voy a poner un control de potencia por PWM realizdo con PIC. Yo lo utilicé para regular los 700w de carga de una dinamo, para utilizarla como freno de una turbina, pero puede usarse para regular igualmente un motor.
    El regulador lo tuve que contruir, porque los reguladores que venden para motores en PWM están alimentados por la misma entrada que se usa para la alimentación de la carga. Esto hace que si la fuente de potencia es variable, tal como un generador,  puede que cuando la fuente esté a pocas revoluciones, o esté a plena potencia de carga, el nivel de tensión caiga y no sea suficiente para encender totalmente el MOSFET, que necesita cerca de 8 V.
   De una forma practica. Si un regulador PWM comercial, lo aplico a la salidad de una dinamo para generar un freno aplicandole carga, cuando la carga sea muy alta, la tensión de la dinamo, si es autoalimentada cae. 
   Para evitar eso hace falta que la tensión de mando sea independiente de la tensión que alimenta la carga. De esta forma aunque apliquemos mucha charga a la dinamo, o bajen sus revoluciones, y caiga su tensión, no provocaremos perdidas de tension en la tensión de mando, y nos aseguraremos de que el MOSFET conmuta de forma rápida y queda en saturación.
  Esto es muy importante, ya que la tensión de mando del MOsfet determina su nivel de conmutación, y este su resistencia interna. A su vez su resitencia interna determinará su calentamiento.
   Para mejor disipación, deberemos utilizar un MOSFET de grán capacidad de amperaje. Esto nos garantiza una resistencia interna muy baja en la unión. Por lo contrario, el nivel de tensión de mando será mayor, y por lo tanto el descrito será más pronunciado.


El mosfet lo tengo separado del circuito y colocado sobre un disipador de PC, dado que necesitaré conmutar 30A.


ESQUEMA

    El esquema es muy simple. una fuente de alimentación para el circuito, ya que lo alimento desde 24V.
    un pic 16F88 a 20Mhz y un circuito de conmutación hecho con tres trnasistores, que me asegura una conmutación rápida tanto en los pasos a 0 como a 1 del mosfet, de forma que descarga la capacidad de este lo más rápidamente posible. En este caso no he colocado diodos Scottys ya que la carga no se trataba de una inductancia.
  El led simplemente está como testigo del RUN del PIC.
  

PROGRAMA

/* Copyright (C) 2015 José Ángel Moneo Fernández
 COMPILADOR CCS
 REGULADOR PWM

 created 2 de marzo de 2015
 by José Angel Moneo
//   This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//   (at your option) any later version.

//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.

//   You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */


struct PA_pin_map {                 // estructura Puerto A
           BOOLEAN ADC;           // no usado
           BOOLEAN led;               // LED
           BOOLEAN Unused0;               // no usado
           BOOLEAN inused1;           // no usado
           BOOLEAN Unused2;        // no usado
           BOOLEAN unused3;           // no usado
           BOOLEAN unused4;               // no usado
           BOOLEAN unused5;               // no usado
        } PA;

#locate PA = getenv("sfr:PORTA") 
#define set_tris_PA(x) set_tris_a(x) 

struct PA_pin_map const normal = {1,0,0,0,0,0,0,0}; // For write mode all pins are out




void main()
{long b;
   set_tris_PA(normal);

  //Analogica
   setup_adc_ports(sAN0|VSS_VDD);
   setup_adc(ADC_CLOCK_INTERNAL);
   //serie
   setup_spi(SPI_SS_DISABLED);
   
   //timer para pulsos
   //setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);

   setup_timer_1(T1_DISABLED);
   //pwm
   setup_timer_2(T2_DIV_BY_1,127,1);
   setup_ccp1(CCP_PWM);
   set_pwm1_duty(0);
   
    //comparadores
   setup_comparator(NC_NC_NC_NC);
   setup_vref(FALSE);

   enable_interrupts(GLOBAL);




   set_adc_channel( 0 );
   delay_ms(10);
   // TODO: USER CODE!!
  PA.led=0;
   
   while (TRUE) {

       b= Read_ADC(); //lee la referencia
       set_pwm1_duty(b);   /la transmite al PWM

       delay_ms(200);
       PA.led^=1;
   }
}