1. 程式人生 > >"廢物利用"也抄襲——“完全”DIY"繪圖儀"<二、下位機程序設計>

"廢物利用"也抄襲——“完全”DIY"繪圖儀"<二、下位機程序設計>

else if interrupt 分支 解釋 控制 dto 發生 移動 速度

就不說怎麽組裝了吧,一把辛酸淚。說程序,因為這有兩把辛酸淚……一把給下位機的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);
}

  就是這……

"廢物利用"也抄襲——“完全”DIY"繪圖儀"<二、下位機程序設計>