Почему "if" медленный?

Я новичок в мире Arduino и надеюсь найти здесь решение. Код ниже функционально работает нормально. Проблема в том, что в строке 45 я вставил оператор if для изменения dac_value. Он очень медленный, пока последовательный, и все, что вне if, работает быстро/хорошо... Что бы было решением, чтобы сделать его быстрым? Пробовал if - else if, а также while... результат тот же. Надеюсь, кто-то может помочь. Спасибо.

{    
#include <Arduino.h>
#include <Wire.h>
// #include <LiquidCrystal_I2C.h>
#define MCP4725 0x61
#define MCP4725B 0x60
unsigned int val;
byte buffer[3];
byte buff[3];

int ClockPinV = PB1; 
int ClockPinA = PA6; 
const int currsensPin = PA3;
const int voltsensPin = PA2;

void setup()
{
  Serial.begin(115200);
  pinMode(currsensPin , INPUT_ANALOG);
  pinMode(voltsensPin , INPUT_ANALOG); 
  pinMode(ClockPinV , INPUT);
  pinMode(ClockPinA , INPUT);
  Wire.begin();
}

int val0 = 1024;
int val1 = 4096;
volatile int dac_value = 0;

void loop()
{
  analogReadResolution(12);


  volatile int svolt = analogRead(ClockPinV);
  volatile int scurr = analogRead(ClockPinA);
  volatile int set_voltage = (3.3/4096)*svolt;
  volatile int set_curr =  (3.3/4096)*scurr;
  volatile int securr = analogRead(currsensPin);
  volatile int real_curr = (3.3/4096)*securr;
  volatile int sevolt= analogRead(voltsensPin);
  volatile int real_output2 = (3.3/4096)*sevolt;
  

if(real_curr < set_curr){
    if(set_voltage > real_output2)
    {
      dac_value = dac_value + 1;
    }
    if(set_voltage < real_output2)
    {
      dac_value = dac_value - 1;
    }  
  }
  else if(real_curr > set_curr)
  {
    dac_value --;         
  }

  dac_value = constrain(dac_value, 0, 2673);
  buffer[0] = 0b01000000; //управляющий байт
  buffer[1] = dac_value >> 4; //MSB 11-4 сдвиг вправо на 4 позиции
  buffer[2] = dac_value << 4; //LSB 3-0 сдвиг влево на 4 позиции
  
  Wire.beginTransmission(MCP4725); // адрес устройства
  Wire.write(buffer[0]); //указатель
  Wire.write(buffer[1]); //8 старший бит
  Wire.write(buffer[2]); //4 младший бит
  Wire.endTransmission();

  // значение1 = ограничение (значение1, 0, 2673);
  buff[0] = 0b01000000; //управляющий байт
  buff[1] = svolt >> 4; //MSB 11-4 сдвиг вправо на 4 позиции
  buff[2] = svolt << 4; //LSB 3-0 сдвиг влево на 4 позиции
  
  Wire.beginTransmission(MCP4725B); // адрес устройства
  Wire.write(buff[0]); //указатель
  Wire.write(buff[1]); //8 старший бит
  Wire.write(buff[2]); //4 младший бит
  Wire.endTransmission();

  
  Serial.println("=============================");
  Serial.println("=============================");
  Serial.println("========= Volt Test =========");
  Serial.println("test: " + String(set_voltage));
  Serial.println("      " + String(real_output2));
  Serial.println("======== Current Test =======");
  Serial.println("test: " + String(set_curr));
  Serial.println("      " + String(real_curr));
  Serial.println("=============================");
  Serial.println("=============================");
}
}

, 👍4

Обсуждение

Как вы видите, что это операторы if, которые медленные? Вы уверены, что ваша проблема не в другом? А что значит "медленный" в данном случае?, @chrisl

Я предполагаю, что... подключил потенциометр к аналоговому выводу и вывел напрямую на ЦАП - он работал быстро..., @Marius

Почему вы везде используете **volatile**? Что вы подразумеваете под "медленно" и "быстро"? Как вы это измерили?, @Nick Gammon

"если" не медленно, просто для информации. То, что вы делаете внутри «если», будет влиять на то, что делает остальная часть кода. Например, если вы говорите «если сегодня понедельник, поспите 4 часа», то проблема не в «если», а в том, чтобы «поспать 4 часа»., @Nick Gammon

Не сам оператор if делает ваш код медленным. Но вполне может быть, что скетч ведет себя не так, как вы ожидаете, что заставляет вас думать, что он был медленным. Сравните и распечатайте метки времени, чтобы определить, сколько времени занимает выполнение определенных частей., @Sim Son

если я читаю потенциометр на аналоговом выводе и записываю в ЦАП в последовательной печати, я вижу, как он меняет значение ЦАП, когда я вращаю потенциометр. В то же время, если выполняются условия в if и я увеличиваю напряжение, я вижу, что dac_value увеличивается очень медленно. требуется несколько секунд, пока он не достигнет установленного значения..., @Marius

как я уже сказал... новичок здесь, так что просьба не прыгать на меня. Я согласен, что моя проблема может быть чем-то другим - поэтому я написал. надеюсь найти решение и узнать что-то новое: P, @Marius

На вас никто не нападает. Нам просто интересно кое-что о вашем коде. Если вы можете объяснить, мы можем лучше посоветовать вам., @Nick Gammon

*если условия в if соблюдены и я увеличиваю напряжение, я вижу, что dac_value увеличивается очень медленно.* - Вы имеете в виду, что последовательные отпечатки медленнее?, @Nick Gammon

да, но только ЦАП читает dac_value. Если я напечатаю что-нибудь еще + dac_value, все быстро обновится, кроме dac_value..., @Marius

Я этого не понял., @Nick Gammon

Из вашего кода кажется, что вы пытаетесь выполнить цикл управления (вывод управляющего сигнала через ЦАП, чтение коррелированных значений обратно и изменение управляющего сигнала на основе этого. Поведение таких контуров управления не всегда очевидно. Я скорее уверен, что это один из таких случаев. Поэтому, пожалуйста, опишите, как все связано, что вы пытаетесь контролировать и что именно вы ожидаете. Схема или схема подключения (и, возможно, хорошее изображение вашей реальной цепи) также будет помогите здесь, @chrisl

попробуйте if (real_curr <= set_curr){, @Juraj

int ClockPinV = PB1; интервал ClockPinA = PA6; это ерунда, кстати. pinMode и AnalogRead работают с номерами выводов Arduino., @DataFiddler

@DataFiddler: сначала сделал это с поворотным приводом. Удалено и теперь используется pot, но имя вывода не изменено... также плата stm32f103, @Marius

добавлен полный main.cpp на github. https://github.com/mariusdp/psu_test.git, @Marius


1 ответ


4

Утверждения if (во множественном числе), которые вы разместили на GitHub сильно отличается от того, что вы написали выше. Они содержат много команд print для ЖК-дисплея, поэтому я думаю, что это замедляет работу вашего кода. Это хорошая попытка красиво отформатировать вывод на дисплее, но это довольно запутанно.

Вот упрощенная версия, которая записывает на ЖК-дисплей как можно меньше (2 перемещения курсора и 2 печати). Форматирование строки выполняется в памяти Arduino перед ее отправкой на ЖК-дисплей в виде одной полной строки.

Я не мог понять ваши имена переменных, поэтому переименовал их.

настройка()

Я переместил analogReadResolution(12) из loop() в setup() для небольшого повышения производительности.

void setup()
{
  pinMode(currsensPin , INPUT_ANALOG);
  pinMode(voltsensPin , INPUT_ANALOG);
  pinMode(voltPin, INPUT_ANALOG);
  pinMode(currPin, INPUT_ANALOG);
  Wire.begin();
  lcd0.begin(20, 4);
  lcd0.backlight();

  analogReadResolution(12);  // Перемещено из loop(). Только нужно позвонить один раз.

  Serial.begin(115200);
}

loop()

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

float real_current_offset = 0;

void loop()
{
  //
  // Получение выборок данных как можно ближе друг к другу без прерываний.
  //
  cli();  // Отключить прерывания.
  int svolt = analogRead(voltPin);
  int scurr = analogRead(currPin);
  int raw_voltage = analogRead(voltsensPin);
  int raw_current = analogRead(currsensPin);
  sei();  // Включить прерывания.

  // Рассчитать заданное напряжение и ток.
  float set_voltage = (3.3 / 4096) * svolt;
  float set_current =  (3.3 / 4096) * scurr;

  // Рассчитать реальное напряжение и ток.
  float real_voltage = (3.3 / 4096) * raw_voltage;
  float real_current = raw_current * (3.3 / 4096) - real_current_offset;
  float real_current_ma = real_current * 1000.0;
  
  int dac_value = dac_value
              + (real_current <= set_current) * (set_voltage > real_voltage)
              - (real_current < set_current) * (set_voltage < real_voltage)
              - (real_current > set_current);

  buffer[0] = 0b01000000;
  buffer[1] = dac_value >> 4;
  buffer[2] = dac_value << 4;
  Wire.beginTransmission(MCP4725);
  Wire.write(buffer[0]);
  Wire.write(buffer[1]);
  Wire.write(buffer[2]);
  Wire.endTransmission();

  DisplayDataOnLcd(set_voltage, set_current, real_voltage, real_current);
}

DisplayDataOnLcd()

Я создал новую функцию для отображения данных на ЖК-дисплее. Рекомендуется разбивать код на более мелкие функциональные блоки.

void DisplayDataOnLcd(const int set_voltage, const int set_current, const int real_voltage, const int real_current)
{
  Serial.println("\nLCD Data Display");

  // Определить форматы для печати на ЖК-дисплее.
  const char *data_format_set  = "Set : %s V %s mA";
  const char *data_format_real = "Real: %s V %s mA";
  char voltage_str[10];
  char current_str[10];
  char data_str[30];

  // Отформатируйте установленное напряжение и ток и отобразите их на ЖК-дисплее.
  dtostrf(set_voltage, 6, 3, voltage_str);
  dtostrf(set_current, 6, 3, current_str);
  sprintf(data_str, data_format_set, voltage_str, current_str);
  lcd0.setCursor(0, 0);
  lcd0.println(data_str);
  Serial.println(data_str);

  // Форматируем реальное напряжение и ток и отображаем их на ЖК-дисплее.
  dtostrf(real_voltage, 6, 3, voltage_str);
  dtostrf(real_current, 6, 3, current_str);
  sprintf(data_str, data_format_real, voltage_str, current_str);
  lcd0.setCursor(0, 1);
  lcd0.println(data_str);
  Serial.println(data_str);
}
,

Спасибо. Попробую позже сегодня. В: Я многого не знаю, поэтому будьте терпеливы со мной, пожалуйста. На ЖК-дисплее единственное значение, которое обновляется медленно, - это чтение с dac (dac_value), все остальное обновляется быстро... Вот почему я предположил, что делаю что-то не так... . Тест, который я сделал, заключался в следующем: потенциометр на аналоговом входе и показания, отправленные через преобразователь i2c в dac, - вывод из преобразователя на другой вывод adc в качестве обратной связи и обратная связь на ЖК-дисплей через i2c ==> эта установка обновлялась в режиме реального времени (или достаточно быстро воспринимать как реальное время и уметь с ним работать)..., @Marius

Я изо всех сил пытаюсь понять ваш контур управления. Не могли бы вы выложить полную схему этого, пожалуйста? Отредактируйте исходный пост, чтобы добавить изображение. Я не вижу, как запись 3 байтов в ЦАП MCP4725 со стандартной скоростью 100 кбит/с резко замедляет цикл. Попробуйте добавить операторы отладки в цикл Serial.println() во время каждой итерации., @tim

ЦАП подключен к аналоговому контакту, управляемому потенциометром или энкодером и условными выражениями "если"... ЦАП управляет интегральной микросхемой драйвера MOSFET. Делитель для монитора напряжения (real_voltage) и сделал датчик тока нижней стороны из LM358 и шунт для "real_current". Способ должен работать, если set_volt < real_volt, выходной сигнал ЦАП должен стать ниже ... он делает это, но медленнее. Короче говоря, курил бы (выход до 100 В постоянного тока, 20 ампер, ЛИНЕЙНЫЙ). Прежде чем спросить - линейный менее шумный и намного дешевле в сборке, чем купить + с использованием trafo, я вижу глупо заставлять его переключать режимы. :Р СПАСИБО!, @Marius

Хороший ответ, парень, я думаю, что твое предположение было правильным. Каждый жидкокристаллический дисплей I2C, который я использовал с Arduino, ужасно медленный по нашей причине. +1, @Tim_Stewart