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:
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.
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.
Prema tom rasporedu je kabel zalemljen na pločicu, sve osim tipaka i linije "USB +5V".
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.
Na protoboardu je sastavljen kontroler tipa arduino from scratch
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):
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 :)
Točno paše za arduino uno.
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:
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.