Сложные арифметические выражения

ildaronii
Offline
Зарегистрирован: 30.09.2018
Задача простая. Необходимо при достижении граничного уровня освещения включить ночник, причем включать его тем ярче чемь темнее становится. К примеру на вход А0 подключен датчик уровня света, то есть имеем значение от 0 до 1023 уровня освещения. Установим уровень включения ночника равным к примеру 800, то есть при падении уровня освещения ниже 800 ночник должен включаться. Причем с падением освещения от 800 до 0, яркость ночника должна подниматься от 0 до 255.
 
Хотел решить данную задачу вот так:
  int lightness = analogRead(A0);       // уровень освещённости
  int level_1   = analogRead(A1);       // потенциометр задающий уровень включения ночника
  int level_11=0;                                  // уровень свечения светильника
  if (lightness < level_1) level_11 = (((level_1-lightness))/(level_1))*255;
 
выражение чисто арифметически написано правильно, но на выходе не получаю значение от 0 до 255, а какую то аххниею! Почему так?
 
Вот полностью программа:
 
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2); // Устанавливаем дисплей
#define LED_PIN1 9
#define LED_PIN2 10
#define LED_PIN3 11
#define LDR_PIN A0
#define POT_PIN A1
void setup()
{
  lcd.init();
  lcd.backlight();// Включаем подсветку дисплея
  pinMode(LED_PIN1, OUTPUT);pinMode(LED_PIN2, OUTPUT);pinMode(LED_PIN3, OUTPUT);
}
 
void loop()
{ int level_11=0;int level_21=0;int level_31=0;int temp=0;
  int lightness = analogRead(LDR_PIN);       // уровень освещённости
  int level_1   = analogRead(POT_PIN);       // потенциометр
  int level_2   = (level_1/3)*2;
  int level_3   =  level_1/3;
  if (lightness < level_1) { temp=level_1*255; level_11 = (((level_1-lightness)*255)/(level_1*255));   // level_11 = (((level_1-lightness)*255)/(level_1*255));
     if (level_11>255) level_11 = 255; }
  if (lightness < level_2) { level_21 = level_2-lightness; if (level_21>255) level_21 = 255; }
  if (lightness < level_3) { level_31 = level_3-lightness; if (level_31>255) level_31 = 255; }
 
  if (lightness < level_3)   {analogWrite(LED_PIN1, level_11);analogWrite(LED_PIN2, level_21);analogWrite(LED_PIN3, level_31);}
  else     
  { if (lightness < level_2) {analogWrite(LED_PIN1, level_11);analogWrite(LED_PIN2, level_21);digitalWrite(LED_PIN3, LOW);}
    else
    { if (lightness < level_1) {analogWrite(LED_PIN1, level_11);digitalWrite(LED_PIN2, LOW);digitalWrite(LED_PIN3, LOW);}
      else {digitalWrite(LED_PIN1, LOW);digitalWrite(LED_PIN2, LOW);digitalWrite(LED_PIN3, LOW);}     
    }
  }
 
// Блок вывода на дисплей
  if (lightness < 10)                     {lcd.setCursor(0, 1);lcd.print(" ");lcd.setCursor(1, 1);lcd.print(" ");lcd.setCursor(2, 1);lcd.print(" ");lcd.setCursor(3, 1);}
  if (lightness > 9  && lightness < 100 ) {lcd.setCursor(0, 1);lcd.print(" ");lcd.setCursor(1, 1);lcd.print(" ");lcd.setCursor(2, 1);}
  if (lightness > 99 && lightness < 1000) {lcd.setCursor(0, 1);lcd.print(" ");lcd.setCursor(1, 1);}
  if (lightness > 999)                     lcd.setCursor(0, 1);lcd.print(lightness);
  if (level_1   < 10)                     {lcd.setCursor(5, 1);lcd.print(" ");lcd.setCursor(6, 1);lcd.print(" ");lcd.setCursor(7, 1);lcd.print(" ");lcd.setCursor(8, 1);}
  if (level_1   > 9  && level_1   <  100) {lcd.setCursor(5, 1);lcd.print(" ");lcd.setCursor(6, 1);lcd.print(" ");lcd.setCursor(7, 1);}
  if (level_1   > 99 && level_1   < 1000) {lcd.setCursor(5, 1);lcd.print(" ");lcd.setCursor(6, 1);}
  if (level_1   > 999)                     lcd.setCursor(5, 1);lcd.print(level_1);
 
  if (level_11  < 10)                     {lcd.setCursor(0, 0);lcd.print(" ");lcd.setCursor(1, 0);lcd.print(" ");lcd.setCursor(2, 0);lcd.print(" ");lcd.setCursor(3, 0);}
  if (level_11  > 9  && level_11  < 100 ) {lcd.setCursor(0, 0);lcd.print(" ");lcd.setCursor(1, 0);lcd.print(" ");lcd.setCursor(2, 0);}
  if (level_11  > 99 && level_11  < 1000) {lcd.setCursor(0, 0);lcd.print(" ");lcd.setCursor(1, 0);}
  if (level_11  > 999)                     lcd.setCursor(0, 0);lcd.print(level_11);
  if (level_21  < 10)                     {lcd.setCursor(5, 0);lcd.print(" ");lcd.setCursor(6, 0);lcd.print(" ");lcd.setCursor(7, 0);lcd.print(" ");lcd.setCursor(8, 0);}
  if (level_21  > 9  && level_21  <  100) {lcd.setCursor(5, 0);lcd.print(" ");lcd.setCursor(6, 0);lcd.print(" ");lcd.setCursor(7, 0);}
  if (level_21  > 99 && level_21  < 1000) {lcd.setCursor(5, 0);lcd.print(" ");lcd.setCursor(6, 0);}
  if (level_21  > 999)                     lcd.setCursor(5, 0);lcd.print(level_21);
 
  delay (100);
 
 
}
sadman41
Offline
Зарегистрирован: 19.10.2016

lightness = 255- map(analogRead(A0), 0, 800, 0, 255);

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

ildaronii пишет:

if (lightness < level_1) level_11 = (((level_1-lightness))/(level_1))*255;
 
выражение чисто арифметически написано правильно, но на выходе не получаю значение от 0 до 255, а какую то аххниею!

Скакого перепугу Вы получаете ахинею?

Я не смотрел в основной код, т.к. он вставлен не по правилам, но в процитированном кусочке при  lightness  отличном от 0 Вы получаете нормальный, стабильный 0, а когда lightness равно 0, тогда - 255. И никакой ахинеи. Собственно, что написали, то и получаете.

Чему по-Вашему равно выражение (level_1-lightness) / level_1 при отличном от нуля и меньшем, чем level_1 lightness? Нулю, конечно! А ноль его хоть на что умножай, всё равно 0 получится. Ну, а при нулевом lightness, выражение (level_1-lightness) / level_1 очевидно равно 1 - вот Вам и 255.

ildaronii
Offline
Зарегистрирован: 30.09.2018

sadman41 пишет:

lightness = 255- map(analogRead(A0), 0, 800, 0, 255);

Огромное спасибо! Я очень не внимателен. Как оказалось в предыдущем уроке "Эксперимент 4. Терменвокс" функция map(value, fromLow, fromHigh, toLow, toHigh) рассматривалась.

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

Со своим выражением-то разберитесь. А то вечно на "волшебных макросах" выезжать не получится.

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

Мне вот еще интересно: "сложные арифметические выражения" - это какие? В которых более одной арифметической операции?

ildaronii
Offline
Зарегистрирован: 30.09.2018

Тогда давайте разберемся. Я Лет 20 тому назад программировал в Паскале и очень не скверно, писал на листочках бумаги программу в точности до запятых, так что компилятор не выдавал ошибок. В общем давайте разбираться...



Итак у нас имеется выражение level_11 = (((level_1-lightness))/(level_1))*255

где в итоге мы хотим получить на выходе число от 0 до 255 (переменная level_11).



Рассмотрим вариант когда level_1 = 300, lightness = 200. На любом калькуляторе, в т.ч. и Windows, получается 85, но так как мы имеем дело с целочисленными переменными в ардуино получется совершенно не то... Что бы понять это разложим выражение.

level_11 = (((level_1-lightness))/(level_1))*255 = level_11 = (((300-200))/(300))*255 = (100/300)*255 = (100/300)*255 = 0*255 (100/300 = 0 так как целочисленное округляется до ближайшего числа, соотвественно 100/300 = 0,33 = 0. И кстати говоря если в калькуляторе Windows выбрать режим "программист" вы получите 100/300 = 0).



Следовательно надо раскрыть выражение так что бы деление было в конце. Итак раскроем выражение.

level_11 = (((level_1-lightness))/(level_1))*255 = ((level_1-lightness)*255)/level_1 = ((300-200)*255)/(300) = (100*255)/(300) = 25500 / 300 = 85.



Отлично, выясним какое максимальное число мы можем получить в выражении. Если lightness = 1022 и level_1 = 1023, получим 1022 х 1023 = 1045506. Но даже тип WORD максимально может быть 65535, получается надо объявить level_11 как тип UNSIGNED LONG для корректного исчисления или всё же будучи даже типом INT всё будет считаться корректно?

ildaronii
Offline
Зарегистрирован: 30.09.2018

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

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

В паскале отдельно целочисленное деление (div) и плавающее. В С деление одно, и сильна зависит от типа операндов

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

Написав 1/4 получишь 0. Написав 1.0/4 получишь 0.25

a5021
Offline
Зарегистрирован: 07.07.2013

ildaronii пишет:
Итак у нас имеется выражение level_11 = (((level_1-lightness))/(level_1))*255

Откуда у вас столь страстное увлечение скобками? Вы ж вроде паскаль вспоминаете, а не лисп. Вот это выражение

level_11 = (level_1 - lightness) / level_1 * 255

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

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

Вы несёте какую-то ахинею. Какие-то калькуляторы ... причём тут калькуляторы?

Есть правиля языка С++, по ним и работаем

ildaronii пишет:

с целочисленными переменными в ардуино

Ардуино тут вообще не при делах. напишите это в программе на С++ для любого компютера, получите тоже самое.

ildaronii пишет:
так как целочисленное округляется до ближайшего числа, 
А эту глупость Вам кто сказал? Кто бы ни сказал, Вас обманули. Это не так.

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