Помогите разобраться с прошивкой для ATtiny88. Скетч компилируется без ошибок, но сбоит при работе.

Romvell

✩✩✩✩✩✩✩
10 Сен 2022
3
0
Здравствуйте уважаемые форумчане.
Помогите, пожалуйста, новичку разобраться с прошивкой.

У ребёнка есть железная дорога Лего.
Делаю пульт управления четырьмя стрелками.
В основе плата ATtiny88. Сам пульт - 4 кнопки и 4 светодиода. Привод - 4 электромотора Лего-М через драйвера L293D.
При нажатии кнопки срабатывает мотор, переводит стрелку и выключается по таймеру. Светодиод загорается. При повторном нажатии стрелка переводится обратно (мотор вращается в обратную сторону), светодиод тухнет. Предусмотрен таймер, блокирующий переключение до завершения предыдущего цикла.

Собственно проблема в следующем:
Если в скетче комментировать код, относящийся к одному из двигателей, всё работает нормально.
Все четыре двигателя работать не хотят. Первые три работают. При попытке включить четвертый, перестаёт работать всё.

Компиляция проходит без ошибок. Свободной памяти много.
Скетч:
//Описание прошивки, ссылки, заметки
/*
  Данный скетч управляет работой четырёх ж/д стрелок.
  Однократное нажатие на кнопку переводит стрелку на движение по ответвлению и зажигает сигнальный светодиод.
  Повторное нажатие возвращает стрелку в исходное положение и гасит светодиод.
*/
//Константы-настройки (define и обычные)
#define TJOB 1000 //Время работы мотора
//Служебные константы (которые следует менять только с полным осознанием дела)
//Подключаемые библиотеки и внешние файлы, объявление соответствующих им типов данных и классов
#include <EncButton.h>
#include "Arrow.h"

//Инициализация объектов
//Кнопки
EncButton<EB_TICK, 15>  btn1(INPUT_PULLUP);
EncButton<EB_TICK, 16>  btn2(INPUT_PULLUP);
EncButton<EB_TICK, 17>  btn3(INPUT_PULLUP);
EncButton<EB_TICK, 18>  btn4(INPUT_PULLUP);
//Стрелки
Arrow Arrow1(13, 14, TJOB);
Arrow Arrow2(10, 11, TJOB);
Arrow Arrow3(7, 8, TJOB);
Arrow Arrow4(4, 5, TJOB);

//Светодиоды
LED LED1(12), LED2(9), LED3(6), LED4(3);
//Глобальные переменные

void setup() {
  Arrow1.Setup();
  Arrow2.Setup();
  Arrow3.Setup();
  Arrow4.Setup();
 
  LED1.LEDSetup();
  LED2.LEDSetup();
  LED3.LEDSetup();
  LED4.LEDSetup();
}
void loop() {

  Arrow1.LoopCall();
  Arrow2.LoopCall();
  Arrow3.LoopCall();
  Arrow4.LoopCall();

  LED1.LoopCall();
  LED2.LoopCall();
  LED3.LoopCall();
  LED4.LoopCall();
 
  //По нажатию кнопки 1
  btn1.tick();
  if (btn1.click()) {
    Arrow1.Toggle();
    LED1.setFlag(Arrow1.getFlag());
  }
  //По нажатию кнопки 2
  btn2.tick();
  if (btn2.click()) {
    Arrow2.Toggle();
    LED2.setFlag(Arrow2.getFlag());
  }
  //По нажатию кнопки 3
  btn3.tick();
  if (btn3.click()) {
    Arrow3.Toggle();
    LED3.setFlag(Arrow3.getFlag());
  }
  //По нажатию кнопки 4
  btn4.tick();
  if (btn4.click()) {
    Arrow4.Toggle();
    LED4.setFlag(Arrow4.getFlag());
  }
}
Библиотека.
Arrow.h:
#pragma once
#include <Arduino.h>
#include <GyverMotor.h>
#include <TimerMs.h>

//Классы
//Стрелки
class Arrow {
  private:
    bool _flag;
    //bool _switch;
    bool _motorStop;
    byte _pin1;
    byte _pin2;
    int _tm;
    TimerMs *tmrDelay;
    TimerMs *tmrJob;
    GMotor *motor;
  public:
    Arrow(byte pin1, byte pin2, int tm);   
    ~Arrow();
    void Setup(void);
      void LoopCall(void);
      void Toggle(void);
    byte getFlag();
};

//Светодиоды
class LED {
  private:
    byte _pin;
    bool _flag;
  public:
    LED(byte pin);
    void LEDSetup();
    void LoopCall(void);
    void Toggle();
    void setFlag(byte flag);
};
Arrow.cpp:
#include "Arrow.h"

//Классы
//Стрелки
 
  Arrow::Arrow(byte pin1, byte pin2, int tm)    {
    _pin1 = pin1;
    _pin2 = pin2;
  _tm = tm;
  tmrDelay = new TimerMs(_tm+1000, 0, 1);
  tmrJob = new TimerMs(_tm, 0, 1);
  motor= new GMotor(RELAY2WIRE, _pin1, _pin2, HIGH);
  }
 
  void Arrow::Setup(void) {
         pinMode(_pin1, OUTPUT);
        pinMode(_pin2, OUTPUT);
        tmrDelay->setTimerMode();
    tmrDelay->start();
        tmrJob->setTimerMode();
        _flag = false;
        _motorStop = true;
    }
    
    void Arrow::LoopCall(void) {
            if (!_motorStop && tmrJob->tick()) {
                motor->setMode(STOP);
                _motorStop = true;
            }
        }

    void Arrow::Toggle(void) {
        if (tmrDelay->tick()) {
        tmrJob->start();
        tmrDelay->start();
        if (!_flag) {
          motor->setMode(FORWARD);
          _flag = true;
        } else {
          motor->setMode(BACKWARD);
          _flag = false;
        }
        _motorStop = false;
      }
    }

 byte Arrow::getFlag() {return _flag;}
 
  Arrow::~Arrow(void){
    delete tmrDelay;
    delete tmrJob;
    delete motor;
  }

//Светодиоды
  LED::LED(byte pin) : _pin(pin) {}
 
  void LED::LEDSetup() {
    pinMode(_pin, OUTPUT);
    _flag = false;
  }
  void LED::LoopCall(void) {digitalWrite(_pin, _flag);}
  //void LED::Toggle() {_flag = !_flag;}
  void LED::setFlag(byte flag) {_flag = flag;}
Скетч использует 4026 байт (59%) памяти устройства. Всего доступно 6780 байт.
Глобальные переменные используют 123 байт (24%) динамической памяти, оставляя 389 байт для локальных переменных. Максимум: 512 байт.
Please plug in the device (will time out in 60 seconds) ...
Device is found!
connecting: 16% complete
connecting: 22% complete
connecting: 28% complete
connecting: 33% complete
Device has firmware version 2.2
Device signature: 0x1e9311
Available space for user applications: 6650 bytes
Suggested sleep time between sending pages: 7ms
Whole page count: 208 page size: 32
Erase function sleep duration: 1456ms
parsing: 50% complete
erasing: 55% complete
erasing: 60% complete
erasing: 65% complete
writing: 70% complete
writing: 75% complete
writing: 80% complete
running: 100% complete
> Micronucleus done. Thank you!
 

Вложения

Kir

★✩✩✩✩✩✩
28 Мар 2020
69
16
У вашего контроллера всего 512 байт ОЗУ, есть подозрение, что памяти вам не хватает. Вам в лог показывают только расход ОЗУ на статически выделенную память, а все что выделяется динамически не показывается, а у вас там выделяются довольно большие объекты, относительно вашего доступного места. И не стоит забывать, что для работы самой программы ещё нужен стек.
Первым шагом я бы отказаться от динамического выделения памяти, и заменить на статическое, это как минимум позволит лучше оценить объем используемой памяти, и уже по результатам смотреть, куда дальше копать, по моим прикидкам реальный расход будет примерно 355 байт, что вроде как бы и норм, но, тем не менее:
C++:
#pragma once
#include <Arduino.h>
#include <GyverMotor.h>
#include <TimerMs.h>

//Классы
//Стрелки
class Arrow {
  private:
    TimerMs tmrDelay;
    TimerMs tmrJob;
    GMotor  motor;
   
    bool _flag;
    //bool _switch;
    bool _motorStop;
    byte _pin1;
    byte _pin2;
    int _tm;

  public:
    Arrow(byte pin1, byte pin2, int tm);  
    ~Arrow() { }
    void Setup(void);
    void LoopCall(void);
    void Toggle(void);
    byte getFlag() const { return _flag; }
};


Arrow::Arrow(byte pin1, byte pin2, int tm)
  : tmrDelay(tm + 1000, 0, 1)
   ,tmrJob(tm, 0, 1)
   ,motor(RELAY2WIRE, pin1, pin2, HIGH)
   ,_pin1(pin1)
   ,_pin2(pin2)
   ,_tm(tm)
   {}


void Arrow::Setup(void) {
    pinMode(_pin1, OUTPUT);
    pinMode(_pin2, OUTPUT);
    tmrDelay.setTimerMode();
    tmrDelay.start();
    tmrJob.setTimerMode();
    _flag      = false;
    _motorStop = true;
}
   
void Arrow::LoopCall(void) {
    if (!_motorStop && tmrJob.tick()) {
        motor.setMode(STOP);
        _motorStop = true;
    }
}

void Arrow::Toggle(void) {
    if (tmrDelay.tick()) {
        tmrJob.start();
        tmrDelay.start();
        if (!_flag) {
            motor.setMode(FORWARD);
            _flag = true;
        } else {
            motor.setMode(BACKWARD);
            _flag = false;
        }
        _motorStop = false;
    }
}
 
  • Лойс +1
Реакции: Romvell

Romvell

✩✩✩✩✩✩✩
10 Сен 2022
3
0
Первым шагом я бы отказаться от динамического выделения памяти, и заменить на статическое,
Были мысли в эту сторону, но как это сделать Я не разобрался. Это мой первый опыт в Ардуино. Да и вообще в программировании.
Если кто подскажет как правильно сделать, буду очень признателен...
 

Romvell

✩✩✩✩✩✩✩
10 Сен 2022
3
0
@Kir, Извиняюсь... Читал с телефона, не разобрался.

Большое спасибо за пример! Он закрыл сразу несколько моих вопросов.
Первоначально я и хотел сделать подобную реализацию но, по незнанию, не разобрался с синтаксисом...
Получил ошибку при компиляции и на этом застрял. Пошёл другим путем...

Ваш код залил. Всё работает. Расход памяти 353 байта.
Допишу индикацию, ещё раз проверю. Должно хватить.

P.S.
Вообще отказался от внешней библиотеки управления двигателем. Прописал весь необходимый код в своём классе.
Расход памяти упал до 115 байт вместе с индикацией. Всё работает.

Спасибо за помощь!.