openpnp/arduino - 二手西门子电动飞达的测试

2023-11-03 18:30

本文主要是介绍openpnp/arduino - 二手西门子电动飞达的测试,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

    • openpnp/arduino - 二手西门子电动飞达的测试
    • 概述
    • 飞达正常的判断标准
    • 先挑出一个手工控制好使的二手飞达用于测试.
    • 推料的手工检测
    • 扒皮的手工检测
    • 飞达测试的接线
    • 通讯的测试
    • 用串口助手测试通讯
    • 先看看是否发送给飞达的管脚是自己接的那个
    • 查看所有可以用到的上位机通讯命令
    • M115 - 打印固件版本信息
    • M600 - 撕皮预动作
    • M601 - 高级送料模式
    • M602 - 取飞达状态
    • M603 - 取飞达送料计数值
    • M604 - 取0x42错误次数
    • M605 - 取0x43错误次数
    • M606 取0x44错误次数
    • M607 取复位次数
    • M608 - 取飞达的料之间的距离(步进值)
    • M628 - 切换步进值
    • M610 取飞达ID
    • M640 设置飞达ID
    • M630 读飞达内的E2PROM
    • M615 读飞达的固件版本
    • M650 - 自测试开始
    • M651 - 自测试结束
    • 备注
    • 改过的飞达控制板的mega2560r3测试工程
    • SchultzController.ino
    • config.h
    • Feeder.h
    • Feeder.cpp
    • gcode.ino
    • 飞达上物料编带正常装入后的通讯测试
    • 查看可用命令列表
    • 查看固件版本
    • 送料之前
    • 先进模式
    • 取飞达状态
    • 取飞达总送料计数
    • 清除飞达计数
    • 取0x42错误计数
    • 取0x43错误计数
    • 取0x44错误计数
    • 取飞达复位计数
    • 取物料步进值
    • 切换物料步进值
    • 取飞达ID
    • 设置飞达ID
    • 读E2PROM
    • 取飞达固件信息
    • 自测试
    • 补充 - 挂好料后, 一个命令就可以测试飞达是否能用
    • 二手飞达测试出的可用率
    • END

openpnp/arduino - 二手西门子电动飞达的测试

概述

现在手头的openpnp设备和自己做的散料飞达都正常用了, 已经将飞达控制板贴出来了.
在这里插入图片描述

下面, 准备将西门子二手飞达接入设备, 让自己的openpnp设备也变成一个有灵魂的贴片机.

当时定制设备时, 同学给我定了50个二手西门子电动飞达.
因为当时设备刚到手, 还在进行设备调试. 飞达到货后, 只是通电看灯是否亮, 发个包, 看看是否能用通讯控制飞达有动作响应. 只是初步抽检.

现在因为前置条件(设备调试完成, 飞达控制板已经贴好了)都好了, 准备将这50个飞达中好的飞达挑出来用, 将不好的飞达尝试通过换件法拼成能用的正常飞达.

那第一步就是判断这些飞达是否可以正常用(是否可以通过通讯来控制? 按钮控制即使坏了也无所谓, 用不到), 进行一些测试, 通过测试了, 就可以正常接入设备用.
以8x2飞达为例

飞达正常的判断标准

  • 可以正常推料, 供吸嘴来取料.

  • 可以正常将物料编带的透明塑料皮扒下来, 拉紧, 防止塑料蒙皮挡住供料窗.
    撕皮有2个部件:
    飞达前面的塑料皮挡板, M600 NX命令后让挡板向飞达后方动5mm, 靠着撕料齿轮压板的力量, 将塑料皮撕开.
    飞达中部的撕料齿轮
    只有这2个部件都是好的, 撕皮操作才正常.

初步用飞达上的按钮来测试, 不能用也无所谓, 只要用通讯能控制这2个动作就行.

从飞达正后方看, 左边是0号飞达, 右边是1号飞达.

先挑出一个手工控制好使的二手飞达用于测试.

因为我是要先采用通讯控制来控制飞达, 不管按钮好不好使, 主要通讯控制能正常控制飞达就行.
但是初步, 我要挑出一个正常能用按钮来控制的飞达来做第一次的测试.
试了4个, 才挑出一个手工按钮控制都好使的8x2的二手飞达.

推料的手工检测

在这里插入图片描述
飞达后端的绿色箭头按钮是进料按钮, 每按一下, 飞达前部的供料齿轮就转一个角度, 带动物料编带向前走.
如果飞达无响应, 也不一定意味着不能用, 也许按钮接触不好(或者按钮控制电路问题). 后续要用通讯协议来控制, 能用就好.

扒皮的手工检测

在这里插入图片描述
飞达正后方的黄色圆形按钮, 是扒皮按钮.
按下后, 如果正常, 飞达中部的扒皮齿轮会转动. 等后续, 将编带的塑料皮夹入2个扒皮齿轮中间就行. 在正常贴片流程中, 就会随着扒皮齿轮的转动, 将废弃的塑料皮带到飞达下部的垃圾仓. 同时, 将物料编带的保护皮撕下来.

飞达测试的接线

在这里插入图片描述
在这里插入图片描述
飞达有4根线:
棕色 : VCC(资料上说要给DC28V, 市面上没有DC28V或者DC30V的电源, 只能是用DC24V的开关电源来供电, 能用)
白色: GND
绿色: 通讯线TX(飞达控制板to飞达的发送, 飞达控制者发包)
黄色: 通讯线RX(飞达to飞达控制板的接收, 飞达控制者收包)
通讯的电平是24V的, 如果和MCU通讯, 要转成MCU适用的电平.

飞达控制板上的控制部件用的是官方原版mega2560R3(支持通讯时触发DTR信号重启设备), 通过USB线连接PC端.
飞达控制板子的供电用的开关电源的DC24V.
和飞达的通讯通过插座(XHD-2*10Y)连接, 线自己压出来, 接飞达航插板.

飞达航插板的供电是开关电源的DC24V, 从飞达控制板来的通讯信号连接到航空插头座(公头).
飞达航插板的航插座(公头)带着24V, GND, TX, RX接飞达出来的航插母头.

通讯的测试

从开源工程上, 将飞达数量改为1, 避免控制不准, 造成错觉(发个包给飞达, 如果发错飞达就尴尬了.).

#if defined (BOARD_MEGA2560)
// 用内存的地方
// FeederClass feeders[NUMBER_OF_FEEDERS];
// 如果不优化(不开DEBUG标记), 只能支持46把飞达
// FEEDER_CNT 47个 : 全局变量使用 8305 个字节(101%)的动态内存,剩下 -113 个字节用于局部变量。最大值为 8192 字节。
// FEEDER_CNT 46个 : 全局变量使用 7903 个字节(96%)的动态内存,剩下 289 个字节用于局部变量。最大值为 8192 字节。
// FEEDER_CNT 45个 : 个全局变量使用 7683 个字节(93%)的动态内存,剩下 509 个字节用于局部变量。最大值为 8192 字节。
// FEEDER_CNT 44个 : 个全局变量使用 7469 个字节(91%)的动态内存,剩下 723 个字节用于局部变量。最大值为 8192 字节。
// 好像局部变量的内存用量, 并没有估计到编译结果中. 局部变量要用多少, 需要自己估计.
// 能编译过的飞达数量是47组, 但是局部变量空间不够. 可以正常运行的飞达数量是44组.
#define FEEDER_CNT 1 // this only for test
#else
#define FEEDER_CNT 20
#endif

确定飞达通讯控制的管脚对应的mega2560R3的数字IO号码.

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
我现在的测试连接, 使这个飞达的通讯接到了D21来控制.
在meg2560r3工程中, 将唯一的发达发送管脚定为D21.

/**  Feeder number to TX port mapping (uses D port numbers)*/
#if defined (BOARD96PIN)const uint8_t TXportPin[20] = { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2 };
#elif defined (BOARD4PIN)const uint8_t TXportPin[20] = { 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12 }; // Matches ref des order of board//const uint8_t TXportPin[20] = { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; // J20 is port 0, then goes counter-clockwise
#elif defined (BOARD_MEGA2560) // BOARD_MEGA2560 已经定义// 官方arduino mega2560 R3 一共有70个数字IO(D0~D69), D0/D1 被编程串口占用了不能用. 剩下的可用数字IO位68个(D2~D69)// 其中D14/D15 = UART3, D16/D17 = UART2, D18/D19 = UART1, 这3个串口留着调试用, 剩下的可用数字IO为62个(D2~D13, D20~D69)   
const uint8_t TXportPin[FEEDER_CNT] = { // D2 => D31 was F1 => F3021, // pin = D21 // 就定义这一个引脚/*2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69*/};
#elseconst uint8_t TXportPin[20] = { 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 12, 14, 15, 16, 17, 18, 19, 20, 21 };
#endif

为了弄懂飞达控制的整个流程, 我加了调试宏 DEBUG, 打印了串口调试语句(被DEBUG宏包围), 哪里不对劲, 就可以根据调试信息去代码里面去找问题.

在arduinoIDE中, 指定开发板和串口, 编译工程, 上传到mega2560R3.

用串口助手测试通讯

测试命令都是M码, 以\n为结尾. 结尾的\n标志着命令发完, 否则飞达控制板不响应命令(没有\n就认为用户命令还没有输入完成).

先看看是否发送给飞达的管脚是自己接的那个

在这里插入图片描述
看到通讯发送的管脚是D21, 没错.

查看所有可以用到的上位机通讯命令

工程改了一下, 可以用M999看到所有可以用到的通讯命令.
在这里插入图片描述
现在挨个来测试通讯命令, 有的命令是和飞达控制板来通讯; 有的命令是通过飞达控制板中转来和飞达通讯.

M115 - 打印固件版本信息

在这里插入图片描述
这个是和飞达控制板通讯, 打印固件版本号, 防止固件版本不是最新.

M600 - 撕皮预动作

在这里插入图片描述
M600 N0 是控制0号位飞达送料, 用眼睛观察, 看到飞达前部左边的撕皮机构, 向后动了一下. 但是中间撕皮齿轮没有动作.
在这里插入图片描述
M600 N1 是控制0号位飞达送料, 用眼睛观察, 看到飞达前部右边的撕皮机构, 向后动了一下. 但是中间撕皮齿轮没有动作.

M601 - 高级送料模式

在这里插入图片描述
M601 N0X0 返回错误, 说没有胶带张力, 因为我现在没有正常上料, 以后再测试这个命令.
在这里插入图片描述
M601 N1X0 返回错误, 说没有胶带张力, 因为我现在没有正常上料, 以后再测试这个命令.

在这里插入图片描述
M601 N0X1 执行后, 只看到0号飞达前面的送料齿轮步进了一下, 没看到撕料机构往后动.
在这里插入图片描述
M601 N0X1 执行后, 只看到1号飞达前面的送料齿轮步进了一下, 没看到撕料机构往后动.

M602 - 取飞达状态

在这里插入图片描述
M602 N0 是取0号位飞达的状态, 现在由于没有正常上料, 飞达编带塑料膜拉紧机构没有检测到张力.

String FeederClass::showStatus() {switch (this->feederStatus) {case STATUS_OK:return "getFeederStatus: feeder OK";break;case STATUS_INVALID:return "getFeederStatus: invalid, status not updated";break;case STATUS_NO_TAPE_TENSION:return "getFeederStatus: No tape tension.  Tape may be broken";break;case STATUS_NO_TAPE_TRIGGER:return "getFeederStatus: Tape take-up not triggered after multiple feeds";break;case STATUS_FEED_ERROR:return "getFeederStatus: Feed motor did not advance";break;default:char statusCode[34];sprintf(statusCode, "Unrecognized status code %02X", this->feederStatus);return statusCode;}
}
/* -----------------------------------------------------------------
*  FEEDER STATUS
*  ----------------------------------------------------------------- */
#define STATUS_OK 0x40
#define STATUS_NO_TAPE_TENSION 0x42 	// cover tape won't tension (no tape, or broken tape)
#define STATUS_NO_TAPE_TRIGGER 0x43  	// didn't trigger a retension after 2 feeds
#define STATUS_FEED_ERROR 0x44          // feed motor stuck
#define STATUS_INVALID 0                // status not checked since last feed

从代码中看到, 只有飞达回0x40时, 才说明飞达没问题.
在这里插入图片描述
M602 N1 是取1号位飞达的状态, 现在由于没有正常上料, 飞达编带塑料膜拉紧机构没有检测到张力.

M603 - 取飞达送料计数值

在这里插入图片描述
M603 N0 命令取回的飞达计数不对, 应该是协议不对. 不过这不重要, 这个命令不在正式场景下应用.
也有可能是, 必须在飞达状态OK时, 才可以去取飞达计数.否则飞达计数回包中是其他含义.
在这里插入图片描述
M603 N1 命令取回的飞达计数不对, 应该是协议不对(以后条件许可时, 可以通过编程器, 读回飞达内的51MCU的bin实现, 逆向分析一下. 听同学说, 这个C51实现是没没有加密的, 直接能读回来). 不过这不重要, 这个命令不在正式场景下应用.

M604 - 取0x42错误次数

在这里插入图片描述

M605 - 取0x43错误次数

在这里插入图片描述

M606 取0x44错误次数

在这里插入图片描述

M607 取复位次数

在这里插入图片描述
看到向2个飞达位发送的命令都是一样的, 说明这个复位次数指的是飞达上电复位的次数, 因为断电再上电, 2个子飞达是一起复位的.

M608 - 取飞达的料之间的距离(步进值)

在这里插入图片描述
看到当前物料之间的距离为4mm.
可以通过飞达按钮的组合来改物料步进值(这个没有原版飞达文档, 得自己去实验). 还可以用M628 NX来切换步进

M628 - 切换步进值

在这里插入图片描述
M628 N0 不带参数, 看到设置的步进类型是0.
读一下步进
在这里插入图片描述
看到当前步进值为2mm
M628 N0 不带参数, 看到设置的步进类型是1.
在这里插入图片描述

读一下步进值
在这里插入图片描述
当前步进是4mm
这个命令等于是每次都先取一下当前步进值, 然后将步进值改变为下一个.
e.g. 8mm飞达只有2个步进值(2mm, 4mm), 每执行一次 M628NX, 就切换到下一种步进值.

M628 N1 同理

M610 取飞达ID

在这里插入图片描述

M640 设置飞达ID

在这里插入图片描述
现在已经将飞达ID设置为了1234
现在读取一下飞达ID看看改过来没有?
在这里插入图片描述
飞达ID已经改过来了.
这个飞达ID有用, 如果某个飞达需要特殊处理, 可以根据飞达ID和飞达位来判断.

M630 读飞达内的E2PROM

这个命令对原厂的工程师才有用.
在这里插入图片描述
这个命令, 从N0/N1的E2PROM中读取了16个字节出来.

M615 读飞达的固件版本

在这里插入图片描述
我现在测试这个飞达版本是v2.4

M650 - 自测试开始

在这里插入图片描述
执行这个命令后, 飞达开始自测试, 能看到所有飞达位的主要的机械结构(推料齿轮, 前面的撕料挡板, 中部的卷带齿轮)都在周期性的在动.
执行这个命令, 可以知道这对应飞达位的这3个机械装置是否完好, 也就知道是否可以通过通讯协议来控制飞达. 如果要初步测试飞达是否可以用通讯协议来控制飞达的机械机构, 用 M650 N0 这1个命令就行了.

执行这个命令时, 不能在上料的情况下进行, 测试的进料忽忽的, 不知道要浪费多少料…

M651 - 自测试结束

在这里插入图片描述
执行 M651 N0后, 自测试就停止了.

备注

西门子二手飞达能提供的协议就这些, 现在只能说飞达可以和飞达控制板通讯, 因为我没看到完整的进料撕膜动作.
下一步, 得用这个飞达连到openpnp中看看效果(在正常上料的情况下), 如果在openpnp中能正常控制送料, 撕膜, 能完成一种物料的连续的正常贴片流程, 才说明这个飞达没问题.

改过的飞达控制板的mega2560r3测试工程

在这里插入图片描述
arduino工程的文件包含关系, 是放在工程目录下的文件都算是源文件, 不是显势包含.
已经试过了, 如果将gcode.ino移动到同级目录下的bk目录, 工程就编译不过了, 因为好多实现在gcode.ino中.
主工程是SchultzController.ino, 因为这个文件中才有setup()和loop().

等用串口助手测试过了, 要给openpnp用时, 要注释掉DEBUG宏, 否则调试信息的回包, 会干扰openpnp的的判断.

SchultzController.ino

/*
* Author: Bill Ruckman
* (c)2020
*
* Adapted from 0816feeder by mrgl
*    https://github.com/mgrl/0816-feeder-firmware
*
* This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
* http://creativecommons.org/licenses/by-nc-sa/4.0/
*
* current version: v0.0
*
*
*/// ------------------  I N C  L I B R A R I E S ---------------#include "config.h"
#include "Feeder.h"// ------------------  V A R  S E T U P -----------------------FeederClass feeders[NUMBER_OF_FEEDERS];// ------------------  U T I L I T I E S ---------------// ------------------  S E T U P -----------------------
void setup() {byte i;Serial.begin(SERIAL_BAUD);while (!Serial);Serial.println(PRJ_NAME " " PRJ_VER " ""starting...");Serial.flush();Serial1.begin(9600);  // The hardware RX portfor (i = 0; i < NUMBER_OF_FEEDERS; i++) {feeders[i].setup(i, i / LANES_PER_PORT, i % LANES_PER_PORT);  // initialize with feeder number, port and lane}// setup listener to serial streamsetupGCodeProc();Serial.println(PRJ_NAME " " PRJ_VER " ""ready.");
}// ------------------  L O O P -----------------------void loop() {// Process incoming serial data and perform callbackslistenToSerialStream();
}

config.h

#ifndef _CONFIG_h
#define _CONFIG_h
#include "arduino.h"#define PRJ_NAME "my_SchultzController"
#define PRJ_VER "v2.0.1.2_build_2023_0909_1333"//#define BOARD96PIN
//#define BOARD4PIN
#define BOARD_MEGA2560// prints some extra information via serial1
// uncomment to disable in production
#define DEBUG
// #define DEBUG1 // my debug flag// simulates connected feeders
//#define SIMULATE#define LANES_PER_PORT 2  // only supports 2 for now
// The serial port = Feeder Number / LANES_PER_PORT
// The lane within the port = Feeder Number % LANES_PER_PORT#if defined (BOARD_MEGA2560)
// 用内存的地方
// FeederClass feeders[NUMBER_OF_FEEDERS];
// 如果不优化(不开DEBUG标记), 只能支持46把飞达
// FEEDER_CNT 47个 : 全局变量使用 8305 个字节(101%)的动态内存,剩下 -113 个字节用于局部变量。最大值为 8192 字节。
// FEEDER_CNT 46个 : 全局变量使用 7903 个字节(96%)的动态内存,剩下 289 个字节用于局部变量。最大值为 8192 字节。
// FEEDER_CNT 45个 : 个全局变量使用 7683 个字节(93%)的动态内存,剩下 509 个字节用于局部变量。最大值为 8192 字节。
// FEEDER_CNT 44个 : 个全局变量使用 7469 个字节(91%)的动态内存,剩下 723 个字节用于局部变量。最大值为 8192 字节。
// 好像局部变量的内存用量, 并没有估计到编译结果中. 局部变量要用多少, 需要自己估计.
// 能编译过的飞达数量是47组, 但是局部变量空间不够. 可以正常运行的飞达数量是44组.
#define FEEDER_CNT 1
#else
#define FEEDER_CNT 20
#endif// 如果是单个飞达做实验, 如果重新插拔了F0TX, 需要给飞达重新上电, 否则不通讯.
// pin0, 1 和arduino上传串口有冲突(也许是同一个串口), 实际操作的D0, 是D2.// 将飞达接到D2, 然后操作D0, 就可以控制
// pin2 => D0 => FD0, FD1
// pin3 => D1 => FD2, FD3
// pin4 => D2 => FD4, FD5
// pin5 => D3 => FD6, FD7 // M601N6X1\n, M601N7X1\n
// pin6 => D4 => FD8, FD9
// pin7 => D5 => FD10, FD11
// pin8 => D6 => FD12, FD13
// pin9 => D7 => FD14, FD15
// pin10 => D8 => FD16, FD17
// pin11 => D9 => FD18, FD19
// pin12 => D10 => FD20, FD21
// pin13 => D11 => FD22, FD23 // M601N22X1\n M601N23X1\n// pin14, pin15 串口3, 留出来通讯用
// pin16, pin17 串口2, 留出来通讯用
// pin18, pin19 串口1, 留出来通讯用, 接收飞达回包. pin19是RXD1 // 总的RX接到了pin19(RXD1)// pin20 => D18 => FD24, FD25
// pin21 => D19 => FD26, FD27 // M601N26X1\n M601N27X1\n
// pin22 => D20 => FD28, FD29 // M601N28X1\n M601N29X1\n
// pin23 => D21 => FD30, FD31
// pin24 => D22 => FD32, FD33
// pin25 => D23 => FD34, FD35
// pin26 => D24 => FD36, FD37
// pin27 => D25 => FD38, FD39 // M601N38X1\n M601N39X1\n
// pin28 => D26 => FD40, FD41
// pin29 => D27 => FD42, FD43
// pin30 => D28 => FD44, FD45
// pin31 => D29 => FD46, FD47
// pin32 => D30 => FD48, FD49
// pin33 => D31 => FD50, FD51
// pin34 => D32 => FD52, FD53
// pin35 => D33 => FD54, FD55
// pin36 => D34 => FD56, FD57 //
// pin37 => D35 => FD58, FD59
// pin38 => D36 => FD60, FD61
// pin39 => D37 => FD62, FD63
// pin40 => D38 => FD64, FD65
// pin41 => D39 => FD66, FD67
// pin42 => D40 => FD68, FD69
// pin43 => D41 => FD70, FD71
// pin44 => D42 => FD72, FD73
// pin45 => D43 => FD74, FD75
// pin46 => D44 => FD76, FD77
// pin47 => D45 => FD78, FD79
// pin48 => D46 => FD80, FD81
// pin49 => D47 => FD82, FD83
// pin50 => D48 => FD84, FD85
// pin51 => D49 => FD86, FD87 // M601N86X1\n M601N87X1\n// pin52 => D50 => FD88, FD89
// pin53 => D51 => FD90, FD91
// #define FEEDER_CNT 46 !!!// pin54 => D52 => FD92, FD93
// pin55 => D53 => FD94, FD95
// pin56 => D54 => FD96, FD97
// pin57 => D55 => FD98, FD99
// pin58 => D56 => FD100, FD101
// pin59 => D57 => FD102, FD103
// pin60 => D58 => FD104, FD105
// pin61 => D59 => FD106, FD107
// pin62 => D60 => FD108, FD109
// pin63 => D61 => FD110, FD111
// pin64 => D62 => FD112, FD113
// pin65 => D63 => FD114, FD115
// pin66 => D64 => FD116, FD117
// pin67 => D65 => FD118, FD119
// pin68 => D66 => FD120, FD121
// pin69 => D67 => FD122, FD123#define NUMBER_OF_FEEDERS (LANES_PER_PORT * FEEDER_CNT)  // number of ports(20) * LANES_PER_PORT/**  Upstream Serial*/
#define SERIAL_BAUD 115200//buffer size for serial commands received
#define MAX_BUFFFER_MCODE_LINE 64	// no line can be longer than this/* -----------------------------------------------------------------
*  FEEDER COMMANDS
*  ----------------------------------------------------------------- */
#define CMD_INFO 0x01
#define CMD_PRE_PICK 0x02
#define CMD_ADVANCE 0x03
#define CMD_STATUS 0x15
#define CMD_EEPROM_READ 0x17
#define CMD_SELF_TEST 0x18
#define CMD_EEPROM_WRITE 0x19
#define CMD_SET_PITCH 0x1c/* -----------------------------------------------------------------
*  FEEDER STATUS
*  ----------------------------------------------------------------- */
#define STATUS_OK 0x40
#define STATUS_NO_TAPE_TENSION 0x42 	// cover tape won't tension (no tape, or broken tape)
#define STATUS_NO_TAPE_TRIGGER 0x43  	// didn't trigger a retension after 2 feeds
#define STATUS_FEED_ERROR 0x44          // feed motor stuck
#define STATUS_INVALID 0                // status not checked since last feed/* -----------------------------------------------------------------
*  FEEDER COMM CONSTANTS
*  ----------------------------------------------------------------- */
#define ACK_TIMEOUT 50
#define RESP_TIMEOUT 50
#define MSG_ACK 0xE0/* -----------------------------------------------------------------
*  M-CODES
*  ----------------------------------------------------------------- */
#define MCODE_DRIVER_INFO 115
#define MCODE_PRE_PICK 600
#define MCODE_ADVANCE 601
#define MCODE_FEEDER_STATUS 602
#define MCODE_GET_FEED_COUNT 603
#define MCODE_CLEAR_FEED_COUNT 623
#define MCODE_GET_ERR42_COUNT 604
#define MCODE_GET_ERR43_COUNT 605
#define MCODE_GET_ERR44_COUNT 606
#define MCODE_GET_RESET_COUNT 607
#define MCODE_GET_PITCH 608
#define MCODE_TOGGLE_PITCH 628
#define MCODE_GET_FEEDER_ID 610
#define MCODE_SET_FEEDER_ID 640
#define MCODE_READ_EEPROM 630
#define MCODE_GET_FIRMWARE_INFO 615
#define MCODE_START_SELF_TEST 650
#define MCODE_STOP_SELF_TEST 651
#define MCODE_CMD_HELP 999//DEFINE config_h-ENDIF!!!
#endif

Feeder.h

#ifndef _FEEDER_h
#define _FEEDER_h#include "arduino.h"
#include "config.h"class FeederClass {protected://on initialize it gets a number.int feederNo=-1;uint8_t port;	// TX serial port for this feederuint8_t lane;	// lane within portuint8_t feederStatus = 0;	// initialized to invalid#ifdef SIMULATEuint8_t eeprom[16]; // simulates eeprom storage for read and write commands#endif/**  Feeder number to TX port mapping (uses D port numbers)*/
#if defined (BOARD96PIN)const uint8_t TXportPin[20] = { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2 };
#elif defined (BOARD4PIN)const uint8_t TXportPin[20] = { 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12 }; // Matches ref des order of board//const uint8_t TXportPin[20] = { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; // J20 is port 0, then goes counter-clockwise
#elif defined (BOARD_MEGA2560)// 官方arduino mega2560 R3 一共有70个数字IO(D0~D69), D0/D1 被编程串口占用了不能用. 剩下的可用数字IO位68个(D2~D69)// 其中D14/D15 = UART3, D16/D17 = UART2, D18/D19 = UART1, 这3个串口留着调试用, 剩下的可用数字IO为62个(D2~D13, D20~D69)   
const uint8_t TXportPin[FEEDER_CNT] = { // D2 => D31 was F1 => F3021, // pin = D21/*2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69*/};
#elseconst uint8_t TXportPin[20] = { 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 12, 14, 15, 16, 17, 18, 19, 20, 21 };
#endifbool inverse_logic = true;  // inverted logic for serial outputbool sendCommand(uint8_t command);  // sends a simple commandbool sendCommand(uint8_t command, uint8_t *dataBuf);  // sends a simple command, gets a response in dataBufbool sendCommand(uint8_t command, uint8_t *dataBuf, uint8_t offset);  // sends a simple command with extra byte (offset) after lane, gets a response in dataBufbool sendCommand(uint8_t command, uint8_t len, uint8_t *dataBuf);  // sends a simple command followed by data in dataBufbool receiveACK();bool receiveMessage(uint8_t *dataBuf);/** software serial routines - adapted from SendOnlySoftwareSerial by Nick Gammon 30th December 2016*/uint8_t _transmitBitMask;volatile uint8_t *_transmitPortRegister;uint8_t m_u8_tx_pin; // 具体是操作核心板哪个引出的管脚// Expressed as 4-cycle delays (must never be 0!)uint16_t _tx_delay;// private methodsvoid setTX(uint8_t transmitPin);// Return num - sub, or 1 if the result would be < 1static uint16_t subtract_cap(uint16_t num, uint16_t sub);// private static method for timingstatic inline void tunedDelay(uint16_t delay);public://store last timestamp command was sent for timeoutunsigned long lastTimeCommandSent;void setup(uint8_t _feeder_no, uint8_t port, uint8_t lane);bool sendPrePick();bool sendAdvance(bool overrideError);bool setPitch(uint8_t pitch);bool clearFeedCount();bool getFeederStatus();bool readEEPROM(uint8_t *buf);bool readInfo(uint8_t *buf);bool startSelfTest();bool stopSelfTest();bool setID(int32_t feederID);String reportStatus();String showStatus();bool feederIsOk();//software serialvoid begin(long speed);virtual size_t write(uint8_t byte);
};extern FeederClass Feeder;#endif

Feeder.cpp


#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <Arduino.h>
#include <util/delay_basic.h>#include "Feeder.h"
#include "config.h"void FeederClass::setup(uint8_t _feederNo, uint8_t port, uint8_t lane) {this->feederNo = _feederNo;this->port = port;this->lane = lane + 1;  // lanes are numbered starting from 1FeederClass::setTX(TXportPin[port]);FeederClass::begin(9600);  // serial baud rate#ifdef DEBUGSerial.print("FeederClass::setup(_feederNo = ");Serial.print(_feederNo);Serial.print(", port = ");Serial.print(port);Serial.print(", lane = ");Serial.print(lane);Serial.print("), TXportPin[port] = ");Serial.print(TXportPin[port]);Serial.print("), this->m_u8_tx_pin = ");Serial.println(this->m_u8_tx_pin);#endif
}bool FeederClass::receiveACK() {bool b_rc = false;uint8_t RXbuf[4];while (!Serial1.available()) {if ((millis() - this->lastTimeCommandSent) > ACK_TIMEOUT) {
#ifdef DEBUGSerial.println(" ACK timeout!");
#endifreturn false;}}Serial1.readBytes(RXbuf, 3);#ifdef DEBUGSerial.print("FeederClass::receiveACK() : recv : ");Serial.print(RXbuf[0], HEX);Serial.print(' ');Serial.print(RXbuf[1], HEX);Serial.print(' ');Serial.println(RXbuf[2], HEX);
#endifb_rc = ((RXbuf[0] == 1) && (RXbuf[1] == 0xE0) && (RXbuf[2] == 0xE1));
#ifdef DEBUGSerial.print("FeederClass::receiveACK() : b_rc = ");Serial.println(b_rc);
#endifreturn b_rc;
}bool FeederClass::receiveMessage(uint8_t *dataBuf) {uint8_t RXbuf[4];uint8_t inChar;uint8_t msgLen = 0;uint8_t RXckSum = 0;bool gotError = false;while (!Serial1.available()) {if ((millis() - this->lastTimeCommandSent) > RESP_TIMEOUT) {
#ifdef DEBUGSerial.println(" ack timeout!");
#endifreturn false;}}// expecting ack messageSerial1.readBytes(RXbuf, 3);#ifdef DEBUGSerial.print("FeederClass::receiveMessage() : recv : ");Serial.print(RXbuf[0], HEX);Serial.print(' ');Serial.print(RXbuf[1], HEX);Serial.print(' ');Serial.println(RXbuf[2], HEX);
#endifgotError = (RXbuf[0] != 1) || (RXbuf[1] != 0xE0) || (RXbuf[2] != 0xE1);// followed by response messagewhile (!Serial1.available()) {if ((millis() - this->lastTimeCommandSent) > RESP_TIMEOUT) {
#ifdef DEBUGSerial.println(" message timeout!");
#endifreturn false;}}// get message lengthinChar = (uint8_t)Serial1.read();msgLen = inChar + 1;
#ifdef DEBUGSerial.print("recv msgLen = ");Serial.println(msgLen);
#endifif ((msgLen > 1) && (msgLen < 64)) {  // valid message is 1 to 64 bytes, otherwise ignore itdataBuf[0] = inChar;                // store lengthSerial1.readBytes(&dataBuf[1], msgLen);#ifdef DEBUGSerial.print("recv(HEX) : ");for (uint8_t i = 0; i <= msgLen; i++) {Serial.print(dataBuf[i], HEX);Serial.print(" ");}Serial.println("");
#endif// verify checksumRXckSum = 0;for (uint8_t i = 0; i < msgLen; i++) {RXckSum += dataBuf[i];}if (RXckSum != dataBuf[msgLen]) {  // verify checksum
#ifdef DEBUGSerial.print(dataBuf[dataBuf[0] + 1], HEX);Serial.print(" != ");Serial.print(RXckSum, HEX);Serial.println(" Checksum failed!");
#endifgotError = true;}return !gotError;} else {
#ifdef DEBUGSerial.println("err : msgLen must > 1 && < 64");
#endifreturn false;}
}bool FeederClass::sendCommand(uint8_t command) {uint8_t cmdLen = 2;uint8_t buf[] = { cmdLen, command, this->lane, 0 };uint8_t i;uint8_t checksum = 0;while (Serial1.available()) {  // get rid of any leftover input dataSerial1.read();}// calculate checksumfor (i = 0; i < cmdLen + 1; i++) {checksum += buf[i];}buf[i] = checksum;#ifdef DEBUGSerial.print("sending to port[");Serial.print(this->port);Serial.print("], m_u8_tx_pin[");Serial.print(m_u8_tx_pin);Serial.print("] =>(HEX) ");for (i = 0; i < cmdLen + 2; i++) {Serial.print(buf[i], HEX);Serial.print(' ');}Serial.println();Serial1.write(command);
#endif#ifdef SIMULATEreturn true;
#endiffor (i = 0; i < cmdLen + 2; i++) {FeederClass::write(buf[i]);}this->lastTimeCommandSent = millis();return FeederClass::receiveACK();
}bool FeederClass::sendCommand(uint8_t command, uint8_t *dataBuf) {uint8_t cmdLen = 2;uint8_t buf[] = { cmdLen, command, this->lane, 0 };uint8_t i;uint8_t checksum = 0;while (Serial1.available()) {  // get rid of any leftover input dataSerial1.read();}// calculate checksumfor (i = 0; i < cmdLen + 1; i++) {checksum += buf[i];}buf[i] = checksum;#ifdef DEBUGSerial.print("sending to port[");Serial.print(this->port);Serial.print("], m_u8_tx_pin[");Serial.print(m_u8_tx_pin);Serial.print("] =>(HEX) ");for (i = 0; i < cmdLen + 2; i++) {Serial.print(buf[i], HEX);Serial.print(' ');}Serial.println();Serial1.write(command);
#endif#ifdef SIMULATEreturn true;
#endiffor (i = 0; i < cmdLen + 2; i++) {FeederClass::write(buf[i]);}this->lastTimeCommandSent = millis();return FeederClass::receiveMessage(dataBuf);
}bool FeederClass::sendCommand(uint8_t command, uint8_t *dataBuf, uint8_t offset) {uint8_t cmdLen = 3;uint8_t buf[] = { cmdLen, command, this->lane, offset, 0 };uint8_t i;uint8_t checksum = 0;while (Serial1.available()) {  // get rid of any leftover input dataSerial1.read();}// calculate checksumfor (i = 0; i < cmdLen + 1; i++) {checksum += buf[i];}buf[i] = checksum;#ifdef DEBUGSerial.print("sending to port[");Serial.print(this->port);Serial.print("], m_u8_tx_pin[");Serial.print(m_u8_tx_pin);Serial.print("] =>(HEX) ");for (i = 0; i < cmdLen + 2; i++) {Serial.print(buf[i], HEX);Serial.print(' ');}Serial.println();Serial1.write(command);
#endif#ifdef SIMULATEreturn true;
#endiffor (i = 0; i < cmdLen + 2; i++) {FeederClass::write(buf[i]);}this->lastTimeCommandSent = millis();return FeederClass::receiveMessage(dataBuf);
}bool FeederClass::sendCommand(uint8_t command, uint8_t dataLen, uint8_t *data) {uint8_t msgLen = dataLen + 2;uint8_t buf[msgLen + 2];uint8_t i;uint8_t checksum = 0;buf[0] = msgLen;buf[1] = command;buf[2] = this->lane;for (i = 0; i < dataLen; i++) {buf[i + 3] = data[i];}// calculate checksumfor (i = 0; i < msgLen + 1; i++) {checksum += buf[i];}buf[i] = checksum;#ifdef DEBUG// Serial.println(checksum, HEX);Serial.print("sending to port[");Serial.print(this->port);Serial.print("], m_u8_tx_pin[");Serial.print(m_u8_tx_pin);Serial.print("] =>(HEX) ");for (i = 0; i < msgLen + 2; i++) {Serial.print(buf[i], HEX);Serial.print(' ');}Serial.println();
#endifwhile (Serial1.available()) {  // get rid of any leftover input dataSerial1.read();}#ifdef SIMULATEfor (i = 0; i < 16; i++) {this->eeprom[i] = buf[i + 3];}return true;
#endiffor (i = 0; i < msgLen + 2; i++) {FeederClass::write(buf[i]);}this->lastTimeCommandSent = millis();return FeederClass::receiveACK();
}bool FeederClass::sendPrePick() {uint8_t dataBuf[6];#ifdef DEBUGSerial.println("send Pre-Pick command");
#endifif (!FeederClass::sendCommand(CMD_PRE_PICK, dataBuf)) {
#ifdef DEBUGSerial.println("No ACK from feeder");
#endifreturn false;}
#ifdef DEBUGSerial.println("Received ACK, check status");
#endif#ifdef SIMULATEthis->feederStatus = STATUS_OK;
#elsethis->feederStatus = dataBuf[1];  // should verify that byte 2 matches the lane?
#ifdef DEBUGSerial.print("feederStatus(dataBuf[1]) = 0x");Serial.println(this->feederStatus, HEX);Serial.println(this->showStatus());
#endif
#endifreturn true;
}bool FeederClass::sendAdvance(bool overrideError) {if (this->feederStatus == STATUS_INVALID) {  // need to read status from feeder if it is not up to dateFeederClass::getFeederStatus();}#ifdef DEBUGSerial.println("advance triggered");Serial.println(this->showStatus());
#endif//check whether feeder is OK before every advance commandif (this->feederStatus != STATUS_OK) {//feeder is in error state, usually this would lead to exit advance with false and no advancing command sentif (!overrideError) {
//error, and error was not overridden -> return false, advance not successful
#ifdef DEBUGSerial.println("error, and error was not overridden -> return false, advance not successful");
#endifreturn false;} else {
#ifdef DEBUGSerial.println("overridden error temporarily");
#endif}}
#ifdef DEBUGSerial.println("send advance command");
#endif#ifdef SIMULATEif (++this->eeprom[2] == 0) ++this->eeprom[3];
#endifif (!FeederClass::sendCommand(CMD_ADVANCE)) {
#ifdef DEBUGSerial.println("No ACK from feeder");
#endifreturn false;}return true;
}bool FeederClass::setPitch(uint8_t pitch) {uint8_t dataBuf[22];for (uint8_t i = 0; i < 22; i++) {dataBuf[i] = 0;}#ifdef DEBUGSerial.print("Set pitch to ");Serial.println(pitch);
#endifdataBuf[0] = pitch;dataBuf[1] = 0;if (!FeederClass::sendCommand(CMD_SET_PITCH, 1, dataBuf)) {
#ifdef DEBUGSerial.println("No ACK from feeder");
#endifreturn false;}// update pitch field in eeprom//   Read current EEPROM data
#ifdef DEBUGSerial.println("1. send read EEPROM command");
#endifif (!FeederClass::sendCommand(CMD_EEPROM_READ, dataBuf, 0)) {
#ifdef DEBUGSerial.println("No ACK from feeder");
#endifreturn false;}#ifdef SIMULATEfor (uint8_t i = 0; i < 16; i++) {dataBuf[i + 1] = this->eeprom[i];}
#endiffor (uint8_t i = 1; i < 17; i++) {dataBuf[i] = dataBuf[i + 3];}dataBuf[0] = 0;dataBuf[5] = pitch;  // pitch byte#ifdef DEBUGSerial.println("send write EEPROM command");
#endif#ifdef DEBUGSerial.print("EEPROM: ");for (uint8_t i = 0; i < 17; i++) {Serial.print(dataBuf[i], HEX);Serial.print(' ');}Serial.println();
#endifif (!FeederClass::sendCommand(CMD_EEPROM_WRITE, 17, dataBuf)) {
#ifdef DEBUGSerial.println("No ACK from feeder");
#endifreturn false;}#ifdef SIMULATEfor (uint8_t i = 0; i < 16; i++) {this->eeprom[i] == dataBuf[i + 1];}
#endifreturn true;
}bool FeederClass::feederIsOk() {if (this->feederStatus == STATUS_OK) {return true;} else {return false;}
}bool FeederClass::getFeederStatus() {int i = 0;uint8_t dataBuf[6];
#ifdef DEBUGSerial.println("send status command(getFeederStatus)");
#endiffor (i = 0; i < 6; i++) {dataBuf[i] = 0;}if (!FeederClass::sendCommand(CMD_STATUS, dataBuf)) {
#ifdef DEBUGSerial.println("No ACK from feeder");
#endifreturn false;}#ifdef DEBUGSerial.print("getFeederStatus(), ACK from feeder(HEX) : ");for (i = 0; i < 6; i++) {Serial.print(dataBuf[i], HEX);Serial.print(" ");}Serial.println("");Serial.print("feederStatus is dataBuf[1] = 0x");Serial.print(dataBuf[1], HEX);Serial.println(" ");
#endif#ifdef SIMULATEthis->feederStatus = STATUS_OK;
#elsethis->feederStatus = dataBuf[1];  // should verify that byte 2 matches the lane?
#endifreturn true;
}bool FeederClass::readEEPROM(uint8_t *dataBuf) {
#ifdef DEBUGSerial.println("2. send read EEPROM command");
#endifif (!FeederClass::sendCommand(CMD_EEPROM_READ, dataBuf, 0)) {
#ifdef DEBUGSerial.println("No ACK from feeder");
#endifreturn false;}#ifdef SIMULATEfor (uint8_t i = 0; i < 16; i++) {dataBuf[i] = this->eeprom[i];}
#elseuint8_t len = dataBuf[0];#ifdef DEBUGSerial.print("len = ");Serial.println(len);#endifuint8_t i = 0;// len = 2时, 没进下面这个循环for (; i < len - 4; i++) {dataBuf[i] = dataBuf[i + 4];}dataBuf[i] = 0;
#endifreturn true;
}bool FeederClass::readInfo(uint8_t *dataBuf) {
#ifdef DEBUGSerial.println("send read info command");
#endifif (!FeederClass::sendCommand(CMD_INFO, dataBuf)) {
#ifdef DEBUGSerial.println("No ACK from feeder");
#endifreturn false;}return true;
}bool FeederClass::clearFeedCount() {uint8_t dataBuf[22];// Read current EEPROM data
#ifdef DEBUGSerial.println("3. send read EEPROM command");
#endifif (!FeederClass::sendCommand(CMD_EEPROM_READ, dataBuf, 0)) {
#ifdef DEBUGSerial.println("No ACK from feeder");
#endifreturn false;}#ifdef SIMULATEfor (uint8_t i = 0; i < 16; i++) {dataBuf[i + 1] = this->eeprom[i];}
#endiffor (uint8_t i = 1; i < 17; i++) {dataBuf[i] = dataBuf[i + 3];}dataBuf[0] = 0;dataBuf[3] = 0;  // count low bytedataBuf[4] = 0;  // count mid bytedataBuf[6] = 0;  // count high byte
#ifdef DEBUGSerial.println("send write EEPROM command");
#endifif (!FeederClass::sendCommand(CMD_EEPROM_WRITE, 17, dataBuf)) {
#ifdef DEBUGSerial.println("No ACK from feeder");
#endifreturn false;}#ifdef SIMULATEfor (uint8_t i = 0; i < 16; i++) {this->eeprom[i] == dataBuf[i + 1];}
#endifreturn true;
}bool FeederClass::setID(int32_t feederID) {uint8_t dataBuf[22];for (uint8_t i = 0; i < 22; i++) {dataBuf[i] = 0;}if (this->lane == 1) {dataBuf[1] = feederID & 0xFF;         // low byte of IDdataBuf[2] = (feederID >> 8) & 0xFF;  // high byte of IDdataBuf[7] = 0x31;dataBuf[8] = 1;} else {dataBuf[8] = 0x3c;}#ifdef DEBUGSerial.println("send write EEPROM command");
#endifif (!FeederClass::sendCommand(CMD_EEPROM_WRITE, 17, dataBuf)) {
#ifdef DEBUGSerial.println("No ACK from feeder");
#endifreturn false;}#ifdef SIMULATEfor (uint8_t i = 0; i < 16; i++) {this->eeprom[i] == dataBuf[i + 1];}
#endifreturn true;
}String FeederClass::reportStatus() {FeederClass::getFeederStatus();return FeederClass::showStatus();
}String FeederClass::showStatus() {switch (this->feederStatus) {case STATUS_OK:return "getFeederStatus: feeder OK";break;case STATUS_INVALID:return "getFeederStatus: invalid, status not updated";break;case STATUS_NO_TAPE_TENSION:return "getFeederStatus: No tape tension.  Tape may be broken";break;case STATUS_NO_TAPE_TRIGGER:return "getFeederStatus: Tape take-up not triggered after multiple feeds";break;case STATUS_FEED_ERROR:return "getFeederStatus: Feed motor did not advance";break;default:char statusCode[34];sprintf(statusCode, "Unrecognized status code %02X", this->feederStatus);return statusCode;}
}bool FeederClass::startSelfTest() {uint8_t dataBuf[2];#ifdef DEBUGSerial.println("send self test command");
#endifdataBuf[0] = 5;dataBuf[1] = 0;if (!FeederClass::sendCommand(CMD_SELF_TEST, 1, dataBuf)) {
#ifdef DEBUGSerial.println("No ACK from feeder");
#endifreturn false;} else {return true;}
}bool FeederClass::stopSelfTest() {uint8_t dataBuf[2];#ifdef DEBUGSerial.println("send stop self test command");
#endifdataBuf[0] = 7;dataBuf[1] = 0;if (!FeederClass::sendCommand(CMD_SELF_TEST, 1, dataBuf)) {
#ifdef DEBUGSerial.println("No ACK from feeder");
#endifreturn false;} else {return true;}
}void FeederClass::setTX(uint8_t tx) {// First write, then set output. If we do this the other way around,// the pin would be output low for a short while before switching to// output high. Now, it is input with pullup for a short while, which// is fine. With inverse logic, either order is fine.digitalWrite(tx, this->inverse_logic ? LOW : HIGH);pinMode(tx, OUTPUT);this->_transmitBitMask = digitalPinToBitMask(tx);uint8_t port = digitalPinToPort(tx);this->_transmitPortRegister = portOutputRegister(port);this->m_u8_tx_pin = tx;
}uint16_t FeederClass::subtract_cap(uint16_t num, uint16_t sub) {if (num > sub)return num - sub;elsereturn 1;
}/* static */
inline void FeederClass::tunedDelay(uint16_t delay) {_delay_loop_2(delay);
}void FeederClass::begin(long speed) {this->_tx_delay = 0;// Precalculate the various delays, in number of 4-cycle delaysuint16_t bit_delay = (F_CPU / speed) / 4;// 12 (gcc 4.8.2) or 13 (gcc 4.3.2) cycles from start bit to first bit,// 15 (gcc 4.8.2) or 16 (gcc 4.3.2) cycles between bits,// 12 (gcc 4.8.2) or 14 (gcc 4.3.2) cycles from last bit to stop bit// These are all close enough to just use 15 cycles, since the inter-bit// timings are the most critical (deviations stack 8 times)this->_tx_delay = subtract_cap(bit_delay, 15 / 4);
}size_t FeederClass::write(uint8_t b) {// By declaring these as local variables, the compiler will put them// in registers _before_ disabling interrupts and entering the// critical timing sections below, which makes it a lot easier to// verify the cycle timingsvolatile uint8_t *reg = this->_transmitPortRegister;uint8_t reg_mask = this->_transmitBitMask;uint8_t inv_mask = ~this->_transmitBitMask;uint8_t oldSREG = SREG;bool inv = this->inverse_logic;uint16_t delay = this->_tx_delay;if (inv)b = ~b;cli();  // turn off interrupts for a clean txmit// Write the start bitif (inv)*reg |= reg_mask;else*reg &= inv_mask;tunedDelay(delay);// Write each of the 8 bitsfor (uint8_t i = 8; i > 0; --i) {if (b & 1)           // choose bit*reg |= reg_mask;  // send 1else*reg &= inv_mask;  // send 0tunedDelay(delay);b >>= 1;}// restore pin to natural stateif (inv)*reg &= inv_mask;else*reg |= reg_mask;SREG = oldSREG;  // turn interrupts back ontunedDelay(delay);return 1;
}

gcode.ino

#include "config.h"String inputBuffer[] = { "", "", "", "" };  // Buffer for incoming G-Code lines
int bufPtr = 0;/**
* Look for character /code/ in the inputBuffer and read the float that immediately follows it.
* @return the value found.  If nothing is found, /defaultVal/ is returned.
* @input code the character to look for.
* @input defaultVal the return value if /code/ is not found.
**/
float parseParameter(String inBuf, char code, float defaultVal) {int codePosition = inBuf.indexOf(code);if (codePosition != -1) {//code found in buffer//find end of number (separated by " " (space))int delimiterPosition = inBuf.indexOf(" ", codePosition + 1);float parsedNumber = inBuf.substring(codePosition + 1, delimiterPosition).toFloat();return parsedNumber;} else {return defaultVal;}
}void setupGCodeProc() {for (int i = 0; i < 4; i++) {inputBuffer[i].reserve(MAX_BUFFFER_MCODE_LINE);}
}void sendAnswer(uint8_t error, String message) {switch (error) {case 0:{Serial.print("ok ");Serial.println(message);break;}case 1:{Serial.print("error ");Serial.println(message);break;}case 2:{Serial.println(message);Serial.println("ok ");break;}}
}bool validFeederNo(int8_t signedFeederNo, uint8_t feederNoMandatory = 0) {bool b_rc = false;do {if (signedFeederNo == -1 && feederNoMandatory >= 1) {//no number given (-1) but it is mandatory.break;} else {//state now: number is given, check for valid rangeif (signedFeederNo < 0 || signedFeederNo > (NUMBER_OF_FEEDERS - 1)) {//error, number not in a valid rangebreak;} else {//valid numberb_rc = true;break;}}} while (0);if (!b_rc) {
#ifdef DEBUGSerial.print("signedFeederNo valid range = [0 ~ ");Serial.print((NUMBER_OF_FEEDERS - 1));Serial.println("]");
#endif}return b_rc;
}bool validPitch(int8_t pitch, uint8_t pitchMandatory = 0) {if (pitch == -1 && pitchMandatory >= 1) {//no number given (-1) but it is mandatory.return false;} else {//state now: number is given, check for valid rangeif (pitch < 0 || pitch > 1) {//error, number not in a valid rangereturn false;} else {//valid numberreturn true;}}
}bool validFeederID(int32_t signedFeederID, uint8_t feederIDMandatory = 0) {if (signedFeederID == -1 && feederIDMandatory >= 1) {//no number given (-1) but it is mandatory.return false;} else {//state now: number is given, check for valid rangeif (signedFeederID < 0 || signedFeederID > 65535) {//error, number not in a valid rangereturn false;} else {//valid numberreturn true;}}
}/**
* Read the input buffer and find any recognized commands.  One G or M command per line.
*/
void processCommand(String cmdBuf) {//get the command, default -1 if no command foundint cmd = parseParameter(cmdBuf, 'M', -1);#ifdef DEBUG1Serial.print("command found: M");Serial.println(cmd);
#endifswitch (cmd) {// M115case MCODE_DRIVER_INFO:{
#ifdef DEBUGSerial.println("cmdproc : MCODE_DRIVER_INFO");
#endifsendAnswer(2, "FIRMWARE_NAME: " PRJ_NAME ", FIRMWARE_VERSION: " PRJ_VER);break;}/*FEEDER-CODES*/case MCODE_PRE_PICK:{
#ifdef DEBUGSerial.println("cmdproc : MCODE_PRE_PICK");
#endifint8_t signedFeederNo = (int)parseParameter(cmdBuf, 'N', -1);#ifdef DEBUGSerial.print("signedFeederNo = ");Serial.println(signedFeederNo);
#endif//check for presence of a mandatory FeederNoif (!validFeederNo(signedFeederNo, 1)) {sendAnswer(1, "feederNo missing or invalid");break;}//send pre pick commandif (!feeders[signedFeederNo].sendPrePick()) {sendAnswer(1, "No acknowledge from feeder");} else {sendAnswer(0, "Shutter opened");}break;}case MCODE_ADVANCE:{
#ifdef DEBUGSerial.println("cmdproc : MCODE_ADVANCE");
#endifint8_t signedFeederNo = (int)parseParameter(cmdBuf, 'N', -1);
#ifdef DEBUGSerial.print("signedFeederNo = ");Serial.println(signedFeederNo);
#endifint8_t overrideErrorRaw = (int)parseParameter(cmdBuf, 'X', -1);
#ifdef DEBUGSerial.print("overrideErrorRaw = ");Serial.println(overrideErrorRaw);
#endifbool overrideError = false;if (overrideErrorRaw >= 1) {overrideError = true;
#ifdef DEBUGSerial.println("Argument X1 found, error will be ignored");
#endif}//check for presence of a mandatory FeederNoif (!validFeederNo(signedFeederNo, 1)) {sendAnswer(1, "feederNo missing or invalid");break;}//send feed commandif (!feeders[signedFeederNo].sendAdvance(overrideError)) {sendAnswer(1, "Unable to advance tape");} else {sendAnswer(0, "Tape advanced");}break;}case MCODE_FEEDER_STATUS:{#ifdef DEBUGSerial.println("cmdproc : MCODE_FEEDER_STATUS");
#endifint8_t signedFeederNo = (int)parseParameter(cmdBuf, 'N', -1);#ifdef DEBUGSerial.print("signedFeederNo = ");Serial.println(signedFeederNo);
#endif//check for presence of FeederNoif (!validFeederNo(signedFeederNo, 1)) {sendAnswer(1, "feederNo missing or invalid");break;}sendAnswer(0, feeders[signedFeederNo].reportStatus());break;}case MCODE_GET_FEED_COUNT:{
#ifdef DEBUGSerial.println("cmdproc : MCODE_GET_FEED_COUNT");
#endifint8_t signedFeederNo = (int)parseParameter(cmdBuf, 'N', -1);
#ifdef DEBUGSerial.print("signedFeederNo = ");Serial.println(signedFeederNo);
#endif//check for presence of FeederNoif (!validFeederNo(signedFeederNo, 1)) {sendAnswer(1, "feederNo missing or invalid");break;}uint8_t inBuf[24];for (int i = 0; i < 24; i++) {inBuf[i] = 0;}if (feeders[signedFeederNo].readEEPROM(inBuf)) {
#ifdef DEBUGSerial.print("inBuf(HEX) = [");for (int i = 0; i < 24; i++) {Serial.print(inBuf[i], HEX);Serial.print(" ");}Serial.println("]");
#endifuint32_t counth = inBuf[5];counth <<= 16;uint32_t countm = inBuf[3];countm <<= 8;uint32_t count = counth + countm + inBuf[2];char countStr[22];sprintf(countStr, "Feed count: %lu", count);sendAnswer(0, countStr);} else {sendAnswer(1, " no response from feeder");}break;}case MCODE_CLEAR_FEED_COUNT:{
#ifdef DEBUGSerial.println("cmdproc : MCODE_CLEAR_FEED_COUNT");
#endifint8_t signedFeederNo = (int)parseParameter(cmdBuf, 'N', -1);
#ifdef DEBUGSerial.print("signedFeederNo = ");Serial.println(signedFeederNo);
#endif//check for presence of FeederNoif (!validFeederNo(signedFeederNo, 1)) {sendAnswer(1, "feederNo missing or invalid");break;}if (!feeders[signedFeederNo].clearFeedCount()) {sendAnswer(1, "Unable to clear feed count");} else {sendAnswer(0, "Feed count cleared");}break;}case MCODE_GET_ERR42_COUNT:{
#ifdef DEBUGSerial.println("cmdproc : MCODE_GET_ERR42_COUNT");
#endifint8_t signedFeederNo = (int)parseParameter(cmdBuf, 'N', -1);
#ifdef DEBUGSerial.print("signedFeederNo = ");Serial.println(signedFeederNo);
#endif//check for presence of FeederNoif (!validFeederNo(signedFeederNo, 1)) {sendAnswer(1, "feederNo missing or invalid");break;}uint8_t inBuf[24];for (int i = 0; i < 24; i++) {inBuf[i] = 0;}if (feeders[signedFeederNo].readEEPROM(inBuf)) {uint16_t count = inBuf[12] & 0x0f;count <<= 8;count += inBuf[8];char countStr[22];sprintf(countStr, "Error 42 count: %u", count);sendAnswer(0, countStr);} else {sendAnswer(1, " no response from feeder");}break;}case MCODE_GET_ERR43_COUNT:{
#ifdef DEBUGSerial.println("cmdproc : MCODE_GET_ERR43_COUNT");
#endifint8_t signedFeederNo = (int)parseParameter(cmdBuf, 'N', -1);
#ifdef DEBUGSerial.print("signedFeederNo = ");Serial.println(signedFeederNo);
#endif//check for presence of FeederNoif (!validFeederNo(signedFeederNo, 1)) {sendAnswer(1, "feederNo missing or invalid");break;}uint8_t inBuf[24];for (int i = 0; i < 24; i++) {inBuf[i] = 0;}if (feeders[signedFeederNo].readEEPROM(inBuf)) {uint16_t count = inBuf[12] & 0xf0;count <<= 4;count += inBuf[9];char countStr[22];sprintf(countStr, "Error 43 count: %u", count);sendAnswer(0, countStr);} else {sendAnswer(1, " no response from feeder");}break;}case MCODE_GET_ERR44_COUNT:{
#ifdef DEBUGSerial.println("cmdproc : MCODE_GET_ERR44_COUNT");
#endifint8_t signedFeederNo = (int)parseParameter(cmdBuf, 'N', -1);
#ifdef DEBUGSerial.print("signedFeederNo = ");Serial.println(signedFeederNo);
#endif//check for presence of FeederNoif (!validFeederNo(signedFeederNo, 1)) {sendAnswer(1, "feederNo missing or invalid");break;}uint8_t inBuf[24];for (int i = 0; i < 24; i++) {inBuf[i] = 0;}if (feeders[signedFeederNo].readEEPROM(inBuf)) {uint16_t count = inBuf[13] & 0x0f;count <<= 8;count += inBuf[10];char countStr[22];sprintf(countStr, "Error 44 count: %u", count);sendAnswer(0, countStr);} else {sendAnswer(1, " no response from feeder");}break;}case MCODE_GET_RESET_COUNT:{
#ifdef DEBUGSerial.println("cmdproc : MCODE_GET_RESET_COUNT");
#endifint8_t signedFeederNo = (int)parseParameter(cmdBuf, 'N', -1);
#ifdef DEBUGSerial.print("signedFeederNo = ");Serial.println(signedFeederNo);
#endif//check for presence of FeederNoif (!validFeederNo(signedFeederNo, 1)) {sendAnswer(1, "feederNo missing or invalid");break;}if (signedFeederNo % 2 != 0) {  // Reset count is only in the lane 1 field, so need to adjust if it is lane 2signedFeederNo--;}uint8_t inBuf[24];for (int i = 0; i < 24; i++) {inBuf[i] = 0;}if (feeders[signedFeederNo].readEEPROM(inBuf)) {uint16_t count = inBuf[13] & 0xf0;count <<= 4;count += inBuf[11];char countStr[22];sprintf(countStr, "Reset count: %u", count);sendAnswer(0, countStr);} else {sendAnswer(1, " no response from feeder");}break;}case MCODE_READ_EEPROM:{
#ifdef DEBUGSerial.println("cmdproc : MCODE_READ_EEPROM");
#endifint8_t signedFeederNo = (int)parseParameter(cmdBuf, 'N', -1);
#ifdef DEBUGSerial.print("signedFeederNo = ");Serial.println(signedFeederNo);
#endif//check for presence of FeederNoif (!validFeederNo(signedFeederNo, 1)) {sendAnswer(1, "feederNo missing or invalid");break;}uint8_t inBuf[24];for (int i = 0; i < 24; i++) {inBuf[i] = 0;}if (feeders[signedFeederNo].readEEPROM(inBuf)) {char hex[50];sprintf(hex, "%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X ", inBuf[0], inBuf[1], inBuf[2], inBuf[3], inBuf[4], inBuf[5], inBuf[6], inBuf[7], inBuf[8], inBuf[9], inBuf[10], inBuf[11], inBuf[12], inBuf[13], inBuf[14], inBuf[15]);sendAnswer(0, hex);} else {sendAnswer(1, " no response from feeder");}break;}case MCODE_GET_FEEDER_ID:{
#ifdef DEBUGSerial.println("cmdproc : MCODE_GET_FEEDER_ID");
#endifint8_t signedFeederNo = (int)parseParameter(cmdBuf, 'N', -1);
#ifdef DEBUGSerial.print("signedFeederNo = ");Serial.println(signedFeederNo);
#endif//check for presence of FeederNoif (!validFeederNo(signedFeederNo, 1)) {sendAnswer(1, "feederNo missing or invalid");break;}uint8_t inBuf[24];for (int i = 0; i < 24; i++) {inBuf[i] = 0;}bool left = true;if (signedFeederNo % 2 != 0) {  // ID is only in the lane 1 field, so need to adjust if it is lane 2left = false;signedFeederNo--;}if (feeders[signedFeederNo].readEEPROM(inBuf)) {uint16_t id = (inBuf[1] << 8) + inBuf[0];char idStr[22];sprintf(idStr, "ID: %u%s", id, (left ? "L" : "R"));sendAnswer(0, idStr);} else {sendAnswer(1, " no response from feeder");}break;}case MCODE_SET_FEEDER_ID:{
#ifdef DEBUGSerial.println("cmdproc : MCODE_SET_FEEDER_ID");
#endifint8_t signedFeederNo = (int)parseParameter(cmdBuf, 'N', -1);
#ifdef DEBUGSerial.print("signedFeederNo = ");Serial.println(signedFeederNo);
#endifint32_t newFeederID = (int)parseParameter(cmdBuf, 'X', -1);//check for presence of FeederNoif (!validFeederNo(signedFeederNo, 1)) {sendAnswer(1, "feederNo missing or invalid");break;}//must be run from lane 1 of feeder, so adjust.if ((signedFeederNo % 2) != 0) {--signedFeederNo;}//check for presence of FeederIDif (!validFeederID(newFeederID, 1)) {sendAnswer(1, "feederID missing or invalid");break;}if (feeders[signedFeederNo].setID(newFeederID)) {if (feeders[signedFeederNo + 1].setID(0)) {  // need to clear 2nd lane alsosendAnswer(0, "ID set");} else {sendAnswer(1, " no response from feeder");}} else {sendAnswer(1, " no response from feeder");}break;}case MCODE_GET_PITCH:{
#ifdef DEBUGSerial.println("cmdproc : MCODE_GET_PITCH");
#endifint8_t signedFeederNo = (int)parseParameter(cmdBuf, 'N', -1);
#ifdef DEBUGSerial.print("signedFeederNo = ");Serial.println(signedFeederNo);
#endif//check for presence of FeederNoif (!validFeederNo(signedFeederNo, 1)) {sendAnswer(1, "feederNo missing or invalid");break;}uint8_t inBuf[24];for (int i = 0; i < 24; i++) {inBuf[i] = 0;}if (feeders[signedFeederNo].readEEPROM(inBuf)) {char pitch[6];sprintf(pitch, "%d MM", (inBuf[4] == 0 ? 2 : 4));sendAnswer(0, pitch);} else {sendAnswer(1, " no response from feeder");}break;}case MCODE_TOGGLE_PITCH:{
#ifdef DEBUGSerial.println("cmdproc : MCODE_TOGGLE_PITCH");
#endifint8_t signedFeederNo = (int)parseParameter(cmdBuf, 'N', -1);
#ifdef DEBUGSerial.print("signedFeederNo = ");Serial.println(signedFeederNo);
#endifint8_t pitch;//check for presence of FeederNoif (!validFeederNo(signedFeederNo, 1)) {sendAnswer(1, "feederNo missing or invalid");break;}uint8_t inBuf[24];for (int i = 0; i < 24; i++) {inBuf[i] = 0;}if (feeders[signedFeederNo].readEEPROM(inBuf)) {if (inBuf[4] == 0) {pitch = 1;} else {pitch = 0;}//send set pitch commandif (!feeders[signedFeederNo].setPitch(pitch)) {sendAnswer(1, "Unable to set pitch");} else {sendAnswer(0, "Pitch set");}} else {sendAnswer(1, " no response from feeder");}break;}case MCODE_GET_FIRMWARE_INFO:{
#ifdef DEBUGSerial.println("cmdproc : MCODE_GET_FIRMWARE_INFO");
#endifint8_t signedFeederNo = (int)parseParameter(cmdBuf, 'N', -1);
#ifdef DEBUGSerial.print("signedFeederNo = ");Serial.println(signedFeederNo);
#endif//check for presence of FeederNoif (!validFeederNo(signedFeederNo, 1)) {sendAnswer(1, "feederNo missing or invalid");break;}uint8_t inBuf[64];for (int i = 0; i < 64; i++) {inBuf[i] = 0;}if (feeders[signedFeederNo].readInfo(inBuf)) {char version[25];sprintf(version, "Firmware version %d.%d", inBuf[2], inBuf[3]);sendAnswer(0, version);} else {sendAnswer(1, " no response from feeder");}break;}case MCODE_START_SELF_TEST:{bool b_rc = false;
#ifdef DEBUGSerial.println("cmdproc : MCODE_START_SELF_TEST");
#endifint8_t signedFeederNo = (int)parseParameter(cmdBuf, 'N', -1);
#ifdef DEBUGSerial.print("signedFeederNo = ");Serial.println(signedFeederNo);
#endif//check for presence of a mandatory FeederNoif (!validFeederNo(signedFeederNo, 1)) {sendAnswer(1, "feederNo missing or invalid");break;}//send self test commandb_rc = feeders[signedFeederNo].startSelfTest();
#ifdef DEBUGSerial.print("startSelfTest() return b_rc = ");Serial.println(b_rc);
#endifif (!b_rc) {sendAnswer(1, "err: No acknowledge from feeder");} else {sendAnswer(0, "ok: Self test started");}break;}case MCODE_STOP_SELF_TEST:{
#ifdef DEBUGSerial.println("cmdproc : MCODE_STOP_SELF_TEST");
#endifint8_t signedFeederNo = (int)parseParameter(cmdBuf, 'N', -1);
#ifdef DEBUGSerial.print("signedFeederNo = ");Serial.println(signedFeederNo);
#endif//check for presence of a mandatory FeederNoif (!validFeederNo(signedFeederNo, 1)) {sendAnswer(1, "feederNo missing or invalid");break;}//send self test commandif (!feeders[signedFeederNo].stopSelfTest()) {sendAnswer(1, "No acknowledge from feeder");} else {sendAnswer(0, "Self test stopped");}break;}case MCODE_CMD_HELP:{Serial.println("command list below:");Serial.println("M115 MCODE_DRIVER_INFO ");Serial.println("M600 MCODE_PRE_PICK ");Serial.println("M601 MCODE_ADVANCE e.g. M601N0X1 is ok, M601N0X0 is err");Serial.println("M602 MCODE_FEEDER_STATUS ");Serial.println("M603 MCODE_GET_FEED_COUNT ");Serial.println("M623 MCODE_CLEAR_FEED_COUNT ");Serial.println("M604 MCODE_GET_ERR42_COUNT ");Serial.println("M605 MCODE_GET_ERR43_COUNT ");Serial.println("M606 MCODE_GET_ERR44_COUNT ");Serial.println("M607 MCODE_GET_RESET_COUNT ");Serial.println("M608 MCODE_GET_PITCH ");Serial.println("M628 MCODE_TOGGLE_PITCH ");Serial.println("M610 MCODE_GET_FEEDER_ID ");Serial.println("M640 MCODE_SET_FEEDER_ID ");Serial.println("M630 MCODE_READ_EEPROM ");Serial.println("M615 MCODE_GET_FIRMWARE_INFO ");Serial.println("M650 MCODE_START_SELF_TEST ");Serial.println("M651 MCODE_STOP_SELF_TEST ");Serial.println("M999 MCODE_CMD_HELP ");}break;default:sendAnswer(0, "unknown or empty command ignored");break;}
}void listenToSerialStream() {while (Serial.available()) {// get the received byte, convert to char for adding to bufferchar receivedChar = (char)Serial.read();// print back for debugging//#ifdef DEBUGSerial.print(receivedChar);//#endif// add to bufferinputBuffer[bufPtr] += receivedChar;// if the received character is a newline, processCommandif (receivedChar == '\n') {
#ifdef DEBUGSerial.print("buffer ");Serial.println(bufPtr);
#endifint curBuf = bufPtr;// switch to next buffer while processing this oneif (++bufPtr > 3)bufPtr = 0;//remove commentsinputBuffer[curBuf].remove(inputBuffer[curBuf].indexOf(";"));inputBuffer[curBuf].trim();processCommand(inputBuffer[curBuf]);//clear bufferinputBuffer[curBuf] = "";}}
}

飞达上物料编带正常装入后的通讯测试

飞达上物料编带已经安装好了(openpnp - 二手西门子电动飞达 - 物料编带安装的正确姿势), 再测试一下通讯, 看看和空飞达有何不同

此时的固件版本中, 已经去掉了DEBUG宏.
启动后, DTR生效, 开机信息如下:

my_SchultzController v2.0.1.2_build_2023_0909_1333 starting...
my_SchultzController v2.0.1.2_build_2023_0909_1333 ready.

查看可用命令列表

M999
command list below:
M115 MCODE_DRIVER_INFO 
M600 MCODE_PRE_PICK 
M601 MCODE_ADVANCE e.g. M601N0X1 is ok, M601N0X0 is err
M602 MCODE_FEEDER_STATUS 
M603 MCODE_GET_FEED_COUNT 
M623 MCODE_CLEAR_FEED_COUNT 
M604 MCODE_GET_ERR42_COUNT 
M605 MCODE_GET_ERR43_COUNT 
M606 MCODE_GET_ERR44_COUNT 
M607 MCODE_GET_RESET_COUNT 
M608 MCODE_GET_PITCH 
M628 MCODE_TOGGLE_PITCH 
M610 MCODE_GET_FEEDER_ID 
M640 MCODE_SET_FEEDER_ID 
M630 MCODE_READ_EEPROM 
M615 MCODE_GET_FIRMWARE_INFO 
M650 MCODE_START_SELF_TEST 
M651 MCODE_STOP_SELF_TEST 
M999 MCODE_CMD_HELP 

查看固件版本

M115
FIRMWARE_NAME: my_SchultzController, FIRMWARE_VERSION: v2.0.1.2_build_2023_0909_1333
ok 

送料之前

M600 N0
ok Shutter opened
M600 N1
ok Shutter opened

先进模式

M601 N0X0
ok Tape advanced
M601 N1X0
ok Tape advanced
M601 N0X1
ok Tape advanced
M601 N1X1
ok Tape advanced
M601 N1
ok Tape advanced
M601 N0
ok Tape advanced

看起来先进送料模式不需要NX后的XX参数.
执行M601 NX后, 直接送料并拉皮.

取飞达状态

M602 N0
ok getFeederStatus: feeder OK
M602 N1
ok getFeederStatus: feeder OK

只要飞达还好的, 而且物料编带正常载入, 飞达状态就是OK.

取飞达总送料计数

M603 N0
ok Feed count: 206
M603 N1
ok Feed count: 307

清除飞达计数

M623 N0
ok Feed count cleared
M623 N1
ok Feed count cleared// 再取一次飞达计数, 已经变为0了.
M603 N0
ok Feed count: 0
M603 N1
ok Feed count: 0

取0x42错误计数

M604 N0
ok Error 42 count: 13
M604 N1
ok Error 42 count: 5

取0x43错误计数

M605 N0
ok Error 43 count: 0
M605 N1
ok Error 43 count: 2

取0x44错误计数

M606 N0
ok Error 44 count: 0
M606 N1
ok Error 44 count: 0

取飞达复位计数

M607 N0
ok Reset count: 4
M607 N1
ok Reset count: 4

取物料步进值

M608 N0
ok 2 MM
M608 N1
ok 4 MM

切换物料步进值

都改成4mm

M628 N0
ok Pitch set
M628 N1
ok Pitch set
M628 N1
ok Pitch set
M608 N0
ok 4 MM
M608 N1
ok 4 MM

取飞达ID

M610 N0
ok ID: 1234L
M610 N1
ok ID: 1234R

设置飞达ID

M640 N0X1001
ok ID set
M640 N1X1002
ok ID set
M610 N0
ok ID: 1002L
M610 N1
ok ID: 1002R

看到, 飞达的ID对于子飞达来说, 都是一样的, 子飞达只是有左右之分.

读E2PROM

M630 N0
ok EA 03 00 00 00 00 31 01 00 00 00 00 00 00 00 00 
M630 N1
ok 00 00 00 00 00 00 00 3C 00 00 00 00 00 00 00 00 

取飞达固件信息

M615 N0
ok Firmware version 2.4
M615 N1
ok Firmware version 2.4

西门子飞达上主控芯片只有一个C51, 所以子飞达的固件版本都是一样的

自测试

M650 MCODE_START_SELF_TEST
M651 MCODE_STOP_SELF_TEST

现在飞达上已经正常载入了物料编带, 自测试不敢做了.
自测试命令可以用在单独检测飞达主要机械部分(推料齿轮, 前面的撕料挡板, 中部的卷带齿轮)是否正常, 可以对飞达好坏有个初步的判断(e.g. 买了二手飞达, 到货后可以快速判断一下飞达是否大概是好的).

补充 - 挂好料后, 一个命令就可以测试飞达是否能用

飞达上电, 飞达封皮拉紧按钮和进料按钮好使(不用太好使, 毕竟都是二手飞达, 只需要按几下按钮, 能动作一下就行), 因为飞达换料过程中要用到这2个按钮(e.g. 飞达使用过程中出现错误, 需要用这2个按钮动作来消除错误)

挂载好物料编带, 飞达的指示灯是灭的.

M601 N0X1
M601 N1X1

进料的同时, 封皮可以拉紧就正常.

二手飞达测试出的可用率

设备到手前, 同学一并帮我买了一堆飞达.
当时刚接触openpnp, 还没到接入二手西门子电动飞达这一步, 也只是上电, 按钮按下是否有反应. 其实看不出飞达是否可用.
现在已经有了可用的飞达控制板, 接了一路, 用通讯控制飞达好使.
将手头的飞达都逐个接到飞达控制板的这一路, 剪断2个小段(10个物料左右)物料编带, 将2个子飞达都正常上料. 使飞达错误指示灯是灭的(说明飞达状态正常, 没有错误发生).
然后用通讯命令逐个测试飞达的推料(M601 NXX1), 如果能连续执行推料, 就测试过了.

先测试8mm飞达, 好的飞达一共41把, 坏的飞达7把. 可用率 = 41/48 = 85%
其他边带宽度飞达(16mm, 24mm)还没测试, 等用到的时候再说.

END

这篇关于openpnp/arduino - 二手西门子电动飞达的测试的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/340082

相关文章

SpringBoot中整合RabbitMQ(测试+部署上线最新完整)的过程

《SpringBoot中整合RabbitMQ(测试+部署上线最新完整)的过程》本文详细介绍了如何在虚拟机和宝塔面板中安装RabbitMQ,并使用Java代码实现消息的发送和接收,通过异步通讯,可以优化... 目录一、RabbitMQ安装二、启动RabbitMQ三、javascript编写Java代码1、引入

Nginx设置连接超时并进行测试的方法步骤

《Nginx设置连接超时并进行测试的方法步骤》在高并发场景下,如果客户端与服务器的连接长时间未响应,会占用大量的系统资源,影响其他正常请求的处理效率,为了解决这个问题,可以通过设置Nginx的连接... 目录设置连接超时目的操作步骤测试连接超时测试方法:总结:设置连接超时目的设置客户端与服务器之间的连接

如何测试计算机的内存是否存在问题? 判断电脑内存故障的多种方法

《如何测试计算机的内存是否存在问题?判断电脑内存故障的多种方法》内存是电脑中非常重要的组件之一,如果内存出现故障,可能会导致电脑出现各种问题,如蓝屏、死机、程序崩溃等,如何判断内存是否出现故障呢?下... 如果你的电脑是崩溃、冻结还是不稳定,那么它的内存可能有问题。要进行检查,你可以使用Windows 11

性能测试介绍

性能测试是一种测试方法,旨在评估系统、应用程序或组件在现实场景中的性能表现和可靠性。它通常用于衡量系统在不同负载条件下的响应时间、吞吐量、资源利用率、稳定性和可扩展性等关键指标。 为什么要进行性能测试 通过性能测试,可以确定系统是否能够满足预期的性能要求,找出性能瓶颈和潜在的问题,并进行优化和调整。 发现性能瓶颈:性能测试可以帮助发现系统的性能瓶颈,即系统在高负载或高并发情况下可能出现的问题

字节面试 | 如何测试RocketMQ、RocketMQ?

字节面试:RocketMQ是怎么测试的呢? 答: 首先保证消息的消费正确、设计逆向用例,在验证消息内容为空等情况时的消费正确性; 推送大批量MQ,通过Admin控制台查看MQ消费的情况,是否出现消费假死、TPS是否正常等等问题。(上述都是临场发挥,但是RocketMQ真正的测试点,还真的需要探讨) 01 先了解RocketMQ 作为测试也是要简单了解RocketMQ。简单来说,就是一个分

【测试】输入正确用户名和密码,点击登录没有响应的可能性原因

目录 一、前端问题 1. 界面交互问题 2. 输入数据校验问题 二、网络问题 1. 网络连接中断 2. 代理设置问题 三、后端问题 1. 服务器故障 2. 数据库问题 3. 权限问题: 四、其他问题 1. 缓存问题 2. 第三方服务问题 3. 配置问题 一、前端问题 1. 界面交互问题 登录按钮的点击事件未正确绑定,导致点击后无法触发登录操作。 页面可能存在

业务中14个需要进行A/B测试的时刻[信息图]

在本指南中,我们将全面了解有关 A/B测试 的所有内容。 我们将介绍不同类型的A/B测试,如何有效地规划和启动测试,如何评估测试是否成功,您应该关注哪些指标,多年来我们发现的常见错误等等。 什么是A/B测试? A/B测试(有时称为“分割测试”)是一种实验类型,其中您创建两种或多种内容变体——如登录页面、电子邮件或广告——并将它们显示给不同的受众群体,以查看哪一种效果最好。 本质上,A/B测

arduino ide安装详细步骤

​ 大家好,我是程序员小羊! 前言: Arduino IDE 是一个专为编程 Arduino 微控制器设计的集成开发环境,使用起来非常方便。下面将介绍如何在不同平台上安装 Arduino IDE 的详细步骤,包括 Windows、Mac 和 Linux 系统。 一、在 Windows 上安装 Arduino IDE 1. 下载 Arduino IDE 打开 Arduino 官网

Verybot之OpenCV应用一:安装与图像采集测试

在Verybot上安装OpenCV是很简单的,只需要执行:         sudo apt-get update         sudo apt-get install libopencv-dev         sudo apt-get install python-opencv         下面就对安装好的OpenCV进行一下测试,编写一个通过USB摄像头采

BIRT 报表的自动化测试

来源:http://www.ibm.com/developerworks/cn/opensource/os-cn-ecl-birttest/如何为 BIRT 报表编写自动化测试用例 BIRT 是一项很受欢迎的报表制作工具,但目前对其的测试还是以人工测试为主。本文介绍了如何对 BIRT 报表进行自动化测试,以及在实际项目中的一些测试实践,从而提高了测试的效率和准确性 -------