用stc15w和旧光驱移植arduino写字机GRBL之三:Bresenham算法
本帖最后由 XTXB 于 2018-11-12 11:42 编辑用stc15w4k32s4和旧光驱移植arduino写字机GRBL连载:
用stc15w和旧光驱移植arduino写字机GRBL之一:机架搭建
https://www.amobbs.com/thread-5701202-1-1.html
用stc15w和旧光驱移植arduino写字机GRBL之二:PCB制作
https://www.amobbs.com/thread-5701573-1-1.html
用stc15w和旧光驱移植arduino写字机GRBL之三:Bresenham算法
写了一个测试程序验证Bresenham算法,没有加减速,没有FIFO循环队列,运动时程序就傻等着,先跑起来增加点信心:
GRBL软件框架图:
步进电机驱动用定时器0完成,没有加减速,意外地,4988电机模块的step脉冲竟然用一句代码就搞定了:
/***************************************************
定时器0溢出中断处理函数 步进电机step脉冲产生
***************************************************/
void Insert_ISR_Timer0_Code(void)
{
if(x_steper_out_flag==1)//如果有x轴输出请求
{
x_steper_out_flag=0;//清x轴输出请求标记
X_STEP_BIT=0;//驱动4988竟然只需极短的低电平脉冲
if((K3_UP==1)&(K4_DOWN==1))X_STEP_BIT=1;
//按键未按下才有Xstep脉冲输出
}
if(y_steper_out_flag==1) //如果有y轴输出请求
{
y_steper_out_flag=0;//清y轴输出请求标记
Y_STEP_BIT=0;
if((K1_LEFT==1)&(K2_RIGHT==1))Y_STEP_BIT=1;
//按键未按下才有 Ystep脉冲输出
}
TL0=0x0F; //步进电机以固定速度运行,24M时钟
TH0=0xA1;
}
//
Bresenham线段插补算法详解,下图是第一象限,其他象限改变步进电机方向即可获得:
C语言实现Bresenham线段插补算法,较DDA算法Bresenham避免了浮点运算:
void plan_buffer_line(u16 x, u16 y){ //(x,y)为当前点的绝对坐标
static u16 step_event_count=0;//总步数
static u16 counter_x=0; static u16 counter_y=0; static u16 step_events_completed=0;//循环计数器
if(x>=last_x){ //当前点在上一点的右边,(last_x,last_y)为上一点的绝对坐标
X_DIRECTION_BIT=0;//第一四象限X电机旋转方向
target_x= (x-last_x);//求相对坐标的绝对值
}else {//当前点在上一点的左边
X_DIRECTION_BIT=1;//第二三象限X电机旋转方向
target_x= (last_x-x);//求相对坐标的绝对值
}//以上一点为原点的直角坐标系中,判断当前点在哪个象限从而决定xy电机的旋转方向
if(y>=last_y) { //当前点在上一点的上边
Y_DIRECTION_BIT=1;//第一四象限电机Y旋转方向
target_y= (y-last_y);
}else {//当前点在上一点的下边
Y_DIRECTION_BIT=0;//第二三象限Y电机旋转方向
target_y= (last_y-y);
}
//DDA/Bresenham直线插补算法
if(target_x>=target_y) step_event_count=target_x;
elsestep_event_count=target_y;//比较起点到终点xy坐标差,取最大坐标作为步进电机总运转步数
counter_x=step_event_count/2; counter_y=step_event_count/2;//右移1位,没有四舍五入
X_STEPPER_DISABLE_BIT=0;Y_STEPPER_DISABLE_BIT=0; //使能电机运转
last_x=x; last_y=y;//保存当前点坐标,作为下一点的原点
for(step_events_completed=0;step_events_completed<step_event_count;step_events_completed++){
counter_x+=target_x; counter_y+=target_y;
if(counter_x>=step_event_count){
x_steper_out_flag=1; //X步进电机走一步标记
counter_x-=step_event_count;
}
if(counter_y>=step_event_count){
y_steper_out_flag=1; //Y步进电机走一步标记
counter_y-=step_event_count;
}
while(x_steper_out_flag);//等待步进电机脉冲输出完成,没有用环形队列FIFO,在这里傻等
while(y_steper_out_flag);
}//循环直到线段的所有步数完成
X_STEPPER_DISABLE_BIT=1; Y_STEPPER_DISABLE_BIT=1; //禁止运转
}
舵机驱动用定时器2完成:
舵机的使用可参照贴子:https://www.amobbs.com/thread-5687693-1-1.html
void write_Angle(unsigned char Channel,unsigned char value)
//舵机驱动函数,channel=通道,value=角度(0-180)
{
AUXR |= 0x10; //定时器2开始计时
PWM_Value=(value*27+1200);
}
////定时器2,12T模式时钟24MHz ,输出1路高电平脉冲,剩下的时间为低电平补足20ms
void timer2(void) interrupt 12
{
AUXR &= B1110_1111; //定时器2开始停止计时
switch(order)
{
case 1:
SERVO_BIT=1; //开第0路
TL2=(-PWM_Value)%256;
//(-PWM_Value)=(65536-PWM_Value) 定时时间会更精准
TH2=(-PWM_Value)/256; //赋值高电平时间
break;
case 2:
SERVO_BIT=0;//关第0路
TL2=(25536+PWM_Value)%256;
//1路脉冲输出完毕,凑足20ms剩下的低电平时间
TH2=(25536+PWM_Value)/256;
order=0;
break;
}
AUXR |= 0x10; //定时器2开始计时
order++;
AUXR |= 0x10; //定时器2开始计时
}
下面是函数调用方法,点的坐标是手工算出的,傻瓜力大,是吧{:lol:}
画圆弧函数可参照帖子https://www.amobbs.com/thread-5687693-1-1.html
switch(ucKeySec) //按键服务状态切换
{
case 1:// K1_Start键
write_Angle(0,SERVO_put_value); //落笔
delay_ms(120);
plan_buffer_line(0,30*53.5);
//53.5是丝杆的参数,4988 设置:MS1=1,MS2=1,MS3悬空,8分频
plan_buffer_line(30*53.5,30*53.5);
plan_buffer_line(30*53.5,0);
plan_buffer_line(0,0);
write_Angle(0,SERVO_lift_value);//抬笔
plan_buffer_line(0,15*53.5);
write_Angle(0,SERVO_put_value); //落笔
delay_ms(120);
bogenGZS(15*53.5, 15*53.5, 14.5*53.5, 3.14,0);//画圆
bogenGZS(15*53.5, 15*53.5, 14.5*53.5, 6.28,3.14);
write_Angle(0,SERVO_lift_value);//抬起
plan_buffer_line(0.87*53.5,19.59*53.5); //E
write_Angle(0,SERVO_put_value); //落笔
delay_ms(120);
plan_buffer_line(29.13*53.5,19.59*53.5);//C
plan_buffer_line(6.27*53.5,2.98*53.5);//A
plan_buffer_line(15*53.5,30*53.5);//D
plan_buffer_line(23.73*53.5,2.98*53.5);//B
plan_buffer_line(0.87*53.5,19.59*53.5); //E
write_Angle(0,SERVO_lift_value);//抬起
plan_buffer_line(0,0);
ucKeySec=0;
break;
case 2:// K2_Stop键
ip=~ip;
if(ip==0)
write_Angle(0,SERVO_put_value); //落笔
else
write_Angle(0,SERVO_lift_value);//抬笔
ucKeySec=0;
break;
} 楼主厉害 哇,感觉这个精度很好啊 贴子发错版块了,咋办?{:sweat:} 不错顶一个 顶一个,厉害 必须顶一下! 不错 !! 楼主相当厉害 不错哦 顶贴,好帖子 顶一个!! 顶楼主,做得真不错。 好帖,顶撸主一个 {:lol:} 厉害,支持一下
这个太溜了,顶楼主,期待更新~ 顶楼主,做得真不错。 楼主相当厉害。顶楼主! 厉害的楼主,这个框架也是给力的。。
看着精度很好 手上还有好几个迷你DVD机的机芯组件, 没拆开看不知道能不能折腾 控制的很好,敬佩。 厉害,顶起来 厉害,这个是高手{:lol:} 不错,顶一个 楼主,能发整个工程文件?谢谢 ZJetWay 发表于 2019-10-12 16:29
楼主,能发整个工程文件?谢谢
我的代码太乱,只是实现了功能而已,拿不出手,GRBL是开源的,原版的代码更严谨,花时间啃原版更合算。 楼主谦虚了{:lol:} 楼主算法是自己写的吧,GRBL好像用的不是Bresenham算法吧! 很厉害。
谢谢分享 谢谢分享 值得学习 太赞了,{:handshake:} Thank you !!!
页:
[1]