Почему Serial Print вызывает различное поведение в зависимости от того, где он находится

Я пытаюсь проверить эти два двигателя, где мне нужно вызвать функцию из цикла. Я хочу вызвать эту функцию только один раз, поэтому я сделал небольшой оператор if, чтобы программа запускалась только один раз. Однако кажется, что когда я запускаю этот файл, установленная мной переменная не продолжает увеличиваться. Код расскажет больше.

int i = 0;

void loop()
{
    // Serial.print("=====");Serial.print(i);Serial.println("=====");
    if(i == 1)
    {
        Serial.print("+++");Serial.print(i);Serial.print("+++");
        DeclinationMotor(DEC_freq, NULL, HIGH);
    }
    i++;
}

Поведение действительно странное в том смысле, что если я просто запущу этот код выше, переменная i не будет увеличиваться и застрянет на 1. Однако, если бы я раскомментировал другой последовательный вывод строку над оператором if, я будет правильно увеличен, и эта функция будет выполнена только один раз.

Для справки, функция DeclinationMotor это

float DeclinationMotor(float freqspeed, float angularDistance, byte direction)
{
    Serial.print(freqspeed); Serial.print("DEfS "); Serial.print(angularDistance); Serial.print("DEaD "); Serial.println(direction);

    int finalLocation = 0;
    if(freqspeed == NULL && angularDistance != NULL)
        freqspeed = DEC_freq;
    if(angularDistance != NULL)
    {
        if(direction == HIGH)
            finalLocation = DEC_degrees_from_home + angularDistance;
        else
            finalLocation = DEC_degrees_from_home - angularDistance;    
    }

    SetPinFrequency(DEC_STEP, freqspeed);
    pwmWrite(DEC_STEP, 128);
    digitalWrite(DEC_DIR, direction);
    digitalWrite(DEC_running, HIGH);

    return finalLocation;
}

Полный код здесь

//-------------------------------- ------------------------------------------------------------
#include <PWM.h>
void analogJoyStick();
void serialEvent();
void meridianFlipT();
float RightAscensionMotor(float freqspeed, float angularDistance, byte direction);
float DeclinationMotor(float freqspeed, float angularDistance, byte direction);
void StopRAMotor();
void StopDECMotor();




// --------------Делать--------------
// Превратить переменные в двигательные действия для последовательного события
// Напишите программу, помогающую вычислить степени энкодеров для своего
// угловая хрень. Возможно, вам просто нужно посчитать, сколько именно
// количество оборотов вручную
// настраиваем isrs для оптических сенсоров и джойстика
// создаем функцию остановки двигателей
//



#define RA_sensor 2
#define DEC_sensor 3

// Светодиоды предельных значений датчика
#define RA_limit 27
#define DEC_limit 29

// Светодиоды состояния телескопа
#define home_pos 35
#define tracking 37

// Джойстик
#define xPin A0      // ось X
#define yPin A1      // ось Y
#define JS_button 26 // Кнопка

// Двигатель 1
#define RA_STEP 10                // Шаг прямого восхождения
#define RA_DIR 12                 // Направление прямого восхождения
#define RA_EN 32                  // Включить отключение двигателя
#define RA_OUT 36                 // Выход неисправности двигателя
int RA_freq = 800;                // Частота для нормального движения
int RA_freq_tracking = 400;       // Частота отслеживания объектов
int RA_freq_slow = 600;           // Частота приближения к желаемой позиции
volatile byte RA_track_DIR = LOW; // Используется для хранения последнего направления двигателя прямого восхождения

// Энкодер 1
#define RA_A 18
#define RA_B 19
#define RA_I 22 
long int RA_Encoder_Count = 0;      // Положительное значение означает, что чистая позиция находится в направлении B
int RA_Complete_Rotations = 0; // Положительное значение означает полное вращение в направлении B
int RA_degrees_from_home = 0;  // Используется для отслеживания того, насколько далеко от исходного положения

// Двигатель 2
#define DEC_STEP 11                 // Шаг отклонения
#define DEC_DIR 13                  // Направление склонения
#define DEC_EN 34                   // Включить отключение двигателя
#define DEC_OUT 38                  // Выход неисправности двигателя
int DEC_freq = 800;                 // Частота для нормального движения
int DEC_freq_slow = 600;            // Частота при приближении к желаемой позиции
volatile byte DEC_track_DIR = LOW;  // Используется для хранения последнего направления двигателя DEC

// Энкодер 2
#define DEC_A 20
#define DEC_B 21
#define DEC_I 24
long int DEC_Encoder_Count = 0;      // Положительное значение означает, что чистая позиция находится в направлении B
int DEC_Complete_Rotations = 0; // Положительное значение означает полное вращение в направлении B
int DEC_degrees_from_home = 0;  // Используется для отслеживания того, насколько далеко от исходного положения


// Связь с ATmega328
#define COMM 39
#define RA_running 23
#define DEC_running 25

int i = 0;

void setup()
{
    // Входы датчиков
    pinMode(RA_sensor, INPUT);
    pinMode(DEC_sensor, INPUT);

    // Светодиоды неисправности датчика
    pinMode(RA_limit, OUTPUT);
    pinMode(DEC_limit, OUTPUT);

    // Светодиоды состояния телескопа
    pinMode(home_pos, OUTPUT);
    pinMode(tracking, OUTPUT);

    // Прерывания для датчиков
    // attachInterrupt(digitalPinToInterrupt(RA_sensor), RA_sensor_trip, CHANGE);
    // attachInterrupt(digitalPinToInterrupt(DEC_sensor), DEC_sensor_trip, CHANGE);

    // Входы и выходы двигателя прямого восхождения
    pinMode(RA_STEP, OUTPUT);
    pinMode(RA_DIR, OUTPUT);
    pinMode(RA_OUT, INPUT);
    // attachInterrupt(digitalPinToInterrupt(RA_OUT), RA_motor_fault, CHANGE);

    // Входные данные кодировщика прямого восхождения
    pinMode(RA_A, INPUT);
    // attachInterrupt(digitalPinToInterrupt(RA_A), RA_Enc, RISING);
    pinMode(RA_B, INPUT);
    pinMode(RA_I, INPUT);

    // Входы и выходы мотора склонения
    pinMode(DEC_STEP, OUTPUT);
    pinMode(DEC_DIR, OUTPUT);
    pinMode(DEC_OUT, INPUT);
    // attachInterrupt(digitalPinToInterrupt(DEC_OUT), DEC_motor_fault, CHANGE);

    // Входные данные кодировщика склонения
    pinMode(DEC_A, INPUT);
    // attachInterrupt(digitalPinToInterrupt(DEC_A), DEC_Enc, RISING);
    pinMode(DEC_B, INPUT);
    pinMode(DEC_I, INPUT);   

    // Вводы джойстика
    pinMode(xPin, INPUT); 
    pinMode(yPin, INPUT); 
    pinMode(JS_button, INPUT);

    // Связь с ATmega328
    pinMode(COMM, OUTPUT); 
    pinMode(RA_running, OUTPUT);
    pinMode(DEC_running, OUTPUT);

    // Постоянный сигнал на ATmega328 для подтверждения работоспособности ATmega2560
    digitalWrite(COMM, HIGH);

    Serial.begin(9600);
    InitTimersSafe();

}

void loop()
{
    // Serial.print("=====");Serial.print(i);Serial.println("=====");
    if(i == 1)
    {
        Serial.print("+++");Serial.print(i);Serial.print("+++");
        DeclinationMotor(RA_freq, NULL, HIGH);
    }
    i++;
}



void analogJoyStick()
{
    int NumbX, NumbY;


    while(digitalRead(JS_button) == LOW)
    {
        NumbX = analogRead(xPin);
        NumbY = analogRead(yPin);

        // Здесь мы переходим к перемещению мотора, и числа были созданы с помощью
        // небольшие числовые буферы, чтобы разрешить мертвые зоны. Это было сделано, чтобы быть линейным ответом
        // на ввод пользователей.

        if(NumbX > 530)
            RightAscensionMotor((RA_freq * (NumbX - 512)/512), NULL, HIGH);
        else if(NumbX < 500)
            RightAscensionMotor((RA_freq * (NumbX - 512)/512), NULL, LOW);
        else
            StopRAMotor();

        if(NumbY > 530)
            DeclinationMotor(800, NULL, HIGH);
        else if(NumbY < 500)
            DeclinationMotor(800, NULL, LOW);
        else
            StopDECMotor();
        delay(50);
    }
    // Отслеживать();
}


// кажется, что эта штука добавлена в конец цикла(), так что непосредственное пересечение должно быть
void serialEvent()
{
    char ident = Serial.read();     // первый символ будет символом-идентификатором

    if(ident == 'R')
    {
        int number1 = Serial.parseInt();        // эти переменные впоследствии должны стать глобальными
        Serial.read();
        int number2 = Serial.parseInt();

        // вы еще не знаете, что такое происхождение, так что..
    }
    else if(ident == 'M')
    {
        int raAngle = Serial.parseInt();
        Serial.read();                              // Эти последовательные чтения предназначены для разделителей в передаваемой строке
        int raDirection = Serial.parseInt();
        Serial.read();
        int dAngle = Serial.parseInt();
        Serial.read();
        int dDirection = Serial.parseInt();

        RightAscensionMotor(RA_freq, raAngle, raDirection);
        DeclinationMotor(DEC_freq, dAngle, dDirection);
    }

    // Serial.readbytes()
    Serial.read();      // это чтение символа новой строки из буфера.
}


// Это нужно вызвать, если
// если(Осенсор1 || Осенсор2)
    // меридианфлип();
void meridianFlipT()
{
    float finalLocationRA, finalLocationD;
    byte RACheck = LOW; byte DCheck = LOW;
    Serial.println("F1");    // новая строка уже включена


    // Верно, RA - это нормально, да что угодно, но склонение сделано таким образом, чтобы позволить противовесу
    // всегда направлен в сторону от штатива. Ибо широта равна 0 на полюсе.
    finalLocationRA = RightAscensionMotor(RA_freq, 180, !RA_track_DIR);
    finalLocationD = DeclinationMotor(DEC_freq, ((180 - DEC_degrees_from_home) * 2), !DEC_track_DIR);

    // Это проверит, повернулись ли оба мотора в соответствующие места
    // небольшой буферный период, который вводится только для того, чтобы увидеть, доберется ли мотор. Мы можем заменить это позже
    while(RACheck == LOW || DCheck == LOW)
    {
        if(RACheck == LOW && (finalLocationRA + 2 > RA_degrees_from_home && finalLocationRA - 2 < RA_degrees_from_home))
        {
            digitalWrite(RA_running, LOW); 
            RACheck = HIGH;
        }
        if(DCheck == LOW && (finalLocationD + 2 > DEC_degrees_from_home && finalLocationD - 2 < DEC_degrees_from_home))
        {
            digitalWrite(DEC_running, LOW); 
            DCheck = HIGH;
        }
        delay(10);
    }

    //Отслеживать(); // Возобновить отслеживание
    Serial.println("F0");
}


// Эти две функции — просто функции для перемещения моторов. Может иметь
// возвращаемся, чтобы сообщить, что все готово или что-то в этом роде
float RightAscensionMotor(float freqspeed, float angularDistance, byte direction)
{
    Serial.print(freqspeed); Serial.print("RAfS "); Serial.print(angularDistance); Serial.print("RAaD "); Serial.println(direction);

    int finalLocation = 0;
    if(freqspeed == NULL && angularDistance != NULL)
        freqspeed = RA_freq;
    if(angularDistance != NULL)
    {
        if(direction == HIGH)
            finalLocation = RA_degrees_from_home + angularDistance;
        else
            finalLocation = RA_degrees_from_home - angularDistance;    
    }

    SetPinFrequency(RA_STEP, freqspeed);                 // Посмотрите на SetPinFrequencySafe()
    pwmWrite(RA_STEP, 128);
    digitalWrite(RA_DIR, direction);
    digitalWrite(RA_running, HIGH);                 // Я думаю, это просто включает его?

    return finalLocation;
}

float DeclinationMotor(float freqspeed, float angularDistance, byte direction)
{
    Serial.print(freqspeed); Serial.print("DEfS "); Serial.print(angularDistance); Serial.print("DEaD "); Serial.println(direction);

    int finalLocation = 0;
    if(freqspeed == NULL && angularDistance != NULL)
        freqspeed = DEC_freq;
    if(angularDistance != NULL)
    {
        if(direction == HIGH)
            finalLocation = DEC_degrees_from_home + angularDistance;
        else
            finalLocation = DEC_degrees_from_home - angularDistance;    
    }

    SetPinFrequency(DEC_STEP, freqspeed);
    pwmWrite(DEC_STEP, 128);
    digitalWrite(DEC_DIR, direction);
    digitalWrite(DEC_running, HIGH);

    return finalLocation;
}

void StopRAMotor()
{
    Serial.println("-RAMotor Stopped-");
    digitalWrite(RA_STEP, LOW);
    digitalWrite(RA_running, LOW);
}
void StopDECMotor()
{
    Serial.println("-DECMotor Stopped-");
    digitalWrite(DEC_STEP, LOW);
    digitalWrite(DEC_running, LOW);
}

Вывод с последовательного монитора примерно через 10 секунд

, 👍0

Обсуждение

Вы пытались вместо этого поместить код в setup(). Он создан для вещей, которые должны запускаться только один раз., @chrisl

Я вполне мог бы, но это не помогает мне понять, почему это не сработает. (Кстати, я только что сделал, и это сработало отлично) @chrisl, @Liro

Ах, извините, F (частотная скорость) была тем, что я просто возился с хранением данных во флэш-памяти вместо SRAM, как я видел в некоторых других потоках. У меня не было этого во время фактического тестирования, поэтому я должен был удалить его из эта почта. Спасибо за улов @juraj Редактировать: Его сейчас удалили., @Liro

Да, И серийная строка внутри **if()** внутри функции **loop** имеет вывод переменной **i**, которая постоянно повторяет, что она равна 1. @Juraj, @Liro


1 ответ


2

Мое предположение, что происходит:

После выполнения кода в операторе if loop() содержит только приращение i. Это будет работать очень быстро. Через довольно короткое время (может быть, кто-то здесь может сделать расчет, чтобы получить время, которое необходимо) i переполнится, переходя от самого положительного значения к самому отрицательному значению, затем увеличиваясь дальше и, наконец, достигнув снова 1. Поскольку это происходит довольно быстро, вы думаете, что i не увеличивается, а просто переливается снова и снова. i имеет тип int, что означает, что требуется 65535 итераций loop(), чтобы добраться до 1 снова.

О версии с дополнительными вызовами Serial.print(): вы не показали нам свой setup(), но, вероятно, выбрали довольно низкую скорость передачи данных. (может быть 9600, как это используется во многих примерах). На первых итерациях вызовы просто помещают данные в буфер и быстро возвращаются. Но после нескольких итераций буфер Serial заполняется. В этом случае функции Serial.print()/Serial.write() (и родственные им функции) блокируют выполнение кода до тех пор, пока в буфере не освободится достаточно места. Таким образом, это значительно замедлит выполнение loop(), тем самым значительно увеличив время между переполнениями i — до такой степени, что вы не будете ждать достаточно долго, чтобы на самом деле увидеть, как он переполняется (но это произойдет в какой-то момент).


Чтобы выполнить часть кода только один раз, вы можете либо использовать функцию setup() (если вызов должен происходить в начале программы), либо использовать простой флаг. Представьте, что вы не увеличиваете i в loop(), а устанавливаете его значение равным нулю внутри оператора if. Это предотвратит повторное выполнение кода в операторе if, пока вы сами не установите для i значение 1 снова.

byte flag = 0;

void setup(){
    flag = 1; // Установите флаг в 1 где-нибудь в вашем скетче, как вам нравится, чтобы выполнить оператор if
}

void loop(){
    if(flag){
        //выполнить нужный код здесь
        flag = 0; // деактивируем этот код, установив флаг в 0
    }
}

Обратите внимание, что здесь я кое-что изменил:

  1. Я изменил имя переменной на flag, потому что это так: простой флаг только с двумя состояниями — нулевым и ненулевым

  2. Я изменил тип переменной на byte, так как вам не нужен целый int (16 бит) для простого флага. Поэтому я использовал наименьший тип переменной, byte. (здесь вы также можете использовать логическое значение, но в основном это то же самое)

  3. Теперь оператор if проверяет только значение flag напрямую. Он будет оцениваться как истина, если flag не равен нулю, как ложь, если он равен нулю. (Таким образом, вы можете установить для flag любое значение, отличное от нуля, чтобы активировать код, но true также определяется как 1, поэтому я остался в соответствии с этим).

  4. Я устанавливаю flag обратно в ноль внутри оператора if, чтобы он не выполнялся в другой раз. Он будет выполняться снова только в том случае, если вы сами установите flag снова в ненулевое значение где-то в своем коде.

,

Хм, это интересно, но я не понимаю, почему тогда он не может просто пропустить некоторые числа в какой-то момент и медленно повторять. Однако вы подали мне идею о том, что может произойти. Другими словами, буфер заполняется 8 отпечатками и цикл продолжается. Как только цикл снова достигает 1, он снова печатает те же 8 отпечатков, которые оказываются на **i** равными 1. Это объясняет, что он снова печатает каждые полсекунды. Если это так, то создание переменной **i** двойного размера или чего-то большего должно замедлить повторяющиеся распечатки., @Liro

Скрин распечатки вставлю в пост, @Liro

Также я только что понял, почему тогда, когда я раскомментирую строку Serial print над оператором **if()**, тогда переменная **i** правильно увеличивается и выводится., @Liro

Могу подтвердить, я сделал наоборот и сделал переменную **i** байтом, и теперь она снова выводит все намного быстрее, чем раньше. Увеличение его до числа с плавающей запятой заставляет его печатать только один, но я не ждал достаточно долго, чтобы компенсировать числовую разницу между int и числом с плавающей запятой., @Liro

Печать в операторе if может выводить только i как 1, поскольку она выполняется только в том случае, если i равно единице. Так что здесь вы не видите, увеличивается ли я. Приращение происходит вне оператора if, поэтому вы можете увидеть приращение только в том случае, если вы выводите i вне оператора if. Но только потому, что вы не видите, что он увеличивается, не означает, что он не увеличивается., @chrisl

Вы должны использовать long вместо float. Платы Arduino на основе AVR не имеют встроенного вычисления с плавающей запятой, поэтому использовать float вместо long — пустая трата времени вычислений., @chrisl

Во-первых, второй, да, я использовал long вместо float. Да, это то, что я говорю. Функция цикла продолжает перебирать int до тех пор, пока i не достигнет 1, добавляя 8 строк печати в буфер. Поскольку он делает это очень быстро, он просто печатает эти операторы снова и снова с приличной скоростью, каждый раз, когда я переполняюсь и нажимаю 1. Изменяя диапазон, в котором я могу зацикливаться, IE делает его байтовым или длинным, изменит скорость на которым напечатаны оттиски. Наличие внешнего serial.print просто заполняет буфер всеми теми другими числами, которые я делаю, заставляя это выглядеть в реальном времени., @Liro

Я могу проверить это позже, но я предполагаю, что если я оставлю это так, буфер Arduino в конечном итоге действительно заполнится, и он начнет пропускать числа. (когда у меня есть выражение out of **if()** без комментариев. Или, по крайней мере, это то, что я почерпнул, играя с ним и читая комментарии каждого., @Liro

Да, точно. Это также замедляет функцию цикла, @chrisl

Нет, он не будет пропускать номера. Вызовы печати будут ждать, пока не будет отправлено достаточно данных из буфера. Это замедляет работу скетча, но не приводит к потере серийных данных., @chrisl

Ах, правда, лол, да, я только что понял, когда ты это печатал, лол, @Liro