обмен данными с пк

iopq
Offline
Зарегистрирован: 05.07.2016

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

сейчас у меня вот так

void loop() {

if (Serial.available() > 0) {
    Serial.readBytes(data, 10);   
  
if(dataFromApp[0] == 0x47){
//тут делаем что-то полезное
}

}
}

и отправка со стороны компьютера -

 @IBAction func send(_ sender: Any) {
    let data = Data([0x47]) 
    self.serialPort?.send(data)
 }

 

но как я понимаю это не корректное решение

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

iopq пишет:

но как я понимаю это не корректное решение

Да, хрен же его знает. Задача не озвучена, а значит ХЗ подходит решение к ней или нет.

iopq
Offline
Зарегистрирован: 05.07.2016

задача- принять от ардуино несколько байт, обработать их на стороне компьютера и вернуть ардуино несколько байт (до 20)

при этом компьютер не знает когда и сколько данных поступит и ардуино не знает как долго компьютер будет с ними работать и вернет ответ

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

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

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

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

в качестве примера посмотрите библиотеку VirtualWire

iopq
Offline
Зарегистрирован: 05.07.2016

с момента нажатия на кнопку до действия микроконтроллера проходит около секунды. это нормально? а если его занять обработкой прерывания значит и реакции не дождусь если она будет в этот момент. есть возможность обнаружения поступления данных в таком случае? или они в буфер в любом случае сохраняются (64 кб вроде) и можно после обработки прерывания просто прочитать его?

asam
asam аватар
Offline
Зарегистрирован: 12.12.2018

iopq пишет:

с момента нажатия на кнопку до действия микроконтроллера проходит около секунды. это нормально?

Это зависит от того как программы на обоих сторонах написаны. Ардуино может и быстрее.

Цитата:
а если его занять обработкой прерывания значит и реакции не дождусь если она будет в этот момент. есть возможность обнаружения поступления данных в таком случае? или они в буфер в любом случае сохраняются (64 кб вроде) и можно после обработки прерывания просто прочитать его?

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

iopq
Offline
Зарегистрирован: 05.07.2016

если читать только 1 байт, то реакция моментальная. если читать 10 байт, то реально секунда уходит. 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

asam пишет:

iopq пишет:

64 кб

В буфере сохраняются. 

Уверены?

iopq
Offline
Зарегистрирован: 05.07.2016

нет. конечно 64 байта. 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

iopq пишет:

если читать только 1 байт, то реакция моментальная. если читать 10 байт, то реально секунда уходит. 

Не, ну если байты передаются с интервалом в delay(100), то конечно!

iopq
Offline
Зарегистрирован: 05.07.2016
void loop() {

if (Serial.available() > 0) {
    Serial.readBytes(data, 10);   
  
if(data[0] == 0x47){
  digitalWrite(13, !digitalRead(13));
}

}
}

нет же никаких делеев

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

iorq, Вы значение слова "передаются" понимаете?

Или, может, тот факт, что байты не могут быть приняты раньше, чем они будут переданы, вызывает у Вас удивление?

 

PS. И вообще, приведенный Вами код может работать совсем не так, как Вы рассчитываете.

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

Дак это код для примера, в настоящем-то их полно.

iopq
Offline
Зарегистрирован: 05.07.2016

совсем запутался. 

void loop() {

if (Serial.available() > 0) { //если в буфере нет данных ничего не делаем иначе -
    Serial.readBytes(data, 10);   читаем из буфера в массив 10 байт
  
if(data[0] == 0x47){ //если 1 байт = 0х47
  digitalWrite(13, !digitalRead(13)); //вкл или выкл светодиод
}

}
}

byte data[11];

что тут может работать не так?

 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

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

iopq
Offline
Зарегистрирован: 05.07.2016

мк в бесконечном цикле проверяет есть ли что прочитать из буфера. если Serial.available() вернула > 0 значит что то пришло. зайдем в условие и прочитаем в массив Serial.readBytes(data, 10);

 

единственное конечно может тут кроется ошибка а точнее мое не понимание работы чтения данных. я представляю буфер как массив на 64 байта т. е за раз передать больше этого не получится. а если я передаю 1 байт то в буфере будет находится 0х47, 0xFF ... 0xFF

либо там есть таймауты и если более байты не следуют то выход по таймауту 100 мкс и так 9 раз

 

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

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

iopq пишет:

void loop() {

if (Serial.available() > 0) { //если в буфере нет данных ничего не делаем иначе -
    Serial.readBytes(data, 10);   читаем из буфера в массив 10 байт

byte data[11];

что тут может работать не так?

ДО ХРЕНА ЧЕГО!

Например, ответьте на вопрос, кто, когда и при каких обстоятельствах сказал Вам, что пришло 10 байтов? В строке №3 Вы проверяете, что что-то пришло. Может 1 байт, может 2, а может 100500. С чего Вы взяли, что пришло 10 и их пора читать?

iopq
Offline
Зарегистрирован: 05.07.2016

как определить сколько байт пришло?

asam
asam аватар
Offline
Зарегистрирован: 12.12.2018

iopq пишет:

как определить сколько байт пришло?

a) Читать по одному.

б) При передачи пакетов разной длины вначале передавать размер пакета. 

iopq
Offline
Зарегистрирован: 05.07.2016

есть у меня строка -

[0x0A, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x1A, 0x1B, 0x1C]

где первый байт это ее длина. если я делаю так -

void loop() {

if (Serial.available() > 0) {

 data[0] = Serial.read();   

 Serial.write(data, 1);
 return; 

то я что отправляю то и получаю

как мне дальше делать?

asam
asam аватар
Offline
Зарегистрирован: 12.12.2018

iopq пишет:

есть у меня строка -

[0x0A, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x1A, 0x1B, 0x1C]

где первый байт это ее длина. если я делаю так -

void loop() {

if (Serial.available() > 0) {

 data[0] = Serial.read();   

 Serial.write(data, 1);
 return; 

то я что отправляю то и получаю

как мне дальше делать?

 

Ну что нибудь вроде



void loop() {

if (Serial.available() > 0) {

  byte len= Serial.read();   
  if(len) Serial.readBytes(data, len); 

  }
}

 

iopq
Offline
Зарегистрирован: 05.07.2016

спасибо. только нужно len-1 т. к 1 байт прочитан

asam
asam аватар
Offline
Зарегистрирован: 12.12.2018

iopq пишет:

спасибо. только нужно len-1 т. к 1 байт прочитан

Ну это как сообщение кодировать. Обычно передают количество последующих данных. Хотя по всякому бывает

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

iopq пишет:

мк в бесконечном цикле проверяет есть ли что прочитать из буфера. если Serial.available() вернула > 0 значит что то пришло. зайдем в условие и прочитаем в массив Serial.readBytes(data, 10);

 

единственное конечно может тут кроется ошибка а точнее мое не понимание работы чтения данных. я представляю буфер как массив на 64 байта т. е за раз передать больше этого не получится. а если я передаю 1 байт то в буфере будет находится 0х47, 0xFF ... 0xFF

либо там есть таймауты и если более байты не следуют то выход по таймауту 100 мкс и так 9 раз

 

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

Начнем с конца - ну т.е. Вы поняли, что та самая задержка в 1 с связана не с чтением нескольких байт, а как раз с тем, что читать нечего, а эта секунда - таймаут.

Далее: таймаут действует на всю команду. Т.е. функция ждет 1 с, если за это время она смогла принять 10 байт, - возвращает управление, если нет - выходит по таймауту. Один раз. Т.е. не по 0.1 с на байт, а 1 с - на все.

Ну и последнее: что будет находиться в буфере (кроме первого символа) мы не знаем.

iopq пишет:

как определить сколько байт пришло?

Читать документацию на те функции, которыми пользуетесь: https://www.arduino.cc/reference/en/language/functions/communication/serial/readbytes/

Кстати, можно и по-другому: if(Serial.available() >= 10)

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

iopq пишет:

как определить сколько байт пришло?

Прочитать, наконец, грёбанное описание функцию, которыми пользуетесь - http://arduino.ru/Reference/Serial/Available

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

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

Прочитать, наконец, грёбанное описание 

Какой ты сёдня злющий. :) 

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

DetSimen пишет:

Какой ты сёдня злющий. :) 

Дык, жизнь же она "такова, какова она есть, и больше никакова" :-(

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

asam пишет:

 

Ну что нибудь вроде



void loop() {

if (Serial.available() > 0) {

  byte len= Serial.read();   
  if(len) Serial.readBytes(data, len); 

  }
}

 

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

Я , обычно, использую два подхода:

1) если скорость лупа не важна, то можно и повисеть в ф-ии приема данных, пока не придет весь пакет

2) если скорость важна - то вычитавать только те байты, что уже пришли. При этом обязательно иметь признаки начала и конца пакета.

asam
asam аватар
Offline
Зарегистрирован: 12.12.2018

5N62V пишет:

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

Я , обычно, использую два подхода:

1) если скорость лупа не важна, то можно и повисеть в ф-ии приема данных, пока не придет весь пакет

2) если скорость важна - то вычитавать только те байты, что уже пришли. При этом обязательно иметь признаки начала и конца пакета.

Если мы передаем данные одним пакетом подряд и в начале шлем его длинну, то откуда возьмуться кракозябры? И задержки, кроме как на прием пакета, не будет. 10 байт на 115200 передаются за 1 миллисекунду. Ну если такая задержка неприемлима, тогда можно и побайтно.

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

asam пишет:

Если мы передаем данные одним пакетом подряд и в начале шлем его длинну, то откуда возьмуться кракозябры? И задержки, кроме как на прием пакета, не будет. 10 байт на 115200 передаются за 1 миллисекунду. Ну если такая задержка неприемлима, тогда можно и побайтно.

ну, во-первых, бодрейт не всегда 115200, а во-вторых тактовая частота не всегда 16МГц, в-третьих пакет бывает длиннее 10ти байт, поэтому кракозябры более чем возможны. Кстати по ай2си та же фигня.

можно вставить, конечно, while(Seial.available()< len);

Но это блокирующая фигня, и если пакет так и не придет, то проц так и останется висеть.

я иногда поступаю так:

 uint32_t timeOut = micros();
 while(Serial.available() < len){
if(micros() - timer > 3000){
return -1; // к примеру
}
}

 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

А кто мешает написать if(Serial.available() >= len)... ?

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

asam
asam аватар
Offline
Зарегистрирован: 12.12.2018

5N62V пишет:

 

ну, во-первых, бодрейт не всегда 115200, а во-вторых тактовая частота не всегда 16МГц, в-третьих пакет бывает длиннее 10ти байт, поэтому кракозябры более чем возможны. Кстати по ай2си та же фигня.

Кракозябры могут быть только если Serial.readBytes отваливается по таймауту. Если данные передаются одним пакетом то таймаута не будет. Даже 300 бод. И если и с перерывами, но  короче таймаута то не отвалится, а по дефаолту это целая секунда. 

А 16MГц, 8 или даже 1 тут совсем по барабану.

iopq
Offline
Зарегистрирован: 05.07.2016

все работает четко. 

а вообще я на ft232 буду переходить. напрягает выбор порта и устройству имя человеческое не дать. 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

iopq пишет:

все работает четко. 

а вообще я на ft232 буду переходить. напрягает выбор порта и устройству имя человеческое не дать. 

я что-то пропустил, в ft232 как и в cp2102 можно своё имя дать и драйвера свои под это имя  подсунуть?

iopq
Offline
Зарегистрирован: 05.07.2016

ua6em пишет:

iopq пишет:

все работает четко. 

а вообще я на ft232 буду переходить. напрягает выбор порта и устройству имя человеческое не дать. 

я что-то пропустил, в ft232 как и в cp2102 можно своё имя дать и драйвера свои под это имя  подсунуть?

драйвера же не по имени ставятся. cp2102 не знаю а в ft232 всю жизнь была такая возможность

iopq
Offline
Зарегистрирован: 05.07.2016

в stm32 с аппаратной поддержкой usb тоже можно задавать свои дескрипторы 

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

iopq пишет:

в stm32 с аппаратной поддержкой usb тоже можно задавать свои дескрипторы 

можно. Только смысла никакого. С левыми дескрипторами она перестанет определяться в винде, вместо этого будет "неизвестное устройство"

iopq
Offline
Зарегистрирован: 05.07.2016

я под macos. с serial port работать проще. с usb пока разбираюсь как хотя бы научиться управлять светодиодом на PB12

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

iopq пишет:

я под macos. с serial port работать проще. с usb пока разбираюсь как хотя бы научиться управлять светодиодом на PB12

не надо управлять светодиодом на PB12 через USB - этот пин при работе USB занят, управлять на нем ничем не получится. Выберите другой пин, кроме PB11-12

iopq
Offline
Зарегистрирован: 05.07.2016

вы уверены?

nik182
Offline
Зарегистрирован: 04.05.2015

То, что запрграмировать можно, ещё не значит что будет работать и поддерживать все возможные функции.

FoxJone
Offline
Зарегистрирован: 19.04.2019

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

Во-первых, кто вам сказал, что на приемник придет именно то, что вы послали с передатчика? Совершенно не факт, и даже с кабелем в 20 сантиметров бывает потери и искажения данных. Редко, но бывают.

Во-вторых, кто вам сказал, что если вы объявили в начале посылки 10 байт, то все эти 10 байт пришли? Передатчик мог взорваться на 5-м байте, метеорит перерубил кабель на 7-м...

Посему, в начале посылки объявляем длину посылки (не обязательно), в конце посылки ставим CRC (обязательно). Если посылка короткая, то тупую байтовую сумму, которая совсем не грузит MK, если посылка длинная - ну там какой нить CRC16/32 на ваш вкус. Сам я, если честно, использовал CRC16 редко, но там реально посылки были в сотни и тысячи байт. В посылках на 5-10-20 байт байтовой суммы вполне хватает. CRC32 не использовал никогда. 

Ну и как читать сериал. Читать по одному байту, это однозначно! Прочитал байт, отложил в буфер, читаешь следующий, если есть. Закончились байты - парсишь буфер на предмет что именно ты получил. Первым делом сверяешь CRC - не совпало, игнорим посылку или даем сигнал на повтор (как задумаешь). Потом уже обрабатываешь посылку на предмет осмысленных действий.

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

iopq пишет:

вы уверены?

Сорри, спутал с PA12

Тогда не понятно, в чем проблема ...

iopq
Offline
Зарегистрирован: 05.07.2016

b707 пишет:

iopq пишет:

вы уверены?

Сорри, спутал с PA12

Тогда не понятно, в чем проблема ...

 

проблема в том что я не знаю как работать с usb и примеров на swift нет. единственное что нашел это вот такой фреймворк https://github.com/Arti3DPlayer/USBDeviceSwift но пока не получается разобраться что отправляется данной функцией 

func getStatus() throws -> [UInt8] {
        //Getting device interface from our pointer
        guard let deviceInterface = self.deviceInfo.deviceInterfacePtrPtr?.pointee?.pointee else {
            throw STM32DeviceError.DeviceInterfaceNotFound
        }
        
        var kr:Int32 = 0
        let length:Int = 6
        var requestPtr:[UInt8] = [UInt8](repeating: 0, count: length)
        // Creating request
        var request = IOUSBDevRequest(bmRequestType: 161,
                                      bRequest: STM32REQUEST.GETSTATUS.rawValue,
                                      wValue: 0,
                                      wIndex: 0,
                                      wLength: UInt16(length),
                                      pData: &requestPtr,
                                      wLenDone: 255)
        
        kr = deviceInterface.DeviceRequest(self.deviceInfo.deviceInterfacePtrPtr, &request)
        
        if (kr != kIOReturnSuccess) {
            throw STM32DeviceError.RequestError(desc: "Get device status request error: \(kr)")
        }
        
        return requestPtr
    }

и где это ловить в stm32

пример отлично обнаруживает мой stm32