Задачка для новичков

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

Задача родилась из занятий с ребёнком.

Собственно задача простая. Требуется, чтобы некая функция исполнялась с заданной частотой (для определённости, скажем – 40Гц). Функция может делать что угодно, например, для определённости, пусть инвертирует светодиод на 13-ом пине (получится 20-тигерцный «блинк»).

Работа не должна зависеть от задержек (delay() или долгих вычислений) в основном цикле программы. Т.е. вот такая программа должна нормально работать:

//
//	Светодиод мигает с частотой 20Гц
//
void ledInvert(void) {
	digitalWrite(LED_BUILTIN, ! digitalRead(LED_BUILTIN));
}

void setup(void) {
//  здесь что-то делаем
}

void loop(void) {
  while(true);
}

При этом нельзя пользоваться сторонними библиотеками (типа TimerOne и т.п.) и нельзя напрямую работать с регистрами МК (например, нельзя вручную конфигурировать таймеры). Всё должно быть в рамках wiring, т.е. средствами, описанными в общем руководстве.

Попробуйте – проверьте свои знания! Удачи!

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

Ах, ну, да, основной цикл (loop()) должен выполняться, не надо писать обычный блинк с делэями в цикле в setup - это читерство. 

По "общим руководством" подразумевается вот эта страница - http://arduino.ru/Reference

sadman41
Offline
Зарегистрирован: 19.10.2016

Просто уточняю из любопытства: допускается ли стреноживание ардуины?

PRC
Offline
Зарегистрирован: 03.02.2019
#define pinLed  13
#define pinINT0 2
#define Freq    40
void ledInvert(void)
{
  digitalWrite(pinLed, ! digitalRead(pinLed));
}
void setup() {
  pinMode(pinLed,OUTPUT);
  attachInterrupt(0, ledInvert, RISING );
  tone(pinINT0,Freq);
}

void loop() {
  while(1);
}

И все?))

 

 

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

PRC пишет:

И все?))

Ну, можно и так :)

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

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

Ну, можно и так :)

то есть это не было тем решением, которое вы держали в уме, задавая вопрос? :)

junior_developer
Offline
Зарегистрирован: 27.11.2017

С помощью millis? Как то так?
 

	 void ledInvert(unsigned long interval) {
		 static unsigned long prevTime=0;
		 if(millis()-prevTime>interval){
    prevTime=millis();
     digitalWrite(LED_BUILTIN, ! digitalRead(LED_BUILTIN));
		 }
    }

и при вызове можно задавать длительность!  То есть например для 40 герц нужно прописать там 25? Верно?

ledInvert(25);
ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

junior_developer пишет:

и при вызове можно задавать длительность!  То есть например для 40 герц нужно прописать там 25? Верно?

Так Вы целиком напишите. Можно прямо из моего примера. Покажите, где и как тут её вызывать?

//
//	Светодиод мигает с частотой 20Гц
//
void ledInvert(void) {
	digitalWrite(LED_BUILTIN, ! digitalRead(LED_BUILTIN));
}

void setup(void) {
//  здесь что-то делаем
}

void loop(void) {
  while(true);
}
ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

b707 пишет:

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

Ну, можно и так :)

то есть это не было тем решением, которое вы держали в уме, задавая вопрос? :)

Мой ХР подсказывает, что под решением подразумевалось сцепить с меандром на таймере )))

junior_developer
Offline
Зарегистрирован: 27.11.2017

Вот мой вариант скетча
 

#define LED_interval 25UL

void ledInvert(void) {
  digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
}
  
void Do_ledInvert(unsigned long interval) {
     static unsigned long prevTime=0;
     if(millis()-prevTime>interval){
    prevTime=millis();
         ledInvert();
     }
    }


void setup(void) {
//  здесь что-то делаем
}

void loop(void) {
  // while(true);
  Do_ledInvert(LED_interval);
}

Оцените пожалуйста!

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

junior_developer пишет:

Вот мой вариант скетча

Так не выполнено условие

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

Работа не должна зависеть от задержек (delay() или долгих вычислений) в основном цикле программы. 

Стоит поставить в loop какой-нибудь делэй и всё поломается.

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

Евгений Петрович! А на чём еще, кроме tone() можно паразитировать? (средствами wiring)

PRC
Offline
Зарегистрирован: 03.02.2019

AnalogWrite еще годится, но хуже.

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

PRC пишет:

AnalogWrite еще годится, но хуже.

так там частота вроде 50 герц - фиксированная

PRC
Offline
Зарегистрирован: 03.02.2019

490Гц.

Так делитель использовать никто не запрещал. Нельзя задержки. Вот тоже самое на нем. Из-за не удобной частоты шима частота будем примерно равно заданой.

#define pinLed  13
#define pinINT0 2
#define Freq    40
void ledInvert(void)
{
  static int i=490/Freq;
  if(!i--)
  {
    digitalWrite(pinLed, ! digitalRead(pinLed));
    i=490/Freq;
  }
}
void setup() {
  pinMode(pinLed,OUTPUT);
  attachInterrupt(0, ledInvert, RISING );
  analogWrite(pinINT0,0x80);
}

void loop() {
  while(1);
}

 

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

6 и 7 строки не понимаю, как работает этот делитель?

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

ua6em пишет:

6 и 7 строки не понимаю, как работает этот делитель?

А чего непнятного то?

Коэффициент деления задается в строке 6, а в стороке 7 происходит декремент и на каждом обнулении - реакция + восстановление значения счетчика.

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

andriano пишет:

ua6em пишет:

6 и 7 строки не понимаю, как работает этот делитель?

А чего непнятного то?

Коэффициент деления задается в строке 6, а в стороке 7 происходит декремент и на каждом обнулении - реакция + восстановление значения счетчика.

почитал про static, теперь понятно, красиво, компактно, но не работает, ашипки, а вот так будет )))
 

#define pinLed  13
#define pinINT1 3
#define Freq    40
void ledInvert(void)
{
  static int i=490/Freq;
  if(!i--)
  {
    digitalWrite(pinLed, ! digitalRead(pinLed));
    i=490/Freq;
  }
}
void setup() {
  pinMode(pinLed,OUTPUT);
  attachInterrupt(1, ledInvert, RISING );
  analogWrite(pinINT1,0x80);
}

void loop() {
  while(1);
}

 

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

PRC пишет:
Вот тоже самое на нем. 

#define pinLed  13
#define pinINT0 2
#define Freq    40
void ledInvert(void)
{
  static int i=490/Freq;
  if(!i--)
  {
    digitalWrite(pinLed, ! digitalRead(pinLed));
    i=490/Freq;
  }
}
void setup() {
  pinMode(pinLed,OUTPUT);
  attachInterrupt(0, ledInvert, RISING );
  analogWrite(pinINT0,0x80);
}

void loop() {
  while(1);
}

Сами-то проверяли? Неужели работает? Боюсь, что нет :)

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

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

PRC пишет:
Вот тоже самое на нем. 

#define pinLed  13
#define pinINT0 2
#define Freq    40
void ledInvert(void)
{
  static int i=490/Freq;
  if(!i--)
  {
    digitalWrite(pinLed, ! digitalRead(pinLed));
    i=490/Freq;
  }
}
void setup() {
  pinMode(pinLed,OUTPUT);
  attachInterrupt(0, ledInvert, RISING );
  analogWrite(pinINT0,0x80);
}

void loop() {
  while(1);
}

Сами-то проверяли? Неужели работает? Боюсь, что нет :)

Мастерство не пропьёшь )))
ЗЫ(я об умении беглым взглядом находить ошибки)

PRC
Offline
Зарегистрирован: 03.02.2019

Так по памяти правил)) Как уже правильно показали на  INT1 работать будет.

junior_developer
Offline
Зарегистрирован: 27.11.2017

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

junior_developer пишет:

Вот мой вариант скетча

Так не выполнено условие

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

Работа не должна зависеть от задержек (delay() или долгих вычислений) в основном цикле программы. 

Стоит поставить в loop какой-нибудь делэй и всё поломается.

Странно. Я не думал, что delay как то привязан к millis! Или функция будет тоже занимать всё время на проверку IF условия? А что если написать так
 

void Do_ledInvert(unsigned long interval) {
     static unsigned long prevTime=0;
     if(millis()-prevTime>interval){
    prevTime=millis();
         ledInvert();
     }
	 else return;
    }

Если интервал не прошел, то происходит мгновенный выход из функции. Верно?
И ещё я не совсем понял несколько строчек в скетче
 

#define pinLed  13
#define pinINT1 3
#define Freq    40
void ledInvert(void)
{
  static int i=490/Freq;
  if(!i--)
  {
    digitalWrite(pinLed, ! digitalRead(pinLed));
    i=490/Freq;
  }
}
void setup() {
  pinMode(pinLed,OUTPUT);
  attachInterrupt(1, ledInvert, RISING );
  analogWrite(pinINT1,0x80);
}

void loop() {
  while(1);
}

Вот эту функцию
 

void ledInvert(void)
{
  static int i=490/Freq;
  if(!i--)
  {
    digitalWrite(pinLed, ! digitalRead(pinLed));
    i=490/Freq;
  }
}

Со строкой  if(!i--)
Как это по русски? Если переменная i не равна??? Знак "!" значит не равно? То есть, если бы значка не было, там могло бы быть что угодно кроме нуля и условие бы выполнялось! Также как while(1). А так получается, что должно быть наоборот, то есть для срабатывания условия там должен быть 0 (ноль)? Верно? То есть можно написать эту строку так:
 

i--;  //  уменьшение на 1
 if(i=0) {  // если ноль
...  
.} 

Я прав?
490 примерная частота шим по умолчанию. Она делиться на 40. Получается примерно 12 . Остаток ведь отбрасывается. А в переменную типа int попадает число 12. 
Получается, что каждый раз оно всё уменьшается и уменьшается на 1 за каждым проходом цикла. И вот когда станет 0 (ноль) состояние светодиода изменяется
 

digitalWrite(pinLed, ! digitalRead(pinLed));

а в переменную i опять попадает 12. Верно?
Вызов функции происходит по прерыванию 1

attachInterrupt(1, ledInvert, RISING );

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

analogWrite(pinINT1,0x80);

стоит число 0x80 или 128. Это значит, что скважность шим будет примерно 50%? Верно?
Функция ledInvert будет вызываться примерно 490 раз в секунду и проверять сработало ли условие, это что-то типа собственного таймера?

 

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

Скорее всего эти конструкции равнозначны, так как при компиляции длина кода одинаковая

   i--;
   if(i==0)

 // if(!i--)

 

sadman41
Offline
Зарегистрирован: 19.10.2016

ua6em пишет:

Скорее всего эти конструкции равнозначны, так как при компиляции длина кода одинаковая

   i--;
   if(i==0)

 // if(!i--)

Насколько я помню - не то, что неравнозначны, но и противоположны.

PRC
Offline
Зарегистрирован: 03.02.2019

Идентичны. Сначала i уменьшается на 1, а затем проверяется на равенство 0.

Beginer123
Offline
Зарегистрирован: 23.11.2018

Если мне не изменяет память, то у Архата помнится такое решалось примерно так (проверять сейчас не на чем):

#define everyMillis(interval, action) \
{                                     \
  static unsigned long t = 0UL;       \
  if( millis() - t > (interval) )     \
  {                                   \
    t = millis();                     \
    { action }                        \
  }                                   \
}
//
//	Светодиод мигает с частотой 20Гц
//
void ledInvert(void) {
	digitalWrite(LED_BUILTIN, ! digitalRead(LED_BUILTIN));
}

void setup(void) {
//  здесь что-то делаем
}

void loop(void) {
  everyMillis(25UL,{ ledInvert(); });
}

А вообще-то у него была полноценная библиотека для конечных автоматов, которой достаточно долго пользовался с успехом.

sadman41
Offline
Зарегистрирован: 19.10.2016

PRC пишет:

Идентичны. Сначала i уменьшается на 1, а затем проверяется на равенство 0.

Тогда что делает --i ?

sadman41
Offline
Зарегистрирован: 19.10.2016

Beginer123 пишет:

Если мне не изменяет память, то у Архата помнится такое решалось примерно так (проверять сейчас не на чем):

 

Это не будет работать в коде из условия задачи.

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

sadman41 пишет:

PRC пишет:

Идентичны. Сначала i уменьшается на 1, а затем проверяется на равенство 0.

Тогда что делает --i ?

Это то, о чём я подумал (предусловие/постусловие?)

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

ua6em пишет:

sadman41 пишет:

PRC пишет:

Идентичны. Сначала i уменьшается на 1, а затем проверяется на равенство 0.

Тогда что делает --i ?

Это то, о чём я подумал (предусловие/постусловие?)

условие не бувает ни пред ни пост.  декремент бывает

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

DetSimen пишет:

ua6em пишет:

sadman41 пишет:

PRC пишет:

Идентичны. Сначала i уменьшается на 1, а затем проверяется на равенство 0.

Тогда что делает --i ?

Это то, о чём я подумал (предусловие/постусловие?)

условие не бувает ни пред ни пост.  декремент бывает

это я так выразился ))) то-есть если --i  сначала декремент, затем проверка, а если i-- то сначала проверка на условие затем декремент?

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

ua6em пишет:

это я так выразился ))) то-есть если --i  сначала декремент, затем проверка, а если i-- то сначала проверка на условие затем декремент?

Да

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

DetSimen пишет:

ua6em пишет:

это я так выразился ))) то-есть если --i  сначала декремент, затем проверка, а если i-- то сначала проверка на условие затем декремент?

Да

а как это проверить?

Сам догадался -
4  3
3  2
2  1
1  0
0 -1 490
490  489
489  488
 

#define pinLed  13
#define pinINT1 3
#define Freq    1
void ledInvert(void)
{
  static int i= 490/Freq; //490/Freq;10000;
  // i--;
  // if(i==0)
  Serial.print(i);
  Serial.print(" ");
  if(!i--)
  {
    digitalWrite(pinLed, ! digitalRead(pinLed));
    Serial.print(i);
    i=490/Freq; //490/Freq; 10000; 
    
  }
   Serial.print(" ");
   Serial.println(i);
}

void setup() {
  Serial.begin(115200);
  pinMode(pinLed,OUTPUT);
  attachInterrupt(1, ledInvert, RISING );
  analogWrite(pinINT1,0x80);
}

void loop() {
  while(1);
}

 

Beginer123
Offline
Зарегистрирован: 23.11.2018

sadman41 пишет:

Beginer123 пишет:

Если мне не изменяет память, то у Архата помнится такое решалось примерно так (проверять сейчас не на чем):

 

Это не будет работать в коде из условия задачи.

поясните "почему не будет работать"?

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

Beginer123 пишет:

sadman41 пишет:

Beginer123 пишет:

Если мне не изменяет память, то у Архата помнится такое решалось примерно так (проверять сейчас не на чем):

 

Это не будет работать в коде из условия задачи.

поясните "почему не будет работать"?

Вставьте после вызова вашей функции любой блокирующий  while() или тот же delay(1000) и на этом 20 герц закончатся

Beginer123
Offline
Зарегистрирован: 23.11.2018

Ну .. так не интересно .. таймеры не трогать (виринг их конфигурять не умеет) .. ну, как тут уже предложено - повесьте на прерывание, а вход прерывания подключите к выходу PWM, канал которого "по умолчанию" настроен на 490гц и считайте в прерывании сколько натикало. Да, не забудьте analogWrite() в сетап со скважностью 50%, а то считать придется каждый раз по-своему..

Одно нипонятно: ЗАЧЕМ отжирать целое прерывание и аж ДВЕ ноги микроконтроллера плюсом ШИМ-канал для такой ерунды? Чесать левой пяткой правое ухо разве что.. ;)

.. и ещё нипанятно: а если надо не 20Гц а 1кГц .. что делать? :)

sadman41
Offline
Зарегистрирован: 19.10.2016

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

Beginer123
Offline
Зарегистрирован: 23.11.2018

Задачка на сообразительность - тут прямо в соседнем топике: каким способом результат вычитания двух беззнаковых целых внезапно становится знаковым. :)

sadman41
Offline
Зарегистрирован: 19.10.2016

А о том, ка в одном байте может хранится как uint8_t, так и int8_t, не размышляли?

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

Beginer123 пишет:

Задачка на сообразительность - тут прямо в соседнем топике: каким способом результат вычитания двух беззнаковых целых внезапно становится знаковым. :)

А если оно отрицательным стало после вычитания, что с ним делать-то, с беззнаковым?

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

Beginer123 пишет:

.. и ещё нипанятно: а если надо не 20Гц а 1кГц .. что делать? :)

Применить первую конструкцию с tone() )))

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

Beginer123 пишет:

Задачка на сообразительность - тут прямо в соседнем топике: каким способом результат вычитания двух беззнаковых целых внезапно становится знаковым. :)

оно беззнаковое пока вы перед ним минус не поставили )))

Beginer123
Offline
Зарегистрирован: 23.11.2018

bwn пишет:

Beginer123 пишет:

Задачка на сообразительность - тут прямо в соседнем топике: каким способом результат вычитания двух беззнаковых целых внезапно становится знаковым. :)

А если оно отрицательным стало после вычитания, что с ним делать-то, с беззнаковым?

А беззнаковое разве может стать "отрицательным"? :)

ua6em пишет:

Beginer123 пишет:

Задачка на сообразительность - тут прямо в соседнем топике: каким способом результат вычитания двух беззнаковых целых внезапно становится знаковым. :)

оно беззнаковое пока вы перед ним минус не поставили )))

Так уже интересней. То есть операции "вычитание" в языке нет и вовсе, несмотря на то что пишут в стандартах, а есть сложение с унарным минусом .. так штоли? :)

Вообще заинтриговали. Что по-вашему будет сложено в таком случае: unsigned long a = - 1UL;

sadman41
Offline
Зарегистрирован: 19.10.2016

Для человека это вычитание, для машины сложение. И никакого парадокса.

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

а это называется область входных значений аргумента, их надо обрабатывать до функции )))

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

Beginer123 пишет:

Задачка на сообразительность - тут прямо в соседнем топике: каким способом результат вычитания двух беззнаковых целых внезапно становится знаковым. :)

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

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

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

Beginer123 пишет:

Задачка на сообразительность - тут прямо в соседнем топике: каким способом результат вычитания двух беззнаковых целых внезапно становится знаковым. :)

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

У всех есть ошибки, КЛАПА тоже вон сколько срача развёл, когда ему указал на логическую  ошибку в его часиках на миллис )))

PRC
Offline
Зарегистрирован: 03.02.2019

Человеку надо почитать представление чисел в процессоре и вопросы пропадут. Заодно станет понятно в какую тыкву превратится char c=127 после прибавления к нему единицы.

  char c=127;
  Serial.println(c,DEC);
  c++;
  Serial.println(c,DEC);

И как лабораторная работа - Запустить этот код и вдумчиво изучить резальтаты)))

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

ua6em пишет:

У всех есть ошибки

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

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

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

ua6em пишет:

У всех есть ошибки

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

компилятор точно не может быть кривым, уже бы давно вылезло и поправили, тем паче GCC )))

junior_developer
Offline
Зарегистрирован: 27.11.2017

sadman41 пишет:

ua6em пишет:

Скорее всего эти конструкции равнозначны, так как при компиляции длина кода одинаковая

   i--;
   if(i==0)

 // if(!i--)

Насколько я помню - не то, что неравнозначны, но и противоположны.

а как тогда правильно? Вот так
 

void ledInvert(void)
{
  static int i=490/Freq;
  if(i==0)
  {
    digitalWrite(pinLed, ! digitalRead(pinLed));
    i=490/Freq;
  }
  else i--;
}

или так
 

void ledInvert(void)
{
  static int i=490/Freq;
  if(i==0)
  {
    digitalWrite(pinLed, ! digitalRead(pinLed));
    i=490/Freq;
  }
    i--;
}

То есть нужно ли там else или не обязательно?