纵观网络上各位大神在数控方面的神作,却没有什么人做一些分享,那么,从我开始好了,这里只做了STC12C5A60S2这种51单片机成功对接Grbl上位机软件以及将接受到的G代码转换为float型坐标,验证这么一个功能,为以后深入编写电机控制,做一个基础铺垫。
这里所完成的功能很简单,所以代码也不难,主要是了解Grbl通信协议,以及G代码解析两个点,下面,我带大家来解读这两个点。
1、了解Grbl
在百度文库上,我上传了基本的协议,可以点击查看
http://wenku.baidu.com/link?url=Mu-JK2FGfv9pStTQqdVxxTF4I7PfQG8PdMl-zhS7tkEWjyNFvraf9uLpgAh7CS28bGrck3vxgHp5r262d6N9WGxBj9CvipySgCYm3v1nhje
这里我就不赘述了。
但是有一点,通信就是,你问我一句,我回你一句,所以是上位机先问下单片机:你是不是Grbl设备啊?,然后单片机得回答说:是啊,我是Grbl xx,然后上位机收到回复后才开始建立通信,这一点,在下面程序里的setup();完成
通信完全建立好后,就进入loop();函数里面,这时就等待上位机发送G代码了,上位机每次发送一行代码,单片机接受后,处理完成后,回复ok,上位机才会发送下一句。
基本就是这些了,其实很简单是不是。
2、将通信协议写入程序代码中,并完成G代码解析:
首先你应该有一点编程基础,然后应该就不难了。
其中动用的主要子函数是
Echo_Position(float x,float y, float z);
用来反馈当前坐标的
Analysis_Gcode(unsigned char *p_buffer);
用来解析G代码的
以下是函数的主体。
#include"STC12C5A60S2.h"
#include"UART.h"
#include"M_string.h"
#include"stdio.h"
enum axis{X_AXIS=0,Y_AXIS,Z_AXIS,E_AXIS};
xdata float Machine_Coordinates[4]={0.0,0.0,0.0,0.0};
xdata float Work_Coordinates[4]={0.0,0.0,0.0,0.0};
const unsigned char code ECHO_GRBL[]="Grbl";
const unsigned char code ECHO_POS[]="";
const unsigned char code ECHO_ERROR[]="error";
#define R_POS 11
const unsigned char code ECHO_OK[]="ok";
void Echo_Position(float x,float y, float z)
{
#define CLR_BUF() do{\
for(i=0;i<12;i++)\
{\
Buffer[i]=0x00;\
}\
}while(0)
unsigned char i;
unsigned char Buffer[12];//1+5+1+3+1+1
Clear_Uart_sendBuffer();
for(sendBuffer_Index=0;sendBuffer_Index" ; Uart_sendBuffer[sendBuffer_Index+1]="0x00;" Serial_println(Uart_sendBuffer); } void setup() { bit can_out="0;" bit Index="1;" unsigned char *pbuffer="Uart_recvBuffer;" unsigned long i="0;" Serial_begin(115200); while((_strchr_(pbuffer,0x18))="=0" && recvBuffer_Index<128); Serial_println(ECHO_GRBL); Clear_Uart_recvBuffer(); do { if(_strchr_(pbuffer,0x0D)) { if(_strchr_(pbuffer,'?')) { Echo_Position(Machine_Coordinates[X_AXIS],Machine_Coordinates[Y_AXIS],Machine_Coordinates[Z_AXIS]); Serial_println(ECHO_OK); } else if(_strstr_(pbuffer,"$13")!="0xFF)" { Serial_println(ECHO_OK); } else if(_strchr_(pbuffer,'$')) { Serial_println("[G0 G54 G17 G21 G90 G94 M0 M5 M9 T0 F250.000]"); Serial_println(ECHO_OK); Clear_Uart_recvBuffer(); } else if(_strchr_(pbuffer,'G')) { can_out="1;Serial_println(ECHO_OK);" } Clear_Uart_recvBuffer(); } }while(!can_out); Clear_Uart_recvBuffer(); } void Analysis_Gcode(unsigned char *p_buffer) { if(_strchr_(p_buffer,'?')) { recvBuffer_Index="0;" Echo_Position(Machine_Coordinates[X_AXIS],Machine_Coordinates[Y_AXIS],Machine_Coordinates[Z_AXIS]); Clear_Uart_recvBuffer(); Serial_println(ECHO_OK); } else if(_strchr_(p_buffer,'G') || _strchr_(p_buffer,'M')) { switch(*p_buffer++) { case 'G': switch(*p_buffer) { case '0': 和G1不区别 case '1': if((p_buffer="_strichr_(p_buffer,'x')))" { p_buffer++; Machine_Coordinates[X_AXIS]="_strtof_(p_buffer);" } if((p_buffer="_strichr_(p_buffer,'y')))" { p_buffer++; Machine_Coordinates[Y_AXIS]="_strtof_(p_buffer);" } if((p_buffer="_strichr_(p_buffer,'z')))" { p_buffer++; Machine_Coordinates[Z_AXIS]="_strtof_(p_buffer);" } if((p_buffer="_strichr_(p_buffer,'e')))" { p_buffer++; Machine_Coordinates[E_AXIS]="_strtof_(p_buffer);" }break; G0,G1 case '2':break; }break; case 'M':switch(*p_buffer++)break; } recvBuffer_Index="0;" Clear_Uart_recvBuffer(); Serial_println(ECHO_OK); } else { recvBuffer_Index="0;" Clear_Uart_recvBuffer(); Serial_println(ECHO_ERROR); } } sbit LED="P1^0;" unsigned long i="0;" void loop(void) { for(i="0;i<500;i++);" if(_strchr_(Uart_recvBuffer,0x0D)) if(Uart_recvBuffer[recvBuffer_Index-1]="=0x0D)" { Uart_recvBuffer[recvBuffer_Index]="0x00;" Analysis_Gcode(Uart_recvBuffer); } LED="!LED;" } void main(void) { setup(); while(1) { loop(); } }< code>
#include"STC12C5A60S2.h"
#include"intrins.h"
#include"UART.H"
xdata unsigned char Uart_sendBuffer[128];
xdata unsigned char Uart_recvBuffer[128];
xdata unsigned char sendBuffer_Index=0;
xdata unsigned char recvBuffer_Index=0;
bit Uart_busy=0;
#define FOSC 11095200 //默认11.0592MHz晶振
//TH1=256-(FOSC/32/baud_rate);
#define UART_PARYTY3
#message "Uart mode 3"
#message "Use this Uart.h will occupy Timer1"
void Clear_Uart_sendBuffer()
{
sendBuffer_Index=0;
while(sendBuffer_Index<128)
{
Uart_sendBuffer[sendBuffer_Index]=0;
sendBuffer_Index++;
}
sendBuffer_Index=0;
}
void Clear_Uart_recvBuffer()
{
recvBuffer_Index=0;
while(recvBuffer_Index<128)
{
Uart_recvBuffer[recvBuffer_Index]=0;
recvBuffer_Index++;
}
recvBuffer_Index=0;
}
void Serial_begin(unsigned long baud_rate)
{
Clear_Uart_sendBuffer();
Clear_Uart_recvBuffer();
#ifdef UART_PARYTY3
AUXR|=0x40;
SCON=0xDA; //9bit 奇校验
TMOD=0x20; //T1 8位自动重装模式
TH1=0xFF-(FOSC/32/baud_rate)+1; //初值
TL1=TH1;
TR1=1;
ES=1;
REN=1;
TI=0;
RI=0;
EA=1;
#else
#endif
}
void Uart_SendByte(unsigned char chr)
{
TI=0;
Uart_busy=1;
SBUF=chr;
while(Uart_busy);
}
void Serial_println(unsigned char *str)
{
unsigned char i;
while(*str && i<128)
{
Uart_SendByte(*str);
str++;
i++;
}
Uart_SendByte(0x0D);
Uart_SendByte(0x0A);
}
void Uart_Recv(void) interrupt 4 using 0
{
// ES=0;//关闭串口中断
if(RI && recvBuffer_Index<128) //如果是接收引发中断,且接收缓冲没有占满
{
Uart_recvBuffer[recvBuffer_Index]=SBUF; //将SBUF数据放入接收缓冲
recvBuffer_Index++;
}
RI=0;//清除接收中断标志
TI=0;
Uart_busy=0;
// ES=1;//打开串口中断
}
Uart.c的头文件,用来引用Uart.c里的函数
#ifndef UART_h
#define UART_h
extern bit Uart_busy;
extern xdata unsigned char Uart_sendBuffer[128];
extern xdata unsigned char Uart_recvBuffer[128];
extern xdata unsigned char sendBuffer_Index;
extern xdata unsigned char recvBuffer_Index;
#include"STC12C5A60S2.h"
void Serial_begin(unsigned long boundrate);
#define Serial_end() do{REN = 0;}
void Clear_Uart_sendBuffer();
void Clear_Uart_recvBuffer();
void Uart_SendByte(unsigned char chr);
void Serial_print(unsigned char *str);
void Serial_println(unsigned char *str);
#endif
其中string.lib是我自己做的库,主要是为了不出现UNCALLED SEGMENT这样的经典WARNING,点击下载(http://pan.baidu.com/s/1i43PSSt),其中包含库,头文件,以及函数原型,有些可能有错误,希望大家发现后能指出,这样,也不枉我共享这些东西。
最后再说一点,也是很重要的一点,波特率115200,不是原先默认的9600,记得改。