Проблема в разработке тахометра

Здравствуйте.
Это разработка тахометра, который должен считать импульсы от датчика Холла.
Вместо датчика Холла (временно) подключена кнопка к пину 2.
Дребезг контактов не имеет значения. Это - разработка.
ТЕОРИЯ
По импульсу от датчика Холла переменная stobor увеличивается на единицу.
Каждую секунду по прерыванию от таймера содержимое stobor присваивеатся stobor2.
Затем в loop() stobor2 передается в порт
ПРАКТИКА
Ни фига не работает как надо.
После нажатия на кнопку в порт выводится какое-то число. Например, 81.
Проблема в том, что это число выводится не один раз, как ожидалось, а 4 - 5 раза.
В чем может быть дело?
Спасибо.

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>

volatile int stobor; // счетчик оборотов
volatile int stobor2; // счетчик оборотов

unsigned long int t1;
unsigned long int t2;
unsigned long int period = 1000;

void setup()
{
  t1 = millis();
  t2 = millis();
  pinMode(2, INPUT_PULLUP);
  Serial.begin(9600);

  stobor = 0;
  stobor2 = 0;

  wdt_enable (WDTO_8S);

  attachInterrupt(digitalPinToInterrupt(2), holl, RISING); // прерывание на пине 2

  // Настройка таймера
  cli();
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1 = 0;
  TCCR1B |= (1 << CS12) | (1 << CS10);
  OCR1A = 15624;
  TIMSK1 |= (1 << OCIE1A);
  sei();
}


void loop()
{
  // обработка счетчика оборотов stobor2
  t2 = millis();
  if (t2 - t1 >= period)
  {
    t1 = t2;
    Serial.println(stobor2);
  }
  wdt_reset();
}

void holl() // Прерывание по датчику Холла
{
  stobor++;
}

ISR(TIMER1_COMPA_vect) // Прерывание по таймеру
{
  stobor2 = stobor;
  stobor = 0;
}

У вас таймер выдает прерывание раз в 4 секунды, а не раз в секунду. Поэтому и получаете 4 значения.

Он по милис выдает в порт, кака разница сколько раз прерывание таймера сработает?
Значение да, но печатать должно раз в секунду.

В прерывании кнопки поднимаете флаг, что кнопка сработала.
Если он поднят - печать, а затем сброс флага.

P.S. Чушь написал. Кнопка - это ведь датчик Холла.))
Но принцип всё равно тот же. Если не нужна многократная печать - поднимаем флаг в нужном месте, и, сразу, после печати сбр.

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

https://robotclass.ru/tutorials/arduino-tacho-irq/ может будет полезно, заодно и датчик дешевый и почти готовый…

Так и есть - раз в секунду.

Не совсем тогда понятен вопрос

4-5 раз в секунду, что-ли?

Как написан код, оно должно выводиться, пока не изменится

Нет. Выводится 4-5 раз за 4-5 секунд. Кнопка отпущена, а ненулевое значание 4-5 раз повторяется. Хотя ожидается 0.

Такое возможно, если @Upper прав, хотя я с ходу не вижу, где косяк, может дело в 16-битных регистрах. Я сейчас должен уходить, если что позже посмотрю.
Может @Upper разъяснит, где косяк в настройках таймера

Не объясню. Я просто запустил в симуляторе. И увидел, что не 1 секунда а около 4

ISR(TIMER1_COMPA_vect) // Прерывание по таймеру
{
  stobor2 = stobor;
  stobor = 0;
  Serial.println(millis());
}
2 лайка

А миллис случайно не на первом же таймере сделан? А то ему там предделитель меняют, не?

Миллис на нулевом таймере.

У ТС предделитель 1024.

16000000/1024/15625=1

Похоже, что прерывание раз в секунду, но почему счета так работает, я хз.

Когда человек задает вопрос, и это единственный комментарий к настройкам, то это моветон (ИМХО)

16000000/1024/15625=1 (15625 в режиме 0000 Normal не сбрасывает таймер)

16000000/1024/65536=0.2384

2 лайка

Здесь проблема не в том, какой период срабатывания прерывания по таймеру, а в том, что состояние в stobor2 сохраняется на 4-5 секунд. В строке 58 stobor2=stobor; Взял количество “попугаев”. В строке 59 stobor обнулил. Значит, если не нажимать кнопку, то при следующем срабатывании прерывания по таймеру в stobor2 должен прийти ноль. А ноль приходит с задержкой в 4-5 секунд.

Так следующее срабатывание таймера и происходит через 4-5 секунд)))
Сделайте так:
TCCR1B |= (1 << CS12) | (1 << CS10) | (1 << WGM12);

P.S.То есть 0 “приходит” через 4-5с после отпускания кнопки. А , пока он не пришёл, один раз в 1 сек выводится старое значение strobor2 (4-5 раз)

1 лайк

ему надо stobor2 сбросить после чтения ?

В 18 @Дим-мычъ предлагает попробовать изменить настройку таймера. Через некоторое время попробую. Сейчас работа мешает хобби.

Может быть. Но сначала stobor2 надо правильно получить и обработать. Возможно, что на время обработки понадобится какой-то флаг, чтобы запретить смену stobor2.

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>

volatile int stobor; // счетчик оборотов
volatile int stobor2; // счетчик оборотов

unsigned long int t1;
unsigned long int t2;
unsigned long int period = 1000;

void setup()
{
  t1 = millis();
  t2 = millis();
  pinMode(2, INPUT_PULLUP);
  Serial.begin(9600);

  stobor = 0;
  stobor2 = 0;

  wdt_enable(WDTO_8S);

  attachInterrupt(digitalPinToInterrupt(2), holl, RISING);

  // Настройка таймера
  cli();
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1 = 0;
  TCCR1B |= (1 << CS12) | (1 << CS10);
  OCR1A = 15624;
  TIMSK1 |= (1 << OCIE1A);
  sei();
}

void loop()
{
  t2 = millis();
  if (t2 - t1 >= period)
  {
    t1 = t2;
    
    // Защищенное чтение stobor2
    int current_stobor2;
    cli(); // Запрещаем прерывания
    current_stobor2 = stobor2;
    sei(); // Разрешаем прерывания
    
    Serial.println(current_stobor2);
  }
  wdt_reset();
}

void holl()
{
  stobor++;
}

ISR(TIMER1_COMPA_vect)
{
  // Защищенное копирование и обнуление
  cli();
  stobor2 = stobor;
  stobor = 0;
  sei();
}

сработает ?)))
возможно Serial.println(stobor2); переместить на ISR(TIMER1_COMPA_vect) { ?)))