Я программирую шаговый двигатель для устройства Arduino. В моем заголовочном файле я объявил экземпляры библиотеки AccelStepper. Позже я хочу перебрать степперы, используя массив указателей. Я обнаружил, что экземпляры имеют разные адреса. Не могли бы вы объяснить, что здесь не так, и дать мне подсказку, как я могу это исправить?
файл: бендер.h
typedef char arg_t[32]; // string type with defined length for passing args from cmd line to the methods
class Bender
{
public:
Bender();
int init(arg_t []);
int feed(arg_t []);
int setStepperMaxSpeed(arg_t []);
private:
char strbuf[128];
AccelStepper feederStepper;
AccelStepper rotationStepper;
AccelStepper benderStepper;
AccelStepper *steppers[3];
arg_t stepperNames[3];
float stepperMaxSpeeds[3];
MessageControl msg_h;
int _setBenderPin(int);
int _setStpMaxSpd(int, float);
};
файл бендер.cpp
#include <settings.h>
#include "BenderControl.h"
#include <Arduino.h>
Bender::Bender()
{
sprintf(strbuf, "MESSAGE 1: &feederStepper=%p", &feederStepper);
msg_h.say(strbuf, L_ALWAYS);
AccelStepper feederStepper(1, P_FEED_STEP, P_FEED_DIR); // (Type:driver, STEP, DIR)
AccelStepper rotationStepper(1, P_ROT_STEP, P_ROT_DIR);
AccelStepper benderStepper(1, P_BEND_STEP, P_BEND_DIR);
steppers[0] = &feederStepper;
steppers[1] = &rotationStepper;
steppers[2] = &benderStepper;
sprintf(strbuf, "MESSAGE 2: &feederStepper=%p", &feederStepper);
msg_h.say(strbuf, L_ALWAYS);
stepperMaxSpeeds[0] = (float)FEEDER_MAX_SPEED;
stepperMaxSpeeds[1] = (float)BENDER_MAX_SPEED;
stepperMaxSpeeds[2] = (float)ROTATION_MAX_SPEED;
strcpy(stepperNames[0], "feed");
strcpy(stepperNames[1], "bend");
strcpy(stepperNames[2], "rot");
benderServo.attach(P_SERVO);
}
int Bender::init(arg_t args[])
{
int result = -1;
msg_h.say("Bender initializing\n", L_INFO);
result = _setStpMaxSpd(0, stepperMaxSpeeds[0]);
/*will evaluate result later, work in progress here*/
_setStpMaxSpd(1, stepperMaxSpeeds[1]);
_setStpMaxSpd(2, stepperMaxSpeeds[2]);
msg_h.say("Bender initialized\n", L_INFO);
return 0;
}
int Bender::_setStpMaxSpd(int idx, float val)
{
//sprintf(strbuf, "PRIVATE _setStpMaxSpd set to ");
//dtostrf(val, 4,1, &strbuf[strlen(strbuf)]);
//strncat(strbuf, "\n", 2);
//msg_h.say(strbuf, L_INFO);
//delay(50);
int ret = -1;
sprintf(strbuf, "MESSAGE3: &feederstepper %p\n", &feederStepper);
msg_h.say(strbuf, L_INFO);
sprintf(strbuf, "MESSAGE4: &benderstepper %p\n", &benderStepper);
msg_h.say(strbuf, L_INFO);
sprintf(strbuf, "MESSAGE5: &rotationstepper %p\n", &rotationStepper);
msg_h.say(strbuf, L_INFO);
sprintf(strbuf, "MESSAGE6: steppers[idx] %p\n", steppers[idx]);
msg_h.say(strbuf, L_INFO);
delay(100);
//feederStepper.setMaxSpeed(val);
steppers[idx]->setMaxSpeed(val);
delay(100);
float maxs = steppers[idx]->maxSpeed();
sprintf(strbuf, "Setpoint / real value: ");
dtostrf((double)val, 4,2, &strbuf[strlen(strbuf)]);
strncat(strbuf, " / ", 3);
dtostrf((double)maxs, 4,2, &strbuf[strlen(strbuf)]);
strncat(strbuf, "\n", 2);
msg_h.say(strbuf, L_INFO);
delay(50);
if (abs(maxs - val) > 0.01)
{
msg_h.say("SET FAIL", L_INFO);
sprintf(strbuf, "Could not set max speed of stepper %s to ", stepperNames[idx]);
dtostrf(val, 4,1, &strbuf[strlen(strbuf)]);
strncat(strbuf, "\n", 2);
msg_h.say(strbuf, L_ERROR);
ret = -1;
}
else
{
sprintf(strbuf, "Stepper %s max speed = ", stepperNames[idx]);
dtostrf(maxs, 4, 1, &strbuf[strlen(strbuf)]);
strncat(strbuf, "\n", 2);
msg_h.say(strbuf, L_INFO);
ret = 0;
delay(50);
}
return ret;
}
Пожалуйста, игнорируйте метод msg_h.say(), это просто Serial.print
в зависимости от уровня отладки.
Я ожидал, что адрес feederStepper
останется прежним, но на самом деле я получаю другие адреса.
Я пометил выходные строки префиксом СООБЩЕНИЕ х.
От MSG1 до MSG2 адреса различаются. Какова причина? Я ожидал, что при объявлении в Bender.h адрес зарезервирован (так и останется).
Выход:
MESSAGE1: &feederStepper=0x1e11
MESSAGE2: &feederStepper=0x21aa // why has the address changed here?
MESSAGE3: &feederstepper 0x1e11 // and why is here the old address from MSG1?
MESSAGE4: &benderstepper 0x1e99
MESSAGE5: &rotationstepper 0x1e55
MESSAGE6: steppers[idx] 0x21aa
Возможно ли, что в моей программе есть два экземпляра feederStepper (и других степперов)? Как правильно это исправить? Должен ли я инициализировать массив указателей прямо в объявлении в файле Bender.h?
Привет, Стефан
Редактировать после комментария пользователя 17732522: Это правильный способ сделать это так:
//file: bender.h
//...
AccelStepper *steppers[3];
//...
и
//file: bender.cpp
//...
steppers[0] = &AccelStepper(1, P_FEED_STEP, P_FEED_DIR);
steppers[1] = &AccelStepper(1, P_BEND_STEP, P_BEND_DIR);
steppers[2] = &AccelStepper(1, P_ROT_STEP, P_ROT_DIR);
//...
Этот раствор лучше?
Вы объявляете новую переменную с именем feederStepper
после первой printf
. Во втором вы имеете в виду это, а не члена класса.
Хорошо, но мне нужно объявить feederStepper в файле .h, чтобы иметь возможность использовать его для всего класса? Как я могу избежать второй переменной тогда?
Вы используете список инициализаторов членов конструктора для инициализации члена. Это должно быть объяснено в любом вводном учебнике по C++. Bender::Bender() : feederStepper(1, P_FEED_STEP, P_FEED_DIR)
.
Код в вашем редактировании не будет компилироваться.
Вы были правы, без компиляции. Не могли бы вы дать мне еще одну подсказку относительно вашего второго комментария, в конце концов ссылку? Интересно, происходит ли инициализация feederStepper внутри конструктора Bender или как это нужно сделать правильно?
Спасибо, завтра посмотрю и сообщу, если получится. Да, я уже давно чувствую особенности Arduino. Но мои познания в C++ 10 лет назад (и были загрязнены Python ^^ ), и я пытаюсь вспомнить свою учебу...
Последний фрагмент кода нуждается в новом. В противном случае, если он скомпилируется, он берет адрес временно созданного объекта, и после инициализации он будет недействительным указателем. Или вы можете просто использовать массив объектов и использовать список инициализаторов конструкторов
Я посмотрел несколько уроков и пришел к этому. В Bender.h я использую эти три строки для инициализации своих экземпляров AccelStepper: AccelStepper feederStepper = AccelStepper (1, P_FEED_STEP, P_FEED_DIR); AccelStepper rotationStepper = AccelStepper (1, P_ROT_STEP, P_ROT_DIR); AccelStepper benderStepper = AccelStepper (1, P_BEND_STEP, P_BEND_DIR);
Это компилируется и работает, как и ожидалось. Поскольку я импортирую библиотеку AccelStepper, я не могу добавить к ней список инициализаторов — и именно об этом я беспокоился в ответе @user17732522. Очевидно автор Acc.St. уже сделал это
Да, вы объявляете несколько объектов AccelStepper. Чтобы создать массив указателей на объекты вашего шагового двигателя, вы можете, как упоминалось выше, полностью удалить объекты из класса Bender, а Только удерживать три указателя на объекты в вашем массиве. Например:
#define FEED_IDX 0
#define ROT_IDX 1
#define BEND_IDX 2
steppers[FEED_IDX] = new AccelStepper(1, P_FEED_STEP, P_FEED_DIR);
steppers[ROT_IDX] = new AccelStepper(1, P_ROT_STEP, P_ROT_DIR);
steppers[BEND_IDX] = new AccelStepper(1, P_BEND_STEP, P_BEND_DIR);
Затем просто используйте константы _IDX, чтобы различать их в коде.
Альтернативный способ, если вы хотите сохранить свои три объекта в объекте Bender, состоит в том, чтобы полностью удалить объявления шагового двигателя в конструкторе Bender и добавить список инициализаторов в конструктор, который устанавливает ваши три шаговых двигателя. Код должен выглядеть примерно так:
Bender::Bender() :
// initializer list to setup stepper motor objects
feederStepper(1, P_FEED_STEP, P_FEED_DIR), // (Type:driver, STEP, DIR)
rotationStepper(1, P_ROT_STEP, P_ROT_DIR),
benderStepper(1, P_BEND_STEP, P_BEND_DIR)
{
sprintf(strbuf, "MESSAGE 1: &feederStepper=%p", &feederStepper);
msg_h.say(strbuf, L_ALWAYS);
steppers[0] = &feederStepper;
steppers[1] = &rotationStepper;
steppers[2] = &benderStepper;
Оба примера кода прекрасно компилируются в Arduino 1.8.12, но у меня нет подходящего оборудования, а ваш код не завершен, поэтому я не могу их запустить.
Большое спасибо за пример. Это очень помогло мне понять, как правильно составить список инициализаторов. Я реализовал оба (#define idx и список инициализации), и все работает нормально! В любом случае, мне нужно немного больше понять, что этот список означает под капотом. Значит, при создании нового экземпляра класса бендера создается только один экземпляр AccelStepper (конечно, на строку) и этому экземпляру присваиваются параметры из списка инициализаторов?
@Stefatronik Да, список инициализаторов вызовет конструкторы объектов с предоставленными параметрами.
Пожалуйста, не отмечайте C, если код явно не C.