就不说怎么组装了吧,一把辛酸泪。说程序,因为这有两把辛酸泪……一把给下位机的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"绘图仪"<二、下位机程序设计>的更多相关文章

  1. "废物利用"也抄袭——“完全”DIY"绘图仪"<三、上位机程序设计>

    上位机的程序主要是解析图片和生成较好的代码,现在实现的功能有灰度打印,二值打印,轮廓打印,骨骼打印.当然,必不可少的是打印大小的控制.测试了一些图片,总体来说,打印速度依次加快,因为打印的内容依次减少 ...

  2. "废物利用"也抄袭——“完全”DIY"绘图仪"<一、准备工作>

    上一个光驱造已经解体若干天了,因为事情很多arduino也不太熟悉,直到今天才做出了一个可以用的样本.当然,上位机和下位机代码都写好了,可以正常工作,但是由于电路知识还是中学那点没还给老师的,加以各种 ...

  3. "废物利用"也抄袭——废旧喷墨打印机和光驱DIY"绘图仪"

    很长时间没有写博客,因为各种各样的事情占去大块时间,只有零碎时间偶尔在CSDN逛逛也偶尔回几个帖子.很久以前就看到一些光驱DIY雕刻机之类的,很是向往,最近这几天得闲就TB了一套Arduino UNO ...

  4. [No0000CA]什么是“普瑞马法则”?以及 如何利用“普瑞马法则”,三步克服惰性

    一般在学习和生活中,我们都可能有这样的经验,就是当说想要作某件事情的时候,但过了好久发现还是没有做:或者觉得有力气使不出来:或者总觉得生活是灰色和抑郁的等等. 这类情况反映在生活中,就是生活好像总是被 ...

  5. Touch Bar 废物利用系列 | 在触控栏上显示 Dock 应用图标

    都说 Intel 第八代 CPU 对比上代是牙膏不小心挤多了,而配备第八代 CPU 的 MacBook Pro,只有 Touch Bar 版本,虽然贵了一点,但就一个字 -- 买! 收到电脑后,兴冲冲 ...

  6. [自娱自乐] 4、超声波测距模块DIY笔记(四)——终结篇·基于C#上位机软件开发

    前言 上一节我们已经基本上把超声波硬件的发射和接收模块全部做好了,接下来我们着手开发一个软硬结合的基于C#的平面定位软件! 目录 一.整体思路 二.效果提前展示 2-1.软件部分展示 2-2.硬件部分 ...

  7. 实验三 Java基本程序设计(2)

                                             实验三 Java基本程序设计(2)                                           ...

  8. 20145208 实验三 Java面向对象程序设计

    20145208 实验三 Java面向对象程序设计 实验内容 初步掌握单元测试和TDD 理解并掌握面向对象三要素:封装.继承.多态 初步掌握UML建模 熟悉S.O.L.I.D原则 了解设计模式 实验步 ...

  9. 利用lipo编译合并iPhone模拟器和真机通用的静态类

    利用lipo编译合并iPhone模拟器和真机通用的静态类 如何编译静态类库,而且现在网上也有很多的教程,现在问题时我们编译好了的静态类库会时两个版本的.a文件,分别用于模拟器和iPhone真迹,因此M ...

随机推荐

  1. HDU 3709 Balanced Number(数位DP)题解

    思路: 之前想直接开左右两边的数结果爆内存... 枚举每次pivot的位置,然后数位DP,如果sum<0返回0,因为已经小于零说明已经到了pivot右边,继续dfs只会越来越小,且dp数组会炸 ...

  2. Redis之字符串

    Redis 字符串 String 是redis最基本的类型,value 不仅可以是 String,也可以是数字. 使用 Strings 类型,可以完全实现目前 Memcached 的功能,并且效率更高 ...

  3. 分布式缓存--系列1 -- Hash环/一致性Hash原理

    当前,Memcached.Redis这类分布式kv缓存已经非常普遍.从本篇开始,本系列将分析分布式缓存相关的原理.使用策略和最佳实践. 我们知道Memcached的分布式其实是一种“伪分布式”,也就是 ...

  4. Qt5_pro_02

    1.g++ 编译参数 如果 用g++编译时,命令行是这样的:“g++ main.cpp -std=c++0x -pthread” 则在Qt的pro文件中这样设置: QMAKE_CXXFLAGS += ...

  5. Spring中的@Transactional

    spring中的@Transactional基于动态代理的机制,提供了一种透明的事务管理机制,方便快捷解决在开发中碰到的问题. 一般使用是通过如下代码对方法或接口或类注释: @Transactiona ...

  6. charles抓取https中出现unknow

    http正常抓包,https则出现unknown 1.安装证书 Help->SSL Proxying->Install Charles Root Certificate 但是!!!装完并没 ...

  7. [转载]从B 树、B+ 树、B* 树谈到R 树

    从B 树.B+ 树.B* 树谈到R 树 作者:July.weedge.Frankie.编程艺术室出品. 说明:本文从B树开始谈起,然后论述B+树.B*树,最后谈到R 树.其中B树.B+树及B*树部分由 ...

  8. java之子类继承抽象类,子类构造器调用抽象类构造器问题

    package com.wtd; public abstract class Car { private String name= "car"; public Car(String ...

  9. ASP.NET(c#) 日期选择控件的另一种实现方法

    这篇文章是在一个英文网站发现的,完全使用ASP.NET实现了日期选择的基本功能,没有使用js,原文链接http://geekswithblogs.net/plundberg/archive/2009/ ...

  10. SQL竖列变横列

    DROP TABLE IF EXISTS curriculumTable; CREATE TABLE curriculumTable ( id INT PRIMARY KEY AUTO_INCREME ...