Микроконтроллер вместо LPT порта под Linux.

    Одним из самых простых интерфейсов для программирования был LPT порт, он позволял не только относительно быстро передавать данные в эпоху до USB, но и контролировать каждый вывод порта (bit bang), что давало возможность реализовывать различные протоколы программно. Наиболее полно потенциал параллельного порта можно было раскрыть в однозадачных операционных системах типа DOS, в которых, например, можно было выводить звук через ЦАП, подключенный к LPT порту, что весьма затруднительно сделать в современных многозадачных операционных системах не реального времени. Но время DOS ушло, так и LPT порт все реже встречается на материнских платах компьютеров, не говоря уже о ноутбуках.
    Долгое время я пользовался JTAG-кабелем на порт LPT, клоном Xilinx parallel III, однако после замены материнской платы, я не нашел места куда его подключить. Конечно, можно приобрести USB JTAG от Xilinx, но мы не ищем легких путей.

    Для работы кабеля нужен именно режим bit bang, что исключает возможность использования многочисленных USB-LPT адаптеров из китайских интернет магазинов, которые могут работать лишь по протоколу centronix.
    Было принято решение использовать микроконтроллер с питанием 5В и интерфейсом USB. У меня был AT90USB82. В принципе, можно использовать и микроконтроллер без USB, реализовав его программно с помощью V-USB, например. Схема устройства весьма типовая, GPIO порты подключены напрямую к разъему DB25.
Схема устройства
    Управляющая программа микроконтроллера использует USB-стек LUFA. Команды с компьютера подаются с помощью запросов (control requests), всего поддерживается 4 команды: инициализация, запись регистра, чтение регистра и управление светодиодом. Код обработчика запросов выглядит следующим образом:

void EVENT_USB_Device_ControlRequest(void)
{
	uint16_t data;
	if(((USB_ControlRequest.bmRequestType & CONTROL_REQTYPE_TYPE) ==          REQTYPE_VENDOR)
		&& ((USB_ControlRequest.bmRequestType & CONTROL_REQTYPE_RECIPIENT) == REQREC_DEVICE))
	{
		if ((USB_ControlRequest.bmRequestType & CONTROL_REQTYPE_DIRECTION)  == REQDIR_HOSTTODEVICE)
		{
			switch(USB_ControlRequest.bRequest)
			{
				case 0xE0:
					/* marks the command as "accepted" by the application, so that LUFA does not process it: */
					Endpoint_ClearSETUP();
					/* mark the whole request as successful: */
					Endpoint_ClearStatusStage();
					/* process command parameters: */
					parport_init();
					led_on;
				break;
				case 0xE1:
					Endpoint_ClearSETUP();
					Endpoint_ClearStatusStage();
					parport_write_reg(USB_ControlRequest.wIndex,USB_ControlRequest.wValue);
				break;
				case 0xE2:
					Endpoint_ClearSETUP();
					Endpoint_ClearStatusStage();
					if(USB_ControlRequest.wValue) led_on;
					else led_off;
				break;
			}
		}
		else
		{
			switch(USB_ControlRequest.bRequest)
			{
				case 0xE1:
					data=parport_read_reg(USB_ControlRequest.wIndex);
					Endpoint_ClearSETUP();
					/* write data to endpoint */
					Endpoint_Write_Word_LE(data);
					/* send packet */
					Endpoint_ClearIN();
					/* and mark the whole request as successful: */
					Endpoint_ClearStatusStage();
				break;
			}
		}
	}
}

Код чтения/записи регистров:

void parport_init(void)
{
	DDRD=0xff;		//data
	DDRC|=0xf0;		//control
	DDRB=0x00;		//status
	PORTB=0xFF;
}

void parport_write_reg(uint8_t reg, uint8_t d)
{
	switch(reg)
	{
		case 0: // data
			PORTD=d;
		break;
		
		case 1: // status
		break;

		case 2: // control
			PORTC=((PORTC & 0x0F) | (d<<4));
		break;
	}
}

uint8_t parport_read_reg(uint8_t reg)
{
	switch(reg)
	{
		case 0: // data
			return PORTD;
		break;
		
		case 1: // status
			return PINB & 0xF8;
		break;

		case 2: // control
			return (PINC >>4);
		break;
	}
}


    Порты микроконтроллера соответствуют регистрам и выводам порта следующим образом:
Таблица соответствия портов.
    Для обеспечения работы данного устройства в качестве параллельного порта необходим драйвер. Конечно, я не первый с идеей замены LPT порта, поэтому уже давно существует микросхема USS720, которая обладает нужной функциональностью, а также существует драйвер для линукса, входящий в состав ядра. Именно этот драйвер и взят за основу моего (опенсурс же). Из драйвера были удалены все функции для эмуляции EPP, ECP и т.п и заменены заглушками. Были переписаны функции запросов к устройству. В LPT некоторые биты регистров были аппаратно инвертированы, поэтому драйвер также обеспечивает инверсию соответствующих разрядов регистров.
    Работоспособность проверялась в среде ISE с подгруженной библиотекой libusb-driver.so. Скорость просто поражает, в плохом смысле, например верификация прошивки маленькой ПЛИС может длится 5-10 минут.
Окно с IMPACT
Весь код доступен на Github.

Комментарии (18)

RSS свернуть / развернуть
Низкая скорость конечно из-за использования USB control transfers. Одна такая передача делается один раз в кадре = 1 мсек. Если, допустим, выдаем клок одной ногой порта, а читаем с другой, то тактовая частота его не может быть выше 500 Гц. Bulk передачи могут посылаться несколько в одном кадре, что подымет скорость в разы.
0
Но понадобится серьезно перепилить как драйвер, так и прошивку.
0
Можно еще с изохронными передачами поизвращаться, но мне пока достаточно просто работы в принципе.
0
Там такая же тема, одна на кадр на конечную точку. В high-speed поинтереснее (до трех в микрофрейме), но железо совсем другое нужно.
0
У меня на похожем контроллере довольно быстро ПЛИС прошивалась.
0
Я использую PCI Multi-IO Controller, два СОМ порта + LPT порт, если есть возможность настроиться на нестандартные адреса, самое оно. И очень быстрый, с USB не сравнить.
0
Если микроконтроллер просто занимается битбангом, то почему не взять FT232 в режиме битбанга?
0
  • avatar
  • Vga
  • 08 января 2016, 10:13
а точнее для этих целей есть ft2232
0
А в чем разница кроме сдвоенности? Или ты имеешь в виду, что у одиночной FT232 недостаточно пинов для LPT?
0
пинов больше, да и само название пинов ADBUS как бы намекает на основное назначение
0
Просто микроконтроллер был под рукой, да и не нужно EEPROM ставить. Сейчас посмотрел на цены (и прифигел), они стоят примерно одинаково.
0
Только FT232, вероятно, пошустрее.
0
Нет, все равно один запрос на фрэйм, это ограничение USB. Даже ft2232H не поможет.
0
Ей можно пакетный запрос заслать. FTBB программатор для AVR, например, один из самых шустрых.
0
В bulk теоретически до 19 (реально — до 14-15, зависит от ПО хоста) запросов на фрейм в фулспиде длиной 64 байта.
0
Проблема не в количестве запросов на фрейм, а в том, что эти запросы не независимы. Логика работы с портом такова, что нельзя эти запросы ставить в очередь, запрос на чтение регистра должен возвращать текущее его состояние.
0
Я уже написал — надо использовать не control transfer, а bulk. Их использование уменьшит латентность, и соответственно, поднимет общую скорость. Bulk запросы на разные конечные точки (01 и 129) в пределах одного кадра придут
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.