Использование указателей для перебора массива экземпляров AccelStepper (Arduino)

Я программирую шаговый двигатель для устройства 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); 
//...

Этот раствор лучше?

Пожалуйста, не отмечайте C, если код явно не C.

user17732522 21.03.2022 17:19

Вы объявляете новую переменную с именем feederStepper после первой printf. Во втором вы имеете в виду это, а не члена класса.

user17732522 21.03.2022 17:24

Хорошо, но мне нужно объявить feederStepper в файле .h, чтобы иметь возможность использовать его для всего класса? Как я могу избежать второй переменной тогда?

Stefatronik 21.03.2022 17:32

Вы используете список инициализаторов членов конструктора для инициализации члена. Это должно быть объяснено в любом вводном учебнике по C++. Bender::Bender() : feederStepper(1, P_FEED_STEP, P_FEED_DIR).

user17732522 21.03.2022 17:35

Код в вашем редактировании не будет компилироваться.

user17732522 21.03.2022 17:37

Вы были правы, без компиляции. Не могли бы вы дать мне еще одну подсказку относительно вашего второго комментария, в конце концов ссылку? Интересно, происходит ли инициализация feederStepper внутри конструктора Bender или как это нужно сделать правильно?

Stefatronik 21.03.2022 17:45
Learncpp.com/cpp-tutorial/constructor-member-initializer-lis‌​ts, но вы должны получить один из рекомендуемые книги по C++. Я не знаю, есть ли какие-либо вводные книги по C++, посвященные Arduino, или есть ли какие-либо вводные учебники по Arduino, хорошо освещающие язык C++. Arduino — это своего рода странная адаптация C++.
user17732522 21.03.2022 17:48

Спасибо, завтра посмотрю и сообщу, если получится. Да, я уже давно чувствую особенности Arduino. Но мои познания в C++ 10 лет назад (и были загрязнены Python ^^ ), и я пытаюсь вспомнить свою учебу...

Stefatronik 21.03.2022 17:52

Последний фрагмент кода нуждается в новом. В противном случае, если он скомпилируется, он берет адрес временно созданного объекта, и после инициализации он будет недействительным указателем. Или вы можете просто использовать массив объектов и использовать список инициализаторов конструкторов

KIIV 21.03.2022 19:11

Я посмотрел несколько уроков и пришел к этому. В 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. уже сделал это

Stefatronik 22.03.2022 08:15
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
10
38
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Да, вы объявляете несколько объектов 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 22.03.2022 16:04

@Stefatronik Да, список инициализаторов вызовет конструкторы объектов с предоставленными параметрами.

mmixLinus 22.03.2022 19:00

Другие вопросы по теме