Зачем в этом коде "!!"?

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

Не могу понять, зачем в этой строке стоит два значка "!". Насколько мне известно,  в программировании так обозначается символ логического отрицания (то есть не равно)!

 

digitalWrite(dataPin, !!(val & (1 << i)));

Однако если символов два, то в итоге получится то же самое разве нет?

Вот полный код, из которого я взял эту строку
 

  /* wiring_shift.c - shiftOut() function
  Part of Arduino - http://www.arduino.cc/

  Copyright (c) 2005-2006 David A. Mellis

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General
  Public License along with this library; if not, write to the
  Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  Boston, MA  02111-1307  USA

  $Id: wiring.c 248 2007-02-03 15:36:30Z mellis $
*/

#include "wiring_private.h"

uint8_t shiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder) {
	uint8_t value = 0;
	uint8_t i;

	for (i = 0; i < 8; ++i) {
		digitalWrite(clockPin, HIGH);
		if (bitOrder == LSBFIRST)
			value |= digitalRead(dataPin) << i;
		else
			value |= digitalRead(dataPin) << (7 - i);
		digitalWrite(clockPin, LOW);
	}
	return value;
}

void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val)
{
	uint8_t i;

	for (i = 0; i < 8; i++)  {
		if (bitOrder == LSBFIRST)
			digitalWrite(dataPin, !!(val & (1 << i)));
		else	
			digitalWrite(dataPin, !!(val & (1 << (7 - i))));
			
		digitalWrite(clockPin, HIGH);
		digitalWrite(clockPin, LOW);		
	}
}

Кто разбирается, подскажите пожалуйста, зачем эти символы? Что делает эта строка? Заранее спасибо!

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

Было: "ноль" или "не ноль".

После !! стало: "ноль" или "единица".

Pyotr
Offline
Зарегистрирован: 12.03.2014

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

Было: "ноль" или "не ноль".

После !! стало: "ноль" или "единица".

Чёт моя "думалка" зависла... не "ноль" -это и есть логическая "единица"== истина.

Или есть иной смысл в "!!" ?

Функция   digitalWrite(uint8_t pin, uint8_t val)

void digitalWrite(uint8_t pin, uint8_t val)
{
	uint8_t timer = digitalPinToTimer(pin);
	uint8_t bit = digitalPinToBitMask(pin);
	uint8_t port = digitalPinToPort(pin);
	volatile uint8_t *out;

	if (port == NOT_A_PIN) return;

	// If the pin that support PWM output, we need to turn it off
	// before doing a digital write.
	if (timer != NOT_ON_TIMER) turnOffPWM(timer);

	out = portOutputRegister(port);

	uint8_t oldSREG = SREG;
	cli();

	if (val == LOW) {
		*out &= ~bit;
	} else {
		*out |= bit;
	}

	SREG = oldSREG;
}

 

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

Pyotr пишет:

Чёт моя "думалка" зависла... не "ноль" -это и есть логическая "единица"== истина.

Это у Вас "от многих знаний, много печали".

"Не ноль", это, например, 10 или 33.

int a = 10;
int b = !! a; // b == 1

Т.е. !! используется тогда, когда нужно гарантированно получить 0 или 1, а не просто 0 или "не 0".

Pyotr
Offline
Зарегистрирован: 12.03.2014

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

"Не ноль", это, например, 10 или 33.

int a = 10;
int b = !! a; // b == 1

Т.е. !! используется тогда, когда нужно гарантированно получить 0 или 1, а не просто 0 или "не 0".

Евгений, это мне понятно, я имел ввиду конкретный код:

void digitalWrite(uint8_t pin, uint8_t val)
{
	.....
	if (val == LOW) {
		*out &= ~bit;
	} else {
		*out |= bit;
	}
	
}

Обязательно ли в этом случае второй аргумент, передаваемый в функцию  приводить к значению "ноль" или "единица"? Может ли без этого как-то нарушиться условие?

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

Нет, конечно, здесь ведь идёт правильная проверка "0 - не 0". СТрока 4 - прямое сравнение с 0, а строка 7 выполняется при любом "не нуле".

Pyotr
Offline
Зарегистрирован: 12.03.2014

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

Нет, конечно, здесь ведь идёт правильная проверка "0 - не 0". СТрока 4 - прямое сравнение с 0, а строка 7 выполняется при любом "не нуле".

Спасибо. Значит в строке, о которой спрашивал ТС, есть лишнее-без чего можно обойтись.

 digitalWrite(dataPin, !!(val & (1 << i)));  //  "!!" лишнее

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

Совершенно верно

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

Вполне возможно, что это архитектурное наследие (digitalWrite() было написано иначе) или же защита от багов при возможном последующем изменении digitalWrite(). Нужно итальянцам написать для выяснения.

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

Помогите пожалуйста разобраться, что делает этот кусочек кода!
 

if (val == LOW) {
		*out &= ~bit;
	} else {
		*out |= bit;
	}

Я только понял, что это проверка условия через оператор if! Если переменная val равна LOW, то выполняется эта строка
 

*out &= ~bit;

а иначе другая

*out |= bit;

Здесь куча каких-то странных символов? Помогите пожалуйста их расшифровать? Заранее спасибо!
 

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

Страуструп тебе может помочь более чем полностью

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

junior_developer,

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

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

А давайте ему расшифруем, че там :)

В строке 2 в ячкйке памяти находящейся по адресу out, остаются все биты яыляющимися активными в инверсном отображении переменной bit, а остальные сбрасываются.

В строке 4 в ячкйке памяти находящейся по адресу out, устанавливаются биты являющиеся активными в переменной bit.

Стало понятно ?

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

brokly, большое Вам спасибо, что не поленились прокомментировать каждую строчку так детально! Я погуглил по всем непонятным мне словам Вашего ответа, и в результате я понял!

Встретился мне вот сегодня ещё непонятный фрагмент. По идее он выполняет (точнее должен выполнять) чтение из памяти 4 байтного числа!

  *((uint8_t*)&counter + 0) = EEPROM.read(0);
  *((uint8_t*)&counter + 1) = EEPROM.read(1);
  *((uint8_t*)&counter + 2) = EEPROM.read(2);
  *((uint8_t*)&counter + 3) = EEPROM.read(3);

  Я вижу здесь кучу символов "*". Это, скорее всего, означает, что с переменной работают через указатель!
Мне только непонятно, почему в каждой строке два символа * и всего один &.
Ведь они выполняют противоволожное действие! & берет адрес, а * возвращает по этому адресу значение?
то есть можно было бы написать

  *((uint8_t)&counter + 0) = EEPROM.read(0);

  а написано

  *((uint8_t*)&counter + 0) = EEPROM.read(0);

  Почему там (uint8_t*)? Зачем этот символ?

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

junior_develope, не будьте таким принципиальным в получении знаний.Поступите как нормальные люди возьмите и изучите учебник.

Правильное прочтение этого выражения *((uint8_t*)&counter + 0) = EEPROM.read(0);

&counter // получить адрес переменной counter
(uint8_t*)&counter // привести этот адресу к указателю типа byte
(uint8_t*)&counter+0// дать смещение в 1 байт (это к тому , для чего надо приводить к этому указателю)
*((uint8_t*)&counter + 0) - переменной (байтовой разумеется) присвоить значение
EEPROM.read(0);

ПС: На меня посмотрят, как на идиота если я спрошу: почему вы написали так много о

Мне только непонятно, почему в каждой строке два символа * и всего один &.

И наверно будут правы, потому что буквы о одинаковы, но в каждом случае у них разные роли. Так и со звездочками *

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

легко меняется на 

EEPROM.get(0,counter);

https://www.arduino.cc/en/Reference/EEPROMGet

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

qwone, большое Вам спасибо, что объяснили так детально! Я всё понял, кроме этой строчки:

qwone пишет:

(uint8_t*)&counter // привести этот адресу к указателю типа byte

Не могли бы Вы подробнее написать, что это означает? Создать указатель типа byte на адрес  переменной counter? Я Вас правильно понял?

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016
int A;// это привычная переменная и занимает 2 байта
int *pA;// это указатель на переменую int которая занимает 2 байта
*pA=A;// это разместить переменную (2 байта) значение переменной A(два байта)
*(pA+1)=A;// это по адресу за теми 2 байтами на след 2 байта разместить переменную
// теперь возмем 
byte B;// это переменная и занимает 1 байт
byte *pB;// это указатель на переменую byte которая занимает 1 байта
*pB=B;// это разместить переменную (1 байт) значение переменной B(1 байт)
*(pB+1)=B;// это по адресу за тем 1 байтом на след 1 байт разместить переменную.
// теперь главный момент как сделать из pA сделать pB. Привести
pB=(byte *)pA;// это присвоение указателя разных типов
*((byte *)pA)=B;// это занесение переменной по адресу если типы не совпадают
*((byte *)pA+1)=B;// это занесение на след байт
//НО
*((byte *)(pA+1))=B;// это через байт тк int *pA , а int это 2 байта

 

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

quone, большое спасибо! Я наконец-то понял!

Хотя появился ещё вопрос: как правильно объявить переменную, которая будет храниться в этой памяти!
Где её инициализировать? Перед setup?
В setup или loop?
Дело в том, что если в ячейках памяти еще ничего не записано, то вообще непонятно, как будет работать программа?
Получается нужно присвоить сначала значение, а потом сразу же заменить значение переменной на другое (считанное из EEPROM)? То есть написать так:

  int ledDelay = 1000;
  EEPROM.get(0,ledDelay);

 
Вот код:
 

#include <EEPROM.h>
int ledPin = 13;   // светодиод подключен на порт 13
int delayTime;     // задержка
int Button1 = 2;
int Button2 = 4;
int Button3 = 7;

void setup()
{
  pinMode(ledPin, OUTPUT);
  int ledDelay = 1000;
  EEPROM.get(0,ledDelay);
}
void loop()
{
 /*
 обработчик нажатия кнопок
 */
 if(button1) delayTime +=10;  // увеличить время задержки
 if(button2) delayTime -=10; // уменьшить
 if(button3) {EEPROM.put(0,ledDelay);} // если нажата,  записать значение в EEPROM

  digitalWrite(ledPin, HIGH);  
  delay(delayTime);                  
  digitalWrite(ledPin, LOW);   
  delay(delayTime);
}

Посмотрите пожалуйста, правильно ли я написал?

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Там все не верно. Похоже бессыслено вас учить. У вас банально нет базовых знаний. А без них только .... Врач сказал в морг, значит в морг.

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

В коде нету обработчика нажатия кнопок! Вместо него я написал:

 /*
 обработчик нажатия кнопок
*/

Я не добавлял ещё кучу кода, чтобы пример был легко читаем!

В итоге обработка нажатий по "флагам". Если установлен button1, то увеличить задержку, button2 -  уменьшить, а button3

if(button3) {EEPROM.put(0,ledDelay);} // если нажата,  записать значение в EEPROM

Это неправильно?

kalapanga
Offline
Зарегистрирован: 23.10.2016

У Вас почему-то две различные переменные ledDelay и delayTime. Ну пусть две, раз так хочется, но тогда наверное одну другой присваивать где-то надо. По поводу строки 11. Понятно что объявить переменную нужно до использования, присваивать значение не обязательно. В каких-то случаях может понадобиться проверка после чтения значения - не ерунду ли какую считали. Ну и записывать можно не каждый раз, а только если значение изменилось, чего ячейку зря напрягать. Да и чего спрашиваете правильно/неправильно написано - залили и проверили!

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

Да, Вы правы я допустил ошибку, неправильно указав имя переменной! И по поводу проверки значения перед записью тоже правы! Большое спасибо Вам за подсказку!
Код я поправил! И отредактировал строку записи в память. Теперь она выглядит так

if((button3) && delayTime!=delayTime) {EEPROM.put(0,delayTime);} // если нажата и значение не равно предыдущему,  записать значение в EEPROM

Код целиком:

#include <EEPROM.h>
int delayTime;     // задержка
int Button1 = 2;
int Button2 = 4;
int Button3 = 7;

void setup()
{
  pinMode(ledPin, OUTPUT);
  int delayTime= 1000;
  EEPROM.get(0,delayTime);
}
void loop()
{
 /*
 обработчик нажатия кнопок
 */
 if(button1) delayTime +=10;  // увеличить время задержки
 if(button2) delayTime -=10; // уменьшить
if((button3) && delayTime!=delayTime) {EEPROM.put(0,delayTime);} // если нажата и значение отличается,  записать новое значение в EEPROM

  digitalWrite(ledPin, HIGH);  
  delay(delayTime);                  
  digitalWrite(ledPin, LOW);   
  delay(delayTime);
}
qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

junior_developer пишет:
Это неправильно?
Разумеется

//**/
unsigned long mill;// переменная для millis()
typedef void (*pDo)() ;// тип -функция обработчик
//------Cl_Led----------------------
// класс светодиод
class Cl_Led {
  protected:
    const byte pin;
    bool led;
    unsigned long past, time;
    byte state; //0 выкл/ 1 вкл / 2 мигать
    /*установить в состояние*/
    void stand(byte state_) {
      past = mill;
      state = state_;
      switch (state) {
        case 0: // выкл
          digitalWrite(pin, led = LOW);
          break;
        case 1: // вкл
          digitalWrite(pin, led = HIGH);
          break;
        case 2:// мигать
          digitalWrite(pin, led = !led);
          break;
        case 3:// короткое выключение
          digitalWrite(pin, led = LOW);
          break;
        case 4:// короткое включение
          digitalWrite(pin, led = HIGH);
          break;
      }
    }
  public:
    /*конструктор*/
    Cl_Led(byte pin_): pin(pin_) {}
    /*инициализация-вставить в setup()*/
    void init() {
      pinMode(pin, OUTPUT);
      OFF();
    }
    /*работа-вставить в loop()*/
    void run() {
      if (state == 2 && mill - past >= time)stand(2);
      if (state == 3 && mill - past >= time)stand(1);
      if (state == 4 && mill - past >= time)stand(0);
    }
    /*включить*/
    void ON() {
      stand(1);
    }
    /*коротко включить*/
    void ON(unsigned long time_) {
      time = time_;
      stand(4);
    }
    /*выключить*/
    void OFF() {
      stand(0);
    }
    /*коротко выключить*/
    void OFF(unsigned long time_) {
      time = time_;
      stand(3);
    }
    /*мигать*/
    void blink(unsigned long time_ = 200) {
      time = time_;
      stand(2);
    }
};
//------Cl_Btn----------------------
// класс кнопка
class Cl_Btn {
  protected:
    const byte pin;
    pDo Do;//обработчик
    bool bounce = 0;
    bool btn = 1, oldBtn;
    unsigned long past;
  public:
    /*конструктор*/
    Cl_Btn(byte pin_, pDo Do_): pin(pin_), Do(Do_) {}
    /*инициализация-вставить в setup()*/
    void init() {
      pinMode(pin, INPUT_PULLUP);
    }
    /*работа-вставить в loop()*/
    void run() {
      bool newBtn = digitalRead(pin);
      if (!bounce && newBtn != btn) {
        bounce = 1;
        past = mill;
      }
      if (bounce && mill - past >= 10) {
        bounce = 0 ;
        oldBtn = btn;
        btn = newBtn;
        if (!btn && oldBtn) Do();
      }
    }
};
//-----компоновка----------------------
#include <EEPROM.h>
unsigned int  address = 0;
Cl_Led Led(/*пин*/13);
unsigned int ledDelay = 1000;

void DoBtn1() { //+
  if (ledDelay <= 32000) ledDelay += 10;
  Led.blink(ledDelay);
}
void DoBtn2() {
  if (ledDelay >= 200) ledDelay -= 10;
  Led.blink(ledDelay);
}
void DoBtn3() {
  EEPROM.update(address, ledDelay);
}
Cl_Btn Btn1(/*пин*/2,/*обработчик*/DoBtn1);
Cl_Btn Btn2(/*пин*/3,/*обработчик*/DoBtn2);
Cl_Btn Btn3(/*пин*/7,/*обработчик*/DoBtn3);
//-----main----------------------
void setup() {
  Led.init();
  if (EEPROM.read(address) <= 200) {
    EEPROM.update(address, ledDelay);
  }
  else {
    ledDelay = EEPROM.read(address);
  }
  Btn1.init();
  Btn2.init();
  Btn3.init();
}
void loop() {
  mill = millis();
  Led.run();
  Btn1.run();
  Btn2.run();
  Btn3.run();
}

/*Скетч использует 2068 байт (6%) памяти устройства. Всего доступно 30720 байт.
  Глобальные переменные используют 56 байт (2%) динамической памяти, оставляя 1992 байт для локальных переменных. Максимум: 2048 байт.
*/

 

kalapanga
Offline
Зарегистрирован: 23.10.2016

junior_developer,  ну Вы сами-то хоть чуть-чуть подумайте над тем, что пишете! Ну что за  условие delayTime != delayTime ? Это же как 2 не равно 2. 

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

kalapanga пишет:

 delayTime != delayTime ? Это же как 2 не равно 2. 

Ну, формально вполне допустимо, и при некоторых значения delayTime вполне может быть истинным :)))

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

kalapanga пишет:
... что за  условие delayTime != delayTime ? Это же как 2 не равно 2.
да, похоже,  я ошибся. Думал, что так можно проверить переменную на изменение.
Однако наверно придеться ввести ещё одну переменную

int prevDelay = EEPROM.get(0,delayTime);
// и тогда...
if((button3) && delayTime != prevDelay) {EEPROM.put(0,delayTime);} 

Правильно?

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

junior_developer пишет:
Правильно?
  А нафига?

EEPROM.update(address, value)Записывает байт в EEPROM. Значение записывается, только если оно отличается от значения уже записанного по этому адресу.

http://radioprog.ru/post/117#p117-update

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

По ссылке, которую Вы дали, написано:
value: значение для записи, от 0 до 255 (byte)
Это значит, что обновляется только переменная размером 1 байт? А если у меня значение переменной например от 0 до 1000? То есть в байт оно не влезает! Разве EEPROM.update(address, value) подойдет?

 

Penni
Penni аватар
Offline
Зарегистрирован: 18.01.2015

Note

This function uses EEPROM.update() to perform the write, so does not rewrites the value if it didn't change.

Это про eeprom.put Если больше байта используйте put она сама вызовет апдейт.