zoukankan      html  css  js  c++  java
  • "废物利用"也抄袭——“完全”DIY"绘图仪"<二、下位机程序设计>

            就不说怎么组装了吧,一把辛酸泪。说程序,因为这有两把辛酸泪……一把给下位机的C代码一把为了VB.NET的图像处理……不过就上上一篇说的,它们可以正确运行了,并且今天克服了Arduino上电过程中步进电机没事瞎转悠的困难。

            其实上位机和下位机的功能界定非常清晰:上位机解释图片为指令,下位机解释上位机指令为硬件动作——就俩步进和一个激光器。当然,如果有读卡器模块,完全可以把命令写成文件实现脱机打印。总体框架就是这样,那么下位机要实现的具体功能有哪些呢?

    1、串口通讯:接收指令和发送请求。既然是通讯,校验是少不了的,我写了一点CRC8校验。

    2、控制步进电机:这方面的文章很多,够学一会的。我修改了Stepper库,当然只是用它的大部分框架,这个框架么……哎

    3、控制激光器:激光器这里调节亮度的时候使用了PWM,正好手头有若干L298N…………

    4、X,Y轴限位:用外部中断来控制,需要注意的是,我用的Uno么有那么多中断口可以挥霍,所以全部的微动开关都是连接在一起的。我是并联的,所以未按下时应该时断开的;如果串联,那么未按下时应该是闭合的。

    5、软复位功能:可以用软件控制Arduino重启,方法也搜了一些,有些看着高大上的却然并卵。所以用的看门狗。

    大体就是这样吧,下面看一下部分代码:

    void setup() {
    	Serial.begin(115200);
    	
    	AboveStepper.setSpeed(aSpeed);			//设置上步进电机每分钟转数
    	BelowStepper.setSpeed(bSpeed);			//设置下步进电机每分钟转数
    	
    
    	AboveStepper.SetEnabled(true);			//初始化完成完成其他初始化之后再开启步进电机
    	BelowStepper.SetEnabled(true);
    
    	attachInterrupt(InterruptIntID, Interrupt, CHANGE);	//高电平
    	DoxGoto0();
    	DoyGoto0();
    
    	while (!Serial) {}
    	Serial.println(r_Ready);
    }
    

    一、初始化函数:这个函数在板子重启后被运行一次。

    a、首先初始化串口,需要注意的是,这个波特率在你的板子所支持的范围内,越高越好——速度差异很大的。在这种频繁收发数据的应用中,9600明显感觉非常慢。

    b、设置步进电机的转速,然后开启步进电机。

    c、附加外部中断,利用微动开关使x,y轴归零。需要注意的是,如果你的板子加电时有扰动,那么应该在附加外部中断之前使x,y轴倒退一定的安全距离。

    d、等待串口就绪,发送准备就绪信号。

    二、外部中断函数

    void Interrupt() {
    	if (digitalRead(InterruptIntPin) == HIGH) {
    		CurState = 0;
    	}
    	else {
    		if (CurState == 0) {			//发生不应有的中断
    			CurState = -1;
    			AboveStepper.steps_left = 0l;		//清理各个电机剩余步数
    			BelowStepper.steps_left = 0l;
    			digitalWrite(LaserPin, 0);			//关闭激光器
    		}
    		else if (CurState == c_xGoto0) {
    			CurState = -c_xGoto0;
    		}
    		else if (CurState == c_yGoto0) {
    			CurState = -c_yGoto0;
    		}
    		else if (CurState == c_lzGoto0) {
    			CurState = -c_lzGoto0;
    		}
    		else if (CurState == c_rzGoto0) {
    			CurState = -c_rzGoto0;
    		}
    
    	}
    }
    

      这个函数也非常清晰,当微动闭合时,证明某一个开关被触动,如果是程序控制的,那么更改当前状态以便退出正在运行的循环;如果是意外中断,那么关闭相应的硬件避免损坏。这个函数应该尽可能短,它在极为有限的时间内就应调用完成,所以一般采用全局变量进行控制,这里就是使用CurState。

    三、运行时的“循环”函数——Loop

            这个函数并不是一次运行的,它是被系统不断的反复调用。我的代码如下:

    void loop() {
    	if (CurState == 0 || CurState == State_Stop) {			//非中断状态
    		if (Serial.available()>=msgBuffSize) {
    			msgLen = Serial.readBytes(msgBuff, msgBuffSize);		//读取消息
    			if (msgBuff[msgBuffSize - 1] == cal_crc_table(msgBuff)) {
    				CommandParsing(msgBuff);							//处理消息
    				if (CurState != State_Stop) {
    					RequestData();											//请求数据
    				}
    			}else{
    				RerequestData();
    			}
    		} 
    	}
    }
    

      这里添加了暂停的功能,所以看起来可能有点乱。首先在正常状态或暂停状态下,尝试读取串口获取指令,当获取到数据后,进行Crc8验证,若未通过则重新申请数据;否则对命令进行解释并执行,随后当不处于暂停状态时再次申请指令。

            命令解释器就不详细说了,无非是一个大的分支结构。这里简要说一下这个AxiDraw用的双电机结构是怎么移动x,y轴的,其实很简单,你装起来之后用手转转就知道了。两个电机不同时针方向运行控制一轴,两个电机同方向运行控制另一轴。我的是这样的(Y+,Y-代表Y轴正方向和负方向上的电机):

    a、Y+顺时针Y-逆时针→X轴向负方向运行

    b、Y+顺时针Y-顺时针→Y轴向负方向运行

    所以代码是这样的:

    void DoxMove(long dBeat) {
    	int dir, step;
    	if (dBeat < 0) {
    		dir = -1;
    		step = -dBeat;
    	}else{
    		dir = 1;
    		step = dBeat;
    	}
    	for (int i = 0; i < step; i++) {
    		AboveStepper.step(dir);
    		BelowStepper.step(-dir);
    	}
    }
    
    void DoyMove(long dBeat) {
    	int dir, step;
    	if (dBeat < 0) {
    		dir = -1;
    		step = -dBeat;
    	}else{
    		dir = 1;
    		step = dBeat;
    	}
    	for (int i = 0; i < step; i++) {
    		AboveStepper.step(dir);
    		BelowStepper.step(dir);
    	}
    }
    
    void Do13Move(long dBeat) {
    	AboveStepper.step(dBeat);
    }
    
    void Do24Move(long dBeat) {
    	BelowStepper.step(dBeat);
    }
    

      当然,完全可以不用For循环。但是走斜线的时候感官上好像“绕远”,看着有点矬。然后是激光器控制,直接用PWM就可以了。最后,是软重启,用看门狗最通用,很稳定,无接线:

    #include <avr/wdt.h>
    
    
    void Soft_ReStart(){        
    	do{                           
    		wdt_enable(WDTO_15MS);		//开启看门狗计时器,然后不喂狗……就重启了。
    	for (;;){ }                       
    	} while (0);
    }
    

      就是这……

  • 相关阅读:
    maven总结二: 常用标签及属性
    maven总结一: 常用命令
    SpringCloud集成Seata并使用Nacos做注册中心与配置中心
    Seata 分布式事务解决方案及特点
    mui 文件附件下载
    Mock学习记录
    postman 测试elasticsearch 导出json
    elasticsearch 整合spring cloud Alibaba nacos 配置文件
    Elasticsearch 多字段高亮分页查询 返回泛型List集合
    Elasticsearch 多字段高亮字段返回json
  • 原文地址:https://www.cnblogs.com/zcsor/p/9058032.html
Copyright © 2011-2022 走看看