Емельянов Эдуард Владимирович (eddy_em) wrote,
Емельянов Эдуард Владимирович
eddy_em

Управление турелью HSFW от Edmund Optics

Появилось у нашей лаборатории желание создать фотометр для Zeiss-1000 с минимумом разработок железа/софта и т.п. Одной из частей фотометра будут две турели High Speed Filter Wheel. Как обычно, железо огороженное. В отличие от предшественника Intelligent Filter Wheel (у которого вполне нормальный последовательный интерфейс с описанным в документации протоколе), у этой железяки только мастдайнутый установщик и никакой документации о протоколах!

При подключении к компьютеру железяка создает устройство /dev/hidrawX. На основе примера работы с этими устройствами из ядра я попытался определить, как же им управлять. И, в принципе, основные вещи реализовал (установка в "дом", установка на заданную позицию), но без понятия, как реализовать сброс (в отличие от usbdevfs эмуляторов последовательных портов здесь простым ioctl'ом перезапустить соединение не вышло). А сброс очень важно реализовать, т.к. любая проблема в протоколе вызывает "глухоту" контроллера: он перестает реагировать на управляющие команды.

Кстати, в опытах выяснил интересную вещь: если в первую десятку регистров hidraw под ведром 3.12 писать ненулевые данные, ядро (случайным образом, кстати: можно десяток раз так сделать без последствий, а можно с первого раза попасть) уходит в глубокий kernel panic, перезагрузка после которого чревата десятиминутным fsck'ом (это еще хорошо, что у меня один винт и небольшой)!


Для ознакомления с возможностью управлять я использовал немного модифицированный пример из linux kernel:
/*
 * Hidraw Userspace Example
 *
 * Copyright (c) 2010 Alan Ott <alan@signal11.us>
 * Copyright (c) 2010 Signal 11 Software
 *
 * The code may be used by anyone for any purpose,
 * and can serve as a starting point for developing
 * applications using hidraw.
 */

/* Linux */
#include <linux/types.h>
#include <linux/input.h>
#include <linux/hidraw.h>

/*
 * Ugly hack to work around failing compilation on systems that don't
 * yet populate new version of hidraw.h to userspace.
 */
#ifndef HIDIOCSFEATURE
#warning Please have your distro update the userspace kernel headers
#define HIDIOCSFEATURE(len)    _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x06, len)
#define HIDIOCGFEATURE(len)    _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x07, len)
#endif

/* Unix */
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <usb.h>
#include <linux/usbdevice_fs.h>
/* C */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>

const char *bus_str(int bus);

void get_features_list(int fd){
	int i, f, res;
	char buf1[256];
	char buf[256];
	for(f = 0; f < 256; ++f){
	if(f == 1) continue;
	// Get Feature
		memset(buf, 0, 256);
		buf[0] = f; // Report Number
		res = ioctl(fd, HIDIOCGFEATURE(256), buf);
		if(res <= 0) continue;
		if(f && 0 == memcmp(buf, buf1, res)) continue;
		memcpy(buf1, buf, res);
		printf("HIDIOCGFEATURE %d returned: %d\n", f, res);
		printf("Report data (not containing the report number):\n\t");
		for (i = 0; i < res; i++)
			printf("0x%02hhx ", buf[i]);
		puts("\n");
	}
}

int main(int argc, char **argv)
{
	int fd;
	int i, res, desc_size = 0;
	char buf[256];
	struct hidraw_report_descriptor rpt_desc;
	struct hidraw_devinfo info;
	char *device = "/dev/hidraw0";

	if (argc > 1)
		device = argv[1];

	/* Open the Device with non-blocking reads. In real life,
	   don't use a hard coded path; use libudev instead. */
	fd = open(device, O_RDWR|O_NONBLOCK);

	if (fd < 0) {
		perror("Unable to open device");
		return 1;
	}

	memset(&rpt_desc, 0x0, sizeof(rpt_desc));
	memset(&info, 0x0, sizeof(info));
	memset(buf, 0x0, sizeof(buf));

	// Get Report Descriptor Size
	res = ioctl(fd, HIDIOCGRDESCSIZE, &desc_size);
	if (res < 0)
		perror("HIDIOCGRDESCSIZE");
	else
		printf("Report Descriptor Size: %d\n", desc_size);

	// Get Report Descriptor
	rpt_desc.size = desc_size;
	res = ioctl(fd, HIDIOCGRDESC, &rpt_desc);
	if (res < 0) {
		perror("HIDIOCGRDESC");
	} else {
		printf("Report Descriptor:\n");
		for (i = 0; i < rpt_desc.size; i++)
			printf("%02hhx ", rpt_desc.value[i]);
		puts("\n");
	}

	// Get Raw Name
	res = ioctl(fd, HIDIOCGRAWNAME(256), buf);
	if (res < 0)
		perror("HIDIOCGRAWNAME");
	else
		printf("Raw Name: %s\n", buf);

	// Get Physical Location
	res = ioctl(fd, HIDIOCGRAWPHYS(256), buf);
	if (res < 0)
		perror("HIDIOCGRAWPHYS");
	else
		printf("Raw Phys: %s\n", buf);

	// Get Raw Info
	res = ioctl(fd, HIDIOCGRAWINFO, &info);
	if (res < 0) {
		perror("HIDIOCGRAWINFO");
	} else {
		printf("Raw Info:\n");
		printf("\tbustype: %d (%s)\n",
			info.bustype, bus_str(info.bustype));
		printf("\tvendor: 0x%04hx\n", info.vendor);
		printf("\tproduct: 0x%04hx\n", info.product);
	}

//goto rd;
	printf("\n\nFEATURES BEFORE:\n");
	get_features_list(fd);

	memset(buf, 0xaa, 16);
	memset(buf+16, 0xee, 16);
	// Set Feature
	buf[0] = 20; // Report Number
	buf[1] = 1;
//	buf[2] = 3;
//	buf[3] = 3;
	res = ioctl(fd, HIDIOCSFEATURE(2), buf);
	if (res < 0)
		perror("HIDIOCSFEATURE");
	else
		printf("ioctl HIDIOCSFEATURE returned: %d\n", res);
	sleep(2);
	printf("\n\nFEATURES AFTER:\n");
	get_features_list(fd);
/*
rd:
	buf[0] = 22; // Report Number
	buf[1] = 1;
	res = ioctl(fd, HIDIOCSFEATURE(2), buf);
	if (res < 0)
		perror("HIDIOCSFEATURE");
	else
		printf("ioctl HIDIOCSFEATURE returned: %d\n", res);
	//Send a Report to the Device
	char *mode = "WSHOME\r\n";
	res = write(fd, mode, 8);
	if (res < 0) {
		printf("Error: %d\n", errno);
		perror("write");
	} else {
		printf("write() wrote %d bytes\n", res);
	}


	// Get a report from the device
	res = read(fd, buf, 16);
	if (res < 0) {
		perror("read");
	} else {
		printf("read() read %d bytes:\n\t", res);
		for (i = 0; i < res; i++)
			printf("%hhx ", buf[i]);
		puts("\n");
	}
*/
	close(fd);
	return 0;
}

const char *
bus_str(int bus)
{
	switch (bus) {
	case BUS_USB:
		return "USB";
		break;
	case BUS_HIL:
		return "HIL";
		break;
	case BUS_BLUETOOTH:
		return "Bluetooth";
		break;
	case BUS_VIRTUAL:
		return "Virtual";
		break;
	default:
		return "Other";
		break;
	}
}

Видно, что активных регистров немного, а по их содержимому можно примерно предположить что есть что.
Я начал с того, что писал число 2 в каждый из этих регистров (вот на первых у меня и вылезали kernel panic'и). Обнаружилось, что запись числа 1-5 в регистр 20 вызывает перемещение турели в указанную позицию, а вот если туда записать что-то вне допустимого диапазона, контроллер "глохнет".
Вот пример выдачи при перемещении из позиции 1 в позицию 2:
FEATURES BEFORE:
HIDIOCGFEATURE 0 returned: 15
Report data (not containing the report number):
    0x00 0x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

HIDIOCGFEATURE 2 returned: 14
Report data (not containing the report number):
    0x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

HIDIOCGFEATURE 10 returned: 6
Report data (not containing the report number):
    0x0a 0xff 0x00 0x00 0x01 0x00

HIDIOCGFEATURE 11 returned: 7
Report data (not containing the report number):
    0x0b 0x01 0x00 0x00 0x05 0x41 0x00

HIDIOCGFEATURE 20 returned: 3
Report data (not containing the report number):
    0x14 0x00 0x00

HIDIOCGFEATURE 21 returned: 3
Report data (not containing the report number):
    0x15 0x00 0x00

HIDIOCGFEATURE 22 returned: 14
Report data (not containing the report number):
    0x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

ioctl HIDIOCSFEATURE returned: 4

FEATURES AFTER:
HIDIOCGFEATURE 0 returned: 15
Report data (not containing the report number):
    0x00 0x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

HIDIOCGFEATURE 2 returned: 14
Report data (not containing the report number):
    0x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

HIDIOCGFEATURE 10 returned: 6
Report data (not containing the report number):
    0x0a 0xff 0x00 0x00 0x02 0x00

HIDIOCGFEATURE 11 returned: 7
Report data (not containing the report number):
    0x0b 0x01 0x00 0x00 0x05 0x41 0x00

HIDIOCGFEATURE 20 returned: 3
Report data (not containing the report number):
    0x14 0xff 0x00

HIDIOCGFEATURE 21 returned: 3
Report data (not containing the report number):
    0x15 0x00 0x00

HIDIOCGFEATURE 22 returned: 14
Report data (not containing the report number):
    0x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

Итак, десятый регистр — статусный. В его байте №4 содержится номер текущей позиции, байт №5 содержит код ошибки (при попытке выхода за диапазон это тройка), как его сбросить, я не знаю. Содержимое 11 регистра не меняется. Возможно, это идентификатор, который можно менять программно, нужно проверять (в мастдайке записать идентификатор и посмотреть, что будет). Регистры 0, 2 и 22 содержат информацию, которая меняется при записи данных (скажем, если в 21 регистр записать 32 байта данных, в этих регистрах будет совсем другое). Как я уже говорил, регистр 20 позволяет устанавливать колесо в требуемую позицию, а регистр 21 при записи в него чего угодно крутит колесо в позицию "дом" (в отличие от турелей IRBISа, здесь каждая позиция не кодируется, а просто отмечается одним магнитом; позиция "1" отмечена дополнительным магнитом).

Вот выдача при попытке перемещения в позицию 255:
FEATURES BEFORE:
HIDIOCGFEATURE 0 returned: 15
Report data (not containing the report number):
    0x00 0x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

HIDIOCGFEATURE 2 returned: 14
Report data (not containing the report number):
    0x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

HIDIOCGFEATURE 10 returned: 6
Report data (not containing the report number):
    0x0a 0xff 0x00 0x00 0x05 0x00

HIDIOCGFEATURE 11 returned: 7
Report data (not containing the report number):
    0x0b 0x01 0x00 0x00 0x05 0x41 0x00

HIDIOCGFEATURE 20 returned: 3
Report data (not containing the report number):
    0x14 0x00 0x00

HIDIOCGFEATURE 21 returned: 3
Report data (not containing the report number):
    0x15 0x00 0x00

HIDIOCGFEATURE 22 returned: 14
Report data (not containing the report number):
    0x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

ioctl HIDIOCSFEATURE returned: 2

FEATURES AFTER:
HIDIOCGFEATURE 0 returned: 15
Report data (not containing the report number):
    0x00 0x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

HIDIOCGFEATURE 2 returned: 14
Report data (not containing the report number):
    0x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

HIDIOCGFEATURE 10 returned: 6
Report data (not containing the report number):
    0x0a 0x00 0x00 0x00 0x00 0x03

HIDIOCGFEATURE 11 returned: 7
Report data (not containing the report number):
    0x0b 0x01 0x00 0x00 0x05 0x41 0x00

HIDIOCGFEATURE 20 returned: 3
Report data (not containing the report number):
    0x14 0xff 0x00

HIDIOCGFEATURE 21 returned: 3
Report data (not containing the report number):
    0x15 0x00 0x00

HIDIOCGFEATURE 22 returned: 14
Report data (not containing the report number):
    0x16 0x00 0x03 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

Как видим, код ошибки дублируется еще и в байте №2 регистра 22.
Запись 16-ти 0xaa и 16-ти 0xee в 21 регистр:

FEATURES BEFORE:
HIDIOCGFEATURE 0 returned: 15
Report data (not containing the report number):
    0x00 0x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

HIDIOCGFEATURE 2 returned: 14
Report data (not containing the report number):
    0x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

HIDIOCGFEATURE 10 returned: 6
Report data (not containing the report number):
    0x0a 0xff 0x00 0x00 0x01 0x00

HIDIOCGFEATURE 11 returned: 7
Report data (not containing the report number):
    0x0b 0x01 0x00 0x00 0x05 0x41 0x00

HIDIOCGFEATURE 20 returned: 3
Report data (not containing the report number):
    0x14 0x00 0x00

HIDIOCGFEATURE 21 returned: 3
Report data (not containing the report number):
    0x15 0x00 0x00

HIDIOCGFEATURE 22 returned: 14
Report data (not containing the report number):
    0x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

ioctl HIDIOCSFEATURE returned: 32

FEATURES AFTER:
HIDIOCGFEATURE 0 returned: 15
Report data (not containing the report number):
    0x00 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xee 0xee 0xee 0xee 0xee 0xee 0xee

HIDIOCGFEATURE 2 returned: 14
Report data (not containing the report number):
    0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xee 0xee 0xee 0xee 0xee 0xee 0xee

HIDIOCGFEATURE 10 returned: 6
Report data (not containing the report number):
    0x0a 0xff 0x00 0x00 0x01 0x00

HIDIOCGFEATURE 11 returned: 7
Report data (not containing the report number):
    0x0b 0x01 0x00 0x00 0x05 0x41 0x00

HIDIOCGFEATURE 20 returned: 3
Report data (not containing the report number):
    0x14 0x00 0x00

HIDIOCGFEATURE 21 returned: 3
Report data (not containing the report number):
    0x15 0xff 0x00

HIDIOCGFEATURE 22 returned: 14
Report data (not containing the report number):
    0x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

Видно также, что при изменении содержимого регистров 20 или 21 в ответе после этого изменения байты №1 соответствующего регистра и регистра 10 принимают значение 0xff.

На основе примера родилась такая простая управлялка:
/*
 * hsfw_rotate.c
 *
 * Copyright 2016 Edward V. Emelianov <eddy@sao.ru, edward.emelianoff@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301, USA.
 */

#include <linux/types.h>
#include <linux/input.h>
#include <linux/hidraw.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <usb.h>
#include <linux/usbdevice_fs.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>

#ifdef EBUG
void get_features_list(int fd){
	int i, f, res;
	char buf1[256];
	char buf[256];
	for(f = 0; f < 256; ++f){
	if(f == 1) continue;
	// Get Feature
		memset(buf, 0, 256);
		buf[0] = f; // Report Number
		res = ioctl(fd, HIDIOCGFEATURE(256), buf);
		if(res <= 0) continue;
		if(f && 0 == memcmp(buf, buf1, res)) continue;
		memcpy(buf1, buf, res);
		printf("HIDIOCGFEATURE %d returned: %d\n", f, res);
		printf("Report data (not containing the report number):\n\t");
		for (i = 0; i < res; i++)
			printf("0x%02hhx ", buf[i]);
		puts("\n");
	}
}
#endif

int main(int argc, char **argv){
	int fd;
	int i, res, pos = 0, getpos = 0;
	char buf[256] = {0};

	struct hidraw_devinfo info;
	char *device = "/dev/hidraw0";

	int getcurpos(){
		buf[0] = 10;
		res = ioctl(fd, HIDIOCGFEATURE(256), buf);
		if(res <= 0){
			perror("HIDIOCGFEATURE");
			return 0;
		}
		if(buf[0] != 10 || res != 6){
			fprintf(stderr, "Wrong answer format!");
			return 0;
		}
		if(buf[5]){
			fprintf(stderr, "Error in controller, code %d\n", buf[5]);
			return -buf[4];
		}
		return buf[4];
	}
	if(argc > 3 || (argc == 2 && strcmp(argv[1], "-h") == 0)){
		printf("usage: %s [/dev/hidrawX] [position] (position==0 - home)\n", argv[0]);
		return 1;
	}
	if(argc == 1) getpos = 1;
	else
	for(i = 1; i < argc; ++i){
		if(argv[i][0] == '/')
			device = argv[i];
		else{
			pos = atoi(argv[i]);
		}
	}
	if(pos < 0 || pos > 5){
		printf("Wrong position, set to HOME\n");
		pos = 0;
	}
	fd = open(device, O_RDWR|O_NONBLOCK);
	if(fd < 0){
		perror("Unable to open device");
		return 1;
	}
	memset(&info, 0x0, sizeof(info));
	//memset(buf, 0x0, sizeof(buf));

	res = ioctl(fd, HIDIOCGRAWINFO, &info);
	if(res < 0){
		perror("HIDIOCGRAWINFO");
		return 1;
	}else{
		if((info.vendor&0xffff) != 0x10c4 || (info.product&0xffff) != 0x82cd){
			fprintf(stderr, "Not Edmund Optics HSFW device!\n");
			return 1;
		}
	}

	if(getpos){
		if((i = getcurpos()) == 0) return 1;
		printf("Current position: %d\n", i);
		return 0;
	}
#ifdef EBUG
	printf("\n\nFEATURES BEFORE:\n");
	get_features_list(fd);
#endif

	//memset(buf, 0, sizeof(buf));

	if(pos == 0){
		buf[0] = 21;
		pos = 1;
	}else{
		if(getcurpos() == pos){
			printf("Already at position\n");
			return 0;
		}
		buf[0] = 20;
		buf[1] = pos;
	}
	res = ioctl(fd, HIDIOCSFEATURE(2), buf);
	if (res < 0)
		perror("HIDIOCSFEATURE");
	else
		printf("ioctl HIDIOCSFEATURE returned: %d\n", res);
	for(i = 0; i < 30; ++i){
		int cur = getcurpos();
		if(cur < 1){
			fprintf(stderr, "some error occured!\n");
			return 1;
		}
		if(cur != pos) usleep(100000);
		else break;
		printf("."); fflush(stdout);
	}
	printf("\nPosition %d %sreached\n", pos, (i != 30) ? "" : "not ");
#ifdef EBUG
	printf("\n\nFEATURES AFTER:\n");
	get_features_list(fd);
#endif
	close(fd);
	return 0;
}

Если ее запускать без параметров, она покажет текущую позицию. В параметрах можно указать имя файла, с которым нужно работать (в принципе, можно открывать его через интерфейс udev по VID/PID, а какое именно колесо нужно — уточнять по его коду), и/или номер позиции, в которую следует установить турель (0 — "дом", даже если счетчик позиций говорит, что мы уже там).


Вот таким жутким велосипедостроением приходится порой заниматься, потому как разработчики железяки закрысили описать протокол (заодно передаю привет Canon'овцам)! То ли еще будет с ПЗС…

UPD


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

К сожалению, "родной" софт для управления не запустился (выпадал еще до появления морды), но заработали тестовые интерфейсы на жабоскрипте и вбскрипте. Правда, там не было команд установки/чтения имен позиций на турели и установки буквенного обозначения самой турели, о них я прочитал позже в справке по API библиотеки.
Зато из протокола стала ясна логика работы.
Кстати, для сброса ошибок тоже есть библиотечная функция, позже, как компьютер освободится, я попытаюсь и ее проанализировать.
Итак, регистры, используемые при смене позиций турели.

Регистр 10 (0x0a)


Состоит из шести байт. Имеет вид 0a RD MH MP CP ER, где
  • RD - ready содержит 0xFF при готовности контроллера принять команду (если не готов - 0)

  • MH - move home содержит 0xFF во время перемещения в "дом", в остальное время - 0

  • MP - move position содержит 0xFF во время перемещения в указанную позицию, в остальное время - 0

  • CP - current position содержит номер текущей позиции (MH и MP должны быть равны нулю, иначе - позиция до начала перемещения)

  • ER - error код ошибки

Регистр 11 (0x0b)


Состоит из семи байт. Имеет вид 0b 01 00 00 05 WH 00. Назначение байт пока не выяснено, ясно лишь, что третий справа байт (05) — общее количество позиций в турели, а второй справа (WH) — символьный идентификатор колеса (в моем случае это 0x41 == 'A').

Регистр 20 (0x14)


Состоит из трех байт. В этот регистр пишем желаемую позицию (например, для перехода в позицию 2 пишем 14 02 00). После записи если команда принята первое чтение вернет 14 FF 00, последующие чтения вернут 14 00 00.

Регистр 21 (0x15)


Состоит из трех байт. Служит для установки турели в позицию "дом". Для этого пишем 15 00 00. Поллинг байта №1 имеет значения аналогично регистру 20, в случае ошибки в этом байте записывается ее код.

UPD-2


Я вспомнил, что у меня есть вендокомпьютер для работы с Шаком-Гартманном (да, каюсь: я уже 6 лет не могу собраться, и написать нормальное ПО без огораживания). С его помощью была проделана оставшаяся работа.

Итак, в данном случае вышло наоборот: почему-то жабкоскрипты глючили, а вот управлялка заработала (правда, скачанная с сайта — с диска тоже не пошла).
Т.к. управлялка позволяла менять имена, я сразу же занялся анализом трафика. И вот что получилось.

Регистр 22 (0x16)


Оказалось, что регистр 22 как раз и есть регистр для работы с EEPROM!
Вот только чтобы что-то сделать, нужно записать туда команду. Кроме номера регистра содержится еще 13 байт (формат чтения):
16 CMD ER ID NO 00 Name[8 bytes]
Кстати, анализ управляющих посылок показал, что вид передаваемых данных либо такой: 16 CMD 00 ID NO 00 Name[8 bytes], либо такой: 16 CMD ID NO Name[8 bytes] 00 00, т.е. похоже, что нули при передаче можно пихать куда угодно.
  • CMD - command команда может быть одной из следующих:
    • 1 сбросить все имена в "умолчательные",

    • 2 изменить имя позиции колеса,

    • 3 узнать имя позиции,

    • 4 изменить имя колеса,

    • 5 узнать имя колеса.


  • ER код ошибки (или 0, если все в порядке), если запросить имя несуществующего фильтра, вернет ошибку,

  • ID идентификатор колеса (ASCII буква от 'A' до 'H', устанавливается конфигурацией магнитиков на колесе),

  • NO номер позиции (1..5),

  • Name имя (колеса или позиции) - 8 символов ASCII, если имя короче, то справа добавляется нужное количество пробелов.


Забавная вещь получилась: до этого я думал, что символьный идентификатор определяет привод, а не само колесо. А в подсказке управлялки турелью было написано, что этот символ определяется пользователем при помощи установки магнитов на колесе. Я видел там свободные отверстия, но не нашел дополнительных датчиков Холла кроме двух основных. Буду искать.
В принципе, правильное решение: саму железку характеризует серийный номер, жестко вшитый во флеш-память МК, а колесо, которое меняется, характеризуется буквой + именем, хранящимся в памяти МК. Единственное ограничение — больше восьми разных колес МК запомнить не может. Но, в принципе, больше и не нужно!

Регистр 2


И напоследок — волшебный регистр 0x02. Я говорил, что запись ненулевых чисел в регистры <10 приводит к kernel panic. Так вот, если в регистр 02 записать число 0 (т.е. послать 2 байта: 02 00), то происходит сброс кода ошибки! Именно с этого и начался пакет опроса МК при включении "управлялки": как только она узрела ошибку в регистре 10, сразу же послала сброс.
Коды ошибок из справки:
  • 0 все в порядке,

  • 1 отсутствует силовое питание (12В; похоже, логика питается от USB),

  • 2 во время процедуры перемещения произошла ошибка (например, указана неправильная позиция),

  • 3 принят неправильный параметр (?),

  • 4 попытка переместить колесо "домой", когда оно еще движется,

  • 5 попытка переместить колесо в новое положение, пока оно еще движется,

  • 6 попытка переместить колесо в новое положение, когда оно еще не выведено "домой".

Кстати, последняя ошибка довольно-таки странная: ведь как только подается питание на +12В и на USB, колесо сразу едет "домой".

Все, как только напишу полноценную управлялку, создам новую тему.
Tags: c, usb, велосипедостроение, железяки
Subscribe

  • Баги в сборке кикада

    Просто оставлю на память: для того, чтобы свежий кикад собрался, нужно указать: cmake ../ -DKICAD_SCRIPTING_MODULES=no -DKICAD_SCRIPTING_WXPYTHON=no…

  • Неортогональные координаты

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

  • Про излишние уровни абстракции

    Один товарищ решил проверить, насколько быстро может работать USB на STM32F103 ( ссылка на не очень хороший ресурс). И получились у него совсем…

promo eddy_em august 17, 2019 12:33 3
Buy for 10 tokens
Юра намедни напечатал корпус для хронометра. Для первого блина получилось неплохо: И еще немного фотографий:
  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic
  • 2 comments