1. Надо вычитать, а не складывать (см сообщение #172), а Вы складываете. Это просто ошибка в программе.
2. При правильном программировании (когда вычитают) всё работает без проблем от слова совсем (см. сообщение #177) позапускайте тот скетч и раберитесь.
3. Я очень просил не задавать мне вопросы "почему так", а вместо этого запустить поиск по форуму - тема обсуждалась 100500 раз. "Не спрашивайте про переполнение миллис и да не посланы будете".
я решил положить этому конец. Сейчас подготовлю пост в который включу скетч из поста #177, задачу из поста #198 и ещё одну задачу про двойку. Напишу текст такого рода: если мол есть вопросы по переполнения миллис, то алгоритм такой:
я например заранее сбрасываю счетчик в своих проектах, не дожидаясь 50 суток
Зря вы это написали в этой ветке... До этого я считал вас грамотным кодером :)
верите - мне пофиг кем вы меня считаете :)
во вторых я уже писал - я не профессиональный программист, и тем более не "кодер" к которым у меня отвратительное отношение т.к. они могут только по ТЗ работать, а мозг не включают совсем.
А может быть просто дополнить описание millis(), unsigned int, unsigned long в разделе программирование?
Понимаете, это собственно не про millis и не про указанные Вами типы. Те же правила действуют для ЛЮБОГО целого типа и в любой другой ситуации - это просто правила арифметики на машинах с этой архитектурой.
Уважаемые практики и начинающие. Позвольте задать вопрос...
У меня стоит задача подсчитать кол-во импульсов на pin в течении 1 сек. Максимальная частота импульсов 300 Гц.
В зависимости от результата (4 пороговых значения) производить те или иные действия. (вечером скину свой скетч. пока не доступен). При этом в зависимости от значения нужно мигать светодиодами с разной частотой и генерить звуковой сигнал.
Я реализовал этот алгоритм с использованием millis() и библиотеки toneAC(). Работает.
Дальше стал изучать таймеры (в том числе эту ветку) и понял, что я только в самом начале пути в изучении возможностей ардуино и самого контроллера.
Учитывая ограниченное ко-во таймеров, на Ваш взгля, на сколько вообще целесообразно задействовать таймеры для таких низкоскоростных процессов (300 Гц)?
Понимаю, что хорошим тоном будет максимальное использование ресурсов самого МК, а уж потом обращение к функциям ардуино. Но должна быть гдето золотая середина в целесообразности обращения к самому МК? Или я рассуждаю не верно в виду отсутствия должного опыта?
И более практический вопрос: Правильным ли будет следующее решение? ( по задаче подсчета импульсов за 1 сек)
Сигнал с датчика завести на прервание и подсчитывать в функции прерывания.
По таймеру генерить прерывание каждую секунду и вызывать программу сравнения.
Или можно настроить МК на подсчтет импульсов за определенное время? (в этом случае мне видится использование сразу 2-ух таймеров, что не есть хорошо)
зы. кроме того в программе нужно помигать светодиодами с разной частотой и воспроизвести звуковой сингал определенной тональности. Т.е. теоретически на все может не хватить таймеров.
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
}
По моей скромной практике - не нужно забивать себе голову лишним, появится проблема/задача - решите ее - появятся новые знания.
Понимаю, что идеала не добиться ни когда. Но как такое решение с точки зрения инженера? Расточительство ресурсов? Или "не забивай голову раз работает"?
Не могу сказать чем еще обрастет задача, но на мой взгляд ресурсы лучше экономить.
И остается открытый вопрос: как задйствовать ресурсы МК что бы посчитать кол-во импульсов за опеределнное время. Можно ли это сделать средствами одного только таймера?
Значение TCNT можно читать в любой момент, но смысл второго таймера в том, чтобы читать его точно тогда, когда нужно.
Почему не останется таймеров? Какой контроллер у Вас? Если 328, то там их три и ещё WTD - тоже какой-никакой таймер.
Пример, разве что вечером, если актуальность не отпадёт.
Во первых:
Спасибо Вам огромное за ЛекБез :) Я только начинаю осваивать это дивный прибор :)
Контроллер iskra neo (leonardo), но это для отладки. В реальную схему думаю мини ставить, что бы размерчик поменьше был.
TCNT можео только читать или записать значение так же можно?
Актуальность не отпадет. Я все пытаюсь золотую середину найти балансируя между желанием сделать все очень грамотно и не в лезть в потенциальные проблемы.
После прочтения Вашей ветки подумал, что и светодиодами нужно блинкать не millis(), а таймерами самого контроллера.
В целом в задаче:
Мигание светодиодами, звуковые сигналы, отсчет кол-ва импульсов за время t. Ну и соответственно управление исполнительными устройствами (электроклапана).
И для отладки хочеться оставить возможнсть Serial.println() , а это вероятнее всего нельзя трогать 0-вой таймер.
Если используете Ардуино среду, смотрите в исходниках ядра открыт для редактирования один из файлов, там используется прерывание ISR0 уже всё настроено, очень удобно, ничего придумывать не нужно. Это прерывание для системного таймера, частота около 1000Гц. Туда дописываете свою функцию, чтобы раз в секунду вызывать обработку.
Это же прервывание выводит МК из режима сна IDLE, тоже иногда удобно.
Спасибо Вам огромное за ЛекБез :) Я только начинаю осваивать это дивный прибор :)
Оффтоп, но малость позанудствую.)))) Ликбез - термин появился на заре Советской власти, образован из двух начальных слогов от "Ликвидация безграмотности".))))
открыт для редактирования один из файлов, там используется прерывание ISR0 уже всё настроено, очень удобно, ничего придумывать не нужно. Это прерывание для системного таймера, частота около 1000Гц.
Простите, боюсь, что я не понял Вас. Можно поподробнее, что за файл, что за прерывание?
Все три записи - абсолютно идентичны. Нет никакой разницы.
TCCR1A = 0x40; // Инвертирование пина 9 по сравнению
TCCR1A=1<<COM1A0; // Переключение пина 9 по сравнени
TCCR1A=1<<6; // это приведет к аналогичному результату?
Пример тактирования таймера от внешнего источника сигнала нужен?
Был бы очень признателен.
И если не затруднит - второй пример как вместо управления пинами (none, toggle, clear, set) инициировать программное прерывание и выполнить указанную процедуру.
Значит так, будем тактировать таймер №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) инициировать программное прерывание и выполнить указанную процедуру.
Ну, вот здесь хорошо видно, что функция "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 из обработчика прерывания плохая идея, по уму бы там флажок взводить, но для маленького теста - нормально.
Где мы указываем, что при прерывании нужно вызвать именно ISR(TIMER1_COMPA_vect) , а не ISR(ohohohoho)
Это нигде не указывается. Прерывания так устроены, что если какое-то случается, то вызывается именно его обработчик, а не какого-то другого. Мы лишь разрешили этому прерыванию происходить (строка 9)
А если прерывание будет с таймера2? Как тогда быть? Какую процедуру тогда вызывать? Или на любое программное прерывание ( совпадение значений, переполнение счетчика и ты) от всех таймеров будет вызвана именно процедура isr() ?
А если прерывание будет с таймера2? Как тогда быть? Какую процедуру тогда вызывать? Или на любое программное прерывание ( совпадение значений, переполнение счетчика и ты) от всех таймеров будет вызвана именно процедура isr() ?
Одна на всех?
Нет. Для каждого таймера есть несколько разных прерываний. Совпадение по 2 каналам (где есть) и переполнение. Соответственно, обрабоччики вызываюца разные
А если прерывание будет с таймера2? Как тогда быть?
У него свой веткор. Я Вам вчера давал ссылку на даташит. Список всех векторов прерываний там на стр. 82. И, кстати, полное описание работы всех таймеров во всех режимах - стр. 125-215.
Я получаю исинное наслаждение от этого сериала, но " пепел Клааса стучит в мое сердце..." и все такое. милосердие ипрочий бред.
В связи с этим вопрос, Вы по аглицки читаете, верно? Вот документация на avr-libc которая и есть основная библиотека компилятора для AVR процессоров, включая нашу любиую Ардуину.
К сожалению, мой опыт в программировании очень скуден. И Си осваиваю вот прямо в настоящее время. Возможно мои вопросы кажутся наивными, но не для новичка. Есть некоторый опыт в программировании на Codesys, для промышленных ПЛК и то только в качестве хобби.
Так что прошу не судить строго за дилетанские вопросы.
Зы. Ветка вроде как раз для вопросов дилетантов
А мою задачу решили? :)))
я наверное тоже что то не понимаю - просветите плиз меня:
вот пример сделал, если у него закомментировать условие
то счетчик останавливается. - т.е. не счетчик а отображение/действие ежесекундное
естественно я 50 суток не ждал - потому и начальные значения сделал почти максимальными
1. Надо вычитать, а не складывать (см сообщение #172), а Вы складываете. Это просто ошибка в программе.
2. При правильном программировании (когда вычитают) всё работает без проблем от слова совсем (см. сообщение #177) позапускайте тот скетч и раберитесь.
3. Я очень просил не задавать мне вопросы "почему так", а вместо этого запустить поиск по форуму - тема обсуждалась 100500 раз. "Не спрашивайте про переполнение миллис и да не посланы будете".
я наверное тоже что то не понимаю - просветите плиз меня:
3.14здец, как это надоело!
Тупой и еще тупее!
Гуглим "арифметика в дополнительном коде".
ВЫЧИТАНИЕ в доп коде нечувствительно к переполнению, а сложение чувствительно, понятно?
поэтому условие вида:
работает без нарушений на 50 день
а условие вида
имеет сбой на 50 день
========================
теперь внимание: вопрос!
Какая из еречисленных арифметических операция использована в твоем примере?
не надо так нервничать :)
понял я, перевожу проекты на счетчики с вычитанием.
"Не спрашивайте про переполнение миллис и да не посланы будете".
Женя! Это судьба...
у тебя был прекрасный пример, который можно кидать спрашивающим, там, где неважно знаковый или беззнаковый тип. Он где-то на форуме есть..
я например заранее сбрасываю счетчик в своих проектах, не дожидаясь 50 суток
Зря вы это написали в этой ветке... До этого я считал вас грамотным кодером :)
wdrakula,
я решил положить этому конец. Сейчас подготовлю пост в который включу скетч из поста #177, задачу из поста #198 и ещё одну задачу про двойку. Напишу текст такого рода: если мол есть вопросы по переполнения миллис, то алгоритм такой:
я например заранее сбрасываю счетчик в своих проектах, не дожидаясь 50 суток
Зря вы это написали в этой ветке... До этого я считал вас грамотным кодером :)
верите - мне пофиг кем вы меня считаете :)
во вторых я уже писал - я не профессиональный программист, и тем более не "кодер" к которым у меня отвратительное отношение т.к. они могут только по ТЗ работать, а мозг не включают совсем.
а Arduino у меня просто хобби.
я решил положить этому конец. Сейчас подготовлю пост...
И назвать его надо "Непереполняемый миллис" и закрепить наверху, чтобы все новички в него тыкались.
И назвать его надо "Непереполняемый миллис" и закрепить наверху, чтобы все новички в него тыкались.
А может быть просто дополнить описание millis(), unsigned int, unsigned long в разделе программирование?
Хорошее название, но я прочитал уже после того, как выложил - http://arduino.ru/forum/programmirovanie/velikoe-perepolnenie-millis
А может быть просто дополнить описание millis(), unsigned int, unsigned long в разделе программирование?
Чем бы вы хотели дополнить описание unsigned long - тем, что оно-таки не бывает отрицательным? :)
А может быть просто дополнить описание millis(), unsigned int, unsigned long в разделе программирование?
Понимаете, это собственно не про millis и не про указанные Вами типы. Те же правила действуют для ЛЮБОГО целого типа и в любой другой ситуации - это просто правила арифметики на машинах с этой архитектурой.
Чем бы вы хотели дополнить описание unsigned long - тем, что оно-таки не бывает отрицательным? :)
Описать, как оно воспринимает отрицательные значения. Ведь восприниммает, факт?
Уважаемые практики и начинающие. Позвольте задать вопрос...
У меня стоит задача подсчитать кол-во импульсов на pin в течении 1 сек. Максимальная частота импульсов 300 Гц.
В зависимости от результата (4 пороговых значения) производить те или иные действия. (вечером скину свой скетч. пока не доступен). При этом в зависимости от значения нужно мигать светодиодами с разной частотой и генерить звуковой сигнал.
Я реализовал этот алгоритм с использованием millis() и библиотеки toneAC(). Работает.
Дальше стал изучать таймеры (в том числе эту ветку) и понял, что я только в самом начале пути в изучении возможностей ардуино и самого контроллера.
Учитывая ограниченное ко-во таймеров, на Ваш взгля, на сколько вообще целесообразно задействовать таймеры для таких низкоскоростных процессов (300 Гц)?
Понимаю, что хорошим тоном будет максимальное использование ресурсов самого МК, а уж потом обращение к функциям ардуино. Но должна быть гдето золотая середина в целесообразности обращения к самому МК? Или я рассуждаю не верно в виду отсутствия должного опыта?
И более практический вопрос: Правильным ли будет следующее решение? ( по задаче подсчета импульсов за 1 сек)
Сигнал с датчика завести на прервание и подсчитывать в функции прерывания.
По таймеру генерить прерывание каждую секунду и вызывать программу сравнения.
Или можно настроить МК на подсчтет импульсов за определенное время? (в этом случае мне видится использование сразу 2-ух таймеров, что не есть хорошо)
зы. кроме того в программе нужно помигать светодиодами с разной частотой и воспроизвести звуковой сингал определенной тональности. Т.е. теоретически на все может не хватить таймеров.
Работает.
если работает зачем столько вопросов.....
По моей скромной практике - не нужно забивать себе голову лишним, появится проблема/задача - решите ее - появятся новые знания.
Вот скетч который получил....
PIN_BT_V_PLUS/PLUS - это заглушки, пока не пришел генератор сигналов.
block
button.h
button.cpp
blinkLED.h
blinkLED.cpp
Работает.
если работает зачем столько вопросов.....
По моей скромной практике - не нужно забивать себе голову лишним, появится проблема/задача - решите ее - появятся новые знания.
Понимаю, что идеала не добиться ни когда. Но как такое решение с точки зрения инженера? Расточительство ресурсов? Или "не забивай голову раз работает"?
Не могу сказать чем еще обрастет задача, но на мой взгляд ресурсы лучше экономить.
И остается открытый вопрос: как задйствовать ресурсы МК что бы посчитать кол-во импульсов за опеределнное время. Можно ли это сделать средствами одного только таймера?
Сигнал с датчика завести на прервание и подсчитывать в функции прерывания.
Я бы сигналом с датчика тактировал бы таймер. Тогда и считать ничего не надо - таймер сам подсчитает.
Т.е. подсчёт вылядит так:
1. Конфигурируем один таймер на тактирование от датчика
2. конфигурируем второй таймер на прерывание через секунду
По прерыванию второго таймера просто снимает TCNT первого - это и есть количество имульсов за секунду.
Сигнал с датчика завести на прервание и подсчитывать в функции прерывания.
Я бы сигналом с датчика тактировал бы таймер. Тогда и считать ничего не надо - таймер сам подсчитает.
Т.е. подсчёт вылядит так:
1. Конфигурируем один таймер на тактирование от датчика
2. конфигурируем второй таймер на прерывание через секунду
По прерыванию второго таймера просто снимает TCNT первого - это и есть количество имульсов за секунду.
1. Т.е. настраиваем один из таймеров МК в режим счетчика импульсов?
2. Второй таймер раз в сек будет генерить прерывания? Или сразу будет возможным значение с TCNT считать?
Можете привести пример как бы это выглядело?
И в этом случае получается, что мы используем 2 таймера. А на функцию toneAT уже не хватит таймера. Она работает с пинами 9,10 и задействует таймер.
Значение TCNT можно читать в любой момент, но смысл второго таймера в том, чтобы читать его точно тогда, когда нужно.
Почему не останется таймеров? Какой контроллер у Вас? Если 328, то там их три и ещё WTD - тоже какой-никакой таймер.
Пример, разве что вечером, если актуальность не отпадёт.
Значение TCNT можно читать в любой момент, но смысл второго таймера в том, чтобы читать его точно тогда, когда нужно.
Почему не останется таймеров? Какой контроллер у Вас? Если 328, то там их три и ещё WTD - тоже какой-никакой таймер.
Пример, разве что вечером, если актуальность не отпадёт.
Во первых:
Спасибо Вам огромное за ЛекБез :) Я только начинаю осваивать это дивный прибор :)
Контроллер iskra neo (leonardo), но это для отладки. В реальную схему думаю мини ставить, что бы размерчик поменьше был.
TCNT можео только читать или записать значение так же можно?
Актуальность не отпадет. Я все пытаюсь золотую середину найти балансируя между желанием сделать все очень грамотно и не в лезть в потенциальные проблемы.
После прочтения Вашей ветки подумал, что и светодиодами нужно блинкать не millis(), а таймерами самого контроллера.
В целом в задаче:
Мигание светодиодами, звуковые сигналы, отсчет кол-ва импульсов за время t. Ну и соответственно управление исполнительными устройствами (электроклапана).
И для отладки хочеться оставить возможнсть Serial.println() , а это вероятнее всего нельзя трогать 0-вой таймер.
TCNT можео только читать или записать значение так же можно?
Разумеется, иначе как им пользоваться. Надо же в начале туда 0 запихать.
возможнсть Serial.println() , а это вероятнее всего нельзя трогать 0-вой таймер.
Никакого отношения в таймерам Serial не имеет.
возможнсть Serial.println() , а это вероятнее всего нельзя трогать 0-вой таймер.
Никакого отношения в таймерам Serial не имеет.
Т.е. serial не использует аппратные таймеры контроллера? А чем достигается выбор скорости 9600, 115200, etc...?
Или для коммуникационных целей там отдельная логика со своим тактованием?
Аппаратная реализация UART (Serail на нём сделан) описана в даташите (см.) - таймеры он точно не занимает.
Если используете Ардуино среду, смотрите в исходниках ядра открыт для редактирования один из файлов, там используется прерывание ISR0 уже всё настроено, очень удобно, ничего придумывать не нужно. Это прерывание для системного таймера, частота около 1000Гц. Туда дописываете свою функцию, чтобы раз в секунду вызывать обработку.
Это же прервывание выводит МК из режима сна IDLE, тоже иногда удобно.
Спасибо Вам огромное за ЛекБез :) Я только начинаю осваивать это дивный прибор :)
Оффтоп, но малость позанудствую.)))) Ликбез - термин появился на заре Советской власти, образован из двух начальных слогов от "Ликвидация безграмотности".))))
открыт для редактирования один из файлов, там используется прерывание ISR0 уже всё настроено, очень удобно, ничего придумывать не нужно. Это прерывание для системного таймера, частота около 1000Гц.
Простите, боюсь, что я не понял Вас. Можно поподробнее, что за файл, что за прерывание?
Что-то я совсем заклинил....
Тут все ясно. Присвоение переменной TCCR1A значения 0х40 (6 бит в 1, остальные 0)
Следующую конструкциию ни как не расшифрую
Берем единичку (0ой бит = 1) и сдвигаем ее влево на COM1A0 бит. А чему равно "COM1A0" ? Полученное присвоили переменной TCCR1A (регистр таймера)
Правильно я понимаю, что первый и 2ой варианты делают одно и тоже, но второй вариант занимает меньше кода (не нужно число 0х40, но нужна команда)
Все три записи - абсолютно идентичны. Нет никакой разницы.
Пример тактирования таймера от внешнего источника сигнала нужен?
Пример тактирования таймера от внешнего источника сигнала нужен?
Был бы очень признателен.
И если не затруднит - второй пример как вместо управления пинами (none, toggle, clear, set) инициировать программное прерывание и выполнить указанную процедуру.
Значит так, будем тактировать таймер №1 от внешнего сигнала.
Это всегда делается через пин 5 и ни через какой другой.
Тактироваться будем по нисходящему фронту, т.е. когда пин переходит с HIGH на LOW (по восходящему фронту тоже можно, в программе это закомментировано - см. текст)
Чтобы не не дергать проводочком на землю, я для теста соединил пин 5 с пином 9 через резистор и подаю сигнал на пин 9 (простым digitalWrite), а т.к. он соединён с пином 5, таймер должен этот сигнал посчитать.
Вот текст
Печатает число 4, т.к. мы подаём 4 сигнала (строки 52-55). Размножьте эти строки, скажем до 9 - будет печатать число 9.
Техника работы понятна?
Теперь спокойно конфигурируете таймер, подключаете свой источник сигнала, а секунду считаете хоть через millis, хоть другим таймером - и все дела.
Позапускайте, поменяйте и убедитесь, что пример понятен.
И если не затруднит - второй пример как вместо управления пинами (none, toggle, clear, set) инициировать программное прерывание и выполнить указанную процедуру.
На каком таймере?
Евгений, спасибо за разъяснения.
Изучая регистры таймера я понял, что биты 0,1,2 (CS10, CS11, CS12) регистра TCCR1B отвечают:
- за делитель при внутреннем тактовании от генератора
- за тактование от пина 5. При чем бит 0 (CS10) отвечает за то по фронту или срезу будет происходить инкремент счетчика.
Регистр TCCR1A вы обнуляете совсем. А разве нельзя при этом продолжить дергать пинами 9,10? TCCR1A = 0x50; ?
Вот эту запись не понял TIMSK1 = 0
И если не затрудинит то прерывание тоже на 1ом таймере.
В целом я начал понимать его работу, но не хватает опыта :)
И знаний :)
Регистр TCCR1A вы обнуляете совсем. А разве нельзя при этом продолжить дергать пинами 9,10? TCCR1A = 0x50; ?
Не было такой задачи.
Вот эту запись не понял TIMSK1 = 0
Просто обнулил на всякий случай. Мало ли кто там чего записал, а мне в нём ничего не нужно.
Ну, вот здесь хорошо видно, что функция "ISR(TIMER1_COMPA_vect)" вызывается раз в секунду.
Вообще-то вызывать Serial.print из обработчика прерывания плохая идея, по уму бы там флажок взводить, но для маленького теста - нормально.
Не найду связь с
ISR(TIMER1_COMPA_vect)
Где мы указываем, что при прерывании нужно вызвать именно ISR(TIMER1_COMPA_vect) , а не ISR(ohohohoho)
По таймеру генерить прерывание каждую секунду и вызывать программу сравнения.
не оно?
По таймеру генерить прерывание каждую секунду и вызывать программу сравнения.
не оно?
Ну это не прерывание, а программная обработка. И боюсь вот это
Где гарантия, что на эту строку кода будет приходится millis() кратная 1000?
А если 1001, а в следующий цикл 2002?
Где мы указываем, что при прерывании нужно вызвать именно ISR(TIMER1_COMPA_vect) , а не ISR(ohohohoho)
Это нигде не указывается. Прерывания так устроены, что если какое-то случается, то вызывается именно его обработчик, а не какого-то другого. Мы лишь разрешили этому прерыванию происходить (строка 9)
А если прерывание будет с таймера2? Как тогда быть? Какую процедуру тогда вызывать? Или на любое программное прерывание ( совпадение значений, переполнение счетчика и ты) от всех таймеров будет вызвана именно процедура isr() ?
Одна на всех?
А если прерывание будет с таймера2? Как тогда быть? Какую процедуру тогда вызывать? Или на любое программное прерывание ( совпадение значений, переполнение счетчика и ты) от всех таймеров будет вызвана именно процедура isr() ?
Одна на всех?
Нет. Для каждого таймера есть несколько разных прерываний. Совпадение по 2 каналам (где есть) и переполнение. Соответственно, обрабоччики вызываюца разные
А если прерывание будет с таймера2? Как тогда быть?
У него свой веткор. Я Вам вчера давал ссылку на даташит. Список всех векторов прерываний там на стр. 82. И, кстати, полное описание работы всех таймеров во всех режимах - стр. 125-215.
Да, нашел вот такую таблицу, где указаны различные адреса для разных прерываний.
В Вашем примере ISR(TIMER1_COMPA_vect)
В таблице такого вектора не нашел. Есть только TIMER1_COMPA
Или при компиляции эти записи одинаково укажут на вектор TIMER1_COMPA ?
Я получаю исинное наслаждение от этого сериала, но " пепел Клааса стучит в мое сердце..." и все такое. милосердие ипрочий бред.
В связи с этим вопрос, Вы по аглицки читаете, верно? Вот документация на avr-libc которая и есть основная библиотека компилятора для AVR процессоров, включая нашу любиую Ардуину.
Вот страничка про прерывания.
По ссылке есть таблица векторов, как они называются ИМЕННО В НАШЕМ компиляторе.
А хто не розумиэ английську мову -то есть на русском :)
К сожалению, мой опыт в программировании очень скуден. И Си осваиваю вот прямо в настоящее время. Возможно мои вопросы кажутся наивными, но не для новичка. Есть некоторый опыт в программировании на Codesys, для промышленных ПЛК и то только в качестве хобби.
Так что прошу не судить строго за дилетанские вопросы.
Зы. Ветка вроде как раз для вопросов дилетантов
В Вашем примере ISR(TIMER1_COMPA_vect)
В таблице такого вектора не нашел. Есть только TIMER1_COMPA
"_vect" - это такой волшебгый суффикс, который надо добавлять в конец названиям векторов прерываний из этой таблицы.