2014年12月31日 星期三

CNC Clock製作

一直很想寫一些,製作玩具或技術型的紀錄網誌,但一直都在忙著生研究進度XDD

剛好趁最近研究進度不錯,可以偷懶一下做點小玩具~
外加UEC回收場裡面堆著一大堆廢棄電腦、印表機、各種想的到,想不到的東西,學校所有報廢的東西都會丟這邊,可以自由拿取,既然有這麼豐富的資源可以利用,不然就來做一台以前一直很想做的CNC時鐘,也為未來想做的CNC鋪路。

成果影片
材料:
光碟機*2
磁碟機*1
LCD1602*1
Ardunio NANO*1
ATMEGA328P*1
SN754410*3
從光碟機拆下來的開關*5
一些木板、螺絲與螺帽
礙於一周只開一天,所以就多撿幾台光碟機以防萬一,前前後後撿了7台光碟機、2台磁碟機。
全部拆到剩下骨架,做CNC平衡度問題挺麻煩的,剛好光機上面需要的東西都有了~
在拆的過程中,意外發現了一些有趣的事,這台筆電用的光碟機,退盤的方式是用solenoid

用直流馬達控制讀頭的光碟機,礙於他的控制晶片datashhet找不到,自己做控制又花時間(想要快速開發),所以就再去回收場多撿幾台﹐確保有光碟機是用步進馬達控制的
再來就是一步一步的寫程式與測試了~
因為手邊沒有邏輯分析儀或示波器,就先用4LED代替,開始先用低頻<10Hz測試,沒問題後再慢慢調高頻率,測試馬達極限,Arduino有很方便的ADC可以用,直接設定大範圍,然後用可變電阻直接調控頻率,就可以省去反覆的測參數的時間。
步進馬達測試完成後,再加上從其他光碟機拆下來的開關,直接用熱溶膠黏上做極限開關,用作安全機制以及開機校正用,習慣上會加上LED方便debug用,以及防止一些基礎錯誤。
再來就是把整台機器架起來啦~
因為烙鐵是跟人借的,沒有借烙鐵架,索性就用光碟機的外殼架起來,剛好有鎖螺絲的缺口,烙鐵放在上面就卡住,所以很安全~
右邊的是鋸成兩半的磁碟機,做Z軸使用(/不畫)
因為測試方便所以全部都用單心線,怕在測試過程中拉扯會把焊點扯掉,所以在馬達或是極限開關把線拉出來後,都用熱熔膠黏好,這樣子在怎麼扯都只會拉到熱熔膠而以~
這是Y軸需要一個可以畫畫的平台,要一個比較平的平面,不然在畫的過程中可能會有些地方畫的到,有些地方畫不到,如果這樣子就要用到Z軸補償,軟體寫起來頗麻煩的,所以開始到處翻拆的屍體們,到底有哪個零件可以用
找老半天,終於找到就是他了!!!
筆電光碟機的無刷馬達,後面有個很好的平面~~ 說不定未來會控制無刷馬達後,他就是第四軸了XDD
因為還有些不合,所以經過各種銼刀洗禮加上熱熔膠,終於弄上去了~
Z軸上面要做點延伸臂,這樣子才能畫到整個平面
三顆馬達原本連結出來的金手指防止短路全部都用紙膠帶貼起來
完成!!!



































再來就把原本寫好的馬達控制模組,同時控制XY軸,然後程式又是一番修改
殘念的是,當時只有兩顆馬達驅動IC(SN754410),跑去秋葉原買太遠外加這時間已經關門了...,但想繼續測Z軸,想說身上還有其他顆馬達驅動IC不然就試試看,然後就出現這種奇怪的景象XDD 測了幾下才發現,邏輯是錯的這顆IC不適合這狀況,只好作罷...,寫其他東西
隔天馬上殺去秋葉原買,然後可以三軸一起控制,畫簡單的直線了~
然後先獨立測試另外燒一顆ArdunioLCD控制
完成後,開始弄寫數字、時間計數、LCD設定時間與兩個晶片協同控制
大功告成!!!
再來是焊成電路板...
因為這邊沒洗板機,只好焊洞洞板 先將電路圖草稿畫下來,記憶力不太好,常常在麵包板上面加加減減的,會忘記接腳在哪,然後把零件大略擺一下,確定需要的洞洞板大小然後裁切,裁切洞洞板只要用斜口鉗用力剪兩端下去,再來用折的即可,如果不放心用鋸子也可,速度會慢一點就是了
































LCD控制板,焊完的樣子
後完後,要先用電表量正負極有沒有短路,ICLCD電源正負極有沒有錯,這樣子至少ICLCD裝上去通電後,不會燒掉
小心駛得萬年船~~~ 然後再測功能正不正確
CNC主控制板
擺好需要的零件怕忘記XDDD
CNC主控版正面,用Arduino NANO可以簡化很多電路~ 以後程式要改,還可以直接燒錄或是拉線出來燒LCD控制板的程式
2pin插頭中塞個排阻偷空間剛剛好XDD
上面三顆LED分別是三軸的第一支接腳,可以大概看一下現在是哪一軸在動
各種亂啊...,邊焊電路還要編整理線,先焊完IC的正負極,用電表檢查一遍,全部焊完,全部在檢查一遍,一通電就正常運作  開心~~ 超級怕,一個沒注意,把Arduino NANO燒了,這樣子就要等一兩天的時間買零件了...
再來是將所有的單心線換成多蕊線,但是拔之前熱熔膠黏的地方有些麻煩,所以就直接剪掉熱熔膠之後的單心線用多蕊線延長,然後拿熱縮套管保護好,看起來就很整齊啦~


CNC時鐘主控制板電路圖大致上長這樣,SN754410(馬達驅動IC),有VCC1VCC2兩個電源,VCC1IC主要工作的電源接上ArduinoVCC即可,VCC2是提供馬達的電源,可以一樣可以不同看需求,在這邊是用外部的變壓器直接提供5V@2A,另外一端拉到Arduino NANO Vin的接腳上,這樣子電源會先經過穩壓IC才提供給Arduino晶片,因為手邊只有5V@2A的變壓器,透過穩壓IC後會有0.7V的壓降,所以提供Arduino晶片只有4.3v左右,還好ATMEGA328p的工作電壓是1.8V~5.5V,所以可以運作,只是因為用同樣的電壓提供LCD就會有點暗,LCD控制板的電路接法可以參考這篇

按鈕旁要個10k電阻接地,不然當按鈕沒有按下時,會變成空接訊號就會亂跳
程式 如果要能include header檔的話,要將整個開發資料夾放在 C:\Users\MCS51\Documents\Arduino\libraries 下面才能使用 原本要將整個控制程式寫成.cpp和.h檔的,但是試了幾下弄不起來 arduino自己本身的問題,所以要include自己寫的要另外弄 後來上網大概有查到方法,但看起來有點麻煩,所以目前就把所有東西都寫在同一個檔案裡啦,改天有時間再來改,全部程式塞在同一個檔案裡,寫起來頗累人的,也看得有點亂就是了....  主程式架構圖 先透過RS232交握訊後後, 收到幾點幾分幾秒(xx:xx:xx),然後做資料處理再開 啟對應的畫數字函數,更新的是三軸方向與位置(全域變數) timerIsr是timer中斷,透過調整中斷觸發時間改變畫圖的速度,當三軸位置與方向更新後將輸出至馬達。 因為只是做小玩具,所以極限開關的部分只用軟體端限制,放在最後輸出的地方,減少會發生不明bug的可能性XD
LCD計數器架構圖 兩個外部中斷 debounceInterrupt1是設定/開始計數的按鈕 debounceInterrupt2是選擇設定哪一位的時間 時間計數交由timer中斷處理,為了測試方便設定500ms加一秒 所以時間會跑得比較快,最後將資料透過RS232傳到主控板上
步進馬達激磁模式定義 1相激磁、2相激磁、1-2相激磁 用const陣列定義好,控制步進馬達激磁方式方便多了
const byte OneCnt[] = { 0x01, 0x02,0x04, 0x08 };
const byte TwoCnt[] = { 0x03, 0x06,0x0c, 0x09 };
const byte OneTwoCnt[] = {0x01, 0x03, 0x02, 0x06,
0x04, 0x0c, 0x08, 0x09 };
DDRB = 0x0f; 等同於pinMode設定接腳imput/output 參考  
GoHome()是每當機器啟動時,3軸會初始化回到原點 第7、8行是timer中斷初始化 第9行是機器啟動時告訴LCD控制板,可以開始傳資料了
DDRB = 0x0f; //pin 0~7
DDRC = 0x0f; //pin A0~A5
DDRD = 0x3f; //pin 8~13
GoHome();
Timer1.initialize( 5000 ); //slow 50k, med 10k, fast 5k
Timer1.attachInterrupt( timerIsr );
Serial.print( "OK" );
view raw Setup.cpp hosted with ❤ by GitHub
接收資料非常方便的函式只是速度很慢,至少目前很夠用 未來如果有速度要求的時候,就要另外寫了
StrData = Serial.readString();
StrData.toCharArray( InData, StrLenMax );
view raw readString.cpp hosted with ❤ by GitHub
這部分是要將這一步動作做完,才能執行下一個指令 不然機器會一筆畫還沒畫完,就在跑下一筆畫了 覺得這邊還可以再寫得更好一點...  目前暫時想不到怎麼改善...
while(1)
{
delay(50);
if( CNC.xOk && CNC.yOk && CNC.zOk )
{
cntTime++;
CNC.temp = cntTime;
break;
if( cntTime > 10 ) break;
if( (CNC.xSet==CNC.xPos) && (CNC.ySet==CNC.yPos) && (CNC.zSet==CNC.zPos) )
break;
}
cntTime++;
if( cntTime > 10 ) break;
CNC.temp = cntTime;
}
view raw CheckOK.cpp hosted with ❤ by GitHub
這是直接控制馬達轉到底,當碰到極限開關就會停下來,只有X軸有<<2,因為X軸馬達是從pin2開始接,所以要先位移才能控制,PORTD是直接輸出到馬達指令 
void runMotorX( int dir )
{
boolean XSW = RangeLimit( "Xaxis", dir );
PORTD = ( LimitSW( XSW, "Xaxis", runMode, dir ) ) << 2;
}
view raw runMotorX.cpp hosted with ❤ by GitHub
這是控制馬達移動距離,當到達距離CNC.xOk=true就執行下一個畫圖指令
void runMotorX( int dir, int xcoord )
{
if( (dir>0) && (CNC.xPos >= xcoord) ) CNC.xOk = true;
else if( (dir<0) && (CNC.xPos <= xcoord) ) CNC.xOk = true;
else CNC.xOk = false;
boolean XSW = RangeLimit( "Xaxis", dir );
if( (dir > 0) && (CNC.xPos < xcoord) )
{
PORTD = ( LimitSW( XSW, "Xaxis", runMode, dir ) ) << 2;
}
else if( (dir < 0) && (CNC.xPos > xcoord) )
{
PORTD = ( LimitSW( XSW, "Xaxis", runMode, dir ) ) << 2;
}
else
xMotorStop();
}
view raw runMotorXd.cpp hosted with ❤ by GitHub
這是極限開關限制,PIND是讀取接腳狀態,第五行是做mask將特定接腳狀態萃取出來
Z軸因為只有一個極限開關,所以另外一端是用計數器算出移動距離做限制
boolean RangeLimit( String StrAxis, int dir )
{
if( StrAxis == "Xaxis" )
{
int inHome = ( PIND & 0x80 ) >> XLimitHome; //read pin
int inEnd = ( PIND & 0x40 ) >> XLimitEnd;
if( (inHome == Xhome) && (inEnd != Xend) && (dir>0) )
return true;
else if( (inHome == Xhome) && (inEnd != Xend) && (dir<0) )
return false;
else if( (inHome != Xhome) && (inEnd == Xend) && (dir>0) )
return false;
else if( (inHome != Xhome) && (inEnd == Xend) && (dir<0) )
return true;
else
return true;
}
if( StrAxis == "Yaxis" )
{
int inHome = ( PINB & 0x20 ) >> YLimitHome; //read pin
int inEnd = ( PINB & 0x10 ) >> YLimitEnd;
if( (inHome == Yhome) && (inEnd != Yend) && (dir>0) )
return true;
else if( (inHome == Yhome) && (inEnd != Yend) && (dir<0) )
return false;
else if( (inHome != Yhome) && (inEnd == Yend) && (dir>0) )
return false;
else if( (inHome != Yhome) && (inEnd == Yend) && (dir<0) )
return true;
else
return true;
}
if( StrAxis == "Zaxis" )
{
int inHome = ( PINC & 0x10 ) >> ZLimitHome; //read pin
if( (inHome == Zhome) && (dir>0) )
return true;
else if( (inHome == Zhome) && (dir<0) )
return false;
else if( (CNC.zPos >= zStep) && (dir>0) )
return false;
else if( (CNC.zPos >= zStep) && (dir<0) )
return true;
else
return true;
}
}
byte LimitSW( int SW, String StrAxis, String StrMode, int dir )
{
if( SW > 0 ) return SwitchMode( StrAxis, StrMode, dir );
else return 0;
}
view raw range.cpp hosted with ❤ by GitHub
步進馬達一相激磁
byte OnePhase( String StrAxis, int dir )
{
int CntTemp = SetSwitchAxisCnt( StrAxis );
if( dir > 0 )
{
CntTemp++;
UpdataPos( StrAxis, dir );
if( CntTemp > 3 ) CntTemp = 0;
}
else
{
CntTemp--;
UpdataPos( StrAxis, dir );
if( CntTemp < 0 ) CntTemp = 3;
}
GetSwitchAxisCnt( StrAxis, CntTemp );
return OneCnt[CntTemp];
}
view raw onephase.cpp hosted with ❤ by GitHub
LCD時間計數程式 當個位數的時候要在十位數補零,不然畫圖時會缺一個位數
void timeFunc()
{
if( itime.Hour >= 24 )
{
itime.Hour = 0;
HourStr = "00";
}
if( itime.Min >= 60 )
{
itime.Min = 0;
MinStr = "00";
itime.Hour++;
if( itime.Hour < 10 )
HourStr = "0" + String( itime.Hour );
else
HourStr = String( itime.Hour );
}
if( itime.Sec >= 60 )
{
itime.Sec = 0;
SecStr = "00";
itime.Min++;
if( itime.Min < 10 )
MinStr = "0" + String( itime.Min );
else
MinStr = String( itime.Min );
}
if( itime.Sec < 10 )
SecStr = "0" + String( itime.Sec );
else
SecStr = String( itime.Sec );
itime.Sec++;
}
view raw timeFunc.cpp hosted with ❤ by GitHub
CNC Clock GitHub網址
不知不覺就寫了一大篇文章XDD

大致上是這樣啦~
未來有時間的話,再來研究線性補間之類的,做一個真正的CNC~

P.S 第一次寫這種文章,格式各種跑掉,改天再來研究要怎麼寫才對...

11 則留言:

  1. 好強
    在下有很多台光碟機
    不嫌棄的話,可以送你喔

    回覆刪除
    回覆
    1. 有燒錄器的嗎XDD
      最近才知道燒錄器裡面的雷射功率有到100mW~200mW
      可以做一些有趣的應用~

      刪除
  2. 版主您好!我不是本科生,在偶然機會下看到版主的文章,深受感動~我也想學習相關的知識和技能,請問我可以在哪裡找到呢?非常感謝您~

    回覆刪除
  3. 不太清楚指的相關技能是程式、電路還是機構?
    做這個玩具需要的知識技能都是以前做的東西,一點一滴慢慢累積出來的~
    有點難解釋怎麼一次全部學會所有的技能
    不知道正不正確,但以前的做法是
    如果想學會Ardunio怎麼寫或是電路怎麼設計,會先設定一個自己有興趣的目標
    然後上網到處查資料慢慢拼湊出來,在過程中會發現有哪些需要加強的

    單一時間專注一個點,一步一步慢慢來是最快的方式
    以前常常把目標訂得太遠或是想要做到一步到位,每個都要做到最好
    反而無疾而終
    後來開始慢慢的了解"先求有再求好"

    如果對Arduino有興趣可以參考這邊
    http://coopermaa2nd.blogspot.jp/
    如果想要做點小玩具可以參考這邊,常常逛這邊看別人是怎麼做機構設計的,有很多很有創意的想法~
    http://www.instructables.com/

    回覆刪除
  4. 請問光碟機的步進馬達驅動電壓多少比較恰當呢?我是使用12v 沒10秒 步進馬達就發燙了??

    回覆刪除
  5. 請問UEC回收場在哪啊?

    回覆刪除