"廢物利用"也抄襲——「徹底」DIY"繪圖儀"<2、下位機程序設計>

        就不說怎麼組裝了吧,一把辛酸淚。說程序,由於這有兩把辛酸淚……一把給下位機的C代碼一把爲了VB.NET的圖像處理……不過就上上一篇說的,它們能夠正確運行了,而且今天克服了Arduino上電過程當中步進電機沒事瞎轉悠的困難。git

        其實上位機和下位機的功能界定很是清晰:上位機解釋圖片爲指令,下位機解釋上位機指令爲硬件動做——就倆步進和一個激光器。固然,若是有讀卡器模塊,徹底能夠把命令寫成文件實現脫機打印。整體框架就是這樣,那麼下位機要實現的具體功能有哪些呢?安全

一、串口通信:接收指令和發送請求。既然是通信,校驗是少不了的,我寫了一點CRC8校驗。框架

二、控制步進電機:這方面的文章不少,夠學一會的。我修改了Stepper庫,固然只是用它的大部分框架,這個框架麼……哎函數

三、控制激光器:激光器這裏調節亮度的時候使用了PWM,正好手頭有若干L298N…………oop

四、X,Y軸限位:用外部中斷來控制,須要注意的是,我用的Uno麼有那麼多中斷口能夠揮霍,因此所有的微動開關都是鏈接在一塊兒的。我是並聯的,因此未按下時應該時斷開的;若是串聯,那麼未按下時應該是閉合的。ui

五、軟復位功能:能夠用軟件控制Arduino重啓,方法也搜了一些,有些看着高大上的卻然並卵。因此用的看門狗。spa

大致就是這樣吧,下面看一下部分代碼:blog

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);
}

1、初始化函數:這個函數在板子重啓後被運行一次。圖片

a、首先初始化串口,須要注意的是,這個波特率在你的板子所支持的範圍內,越高越好——速度差別很大的。在這種頻繁收發數據的應用中,9600明顯感受很是慢。it

b、設置步進電機的轉速,而後開啓步進電機。

c、附加外部中斷,利用微動開關使x,y軸歸零。須要注意的是,若是你的板子加電時有擾動,那麼應該在附加外部中斷以前使x,y軸倒退必定的安全距離。

d、等待串口就緒,發送準備就緒信號。

2、外部中斷函數

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。

3、運行時的「循環」函數——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);
}

  就是這……

相關文章
相關標籤/搜索