Какой тип возвращаемых данных у функции?

junior_developer
Offline
Зарегистрирован: 27.11.2017
Не могу понять почему компилятор не видит тип данных, если я его явно указываю вот так
enum BTN_scan_states{
	NOT_PRESSED_ = 0,
	PRESSED_ = 1,
	HOLDED_ = 2,
	RELEASED_ = 3
};

При компиляции вылазит: 'BTN_scan_states' does not name a type! То есть компилятор буквально говорит, что это не тип данных?

Подсчечивается строка 
BTN_scan_states button_scan(){ 

Первое слово - тип возращаемого значения функции. По идее она должна вернуть состояние кнопки. Насколько я понял, компилятор не понимает, этот тип данных? Код целиком

#define LED_Pin 0
#define BTN_Pin 4
#define interval_slow 500UL
#define interval_fast 50UL
#define ext_interval 2000UL

void setup(){ 
pinMode(LED_Pin, OUTPUT); 
pinMode(BTN_Pin, INPUT_PULLUP);
}

bool progTimer(unsigned long interval){
  static unsigned long prevTime=0;
  if(millis()-prevTime>interval){
    prevTime=millis();
    return false;
  }
  else return true;
}
enum BTN_scan_states{
  NOT_PRESSED = 0,
  PRESSED_ = 1,
  HOLDED_ = 2,
  RELEASED_= 3
};
BTN_scan_states button_scan(){
  static bool prev_BTN_State = FALSE; // предыдущее состояние кнопки (нажата или нет)
    bool BTN_State = !(digitalRead(BTN_Pin)); // текущее состояние кнопки
  if (BTN_State==FALSE && prev_BTN_State==FALSE) {prev_BTN_State==FALSE; return NOT_PRESSED;} // кнопка не нажата
  if (BTN_State==TRUE && prev_BTN_State==FALSE) {prev_BTN_State==TRUE; return PRESSED_;} // кнопка нажата
  if (BTN_State==TRUE && prev_BTN_State==TRUE) {prev_BTN_State==TRUE; return HOLDED;} // кнопка удерживается
  if (BTN_State==FALSE && prev_BTN_State==TRUE) {prev_BTN_State==FALSE; return RELEASED_;} // отпущена
}

void loop(){
 static BTN_scan_states b_state = button_scan();
 switch(b_state){
   case: NOT_PRESSED {Blink (LED_Pin, interval_slow); break;} // не нажата
   case: PRESSED {Blink (LED_Pin, interval_fast); break;} // нажатие
   case: HOLDED {Blink (LED_Pin, interval_fast); break;} // удержание кнопки нажатой
   case: RELEASED {if (progTimer(ext_interval)){Blink (LED_Pin, interval_fast)}
   else{Blink (LED_Pin, interval_slow);} break;} //отпускание
 }
}

Три строчки кода в правильности написания которых сомневаюсь, я выделил! Посмотрите пожалуйста, кто разбирается!

mixail844
Offline
Зарегистрирован: 30.04.2012

enum это не тип данных , это "способ" переименования ряда целых чисел.

enum становиться типом данных когда : 

typedef enum 
{
NOT_PRESSED , 
PRESSED,
HOLD,
RELEASED,
}BTN_scan_staes;

вы его типом обьявляете

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

enum при более накрученых кодах склонен сбивать компилятор. Так что рекомендую делать так.

const byte  NOT_PRESSED_ = 0;
const byte  PRESSED_ = 1;
const byte  HOLDED_ = 2;
const byte  RELEASED_ = 3;

и возвращаемый тип будет byte.

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

Сорри, написал не разобравшись. Удалил

mykaida
mykaida аватар
Offline
Зарегистрирован: 12.07.2018

Не, ну почему все идут "через жо..у и сваркой"? Неужели нет других способов ну, например, #define?

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

mykaida пишет:

Не, ну почему все идут "через жо..у и сваркой"? Неужели нет других способов ну, например, #define?

а что плохого в enum? Когда какая-то переменная принимает несколько фиксированных значений - я как раз обычно enum использую - это и нагляднее, и надежнее. С #define вы можете случайно присвоить переменной какое-то левое значение, а с enum вам компилятор даст выбрать только заранее описанные.

mykaida
mykaida аватар
Offline
Зарегистрирован: 12.07.2018

b707 пишет:

а что плохого в enum? Когда какая-то переменная принимает несколько фиксированных значений - я как раз обычно enum использую - это и нагляднее, и надежнее. С #define вы можете случайно присвоить переменной какое-то левое значение, а с enum вам компилятор даст выбрать только заранее описанные.

Ну, переменной присвоить значение с помощью #define, конечно надо постараться. Но идею я понял. При достаточно больших программах - enum является защитой, но в указанном случае - пожирателем пространства и вые-ом, извините за грубость.

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

mykaida пишет:

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

не знаю, что он там пожирает

Вот эти два кода абсолютно идентичны по "пожиранию пространства"

через enum

enum aa {one, two, three, four};
aa bb, cc;

void setup() {
 
if (bb == one) bb = four;
if (cc == one) cc = two;
}

void loop() {}

через дефайны

#define one 1
#define two 2
#define three 3
#define four 4

int bb, cc;

void setup() {
if (bb == one) bb = four;
if (cc == one) cc = two;
}

void loop() {}

 

 

Скетч использует 492 байт (1%) памяти устройства. Всего доступно 30720 байт.
Глобальные переменные используют 13 байт (0%) динамической памяти

 

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

У енума есть хороший побочный эффект: когда его свичуешь, к примеру, компилятор проверяет - все ли значения попали в свич. Если не все, то предупреждает, что какое-то не обрабатывается, не забыл ли про него уважаемый автор исходника?

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

Не понимаю, как, но этот мой код наконец-то заработал! Примерно так как предполагалось.
 

#define LED_Pin 0
#define BTN_Pin 4
#define interval_slow 500UL
#define interval_fast 50UL
#define ext_interval 2000UL

void setup(){ 
pinMode(LED_Pin, OUTPUT); 
pinMode(BTN_Pin, INPUT_PULLUP);
}

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

bool progTimer(unsigned long interval){
  static unsigned long prevTime=0;
  if(millis()-prevTime>interval){
    prevTime=millis();
    return false;
  }
  else return true;
}

 enum BTN_st{
  NOT_PRESSED_ = 0,
  PRESSED_ = 1,
  HOLDED_ = 2,
  RELEASED_= 3
};

 byte button_scan(){
  static bool prev_BTN_State = false; // предыдущее состояние кнопки (нажата или нет)
    bool BTN_State = !(digitalRead(BTN_Pin)); // текущее состояние кнопки
   
  if (BTN_State==false && prev_BTN_State==false) {return NOT_PRESSED_; prev_BTN_State==false; } // кнопка не нажата
  if (BTN_State==true && prev_BTN_State==false) {return PRESSED_; prev_BTN_State==true; } // кнопка нажата
  if (BTN_State==true && prev_BTN_State==true) {return HOLDED_; prev_BTN_State==true; } // кнопка удерживается
  if (BTN_State==false && prev_BTN_State==true) {return RELEASED_; prev_BTN_State==false; } // отпущена
}

void loop(){
 byte  b_state = button_scan();
 static bool t_state;
 switch(b_state){
  case NOT_PRESSED_: if(!t_state) {Blink (LED_Pin, interval_slow); 
   break; }// не нажата
  case PRESSED_:{ t_state = progTimer(ext_interval);
   if(t_state) Blink (LED_Pin, interval_fast);
   break;}
   }
 }

Хотя интервалы времени (задержки перехода светодиода в другой режим) иногда отличаюся! Наверно стоит добавить ещё защиту от дребезга!
 А кусочек кода с enum там такой:

 enum BTN_st{
  NOT_PRESSED_ = 0,
  PRESSED_ = 1,
  HOLDED_ = 2,
  RELEASED_= 3
};

Я не знаю, что такое в данном случае BTN_st. Переменная или название перечисления, но обратите внимние, что в коде её вообще нету! Была строка 

BTN_st b_state = NOT_PRESSED_;

Но она  закомментирована! А буквально её можно было бы расшифровать так: создать переменную типа BTN_st с названием b_state и присвоить ей значение NOT_PRESSED_ (кнопка не нажата). Или я ошибаюсь, и компилятор понимает это как-то по другому?

 

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

Мужики, Вас куда-то понесло, правда.

ТС, Всё Вы правильно объявили, всё нормально. И тип там нормальный и всё нормально (в коде полно других неправильностей на которые ругается компилятор, но с enum как раз всё окей).

Проблема стара как Ардуино IDE - охренительно дружественное для чайников решение, которое выносит все объявления функций в начало файла.

Для того, чтобы тип заработал нормально достаточно этот enum (никак не меняя) вынести в отдельный файл и включить его в основной при помощи #include. Включать надо как можно выше (например, первой строкой программы).

Всё сказанное относится к коду из стартового поста.

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

sadman41 пишет:

У енума есть хороший побочный эффект

И это не единственный его хороший эффект.

Им можно объявлять значения переменных для битовых полей и он сам позаботится, чтобы они влезли ы нужное количество бит. См. пример здесь.

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

Насколько я запомнил - выносить во внешний .h необязательно, достаточно объявить прототипы после enum и тогда IDE не будет их пихать выше радуги.

А вообще, вот так примерно с енумами обхожусь:

typedef enum {
  stInit = 0,
  stReady,
  stSteady,
  stGo,
  stFail
} stages_t;

stages_t whatStageIs(uint8_t);

void setup() {
  Serial.begin(115200);
  Serial.println("Stages handling demo");
}

void loop() {
  uint8_t number = random(4);
  switch (whatStageIs(number)) {
    case stInit:
      Serial.println("Init stage running");
      break;
 
   case stReady:
      Serial.println("Ready stage running");
      break;
 
   case stSteady:
      Serial.println("Steady stage running");
      break;
 
   case stGo:
      Serial.println("Go stage running");
      break;
  }
}

stages_t whatStageIs(uint8_t number) {
  stages_t returnStage;
  switch (number) {
    case 0:
      Serial.print("Init");
      returnStage = stInit;
      break;
 
   case 1:
      Serial.print("Ready");
      returnStage = stReady;
      break;
 
   case 2:
      Serial.print("Steady");
      returnStage = stSteady;
      break;
 
   case 3:
      Serial.print("Go");
      returnStage = stGo;
      break;
 
   default:
      Serial.print("None");
      returnStage = stGo;
      break;
  }
  Serial.println(" stage matched");
  return returnStage;
}

 

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

sadman41 пишет:

достаточно объявить прототипы после enum и тогда IDE не будет их пихать выше радуги.

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

В люьом случае, ТС должен понимать, что у него всё нормально, проблема в "умности" IDE.

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

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

В люьом случае, ТС должен понимать, что у него всё нормально, проблема в "умности" IDE.

да где ж нормально, Евгений Петрович? Посмотрите на второй абзац заглавного сообщения,

junior_developer пишет:

 
Подсчечивается строка 
BTN_scan_states button_scan(){ 

Первое слово - тип возращаемого значения функции. По идее она должна вернуть состояние кнопки.

о какой функции тут вообще идет речь?

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

О функции button_scan

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

Вот, пожалуйста.

Файл xxx.ino

#include "kaka.h"

BTN_scan_states button_scan() {
	return NOT_PRESSED;
}

void loop(void){}
void setup(void){}

Файл kaka.h

enum BTN_scan_states{
  NOT_PRESSED = 0,
  PRESSED_ = 1,
  HOLDED_ = 2,
  RELEASED_= 3
};

Можете убедиться, что всё компилируется нормально. И работать будет нормально, уверяю Вас.

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

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

Можете убедиться, что всё компилируется нормально. И работать будет нормально, уверяю Вас.

ну да, я уж и сам увидал - после первого вашего сообщения. Поначалу не заметил эту функцию в коде - думал это ТС так enum повторно описывает....

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

У меня появился ещё вопрос насчет НЕ логичного возвращение функцией значения! Вроде бы проще не бывает.

bool button_scan(){
    bool BTN_State = !(digitalRead(BTN_Pin)); // текущее состояние кнопки
    static bool TimerSet; // активен ли ещё таймер 
  if (BTN_State) {TimerSet = progTimer(ext_interval);} // если нажата, запустить таймер и считать состояние в переменную
  if (TimerSet) return true; // таймер активен
  if (!TimerSet) return false; // не активен
}

Тип  bool, то есть функция возвращает 0 или 1. По идее она должна возвращать:

1. false в обычном состоянии - кнопка не нажата
2. true при нажатии кнопки и ещё в течении заданного времени (значение ext_interval) например 2 секунды.

На самом же деле false она возвращает только до нажатия кнопки, а дальше всё время true! Впечатление такое, что таймер запуcкается и прокручивается всё время, даже если кнопка не нажата! То есть условие
 

if (BTN_State) {TimerSet = progTimer(ext_interval);}

как бы игнорируется? Посмотрите пожалуйста, кто разбирается, вот код целиком
 

#define LED_Pin 0
#define BTN_Pin 4
#define interval_slow 500UL
#define interval_fast 50UL
#define ext_interval 2000UL

void setup(){ 
pinMode(LED_Pin, OUTPUT); 
pinMode(BTN_Pin, INPUT_PULLUP);
}

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

bool progTimer(unsigned long interval){
  static unsigned long prevTime=0;
  if(millis()-prevTime>interval){
    prevTime=millis();
    return false;
  }
  else return true;
}

bool button_scan(){
    bool BTN_State = !(digitalRead(BTN_Pin)); // текущее состояние кнопки
    static bool TimerSet; // активен ли ещё таймер 
  if (BTN_State) {TimerSet = progTimer(ext_interval);} // если нажата, запустить таймер 
  // и считать состояние в переменную
  if (TimerSet) return true; // таймер активен
  if (!TimerSet) return false; // не активен
}

void loop(){
//button_scan();
if (button_scan()) Blink (LED_Pin, interval_fast);
else Blink (LED_Pin, interval_slow);
}

 

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

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

junior_developer пишет:
По идее она должна возвращать:

1. false в обычном состоянии - кнопка не нажата
2. true при нажатии кнопки и ещё в течении заданного времени (значение ext_interval) например 2 секунды.

Так рассуждают, когда проектируют программу и думают как её написать.

А вот на этапе поиска ошибки (на котором Вы сейчас находитесь) от таких рассуждений пользы не много. На этом этапе рассуждают на более низком уровен, а именно "По идее она должна возвращать true, если TimerSet равно true, и false в противном случае".

И, кстати, если бы Вы рассуждали так, то Вы бы немедленно заметили, что код можно упростить, а именно 

//
// ВМЕСТО строк
//
if (TimerSet) return true; // таймер активен
if (!TimerSet) return false; // не активен

//
// ДОСТАТОЧНО
//
return TimerSet;    // true если таймер активен

А более простой код - это не только и не столько машинная эффективность, сколько "лучшая читаемость человеком", а значит лучший поиск ошибок.

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

Да. Вы правы. Такая запись гораздо проще. Я просто ещё плохо разбираюсь в синтаксисе! Спасибо за подсказку!
Если не сложно, подскажите пожалуйста, в данном случае

if (BTN_State) {TimerSet = progTimer(ext_interval);}

таймер ведь будет обнуляться сам? Или нужно ещё что-то дописывать? Точнее переменная TimerSet сама обнулится, когда функция progTimer вернет false?

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

junior_developer пишет:

На самом же деле false она возвращает только до нажатия кнопки, а дальше всё время true! Впечатление такое, что таймер запуcкается и прокручивается всё время, даже если кнопка не нажата! То есть условие
 

if (BTN_State) {TimerSet = progTimer(ext_interval);}

как бы игнорируется?

вы сами написали это условие так, что после отпускания кнопки оно игнорируется, ведь при отпущенной кнопке BTN_State - false. Так что таймер запускается, отсчитывает свои 2 сек и останоавливается, но поскольку переменную TimerSet вы не обновляете - она остается true

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

junior_developer пишет:

переменная TimerSet сама обнулится, когда функция progTimer вернет false?

конечно нет. С чего бы?

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

а как её можно обнулить? Напишите пожалуйста!

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

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

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

junior_developer пишет:

а как её можно обнулить? Напишите пожалуйста!

например, если TimerSet == true , запрашиваем состояние таймера и обновляем TimerSet

bool button_scan(){
    bool BTN_State = !(digitalRead(BTN_Pin)); // текущее состояние кнопки
    static bool TimerSet; // активен ли ещё таймер 
  if (BTN_State || TimerSet) {TimerSet = progTimer(ext_interval);} // если нажата, запустить таймер и считать состояние в переменную
  return TimerSet;
}

 

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

Большое спасибо за поссказку!
Те две черточки в строке
 

if (BTN_State || TimerSet) {TimerSet = progTimer(ext_interval);}

Означают ИЛИ. То есть буквально
если кнопка нажата ИЛИ переменная TimerSet равна true, то присвоить переменной TimerSet значение из функции  progTimer(ext_interval). 
В итоге, если кнопка не нажата, то через интервал (ext_interval) в переменную TimerSet попадет false и функция вернет false!

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

junior_developer - все верно