Выдуманное значение. откуда он его взял?

78125

✩✩✩✩✩✩✩
1 Дек 2019
76
2
Добрый день!
Контроллер крутит ролик протяжки шаговым двигателем, скорость получает по modbus
Попутно считает шаги и выcчиляет длину протянутого кабеля, и отправляет его обратно в modbus
Проблема в том, что иногда, абсолютно в разное время контроллер присылает в качестве метров левое число,
например было 100 метров, а следом приходит какое-нибудь 25218.
Проблема точно не в канале передачи данных, контроллер уверенно шлет дальше кривое значение стабильно вращая роликом как ни в чем не бывало.
Помогите, как вычислить и устранить глюк?
C++:
#include <ModbusRtu.h>
#include <GyverTimers.h>
bool s=0;
int modbus_array[3] = {4000, 0, 0}; //Готовим массив данных скорость и метраж, третье значение не исп
int ledState = LOW;
long LEDTimer, Steps = 0;
int Meters = 0;
Modbus bus(2,1,4); // this is slave @2 and RS-232 or USB-FTDI

void setup() {
              pinMode(StepperEnb, OUTPUT);
              pinMode(StepperDir, OUTPUT);
              pinMode(StepperPul, OUTPUT);
              digitalWrite(StepperEnb, LOW);
              digitalWrite(StepperDir, LOW);
              bus.begin( 19200 ); // baud-rate at 19200
              Timer2.setFrequency(4000); //частота таймера
              Timer2.enableISR(CHANNEL_A); // Подключить прерывание таймера 2, канал A
              Serial.begin(19200);
              }

ISR(TIMER2_A) { //прерывание по таймеру 2А
                s = not(s);
                digitalWrite(StepperPul, s);
                Steps++;
               }

void loop() {
bus.poll(modbus_array,sizeof(modbus_array)/sizeof(modbus_array[0]));       //Used to receive or write value from Master
Meters = Steps/6400*3.1415*0.0420;
modbus_array[1] = Meters;
if (modbus_array[0] == 0) {
                          Timer2.pause();
                          } else {
                                  Timer2.setFrequency(modbus_array[0]); //обновляем скорость
                                  }
if(millis() - LEDTimer > 500) {   
  Serial.println(Meters);
                              LEDTimer = millis();
                              ledState = !ledState;
                              digitalWrite(ledPin, ledState);  //мигаем
                              }
}
 

bort707

★★★★★★✩
21 Сен 2020
3,058
910
Для начала все параметры, изменяемые в прерывании, должны быть volatile
 

78125

✩✩✩✩✩✩✩
1 Дек 2019
76
2
Спасибо.
указания переменных что живут в прерывании достаточно?
C++:
volatile bool s = 0;
volatile unsigned long Steps = 0;
 

bort707

★★★★★★✩
21 Сен 2020
3,058
910
указания переменных что живут в прерывании достаточно?
для прерывания - да. А вот для правильной работы программы не знаю...
Например не вижу, где вы проверяете, пришли ли новые данные по модбасу?
 

78125

✩✩✩✩✩✩✩
1 Дек 2019
76
2
нигде. просто в лоб обновляю частоту таймера.
C++:
Timer2.setFrequency(modbus_array[0]);
 

bort707

★★★★★★✩
21 Сен 2020
3,058
910
Вы это делаете сотни раз в секунду? Зачем его обновлять, если например оно не менялось?

Я не хочу сказать, что из-за этого программа глючит - может оно и не влияет, но зачем делать неправильно... У метода poll есть возвращаемый параметр, который показывает, пришли новые данные или нет.
 
  • Лойс +1
Реакции: 78125

78125

✩✩✩✩✩✩✩
1 Дек 2019
76
2
Хуже не будет, добавил.
C++:
if (modbus_array[0] != Frequency) { //проверим изменения
                                   Frequency = modbus_array[0];
                                   if (modbus_array[0] == 0) {
                                                              Timer2.pause();
                                                              } else {
                                                                      Timer2.setFrequency(modbus_array[0]); //обновляем скорость
                                                                      }
                                  }
 

Bruzzer

★★★✩✩✩✩
23 Май 2020
472
134
@78125,
Волатильность в данном конкретном случае не обязательна, но надо запрещать прерывания при чтении многобайтной переменной изменяемой в прерывании.
noInterrupts();
Meters = Steps/6400*3.1415*0.0420;
interrupts();
 

78125

✩✩✩✩✩✩✩
1 Дек 2019
76
2
Попробую.
но ведь оно при вычислении метров не один раз ошибается, а потом тоже.
то есть программа как то "ломает" заначение Steps в памяти?
 

Bruzzer

★★★✩✩✩✩
23 Май 2020
472
134
@78125,
Если после сбоя продолжает счет с неправильного сбойного значения, значит дело не в отсутствии запрета прерываний. Но их запрещать надо.
Неправильно выводит строку Serial.println(Meters); Да?
 

78125

✩✩✩✩✩✩✩
1 Дек 2019
76
2
неправильным я его вижу уже на принимающей стороне modbus но потом оно продолжает идти вперед со сбойного момента.
добавил отключение прерываний и волатильность

C++:
#include <ModbusRtu.h>
#include <GyverTimers.h>
volatile bool s = 0;
volatile unsigned long Steps = 0;
bool ledState = 0;
unsigned int modbus_array[3] = {4000, 0, 0}; //Готовим массив данных скорость и метры
unsigned int Frequency = modbus_array[0];
unsigned long LEDTimer = 0;
Modbus bus(2,1,4); // this is slave @2 and RS-232 or USB-FTDI

void setup() {
              pinMode(StepperEnb, OUTPUT);
              pinMode(StepperDir, OUTPUT);
              pinMode(StepperPul, OUTPUT);
              pinMode(ledPin, OUTPUT);
              digitalWrite(StepperEnb, LOW);
              digitalWrite(StepperDir, LOW);
              bus.begin( 19200 ); // baud-rate at 19200
              Timer2.setFrequency(4000); //частота таймера
              Timer2.enableISR(CHANNEL_A); // Подключить прерывание таймера 2, канал A
              //Serial.begin(19200);
              }

ISR(TIMER2_A) { //прерывание по таймеру 2А
                s = not(s);
                digitalWrite(StepperPul, s);
                Steps++;
               }

void loop() {
bus.poll(modbus_array,sizeof(modbus_array)/sizeof(modbus_array[0]));       //Used to receive or write value from Master
noInterrupts(); // откл прерывания
modbus_array[1] = int(Steps/6400*3.1415*0.0420);
interrupts(); // вкл прерывания
if (modbus_array[0] != Frequency) { //проверим изменения
                                   Frequency = modbus_array[0];
                                   if (modbus_array[0] == 0) {
                                                              Timer2.pause();
                                                              } else {
                                                                      Timer2.setFrequency(modbus_array[0]); //обновляем скорость
                                                                      }
                                  }                                     
if(millis() - LEDTimer > 500) {   
                              LEDTimer = millis();
                              ledState = !ledState;
                              digitalWrite(ledPin, ledState);  //мигаем
                              }
}
на принимающей стороне приходит после корректных данных просто 0 и начинает считать дальше.
как будто контроллер просто перезапустился =((
как нибудь это можно проверить?
 

Boroda22

★✩✩✩✩✩✩
23 Фев 2022
251
42
Возможно переполнение типа происходит, поэтому и глюк, но это не точно.
 

78125

✩✩✩✩✩✩✩
1 Дек 2019
76
2
чтобы переполнить переменные надо годами работать...
шаги unsigned long Steps
метров поместится аж 65 тысяч
unsigned int modbus_array[3]
 

Kir

★✩✩✩✩✩✩
28 Мар 2020
69
16
@78125,
Возможно проблема в порядке байт.
в LE представленнии 100 = uint16_t { 0x64, 0x00 }
в BE представлении 100 = uint16_t { 0x00, 0x64 }
если uint16_t собранное для BE интерпретировать в LE как есть. получим не 100, а 25600.

Разберите пакет на стороне, которая принимает данные о метрах, скорее всего проблема на "той" стороне.