Этюды для начинающих: blink и без delay, и без millis

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

cyber-jet пишет:
Ответ очевиден, я думаю :)

А мою задачу решили? :)))

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

я наверное тоже что то не понимаю - просветите плиз меня:

вот пример сделал, если у него закомментировать условие 

  if ((timerSec > 0xFFFFFFF5) && (ctmr < 0xFFFFFFFF)) {
    timerSec = 0;
  }

то счетчик останавливается. - т.е. не счетчик а отображение/действие ежесекундное

естественно я 50 суток не ждал - потому и начальные значения сделал почти максимальными

unsigned long timerSec = 0xFFFFFFF0;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:
  // count timers
  unsigned long ctmr = (0xFFFFFFF0 + (long(millis() / 1000)));
  if ((timerSec > 0xFFFFFFF5) && (ctmr < 0xFFFFFFFF)) {
    timerSec = 0;
  }
  if (ctmr > timerSec) {
    timerSec = ctmr;
    // main action
    Serial.println(timerSec,HEX);
    Serial.println(ctmr,HEX);
    Serial.println('-');
    // end action
  }
  delay(100);
}

 

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

1. Надо вычитать, а не складывать (см сообщение #172), а Вы складываете. Это просто ошибка в программе.

2. При правильном программировании (когда вычитают) всё работает без проблем от слова совсем (см. сообщение #177) позапускайте тот скетч и раберитесь.

3. Я очень просил не задавать мне вопросы "почему так", а вместо этого запустить поиск по форуму - тема обсуждалась 100500 раз. "Не спрашивайте про переполнение миллис и да не посланы будете".

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

andycat пишет:

я наверное тоже что то не понимаю - просветите плиз меня:

3.14здец, как это надоело!

Тупой и еще тупее!

Гуглим "арифметика в дополнительном коде".

ВЫЧИТАНИЕ в доп коде нечувствительно к переполнению, а сложение чувствительно, понятно?

поэтому  условие вида:

if (millis() - OldMillis > Interval)

работает без нарушений на 50 день

а условие вида

if (millis() > OldMillis + Interval)

имеет сбой на 50 день

========================

теперь внимание: вопрос!

Какая из еречисленных арифметических операция использована в твоем примере?

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

не надо так нервничать :)

понял я, перевожу проекты на счетчики с вычитанием.

 

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

ЕвгенийП пишет:

"Не спрашивайте про переполнение миллис и да не посланы будете".

Женя! Это судьба...

 у тебя был прекрасный пример, который можно кидать спрашивающим, там, где неважно знаковый или беззнаковый тип. Он где-то на форуме есть..

b707
Offline
Зарегистрирован: 26.05.2017

andycat пишет:

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

Зря вы это написали в этой ветке... До этого я  считал вас грамотным кодером :)

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

wdrakula,

я решил положить этому конец. Сейчас подготовлю пост в который включу скетч из поста #177, задачу из поста #198 и ещё одну задачу про двойку. Напишу текст такого рода: если мол есть вопросы по переполнения миллис, то алгоритм такой:

do {
} while (не сумел решить обе задачи)
 
if (по-прежнему осталось беспокойство о переполнении) {
       ознакомиться с материалом вот здесь
}
 
Заложу это в закладки и буду давать всем свидетелям Святого Переполнения.
andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

b707 пишет:

andycat пишет:

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

Зря вы это написали в этой ветке... До этого я  считал вас грамотным кодером :)

верите - мне пофиг кем вы меня считаете :)

во вторых я уже писал - я не профессиональный программист, и тем более не "кодер" к которым у меня отвратительное отношение т.к. они могут только по ТЗ работать, а мозг не включают совсем.

а Arduino у меня просто хобби.

arduinec
Offline
Зарегистрирован: 01.09.2015

ЕвгенийП пишет:

я решил положить этому конец. Сейчас подготовлю пост...

И назвать его надо "Непереполняемый миллис" и закрепить наверху, чтобы все новички в него тыкались.

cyber-jet
cyber-jet аватар
Offline
Зарегистрирован: 17.10.2017

arduinec пишет:

И назвать его надо "Непереполняемый миллис" и закрепить наверху, чтобы все новички в него тыкались.

А может быть просто дополнить описание millis(), unsigned int, unsigned long в разделе программирование?

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

Хорошее название, но я прочитал уже после того, как выложил - http://arduino.ru/forum/programmirovanie/velikoe-perepolnenie-millis

b707
Offline
Зарегистрирован: 26.05.2017

cyber-jet пишет:

А может быть просто дополнить описание millis(), unsigned int, unsigned long в разделе программирование?

Чем бы вы хотели дополнить описание unsigned long - тем, что оно-таки не бывает отрицательным? :)

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

cyber-jet пишет:

А может быть просто дополнить описание millis(), unsigned int, unsigned long в разделе программирование?

Понимаете, это собственно не про millis и не про указанные Вами типы. Те же правила действуют для ЛЮБОГО целого типа и в любой другой ситуации - это просто правила арифметики на машинах с этой архитектурой.

cyber-jet
cyber-jet аватар
Offline
Зарегистрирован: 17.10.2017

b707 пишет:

Чем бы вы хотели дополнить описание unsigned long - тем, что оно-таки не бывает отрицательным? :)

Описать, как оно воспринимает отрицательные значения. Ведь восприниммает, факт?

Smith2007
Offline
Зарегистрирован: 30.10.2017

Уважаемые практики и начинающие. Позвольте задать вопрос...

У меня стоит задача подсчитать кол-во импульсов на pin в течении 1 сек. Максимальная частота импульсов 300 Гц.

В зависимости от результата (4 пороговых значения) производить те или иные действия. (вечером скину свой скетч. пока не доступен). При этом в зависимости от значения нужно мигать светодиодами с разной частотой и генерить звуковой сигнал.

Я реализовал этот алгоритм с использованием millis() и библиотеки toneAC(). Работает. 

Дальше стал изучать таймеры (в том числе эту ветку) и понял, что я только в самом начале пути в изучении возможностей ардуино и самого контроллера.

Учитывая ограниченное ко-во таймеров, на Ваш взгля, на сколько вообще целесообразно задействовать таймеры для таких низкоскоростных процессов (300 Гц)?

Понимаю, что хорошим тоном будет максимальное использование ресурсов самого МК, а уж потом обращение к функциям ардуино. Но должна быть гдето золотая середина в целесообразности обращения к самому МК? Или я рассуждаю не верно в виду отсутствия должного опыта?

 

И более практический вопрос: Правильным ли будет следующее решение? ( по задаче подсчета импульсов за 1 сек)

Сигнал с датчика завести на прервание и подсчитывать в функции прерывания.

По таймеру генерить прерывание каждую секунду и вызывать программу сравнения.

Или можно настроить МК на подсчтет импульсов за определенное время? (в этом случае мне видится использование сразу 2-ух таймеров, что не есть хорошо)

зы. кроме того в программе нужно помигать светодиодами с разной частотой и воспроизвести звуковой сингал определенной тональности. Т.е. теоретически на все может не хватить таймеров.

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

Smith2007 пишет:

 Работает. 

если работает зачем столько вопросов.....

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

 

Smith2007
Offline
Зарегистрирован: 30.10.2017

Вот скетч который получил....

PIN_BT_V_PLUS/PLUS - это заглушки, пока не пришел генератор сигналов.

block


// 
#include <button.h>
#include <BlinkLED.h> 
#include <toneAC.h> 


#define PIN_LED_REAR 11     // индикатор работы блокировки
#define PIN_SOLENOID 12     // тндикатор клапана блокировки
#define PIN_BT_LOCK_REAR 2  // кнопка включения блокировки
#define PIN_BT_V_PLUS 3     // кнопка увеличения скорости
#define PIN_BT_V_MINUS 4    // кнопка снижения скорости

BlinkLED ledRear(PIN_LED_REAR);      // индикатор работы блокировки

button btLockRear(PIN_BT_LOCK_REAR, true, 50); // Включение блокировки
button btVplus(PIN_BT_V_PLUS, true, 50); // изменение чвстоты вращения (+) 
button btVminus(PIN_BT_V_MINUS, true, 50); // изменение чвстоты вращения (-)


int imp_sum = 0;
int freq_cur =0;
int freq_blockOn = 5;   // запрет на включение блокировки 
int freq_warn  = 10;     // предупреждение об отключении
int freq_critical = 12;  // отключение
int df = 10; // шаг изменения чвстоты
unsigned int t_sum = 10; // время интеграции в сек
unsigned int _t_sum_ms; // время интеграции в мс
boolean blockRear = false; // флаг включения блокировки

unsigned long prewMillis;

void setup(void) {
  delay(1000);
  Serial.begin(115200);
  
  pinMode(PIN_SOLENOID, OUTPUT); // клапан блокировки

  ledRear.turnOff();
  _t_sum_ms = t_sum * 1000;
  prewMillis = millis();
}

void loop () {
  unsigned long _currentMillis = millis();
  btLockRear.update(); // обработка кнопки включения блокировки
  btVplus.update(); // кнопка увеличения скорости
  btVminus.update(); // кнопка снижения скорости

  // вычисление частоты
  
  if ( (_currentMillis - prewMillis) >= _t_sum_ms) {
    if (imp_sum == 0) {
      freq_cur = 0;}
    else { 
      freq_cur = imp_sum/t_sum;
      imp_sum = 0;
    }
    prewMillis = _currentMillis;
    Serial.println(freq_cur, DEC);                 
  }

  ledRear.update();

  // включаем флаг блокировки если нажата клавиша и скорость допустима
  if (btLockRear.btOn && (freq_cur <= freq_blockOn )) { // срабатывает по фронту включения кнопки
      blockRear = true; // установили флаг требования блокировки
      Serial.println("Block On"); 
      digitalWrite(PIN_SOLENOID, blockRear);
      toneAC(2360,10, 100);
  }
  // отключаем флаг блокировки
  if (btLockRear.btOff) {        // срабатывает по фронту выключения кнопки
    blockRear = false; // сняли флаг требования блокировки
    ledRear.turnOff(); // отключили светодиод кнопки
    Serial.println("Block Off"); 
    digitalWrite(PIN_SOLENOID, blockRear);
    toneAC(2360,10, 100);
  }

  if (btVplus.btOn) {
    imp_sum += df;
    Serial.print("imp_sum = "); 
    Serial.println(imp_sum); 
  }

  if (btVminus.btOn) {
    imp_sum -= df;
    if ( imp_sum < 0 ) { imp_sum = 0;}
    Serial.print("imp_sum = "); 
    Serial.println(imp_sum); 
  }
  // проверка на скорость
  if (freq_cur >= freq_critical) { 
    if (blockRear) { toneAC(2360, 10, 2000);}
    blockRear = false;
    digitalWrite(PIN_SOLENOID, blockRear); // выключили клапан блокировки
    ledRear.turnOff(); // отключили светодиод кнопки
  } else if (freq_cur >= freq_warn && blockRear) {
    ledRear.blink(150, 30);
  } else if (freq_cur >= freq_blockOn && blockRear) {
    ledRear.blink(300, 100);
  }else if (freq_cur >= 0 && blockRear) {
    ledRear.blink(600, 400);
  }
}  

button.h

#ifndef button_h
#define button_h

#if defined(ARDUINO) && ARDUINO >= 100
  #include "Arduino.h"
#else
  #include "WProgram.h"
#endif 

class button {
public:
    int pinBt; // номер пина кнопки, задается при инициализации
    bool state; // текущее состояние кнопки
    bool btOn; // момент нажатия кнопки
    bool btOff; // момент отпускания кнопки
  
    // Конструктор класса
    button (int pin, bool mode, int wait);
    
    // Нажатие кнопки
    bool update(void);
    unsigned long _prewMillis;

private:
    int _wait; // время задержки антидребезга
    bool _mode; // режим работы кнопки, задается при инициализации
                  // -0- если нормально разомкнутая кнопка соеденена с минусом
                  // -1- если нормально разомкнутая кнопка соеденена с плюсом через резистор 1К
    bool _p_state; // предыдущее состояние кнопки
    bool _p_state_filtr;
    unsigned long _currentTime;
    bool filtr(void);
    void clickOn(void);
    void clickOff(void);
};

#endif

 

button.cpp

#include "button.h"

//
// конструктор - вызывается всегда при создании экземпляра
//

    // Конструктор класса
    button::button (int pin, bool mode, int wait) {
      this->pinBt = pin;
      this->_mode = mode;
      this->_wait = wait;
      this->_prewMillis=0;
      this->state = false;
      this->btOn = false;
      this->btOff = false;
      this->_p_state = false;
      this->_p_state_filtr = false;
      pinMode(pinBt, INPUT_PULLUP);
    }
    
    // обработка нажатий кнопки
    bool button::update () {
      filtr(); // антидребезг
      clickOn();
      clickOff();
      return state; // вернуть значение
    }

// Антидребезг
    bool button::filtr() {
      _p_state = state;
      _currentTime = millis();
      if (digitalRead(pinBt) == LOW) { // если кнопка нажата
        if (!_p_state_filtr) { // если предыдущее значение кнопки "не нажата"
          _prewMillis = _currentTime; // засечь время
          _p_state_filtr = true; // установить предыдущее значение кнопки в "нажата"
        } else if ( (_currentTime - _prewMillis) >= _wait) { // если вышло время ожидания окончания дребезга
          state = true; // установить значение кнопки "нажата"
        }
      }
      else { // если кнопка не нажата
        if (_p_state_filtr) {
          _prewMillis = _currentTime; // засечь время
          _p_state_filtr = false; // установить предыдущее значение кнопки в "нажата"
        } else if ( (_currentTime - _prewMillis) >= _wait) { // и если вышло время ожидания окончания дребезга 
          state = false; // сброс значения текущего состояния кнопки
        }
      }
      return state;
    };


   
    // Нажатие кнопки
    void button::clickOn() {
      if (state && !_p_state) btOn = 1;
      else btOn = 0;
    }
    
    // Отпускание кнопки
    void button::clickOff() {
      if (!state && _p_state) btOff = 1;
      else btOff = 0;
    }

 

blinkLED.h

/*
	BlinkLED.h - library for Arduino - Version 0.1
	
	Original library 		(0.1) by Igor Kuznetsov

        Управление светодиодом
        update() - включить в основное тело программы (вызывать постоянно)

        Управление
        turnOn() - включить
        turnOff() - выключить
        blink(onPeriod, offPeriod) - пульсация. зажечь светодиод на onPeriod, погасить светодиод на offPeriod
                                     общий период = onPeriod + offPeriod, мс
                                     частота = 1 / (onPeriod + offPeriod) * 1000
 */

// ensure this library description is only included once
#ifndef BlinkLED_h
#define BlinkLED_h
#if defined(ARDUINO) && ARDUINO >= 100
  #include "Arduino.h"
#else
// include types & constants of Wiring core API
  #include "WProgram.h"
//  #include "Arduino.h"
#endif


// library interface description
class BlinkLED {
 
  // user-accessible "public" interface
  public:
  // Конструктор
    BlinkLED(int pinLED);
    
    void update(void);                // всегда вызывать в основном цикле
    void turnOn(void);	              // включить светодиод
    void turnOff(void);               // выключить светодиод
    void blink(int onPeriod, int offPeriod);    // включить мигание ton-период вкл, период выкл, мс

  private:
    int _pinLED;   // пин светодиода
    unsigned long int _currentTime; // временная переменная текущего времени   
    unsigned long int _ton;     // засечка времени на включение
    unsigned long int _toff;    // засечка времени на отключение
    bool _blink;                // режим мигания true - мигаем, false - не мигаем
    bool _ledState;             // состояние светодиода
    int _onPeriod;              // период свечения, мс
    int _offPeriod;             // период выключения, мс 
    void updatePin(bool);       // управление выходом
};

#endif

 

blinkLED.cpp

/*
  BlinkLED.h - for Wiring/Arduino
  (cc) 2017 Igor Kuznetsov. Russian
  
  Created 12 Nov 2017
  Version 0.1
*/


// include core Wiring API
//#include "WProgram.h"
#if defined(ARDUINO) && ARDUINO >= 100
  #include "Arduino.h"
#else
// include types & constants of Wiring core API
  #include "WProgram.h"
//  #include "Arduino.h"
#endif


// include this library's description file
#include "BlinkLED.h"

   
  
// Constructor /////////////////////////////////////////////////////////////////
// Function that handles the creation and setup of instances

//-------------------------- Using Arduino Pin Num
BlinkLED::BlinkLED(int pinLED)
{
    // инициализация переменных
    this->_pinLED = pinLED;  // pin к которому подключен светодиод
    this->_blink = false;    // отключить мигание при инициализации
    this->_ledState = false; // 
    this->_ton = 0;          //
    this->_toff = 0;         //
    this->_onPeriod = 500;   //
    this->_offPeriod = 500;  //
    pinMode(this->_pinLED, OUTPUT); // настройка pin
  
}

// Public Methods //////////////////////////////////////////////////////////////

//---------////////////////////MAIN//////////--------------//
// Вызывать на каждом цикле основной программы
void BlinkLED::update() {
   if (_blink) {                      // если включен режим мигания
     _currentTime = millis();         // текущее время
     if (_ledState) {                 // если светодиод включен
        if ( (_currentTime - _toff) >= _offPeriod ) { // выждали время отключения
             _ton = _currentTime;     // засечка времени включения
             _ledState = !_ledState;  // инвертируем (выключили)
             updatePin(_ledState);    // обновили выходной pin
           }
     } else {                         // если светодиод выключен
        if ( (_currentTime - _ton) >= _onPeriod ) {  // выждали время включения
             _toff = _currentTime;    // засечка времени выключения
             _ledState = !_ledState;  // инвертируем (выключаем)
             updatePin(_ledState);    // обновили выходной pin
             }
     }
   } 
}


void BlinkLED::turnOn(void){
    _blink = false;        // отключить пульсацию
    _ledState = true;
    updatePin(_ledState);  // включить светодиод
}

void BlinkLED::turnOff(void){
    _blink = false;       // отключить пульсацию
    _ledState = false;
    updatePin(_ledState); // выключить светодиод
}


void BlinkLED::blink(int onPeriod, int offPeriod) {
     _onPeriod = onPeriod;   // установить период включения
     _offPeriod = offPeriod; // установить период выключения
     _blink = true;          // включить пульсацию
}


// Private Methods //////////////////////////////////////////////////////////////

void BlinkLED::updatePin(bool pinValue) {
    digitalWrite(_pinLED, pinValue);     // обновление выходного pin
}

 

Smith2007
Offline
Зарегистрирован: 30.10.2017

andycat пишет:

Smith2007 пишет:

 Работает. 

если работает зачем столько вопросов.....

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

 

Понимаю, что идеала не добиться ни когда. Но как такое решение с точки зрения инженера? Расточительство ресурсов?  Или "не забивай голову раз работает"? 

Не могу сказать чем еще обрастет задача, но на мой взгляд ресурсы лучше экономить.

И остается открытый вопрос: как задйствовать ресурсы МК что бы посчитать кол-во импульсов за опеределнное время. Можно ли это сделать средствами одного только таймера? 

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

Smith2007 пишет:

Сигнал с датчика завести на прервание и подсчитывать в функции прерывания.

Я бы сигналом с датчика тактировал бы таймер. Тогда и считать ничего не надо - таймер сам подсчитает.

Т.е. подсчёт вылядит так:

1. Конфигурируем один таймер на тактирование от датчика

2. конфигурируем второй таймер на прерывание через секунду

По прерыванию второго таймера просто снимает TCNT первого - это и есть количество имульсов за секунду.

Smith2007
Offline
Зарегистрирован: 30.10.2017

ЕвгенийП пишет:

Smith2007 пишет:

Сигнал с датчика завести на прервание и подсчитывать в функции прерывания.

Я бы сигналом с датчика тактировал бы таймер. Тогда и считать ничего не надо - таймер сам подсчитает.

Т.е. подсчёт вылядит так:

1. Конфигурируем один таймер на тактирование от датчика

2. конфигурируем второй таймер на прерывание через секунду

По прерыванию второго таймера просто снимает TCNT первого - это и есть количество имульсов за секунду.

1. Т.е. настраиваем один из таймеров МК в режим счетчика импульсов?

2. Второй таймер раз в сек будет генерить прерывания? Или сразу будет возможным значение с TCNT считать? 

Можете привести пример как бы это выглядело?

И в этом случае получается, что мы используем 2 таймера. А на функцию toneAT уже не хватит таймера. Она работает с пинами 9,10 и задействует таймер. 

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

Значение TCNT можно читать в любой момент, но смысл второго таймера в том, чтобы читать его точно тогда, когда нужно.

Почему не останется таймеров? Какой контроллер у Вас? Если 328, то там их три и ещё WTD - тоже какой-никакой таймер.

Пример, разве что вечером, если актуальность не отпадёт.

Smith2007
Offline
Зарегистрирован: 30.10.2017

ЕвгенийП пишет:

Значение TCNT можно читать в любой момент, но смысл второго таймера в том, чтобы читать его точно тогда, когда нужно.

Почему не останется таймеров? Какой контроллер у Вас? Если 328, то там их три и ещё WTD - тоже какой-никакой таймер.

Пример, разве что вечером, если актуальность не отпадёт.

Во первых:

Спасибо Вам огромное за ЛекБез :) Я только начинаю осваивать это дивный прибор :)

Контроллер iskra neo (leonardo), но это для отладки. В реальную схему думаю мини ставить, что бы размерчик поменьше был.

TCNT можео только читать или записать значение так же можно?

Актуальность не отпадет. Я все пытаюсь золотую середину найти балансируя между желанием сделать все очень грамотно и не в лезть в потенциальные проблемы.

После прочтения Вашей ветки подумал, что и светодиодами нужно блинкать не millis(), а таймерами самого контроллера.

В целом в задаче:

Мигание светодиодами, звуковые сигналы, отсчет кол-ва импульсов за время t. Ну и соответственно управление исполнительными устройствами (электроклапана).

И для отладки хочеться оставить возможнсть Serial.println() , а это вероятнее всего нельзя трогать 0-вой таймер.

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

Smith2007 пишет:

TCNT можео только читать или записать значение так же можно?

Разумеется, иначе как им пользоваться. Надо же в начале туда 0 запихать.

Smith2007 пишет:

возможнсть Serial.println() , а это вероятнее всего нельзя трогать 0-вой таймер.

Никакого отношения в таймерам Serial не имеет.

 

Smith2007
Offline
Зарегистрирован: 30.10.2017

ЕвгенийП пишет:

Smith2007 пишет:

возможнсть Serial.println() , а это вероятнее всего нельзя трогать 0-вой таймер.

Никакого отношения в таймерам Serial не имеет.

Т.е. serial не использует аппратные таймеры контроллера? А чем достигается выбор скорости 9600, 115200, etc...?

Или для коммуникационных целей там отдельная логика со своим тактованием?

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

Аппаратная реализация UART (Serail на нём сделан) описана в даташите (см.) - таймеры он точно не занимает.

sim31
sim31 аватар
Offline
Зарегистрирован: 26.07.2017

Если используете Ардуино среду, смотрите в исходниках ядра открыт для редактирования один из файлов, там используется прерывание ISR0 уже всё настроено, очень удобно, ничего придумывать не нужно. Это прерывание для системного таймера, частота около 1000Гц. Туда дописываете свою функцию, чтобы раз в секунду вызывать обработку.

Это же прервывание выводит МК из режима сна IDLE, тоже иногда удобно.

bwn
Offline
Зарегистрирован: 25.08.2014

Smith2007 пишет:

Спасибо Вам огромное за ЛекБез :) Я только начинаю осваивать это дивный прибор :)

Оффтоп, но малость позанудствую.)))) Ликбез - термин появился на заре Советской власти, образован из двух начальных  слогов от "Ликвидация безграмотности".))))

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

sim31 пишет:

открыт для редактирования один из файлов, там используется прерывание ISR0 уже всё настроено, очень удобно, ничего придумывать не нужно. Это прерывание для системного таймера, частота около 1000Гц. 

Простите, боюсь, что я не понял Вас. Можно поподробнее, что за файл, что за прерывание?

Smith2007
Offline
Зарегистрирован: 30.10.2017

Что-то я совсем заклинил....

Тут все ясно. Присвоение переменной TCCR1A значения 0х40 (6 бит в 1, остальные 0)

TCCR1A = 0x40;          // Инвертирование пина 9 по сравнению

Следующую конструкциию ни как не расшифрую

TCCR1A=1<<COM1A0;  // Переключение пина 9 по сравнению

Берем единичку (0ой бит = 1) и сдвигаем ее влево на COM1A0 бит. А чему равно "COM1A0" ? Полученное присвоили переменной TCCR1A (регистр таймера)

Правильно я понимаю, что первый и 2ой варианты делают одно и тоже, но второй вариант занимает меньше кода (не нужно число 0х40,  но нужна команда)

 

TCCR1A=1<<6; // это приведет к аналогичному результату?

 

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

Все три записи - абсолютно идентичны. Нет никакой разницы.

TCCR1A = 0x40;          // Инвертирование пина 9 по сравнению
TCCR1A=1<<COM1A0;  // Переключение пина 9 по сравнени
TCCR1A=1<<6; // это приведет к аналогичному результату?
ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

Пример тактирования таймера от внешнего источника сигнала нужен?

Smith2007
Offline
Зарегистрирован: 30.10.2017

ЕвгенийП пишет:

Пример тактирования таймера от внешнего источника сигнала нужен?

Был бы очень признателен.

И если не затруднит - второй пример как вместо управления пинами (none, toggle, clear, set) инициировать программное прерывание и выполнить указанную процедуру.

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

Значит так, будем тактировать таймер №1 от внешнего сигнала.

Это всегда делается через пин 5 и ни через какой другой.

Тактироваться будем по нисходящему фронту, т.е. когда пин переходит с HIGH на LOW (по восходящему фронту тоже можно, в программе это закомментировано - см. текст)

Чтобы не  не дергать проводочком на землю, я для теста соединил пин 5 с пином 9 через резистор и подаю сигнал на пин 9 (простым digitalWrite), а т.к. он соединён с пином 5, таймер должен этот сигнал посчитать.

Вот текст

// Источник внешнего тактирования таймера 1 - ВСЕГДА пин 5!
static const uint8_t pinT1 = 5;	

// на этом пине мы иммитируем сигнал - любой пин
static const uint8_t pinSignal = 9;	

//
//	Инициализация таймера для внешнего тактирования
//
static void timerOneInit(void) {
	TCCR1A = 0; // на всякий сулчай, если кто-то до нас туда что-то записал
	TIMSK1 = 0; // на всякий сулчай, если кто-то до нас туда что-то записал
	//
	// Тактирование с пина T1 по ниспадающему фронту
	TCCR1B = bit(CS12) | bit(CS11); 
	// Тактирование с пина T1 по восходящему фронту
	//TCCR1B = bit(CS12) | bit(CS11) | bit(CS10); 
}

//
//	Сброс счётчика таймера 1
//
static inline void clearTimerOne(void) {
	TCNT1 = 0;
}

//
//	Чтение и, если надо, сброс счётчика таймера 1
//
static inline uint16_t readTimerOne(const bool clear = true) {
	const uint16_t res = TCNT1;
	if (clear) clearTimerOne();
	return res;
}

//
//	Выдать сигнал 
// (пин "pinSignal" соединён с пятым пином через резистор)
//
static void issueSignal(void) {
	digitalWrite(pinSignal, HIGH);
	digitalWrite(pinSignal, LOW);
}

void setup() {
	pinMode(pinT1, INPUT_PULLUP);
	pinMode(pinSignal, OUTPUT);
	Serial.begin(115200);
	Serial.println("Fun begins!");
	timerOneInit(); // Подготовить таймер
	clearTimerOne(); // очистить таймер
	issueSignal(); // выдать сигнал
	issueSignal(); // выдать сигнал
	issueSignal(); // выдать сигнал
	issueSignal(); // выдать сигнал
	Serial.print("Signals captured:");
	Serial.println(readTimerOne()); // напечатать количество пойманных сигналов.
}

void loop() {
}

Печатает число 4, т.к. мы подаём 4 сигнала (строки 52-55). Размножьте эти строки, скажем до 9 - будет печатать число 9.

Техника работы понятна?

Теперь спокойно конфигурируете таймер, подключаете свой источник сигнала, а секунду считаете хоть через millis, хоть другим таймером - и все дела.

Позапускайте, поменяйте и убедитесь, что пример понятен.

Smith2007 пишет:

И если не затруднит - второй пример как вместо управления пинами (none, toggle, clear, set) инициировать программное прерывание и выполнить указанную процедуру.

На каком таймере?

Smith2007
Offline
Зарегистрирован: 30.10.2017

Евгений, спасибо за разъяснения.

Изучая регистры таймера я понял, что биты 0,1,2 (CS10, CS11, CS12) регистра TCCR1B отвечают:

- за делитель при внутреннем тактовании от генератора

- за тактование от пина 5. При чем бит 0 (CS10) отвечает за то по фронту или срезу будет происходить инкремент счетчика.

Регистр TCCR1A вы обнуляете совсем. А разве нельзя при этом продолжить дергать пинами 9,10? TCCR1A = 0x50; ?

Вот эту запись не понял TIMSK1 = 0

 

И если не затрудинит то прерывание тоже на 1ом таймере.

В целом я начал понимать его работу, но не хватает опыта :)

И знаний :)

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

Smith2007 пишет:

Регистр TCCR1A вы обнуляете совсем. А разве нельзя при этом продолжить дергать пинами 9,10? TCCR1A = 0x50; ?

Не было такой задачи.

Smith2007 пишет:

Вот эту запись не понял TIMSK1 = 0

Просто обнулил на всякий случай. Мало ли кто там чего записал, а мне в нём ничего не нужно.

 

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

Ну, вот здесь хорошо видно, что функция "ISR(TIMER1_COMPA_vect)" вызывается раз в секунду.

ISR(TIMER1_COMPA_vect) {
	Serial.println(millis());
}

void setup() {
	Serial.begin(115200);
	TCCR1A = 0;	// Пины 9 и 10 отключаем (ими можно пользоваться обычным образом)
	OCR1A = 15625u;	// при делителе 1024, это 1 сек., если я нигде не лажанулся
	TIMSK1 = bit(OCIE1A);	// Разрешаем прерывание по сравнению с OCR1A
	TCCR1B = bit(WGM12) | bit(CS12) | bit(CS10);  // Установить СТС режим и делитель частоты
 }

void loop() {}

Вообще-то вызывать Serial.print из обработчика прерывания плохая идея, по уму бы там флажок взводить, но для маленького теста - нормально.

Smith2007
Offline
Зарегистрирован: 30.10.2017

Не найду связь с 

ISR(TIMER1_COMPA_vect) 

 

Где мы указываем, что при прерывании нужно вызвать именно ISR(TIMER1_COMPA_vect) , а не ISR(ohohohoho) 

cyber-jet
cyber-jet аватар
Offline
Зарегистрирован: 17.10.2017

Smith2007 пишет:

По таймеру генерить прерывание каждую секунду и вызывать программу сравнения.

void loop() 
{
  if( millis() % 1000 == 0 ){
    do something...
  }
}

не оно?

 

Smith2007
Offline
Зарегистрирован: 30.10.2017

cyber-jet пишет:

Smith2007 пишет:

По таймеру генерить прерывание каждую секунду и вызывать программу сравнения.

void loop() 
{
  if( millis() % 1000 == 0 ){
    do something...
  }
}

не оно?

Ну это не прерывание, а программная обработка. И боюсь вот это

 if( millis() % 1000 == 0 ){ не будет работать вовсе.

Где гарантия, что на эту строку кода будет приходится millis() кратная 1000?

А если 1001, а в следующий цикл 2002?

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

Smith2007 пишет:

Где мы указываем, что при прерывании нужно вызвать именно ISR(TIMER1_COMPA_vect) , а не ISR(ohohohoho) 

Это нигде не указывается. Прерывания так устроены, что если какое-то случается, то вызывается именно его обработчик, а не какого-то другого. Мы лишь разрешили этому прерыванию происходить (строка 9)

Smith2007
Offline
Зарегистрирован: 30.10.2017

А если прерывание будет с таймера2? Как тогда быть? Какую процедуру тогда вызывать? Или на любое программное прерывание ( совпадение значений, переполнение счетчика и ты) от всех таймеров будет вызвана именно процедура isr() ?

Одна на всех?

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Smith2007 пишет:

А если прерывание будет с таймера2? Как тогда быть? Какую процедуру тогда вызывать? Или на любое программное прерывание ( совпадение значений, переполнение счетчика и ты) от всех таймеров будет вызвана именно процедура isr() ?

Одна на всех?

Нет.  Для каждого таймера есть несколько разных прерываний.  Совпадение по 2 каналам (где есть) и переполнение.  Соответственно, обрабоччики вызываюца разные

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

Smith2007 пишет:

А если прерывание будет с таймера2? Как тогда быть? 

У него свой веткор. Я Вам вчера давал ссылку на даташит. Список всех векторов прерываний там на стр. 82. И, кстати, полное описание работы всех таймеров во всех режимах - стр. 125-215.

Smith2007
Offline
Зарегистрирован: 30.10.2017

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

В Вашем примере ISR(TIMER1_COMPA_vect)

В таблице такого вектора не нашел. Есть только TIMER1_COMPA

Или при компиляции эти записи одинаково укажут на вектор TIMER1_COMPA ?

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Я получаю исинное наслаждение от этого сериала, но  " пепел Клааса стучит в мое сердце..." и все такое. милосердие ипрочий бред.

В связи с этим вопрос, Вы по аглицки читаете, верно? Вот документация на avr-libc которая и есть основная библиотека компилятора для AVR процессоров, включая нашу любиую Ардуину.

Вот страничка про прерывания.

По ссылке есть таблица векторов, как они называются ИМЕННО В НАШЕМ компиляторе.

 

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

А хто не розумиэ английську мову -то есть на русском :)

Smith2007
Offline
Зарегистрирован: 30.10.2017

К сожалению, мой опыт в программировании очень скуден. И Си осваиваю вот прямо в настоящее время. Возможно мои вопросы кажутся наивными, но не для новичка. Есть некоторый опыт в программировании на Codesys, для промышленных ПЛК и то только в качестве хобби.
Так что прошу не судить строго за дилетанские вопросы.
Зы. Ветка вроде как раз для вопросов дилетантов

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

Smith2007 пишет:

В Вашем примере ISR(TIMER1_COMPA_vect)

В таблице такого вектора не нашел. Есть только TIMER1_COMPA

"_vect" - это такой волшебгый суффикс, который надо добавлять в конец названиям векторов прерываний из этой таблицы.

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

Smith2007 пишет:
Си осваиваю вот прямо в настоящее время.
А причём тут Си? Программировать контроллер можно на чём угодно, хоть на Брейнфаке, хоть на, прости Господи, Паскале.