ARDUINO Как использовать GyverTM1637 и пьезо пищалку одновременно

Fompi

✩✩✩✩✩✩✩
7 Июл 2022
17
0
Здравствуйте, появился вопрос. Хочу использовать библиотеку GyverTM1637 и пьезо пищалку в проекте. Но, вместе они не хотят работать, пьезо пищалка (пассивная) при использовании библиотеки начинает звенеть постоянно, на команды tone() или analogWrite() не реагирует вообще. Скорее всего, они используют один и тот же таймер, но мне не хватает знаний как заставить их работать, не мешая друг другу. Пробовал очень много способов, и подключал пищалку к аналоговым пинам, и к D10/D11, которые, вроде как, используют разные таймеры, все бесполезно.

C++:
#include <GyverTM1637.h>
#define BUZ 10
#define DIO 13
#define CLK 12

//убрал лишние строки, которые не влияют на проблему

GyverTM1637 disp(CLK, DIO);
byte numberSegments[10] = {
  0b00111111, 0b00000110, 0b01011011, 0b01001111, 0b01100110,
  0b01101101, 0b01111101, 0b00000111, 0b01111111, 0b01101111,
  };

uint32_t Timer1;
uint32_t Timer2;
uint32_t Timer3;

void setup() {
  pinMode(BUZ, OUTPUT);
  Serial.begin(9600);
  disp.brightness(7);  // яркость, 0 - 7 (минимум - максимум)
}

void workMode() {
  disp.clear();
 static int a, b, c, d;
 bool flag = 1;
  disp.point(flag);
  disp.displayByte(numberSegments[a], numberSegments[b], numberSegments[c], numberSegments[d]);
  Timer1 = millis(); // сброс таймера
  Timer2 = millis(); // сброс таймера
  while(1){
      if (!digitalRead(SWCH)) {
     relaxMode();
      }
      if(millis() - Timer2 >= 500) {
        flag = !flag;
        disp.point(flag);
        Timer2 = millis(); // сброс таймера
        }
      if (millis() - Timer1 >= 60000) {   // ищем разницу (1 мин)
    d++;
    if(d == 10){d = 0; c++;}
    if(c == 6) {c = 0; b++;}
    if(b == 10){b = 0; a++;}
    if(a == 6) {a = 0;}
    Timer1 = millis(); // сброс таймера
    break;
      }
    }
    if(d == 1) {
      disp.displayByte(numberSegments[a], numberSegments[b], numberSegments[c], numberSegments[d]);
      for(int i = 0; i <= 10; i++){
      tone(BUZ, 800);
      digitalWrite(RED, 1);
      Timer3 = millis(); // сброс таймера
      while(1){
      if (!digitalRead(POW)) {
     relaxMode();
      }
      if(millis() - Timer3 >= 1000) {
        noTone(BUZ);
        Timer3 = millis(); // сброс таймера
        break;
        }
      }
      }
    }
    workMode();
}

void relaxMode() {
 digitalWrite(RED, 0);
  disp.displayByte(_0, _0, _0, _0);
  noTone(BUZ);
  relaxMode();
}

void loop() {
workMode();
}
 

bort707

★★★★★★✩
21 Сен 2020
3,067
915
то, о чем вы пишете - обьясняется конфликтом аппаратных таймеров микроконтроллера. Однако в приведенном вами коде вообще нет аппаратных таймеров, только программные, соответвенно по этому коду ничего сказать нельзя.
Откройте исходники tone(), посмотрите, может там есть опции настройки для использования другого таймера
 

Fompi

✩✩✩✩✩✩✩
7 Июл 2022
17
0
@bort707,попробовал я открыть tone.cpp, вот честно, вообще не понял как, там почему-то вообще пишется о каких-то TIMER4 и TIMER5, о которых я в первый раз слышу... :(

P.S. понял, что эти таймеры используются на меге
 

poty

★★★★★★✩
19 Фев 2020
3,261
948
Таймеры вообще ни при чём. Здесь ошибка именно в логике кода.
Во-первых, кто Вас надоумил использовать рекурсию там, где она вообще не нужна? Обе функции (workMode и relaxMode) вызывают сами себя, при каждом вызове расходуют стек. Фактически, ни одна из них не имеет выхода из рекурсии, соответственно, стек просто забьётся в скором времени.
Во-вторых, где происходит отключение сигнала (функция noTone)? Есть два места:
  • в функции relaxMode, выполнение которой зависит от сигнала на SWCH или POW, который может не возникнуть, соответственно, noTone не выполнится;
  • в куске кода, выполняющегося по условию if(d == 1). Фактически, это условие выполнится через минуту после начала работы (
    if (millis() - Timer1 >= 60000) { d++; ...
    ) и будет выполняться в течение минуты после этого. Звук включается в строке tone(BUZ, 800); и должно выключаться noTone(BUZ);, но интервал между noTone и tone в следующем цикле - микросекунды, а по завершении цикла for(int i = 0; i <= 10; i++) мы снова туда попадём рекурсивным вызовом workMode через пару десятков микросекунд.
 

Fompi

✩✩✩✩✩✩✩
7 Июл 2022
17
0
@poty, relaxMode пока что является недописанной функцией, POW и SWCH - это кнопки, т.е, при нажатии кнопки действие переходит в другую функцию. Я тестировал систему, и relaxMode, пока что является заглушкой, имеющей бесконечную функцию (хотя, лучше бы наверно оставил while(1){} и все).

Про микросекунды в tone и noTone не заметил, спасибо. if(d == 1) тоже только для теста, позже там будет if(b == 1) (спустя один час)

Но, я так и не знаю, почему это происходит. Если я отключу TM1637 от ардуинки, пищалка работает нормально, хотя из кода я даже ничего не вырезал
 

poty

★★★★★★✩
19 Фев 2020
3,261
948
@Fompi, сначала уберите рекурсии, их использование здесь совсем неуместно.
Что касается работы при отключении TM1637, то без анализа полного кода сделать какие-либо выводы проблематично. То, что я увидел, я написал.
 

Fompi

✩✩✩✩✩✩✩
7 Июл 2022
17
0
@poty,сейчас код выглядит так и ничего не работает

C++:
#include <GyverTM1637.h>

#define RED 2
#define GREEN 3
#define BUZ 5
#define DIO 13
#define CLK 12
#define POW 7
#define SWCH 8

GyverTM1637 disp(CLK, DIO);
byte numberSegments[10] = {
  0b00111111, 0b00000110, 0b01011011, 0b01001111, 0b01100110,
  0b01101101, 0b01111101, 0b00000111, 0b01111111, 0b01101111,
  };

uint32_t Timer1;
uint32_t Timer2;
uint32_t Timer3;

void setup() {
  // put your setup code here, to run once:
for(int i = 2; i <= 6; i++) {
  pinMode(i, OUTPUT);
  }
  pinMode(BUZ, OUTPUT);
  pinMode(POW, INPUT_PULLUP);
  pinMode(SWCH, INPUT_PULLUP);
  Serial.begin(9600);
  disp.brightness(7);  // яркость, 0 - 7 (минимум - максимум)
}

void workMode() {
  disp.clear();
 static int a, b, c, d;
 while(1){
 bool flag = 1;
  disp.point(flag);
  disp.displayByte(numberSegments[a], numberSegments[b], numberSegments[c], numberSegments[d]);
  Timer1 = millis(); // сброс таймера
  Timer2 = millis(); // сброс таймера
  while(1){
      if (!digitalRead(SWCH)) {
     relaxMode();
      }
      if(millis() - Timer2 >= 500) {
        flag = !flag;
        disp.point(flag);
        Timer2 = millis(); // сброс таймера
        }
      if (millis() - Timer1 >= 60000) {   // ищем разницу (1 мин)
    d++;
    if(d == 10){d = 0; c++;}
    if(c == 6) {c = 0; b++;}
    if(b == 10){b = 0; a++;}
    if(a == 6) {a = 0;}
    Timer1 = millis(); // сброс таймера
    break;
      }
    }
    if(d == 1) {
      disp.displayByte(numberSegments[a], numberSegments[b], numberSegments[c], numberSegments[d]);
      for(int i = 0; i <= 10; i++){
      tone(BUZ, 800);
      digitalWrite(RED, 1);
      Timer3 = millis(); // сброс таймера
      while(1){
      if (!digitalRead(POW)) {
     relaxMode();
      }
      if(millis() - Timer3 >= 1000) {
        Timer3 = millis(); // сброс таймера
        break;
        }
      }
      }
    }   
 }
}

void relaxMode() {
  noTone(BUZ);
  digitalWrite(RED, 0);
  disp.displayByte(_0, _0, _0, _0);
  while(1) {
  }
}

void loop() {
  // put your main code here, to run repeatedly:
workMode();
}
 

poty

★★★★★★✩
19 Фев 2020
3,261
948
@Fompi, если не работает ничего, то помочь отладить код Вам никто не сможет. Вы ставьте какие-то задачи (что должно происходить) и наблюдайте за результатом (что в реальности происходит и как это согласуется с поставленными задачами).
Где у Вас выключается звук сейчас? Можете показать?
Посмотрите на функцию workMode:
  • чистим дисплей;
  • выводим точку;
  • выводим все нули;
  • ждём 1 минуту моргая точкой раз в секунду (проверки кнопок не беру);
  • изменяем a, b, c, d;
  • выводим a, b, c, d при d==1;
  • 11 раз (с периодом 1 секунду) включаем сигнал и светодиод (не выключаем нигде!);
  • начинаем заново, при этом следующий раз d станет равным 1 через 10 минут.
Что из этого у Вас не работает?
 

Fompi

✩✩✩✩✩✩✩
7 Июл 2022
17
0
@poty, со стороны дисплея работает все, не работает ТОЛЬКО пьезопищалка, она начинает звенеть вместе СО СТАРТОМ ардуино
 

poty

★★★★★★✩
19 Фев 2020
3,261
948
@Fompi, ответьте на вопрос из моего предыдущего сообщения. У Вас в коде нет выключения звука.
 

Fompi

✩✩✩✩✩✩✩
7 Июл 2022
17
0
@poty, забыл добавить строку, сейчас я её вставил между 36 и 37 строкой

@poty,методом экспериментов я понял, что как только я ввожу displayByte();, пищалка начинает жужжать, как только я ввожу disp.clear();, жужжание пропадает, скорее всего, вывод цифр на экран идет шимом, поэтому таймеры не делят друг друга

Если убрать в коде все tone и noTone, т.е., никак не использовать пищалку, она так же не шумит
 

poty

★★★★★★✩
19 Фев 2020
3,261
948
@Fompi, не придумывайте. Никакого ШИМ в библиотеке нет. И таймеры она не использует. Всё - элементарная невнимательность.
 

Fompi

✩✩✩✩✩✩✩
7 Июл 2022
17
0
@poty, ошибаетесь, проблема вообще оказалось в аппаратной части, я провел пищалку к отдельной земле, и проблема пропала, скорее всего, сигнал с часов шел на общую gnd, и затем часть тока уходила в зуммер.
 

poty

★★★★★★✩
19 Фев 2020
3,261
948
@Fompi, если бы это было так, как Вы говорите, то использование tone и noTone никак не влияло бы на ситуацию: "сигнал с часов шёл на общую gnd" в любом случае. А по поводу отсутствия ШИМ и таймеров - я уверен, нет их там. Впрочем, код библиотеки открыт, сами можете посмотреть.