切换到宽版
爱科技/爱创意/爱折腾/爱极致;技术知识分享平台,点击进入新版数码之家网站
  • 236964阅读
  • 162回复

从零开始做光驱激光雕刻机 [复制链接]

上一主题 下一主题
离线ly7317090803
 

发帖
52
M币
5575
专家
58
粉丝
103
只看楼主 倒序阅读 我要置顶 楼主  发表于: 2015-12-27
— 本帖被 發騷友 设置为精华,作者+3000M币+5专家(2015-12-27) —
目前很多便宜的激光雕刻机都是直接使用Arduino板子为主控直接组装的,其实只要往ATmega328芯片烧录网上GRBL的固件即可(同时做好外围供电电路),但本文则是从零开始讲述各个硬件及软件的组建,废话不多说,以下主要讲述4个方面:X、Y轴的硬件平台组成;驱动电路及行程限位;激光模组;主控及软件分析;

1. XY轴硬件组成。
其实很简单,为了尽量节约成本,除激光模块及主控芯片外成本也就4块钱(光驱是从古董电脑里免费拆的)。

两个光驱,把激光部分及其附属电路拆下,就留一个能固定东西的导轨,一个平放做Y轴,一个横着固定在架子上做X轴,固定的架子也很简单,直接利用下面光驱上另一块铁片折弯90°做的,再加上几根扎带固定,图中的中性笔笔芯用来固定X轴的水平度。

弄个坏的手机背盖做平台(Sony Z1后盖),用双面胶粘在Y轴的滑块上,不过注意要保持水平度。


2. 驱动电路及行程开关
驱动芯片采用国产便宜的芯片MD127,低电压驱动芯片(2.7-5.5V均可)

当然淘宝上也有很多类似的芯片价格也就1元左右。电源处必须加10uF电容,少了它将无法工作。但需注意一点:此芯片不支持细分,也就是无法将每步分得更细,只能8步为一周期(相当于1/2细分),因此精度也就每步0.07mm,随之而来的就是相当大的误差,这个必须由程序去弥补,下面会说到。

限位开关采用光电对管,一个红外发射一个红外接收,当其中有东西挡在它们之间时接收管则会因无光而断开回路。

注意挡片要不透光的

原理如上,挡住时(黄色线)输出高(10K上拉电阻),没挡住时输出低。

3. 激光模组
说实话这激光便宜的还真买不得,我这个50多块,100mW 蓝紫激光,不到一个月因激光衰减换过一次激光头,老板说工作温度不得超过50度,于是我加了个大风扇专门吹它,大冬天的人都快冷怕了。可是不到20天后激光模组又无法雕刻了,因为平时调试时都是把激光关掉或用PWM把光调暗,目前只能再去买个了,说实话这个衰减还真跟温度无直接关系,自从上次换过后平时最高温度不超过30度(摸上去冰冷的)。

由于激光模组只有2根线(电源+、电源-),因此在电源-极串加N沟道场效应管SI2306,因为其内部有恒流电路,因此占空比<2%时输出弱光(相当于提供一个达不到恒流芯片工作的电压),频率要求不高,约1kHz左右,电源5V。

固定外面的散热铝块到X轴滑块上,背面贴了双面胶,再用扎带捆住。前面2颗螺丝用来固定激光模组。

4. 主控、主体软件
其实上面3点均不难,怎么做基本都能实现,仅是精度上的问题而已。但软件则不同,错一点可能整幅作品就全废了。

首先是硬件电路图,为了简单明了就没用电路绘图软件了:

电路很简单,整个系统可以在3V电源下正常工作(注意给STM32F103主控芯片的电源搞好点,免得串口读到的数据有误),也就是电池都能轻松带动。整个实物如下,由于未全部整合,主控等目前用杜邦线相连:





接着是软件部分,也是最关键的,首先肯定是如何驱动光驱的步进电机,一般光驱传动轴转一圈前进(后退)3mm,由于是1/2细分,故需40步才能转1圈(不细分时20步走一圈),也就是每步进1mm需要13.33步(脉冲)--13.33 step/mm,每个步进电机需4个IO控制(以下用M1_O1~M1_O4表示,即电机M1的OUT1~4,_H表示输出高,_L表示输出低)
  1. void Step_out(u8 Motor1,u8 Motor2)//电机M1 M2输出控制函数,控制2相线圈轮流导通,详细参看网上步进电机驱动资料
  2. {  
  3.          if(Motor1&0x80)// Motor1 bit7表示是否使能输出
  4.                 {        
  5.                         switch(Motor1&0x7F)//Motor1 bit7表示是否使能输出,其余位表示输出第几步
  6.                         {
  7.                 case 0:                M1_O1_H;M1_O2_L;M1_O3_L;M1_O4_L; break;
  8.                 case 1:                M1_O1_H;M1_O2_L;M1_O3_H;M1_O4_L;break;
  9.                 case 2:                M1_O1_L;M1_O2_L;M1_O3_H;M1_O4_L;break;
  10.                 case 3:                M1_O1_L;M1_O2_H;M1_O3_H;M1_O4_L;break;
  11.                 case 4:                M1_O1_L;M1_O2_H;M1_O3_L;M1_O4_L;break;
  12.                 case 5:                M1_O1_L;M1_O2_H;M1_O3_L;M1_O4_H;break;
  13.                 case 6:                M1_O1_L;M1_O2_L;M1_O3_L;M1_O4_H;break;
  14.                 case 7:                M1_O1_H;M1_O2_L;M1_O3_L;M1_O4_H;break;
  15.                         }
  16.                 }
  17.                 if(Motor2&0x80)
  18.                 {        
  19.                         switch(Motor2&0x7F)//与Motor1同理
  20.                         {
  21.                 case 0:                M2_O1_H;M2_O2_L;M2_O3_L;M2_O4_L;break;
  22.                 case 1:                M2_O1_H;M2_O2_L;M2_O3_H;M2_O4_L;break;
  23.                 case 2:                M2_O1_L;M2_O2_L;M2_O3_H;M2_O4_L;break;
  24.                 case 3:                M2_O1_L;M2_O2_H;M2_O3_H;M2_O4_L;break;
  25.                 case 4:                M2_O1_L;M2_O2_H;M2_O3_L;M2_O4_L;break;
  26.                 case 5:                M2_O1_L;M2_O2_H;M2_O3_L;M2_O4_H;break;
  27.                 case 6:                M2_O1_L;M2_O2_L;M2_O3_L;M2_O4_H;break;
  28.                 case 7:                M2_O1_H;M2_O2_L;M2_O3_L;M2_O4_H;break;
  29.                         }
  30.                 }
  31.         if((Motor1&0x80)||(Motor2&0x80))delay_n100us_loop(9);//若有输出则延时1ms左右
  32. }
  33. //电机输出控制函数,输入参数:步数+方向  返回错误标记或0
  34. // M1_step、M2_step最高位bit15表示正反转,0-顺时针  1-逆时针,其余位表示输出步数
  35. u8 Motor_Move(u16 M1_step,u16 M2_step)
  36. {
  37. unsigned char stepinfo_M1,stepinfo_M2;
  38. u8 Error_mark=0;//错误标示,初始值为0
  39.         while((M1_step&0x7FFF)!=0 || (M2_step&0x7FFF)!=0)//循环,一直到步数全部减到0
  40.         {
  41.         Error_mark=0;//清零错误标记
  42.         stepinfo_M1=stepinfo_M2=0; //清零输出,stepinfo_M1(M2)输出到上面的Step_out函数
  43.         if(M1_step&0x7FFF)//计算motor1的输出
  44. {
  45.         M1_step--;
  46.         if((M1_step&0x8000)==0) //表示正转
  47.         {
  48.                 M1_step_calc++;//M1计算时用的坐标位置记录寄存器,因为可能强制设置原点,故会导致当前设置的原点不是机器实际的原点,但为防止机器超出范围工作,因此设置Distance_M1寄存器来记录机器实际位置。Virtual_offset_M1表示在强制设置原点后Distance_M1与M1_step_calc的偏差值,假如机器需工作的范围超出实际范围,Distance_M1的值不会变,且实际的轴也不会动,但M1_step_calc会虚拟地变化,当M1_step_calc与Distance_M1的差值恢复到Virtual_offset_M1后则表示计算的工作坐标已恢复到正常范围内,即可以执行电机的动作。下同
  49.                 if((Distance_M1==X_MAX || Virtual_offset_M1+M1_step_calc-1 != Distance_M1)&& (CNC_State&0x40) ){Error_mark=2;}
  50.                 else
  51.                 {Distance_M1++; //记录机器实际位置,以限位开关为原点的位置
  52.                 if( (Step_index_last & 0x007F) ==0) //到达步数最小值则下一步为第7步,下同
  53. Step_index_last |= 0x07;
  54.                 else Step_index_last--;}
  55.         }
  56.         else  //反转
  57.         {
  58.                 M1_step_calc--;///
  59.                 if((Distance_M1==0  || Virtual_offset_M1+M1_step_calc+1 != Distance_M1)&& (CNC_State&0x40)){Error_mark=1;}
  60.                 else {Distance_M1--; //记录机器实际位置,以限位开关为原点的位置,下同
  61.                 if( (Step_index_last & 0x007F) ==0x07)//到达步数最大值则下一步为第0步,下同
  62. Step_index_last &= 0xFFF8;
  63.                 else Step_index_last++;}
  64.         }
  65.         if(!Error_mark)stepinfo_M1 |= 0x80;//使能输出
  66. }
  67.          if(M2_step&0x7FFF)//计算motor2的输出
  68.          {
  69.                  M2_step--;
  70.                  if((M2_step&0x8000)==0) //表示正转
  71.                  {
  72.                          M2_step_calc++;////
  73.                          if((Distance_M2==Y_MAX  || Virtual_offset_M2+M2_step_calc-1 != Distance_M2)&& (CNC_State&0x40))//Virtual_offset_M2+M2_step_calc-1 != Distance_M2判断是否回归到正常工作坐标
  74.                                 {Error_mark|=8;}
  75.                          else {Distance_M2++;
  76.                          if( (Step_index_last & 0x7F00) ==0) Step_index_last |= 0x700;
  77.                          else Step_index_last -= 0x100;}
  78.                  }
  79.                  else  //反转
  80.                  {
  81.                          M2_step_calc--;////
  82.                           if((Distance_M2==0  || Virtual_offset_M2+M2_step_calc+1 != Distance_M2)&& (CNC_State&0x40)){Error_mark|=4;}
  83.                         else
  84.                         { Distance_M2--;
  85.                          if( (Step_index_last & 0x7F00) ==0x0700) Step_index_last &= 0x80FF;
  86.                                 else Step_index_last += 0x100;}
  87.                  }
  88.                 if(!(Error_mark&0x0C)) stepinfo_M2 |= 0x80;//使能输出
  89.          }
  90. // Step_index_last记录上一次电机运行到第几步,高8位(bit15-8)为电机M2,bit7-0记录M1,本次运行的步数取决于上一步
  91. stepinfo_M1 |= (Step_index_last&0xFF);
  92. stepinfo_M2 |= ((Step_index_last&0xFF00)>>8);
  93. Step_out(stepinfo_M1,stepinfo_M2);//输出
  94.         }
  95.         return Error_mark;
  96. }



以上两个函数为电机驱动的底层函数,实现的主要目的是输入步数和方向来控制电机的输出。
底层步进电机驱动函数外,软件上主要需解决3个方面问题:1、串口通信部分;2、常用的指令逐个解析及回复;3、CNC插补算法。

● 串口通信,串口一般9600的波特率,虽然速度不快但需对串口数据接收及主程序的执行控制好,一边对串口收到的数据不可漏掉或未读完就提取指令;一边要对输出进行计算同时电机输出也得控制好。本程序里是先给串口接收区划分一个1000字节的缓冲区,同时来再多数据也能不拉下(GRBL有时一次最大发送700字节的指令,故在此不得不设置一个大点的区域),若单片机的寄存器不能设置那么大,其实也可以设置一个环形的缓冲区域:

主程序每200ms判断一次串口是否收到数据,若有数据则收到数据标记位置位(USART_RX_STA的bit7置为1)。
另外在主程序解析串口数据时也要监测串口中是否又来了新的数据,
  1. H1:
  2. if(USART_RX_STA&0x01)Wait_Nodata();//若有数据来了则等待接收完
  3. /………解析指令部分………/
  4. if(uart_buf_index<USART_RX_NUM && uart_buf_index)goto H1;//本条指令读取结束,看是否读取数据完毕,若还有未读完的则再循环读取. USART_RX_NUM串口总数据长度,uart_buf_index当前解析指令的位置。
  5. //判断是否还有数据的函数,等待接收完
  6. u8 Wait_Nodata()//等待串口无数据,有数据时USART_RX_STA的bit0会置1
  7. {
  8.         do{
  9.                 USART_RX_STA &= 0xFE;
  10.                 delay_ms_loop(70);//延时70ms
  11.         }while( USART_RX_STA&0x01);
  12.         return 0;
  13. }

● 指令解析及回复
指令主要有G0、G01、G02、G03、G90、G92、M2、M3、M4、M5,用法及意义如下:
G0  快速定位,如:G00 X10.111 Y3.254 即移动到10.111,3.254的位置
G01 直线插补,不是G0那样最快移动到目的地,如:G01 X10.111 Y3.254  表示从当前坐标位置直线移动到10.111,3.254,经过的路径是一条直线
G02 顺圆插补,顺时针的圆弧插补,如 G02 X1.211 Y3.222 I-1.0 J2.111  ‘X1.211 Y3.222’表示圆弧终点是1.211,3.222,圆弧的圆心是距离当前机器坐标(-1.0,2.111)的位置
G03 与G02类似,只是变为逆时针的圆弧
G90 表示当前的执行参数为绝对尺寸,G91为相对尺寸,例如:G91  G00 X10 Y10且当前位置是(1,1),则最后执行后将停在(11,11),而G90 则是停在(10,10)的位置
G92 强制设定当前的坐标值
M2 表示程序结束。注意在收到此条指令后需发送“Grbl 0.8c ['$' for help]”,即GRBL版本号
M3、M4对于激光雕刻来说意义不大(都是表示开启激光),切割时表示电机的正反转
M5 关闭激光或电机
此外在收到0x18时需返回“Grbl 0.8c ['$' for help]”,收到’$G’时需回复“[G0 G54 G17 G21 G90 G94 M0 M5 M9 T0 F250.000]”,只是从原固件程序模仿的,实际参数意义不大。收到“$$”时需回复$0-$22各参数的配置,目前主要用到的是$0、$1,$0表示x轴的精度step/mm,即1毫米需要多少步才能到,$1表示y轴的 (step/mm)。设置$0则发送$0=13.333即可。
当电脑端发送’?’时,表示请求获取当前机器的状态及工作坐标,此时回复“<Run,MPos:1.234,1.444,0.000,WPos:1.231,1.451,0.000>”,Run表示机器有轴正在运行,Idle表示处于空闲,后面分别则是机器坐标、工作坐标(X Y Z三个轴的坐标)

注意收到的每条指令均是以16进制0x0D为结尾,回复指令时则回复 0x0D 0x0A两字节为结尾。
Arduino有个IO单独监控串口的DTR,当串口被打开时,DTR会产生一个脉冲,此时Arduino的主控会发送“Grbl 0.8c ['$' for help]”来请求连接主机。接着电脑软件会依次发送?、$G、$$三条指令来获取基本信息。

解析这些指令也很简单,首先读取第一字符,判断是属于哪种指令,再解析下一字节的数字代表什么功能,接着提取坐标等数值,最后执行即可。

另外每读取到一条指令后均需要返回’ok’,电脑端软件以此判断队列中还有几条指令未执行,若未执行的指令过多软件就会等待(默认是等待50S),遇到无法解析的指令时则发送error: Expected command 来反馈无法解析此指令。



[ 此帖被ly7317090803在2015-12-31 17:03重新编辑 ]
本文内容包含图片或附件,获取更多资讯,请 登录 后查看;或者 注册 成为会员获得更多权限
本帖最近打赏记录:共51条打赏M币+172
离线ly7317090803

发帖
52
M币
5575
专家
58
粉丝
103
只看该作者 1楼 发表于: 2015-12-27
从零开始做小激光雕刻机
请登录后查看
[ 此帖被ly7317090803在2015-12-31 17:14重新编辑 ]
本文内容包含图片或附件,获取更多资讯,请 登录 后查看;或者 注册 成为会员获得更多权限
本帖最近打赏记录:共61条打赏M币+196专家+1
离线494448345

发帖
16
M币
980
专家
0
粉丝
3
只看该作者 2楼 发表于: 2015-12-27
请登录后查看
本帖最近打赏记录:共1条打赏M币+3
离线lydega

发帖
1593
M币
2821
专家
1
粉丝
15
只看该作者 3楼 发表于: 2015-12-27
请登录后查看
离线蓝_猫

发帖
15149
M币
0
专家
11
粉丝
1220
只看该作者 4楼 发表于: 2015-12-27
请登录后查看
本帖最近打赏记录:共1条打赏M币+3
离线518zl

发帖
1238
M币
2492
专家
1
粉丝
30
只看该作者 5楼 发表于: 2015-12-27
请登录后查看
离线素食猫

发帖
4226
M币
1232
专家
6
粉丝
51
只看该作者 6楼 发表于: 2015-12-27
离线zhmy8828

发帖
174
M币
4779
专家
1
粉丝
23
只看该作者 7楼 发表于: 2015-12-27
请登录后查看
离线hzxin

发帖
302
M币
2477
专家
3
粉丝
51
只看该作者 8楼 发表于: 2015-12-27
请登录后查看
离线xiamofeng

发帖
5910
M币
1760
专家
1
粉丝
70
只看该作者 9楼 发表于: 2015-12-27
请登录后查看
快速回复
限80 字节
如果您提交过一次失败了,可以用”恢复数据”来恢复帖子内容
 
上一个 下一个