openpnp - use STM32 arduino on SchultzController

文章目录

    • openpnp - use STM32 arduino on SchultzController
    • 概述
    • 笔记
    • 官方的起始文档
    • 增加arduino第三方开发板库索引地址
    • 改好后, 能编译过的工程
    • SchultzController.ino
    • Feeder.h
    • Feeder.cpp
    • 再验证一下内存是否够用
    • 补充 - 如果是自己做的板子
    • END

openpnp - use STM32 arduino on SchultzController

概述

我的飞达控制板用的控制主板是原装的arduino 2560 R3, MCU内存太小了, 导致52路(104位)的西门子二手飞达控制无法实现(只能控制22路(44位飞达), 否则内存受限, 编译不过去, 或者编译过去了, 运行时内存不够导致栈溢出/导致逻辑异常).

看到资料, 可以在arduino IDE中用STM32开发板, arduino工程实现不用改, 只需要选好板子, 重新编译下载, 这挺方便的.
手头有好多STM32官方的板子(大部分STM32的官方板子都支持arduino应用), 总有一款适用的, 正好整起来.
等用单独的官方开发板将工程编译过了, 剩下的事情: 查STM32官方板子的arduino引脚定义, 再画一个板子就O了.

笔记

官方的起始文档

https://github.com/stm32duino/BoardManagerFiles
https://github.com/stm32duino/Arduino_Core_STM32/wiki
有了这2个索引文档, 就可以按照说明来使用 STM32 arduino了.

STM32Arduino 社区 : http://www.stm32duino.com/

使用的arduino IDE 版本为 2.2.1
在这里插入图片描述

增加arduino第三方开发板库索引地址

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

https://github.com/stm32duino/BoardManagerFiles/raw/main/package_stmicroelectronics_index.json

此时, arduinoIDE从网上的库地址, 已经将库索引下来完成.
在库管理中, 查找并安装STM32Arduino的库
在这里插入图片描述
从库索引.json文件中, 可知包作者名称为 STMicroelectronics
在这里插入图片描述
在这里插入图片描述
将这2个包的版本选为最新(STM32 Arduino 2.6, STM8 Arduino 1.0), STM32Arduino提供的STM32和STM8的库就一起就装上.
装的时候有点慢, 保持网络畅通.
在这里插入图片描述
看STM32 Arduino支持的STM32官方板子中是否有自己手头的板子. https://github.com/stm32duino/Arduino_Core_STM32/wiki
在这里插入图片描述
https://github.com/stm32duino/Arduino_Core_STM32/#supported-boards
在这里插入图片描述

去查NUCLEO-H723ZG的官方文档, 看是否支持arduino, 是否有相关引出插座上的管脚定义 https://www.st.com/en/evaluation-tools/nucleo-h723zg.html#documentation
在这里插入图片描述
在这里插入图片描述
可知, NUCLEO-H723ZG支持arduino, 但是引脚外形结构引出和mega2560R3不相同, 需要重新画一个板子(引脚布局差的有点大, 如果做一块转接板, 不太好弄).
在这里插入图片描述
选择具体的官方开发板
在这里插入图片描述
尝试编译工程通过时, 不需要连接物理开发板到计算机, 等工程编译过了, 再连接物理开发板不迟.

直接编译工程, 是会报错的, 需要修改工程.
修改工程的原因:
mega2560R3是8位的主控, 数据类型包括指针都是8位的.
H7是32位的主控. 工程中用到的一些数据类型需要由uint8_t改为uint32_t.
有些函数, 是AVR独有的函数, 要添加自己的函数. e.g. 用指令周期循环做的短延时函数.
因为这个延时函数是用于IO模拟串口发送用的, 必须是算好的短延时函数, 不能是ms级别的演示.

如果是第一次玩arduino, 编译不过时的报错给人的感觉挺奇怪的. 得自己折腾, 适应几个小时, 调试的感觉就回来了.
编译报错时, 如果要加代码, 可以看arduino提供的STM32开发板的例子工程.
看STM32Arduino官方给的例子工程了(看看每个知识点的头文件包含, 调用的Arduino API).
根据报错, 将知识点用到的API换成例子中的头文件和函数调用就搞定.

Arduino比较好的一点, 例子都在IDE中有新建例子工程, 不用磁盘上到处去找.
随便打开一个NUCLEO144的例子工程.
在这里插入图片描述
新建的例子工程, 开发板的选项和父工程相同.
在这里插入图片描述

编译一下, 看看是否能通过.
在这里插入图片描述
例子工程编译过了
在这里插入图片描述
看看例子工程在磁盘哪个位置, 然后就可以用VSCode打开集中观摩学习了.

FQBN: STMicroelectronics:stm32:Nucleo_144:pnum=NUCLEO_H723ZG
使用平台的 ‘Nucleo_144’ 开发板,在列出的文件夹中:C:\Users\chenx\AppData\Local\Arduino15\packages\STMicroelectronics\hardware\stm32\2.6.0
使用平台的 ‘arduino’ 代码,在列出的文件夹中:C:\Users\chenx\AppData\Local\Arduino15\packages\STMicroelectronics\hardware\stm32\2.6.0

从例子工程的编译信息中可知, 位置如下:

C:\Users\chenx\AppData\Local\Arduino15\packages\STMicroelectronics\hardware\stm32\2.6.0

在这个目录中搜索*.ino, 找到了所有的例子工程.

C:\Users\chenx\AppData\Local\Arduino15\packages\STMicroelectronics\hardware\stm32\2.6.0\libraries

在这里插入图片描述

这个目录下, 每个子目录都是库, 都有对应使用的例子.
每个子目录下都有一个examples目录, 里面都是例子工程.
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
先将例子工程都过一遍(不用Arduino IDE, 用VSCode快点, 只看内容进行知识点的初步学习, 不编译).
翻了一遍例子工程, 发现工程都是一个.ino了事, 将所有例子工程都拷贝到一个临时目录
在这里插入图片描述
然后根据要编译的自己的原始工程的报错, 来用SI来搜索头文件和API.
现在可以编译自己工程了, 根据报错提示来更新头文件的引用.
开始尝试编译, 修正报错

工程编译过了, 将修改点, 按照git归档记录一下.
不是经常弄arduino, 看着报错信息挺怪的, 适应后就好了.

在这里插入图片描述

改好后, 能编译过的工程

编译过后, 一共改了3个源文件.
在这里插入图片描述
修改处加了@bugfix 标记, 方便查找, 移动改了16处代码.
修改后的3个文件内容如下:

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];// @bugfix 1
HardwareSerial Serial1(1); // 增加的给飞达发送指令的物理串口, 库中没有定义好的Serial1可用.// ------------------  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();// @bugfix 2// Serial1.begin(9600);  // The hardware RX portSerial1.begin(9600, SERIAL_8N1); // 这里的发送管脚不要做数字IO用了, 发送脚闲置, 只用接收引脚for (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();
}

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*/// @bugfix 3// uint8_t _transmitBitMask; // 8位MCU的数据类型uint32_t _transmitBitMask; // 32位MCU的数据类型// volatile uint8_t *_transmitPortRegister;  // 8位MCU的数据类型volatile uint32_t *_transmitPortRegister; // 32位MCU的数据类型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);// @bugfix 4static inline void _delay_loop_2(uint16_t __count); // 增加的指令周期级别的延时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


// @bugfix 5
// #include <avr/interrupt.h>// @bugfix 6
// #include <avr/pgmspace.h>
#include <Arduino.h>// @bugfix 7
// #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);// @bugfix 8// uint8_t port = digitalPinToPort(tx);uint32_t port = (uint32_t)digitalPinToPort(tx);// @bugfix 9// this->_transmitPortRegister = portOutputRegister(port);this->_transmitPortRegister = portOutputRegister(((GPIO_TypeDef *)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);
}/** \ingroup util_delay_basicDelay loop using a 16-bit counter \c __count, so up to 65536iterations are possible.  (The value 65536 would have to bepassed as 0.)  The loop executes four CPU cycles per iteration,not including the overhead the compiler requires to setup thecounter register pair.Thus, at a CPU speed of 1 MHz, delays of up to about 262.1milliseconds can be achieved.*/// @bugfix 10// 增加的自己的指令周期级别的短延时函数
inline void FeederClass::_delay_loop_2(uint16_t __count)
{uint16_t loop = 0xffff;if (__count <= 0){return;}do {// 因为用于GPIO模拟串口发送时的延时, 具体延时值, 调试时再根据实际情况决定.loop = 0xffff;do {__ASM( "NOP" );} while (--loop > 0);} while (--__count > 0);
}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 timings// @bugfix 11// volatile uint8_t *reg = this->_transmitPortRegister;volatile uint32_t *reg = this->_transmitPortRegister;// @bugfix 12// uint8_t reg_mask = this->_transmitBitMask;uint32_t reg_mask = this->_transmitBitMask;// @bugfix 13// uint8_t inv_mask = ~this->_transmitBitMask;uint32_t inv_mask = ~this->_transmitBitMask;// @buffix 14// uint8_t oldSREG = SREG;bool inv = this->inverse_logic;uint16_t delay = this->_tx_delay;if (inv)b = ~b;// @bugfix 15// cli();  // turn off interrupts for a clean txmitnoInterrupts();// 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;// @bugfix 16// SREG = oldSREG;  // turn interrupts back oninterrupts();tunedDelay(delay);return 1;
}

再验证一下内存是否够用

在这里插入图片描述
STM32H723ZGT6有564KB内存.
将飞达路数改为120, 编译一下试试.

#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 120
#else
#define FEEDER_CNT 120
#endif
#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 => F30// 21, // 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, 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,// };
#else

编译结果

使用 1.0.1 版本的 SrcWrapper 库,在列出的文件夹中:C:\Users\chenx\AppData\Local\Arduino15\packages\STMicroelectronics\hardware\stm32\2.6.0\libraries\SrcWrapper
"C:\\Users\\chenx\\AppData\\Local\\Arduino15\\packages\\STMicroelectronics\\tools\\xpack-arm-none-eabi-gcc\\12.2.1-1.2/bin/arm-none-eabi-size" -A "C:\\Users\\chenx\\AppData\\Local\\Temp\\arduino\\sketches\\7D2B76A821EDD7929D88D55B655B13DD/SchultzController.ino.elf"项目使用 50872 字节(4%)的程序存储空间。最大值为 1048576 字节。个全局变量使用 37860 个字节(11%)的动态内存,剩下 289820 个字节用于局部变量。最大值为 327680 字节。

可以看到, 内存还是大大的有. 只不过, 为啥显示最大内存为327680字节? 这才320KB, 不是564KB. 是不是有一部分内存没打开.
算了, 反正对于飞达控制板应用来说, 内存足够了.
下一步, 就画一块和NUCLEO-H723ZG引出管脚配套的板子了.
具体程序实现, 是否能正常控制飞达, 等板子出来, 接上飞达再调整.

补充 - 如果是自己做的板子

官方wiki中, 也有对于只使用STM32MCU做的板子(不使用官方板子), 来使用arduino的说明.
没细看, 知道有这么个资料就行, 用到时再说.

如果是自己做的板子, 一般也就不想用arduino了(控制粒度小, 内存浪费一些).
不过要是有一块第三方的板子, 想拿来快速实验, 倒是可以参考官方的资料来让第三方的板子可以用arduino来编程, 毕竟想看效果会快一些.

END

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/138063.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

git安装配置教程

目录 git安装配置1. 安装git2. git 配置3.生成ssh key:4. 获取生产的密钥3. gitee或者github添加ssh-key4.git使用5. git 使用-本地仓库与远程仓库建立连接第一步&#xff1a;进入项目文件夹&#xff0c;初始化本地仓库第二步&#xff1a;建立远程仓库。 建立远程连接的小技巧 …

echarts-可视化地图防重叠文本框

我在第一篇可视化地图中&#xff0c;有一些基础介绍&#xff0c;本篇文章就是多展示一些效果&#xff0c;大家可以按需获取。 先直接上效果图 这里的配置项有用到 1、通过geo展示多层地图&#xff0c;这样可以像上图所示&#xff0c;通过错位有了一些3D效果&#xff1b; 2、北…

沉积物微体古生物鉴定

声明 本文是学习GB-T 42629.4-2023 国际海底区域和公海环境调查规程 第4部分&#xff1a;海洋沉积物物理特性调查. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本文件规定了国际海底区域和公海环境调查中的沉积物组成、物理力学性质、生物…

【AWS】AI 代码生成器—Amazon CodeWhisperer初体验 | 开启开挂编程之旅

使用 AI 编码配套应用程序更快、更安全地构建应用程序 文章目录 1.1 Amazon CodeWhisperper简介1.2 Amazon CodeWhisperer 定价2.1 打开VS Code2.2 安装AWS ToolKit插件 一、前言 1.1 Amazon CodeWhisperper简介 1️⃣更快地完成更多工作 CodeWhisperer 经过数十亿行代码的训…

Docker实战技巧(二):Kubernetes基础操作实战

Kubernetes定位在Saas层,重点解决了微服务大规模部署时的服务编排问题 1、关闭防火墙并设置开机禁用   systemctl stop firewalld   systemctl disable firewalld 2、配置repo   cd /etc/yum.repos.d/   下载Docker repo   wget https://mirrors.aliyun.com/docker-…

Android毕业设计,基于Android 语音朗读书籍管理系统

视频演示&#xff1a; 基于Android 语音朗读书籍管理系统 基于 Android 的语音朗读书籍管理系统可以提供用户管理书籍、朗读书籍的功能。以下是一个简单的步骤和功能列表&#xff1a; 用户注册和登录功能&#xff1a; 用户可以注册新账号或使用现有账号登录系统。用户信息可以包…

【扩散生成模型】Diffusion Generative Models

提出扩散模型思想的论文&#xff1a; 《Deep Unsupervised Learning using Nonequilibrium Thermodynamics》理解 扩散模型综述&#xff1a; “扩散模型”首篇综述论文分类汇总&#xff0c;谷歌&北大最新研究 理论推导、代码实现&#xff1a; What are Diffusion Models?…

基于win32实现TB登陆滑动验证

这里写目录标题 滑动验证触发条件&#xff1a;失败条件&#xff1a;解决方法:清除cooKie 滑动验证方式一&#xff1a;win32 api获取窗口句柄&#xff0c;选择固定位置 成功率高方式二&#xff1a; 原自动化滑动&#xff0c;成功率中 案例 先谈理论&#xff0c;淘宝 taobao.com …

【网络协议】Http-中

搜索引擎&#xff1a;搜索引擎是指根据一定的策略、运用特定的计算机程序从互联网上采集信息&#xff0c;在对信息进行组织和处理后&#xff0c;为用户提供检索服务&#xff0c;将检索的相关信息展示给用户的系统。搜索引擎是工作于互联网上的一门检索技术&#xff0c;它旨在提…

向表中针对全部列插入数据

MySQL从小白到总裁完整教程目录:https://blog.csdn.net/weixin_67859959/article/details/129334507?spm1001.2014.3001.5502 语法格式: insert into 表名 values(); 我们来查看test01表里面有几列 mysql> show databases; -------------------- | Database …

易知微防洪“四预”智慧水利平台上线!全面助力智慧水利建设发展

自古以来&#xff0c;中国的基本水情一直是夏汛冬枯、北缺南丰&#xff0c;水资源时空分布极不均衡&#xff0c;水旱灾害多发频发重发。因此&#xff0c;兴水利、除水害&#xff0c;一直都是涉及国家长治久安的大事要事。随着全球气候变化影响加剧&#xff0c;极端天气增多&…

行业首款数字牙刷 F10 系列:笑容加带来的智能刷牙革命

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

【proverif】proverif的下载安装和初使用

文章目录 一、proverif下载1. 下载proverif安装包2. 解压proverif安装包3. 点开其中的README&#xff0c;安装graphciz和gtk4. 查看安装是否成功5. 测试 一、proverif下载 1. 下载proverif安装包 官网&#xff1a;proverif 首先下载全过程无需开外网&#xff0c;而且安装包下…

基于SSM的出租车管理系统的设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

9.25day5---Qt

登录页面设计&#xff0c;建立用户注册以及登录的数据库&#xff0c;数据库保存用户名和密码 &#xff08;ps:本篇只完成了登录功能&#xff0c;其他功能&#xff0c;请见下篇嘿嘿。&#xff09; 再次注册则失败&#xff1a; 代码如下&#xff1a; 头文件&#xff1a; 登录…

堆的OJ题

&#x1f525;&#x1f525; 欢迎来到小林的博客&#xff01;&#xff01;       &#x1f6f0;️博客主页&#xff1a;✈️林 子       &#x1f6f0;️博客专栏&#xff1a;✈️ 小林的算法笔记       &#x1f6f0;️社区 :✈️ 进步学堂       &am…

redis深度历险 千帆竞发 —— 分布式锁

分布式应用进行逻辑处理时经常会遇到并发问题。 比如一个操作要修改用户的状态&#xff0c;修改状态需要先读出用户的状态&#xff0c;在内存里进行修改&#xff0c;改完了再存回去。如果这样的操作同时进行了&#xff0c;就会出现并发问题&#xff0c;因为读取和保存状态这两个…

生产消费者模型的介绍以及其的模拟实现

目录 生产者消费者模型的概念 生产者消费者模型的特点 基于阻塞队列BlockingQueue的生产者消费者模型 对基于阻塞队列BlockingQueue的生产者消费者模型的模拟实现 ConProd.c文件的整体代码 BlockQueue.h文件的整体代码 对【基于阻塞队列BlockingQueue的生产者消费者模型…

uniapp----微信小程序 日历组件(周日历 月日历)【Vue3+ts+uView】

uniapp----微信小程序 日历组件&#xff08;周日历&& 月日历&#xff09;【Vue3tsuView】 用Vue3tsuView来编写日历组件&#xff1b;存在周日历和月日历两种显示方式&#xff1b;高亮显示当天日期&#xff0c;红点渲染有数据的日期&#xff0c;点击显示数据 1. calenda…

数据结构——AVL树

目录 1.什么是AVL树&#xff1f; 2.AVL树插入的模拟实现 ①节点定义 ②插入 ③旋转 ⑴右单旋 ⑵左单旋 ⑶双旋&#xff08;右左旋&#xff09; ⑷双旋&#xff08;左右旋&#xff09; ⑸完整的插入代码 3.AVL树的性能分析 1.什么是AVL树&#xff1f; AVL树是一种自…