Запуск кода кодировщика одновременно с другим кодом в Arduino

volatile unsigned int temp, counter = 0; 
int county=0; 
void setup()
{
  
  Serial.begin(9600);   
  pinMode(2, INPUT_PULLUP); 
  pinMode(3, INPUT_PULLUP); 
  attachInterrupt(0, ai0, RISING);
  attachInterrupt(1, ai1, RISING);  
}
 
void loop()
{
  county= (counter);
  if (counter>2000){
county=0;
  }
  temp = counter;
  
  Serial.println(county); // Счетчик шагов энкодера
  
}

  void ai0() {if(digitalRead(3)==LOW) {counter++;}else{counter--;}}
  void ai1() {if(digitalRead(2)==LOW) {counter--;}else{counter++;}} 

Уважаемые друзья, приведенный выше код Arduino используется для кодировщика в качестве счетчика и код взят в кавычки из здесь приведенный выше простой код работает нормально. Однако, если я добавлю несколько строк кода ниже Serial.println(county);, что на самом деле задержит время выполнения, счетчик на самом деле пропустит шаги и испортит вверх по прилавку, который я очень разочарован.

Могу ли я спросить, как решить эту проблему, я думаю, может ли arduino одновременно запускать 2 цикла void, чтобы код ccounter мог выполняться одновременно и на него не влияли другие строки кода, которые вводят время задержки, которое беспорядок вверх по прилавку. Будем признательны за любую помощь :)

volatile unsigned int temp, counter = 0; 
void setup()
{
  
  Serial.begin(9600);   pinMode(2, INPUT_PULLUP); pinMode(3, INPUT_PULLUP); attachInterrupt(0, ai0, RISING);attachInterrupt(1, ai1, RISING);  
}
 
void loop()
{
noInterrupts();
unsigned int county = counter;
interrupts();

  if (counter>2000){county=0;}
  
  Serial.println(county); // Счетчик шагов энкодера
  
}

  void ai0() {if(digitalRead(3)==LOW) {counter++;}else{counter--;}}
  void ai1() {if(digitalRead(2)==LOW) {counter--;}else{counter++;}} 

PID-код

#include <PID_v1.h>
double Setpoint ;  double Input; double Output ;
volatile unsigned int temp, counterrr = 0; 
int county=0; 

double Kp=0, Ki=2, Kd=0;  
PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT);


void setup()
{
  
  Serial.begin(9600);   
  Setpoint = 120;
  myPID.SetMode(AUTOMATIC);
  myPID.SetTunings(Kp, Ki, Kd);
  pinMode(2, INPUT_PULLUP); 
  pinMode(3, INPUT_PULLUP); 
  attachInterrupt(0, ai0, RISING);
  attachInterrupt(1, ai1, RISING);  
}
 
void loop()
{
    
}

  void ai0() {
    if(digitalRead(3)==LOW) {
    counterrr++;}
    else{
      counterrr--;}

        county= (counterrr);
  Serial.println(county);
  Input = map(county, 0, 1024, 0, 1200);  // фотодатчик устанавливается на аналоговый контакт 5
  myPID.Compute();
  analogWrite(9,Output); 
  Serial.print(Input);
  }
  void ai1() {
    if(digitalRead(2)==LOW) {
      counterrr--;}
      else{
        counterrr++;
        }

          county= (counterrr);
  Serial.println(county);
  Input = map(county, 0, 1024, 0, 1200);  // фотодатчик устанавливается на аналоговый контакт 5
  myPID.Compute();
  analogWrite(9,Output); 
  Serial.print(Input);
        }  

, 👍1


1 ответ


3

может ли arduino одновременно запускать 2 цикла void

Нет, нельзя. Все Arduino на основе AVR (такие как Uno, Nano, Mega) имеют только одно ядро, поэтому они могут выполнять только одну операцию за раз.

Обычный способ – просто не вводить задержек и делить сложные вещи на более мелкие части. Таким образом, вы сможете быстро переключаться между разными частями кода, не блокируясь на более длительное время. Для людей тогда будет казаться, что части выполняются одновременно, хотя на самом деле они выполняются последовательно.

Чтобы делать вещи с таймером, вы должны использовать неблокирующий стиль кодирования из примера BlinkWithoutDelay, который поставляется с Arduino IDE. Он использует функцию millis() в качестве секундомера, на каждой итерации цикла проверяя, прошло ли достаточно времени с момента последнего выполнения, и выполняя снова, только если прошло достаточно времени. Так же, как вы бы не стали печь пиццу и сидеть перед духовкой, пока она не будет готова. Вы бы регулярно смотрели на часы, чтобы проверить, достаточно ли времени прошло, занимаясь другими делами между ними.


Тем не менее, вы изменили код после копирования и исказили его странным образом, так что это не имеет особого смысла. Кроме того, исходный код не является безопасным для прерываний.

Код в loop() должен выполнять атомарный доступ к изменчивой переменной counter. Это делается для предотвращения изменения переменной между ее чтением. Чтение однобайтовой переменной (типа byte) всегда является атомарным и не может быть прервано, поскольку это одна инструкция, которая будет завершена до вызова любой функции обслуживания прерываний (ISR). Но unsigned int имеет размер 2 байта. Поэтому в редких случаях может произойти следующее:

  • Первый байт считывается из энергозависимой переменной в энергонезависимую.
  • ISR запускает и изменяет изменчивую переменную
  • Второй байт (который теперь изменен) считывается в энергонезависимую переменную

Это может привести к повреждению данных.

Обычный способ сделать это — временно отключить прерывания, скопировать переменную volatile в локальную переменную, а затем снова разрешить прерывания. Затем вы можете использовать локальную переменную в качестве моментального снимка изменяемой переменной без повреждения данных.

noInterrupts();
unsigned int local_counter = counter;
interrupts();
if(local_counter > 2000)
    ...

Для вашего кода вы также должны использовать тот же механизм, что и в исходном коде, и отправлять значение по последовательному номеру только в том случае, если оно действительно изменилось. В противном случае вы будете спамить ваше последовательное соединение, что автоматически замедлит ваш код (поскольку вы заполняете последовательный буфер быстрее, чем он отправляется, так что дальнейшие вызовы Serial.println() будут блокировать до тех пор, пока в буфере не будет достаточно места).

,

Привет, я использовал ваш код выше, и он не работает, проблема все та же. Итак, я попробовал функцию millis() в своем обновленном коде в своем посте :), но она по-прежнему дает ту же проблему. Могу я спросить, правильный ли мой обновленный код в моем посте :)?, @Maximus Su

Извините, но это совсем другой код. В первом вы использовали прерывания для подсчета (что хорошо), во втором вы убрали весь код прерывания. И из-за этого вы не использовали мой код (этот код нужен только при использовании прерываний для подсчета)., @chrisl

«это не работает, проблема все та же» - я думаю, сначала вам действительно нужно понять, что делает код из этого видео. Похоже, вы этого не знаете. И тогда вы можете начать изменять его. На данный момент вы показали, какой код вы добавили к оригиналу, а также не указали, что именно не работает. Вам необходимо предоставить подробное описание того, что вы ожидали и что произошло на самом деле, а также код и последовательный вывод рассматриваемого кода. В противном случае мы не сможем вам помочь, @chrisl

Привет, Крисл, я обновил свой код, чтобы использовать noInterrupts() в своем сообщении, могу ли я узнать, правильно ли это? :), @Maximus Su

Большое спасибо за ответ, кстати, очень лаконичный :), @Maximus Su

Лучше, но вы все еще используете counter в операторе if и для присваивания temp. Смысл в том, чтобы использовать counter только для присвоения его значения энергонезависимой переменной, в данном случае county. В последующем коде (внутри цикла()) вы должны использовать только county., @chrisl

И если вы хотите сбросить счетчик, сделайте это внутри ISR, то есть в ai0() и ai1(), @chrisl

Давайте продолжим это обсуждение в чате, @Maximus Su