Обновление LCD экрана прерывает работу шагового двигателя

dxf

✩✩✩✩✩✩✩
17 Апр 2020
47
2
Подскажите как можно поправить код, что бы не происходило прерывания работы шагового двигателя при обновлении экрана.
Обновление экрана необходимо делать с пузой 500 мс, т.к. выводятся показания датчиков, которые опрашиваются ещё чаще.

Код:
#include <Arduino.h>

#include "GyverStepper2.h"
GStepper2<STEPPER2WIRE> stepper(2048, 3, 4);
bool bool_tmr1 = 1;
bool bool_tmr2 = 0;
uint32_t tmr1 = millis();
#define STEPPER_PIN_START 11

#include <EncButton.h>
#define BTN_PIN_START 12
EncButton<EB_TICK, BTN_PIN_START> btn1;
bool flagStart = false;
bool flagStaus = false;

#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);
uint32_t timerLCDredraw = 0;

void LCD()
{
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("11111111");
}

void setup()
{
  Serial.begin(9600);
  pinMode(STEPPER_PIN_START, OUTPUT);
  digitalWrite(STEPPER_PIN_START, LOW);
  lcd.init();
  lcd.backlight();
}

void loop()
{
  btn1.tick();
  stepper.tick();

  if (btn1.press())
  {
    flagStart = !flagStart;
    flagStaus = !flagStaus;
  }

  if (flagStart == true && flagStaus == true)
  {
    stepper.setSpeedDeg(120);
    digitalWrite(STEPPER_PIN_START, LOW);
    flagStaus = !flagStaus;
  }
  if (flagStart == false && flagStaus == true)
  {
    digitalWrite(STEPPER_PIN_START, HIGH);
    flagStaus = !flagStaus;
  }

  if (millis() - timerLCDredraw >= 500)
  {
    timerLCDredraw = millis();
    LCD();
  }
}
 

dxf

✩✩✩✩✩✩✩
17 Апр 2020
47
2
@Старик Похабыч, верно ли понимаю что надо добавить?
Выделил в коде изменения, но это ничего не меняет. Так же есть прерывания в движении обусловленные обновлением экрана.
Новый код:
#include <Arduino.h>

#include "GyverStepper2.h"
GStepper2<STEPPER2WIRE> stepper(2048, 3, 4);
bool bool_tmr1 = 1;
bool bool_tmr2 = 0;
uint32_t tmr1 = millis();
#define STEPPER_PIN_START 11
uint32_t timerStepper = 0;

#include <EncButton.h>
#define BTN_PIN_START 12
EncButton<EB_TICK, BTN_PIN_START> btn1;
bool flagStart = false;
bool flagStaus = false;

#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);
uint32_t timerLCDredraw = 0;

void LCD()
{
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("11111111");
}

void setup()
{
  Serial.begin(9600);
  pinMode(STEPPER_PIN_START, OUTPUT);
  digitalWrite(STEPPER_PIN_START, LOW);
  lcd.init();
  lcd.backlight();
  stepper.reverse(1);
}

void loop()
{
  btn1.tick();

  if (micros() - timerStepper >= 7)
  {
    timerStepper = micros();
    stepper.tick();
  }

  if (btn1.press())
  {
    flagStart = !flagStart;
    flagStaus = !flagStaus;
  }

  if (flagStart == true && flagStaus == true)
  {
    stepper.setSpeedDeg(60);
    digitalWrite(STEPPER_PIN_START, LOW);
    flagStaus = !flagStaus;
  }
  if (flagStart == false && flagStaus == true)
  {
    digitalWrite(STEPPER_PIN_START, HIGH);
    flagStaus = !flagStaus;
  }

  if (millis() - timerLCDredraw >= 500)
  {
    timerLCDredraw = millis();
    LCD();
  }
}
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,263
1,301
Москва
Нет не верно, посмотрите библиотеку GyverTimer , там есть примеры.
По прерыванию функция будет вызываться с постоянным интервалом, вне зависимости от того, процесса, что сейчас происходит.
 

dxf

✩✩✩✩✩✩✩
17 Апр 2020
47
2
@Старик Похабыч, что то не ясно. Библиотека также использует micros() в результате паузы в работе шаговика никуда не деваются.

Код с библиотекой таймера:
#include <Arduino.h>

#include "GyverStepper2.h"
GStepper2<STEPPER2WIRE> stepper(2048, 3, 4);
bool bool_tmr1 = 1;
bool bool_tmr2 = 0;
uint32_t tmr1 = millis();
#define STEPPER_PIN_START 11

#include <EncButton.h>
#define BTN_PIN_START 12
EncButton<EB_TICK, BTN_PIN_START> btn1;
bool flagStart = false;
bool flagStaus = false;

#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);
uint32_t timerLCDredraw = 0;

#include <TimerMs.h>
TimerMs tmr(7, 1, 0);

void LCD()
{
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("11111111");
}

void setup()
{
  Serial.begin(9600);
  pinMode(STEPPER_PIN_START, OUTPUT);
  digitalWrite(STEPPER_PIN_START, LOW);
  lcd.init();
  lcd.backlight();
  stepper.reverse(1);

  tmr.setMicros(true);
  tmr.setPeriodMode();
}

void loop()
{
  btn1.tick();

  if (tmr.tick())
  {
    stepper.tick();
  }

  if (btn1.press())
  {
    flagStart = !flagStart;
    flagStaus = !flagStaus;
  }

  if (flagStart == true && flagStaus == true)
  {
    stepper.setSpeedDeg(60);
    digitalWrite(STEPPER_PIN_START, LOW);
    flagStaus = !flagStaus;
  }
  if (flagStart == false && flagStaus == true)
  {
    digitalWrite(STEPPER_PIN_START, HIGH);
    flagStaus = !flagStaus;
  }

  if (millis() - timerLCDredraw >= 500)
  {
    timerLCDredraw = millis();
    LCD();
  }
}
@poty, почитал ещё про аппаратный таймер. Пока не понимаю как его задействовать. Но если не ошибаюсь то при перенастройке аппаратного таймера Timer0, изменится работа millis(), что не подходит, т.к. от его стандартного значения зависят другие участки кода.
 

poty

★★★★★★✩
19 Фев 2020
3,230
940
@dxf, я не знаю,, что за микропроцессор используется, но, допустим, в Atmega 328P - три таймера. Используйте два оставшихся. Настраиваете прерывание по таймеру, в обработчике прерывания вызываете нужную команду. Насколько я помню, это даже в примерах есть.
Библиотека также использует micros()
Вы говорите о разных библиотеках. Термин "таймер", вводящий в заблуждение, используется широко Гайвером как "программный таймер", что на самом деле по смыслу подходит, но пересекается с "аппаратными таймерами" в понятийном аппарате микропроцессоров.
 

dxf

✩✩✩✩✩✩✩
17 Апр 2020
47
2
@poty, все верно использую Atmega 328P. Спасибо.
Почитал у Гайвера, что от аппаратного таймера Timer0 зависит millis(). Просто везде читаю, что в библиотеке Гайвера можно задействовать аппаратный таймер, но не разобрался с этим.
С ардуино не настолько знаком, что бы задействовать аппаратный таймер, буду изучать. Если чего подскажете, очень поможет.
Тему прошу не закрывать пока.
 

Bruzzer

★★★✩✩✩✩
23 Май 2020
473
133
@dxf,
Посмотрите Пример sweepISR.ino из GyverStepper2.h
Насколько он рабочий не проверял.
 

dxf

✩✩✩✩✩✩✩
17 Апр 2020
47
2
@Старик Похабыч, как раз здесь смотрел. Представленный в статье "Простой пример с прерываниями", не рабочий.

Посмотрел статью на хабре, там более понятно написано. Решил пойти другим путем, обновлять экран в прерывании, а не работу шаговика. Пример сделал, но не понимаю почему не работает с обновлением LCD экрана (аналогично с с шаговым двигателем). Экранчик просто черный (отключен), а светодиод постоянно горит. Если отключить в прерывании обновление экрана (срока 55 в коде), то светодиод на 13 пине исправно мигает раз в секунду как задано регистрами.

тест работы прерывания:
// avr-libc library includes
#include <avr/io.h>
#include <avr/interrupt.h>
#include <Arduino.h>
#define LEDPIN 13

#include <LiquidCrystal_I2C.h>
#define _LCD_TYPE 1 // для работы с I2C дисплеями
#include <LCD_1602_RUS_ALL.h>
// LCD_1602_RUS lcd(0x3F, 20, 4);
LCD_1602_RUS lcd(0x27, 20, 4);

int16_t WX = 123;
String textSetting = "sdf";

void LCD()
{
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("TEST");
  lcd.setCursor(1, 1);
  lcd.print(textSetting);
  lcd.setCursor(0, 2);
  lcd.print("HYS");
  lcd.setCursor(1, 3);
  lcd.print(WX, 1); // Вывод
}

void setup()
{
  pinMode(LEDPIN, OUTPUT);

  // инициализация Timer1
  cli();      // отключить глобальные прерывания
  TCCR1A = 0; // установить TCCR1A регистр в 0
  TCCR1B = 0;

  // включить прерывание Timer1 overflow:
  TIMSK1 = (1 << TOIE1);
  // Установить CS10 бит так, чтобы таймер работал при тактовой частоте:

  TCCR1B |= (0 << CS10);
  TCCR1B |= (0 << CS11);
  TCCR1B |= (1 << CS12);

  sei(); // включить глобальные прерывания

  lcd.init();
  lcd.backlight();
}

ISR(TIMER1_OVF_vect)
{
  digitalWrite(LEDPIN, !digitalRead(LEDPIN));
  LCD();
}

void loop()
{
}
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,263
1,301
Москва
Странно что пример не рабочий, может с обновлением было что, но я основные примеры тестировал и они работали.
Не надо обновлять экран в прерывании, обновляйте как и раньше, может быть сделав на миллис (как изначально думали про прерывания) обновление экрана раз 10 в секунду, для информативности хватит.
 

dxf

✩✩✩✩✩✩✩
17 Апр 2020
47
2
@Старик Похабыч, LCD обновляю раз в полсекунды, чаще не требуется.
Вцелом разобрался с работой по таймеру, спасибо всем. Кто столкнётся смотрите статью на хабре, очень понятное описание, и иностранную статью с состоянием регистров.
Код по работе на совпадение по таймеру:
// https://habr.com/ru/articles/453276/
//  https://microcontrollerslab.com/arduino-timer-interrupts-tutorial/

#include <Arduino.h>

#define LEDPIN 13

volatile uint16_t seconds;
volatile uint16_t overF = 1;

#include "GyverStepper2.h"
GStepper2<STEPPER2WIRE> stepper(2048, 3, 4);
bool bool_tmr1 = 1;
bool bool_tmr2 = 0;
uint32_t tmr1 = millis();
#define STEPPER_PIN_START 11

#include <EncButton.h>
#define BTN_PIN_START 12
EncButton<EB_TICK, BTN_PIN_START> btn1;
bool flagStart = true;
bool flagStaus = true;

#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);
uint32_t timerLCDredraw = 0;

#include <TimerMs.h>
TimerMs tmr(7, 1, 0);

void LCD()
{
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("11111111");
}

void setup()
{
  // инициализация Timer1
  cli();      // отключить глобальные прерывания
  TCCR2A = 0; // установить TCCR1A регистр в 0
  TCCR2B = 0;

  // // включить прерывание Timer1 overflow:
  // TIMSK1 = (1 << TOIE1);
  // // Установить CS10 бит так, чтобы таймер работал при тактовой частоте:

  OCR2A = 255; // установка регистра совпадения

  TCCR2B |= (1 << WGM22); // включить CTC режим

  TCCR2B |= (1 << CS20); // 1
  TCCR2B |= (1 << CS21); // 0
  TCCR2B |= (0 << CS22); // 0

  TIMSK2 |= (1 << OCIE2A); // включить прерывание по совпадению таймера

  sei(); // включить глобальные прерывания

  pinMode(LEDPIN, OUTPUT);

  Serial.begin(9600);
  pinMode(STEPPER_PIN_START, OUTPUT);
  digitalWrite(STEPPER_PIN_START, LOW);
  lcd.init();
  lcd.backlight();
  stepper.reverse(1);

  tmr.setMicros(true);
  tmr.setPeriodMode();
}

ISR(TIMER2_COMPA_vect)
{
  seconds++;
  if (seconds == overF)
  {
    seconds = 0;
    stepper.tick();
    digitalWrite(LEDPIN, !digitalRead(LEDPIN));
  }
}

void loop()
{
  btn1.tick();

  if (btn1.press())
  {
    // sei(); // включить глобальные прерывания

    // OCR2A = 7000; // установка регистра совпадения

    // // flagStart = !flagStart;
    // // flagStaus = !flagStaus;
    // sei(); // включить глобальные прерывания
    overF = 2000;
  }

  if (flagStart == true && flagStaus == true)
  {
    stepper.setSpeedDeg(60);
    digitalWrite(STEPPER_PIN_START, LOW);
    flagStaus = !flagStaus;
  }
  if (flagStart == false && flagStaus == true)
  {
    digitalWrite(STEPPER_PIN_START, HIGH);
    flagStaus = !flagStaus;
  }

  if (millis() - timerLCDredraw >= 500)
  {
    timerLCDredraw = millis();
    LCD();
  }
}