Как подсчитать количество нарастающих фронтов на DS3231 rtc?

Я использую DS3231 rtc, и мне нужно получить от него точность в миллисекундах, поэтому я решил использовать генерируемые им часы с частотой 32 кГц. Я написал код, который будет подсчитывать нарастающие и спадающие фронты с помощью прерывания с (параметрами RISING или FALLING), но я получаю только нули на своей плате Serial для подсчета.

Вот код, который я использую:

#include <DS3231.h>
#include <RTClib.h>
#include <Wire.h>
#include "rtc.h"
#define InterruptPin 3

volatile long lcnt = 0;

RTC_DS3231 rtc;

void setup() {
  Wire.begin();
  Serial.begin(74880); // инициализируем последовательный порт
  pinMode(InterruptPin, INPUT_PULLUP);
  rtc.begin();
  rtc.adjust(DateTime(F(__DATE__),F(__TIME__)));
  attachInterrupt(digitalPinToInterrupt(InterruptPin), fcnt, FALLING);
}
void loop() {
  unsigned long a=millis();
  if (a<1000){
  Serial.println(lcnt);
  }
}

void fcnt(){
  lcnt+=1;
}

Я просто хочу проверить, сколько у меня нарастающих фронтов за 1 с. Странно то, что раньше этот код работал, а теперь нет ^^'... Не знаю, изменил ли я что-то по ошибке или удалил что-то полезное.

Может ли кто-нибудь помочь мне решить эту небольшую проблему?

Большое спасибо за ваше время!

, 👍1

Обсуждение

попробуйте отключить подтягивающий резистор на входном контакте, @jsotola


2 ответа


1

Я не уверен, почему вы получаете 0 как таковой, но ваша методология прерываний и переменных оставляет желать лучшего (вероятно, причина "учебников", которым вы следовали).

Меня больше всего беспокоит то, что вы используете 32-битную "длинную" переменную в прерывании и ссылаетесь на нее непосредственно вне прерывания. Arduino — это 8-битная система, а это означает, что все обращения к этой переменной требуют множества инструкций для упорядочивания (разделение на 4 8-битных значения, манипулирование ими, рекомбинация и т. д.). Из-за этого никакие операции с этой переменной не являются «атомарными».

Вы должны сделать их атомарными. Если вы этого не сделаете, то вполне возможно, что прерывание сработает и изменит значение в середине того, что вы делаете с этим значением.

Самый простой способ сделать его атомарным — отключить прерывания, затем скопировать значение в другую переменную и/или изменить его, а затем снова включить прерывания.

Вот пример, который я считаю стабильным:

#include <DS3231.h>
#include <RTClib.h>
#include <Wire.h>
#include "rtc.h"
#define InterruptPin 3

volatile uint32_t lcnt = 0;

RTC_DS3231 rtc;

void setup() {
  Wire.begin();
  Serial.begin(74880); // инициализируем последовательный порт
  pinMode(InterruptPin, INPUT_PULLUP);
  rtc.begin();
  rtc.adjust(DateTime(F(__DATE__),F(__TIME__)));
  attachInterrupt(digitalPinToInterrupt(InterruptPin), fcnt, FALLING);
}

void loop() {
  static uint32_t ts = millis();
  uint32_t a = millis();
  if (a - ts >= 1000) {
    ts = a;
    noInterrupts();
    uint32_t currentCount = icnt;
    icnt = 0;
    interrupts();
    Serial.println(currentCount);
  }
}

void fcnt(){
  lcnt+=1;
}

Это должно напечатать количество импульсов за последовательные 1-секундные периоды.

,

Спасибо за ваш ответ, я загрузил ваш код, и я все еще получаю только нули каждую секунду на моем экране..., @JamesONeil

@JamesONeil Тогда вам следует трижды проверить свою проводку., @Majenko

Я уже использую одно прерывание от ds3231 для будильника на контакте SQW, чтобы разбудить мою Arduino, и он отлично работает, поэтому проблема может исходить только от контакта тактовой частоты 32 кГц. Я дважды проверил свою проводку, и ничего там не было..., @JamesONeil


0

Я использовал тот же код, что и ваш, просто изменил тип данных с uint32_t на unsigned long, и все заработало. Может быть, вы можете попробовать то же самое.

,