纵观网络上各位大神在数控方面的神作,却没有什么人做一些分享,那么,从我开始好了,这里只做了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();
}
}
#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,记得改。