Передача аналогового порта в класс ADC

5N62V
Offline
Зарегистрирован: 25.02.2016

Всем бобра! 

Есть у меня ардуино Зеро, и надо мне обрабатывать 6 АЦП портов. Написал класс, но как красиво передать ему а на каком собсно порту мерять каждый экземпляр - не знаю. Пока написал так, как в коде внизу. Вроде компилится, но оптимальный ли это вариант - не уверен. Подскажите плз, как сделать элегантнее. 

Недавно скурил книгу Лафоре, пытаюсь применять на практике, поэтому критика приветствуется. :)

#define AVERAGING 10

#define HIGH_VOL A1
#define HIGH_CUR A2
#define V_BAT A3
#define DC1_CUR A4
#define DC2_CUR A5
#define DC3_CUR A6

enum ADCport {
  HIGH_SIDE_VOLTAGE = 1,
  HIGH_SIDE_CURRENT,
  BATTERY_VOLTAGE,
  CURRENT_DC1,
  CURRENT_DC2,
  CURRENT_DC3
};

class MyAdc
{ ADCport pin;
  uint8_t averaging;
  uint32_t reading;  
  float koef;  
  int16_t value;// in mili Volts
  public:  
  MyAdc(ADCport _p, float _k = 1, uint8_t _a = AVERAGING):pin(_p), koef(_k), averaging(_a){}
  int16_t measure(){
    uint16_t tmp = 0;
    switch (pin){ // выбираем порт
      case HIGH_SIDE_VOLTAGE : tmp = analogRead(HIGH_VOL);break;
      case HIGH_SIDE_CURRENT : tmp = analogRead(HIGH_CUR);break;
      case BATTERY_VOLTAGE : tmp = analogRead(V_BAT);break;
      case CURRENT_DC1 : tmp = analogRead(DC1_CUR);break;
      case CURRENT_DC2 : tmp = analogRead(DC2_CUR);break;
      case CURRENT_DC3 : tmp = analogRead(DC3_CUR);break;      
    }
    reading = (reading * (averaging - 1) + tmp) / averaging;
    value = (reading *3300 /4096) * koef;
    return value;
  }
};

MyAdc hVolt(HIGH_SIDE_VOLTAGE);//Объявление экземпляров.
MyAdc hCurr(HIGH_SIDE_CURRENT);
MyAdc bat(BATTERY_VOLTAGE);
MyAdc dc1(CURRENT_DC1);
MyAdc dc2(CURRENT_DC2);
MyAdc dc3(CURRENT_DC3);

 

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

Ну, передавать-то проще так:

#define AVERAGING 10

#define HIGH_VOL A1
#define HIGH_CUR A2
#define V_BAT A3
#define DC1_CUR A4
#define DC2_CUR A5
#define DC3_CUR A6

enum ADCport {
  HIGH_SIDE_VOLTAGE = HIGH_VOL,
  HIGH_SIDE_CURRENT = HIGH_CUR,
  BATTERY_VOLTAGE = V_BAT,
  CURRENT_DC1 = DC1_CUR,
  CURRENT_DC2 = DC2_CUR,
  CURRENT_DC3 = DC3_CUR
};

class MyAdc
{ ADCport pin;
  uint8_t averaging;
  uint32_t reading;  
  float koef;  
  int16_t value;// in mili Volts
  public:  
  MyAdc(ADCport _p, float _k = 1, uint8_t _a = AVERAGING):pin(_p), koef(_k), averaging(_a){}
  int16_t measure(){
    uint16_t tmp = analogRead(pin);
    reading = (reading * (averaging - 1) + tmp) / averaging;
    value = (reading *3300 /4096) * koef; // Не знаю, чего ты хотел, но здесь явно не то!!!!
    return value;
  }
};

MyAdc hVolt(HIGH_SIDE_VOLTAGE);//Объявление экземпляров.
MyAdc hCurr(HIGH_SIDE_CURRENT);
MyAdc bat(BATTERY_VOLTAGE);
MyAdc dc1(CURRENT_DC1);
MyAdc dc2(CURRENT_DC2);
MyAdc dc3(CURRENT_DC3);

только я бы ещё описание enum'а втащил с класс - нефиг ему снаружи делать.

Ну, и не мой комментарий обрати внимание. Там у тебя при koeff < 1 всегда будет 0, а при больших значениях - очень грубое округление получится.

rkit
Offline
Зарегистрирован: 23.11.2016

Енум лишний. Передавай просто номер пина.

5N62V
Offline
Зарегистрирован: 25.02.2016

Ворота пишет:

Ну, передавать-то проще так:

Да, действительно, не знал что enum-у можно присвоить значание А1.

Втащить энум в класс не получается - объявление экземпляра использует его.

 

Ворота пишет:

Там у тебя при koeff < 1 всегда будет 0

Эммм.... я чего-то полагал, что часть в скобках должна неявно привестись к float.  Так будет правильнее:

value = (reading * 3300 /4096.) * koef;

5N62V
Offline
Зарегистрирован: 25.02.2016

rkit пишет:

Енум лишний. Передавай просто номер пина.

Похоже Вы правы. Нашел вот такую таблицу:

 +------------+------------------+---------+---------+----------+

 | 16         | A1               |         |   5/00  |          |

 | 17         | A2               |         |   5/01  |          |

 | 18         | A3               |         |   0/00  |          |

 | 19         | A4               |         |   0/01  |          |

 | 20         | A5               |         |   0/02  |          |

 | 21         | A6               |         |   0/03  | I2S/SD0  |

 +------------+------------------+---------+---------+----------+

rkit
Offline
Зарегистрирован: 23.11.2016

И таблицы никакой не надо. Прямо A1 или что надо передаешь и всё.

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

5N62V пишет:

Втащить энум в класс не получается - объявление экземпляра использует его.

И что? Объявляй через квалификатор класса, дело в-то. Имя класса, два двоеточия.

5N62V
Offline
Зарегистрирован: 25.02.2016

Да, я понял, спасибо. Немного громозкая запись получается, но по феньшую  - так по феньшую! :)

А это не будет для каждого экземпляра отжирать память?

5N62V
Offline
Зарегистрирован: 25.02.2016

rkit пишет:

И таблицы никакой не надо. Прямо A1 или что надо передаешь и всё.

Сбивают с толку эти А1. Роюсь ищу где у Зеро пин эссайнмент, чем эта зараза заменяется.

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

5N62V пишет:

А это не будет для каждого экземпляра отжирать память?

С какого перепою?

А про то, что енум вообще не нужен, ты больше всяких грамотеев слушай. Ты правильно подошёл к делу и не позволяй себя сбивать с пути. Хочешь учиться программировать, учись делать это правильно, а не колхозить.

rkit
Offline
Зарегистрирован: 23.11.2016

Ворота пишет:

А про то, что енум вообще не нужен, ты больше всяких грамотеев слушай. Ты правильно подошёл к делу и не позволяй себя сбивать с пути. Хочешь учиться программировать, учись делать это правильно, а не колхозить.

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

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

Поспорить решил?

Спорить не буду, ибо смешно.

Если не понимаешь какая польза от enum в этом применении, задавай вопросы. Отвечу, если нормально спросишь.

5N62V
Offline
Зарегистрирован: 25.02.2016

Ворота пишет:

Поспорить решил?

Спорить не буду, ибо смешно.

Если не понимаешь какая польза от enum в этом применении, задавай вопросы. Отвечу, если нормально спросишь.

Мне расскажите плз. А то я вначале хотел статическую переменную, инкременируемую в конструкторе,  заводить, а дальше через switch канал выбирать. Остановило меня то, что порядок объявления экземпляров влиял на то, с какого канала будут данные. :)

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

Ну, если уж ты решил нормально учиться, то первое. что нужно сделать (если ещё не сделал), это вытащить голову из песка (куда её засунули разработчики IDE) и включить вывод сообщений компилятора. В IDE по умолчанию он выключен, чтобы блондинок глупостЯми не беспокоить. Компилятор говорит очень много полезного, но разработчики IDE скрыли это от тебя.

Включаются они на вкладке "Настройки" вот так (красным обведено).

 

Теперь, когда ты больше не слепой, давай поговорим про пользу enum.

Представим себе, что имеется функция, параметром которой должен быть ШИМ-пин. Т.е. не какой попало, а именно 3, 5, 6, 9, 10 или 11.

Допустим, мы послушались особо умных советчиков и не стали "раздувать код", а обозвали параметр просто int. А потом, где-то почему-то ошиблись и пихнули этой функции неправильное значение, например вот так:

void startPWM(const int pin, const uint8_t duty) {
	analogWrite(pin, duty);
}

void setup(void) {
	int v = analogRead(0);
	startPWM(v, 123); // Вместо номера пина пихаем чё попало по ошибке
}

void loop(void) {}

Или даже так - просто с константой, но с неправильной:

void startPWM(const int pin, const uint8_t duty) {
	analogWrite(pin, duty);
}

void setup(void) {
	startPWM(7, 123); // На 7 пине нет ШИМа!
}

void loop(void) {}

И что? А ничего! Компилятор съел без единого замечания. А чего ему? int нужен, int и передаём, у него не может быть претензий! Обнаружим мы это при исполнении и (если программа не 10 строк, как у нас, а с пару тысяч) будем долго и матерно искать в чём же проблема!

А вот теперь, попробуем объяснить компилятору, что нам нужен не какой попало int, а именно номер ШИМ'овского пина. Сделаем вот так:

enum PWMPin {
	D3 = 3, D5 = 5, D6 = 6, D9 = 9, D10 = 10, D11 = 11
};

void startPWM(const PWMPin pin, const uint8_t duty) {
	analogWrite(pin, duty);
}

void setup(void) {
	int v = analogRead(0);
	startPWM(D5, 123); // Правильно
	startPWM(v, 123); // Вместо номера пина пихаем чё попало по ошибке
	startPWM(7, 123); // на 7 пине нет ШИМ'а
}

void loop(void) {}

Скомпилируй сам и посмотри. В принципе компилятор сгенерил код (не сказал "exit 1"), но на строки №№ 12 и 13 выдал предупреждение:

warning: invalid conversion from 'int' to 'PWMPin' [-fpermissive]

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

Это (то, что я сейчас сделал) не стоит ни одной копейки в плане объёма исполняемого кода и времени выполнения, а времени и нервов при случае сохранит уйму. Только особо продвинутые (те, кто никогда не ошибается) могут говорить, что это бесполезно. Но эти ребята - не программисты - они сапёры.

5N62V
Offline
Зарегистрирован: 25.02.2016

Oчень познавательно и доступно. Огромное спасибо за потраченное время и труд! Я не особо умею красиво и красочно выражаться, тем более  в письменной форме, но реально уважаю людей которые делают добро за здорово живешь. :) на форуме без таких - труба! 

ПС. Сообщения включены, и даже иногда их читаю. 

 

Ворота пишет:

С какого перепою?

ага, туплю, это же не переменная, а ее описание.

rkit
Offline
Зарегистрирован: 23.11.2016

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

Не говорю, что это всегда правда, но в текущей ситуации именно так.

И великий советчик это отлично знает, потому что сам не использует подобного рода костыли в своем коде.

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

Родное сердце (как выражается наш уважаемый граф), я уже сказал, что спорить с тобой я не буду, потому что если и после этого объяснения, ты продолжаешь газировать лужу, то помочь тебе я всё равно не смогу - тут психиатр нужен. Что же до того, что я использую, а что - нет, ты этого не знаешь, так зачем тогда "языком по голой заднице"?  Впрочем, можешь посмотреть мой код в "проектах" и поучиться что и как использовать.

За сим прощаюсь. Если тебе захочется ещё немного подемонстрировать свою дремучесть - не возражаю, но без меня. Сам себя обслужи.

5N62V
Offline
Зарегистрирован: 25.02.2016

Сопрягал я как-то свое устройство с распберри, код для которой писал программер. Надо было с ардуины передать структурку байт в 13 по уарту. Я сделал как Евгений когда-то советовал: через указатель на структуру. Работало без проблем. Но не слишком этот подход показался программеру концептуальным. В итоге мы задействовали JSON, слать стали раз в пять длиннее пакеты, а я понял, что нифига не понимаю как выбрать между практичностью и феньшуем. Но на всякий случай решил подучить феньшуй :)

Самое смешное, что тот программер сам не раз говорил, что прав тот, у кого работает :) Видимо от настроения выбор подхода зависит. :) Не ругайтесь! ;)

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

JSON, XML - это модно, человекопонятно и универсально.

Если данные идут только в одном направлении - МК > ПК, то JSON уместен, а если обмен двусторонний, то извращаться в ограниченном объеме памяти МК для парсинга текстового формата обмена достаточно бессмысленно.

Нюансов много в выборе способа взаимодействия разнородных систем.