Возникли проблемы с определением CTC на таймере 1 с помощью ATTiny85.

Я использую Timer1 на Attiny85 с тактовой частотой 1 МГц, чтобы переключать светодиод каждую секунду. Однако у меня возникли некоторые проблемы с выбором прескалера и значения OSC.

Я использую эту формулу, которую нашел в техническое описание для Attiny.

fOCnx = fclk/(2*N*(1+OCRnx))

N = премасштабатор
Я беру fOCnx = 0,5 Гц, так как хочу прямоугольную волну
Итак, основываясь на приведенной выше формуле, я попробовал следующие случаи

  1. N = 2048, OCR1C = 243
  2. N = 4096, OCR1C = 122
  3. N = 8192, OCR1C = 61
  4. N = 16 384, OCR1C = 30

Светодиодный индикатор переключается с интервалом в 1 секунду для случаев 3 и 4. Однако для случаев 1 и – через 2 секунды. Кто-нибудь может сказать мне, почему это происходит? В конце концов, я хочу использовать это для сбора данных с датчика, который у меня есть, поэтому мне нужно быть точным.

Вот мой код

#include <SoftwareSerial.h>

int ledPin = 1;
int state = 0;

SoftwareSerial ser(3, 4);
void setup() {
  // put your setup code here, to run once:
  TCCR1 |= 0x8F;
  OCR1C = 30;
  TIMSK = (1 << OCIE1A);
  pinMode(ledPin, OUTPUT);
  ser.begin(9600);
}


ISR(TIMER1_COMPA_vect)
{
  state = !state;
  digitalWrite(ledPin, state);
  ser.println(state);
}

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

, 👍1

Обсуждение

Вы дважды проверили свои настройки для каждого случая. Я не хочу сказать, что ты ошибся. Но то, что происходит, трудно объяснить иначе. Мои значения будут (TCCR1, OCR1C) -> (0x8f, 30), (0x8e, 61), (0x8d, 122), (0x8c, 244). Я вижу только один из них в коде, поэтому не могу сказать, использовали ли вы другие. Если вы случайно использовали (0x8c,122), а затем просто вычли 1 и удвоили для следующего текста (0x8b,244), наблюдение можно объяснить. У меня тоже проблема с 2 в формуле. От куда это?, @Peter Paul Kiefer

А, я понимаю, почему вы использовали 2 в формуле. Вы не хотите 0,5 Гц, вы хотите, чтобы 2 Гц переключались 2 раза в течение 1 секунды (0,5 секунды от переключения до переключения. 0,5 Гц будет 2 секунды от переключения до переключения)., @Peter Paul Kiefer

Я написал 0,5 Гц, потому что период цикла (вкл.+выкл.) = 1+1 = 2 с, значит, 0,5 Гц. Надеюсь, я смог уточнить., @Souvik Saha

да, тогда я неправильно понял. Но тогда у меня нет клея, почему в формуле стоит 2. fOCnx = fclk/(2*N*(1+OCRnx)) Я имею в виду, что у меня нет клея, почему он стоит в числителе дроби. Если я вставлю одну из пар значений выше, я получу 1 Гц. Если я умножу обе стороны на 2, я получу 2 Герца, но не 0,5 Гц. Вы видите мою проблему?, @Peter Paul Kiefer

Что ж, если нет серьезных проблем с моим пониманием, все рассчитанные значения удовлетворяют формуле, которую я нашел в таблице данных на странице 72. Какую формулу вы используете?, @Souvik Saha

Ах я вижу. Формула используется для вычисления частоты сигнала квадратной формы, но для полной длины волны (включая фазы High и LOW). Таким образом, они должны переключать выходной контакт два раза для одной длины волны. С этой формулой процедура прерывания, когда вы переключаете контакт, срабатывает в два раза чаще, чем вы хотите. Вычисленные вами значения верны для этой формулы, и вы получаете 1 Гц для каждой пары. Но этот 1 Гц означает прямоугольную волну с частотой 1 Гц. Но вам нужно, чтобы процедура IRQ работала с частотой 1 Гц. Для этого вам пришлось удалить 2 из формулы и пересчитать значения., @Peter Paul Kiefer

Да, но проблема в том, что я получаю 1 Гц только для случаев 3 и 4. Что происходит, что вызывает у меня проблему?, @Souvik Saha

Вы правы, мы отошли от этого. Поэтому я спросил вас, проверяли ли вы дважды значение, которое вы использовали в программе. Я бы использовал следующие значения: (TCCR1, OCR1C) -> (0x8f, 30), (0x8e, 61), (0x8d, 122), (0x8c, 243). Я не хочу тратить время на размышления о других возможностях неудачи, если это не прояснено., @Peter Paul Kiefer

Ааа, теперь я понимаю ваше замешательство. Значения, которые я использовал, указаны в моем вопросе, и все они такие же, как вы упомянули. Я только что привел один случай в своей программе, это случай 4 в соответствии с вопросом., @Souvik Saha

Любое решение @Peter Paul Kiefer? Я попробовал все значения, которые вы предложили, и работают только первые две пары., @Souvik Saha

Извините, я не видел ваш предпоследний комментарий, я не был уведомлен. Насколько я понимаю, вы использовали точно такие же значения. Тогда единственное объяснение, которое у меня есть, состоит в том, что software serial должен отключать прерывания глобально, когда он работает (print). Я добавил ответ, чтобы объяснить мое предположение. Другой вывод заключается в том, что вы установили значения регистра с помощью комбинированной операции или операции |=. Если по какой-либо причине в регистре есть несколько единиц, результирующее значение будет не таким, как вы ожидали. скажем TCCR1 == 0x01, тогда присваивание TCCR1 |= 0x8E; приводит к TCCR1 == 0x8F, @Peter Paul Kiefer

А, понятно! Попробую отключить Software Serial и проверить результат. Также я постараюсь не маскировать вывод с помощью |=, как вы предложили., @Souvik Saha


1 ответ


Лучший ответ:

2

Единственное объяснение, которое у меня есть, заключается в том, что последовательное программное обеспечение отключает прерывания во время работы (print). Если прерывания заблокированы, когда таймер завершает работу, вы теряете тик. Это не объясняет различное поведение четырех пар значений, но на немецком форуме сообщалось о некоторых проблемах. Они не нашли ответа, но предположили, что это зависит от функции 'delay()', которая используется для прогрмаммного последовательного порта, и того факта, что прерывания заблокированы, чтобы сделать функцию задержки более точной.

Если вы хотите быть уверенным, вы можете попробовать это, полностью удалив Software Serial из вашей программы. Если вы сделаете значения переключаемыми, например, нажатием кнопки, вы сможете изменить их без перезагрузки программы. Тогда легче увидеть разные интервалы мигания.

int ledPin = 1;
int state = 0;

int regValues[] = { 0x8f, 0x8e, 0x8d, 0x8c };
int cntValues[] = { 30, 61, 122, 243 };

const int buttonPin = 2;
int buttonState = LOW;

long lastDebounceTime = 0;
long debounceDelay = 80; 
int lastButtonState = LOW; 

int pairIndex = 0;

void setup() 
{
  TCCR1 = regValues[ pairIndex ];
  OCR1C = cntValues[ pairIndex ];

  TIMSK = (1 << OCIE1A);

  pinMode(ledPin, OUTPUT);
  pinMode(buttonPin, INPUT);
}


ISR(TIMER1_COMPA_vect)
{
  state = !state;
  digitalWrite(ledPin, state);
}

void loop() 
{ 
  int reading = digitalRead(buttonPin);

  if (reading != lastButtonState) 
  {
    lastDebounceTime = millis();
  }

  if ( (millis() - lastDebounceTime) > debounceDelay) 
  {
   if (reading != buttonState) 
   {
      buttonState = reading;

      if (buttonState == HIGH) 
      {
        if ( ++pairIndex > 3 )
        {
          pairIndex = 0;
        }

        TCCR1 = regValues[ pairIndex ];
        OCR1C = cntValues[ pairIndex ];
      }

    }

  }

}

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

,

Ахх спасибо большое! Я попробую этот код и вернусь к вам., @Souvik Saha

Я опробовал ваши предложения. Похоже, это начало работать, как только я изменил назначение на = вместо |=, как я делал ранее. Я предполагаю, что какой-то другой бит влиял на значение таймера. Спасибо за вашу помощь!, @Souvik Saha