Управление PWM с ПК

hugonavy
Offline
Зарегистрирован: 21.10.2012

Здравствуйте! Прошу помощи, али совета.

Задумка следующая: отправлять на ардуино величину (0-255), на которую что-то будет включаться. Пока, на стадии тестирования, работаю со светодиодиком и управляющие команды через "монитор порта".

Проблема: при отправке нескольких символов в одном "письме", ардуино воспринимает каждый символ отдельно.

int ledPin = 9;    //испытуемый
int val = 0;       //тут храним команду

void setup() {
 Serial.begin(9600); 
 pinMode(ledPin, OUTPUT); 
}

void loop() {
 while(Serial.available() == 0);   //ждем, пока не поступит сигнал
 if (Serial.available() > 0 && Serial.available() < 255) { 
   /*Если есть сигнал, он больше нуля и меньше 255, считываем его в переменную
   и выводим ее обратно для проверки, а так же зажигаем на эту
   величину светодиод*/
  val = Serial.read() - '0';
  Serial.println(val);
  analogWrite(ledPin, val);
 }
 //Иначе говорим об ошибке
 else Serial.println("Error");
}

В итоге светодиод еле-еле что-то выдает, а на максимум не зажечь, т.к. 255 воспринимается как 2, потом 5, потом еще раз 5 (во всяком случае я так понял).

Вопрос: как считывать сообщение как одно целое?

memfise
Offline
Зарегистрирован: 30.06.2012

конечно, может не в тему, но вдруг пригодиться. вот мой пример включения определенного реле (код урезан, постарался оставить только нужные вещи для понимания чего я делал). В кратце: код сделан по запросу - если в мониторе - "Z" просим ввести число(таких чисел может быть столько сколько разрядов в числе необходимом для ввода), после ввода числа/чмсел переприсваиваем переменные и в следующей функции переприсваиваем номеру реле входящий символ из монитора порта, включаем реле по его номеру введенному в мониторе в итоге. Ну вот как то так. На применение именно этого алгоритма не настаиваю,но у меня при моих 10 реле все это работает, по аналогии можно сделать ввод чисел (например 12, 155, 1009 и др)

int vizov_rele_on = 0; // флажок для включения вызова реле
int simvol_wait_on = 0; // ожидающий символ для включения
int simvol_rele_on = -1; // символ для включения (по-умолчанию его нет)
byte rele_id_on; // идентификатор номера реле (для включения реле по его номеру)

// чтение команд с Serial
void readSerialCommands(){
  if (Serial.available() > 0 ){ // посылаем запрос только после появления информации на порту
    incomingByte = Serial.read(); //читаем данные порта
    if (incomingByte == 'z' || incomingByte == 'Z'){ // включение определенного реле по его номеру
      Serial.println("*** Enter number rele to ON ***");
      incomingByte = 0;
      vizov_rele_on = 1;
      simvol_wait_on = 1;
      Serial.flush();
    }
    /* --------------  */    // если есть ожидающий символ включения
    if (incomingByte && simvol_rele_on == -1 && simvol_wait_on == 1 && vizov_rele_on == 1){
      rele_id_ON(); // включаем определенное реле
    }
  }
}
/* ------------------------------------------------------------------------  */
// включаем определенное реле
void rele_id_ON(){
  rele_id_on = incomingByte - 48; 
  if (rele_id_on > 0 && rele_id_on <= rele_count){ 
    incomingByte = 0; 
    Serial.flush();
    simvol_rele_on = rele_id_on;
    digitalWrite(pin_rele[rele_id_on - 1], HIGH);
    Serial.print("RELE "); Serial.print(rele_id_on); Serial.println(" ON");
  }
  else
    Serial.println("Inkorect number RELE");
  vizov_rele_on = 0; // обнуляем вызов реле
  simvol_rele_on = -1; // обнуляем символ
  simvol_wait_on = 0; // обнуляем ожидание символа
}



 

ich
Offline
Зарегистрирован: 10.06.2012

Почитайте вот в эту сторону.

Serial.parseInt()

 

hugonavy
Offline
Зарегистрирован: 21.10.2012

 Спасибо ich. Пока искал примеры указанной функции, нашел решение проблемы :D Может кому пригодится - http://arduino.ru/forum/programmirovanie/perevod-prinyatogo-simvola-s-com-porta-v-chislo

Там в примере задается командная буква и время, которое светодиод должен светить. Я немного модернизировал:

int ledF = 9;
int ledR = 8;
long b = 0;
boolean End, minus, f, r = 0;
 
void setup() {               
  Serial.begin(9600);   
}
 
void loop() {
 
  while(Serial.available()){
    delay(2);
    char a = Serial.read();
    if(a == 45){
      minus = 1;
    }
    if(a == 'f'){
      f = 1;
    }
    if(a == 'r'){
      r = 1;
    }
    b *= 10;
    b += cifra(a);   
    End = 1;
  }
 
  if(End == 1){
    if(minus == 1){
      b = -b;
      minus = 0;
    }
    Serial.println(b, DEC);
    End = 0;    
  }
 
  if(f){
    analogWrite(9, b);   // set the LED on
    delay(1000);              // wait for a second
    f = 0; 
  }
 
  if(r){
    analogWrite(8, b);   // set the LED on
    delay(1000);              // wait for a second
    r = 0; 
  }
 
  b = 0;
 
}
 
int cifra(char a){
  int b = 0;
  if(a >= 48 && a <= 57){
    b = a - 48;    
  }
   return b; 
}

Сыренько еще, конечно, но однако работает. В монитор пишем командную букву "f" (или "r") и число (0-255), задавая яркость светодиода.

maksim
Offline
Зарегистрирован: 12.02.2012

 

#define ledPin 9   //испытуемый

void setup() {
  Serial.begin(9600); 
  pinMode(ledPin, OUTPUT); 
}

void loop() {
  if(Serial.available()) analogWrite(ledPin, Serial.parseInt());
}

Только вот parseInt() медленная, а точнее она ждет прежде чем вернуть значение.

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

Можно все сложить в String и воспользоваться методом toInt - смотрите стандартный пример StringToInt 

hugonavy
Offline
Зарегистрирован: 21.10.2012

 Представленный вариант как раз-таки отлично подходит для поставленной мной задачи. Возник попутный вопрос: когда поступившая команда выполнена, как прервать программу и вернуться к циклу ожидания сигнала? return?

Ну и раз уж начал: как я понимаю, цикл с Serial.Available полностью тормозит программу и пока команда из сериал порта не поступит - ничего не произойдет. Т.е., допустим, сигнал с датчика может не поступить вовремя (а если обработку датчиков сделать в конце и первый абзац глаголит истину, то сигнала с датчика я и вовсе не получу никогда). Тут с прерываниями надо что-то делать? (просто уже много прочитал про прерывания на ардуино, но так и не понял что это. Вернее примерно понял, но как это реализовать для конкретной задачи - не ясно)

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

 цикл с Serial.Available полностью тормозит программу только в случае, если Вы примените что-нибудь в стиле parseInt. Если просто будете читать символы в буфер - то ничего не тормозит, потому что выполняется только при наличии необработаных символов в буфере приема. Изучите внимательнее пример. Снаружи цикла с Serial.Available можно делать все что угодно.

maksim
Offline
Зарегистрирован: 12.02.2012

 Вот это все равно подтормаживает 

 while(Serial.available()){
    delay(2);
    char a = Serial.read();
......

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

 if(Serial.available() == 10){
    while(Serial.available()){
       char a = Serial.read();
......

где 10 - длинна сообщения, но тут есть ограничение - размер буфера.

hugonavy
Offline
Зарегистрирован: 21.10.2012

 Нет, видимо я не так выразился. Будем опираться на код из сообщения №3. При включении, ардуино начинает выполнять программу. Как я понимаю, программа доходит до 12 строки и "останавливается", пока не будет начата передача команд. Т.о., пока я не введу что-нибудь в порт, дальше ничего не происходит, программа крутится в цикле while. Так?

Далее, допустим, у меня много различных команд, но я не хочу пролистывать весь "loop", после выполнения, скажем, первого условия. Возможно ли принудительно на определенном моменте остановить работу раздела "void loop" и вернуться к его началу (к строчке 10)?

Идем далее. Если условие, поставленное в предыдущем абзаце реализуемо, то при определенном стечении обстоятельств можно самих себя завести в тупик)) Если после всех условий на выполнение команд разместить обработку данных с датчиков, то данные команды в принципе выполняться никогда не будут, т.к. пока команды нет - программа вертится в 12 строчке, а когда команда есть, она выполняется и возвращает процесс в начало в строку 10. Допустим, датчик всего 1 и, если я правильно понял смысл "прерываний в ардуино", я могу в "void setup" задать команду-прерывание, которая автоматически, никак не влияя на ход выполнения основной программы, будет с определенным интервалом опрашивать датчик и быстренько отправлять данные в сериал порт.

Извиняюсь за фантазии, просто хочется знать, правильно ли я понимаю логику работы МК.

2 maksim: это полезная информация, спасибо!

maksim
Offline
Зарегистрирован: 12.02.2012

  

maksim
Offline
Зарегистрирован: 12.02.2012

hugonavy пишет:

 Нет, видимо я не так выразился. Будем опираться на код из сообщения №3. При включении, ардуино начинает выполнять программу. Как я понимаю, программа доходит до 12 строки и "останавливается", пока не будет начата передача команд. Т.о., пока я не введу что-нибудь в порт, дальше ничего не происходит, программа крутится в цикле while. Так?

Нет, не так этот цикл будет выполняться только если в буфере что-нибудь есть, если ничего нет то программа "пройдет мимо" всего тела цикла не заходя в него (с 12 по 52 строки). Если есть 1 байт и в течении 2 миллисекунд ничего в буфер не пришло, то вся программа "остановаится" на 2 миллисекунды, если в буфер пришло 2 байта, то программа "остановится" на 4 миллисекунды и т.д.

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

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

Читайте про условные переходы и рисуйте алгоритмы