Управление PWM с ПК
- Войдите на сайт для отправки комментариев
Вс, 21/10/2012 - 10:03
Здравствуйте! Прошу помощи, али совета.
Задумка следующая: отправлять на ардуино величину (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 (во всяком случае я так понял).
Вопрос: как считывать сообщение как одно целое?
конечно, может не в тему, но вдруг пригодиться. вот мой пример включения определенного реле (код урезан, постарался оставить только нужные вещи для понимания чего я делал). В кратце: код сделан по запросу - если в мониторе - "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; // обнуляем ожидание символа }Почитайте вот в эту сторону.
Serial.parseInt()
Спасибо 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), задавая яркость светодиода.
#define ledPin 9 //испытуемый void setup() { Serial.begin(9600); pinMode(ledPin, OUTPUT); } void loop() { if(Serial.available()) analogWrite(ledPin, Serial.parseInt()); }Только вот parseInt() медленная, а точнее она ждет прежде чем вернуть значение.
Можно все сложить в String и воспользоваться методом toInt - смотрите стандартный пример StringToInt
Представленный вариант как раз-таки отлично подходит для поставленной мной задачи. Возник попутный вопрос: когда поступившая команда выполнена, как прервать программу и вернуться к циклу ожидания сигнала? return?
Ну и раз уж начал: как я понимаю, цикл с Serial.Available полностью тормозит программу и пока команда из сериал порта не поступит - ничего не произойдет. Т.е., допустим, сигнал с датчика может не поступить вовремя (а если обработку датчиков сделать в конце и первый абзац глаголит истину, то сигнала с датчика я и вовсе не получу никогда). Тут с прерываниями надо что-то делать? (просто уже много прочитал про прерывания на ардуино, но так и не понял что это. Вернее примерно понял, но как это реализовать для конкретной задачи - не ясно)
цикл с Serial.Available полностью тормозит программу только в случае, если Вы примените что-нибудь в стиле parseInt. Если просто будете читать символы в буфер - то ничего не тормозит, потому что выполняется только при наличии необработаных символов в буфере приема. Изучите внимательнее пример. Снаружи цикла с Serial.Available можно делать все что угодно.
Вот это все равно подтормаживает
while(Serial.available()){ delay(2); char a = Serial.read(); ......но эта задержка не просто так сделана, а сделана для того, что бы байты успевали приходть в буфер и цикл выполнялся пока не пришло все сообщение. Еесли известна длинна сообщения, то можно избавиться и от этой задержки
if(Serial.available() == 10){ while(Serial.available()){ char a = Serial.read(); ......где 10 - длинна сообщения, но тут есть ограничение - размер буфера.
Нет, видимо я не так выразился. Будем опираться на код из сообщения №3. При включении, ардуино начинает выполнять программу. Как я понимаю, программа доходит до 12 строки и "останавливается", пока не будет начата передача команд. Т.о., пока я не введу что-нибудь в порт, дальше ничего не происходит, программа крутится в цикле while. Так?
Далее, допустим, у меня много различных команд, но я не хочу пролистывать весь "loop", после выполнения, скажем, первого условия. Возможно ли принудительно на определенном моменте остановить работу раздела "void loop" и вернуться к его началу (к строчке 10)?
Идем далее. Если условие, поставленное в предыдущем абзаце реализуемо, то при определенном стечении обстоятельств можно самих себя завести в тупик)) Если после всех условий на выполнение команд разместить обработку данных с датчиков, то данные команды в принципе выполняться никогда не будут, т.к. пока команды нет - программа вертится в 12 строчке, а когда команда есть, она выполняется и возвращает процесс в начало в строку 10. Допустим, датчик всего 1 и, если я правильно понял смысл "прерываний в ардуино", я могу в "void setup" задать команду-прерывание, которая автоматически, никак не влияя на ход выполнения основной программы, будет с определенным интервалом опрашивать датчик и быстренько отправлять данные в сериал порт.
Извиняюсь за фантазии, просто хочется знать, правильно ли я понимаю логику работы МК.
2 maksim: это полезная информация, спасибо!
Нет, видимо я не так выразился. Будем опираться на код из сообщения №3. При включении, ардуино начинает выполнять программу. Как я понимаю, программа доходит до 12 строки и "останавливается", пока не будет начата передача команд. Т.о., пока я не введу что-нибудь в порт, дальше ничего не происходит, программа крутится в цикле while. Так?
Нет, не так этот цикл будет выполняться только если в буфере что-нибудь есть, если ничего нет то программа "пройдет мимо" всего тела цикла не заходя в него (с 12 по 52 строки). Если есть 1 байт и в течении 2 миллисекунд ничего в буфер не пришло, то вся программа "остановаится" на 2 миллисекунды, если в буфер пришло 2 байта, то программа "остановится" на 4 миллисекунды и т.д.
По поводу остального почитайте Программирование про прерывания и условные операторы, есть еще такая штука как прерывание по таймеру, можете воспользоваться готовой библиотекой, здесь где-то недавно обсужналось.
Читайте про условные переходы и рисуйте алгоритмы