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

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

Ну от. Значить, gcc всёже лучше меня оптимизирует :) 

Green
Offline
Зарегистрирован: 01.10.2015

Оптимизация вообще последнее дело. Только в исключительных случаях.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

ну это понятно, сдвинуть на десять разрядов за два такта совсем никак, хотя бы за десять )))

Bruzzer
Offline
Зарегистрирован: 17.03.2020

ua6em пишет:

ну это понятно, сдвинуть на десять разрядов за два такта совсем никак, хотя бы за десять )))

Меня переклинило потому что я предположил - т.к результат uint_8t то  вдруг он просто берет второй байт от long. Ну а то, что в байте не 10 бит, это мой мозг пропустил, в попытке обосновать 2 такта.

NikShel
Offline
Зарегистрирован: 21.01.2018

Уважаемый dimax !

Пытался сделать блинк из Вашего "Терминального" генератора

Немного переделал, т.к. мне очень надо, чтобы ещё была частота: 0 (без блинка).

Это я пытаюсь сделать индикацию режимов.  Задача простая

Частота: 0 - обычный режим;

Частота №1 - режим настройки  №1;

Частота №2 - режим настройки  №2.

Переключение режимов вручную (пока не задано новое значение).

Но пока у меня получилось, что блинк работает с нужной частотой примерно 1 сек. Переменная reqfreq почему-то обнуляется в конце void loop() , хотя я пытался сделать её глобальной переменной.

Подскажите, что не так!

[code]
volatile long reqfreq = 0; //<--N.Sh.
uint32_t ocr = OCR1A; // uint16_t divider = 1;  float freq;  //<--N.Sh.

void setup() {
  Serial.begin(9600);
  pinMode (9, OUTPUT); // выход генератора
  TCCR1A = 0; TCCR1B = 0;
}

void loop() {
  //  static uint32_t reqfreq = 0; //<-- dimax переменная запроса частоты
  uint16_t divider = 1;  float freq; // uint32_t ocr = OCR1A; <-- dimax
  if (Serial.available() > 0) {
    reqfreq = Serial.parseInt();

    if (reqfreq > F_CPU / 2) {
      return;
    }
    if (reqfreq == 0) {
      TCCR1A = 0; TCCR1B = 0;
      Serial.println("0 Hz");
      return;
    }  // <--N.Sh. / dimax--> if (reqfreq==0 || reqfreq>F_CPU/2) {return;}
    ocr = (F_CPU / reqfreq / 2 / divider);
    byte shifts[] = {3, 3, 2, 2};
    for (byte i = 0; i < 4; i++) {
      if (ocr > 65536) {
        divider <<= shifts[i];
        ocr = F_CPU / reqfreq / 2 / divider;
      }
      else {
        TCCR1B = (i + 1) | (1 << WGM12);   //Mode4 (CTC)
        break;
      }
    }
    OCR1A = ocr - 1; TCCR1A = 1 << COM1A0;
    freq = (float) F_CPU / 2 / (OCR1A + 1) / divider;
    if (freq < 10000) {
      Serial.print(freq, 1);
      Serial.println(" Hz ");
    }
    if (freq >= 10000) {
      Serial.print(freq / 1000, 3);
      Serial.println(" kHz");
    }
  }
  // Serial.println(reqfreq);
}

//Скетч использует 4430 байт (14%) памяти устройства. Всего доступно 30720 байт.
//Глобальные переменные используют 222 байт (10%) динамической памяти, оставляя 1826 байт для локальных переменных. Максимум: 2048 байт.
[/code]

 

venus
venus аватар
Offline
Зарегистрирован: 08.10.2019

volatile-то вам зачем? Serial.parseInt() у вас возвращает ноль не только если введена строка "0", но и если мусор считал.

uint32_t ocr = OCR1A; // uint16_t divider = 1;  float freq;  //<--N.Sh.

void setup() {
  Serial.begin(9600);
  pinMode (9, OUTPUT); // выход генератора
  TCCR1A = 0; TCCR1B = 0;
}

void loop() {
  static uint32_t reqfreq = 0; //<-- dimax переменная запроса частоты
  static char in[16];
  uint16_t divider = 1;  float freq; // uint32_t ocr = OCR1A; <-- dimax
  if (Serial.available() > 0 && Serial.readBytes(in, 15) > 0 && sscanf(in, "%lu", &reqfreq) == 1) {
    if (reqfreq > F_CPU / 2) {
      return;
    }
    if (reqfreq == 0) {
      TCCR1A = 0; TCCR1B = 0;
      Serial.println("0 Hz");
      return;
    }  // <--N.Sh. / dimax--> if (reqfreq==0 || reqfreq>F_CPU/2) {return;}
    ocr = (F_CPU / reqfreq / 2 / divider);
    byte shifts[] = {3, 3, 2, 2};
    for (byte i = 0; i < 4; i++) {
      if (ocr > 65536) {
        divider <<= shifts[i];
        ocr = F_CPU / reqfreq / 2 / divider;
      }
      else {
        TCCR1B = (i + 1) | (1 << WGM12);   //Mode4 (CTC)
        break;
      }
    }
    OCR1A = ocr - 1; TCCR1A = 1 << COM1A0;
    freq = (float) F_CPU / 2 / (OCR1A + 1) / divider;
    if (freq < 10000) {
      Serial.print(freq, 1);
      Serial.println(" Hz ");
    }
    if (freq >= 10000) {
      Serial.print(freq / 1000, 3);
      Serial.println(" kHz");
    }
  }
  // Serial.println(reqfreq);
}

//Скетч использует 4430 байт (14%) памяти устройства. Всего доступно 30720 байт.
//Глобальные переменные используют 222 байт (10%) динамической памяти, оставляя 1826 байт для локальных переменных. Максимум: 2048 байт.

 

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

NikShel пишет:

 Переменная reqfreq почему-то обнуляется в конце void loop() , хотя я пытался сделать её глобальной переменной. Подскажите, что не так!

По виду всё так. Наверно из терминалки прилетает ноль. Если в ардуиновском мониторе смотрите, то выберете в настройке "Нет конца строки"

venus
venus аватар
Offline
Зарегистрирован: 08.10.2019

dimax пишет:
Наверно из терминалки прилетает ноль.

там что попало прилетает, функция parseint просто такая. замена на readbytes плюс sscanf решает вопрос. проверил.

NikShel
Offline
Зарегистрирован: 21.01.2018

в настройке "Нет конца строки"

Помогло, спасибо!

NikShel
Offline
Зарегистрирован: 21.01.2018

venus пишет:

dimax пишет:
Наверно из терминалки прилетает ноль.

там что попало прилетает, функция parseint просто такая. замена на readbytes плюс sscanf решает вопрос. проверил.

 readbytes плюс sscanf  - можно по-подробней. В справочниках не нашёл.

venus
venus аватар
Offline
Зарегистрирован: 08.10.2019

NikShel пишет:
readbytes плюс sscanf  - можно по-подробней. В справочниках не нашёл.

я же прицепил исправленный скетч в ответе на ваш вопрос. сравните со своим, там лишь пара строк отличается.

NikShel
Offline
Зарегистрирован: 21.01.2018

venus пишет:

 исправленный скетч в ответе на ваш вопрос. сравните со своим, там лишь пара строк отличается.

Я протестил: работает, спасибо!

NikShel
Offline
Зарегистрирован: 21.01.2018

Ну и вот он мой вариант Блинка:

#define D9_High TCCR1A = B11 << COM1A0; // 11 - установить пин_9 HIGH = off
#define D9_LOW TCCR1A = B10 << COM1A0; // 10 - установить пин_9 LOW = on
#define D9_F_1Hz TCCR1B = 12; OCR1A = 31249; TCCR1A = 1 << COM1A0; //  Frequency: 1Hz; 
#define D9_F_2Hz TCCR1B = 11; OCR1A = 62499;  TCCR1A = 1 << COM1A0; //  Frequency: 2Hz;

void setup() {
  Serial.begin(9600);
  pinMode (9, OUTPUT); // выход генератора
  TCCR1A = B10 << COM1A0;
}

void loop() {

  static uint32_t var = 0; //<-- dimax переменная запроса режима
  static char in[1];

  if (Serial.available() > 0 && Serial.readBytes(in, 1) > 0 && sscanf(in, "%lu", &var) == 1) { //  <-- venus
    Serial.println(var);
    switch (var) {
      case 0:
        D9_High //выполняется, когда var равно 0 - выкл
        break;
      case 1:
        D9_F_1Hz //выполняется, когда var равно 1
        break;
      case 2:
        D9_F_2Hz //выполняется когда  var равно 2
        break;
      default:
        D9_LOW // обычный режим

    }
  }
}

//Скетч использует 3836 байт (12%) памяти устройства. Всего доступно 30720 байт.
//Глобальные переменные используют 197 байт (9%) динамической памяти, оставляя 1851 байт для локальных переменных. Максимум: 2048 байт.

Спасибо, venus 

Спасибо, dimax 

Спасибо, ЕвгенийП !

NikShel
Offline
Зарегистрирован: 21.01.2018

Евгений, в качестве продолжения темы.

А как переделать Ваш калькулятор для рассчёта параметров для Timer2.

Есть желание сделать на одной Нане два Блинка с разными регулируемыми частотами.

Пока на втором Таймере получилась пищалка.

с Наступающим!

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

Сделай всё то же самое, но на одном таймере, я разрешил

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

NikShel пишет:

А как переделать Ваш калькулятор для рассчёта параметров для Timer2.

Ну, калькулятор - игрушка, а делать расчёты для всех таймеров позволяет вот такая библиотека. Прелесть её в том, что она ВСЕ вычисления делает на этапе компиляции и в код попадают уже готовые цифири 0 никаких расчётов во время выполнения. Там на гитхабе есть ссылка на обсуждение и подробности на этом форуме.

Upper
Offline
Зарегистрирован: 23.06.2020

DetSimen пишет:

Сделай всё то же самое, но на одном таймере, я разрешил

На гитхабе в описании к библиотеке написано, что  с вопросами и предложениями лучше обращаться на форум arduino.ru. Если на форуме есть своя тема по этой библиотеке, может имеет смысл на гитхабе дать не нее ссылку?

Сама библиотека вызывает двоякое впечатление. С одной стороны мне было интересно и полезно посмотреть в коде, как на практике используются разные приемы программирования. С другой стороны, "поломка" работы millis (ибо кого волнует точность?) отталкивает от желания использовать на практике.

Еще вопрос. В операторе "--" вы разрешаете прерывания перед вызовом fCallback();.  Насколько это допустимо? Ведь этот оператор используется внутри обработки прерывания ISR(TIMER0_COMPA_vect).

	TCounterDown &operator --(int) {

		uint8_t sreg = SREG;   // на всякий случай запомним состояние прерываний

		cli();				// запрещаем прерывания

		if ((isActive()) && (!isEmpty()) && ((--fWorkCount) == 0)) {  // если счетчик досчитал до 0
			fWorkCount = fInitCount; // перезагружаем рабочий счётчик начальным значением, чтобы считать заново
			sei();				// разрешаем прерывания
			fCallback();		// и вызываем нашу фунцию обратного вызова
		}
		
		SREG = sreg;     // восстанавливаем прерывания
		return *this;
	}

 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

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

NikShel пишет:

А как переделать Ваш калькулятор для рассчёта параметров для Timer2.

Ну, калькулятор - игрушка, а делать расчёты для всех таймеров позволяет вот такая библиотека. Прелесть её в том, что она ВСЕ вычисления делает на этапе компиляции и в код попадают уже готовые цифири 0 никаких расчётов во время выполнения. Там на гитхабе есть ссылка на обсуждение и подробности на этом форуме.

а Важгад не к этой библиотеке добавляли?

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

Upper пишет:
...отталкивает от желания использовать на практике.

Так я ж и не настаиваю. :) 

В последней версии millis() не врёт, используется такая конструкция

OCR0A += TIMER_ONE_MS;

вместо

TCNT0 = 0xFF;

Green
Offline
Зарегистрирован: 01.10.2015

А я вообще сторонник не накрученности. Чем проще, чем понятнее, тем лучше. Или нет?

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

Вапще-то, правила хорошего тона, в низкоуровневом программировании, требуют разрешать прерывания при заходе в обработчик, за исключением случаев, критичных ко времени обработки.  Зачем создавать очередь отложенных прерываний, если оне друг другу не мешают?  Но, тут нужна еще культура написания самого обработчика, чтоб он выполнялся как можно быстрее и не делал ничего лишнего.  Поэтому, в последних версиях я отказался от колбэков, сработавший таймер просто кладёт в очередь сообщение о событии, а оно потом уже штатно обрабатывается диспеччером в loop().  И никто никаво не тормозит. 

Upper
Offline
Зарегистрирован: 23.06.2020

DetSimen пишет:

В последней версии millis() не врёт, используется такая конструкция

OCR0A += TIMER_ONE_MS;

вместо

TCNT0 = 0xFF;

Может мы о разных библиотека говорим?
Я говорю о той, на которую вы дали ссылку
https://github.com/DetSimen/Arduino_TimerList/blob/master/TimerList.cpp
В ней TCNT0 = 0xFF;

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

DetSimen пишет:

  Поэтому, в последних версиях я отказался от колбэков, сработавший таймер просто кладёт в очередь сообщение о событии, а оно потом уже штатно обрабатывается диспеччером в loop().  И никто никаво не тормозит. 

+100500!

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

Upper пишет:

Может мы о разных библиотека говорим?

Я говорю о той, на которую вы дали ссылку
https://github.com/DetSimen/Arduino_TimerList/blob/master/TimerList.cpp
В ней TCNT0 = 0xFF;

На гитхабе старая. Но, для несложных задач падёт, типа 2-х независимых блинков, как нужно было вопрошающему.  Я поправлю, как руки дойдут. :) 

NikShel
Offline
Зарегистрирован: 21.01.2018

а делать расчёты для всех таймеров позволяет вот такая библиотека. Прелесть её в том, что она ВСЕ вычисления делает на этапе компиляции и в код попадают уже готовые цифири 0 никаких расчётов во время выполнения. Там на гитхабе есть ссылка на обсуждение и подробности

ConstTimersComplexTest:

Arduino: 1.8.5 (Windows 10), Board: "Arduino Nano, ATmega328P"
 
C:\Arduino\Gen1-2Hz\ConstTimers\ConstTimers.ino: In function 'void setup()':
 
ConstTimers:33: error: ambiguous overload for 'operator<<' (operand types are 'Print' and 'char')
 
           Serial << "*** Error with \"by period\" calculation: timer/counter:" << timerId << "; period:" << period << "; bits:" << bits << '(' << prescalerValue(timerId, bits) << "); ticks:" << ticks << "; realPeriod:" << realPeriod << "\r\n"; // NikShel edition
 
                                                                                                                                         ^
 
C:\Arduino\Gen1-2Hz\ConstTimers\ConstTimers.ino:33:137: note: candidates are:
 
In file included from C:\Arduino\Gen1-2Hz\ConstTimers\ConstTimers.ino:8:0:
 
C:\Install\arduino-1.8.5-windows\arduino-1.8.5\libraries\ConstTimers/Printing.h:10:17: note: Print& operator<<(Print&, __int24)
 
  inline Print & operator << (Print &s, __int24 n) { s.print((int32_t)n); return s; }
 
                 ^
 
C:\Install\arduino-1.8.5-windows\arduino-1.8.5\libraries\ConstTimers/Printing.h:11:17: note: Print& operator<<(Print&, __uint24)
 
  inline Print & operator << (Print &s, __uint24 n) { s.print((uint32_t)n); return s; }
 
                 ^
 
ConstTimers:45: error: ambiguous overload for 'operator<<' (operand types are 'Print' and 'char')
 
                    << "; bits:" << bitsF << '(' << prescalerValue(timerId, bitsF) << "); ticks:"
 
                                          ^
 
C:\Arduino\Gen1-2Hz\ConstTimers\ConstTimers.ino:45:42: note: candidates are:
 
In file included from C:\Arduino\Gen1-2Hz\ConstTimers\ConstTimers.ino:8:0:
 
C:\Install\arduino-1.8.5-windows\arduino-1.8.5\libraries\ConstTimers/Printing.h:10:17: note: Print& operator<<(Print&, __int24)
 
  inline Print & operator << (Print &s, __int24 n) { s.print((int32_t)n); return s; }
 
                 ^
 
C:\Install\arduino-1.8.5-windows\arduino-1.8.5\libraries\ConstTimers/Printing.h:11:17: note: Print& operator<<(Print&, __uint24)
 
  inline Print & operator << (Print &s, __uint24 n) { s.print((uint32_t)n); return s; }
 
                 ^
 
ConstTimers:60: error: ambiguous overload for 'operator<<' (operand types are 'Print' and 'const char*')
 
          << "\r\n\r\nВремÑ? выполнениÑ?: " << (minutes < 10 ? "0" : "") << minutes << ':' << (seconds < 10 ? "0" : "") << seconds
 
                                                         ^
 
C:\Arduino\Gen1-2Hz\ConstTimers\ConstTimers.ino:60:57: note: candidates are:
 
In file included from C:\Arduino\Gen1-2Hz\ConstTimers\ConstTimers.ino:8:0:
 
C:\Install\arduino-1.8.5-windows\arduino-1.8.5\libraries\ConstTimers/Printing.h:10:17: note: Print& operator<<(Print&, __int24) <near match>
 
  inline Print & operator << (Print &s, __int24 n) { s.print((int32_t)n); return s; }
 
                 ^
 
C:\Install\arduino-1.8.5-windows\arduino-1.8.5\libraries\ConstTimers/Printing.h:10:17: note:   no known conversion for argument 2 from 'const char*' to '__int24'
 
C:\Install\arduino-1.8.5-windows\arduino-1.8.5\libraries\ConstTimers/Printing.h:11:17: note: Print& operator<<(Print&, __uint24) <near match>
 
  inline Print & operator << (Print &s, __uint24 n) { s.print((uint32_t)n); return s; }
 
                 ^
 
C:\Install\arduino-1.8.5-windows\arduino-1.8.5\libraries\ConstTimers/Printing.h:11:17: note:   no known conversion for argument 2 from 'const char*' to '__uint24'
 
exit status 1
ambiguous overload for 'operator<<' (operand types are 'Print' and 'char')
 
This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.
NikShel
Offline
Зарегистрирован: 21.01.2018

ConstTimersPartialTest:

Функционирует. Но, как я понял это для Timer1

Но не все результаты совпадают с выданными калькулятором.

 

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

NikShel пишет:

Функционирует. 

Т.е. с int24 разобрались? Надо,, блин, убрать её оттуда, это Ворота выпендрился.

NikShel пишет:

Но, как я понял это для Timer1 ? 

Блин, ну там же в начале файла библиотеки полная документация, все написано! 

Это для любого таймера, номер таймера задаётся в первом параметре "timerTicksByFrequency(1, ..." и "prescalerBitsByFrequency(1, ..."

NikShel пишет:

Но не все результаты совпадают с выданными калькулятором.

Не обязано.