Stari skener u laser cutter upgrade

Od starih skenera namjeravamo napraviti uradi-sam CNC mašinu.

Nešto slično kao ovo, ali ne baš tako, nego više kao DIY_Micro_Laser_Cutter.

Namjeravamo napraviti XY mašinu sa minimumom promjena na skenerima. Skenere bi montirali jedan na drugi, zašarafili njihove kutije pod kutem od 90°. Donji skener bi pomicao materijal a gornji alat (tj laser).

Mali plavi DVD laser mogao bi "pržiti" po foto-osjetljivoj štapanoj pločici linijama koje
se dobiju programom "visolate".
Kad se pločica razvije i stavi u kiselinu, nestaće bakra na onim mjestima koja su bila osvijetljena
laserom (kao kad bi laser bio dovoljno jak da ga odmah sprži).

Razvijanje pločica namjeravamo napraviti i klasičnim foto postupkom sa UV lampom za pedikuru ili ovako The-return-of-the-dead-flatbed-scanner.

XY mašinu radimo da bi se upoznali sa tehnologijom i napraviti nešto svoje, usput i nove stvari naučili.

Kad budemo mogli imati veliki i super-jaki laser, njegov snop ćemo optičkim vlaknom
dovesti do vozilice skenera i rezaće tvrđe i deblje stvari.

Otvorili smo skenere HP i Genius. Pomoću univerzalnog instrumenta smo našli na toj pločici kontakte od kontrolera motora, senzora graničnika, LED diode, tipaka, +5V napajanja i USB.

To isto smo našli i na HP-ovoj pločici (ali nije još uploadana slika). Oba imaju isti stepper motor driver sa TTL nivoima na +5V. O tom driveru sve piše u datasheetu L6219DS. Upravlja se sa ukupno 6 TTL linija. Motor ima 2 zavojnice. Svaka zavojnica treba 3 linije: jakost struje: I01 i I11 i smjer struje PHASE1. Isto je za drugu zavojnicu I02, I12, PHASE2. Kada su linije Ixx na +5V ili nisu spojene, pogon motora je isključen.

Slijedeća linija bez koje se ne može je signal graničnika. Kad se vozilica vraća u svoj početni položaj, jedna pločica uđe u senzor (optičku rampu) i signalizira da motor mora stati. To je veoma bitno da se mehanika ne ošteti. Na oba skenera napon je 0V kada vozilica nije došla do kraja a +5V kada dođe do kraja.

Montirali smo "muški 25-pinski crimp D-konektor" na Genius skener. Ostalo je dovoljno mjesta da vozilica ne zapinje:

flickr:6585070507
flickr:6585089919

Prekinuti su vodovi od procesora za upravljanje motorima, LED diodom i USB i stavljen je tinol na dostupne VIA s gornje i donje strane na koje bi se mogle zalemiti žice prema konektoru.

flickr:6813318685
flickr:6813319617

Ovako ćemo definirati naš "standard" spajanja 25-pinskog D-konektora. Svaki ćemo skener pokušati adaptirati ovako
zato da i ostali adaptirani skeneri imaju isti raspored pinova, da budu međusobno "kompatibilni" :-).

konektor  AVR    kabel       pločica
--------  ---    -----       --------
    1     32       1         HOME SENSOR (GRANIČNIK)
14                 2         TOOL
    2     29       3         I01
15                 4         KEY1
    3     30       5         I11
16                 6         KEY2
    4      8       7         PHASE1
17                 8         KEY3
    5     28       9         I02 
18                10         KEY4
    6     27      11         I12
19                12         KEY5
    7      9      13         PHASE2
20                14
    8     15      15         LED
21                16             
    9             17             
22                18             
   10             19         USB +5V
23                20             
   11     14      21         USB D+
24                22                
   12     13      23         USB D-
25        5,20    24         GND
   13     4,19    25         +5V

Uz dobro osvjetljenje i povećalo se flat kabel lemi, počevši od linije 1 (ona je jedina crvena, ostale su sive). Treba spojiti što više kontakata s s donje strane (izbjegavati gornju stranu) zato da žice što manje smetaju prolasku vozilice i da sama pločica ne dopušta da se pomiče žica i time oslabljuju zalemljeni spojevi.

flickr:6642865145

Prema tom rasporedu je kabel zalemljen na pločicu, sve osim tipaka i linije "USB +5V".

flickr:6642968127

Na malom 7-pinskom konektoru sve tipke su jednim krajem zajedno spojene na +5V a drugi kraj svake tipke je pojedinačno dostupan. Kada tipka nije pritisnuta, njezina linija ima 0V, a kada je pritisnuta ima +5V. Treba odlemiti diode D1 i D2 sa gornje strane i spojiti linije KEY1 i KEY2 sa otpornikom 10k i kondenzatorom 1nF paralelno (jedan iznad drugog) prema GND.

flickr:6668713021

Na protoboardu je sastavljen kontroler tipa arduino from scratch

flickr:6691604077
flickr:6655922081

Koristio se jednostavan program za atmega64M1 koji je preko PWM timera generirao 2 faze pod 90° sa kojima vozi motor na punoj snazi od 667mA u full step modu frekvencijom od 62.5Hz. Pogon motora je opisan u datasheetu L6219DS.

#include <avr/io.h>
#include <stdlib.h>
#include <stdint.h>
#include <avr/interrupt.h>

uint16_t counter = 0;
uint8_t direction = 0;
/* PSC end of cycle interrupt counts and stops motor */
ISR(PSC_EC_vect)
{
  /* blink led */   
  PIND = (1<<PD7);
  counter++;
  if(counter > 2000)
  {
    counter = 0;
    /* motor reverse done by reversing one phase */
    POCR2SA = 3000; /* phase1 ON  */
    POCR2RA = 1000; /* phase1 OFF */
    PCNF &= ~(1<<PULOCK); /* commit changes */

    direction = 1; /* going back */
  }
  /* check home position sensor if going back */
  if( ( (PIND & (1<<PD1)) != 0) && (direction != 0) )
    PCTL &= ~(1<<PRUN); /* STOP !! */
}

/* catch otherwise unhandled interrupts 
** if this is missing, a sei() instruction
** will cause pending interrupt to jump
** to reset location
*/
ISR(__vector_default)
{
}

void main()
{
  int i;

  DDRD |= (1<<PD7);
  PORTD &= ~(1<<PD7); /* LED OFF */

  /* enable PSC oscillator output stage */
  /* run PLL at 250kHz */
  PLLCSR = (1<<PLLF)|(1<<PLLE)|(1<<PLOCK);
  PCNF = (1<<PULOCK)|(0<<PMODE)|(0<<POPA)|(0<<POPB); /* one ramp on PLL 250kHz, invert A, invert B */
  POC = (1<<POEN0A)|(1<<POEN0B)|(1<<POEN1A)|(0<<POEN1B)|(1<<POEN2A)|(1<<POEN2B); /* enable outputs */

  /* set initial period (define the frequency) */
  POCR_RB = 4000; /* 0-4095 define period (1/frequency) 250kHz/4000 = 62.5Hz */

  /* I01 (167mA COIL1): MCU PD0 pin 29 -> DB25 pin 2 */
  POCR0SA = 0;     /* COIL1 167mA ON */
  POCR0RA = 4000;  /* COIL1 167mA OFF */
  /* I02 )167mA COIL2): MCU PB7 pin 28 -> DB25 pin 5 */
  POCR0SB = 0;     /* COIL2 167mA ON */

  /* I11: (500mA COIL1): MCU PC0 pin 30 -> DB25 pin 3 */
  POCR1SA = 0;    /* COIL1 500mA ON */
  POCR1RA = 4000; /* COIL1 500mA OFF */
  /* I12: (500mA COIL2): MCU PB6 pin 27 -> DB25 pin 6 */
  POCR1SB = 0;    /* COIL2 500mA ON */

  /* COIL1 PHASE1: MCU PB0 pin 8 -> DB25 pin 4 */
  POCR2SA = 1000; /* phase1 ON  */
  POCR2RA = 3000; /* phase1 OFF */
  /* COIL2 PHASE2: MCU PB1 pin 9 -> DB25 pin 7 */
  POCR2SB = 2000; /* phase2 ON (OFF at end of cycle) */

  /* PSC Interrupt Mask register */
  PIM  = (0<<PEVE2)  /* fault2 interrupt */
      |  (0<<PEVE1)  /* fault1 interrupt */
      |  (0<<PEVE0)  /* fault0 interrupt */
      |  (1<<PEOPE); /* end of cycle interrupt */

  /* limiter sensor at MCU PD1/PSCIN0 pin 32 -> DB25 pin 1 */
  DDRD &= ~(1<<PD1);

  /* enable overlay */
  PMIC0 = (1<<POVEN0);
  PMIC1 = (1<<POVEN1);
  PMIC2 = (1<<POVEN2);
  PCNF &= ~(1<<PULOCK); /* commit changes */

  sei(); /* enable interrupts */

  PCTL = (1<<PCLKSEL)|(1<<PPRE1)|(1<<PPRE0)|(1<<PCCYC)|(1<<PRUN); /* RUN !! */

  for(;;)
  {
    /* forever */
  }
}

I to radi! Vozi vozilicu naprijed i nazad, staje na graničniku i blinka LED na prednjoj ploči skenera.

To je za jedan skener motor. Za dva skenera (i 2 motora) shema bi bila recimo ovakva (i drugačiji software bi trebao biti napisan za ovo):

flickr:6691616641

Damir je napravio 25-pinski adapter za arduino uno (ovo je njegova Shema) koji imamo u labosu. Ovo je Damirov plan spajanja:

Arduino ATMega     pins / ports                 Konektor X       Konektor Y
--------------    ---------------------------  ---------------  --------------
digital 0 (RX)     2> PD0 (RXD/PCINT16)
digital 1 (TX)     3> PD1 (TXD/PCINT17)
digital 2          4> PD2 (INT0/PCINT18)         1 HOME SENSOR
digital 3 (PWM)    5> PD3 (INT1/OC28/PCINT19)   14 LASER
digital 4          6> PD4 (T0/XCK/PCINT20)      15 KEY1
digital 5 (PWM)   11> PD5 (T1/OC0B/PCINT21)      8 LED
digital 6 (PWM)   12> PD6 (AIN0/OC0A/PCINT22)                    1 HOME SENSOR
digital 7         13> PD7 (AIN1/PCINT23)                        15 KEY1
digital 8         14> PB0 (ICP1/CLKO/PCINT0)     2 I01
digital 9 (PWM)   15> PB1 (OC1A/PCINT1)          3 I11
digital 10 (PWM)  16> PB2 (SS/OC1B/PCINT2)       4 PHASE1
digital 11 (PWM)  17> PB3 (MOSI/OC2A/PCINT3)     5 I02
digital 12        18> PB4 (MISO/PCINT4)          6 I12
digital 13        19> PB5 (SCK/PCINT5)           7 PHASE2
analog in 0       23> PC0 (ADC0/PCINT8)                          2 I01
analog in 1       24> PC1 (ADC1/PCINT9)                          3 I11
analog in 2       25> PC2 (ADC2/PCINT10)                         4 PHASE1
analog in 3       26> PC3 (ADC3/PCINT11)                         5 I02
analog in 4       27> PC4 (ADC4/SDA/PCINT12)                     6 I12
analog in 5       28> PC5 (ADC5/SCL/PCINT13)                     7 PHASE2
+5V               13  +5V                      (13 +5V ?)
GND               25  GND                       25 GND          25 GND

Prema tom planu je napravio pravi adapter sa 25-pinskim ženskim konektorima (ovo je bilo pravo iznenađenje kad ga je donio :)

flickr:6697914191

Točno paše za arduino uno.

flickr:6697949041

Adruino uno ima dovljno pinova za ono osnovno, motore i graničnike te 2 tipke, 1 LED i 1 laser. Arduino mega 2560 ima dovoljno pinova za sve, i plan spajanja bi bio ovakav:

Arduino ATMega  pins     Konektor X      Konektor Y
-------------- -------  ---------------  --------------
digital   49    PL0    1 HOME SENSOR
digital   48    PL1   15 KEY1
digital   47    PL2   16 KEY2
digital   46    PL3   17 KEY3
digital   45    PL4   18 KEY4
digital   44    PL5   19 KEY5
digital   42    PL7                    1 HOME SENSOR
digital   43    PL6                   15 KEY1
digital   41    PG0                   16 KEY2
digital   40    PG1                   17 KEY3
digital   39    PG2                   18 KEY4
digital   38    PD7                   19 KEY5
digital   22    PA0    2 I01
digital   23    PA1    3 I11
digital   24    PA2    4 PHASE1
digital   25    PA3    5 I02
digital   26    PA4    6 I12
digital   27    PA5    7 PHASE2
digital   28    PA6   14 TOOL
digital   29    PA7    8 LED
digital   37    PC0                    2 I01
digital   36    PC1                    3 I11
digital   35    PC2                    4 PHASE1
digital   34    PC3                    5 I02
digital   33    PC4                    6 I12
digital   32    PC5                    7 PHASE2
digital   31    PC6                   14 TOOL
digital   30    PC7                    8 LED
GND             GND   25 GND          25 GND

Spojili smo taj naš "proizvod" na 2 skenera koje smo adaptirali:

flickr:6698079753

To radi.

Za arduino sam napisao program u C-u koji u 2 zavojnice motora pušta izmjeničnu struju u fazama pomaknutim za 90°. Software za sada ne prati graničnik i poziciju vozilice (a trebao bi). Ako ide natrag treba stati kada se na graničniku pojavi logička 1 (+5V), ako ide naprijed treba prebrojiti broj okretaja motora i stati prije nego što dođe do drugog ruba.

#include <avr/io.h>
#include <stdlib.h>
#include <stdint.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>

#include "binary.h"

enum { TCSTOP=0, TCDIV1, TCDIV8, TCDIV64, TCDIV256, TCDIV1024, TCDIV16, TCDIV32 };

/* X-direction motor, tool and LED port */
#define PORTX PORTB
#define PINX  PINB
#define DDRX  DDRB

/* X-LED port */
#define PORTLEDX PORTD
#define DDRLEDX  DDRD
#define PINLEDX  PIND
#define LEDX     (1<<PD5)

/* X-TOOL port */
#define PORTTOOLX PORTD
#define DDRTOOLX  DDRD
#define PINTOOLX  PIND
#define TOOLX     (1<<PD3)

/* Y-direction motor, tool and LED port */
#define PORTY PORTC
#define PINY  PINC
#define DDRY  DDRC

/* Y-LED port */
#define PORTLEDY PORTB
#define DDRLEDY  DDRB
#define PINLEDY  PINB
#define LEDY     (1<<PB6)

/* home position sensor port */
#define PORTH PORTD
#define PINH  PIND
#define DDRH  DDRD
#define HOMEX (1<<PD2)
#define HOMEY (1<<PD6)

/* keyboard port */
#define PORTK PORTD
#define PINK  PIND
#define DDRK  DDRD
#define KEY1 (1<<PD4)
#define KEY2 (1<<PD4)
#define KEY3 (1<<PD4)
#define KEY4 (1<<PD4)
#define KEY5 (1<<PD4)

int16_t counter[2] = {0, 0}, max_counter[2] = {10000, 15000};
int8_t  direction[2] = {1, 1};

/* step motor coil 16-step firing sequence. 
** inverted for readability -> low level activates coils 
*/
uint8_t step[] = {
/*  LTPIIPII
    EUH10H10
    DL222111  */
~B8(00100011), /* 0: max phase 1 + */
~B8(00100011), /* 1: max phase 1 + */
~B8(00001010),
~B8(00010001),
~B8(00011000), /* 4: max phase 2 + */
~B8(00011000), /* 5: max phase 2 + */
~B8(00010101),
~B8(00001110),
~B8(10000111), /* 8: max phase 1 - */
~B8(10000111), /* 9: max phase 1 - */
~B8(10101110),
~B8(10110101),
~B8(10111100), /* 12: max phase 2 - */
~B8(10111100), /* 13: max phase 2 - */
~B8(10110001),
~B8(10101010),
~B8(11000000), /* 16: motor off, led off */
};

#define STEP_OFF (sizeof(step)-1)
#define STEP_MASK (STEP_OFF-1)   

/* output motor port */
void port(uint8_t i, uint8_t value)
{
/*                  LTPIIPII
                    EUH10H10
                    DL222111  */
  uint8_t mask = B8(00000000);  

  if(i)
    PORTY = step[value] | mask;
  else
    PORTX = step[value] | mask;
}

/* check home limter */
uint8_t limit(uint8_t i)
{
  if(i)
    return (PINH & HOMEY);
  else
    return (PINH & HOMEX);
}

/* standard timer1 count up to comapere value 16-bit interrupt */
ISR(TIMER1_COMPA_vect)
{
  uint8_t i; /* i: 0->x, 1->y */

  for(i = 0; i < 2; i++)
  {
    if(direction[i])
    {
      if(direction[i] > 0)
      {
        if(counter[i] >= max_counter[i])
          direction[i] = -direction[i]; /* reverse direction) */
      }

      counter[i] += direction[i];

#if 0
      /* limiters must be working perfectly if we disable this */
      if(counter[i] < 0) /* counter negative ? */
        goto motor_off;
#endif

      if(direction[i] < 0 || counter[i] > 200) /* going back or 200 steps from home position */
      {
        if( limit(i) )  /* reached home position? */
        {
        motor_off:;
          direction[i] = 0;  /* stop motor */
          port(i, STEP_OFF);
          PORTLEDX &= ~LEDX;
          PORTTOOLX &= ~TOOLX;

        }
      }  

      if(direction[i])
        port(i, counter[i] & STEP_MASK);

    } 
  }   
}     

/* catch otherwise unhandled interrupts 
** if this is missing, a sei() instruction
** will cause pending interrupt to jump   
** to reset location
*/
ISR(__vector_default)
{
}

void main()
{

start:;
  wdt_disable();
  PORTX = step[STEP_OFF];
  DDRX = 0xFF; /* whole port is output */

  PORTLEDX |= LEDX;
  DDRLEDX |= LEDX; 

  PORTTOOLX |= TOOLX;
  DDRTOOLX |= TOOLX; 

  PORTY = step[STEP_OFF];
  DDRY = 0xFF; /* whole port is output */

  PORTLEDY |= LEDY;
  DDRLEDY |= LEDY; 

  /* activate timer1 interrupt */
  /* 16-bit timer will increment up to OCR1A and restart at 0 */
  TIMSK1 |= (1<<OCIE1A);   /* timer 0 overflow enable */

  /* timer will count up to this value */
  OCR1A = 20;

  /* disable output of timer1 pwm */
  TCCR1A = 0;

  /* timer tick: 16MHz/256 = 62.5 kHz */
  TCCR1B  = (1<<WGM12) | TCDIV256;  /* set timer1 ctc mode (count up to OCR1A) and prescaler clock/256 and start timer */

  /* enable weak pull up for inputs HOMEX and HOMEY */
  PORTH |= (HOMEX)|(HOMEY);

  /* enable weak pull up for inputs KEYn  */
  PORTK |= (KEY1)|(KEY2)|(KEY3)|(KEY4)|(KEY5);

  sei(); /* enable interrupts */

  for(;;)
  {
#if 1
    /* forever, read keyboard */
    if( (PINK & KEY1) != 0)
    {
      // PORTLEDX |= LEDX;
      wdt_enable(WDTO_500MS);
    }
    else
    {   
      // PORTLEDX &= ~(LEDX); 
    }
#endif
  }   
}

S obzirom da analogne ulaze koristimo kao digitalne izlaze za jedan motor treba u arduino IDE nešto modificirati (napraviti drugi "wiring", ili nekako preprogramirat AVR registre). A meni je bilo jednostavnije napisati program za atmega 328p direktno u C, jer omogućava potpunu kontrolu nad funkcijama pinova arduina. Program sam na linuxu skompajlirao sa avr-gcc i uploadao u arduino uno preko usb pomoću avrdude:

avrdude -p atmega328p  -P /dev/ttyACM0 -b 115200 -c arduino -y -u \
          -U flash:w:motor.flash.ihex:i

Planovi za dalje:

Uzmimo 2 lošija skenera (onaj hp sa razbijenim staklom i UMAX naprimjer) i razmislimo kako bi ih se montiralo (zašarafilo) u XY smjeru okomito jedan skener na drugog. I da se sve moguće osi okomitosti sto bolje poklope. Ne samo XY nego Z, i ravnine kako su montirani
laser ima fokus na određenoj udaljenosti, zato donji skener koji vozi materijal (a to je pločica)
treba uvijek biti jednako udaljen od lasera. To je čak bitnije nego da XY budu doista okomiti, jer se to može matematički kompenzirati a fokus ne.

I naravno da se kroz gornji probuši rupa (zapravo prorez u podu gornjeg skenera, tanak i dugačak kroz prolazi laser koji je montiran fixno negdje na vozilici)

Donji skener može imati i staklo da ne pada prašina, laser ionako prolazi kroz to staklo

treba biti oprezan, zato bolje ne radimo XY montažu sa adaptiranim geniusima
(bilo je to dosta truda) da nešto ne strgamo

Adaptirane skenere genius ćemo koristiti prilikom adaptiranja softwarea teacup ili
marlin.

Za mehanički dio prvo da ga naučimo na bezveznim skenerima (ako i bude grešaka, nema veze)

Prvo treba početi sa brainstormingom, isplanirati i onda ići raditi :)

Tekst: Emard

Content is available under Attribution-NonCommercial 3.0 Unported.

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License