Помогите Уважаемые гуру. HELP.

yuriy m
Offline
Зарегистрирован: 23.12.2012

Доброго времени суток гуру. Пытаюсь разобраться в программирование arduino да и вообще контроллеров awr. Но пока как-то  туго получается... :((  .  Значит есть простой алгоритм - есть 2 входных аналоговых напряжения batt и vpower которые мы должны контролировать и  один цифровой входной сигнал acc. Также 2 выхода - MOSFET1,MOSFET2.

 

  1. Проверяем  напряжение batt - если оно меньше чем 2.5в более чем 30сек., то на включение  кнопки acc не реагируем и проверяем наличие напряжения vpower. Если vpower меньше 2.5в длительное время ,то ничего не делаем (подразумеваем что MOSFET2- выключен,если нет ,то выключаем).Если vpower больше чем 2.5в (тоесть как минимум включен MOSFET2),то включаем MOSFET1 на 1сек. и контролируем напряжение vpower. Eсли через 120 сек напряжение не пропало ,то выключаем MOSFET2. Если пропало то MOSFET2 выключаем через 10сек после того как пропало напряжение vpower.
  2.  Проверяем напряжение batt – если оно больше чем 2.5в,то готовы реагировать на включение/выключение кнопки acc .  При наличии логической единицы на кнопки acc , при том что batt больше 2.5в и отсутствие напряжения vpower ,при том что MOSFET2 включен ,если выключен то включаем MOSFET2  , ждем 3 сек.если MOSFET2 включен ,то не ждем 3 сек. асразу включаем MOSFET1 На 1сек ,через 5сек проверяем включение напряжения vpower. Vpower должен появится.
  3. При выключении acc ждем 5минут и если acc не появилось ,то включаем MOSFET1 на 1 сек и контролируем снятие напряжения vpower если снялось то ok,если не снялось через 120 сек то выключаем MOSFET2.
//-----------------------------------------
//- (пины) ардуино -
//-----------------------------------------
int batt=A0;                             //аналоговый вход(пин) контроля напряжения борт сети
int vpower=A1;                       // аналоговый вход(пин) контроля напряжения материнки
int acc=0;                               // аналоговый вход(пин2) прерывание 0

int MOSFET1=6;                     // цыфровой выход(пин) ключа1   
int MOSFET2=13;                   // цыфровой выход(пин) ключа2

void setup()
{

pinMode(MOSFET1,OUTPUT);
pinMode(MOSFET2,OUTPUT);


attachInterrupt(acc, DChi, RISING);             // прерывание
attachInterrupt(acc, DClow, FALLING);        // прерывание

}
void DChi()
{

}

void DClow()
{

}

void loop()
{

int battValue=analogRead(batt);                      // считываем и присваеваем значения напряжения
if (battValue<800 )                                           //Если напряжение питания ниже примерно 2.5v то-
battValue = LOW;                                            // =0
else if (battValue>810)                                    //Если напряжение питания больше примерно 2.5v то-
battValue = HIGH;                                           // =1


int vpowerValue=analogRead(vpower);         // считываем и присваеваем значения напряжения
if (vpowerValue<800)                                     //Если напряжение питания ниже примерно 2.5v то-
vpowerValue = LOW;                                     // =0
else if (vpowerValue>810)                             //Если напряжение питания больше примерно 2.5v то-
vpowerValue = HIGH;                                    // =1
}

Чет не могу вкурить как организовать на С.  Я работаю на PLC контроллерах на языке РКС,там все просто... А здесь.... Наверное старею. GGG. Если кто может направте на путь истинный с подробными каментариями,как можно код написать данного алгоритма и какие есть варианты. Сильно не пинайте - всего 3 дня вожусь с ардуиной... Спасибо.

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

Аналоговые входы 10 бит, т.е. значения от 0 до 1023, т.е. 0 в до 5 в. 800 - это не 2.5 в.
Гистерезис - прекрасно. Я про сравнение 800 - 810.
Чтобы определить длительность превышения напряжения фиксируйте моменты измерения времени с помощью millis(), потом сравнивайте текущее время с сохраненным.
Прерывания на кнопку.. можно, но посмотрите примеры, что бы бороться с дребезгом.
Использование пина 13 позволяет видеть его состояние, поскольку на этот пин подключен светодиод на плате. Можно это увидеть, если запустить пример Blink.

yuriy m
Offline
Зарегистрирован: 23.12.2012

800 эт просто пример... с дребезгом не проблема (rc ) цепочка или триггер шмитта,большого быстродействия не надо.. светодиодом моргал пару дней назад :)).  Немного не догоняю с прерываниями и как использовть millis если надо разные задержски в разном месте?

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

А что с прерываниями? В них нужно просто изменить состояние переменной, которая хранит состояние кнопки. Плюс обработка должна быть как можно короче.
По поводу millis, нам нужно знать, как долго есть превышение напряжения, значит нужно завести переменную (хотя я бы сделал структуру) на каждый вход, в ней запоминать время начала превышения напряжения. Аналогично по снижению. Аналогично для каждого входа.
После того, как это сделано, проверяем наступление событий.
Еще, переменные, используемые в прерываниях, нужно объявить volatile. Почитайте про это.

leshak
Offline
Зарегистрирован: 29.09.2011

kisoft пишет:
Аналоговые входы 10 бит, т.е. значения от 0 до 1023, т.е. 0 в до 5

Не всегда. Это в случае елси дуина сама питается точно 5v-вольтами, а если там просело до 4.3 то и 1023 будет означать 4.3. Вообщем 1023 означает "100% питания камня".

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

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

В частных случаях - конечно так. Согласен, надо было написать 100% питания. Тем более вчера мерял, у меня 4,96В или около того.

 

 

yuriy m
Offline
Зарегистрирован: 23.12.2012

kisoft пишет:
По поводу millis, нам нужно знать, как долго есть превышение напряжения, значит нужно завести переменную (хотя я бы сделал структуру) на каждый вход, в ней запоминать время начала превышения напряжения. Аналогично по снижению. Аналогично для каждого входа. После того, как это сделано, проверяем наступление событий. Еще, переменные, используемые в прерываниях, нужно объявить volatile. Почитайте про это.

Что значит зделал бы структуру ? Можно на примере как работать с millis и с прерыванием ?

leshak
Offline
Зарегистрирован: 29.09.2011

А зачем вам тут прерывания? Тем более, с аналоговым пином оно вам не сильно поможет.

attachInterrupt работает со вторым и третим цифровыми пинами.

Почитайте про attachInterrupt в документации.

Вообщем "прерывания" - в сторону.

Что вы, в стартовом скетче, фактически и сделали.

Просто крутимся в loop(), читаем аналоговые пины, если у них какие-то нужные нам значения - что-нибудь делаем

Меняем переменную-флаг, запоминаем время "когда это произошло"  (функция millis() - возвращается текущие время в миллисекундах), вызваем какие-то "функции обработчи".

if (battValue<800 ) { // напряжение упало
   if(!battLowOn)battLowOn=millis(); // если перед этим оно небыло Low, то запоминаем "когда начало"

} else {
  battLowOn=0; // сбрасываем "счетчик времени" 
}


  if(battLowOn && (millis()-battLowOn)>3000){ // если низкое значение держится больше 3 sec
    что-то делаем
  }

 

yuriy m
Offline
Зарегистрирован: 23.12.2012

leshak пишет:

А зачем вам тут прерывания? Тем более, с аналоговым пином оно вам не сильно поможет.

attachInterrupt работает со вторым и третим цифровыми пинами.

Почитайте про attachInterrupt в документации.

Вообщем "прерывания" - в сторону.

Что вы, в стартовом скетче, фактически и сделали.

Просто крутимся в loop(), читаем аналоговые пины, если у них какие-то нужные нам значения - что-нибудь делаем

Меняем переменную-флаг, запоминаем время "когда это произошло"  (функция millis() - возвращается текущие время в миллисекундах), вызваем какие-то "функции обработчи".

С millis немного догнал... Спасибо .  Там опечатка - это 2й цифровой пин. Просто по условию задачи мне надо однократно реагиравать на появление либо снятия сигнала acc . Вот и думал что на прерывании по фронту и по спаду... Можно как то по другому организовать?

leshak
Offline
Зарегистрирован: 29.09.2011

yuriy m пишет:

С millis немного догнал... Спасибо .  Там опечатка - это 2й цифровой пин. Просто по условию задачи мне надо однократно реагиравать на появление либо снятия сигнала acc . Вот и думал что на прерывании по фронту и по спаду... Можно как то по другому организовать?

Можно, что мешает? 

Заведите себе переменную "прошлое состояние" пина.

В каждом проходе loop смотрите на пин. Если прошлое 0, а текущие 1 - значит это "фронт", если прошлой 1 а текущие 0 - значит спад. 

Если прошлое и текущие равны - значит ничего не изменилось.

Ну и в конце loop, запомнить "текущие состояние" в переменную "прошлое" (для будущего прохода).

Примерно так:

bool prevState=false;
void loop(){
  bool state=digitalRead(PIN);

  if(!prevState && state) Serial.println("FRONT");

  if(prevState && !state) Serial.println("DOWN");

  prevState=state;
}

 

yuriy m
Offline
Зарегистрирован: 23.12.2012

Спасибо . Сейчас буду мозговать .   :)

leshak
Offline
Зарегистрирован: 29.09.2011

 

yuriy m пишет:

Спасибо . Сейчас буду мозговать .   :)

Это самое трудное (без сарказма). Причем, боюсь, ваш опыт будет скорее мешать чем помогать. Так как тут нужно именно "перестроить мышление".

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

yuriy m
Offline
Зарегистрирован: 23.12.2012

leshak пишет:
Это самое трудное (без сарказма). Причем, боюсь, ваш опыт будет скорее мешать чем помогать. Так как тут нужно именно "перестроить мышление".

Здесь попал в точку - мозг сопротивляется до последнего...

Я больше по железу причем профессионально. На логике разработать такую схему  - несколько минут, написать программу под PLC –тоже быстро, но С+ ….Пока мой мозг очень туго воспринемает..

http://any-book.org/download/48176.files/image100.jpg

http://www.indusoft.ru/files/articles/Pikad_durus6.jpg

Это ,так из простых примеров..

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

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

А вообще, если я правильно понял PLC & PKC, то это автоматы. Почему бы здесь не сделать точно так же?

Спроектировать машину состояний/переходов (МСП) и т.п. Потом это запрограммировать.

МСП ты спроектируешь, с программированием - помогу, если нужно.

Я попробовал, набросал кусочек проги по простому, но без МСП достаточно муторно/мутно и всё плохо получается :) потому дальше не стал маяться.

Или я не правильно понял про PLC & PKC?

 

yuriy m
Offline
Зарегистрирован: 23.12.2012

kisoft пишет:
Или я не правильно понял про PLC & PKC?

PLC - Программируемый логический контроллер. К примеру  http://simatek.by/assets/images/slides/4.jpg   http://www.pjboner.com/wp-content/uploads/wpsc/product_images/rx3i.jpg  У них может быть 200входов + 200 выходов ,аналоговые ,цифровые,шим,поддержка куча разных протоколов связи между собой и т.д.

Для управления разного промышленного и не только оборудования  http://www.neoteo.com/images/Cache/C1B3x590y590.jpg  ( в принцепе ограничения применения нету...)

 

 

LAD- он же РКС (язык технологического программирования), оперирующий типовыми элементами Релейно-Контактной Схемы (логики) и автоуправления. Имеет эффективные средства для программирования математических функций.

FBD-он же ФДБ – язык функционально-блоковых диаграмм. Обеспечивает создание исходного текста при помощи графического редактора с использованием графических образов функциональных блоков. Установка взаимосвязей между блоками осуществляется простым указанием их входов/выходов.

 

 

Алгоритм выключения при разряженном аккумуляторе написал,но как написать тоже самое на millis ?

int batt = A0;     //аналоговый вход(пин) контроля напряжения борт сети
int power= A1;     // аналоговый вход(пин) контроля напряжения материнки
 int acc = 2;       // цифровой вход(пин) ключа зажигания

int mosfet1=6;   // цифровой выход(пин) ключа1
int mosfet2=13;  // цифровой выход(пин) ключа2
int mosfet3=7;    // цифровой выход(пин) ключа3

void setup()
{
 
 pinMode(mosfet1,OUTPUT);          //ставим как выход
 pinMode(mosfet2,OUTPUT);          //ставим как выход
 pinMode(mosfet3,OUTPUT);          //ставим как выход
 digitalWrite (mosfet2,HIGH);      //для наглядности выключения - включаем
 digitalWrite (mosfet3,HIGH);      //для наглядности выключения - включаем
}
  
void loop()
{
int battVal = analogRead(batt);    //присваеваем переменной значение
int powerVal = analogRead(power);  //присваеваем переменной значение
 
 if (battVal < 600)                //если аккумулятор разряжен
{
  
 if  (powerVal > 600)              //если напряжение на материнке присутствует
  {
 digitalWrite (mosfet3,LOW);       //выключаем mosfet3
 digitalWrite (mosfet1,HIGH);      // выключаем материнку
 delay(1000);
 digitalWrite (mosfet1,LOW);
 delay (5000);                     //должно быть 120000 (долго ждать...)
 digitalWrite (mosfet2,LOW);       // все обесточиваем
  }
 else 
 {
   digitalWrite (mosfet2,LOW);     // все обесточиваем
 }}}
 

 

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

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

enum SensorType
{
  stAnalog = 0,
  stDigital
};

struct State
{
  byte    pin;
  word    state;
  byte    digitalState;
  unsigned long  state_time;
  word    type;
  word    low_level;
  word    high_level;
};

enum SensorName
{
  snBattery = 0,
  snVPower,
  snButton
};

State sensor_array[] =
{
  { A0, 0,   LOW, 0, stAnalog, 800, 810 },
  { A1, 0,   LOW, 0, stAnalog, 800, 810 },
  { 0,  LOW, LOW, 0, stDigital, LOW, HIGH }
};

enum DriverName
{
  dnMOSFET1 = 0,
  dnMOSFET2
};

State driver_array[] =
{
  { 6,  LOW, 0 },
  { 13, LOW, 0 }
};

#define sensor_array_kol ( sizeof( sensor_array ) / sizeof( State ) )
#define driver_array_kol ( sizeof( driver_array ) / sizeof( State ) )

unsigned long g_last_millis;

byte getDigitalState( word p_state, word p_index )
{
  byte l_dstate = sensor_array[ p_index ].digitalState;
  if( p_state < sensor_array[ p_index ].low_level )
  {
    l_dstate = LOW;
  }
  else if( p_state > sensor_array[ p_index ].high_level )
  {
    l_dstate = HIGH;
  }
  return l_dstate;
}

void setup()
{
  /* Задать режимы пинам */
  for( word i = 0; i < driver_array_kol; ++i )
  {
    pinMode( driver_array[ i ].pin, OUTPUT );
    pinMode( driver_array[ i ].pin, driver_array[ i ].state );
  }
  /* Читаем начальное состояние системы */
  g_last_millis = millis();
  for( word i = 0; i < sensor_array_kol; ++i )
  {
    if( stAnalog == sensor_array[ i ].type )
    {
      sensor_array[ i ].state = analogRead( sensor_array[ i ].pin );
      sensor_array[ i ].digitalState = getDigitalState( sensor_array[ i ].state, i );
    }
    else
    {
      sensor_array[ i ].digitalState = sensor_array[ i ].state = digitalRead( sensor_array[ i ].pin );
    }
    sensor_array[ i ].state_time = 0;
  }
}

/*
 * Читаем состояние сенсоров
 */
boolean getSensorsState()
{
  boolean l_ret = false;
  unsigned long l_current_millis = millis();
  for( word i = 0; i < sensor_array_kol; ++i )
  {
    word l_state = analogRead( sensor_array[ i ].pin );
    if( stAnalog == sensor_array[ i ].type )
    {
      byte l_dstate = getDigitalState( l_state, i );
      /* Новое состояние отличается */
      if( l_dstate != sensor_array[ i ].digitalState )
      {
        sensor_array[ i ].state_time = 0;
        l_ret = true;
      }
      else
      {
        sensor_array[ i ].state_time += l_current_millis - g_last_millis;
      }
      sensor_array[ i ].state = l_state;
    }
    else  // stDigital
    {
      word l_state = digitalRead( sensor_array[ i ].pin );
      if( l_state != sensor_array[ i ].state )
      {
        sensor_array[ i ].state_time = 0;
        l_ret = true;
      }
      else
      {
        sensor_array[ i ].state_time += l_current_millis - g_last_millis;
      }
      sensor_array[ i ].state = l_state;
    }
  }
  return l_ret;
}

#define SECS_30   (30*1000)
#define SECS_120  (120*1000)

/* Проверка состояния сенсоров и переключение исполнительных переключателей */
void checkStates()
{
  /* Проверяем состояние сенсоров и подача сигнала исполнительным механизмам */

  byte l_button_state = sensor_array[ snButton ].state;
  if( LOW == sensor_array[ snBattery ].digitalState )
  {
    if( sensor_array[ snBattery ].state_time > SECS_30 )
    {
      /* Игнорируем её состояние */
      l_button_state = LOW;
    }
  }
  if( LOW == sensor_array[ snVPower ].state && sensor_array[ snVPower ].state_time > SECS_120 )
  {
    digitalWrite( driver_array[ dnMOSFET2 ].pin, LOW );
  }
  else
  {
    digitalWrite( driver_array[ dnMOSFET2 ].pin, HIGH );
  }
}

void loop()
{
  /* Считать состояние сенсоров */
  getSensorsState();
  checkStates();
  /* Здесь не уверен, что это нужно. Задержку добавить по месту или совсем убрать */
  delay( 100 );
}

Диаграмма состояний - это классно. От этого и нужно плясать.

Главное в следующем, loop - это можно рассматривать как бесконечный цикл (условно, разумеется, потому что при выходе из loop еще кое что происходит). Поэтому нужно в каждом цикле проверять состояния сенсоров/датчиков, считать время состояния сенсоров (это уже есть) и управлять исполнительными устройствами. В настоящем варианте нет именно обработки задержек включения мосфетов и т.п. Остальное вроде должно работать. Все пины в начале программы описаны, задержки в середине исходника, возможно где то не совсем такие, какие надо. Но сегодня уже спать нужно :) Итак из-за Ардуины недосып, который у меня уже хронический, перерос даже не знаю во что, сон каждый день по 5,5 часов. Какой гад придумал Ардуино! :)

Возможно для тебя этот код будет сложноватым, чтобы с этим разбираться, потому сильно не заморачивайся, хотя посмотреть - посмотри. Если что объясню. А может и народ подтянется, кому интересно. Да, комментарии в коде есть, так что может быть будет проще разбираться.

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

UPD: Да, никаких delay в коде не нужно делать, здесь всё основано на том, что прошло какое то время с моемнта срабатывания сенсора, аналогично нужно сделать, при включении мосфета1, зафискировать время и также как для сенсоров, считать, как долго он включен, потом переключить второй мосфет или что там по алгоритму. Кстати, совсем немного прогу не доделал, но теперь уже только в выходные.

leshak
Offline
Зарегистрирован: 29.09.2011

Без delay() нужно почитать вот этот пример:

http://arduino.ru/tutorials/BlinkWithoutDelay

Или вот еще примерчик накорябал. Включаем диод по нажатию кнопки (или какому-то другому вашему условию) на 5 sec без delay()

 

#define BUTTON_PIN 2 // пин кнопки, через резистор поднянут к земле. HIGH - Означает "нажата"
#define LED 13
unsigned long ledTurnOnTime=0; // тут будем хранить время когда нажали кнопку
void setup(){
  pinMode(LED,OUTPUT);
}

void loop(){
   if(ledTurnOnTime==0 && digitalRead(LED)==HIGH){ // если диод еще не включен (признаком этого является то что время "влюкчение" - никакое) и нажата кнопка
       digitalWrite(LED,HIGH); // включаем диод
       ledTurnOnTime=millis(); // запоминем когда мы его включили
   };
   
    
   if(ledTurnOnTime>0 && (millis()-ledTurnOnTime>5000)){ // если у нас есть какой-то конкретное "время включения", а не "дефолтный ноль", то вычисляем сколько прошло милесекунд с того момента. Если больше 5 sec, то
      digitalWrite(LED,LOW); // выключаем диод
      ledTurnOnTime=0; // обнуляем "время включения". Это наш признак что "отсчет горения" не ведется. 
      
   }
   
}