lunes, 4 de abril de 2011

Protel DXP (Altium), Scripting y Mecanizado de PCBs.



La implantación definitiva de componentes SMD, a supuesto una revolución en cuanto a la miniaturización de la electrónica, no obstante, los nuevos encapsulados utilizados han traido una nueva problematica. Lograr con técnicas actuales de prototipado la precisión suficiente como para hacer las pistas y los pad en los circuitos impresos.

En ésta línea de trabajo, la empresa germana LKPF ha desarrollado unos routers que mecanizan PCB con la presición suficiente como para lograr pads de 0,1mm.

El problema es que el precio de éstas máquinas las hace inaccesibles para aficionados.

La solución es hacerse por internet con un router de 3 ejes gobernado por motores PAP una interfaz y el programa MATCH3.





El problema hasta el momento ha sido encontrar un software adecuado para la generación de código de control numérico:

  • El software utilizado por LKPF evidentemende es propio y no genera código alguno estandard compatible con routers normales (Lo cual es normal, no van a vivir del aire).
  • Diversos softwares semiprofesionales como Copper CAM, tienen un precio moderado pero en su momento no permitían aislar zonas del PCB, muy importante cuando se trabaja con mucha tensión.
  • Los demás softwares valorados, son versiones beta y la verdad es que no están muy depurados.


En ésta entrada del blog os presento el software que desarrollé para generar desde el editor de PCB de Protel DXP (Altium) programas de control numerico (GCODE) para mecanizar PCB, en pequeñas máquinas de control numérico.

La plataforma Portel DXP es muchísimo más potente de lo que en un principio puede parecer, e incorpora su propio lenguaje de script através del cual, se pueden automatizar infinidad de tareas.


El script presentado incorpora las siguientes novedades:
  • Generación de código de control numérico estandard (GCODE).
  • Generación de marcas de centrado.
  • Optimización de movimientos para taladrado.
  • Optimización de movimientos de fresado.
  • Posibilidad de selección de diferentes herramientas para cada trabajo.
  • Vaciado de zonas de cobre muerto.
  • Ejecución desde Mach3 o EMC.
Para cargar la aplicación desarrollada en la plataforma procederemos del siguiente modo:


En primer lugar haremos click en el menú desplegable de altium DXP y seleccionaremos la opción Run. 


Tras lo cual se abrirá el menú con los scripts que tenemos instalados.
Para instalar el script pulsamos el botón "Browse".


Finalme buscamos el directorio donde hemos copiado los archivos de script y cargamos el script "GCode_Exporter.pas".

Una vez cargado ya podemos ejecutarlo haciendo doble click sobre el icono.

Al ejecutarlo se nos abrirá una ventana con las diversas opciones de configuración:


En la ventana de confguración podemos observar diferentes grupos de opciones:
  • Milling 
  1. Cutter Diameter: Diametro de la herramienta de corte.
  2. Cutter Depth: Profundidad de la herramienta de corte
  3. XY-Feedrate: Velocidad de desplazamiento de la herramienta de corte.
  4. Mill RPM: Revoluciones de la fresa.
  •  End Mill


  1. Cutter Diameter: Diametro de la herramienta de corte.
  2. Cutter Depth: Profundidad de la herramienta de corte
  3. XY-Feedrate: Velocidad de desplazamiento de la herramienta de corte.
  4. End Mill RPM: Revoluciones de la fresa.
  •  Drilling
  1. Drill speed: Revoluciones de la fresa.
  2. Drill Z-Feedrate: Velocidad de bajada de la broca.
  3. Drill deepth: Profudidad del taladro.


Código fuente de la aplicación:


domingo, 3 de abril de 2011

Contador de marcha engranada para motocicleta.

Las motos de carretera tienen motores muy lineales con régimenes que pueden abarcar de las 4000RPM hasta las 20.000RPM.

Un margen tan amplio de revoluciones condiciona cajas de cambios con desarrollos muy similares, por lo cual, en un viaje largo es fácil olvidarse y no saber que marcha tenemos engranada.

Debido a ésto, en su momento proliferaron unos displays que indicaban la marcha engranda midiendo los desarrollos mediante las revoluciones del motor y del piñón de ataque.



En la entrada del blog de hoy, os presento el diseño que realicé hace ya tres años y que tuve instalado durante en varios años en mi moto.


 Dviersas pruebas de software en casa.

Verificando el conjunto sobre la motocicleta.

Hardware
El diseño del hardware es simple, meditante dos etapas inversoras realizadas con transistores mosfet las señales del motor son adaptadas a los niveles de tensión del microcontrolador, evitando la llegada de glitch de alta frecuencia, provinientes del circuito de encendido del motor y que afectan fuertemente al micro.
La protección de las puertas de los mosfet se realiza mediante una red RC y diodos.

La etapa de alimentación está formada por un simple regulador 7805 smd y condensadores.

Un display de 7-segmentos es conectado al micro mediante su puerta b y resistencias de polarización.

El desarrolló del pcb lo realicé mediante la plataforma Orcad.


Software
La programación la realicé en C utilizando el compilador CCS.

El programa está basado en una interrupcion por flanco anscendente y un contador de pulsos configurado mediante el Timer0.

Por cada flanco ascendente el programa cuenta las vueltas del motor, calculando el desarrollo.

Comparando el desarrollo con los valores programados en la memoria flash interna calcula la marcha engranada representandola en el display de 7 segmentos.


Código fuente.

/*
Indicador de marchas digital con luces de cambio.
Fco. Javier Ruiz Vidorreta                                                 V1.0.

*/
#include "Indicador.h"

// Constantes
#define Numero_de_RPM      30  // Numero de vueltas del motor a contar.
#define Veces_a_Verificar       2   // Nº de veces que queremos que se verifique el dato antes de grabar.
#define Marchas                      6   // Nº de marchas a programar.
#define CATODO_COMUN  1   // Tipo de display poner a 0 si es anodo común. 

// Conexionado.
#byte port_b=6                            // Dirección del puerto B (donde está conectado el display).

// ->Entradas
#define V_Encoder PIN_A4        // Cable del encoder de velocidad.
#define RPM_Encoder PIN_B0   // Cable del encoder de revoluciones.
#define Neutral   PIN_A0            // Cable del punto muerto.
#define User_Button  PIN_A5     // Pulsador del usuario.
#define Programar_Bit PIN_A6   // Jumper de programación.

// ->Salidas
#define LED_RPM_MAX PIN_A1                        // Led de RPM máximas.
#define LED_CAMBIO_PROHIBIDO PIN_A2    // Led no bajar de marcha.
#define Punto_Display   PIN_A3                            // Led busy.
#define Depurador_bugs  (if(input(Punto_Display)){output_low(Punto_Display);}else{output_high(Punto_Display);};)

#use rs232(baud=9600, xmit=User_button,rcv=Neutral)   // Pines de envio y recepción para la reprogramación de codigo.

//Mapa con los led del display.
#if CATODO_COMUN
   byte CONST LED_MAP[12] = {0x81,0xE6,0x48,0x42,0x26,0x12,0x10,0x86,0x00,0x02,0x7E,0x0C};
#elif
   byte CONST LED_MAP[12] = {0x7E,0x18,0x6D,0x3D,0x1B,0x37,0x77,0x1E,0x7F,0x3F,0x01,0x4F};
#endif

// Variables globales.
int Vueltas_Del_Motor=0, Marcha=1, Veces_Verificado=0, RPM=0;
int Delay_Blinking=0;
int16 Periodo_RPM, Periodo_MIN;
int1 Contando_Vueltas_Del_Motor=0; 
int1 Blinking=1;                         // Bit de parpadeo.
int1 Programado=0;                  // Se setea si está pogramado.

// Funciones usadas.
void programar(int relacion_de_cambio);

/* Por cada vuelta del motor salta la interrupción
     -Mide el tiempo que ha tardado para sacar las RPM, ilumina los led de cambio.
     -Compara las vueltas y los pulsos del piñon para sacar el desarrollo.    
     */

#INT_EXT
void int_ext_isr(){
      int   Pulsos_Velocimetro,i;
      int16 Periodo_anterior;
     
      // Leemos el valor del contador de tiempo para sacar la velocidad del motor.
      Periodo_RPM=get_timer1();
      set_timer1(0);                         // Lo ponemos a 0 para sacar la siguiente cuenta.
     
      // Sincronizamos las dos señales esperando a que los flancos cuincidan.
      if(!input(V_Encoder)&&(Contando_Vueltas_del_motor==0)){  
         delay_us(200);
         if(input(V_Encoder)){       // Si cuinciden,
            Contando_Vueltas_del_Motor=1; //  comenzamos a contar los pulsos.
            Vueltas_del_motor=0;
            set_timer0(0);
            output_low(Punto_Display); //            
         }
      }
     
      if(Contando_Vueltas_del_motor){     
         if(Vueltas_del_motor==Numero_de_RPM){ // Verificamos que el motor ha dado las vueltas necesarias,                    
            Pulsos_Velocimetro=get_timer0();    // Pillamos la cuenta de pulsos del velocimetro.           
            if(Programado){
               for(i=1;i<=Marchas;i++){
                  if(Pulsos_Velocimetro==read_eeprom(i)){
                    Marcha=i;                     
                  }
               }
            }else{
               Programar(Pulsos_Velocimetro);
            }
              
            Vueltas_del_motor=0;                  // Ponemos a 0 la cuenta.
            Contando_Vueltas_del_motor=0; // Dejamos de medir.
            output_high(Punto_Display);         // Indicamos que ya hemnos dejado de medir.
           
         }else{                                             // Si no se han sucedido los pulsos necesarios.
            Vueltas_del_motor++;                 //  seguimos contando.
         }
      }else{   // Si no estamos calculando el desarrollo calculamos las luces.
           
         if(Periodo_Min>=Periodo_RPM){  // Si el motor va más chuscao de lo que queremos.
            output_low(LED_RPM_MAX);   //  encendemos el led.
         }else{
            output_high(LED_RPM_MAX);  //   si no, lo apagamos.
         };
     
         output_high(LED_CAMBIO_PROHIBIDO);
         if(Marcha>1){
            // Calculamos las revoluciones que habría en la marcha anterior através del desarrollo.
            Periodo_anterior=(Periodo_RPM/read_eeprom(marcha))*read_eeprom(marcha-1);
            // Iluminamos el led si al descender de marcha superaramos las RPM programadas.
            if(Periodo_anterior<Periodo_Min){
               output_low(LED_CAMBIO_PROHIBIDO);
            };
         }
      }
}


/* Brillo del display y bits de intermitencia.
*/
#INT_TIMER2
void int_tmr2(){
     
      // Intermitencia
      if(delay_blinking==10){        
         delay_blinking=0;
         Blinking=!Blinking;  // Intermitencia.
      }else{
         delay_blinking++;
      };          
}


/* Función para la programación del desarrollo en el dispositivo. */

void Programar(int Pulsos_Velocimetro){
      // Verificamos la relación de desarrollo.
      if((Pulsos_Velocimetro>(read_eeprom(Marcha-1)))&&(Pulsos_Velocimetro>0)){
         if((Veces_Verificado==Veces_a_Verificar)){             
            write_eeprom(Marcha,Pulsos_Velocimetro);  // La programamos,
            port_b=LED_MAP[Marcha];                   //  y la mostramos durante
            delay_ms(4000);                           //  4 segundos.
            Marcha++;
            Veces_Verificado=0;
            if(Marcha>Marchas){
               write_eeprom(0,0x55);             
               Programado=1;
               Marcha=Marchas;
            }
         }else{
            Veces_Verificado++;
         }     
      }else{
         Veces_Verificado=0;
      };
}


/*Función para que el usuario establezca las luces de cambio.*/

void Establece_Luz_De_Cambio(){
      int   Numero, i;
           
      RPM++;   // Incrementamos las RPM
      IF(RPM>19)
         RPM=0;
      // Las visualizamos parpadeando en el display.
      if(RPM>9){
         Numero=RPM-10;
         output_low(Punto_Display);
      }else{
         Numero=RPM;
         output_high(Punto_Display);
      };
      for(i=1;i<10;i++){
         delay_ms(100);
         port_b=LED_MAP[Numero];
         delay_ms(100);
         #if CATODO_COMUN
            port_b=0xFF;
         #elif
            port_b=0x00;
         #endif                
      };
     
      // Calculamos el periodo seleccionado.
      Periodo_Min=7500/RPM; // 7500=(60/(8*4))*(CLK/1000)
      // Lo guardamos en la eeprom.
      i=(&Periodo_MIN);          //  ->Ésto es pa guardar un int16
      write_eeprom(11,*i);       //     en dos posiciones de la eeprom.    
      write_eeprom(12,*(i+1));
}


void main() {
   // Variables.
   int i;
  
   // Configuracion del hardware.
   setup_timer_0(RTCC_EXT_H_TO_L|RTCC_DIV_1);
   setup_timer_1(T1_INTERNAL|T1_DIV_BY_8);
   setup_timer_2(T2_DIV_BY_16,200,16);
   setup_comparator(NC_NC_NC_NC);
   setup_vref(FALSE);
   enable_interrupts(INT_TIMER2);
   enable_interrupts(INT_EXT);
    
   // Configuración de las entradas/salidas.
   set_tris_b(0x01);    //
   set_tris_a(0xFF);    //
   // Test del display.
   for(i=0;i<=11;i++){     
      port_b=LED_MAP[i];
      delay_ms(100);
   }
  
   // Verificación para reprogramar desarrollos.
   if(!input(User_Button)){        // Si se encuentra activo el bit,
      port_b=LED_MAP[11];
      Delay_ms(5000);             //  esperamos 5 segundos para confirmar,
         if(!input(User_Button)){  //  en caso afirmativo...
            write_eeprom(0,0x00); //  Establecemos la marca de chip virgen.
         }
   }
   if((0x55==read_eeprom(0))){   
      Programado=1;
   }
  
   // Cargamos la configuración de luces de cambio.
   i=(&Periodo_MIN);        //-> Ésto es pa sacar un int16 de dos
   (*i)=read_eeprom(11);    //    posiciones de la eeprom interna.
   (*(i+1))=read_eeprom(12);
  
   // Habilitamos las interrupciones para empezar con la ejecución.  
   enable_interrupts(GLOBAL);
  
   // Bucle principal del programa.
   while(TRUE){
  
      // Si no estamos en punto muerto, mostramos la marcha.
      if(input(Neutral)){
         if(Programado==0){          
            if(Blinking){                 // Parpadeando si está sin programar.
               port_b=LED_MAP[Marcha];
            }else{
               #if CATODO_COMUN
                  port_b=0xFF;
               #elif
                  port_b=0x00;
               #endif
            };
         }else{
            port_b=LED_MAP[Marcha];       // Fija si ya está programado-
         }       
      }else{   // Si estamos en punto muerto mostramos la rayita.
         port_b=LED_MAP[10];
         delay_ms(20);
         if(!input(Neutral)){  // Revisamos que esté fija.
            Marcha=1;          //  si es así, la siguiente marcha será primera.
         };
        
         if(!input(User_button)){       // Si el usuario pulsa el boton, estando en punto muerto,
            Establece_Luz_De_Cambio(); //  pues es que quiere configurar las luces de cambio.
         };
      }           
   }

sábado, 2 de abril de 2011

Simulación En Spice De Encendido Electrónico.

Como proyecto final de carrera participé en la competición motostudent, (http://www.motostudent.com/) durante la cual, desarrollé un sistema de encendido electrónico por descarga capacitiva para un motor de 125cc.


El desarrollo de un sistema de encendido electrónico implica trabajar con un elemento claramente no lineal como es la bujía, además, el trabajar con alta tensión no nos permite realizar mediciones con un equipamiento asequible a un precio económico.


Debido a todo ésto, la única manera de poder predecir con exactitud el comportamiento de los componentes es procedeer a su simulación y comparar los datos que se extraigan con la realidad, para poder verificar que todo funcine según lo dispuesto.


En ésta entrada del blog voy a presentar la simulación que realicé para un sistema de encendido por descarga capacitiva, además voy a proponer los métodos para calcular las potencias de los componentes principales.




viernes, 1 de abril de 2011

El proposito de todo ésto.


Una gran afición personal por el mundo de la física y la tecnología, me llevaron a comenzar unos estudios universitarios.

Decidí cursar una ingeniería técnica industrial, al ser una formación muy corta y con buenas perspectivas laborales.

El propósito de éste blog es compartir conocimiento y proyectos personales, así como darme a conocer de una forma profesional.