Собрал на макетных платах минимальную начинку электронной нагрузки и стал искать готовую программу для компьютера, которая имеет описанный протокол работы с железом. На GitHub нашелся проект Electronic_load_px100 у которого есть описание протокола.
Чтобы отладить работу с протоколом я взял свободную Arduino Leonardo и набросал для нее скетч, который эмулирует поведение электронной нагрузки. После отладки стал переносить код протокола в основной скетч уже моей электронной нагрузки (которая сделана на базе Freeduino 2009, аналога Arduino Duemilanove) и столкнулся с проблемой, что программа на компьютере больше не определяет что подключена электронная нагрузка.
ASRL/dev/ttyUSB0::INSTR SerialInstrument at ASRL/dev/ttyUSB0::INSTR probe <class 'pyvisa.errors.VisaIOError'> ('VI_ERROR_TMO (-1073807339): Timeout expired before operation completed.',) VI_ERROR_TMO (-1073807339): Timeout expired before operation completed. error reading bytes no answer ko No instruments found
Был уже поздний вечер и хотелось увидеть реальные показания и потому сразу попробовал поменять местами Freeduino и Arduino Leonardo - не помогло. Разные распиновки для I2C и не инициализируется периферия. Залил скетч эмулятора в обе платы и стал сравнивать их поведение - на Leonardo все просто работает, а на Freeduino - ошибка. Обратил внимание что отличаются названия портов - у Leonardo это /dev/ttyACM0, а у Freeduino это /dev/ttyUSB0, но почему на одном работает, а на втором - нет я не понял и пошел спать.
Сегодня расчехлил логический анализатор и прицепился к RX/TX, нескольким цифровым пинам и RESET. По коду набросал отдадочных "дерганий" пинами, которые подключены к анализатору. В начале setup() делается несколько "дрыганий" одним из пинов, а все остальные ставятся в низкий уровень. В конце setup() этот пин ставится в низкий уровень и поднимается пин для loop() и так далее. Так я могу понять когда начинается и завершается выполнение каждой секции кода и как они соотносятся с передачей данных через UART.
В результате получилось что при открывании порта выполнялся сброс (RESET в низком уровне) и сразу же передавались данные от программы на компьютере, но в этот момент еще даже не начиналось выполнение функции setup() в которой настраивается последовательный порт и данные уходили в /dev/null. Если добавить задержку 1.5 секунды между открыванием порта и отправкой данных, то все начинало работать.
Добрые люди подсказали что дело в сигнале DTR, который управляет сбросом на платах Arduino, а пауза перед setup() это загрузчик ожидает прошивку. Чтобы это не происходило нужно изменить программу и не использовать DTR при открывании порта. Вариант до которого я додумался сам - прицепить отдельный адаптер USB-UART в котором будут подключены только пины RX/TX и GND и это помогло, но пришлось перебрать три USB-UART адаптера (FT232, CH340 и PL2303). В отношении первых двух у меня сомнения относительно их оригинальности, а PL2303 покупался очень давно и скорее всего микросхема там оригинальная - вот с ним работает стабильно.
Далее были эксперименты с Pyserial чтобы работало без дополнительного адаптера. Я попробовал отключать DTR в настройках порта, но это не дало результата (DTR дергало несмотря на ser.dsrdtr = False
).
stty -F /dev/ttyUSB0 -hupcl
и после этого уже можно запускать программу и она нормально работает. А тут нашелся пример кода который делает аналогичное изменение в Python. В итоге тестовый код, который открывает порт и запрашивает значение напряжения:import serial import sys import termios cmd_get_voltage = b'\xb1\xb2\x11\x00\x00\xb6' if len(sys.argv) > 1: port = sys.argv[1] else: port = '/dev/ttyUSB0' f = open(port) attrs = termios.tcgetattr(f) attrs[2] = attrs[2] & ~termios.HUPCL termios.tcsetattr(f, termios.TCSAFLUSH, attrs) f.close() ser = serial.Serial() ser.port = port ser.baudrate = 9600 ser.bytesize = serial.EIGHTBITS ser.parity = serial.PARITY_NONE ser.stopbits = serial.STOPBITS_ONE ser.trscts = False ser.dsrdtr = False ser.timeout = None ser.open() ser.write(cmd_get_voltage) resp = ser.read(7) print(resp) ser.close()
В итоге написал скприт-обертку, который подключает virtualenv с зависимостями и выполняет нужную команду.
Комментариев нет:
Отправить комментарий