Ручная настройка USB

Кто любит RISC в жизни, заходим, не стесняемся.
misyachniy
Прорезались зубы
Сообщения: 219
Зарегистрирован: Вт июл 02, 2013 09:17:49

Re: Ручная настройка USB

Сообщение misyachniy »

Ручной настройкой USB я разбирался давно, еще на ассемблере.
http://njnmnp.narod.ru/proj/flashbom/flashbom.html

В принципе нет ничего сложного.
Получил запрос от компьютера - отдал ответ.
Захотел передать данные - "положил в нужное место" и ждешь пока данные заберет компьютер.

Единственная заморочка - после приема сигнала "USB Reset" нужно прекратить передачу и перейти в исходное состояние.

Сейчас я перешел на более высокий уровень "некомпетентности".
Если мне нужен проект на USB для любого контроллера, я беру пример от производителя Mass Storage Device.
Меняю VID/PID и тип устройства.

Теперь у меня есть проект с двумя endpoint на прием передачу.
Со стороны компьютера беру libusb0 и генерирую inf файл.

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

Все лишнее по MSD удаляю и пишу свое.
Реклама
Аватара пользователя
IfoR
Поставщик валерьянки для Кота
Сообщения: 2029
Зарегистрирован: Сб ноя 15, 2008 10:09:56
Откуда: г. Тула
Контактная информация:

Re: Ручная настройка USB

Сообщение IfoR »

Так дело в том, что я не могу узнать, что хост от меня хочет, потому что в буффер не пишется абсолютно ничего.
Изображение
/dev/urandom - гигабайты информации.

OS: openSUSE 13.2 (x86_64)
Реклама
misyachniy
Прорезались зубы
Сообщения: 219
Зарегистрирован: Вт июл 02, 2013 09:17:49

Re: Ручная настройка USB

Сообщение misyachniy »

IfoR писал(а):Так дело в том, что я не могу узнать, что хост от меня хочет, потому что в буффер не пишется абсолютно ничего.
Обычно нужно посмотреть куда подсоединен резистор 1,5К в схеме.
Один конец к D+ второй к 3,3В. Напрямую, через транзистор с вывода МК.
Если резистор встроенный, то пример должен заработать не зависимо от схемы.

Если резистор есть, нужно найти в примере работу с ним. Обычно функция типа USB_connect();
Бывает что пока отладчик загрузит ПО в МК, windows пытается проинициализировать устройство.

Если внешний резистор есть, то переписать пример в части манипуляции данным резистором.

Если windows пишет, что устройство USB не определено, значит резистор подключен.

Первое обращение по шине USB не посылка данных а специальная манипуляция проводами D+/D-, которое устройство должно декодировать как сброс шины.
Аватара пользователя
IfoR
Поставщик валерьянки для Кота
Сообщения: 2029
Зарегистрирован: Сб ноя 15, 2008 10:09:56
Откуда: г. Тула
Контактная информация:

Re: Ручная настройка USB

Сообщение IfoR »

Резистор просто подключён напрямую к Vcc. Мне ни к чему разрывать шину программно (а хост, после подключения, ждёт что-то около 100 мс для самоинициализации устройства, прежде чем начнёт обмен). Прошивается у меня микроконтроллер но SWD. USB который нужно настроить расположен отдельно и подключаю я кабель USB уже после запуска откладки. При работе без отладчика (с выводом результатов на дисплей) результат тот же самый.
Низкоуровневая манипуляция с шиной, грубо говоря, - это забота контроллера, а не моя. Мне приходит лишь событие сброса и оно (в if-е с USB->ISTR & USB_ISTR_RESET) отрабатывается нормально. Опять повторюсь - аппаратная часть работает вроде как нормально. Всё проходит вроде как штатно как со стороны контроллера, так и со стороны компа. Проблема в том, что контроллер хоть и правильно принимает пакеты, но почему то не пишет принятые данные в буффер для разбора программой. Хост мне отправляет первый запрос дескриптора устройства (контроллер чётко понимает что пришёл SETUP пакет с запросом данных от хоста, что явно указывается в регистрах), что длиной 64, однако я не могу этого понять, т.к. пакета то нет в памяти. А в чём дело понять не могу. Вроде бы адреса и размеры

Код: Выделить всё

bufferDescriptor->ADDR0_RX = EP0_ADDR_RX;
      bufferDescriptor->ADDR0_TX = EP0_ADDR_TX;
      bufferDescriptor->COUNT0_RX = maxPacketSize;
      bufferDescriptor->COUNT0_TX = maxPacketSize;
Пишутся правильно в нужное место при BTABLE = 0. Это видно по дампу памяти, что на скрине выше. Но больше ничего не происходит. :(
Я не в винде смотрю, но лог соединения на стороне хоста говорит, что подсоединение происходит успешно, но в процессе энумерации всё стопорится: хост отправляет запрос GET_DESCRIPTOR и примерно через 2 мс приходит в ответ (от контроллера) пакет 0-й длины. Хост повторяет запрос ещё 2 раза с тем же исходом, после чего сбрасывает шину и пробует повторить запрос ещё раз. Я же на контроллера каждый раз не могу прочитать присланный пакет. :(
Изображение
/dev/urandom - гигабайты информации.

OS: openSUSE 13.2 (x86_64)
Реклама
Эиком - электронные компоненты и радиодетали
Аватара пользователя
include
Открыл глаза
Сообщения: 43
Зарегистрирован: Пт апр 20, 2012 11:32:02
Откуда: ua

Re: Ручная настройка USB

Сообщение include »

Ребята, объясните пожалуйста не понятный момент с BTABLE.
Due to the common APB bridge limitation on word addressability, all packet memory
locations are accessed by the APB using 32-bit aligned addresses, instead of the actual
memory location addresses utilized by the USB peripheral for the USB_BTABLE register
and buffer description table locations.

To obtain the correct STM32F10xxx memory address value to be used in
the application software while accessing the packet memory, the actual memory location
address must be multiplied by two.
Зачем умножать адрес на два?
Чувствую тут можно накосячить, как то запутанно.

Например BTABLE по адресу 0x40006000, пусть будет описан один ендпоинт, одна запись в таблице, длина таблицы 8 байт, далее следует 2 байта count0_tx, далее буффер addr0_tx.
COUNT0_TX = 0x0008, ADDR0_TX = 0x000A
Соответственно буффер начинается с 0x000A, записывать данные в него можно с этого оффсет адреса, верно?
Последний раз редактировалось include Пн сен 21, 2015 14:39:40, всего редактировалось 1 раз.
Реклама
Аватара пользователя
YS
Друг Кота
Сообщения: 7518
Зарегистрирован: Вс мар 29, 2009 22:09:05
Контактная информация:

Re: Ручная настройка USB

Сообщение YS »

Зачем умножать адрес на два?
Там же написано - Due to the common APB bridge limitation on word addressability.

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

Умножать на два надо.

Если найду свои упражнения годовой давности, выложу свой код доступа к этим областям. Он у меня работал.
Разница между теорией и практикой на практике гораздо больше, чем в теории.
Реклама
Аватара пользователя
include
Открыл глаза
Сообщения: 43
Зарегистрирован: Пт апр 20, 2012 11:32:02
Откуда: ua

Re: Ручная настройка USB

Сообщение include »

YS писал(а):
Зачем умножать адрес на два?
Там же написано - Due to the common APB bridge limitation on word addressability.
Due to the common limitation of APB1
bridges on word addressability, all register addresses are aligned to 32-bit word boundaries
although they are 16-bit wide. The same address alignment is used to access packet buffer
memory locations, which are located starting from 0x4000 6000.
Похоже я начинаю понимать. Это настоящее шаманство :). Выходит для приложения память PMA выглядит как 32*word , а для USB как 32*half-word;
В таком случае да, умножение на два требуется.
Аватара пользователя
YS
Друг Кота
Сообщения: 7518
Зарегистрирован: Вс мар 29, 2009 22:09:05
Контактная информация:

Re: Ручная настройка USB

Сообщение YS »

Это настоящее шаманство :)
Это вы еще не видели (или видели? :) ) как в STM8 устроена пара регистров, устанавливающая скорость UART'а... :)))
Разница между теорией и практикой на практике гораздо больше, чем в теории.
Аватара пользователя
include
Открыл глаза
Сообщения: 43
Зарегистрирован: Пт апр 20, 2012 11:32:02
Откуда: ua

Re: Ручная настройка USB

Сообщение include »

YS писал(а):
Это настоящее шаманство :)
Это вы еще не видели (или видели? :) ) как в STM8 устроена пара регистров, устанавливающая скорость UART'а... :)))
Не видел :( Лежат две штучки, всё никак не доберусь. Интересно конечно, поковырять, займусь позже. Меня очень расстроило то, что ST не предоставили для STM8 компилятора под разные ОС. Типа пользуйтесь своим Кейлом, нас это не заботит. Правда положение дел сейчас исправляет SDCC. Главное дебагер раздобыть, ну или самому сделать.
Зато видел кривые реализации прерываний для spi и usart в stm32, такие костыли приходится городить, что порой хочется вообще без прерываний. Сюда можно и i2c отнести с его необходимостью заранее NUCK-ать.(В старших моделях вроде пофиксили).
Скучать в общем не приходится :D
Аватара пользователя
YS
Друг Кота
Сообщения: 7518
Зарегистрирован: Вс мар 29, 2009 22:09:05
Контактная информация:

Re: Ручная настройка USB

Сообщение YS »

Меня очень расстроило то, что ST не предоставили для STM8 компилятора под разные ОС.
Я с некоторых пор для STM8 использую Cosmic + ST Visual Develop. Cosmic, правда, тоже, гм, не без особенностей (например, в нем из коробки нет stdint.h, ну или я не нашел), но вообще эта связка очень напоминает милую моему сердцу AVR Studio 4. :) До того писал для STM8 в IAR.

Жалко что пока не сделали соответствующий backend для GCC. Даже странно, контроллеры-то очень приятные.
Зато видел кривые реализации прерываний для spi и usart в stm32, такие костыли приходится городить, что порой хочется вообще без прерываний.
А что там?
Разница между теорией и практикой на практике гораздо больше, чем в теории.
Аватара пользователя
include
Открыл глаза
Сообщения: 43
Зарегистрирован: Пт апр 20, 2012 11:32:02
Откуда: ua

Re: Ручная настройка USB

Сообщение include »

YS писал(а):Жалко что пока не сделали соответствующий backend для GCC. Даже странно, контроллеры-то очень приятные.
Наверное сказывается не популярность сих архитектур. Но как я и говорил, есть SDCC, правда код генерирует он слегка "пухлый" :)
А что там?
Подробности с USART не опишу, давно работал с ним, но как мне помнится, у меня нормально не вышло включить прерывания одновременно на TX и RX.
У SPI болеет TX прерывание. Оно работает совсем не так, как того ожидаешь. При включении прерывания, при отсутствии данных обработчик будет "долбиться" пока прерывание не вырубишь. Ожидается, что после снятия tx флага прерывания, прерывание не вызовется до тех пор, пока я снова не положу данные в буфер и он освободится.
Официальный костыль от ST - выключение прерывания в обработчике. Такое tx прерывание подходит разве что, для больших объёмов данных, а не, скажем, когда нужно отправить пару байт.
Хотя, можно конечно не париться и включить DMA. Но так и DMA каналов не хватит, если на каждую кривую периферию врубать DMA.
Аватара пользователя
oleg110592
Друг Кота
Сообщения: 3832
Зарегистрирован: Сб сен 10, 2011 17:46:25

Re: Ручная настройка USB

Сообщение oleg110592 »

include писал(а):Наверное сказывается не популярность сих архитектур
несколько не так, в линейке STM8 имеет смысл использовать STM8S003F3P6 из-за крайней дешевизны оного - там 8 Kbytes Flash, поэтому можно использовать любой коммерческий компилятор даже с ограничениями. Cosmic (неплохой компилятор) сейчас раздают в одном флаконе STM8 32K + STM32 32K на шару (FREE, лично получал): http://www.cosmic-software.com/download.php
Для всего остального, как замену всем устаревшим восьмибитникам PIC AVR, имеет смысл использовать серию STM32F0 - там с GCC все в ажуре, но для этой серии отличный компилятор, от самого производителя ядра (ARM), вместе с ИДЕ Keil раздается бесплатно (free-to-use professional tool suite) http://www2.keil.com/stmicroelectronics-stm32/mdk
gargargar
Первый раз сказал Мяу!
Сообщения: 27
Зарегистрирован: Сб окт 03, 2015 01:58:15

Re: Ручная настройка USB

Сообщение gargargar »

Может кому понадобится, написал на коленках работу с USB через регистры, используются только header'ы для числовых define'ов. Дескрипторы устройства и репорты HID брались отсюда STM32 и USB-HID — это просто или на geektimes.ru. Можно использовать Windows программу из статей для проверки, отправка report'ов на устройство не обрабатывается.

Особенности:
1) Код очень корявый, куча мест не проверятся, куча debug переменных, но он работает и очень помог бы мне вначале сократить время на разработку (а теперь нет желания писать дальше). Для примера с чего начать самое то.
2) Писался для STM32F103C8T8
3) Надо очень правильно инициировать USB, соблюдать последовательность, я уже забыл все нюансы.
4) Память под внутренние буферы находится в адресном пространстве регистров периферии, внутренняя адресация по 16бит, мы же пишем выровнено по 32 и рассматриваем 16бит данных как 32бита. Не пишите туда по-байтно, только разом по 16бит, иначе будет дублирование старших/младших байт.
5) У одного USB регистра USB_EPnR куча toggle битов, нужно извращаться с XOR'ами и AND'ами.
6) Вообще реализацию USB на этих микроконтроллерах делал садист и получал удовольствие от каждого разработчика кто пытался разобраться в потрохах (ну она очень мудрено-извращенная), лучше используйте готовые библиотеки, RAW MODE только если нехватка памяти.
Спойлер

Код: Выделить всё

#include "stm32f10x.h"
#include "stdbool.h"
#include "string.h"
#include "usb_def.h"
#include "usb_hid.h"

volatile uint32_t g_msTicks = 0;
volatile uint32_t g_times = 0;
volatile uint32_t g_resets = 0;
volatile uint32_t g_controls = 0;
volatile uint32_t g_cntr = 0;

struct History
{
	uint16_t EP_before;
	uint16_t EP_after;
	uint8_t signal;
	uint8_t value;
	uint16_t sz;
	char cmd;
};

struct History history[100];
#define HISTORY_MAX 100
uint32_t historyCntr = 0;

const uint8_t *g_controlBuf = NULL;
uint32_t g_controlSize = 0;
uint8_t g_newAddr = 0;

#define HID_SIZE_DEVICE_DESC 18
#define E0_MAX_PACKET_SIZE 8
#define E1_MAX_PACKET_SIZE 8
#define E2_MAX_PACKET_SIZE 8

#define RHID_SIZ_CONFIG_DESC                41
#define RHID_SIZ_REPORT_DESC                79

#define wMaxPacketSize 8

#define USB_BASE_ADDR   0x40005C00  /* USB Registers Base Address */
#define USB_PMA_ADDR    0x40006000  /* USB Packet Memory Area Address */

#define LOWBYTE(v)   ((unsigned char) (v))
#define HIGHBYTE(v)  ((unsigned char) (((unsigned int) (v)) >> 8))

/* Common Registers */
#define USB_CNTR    *((volatile unsigned int *)(USB_BASE_ADDR + 0x40))   /* Control Register */
#define USB_ISTR    *((volatile unsigned int *)(USB_BASE_ADDR + 0x44))   /* Interrupt Status Register */
#define USB_FNR     *((volatile unsigned int *)(USB_BASE_ADDR + 0x48))   /* Frame Number Register */
#define USB_DADDR   *((volatile unsigned int *)(USB_BASE_ADDR + 0x4C))   /* Device Address Register */
#define USB_BTABLE  *((volatile unsigned int *)(USB_BASE_ADDR + 0x50))   /* Buffer Table Address Register */

#define USB_EP0R    *((volatile unsigned int *)(USB_BASE_ADDR + 4*0))
#define USB_EP1R    *((volatile unsigned int *)(USB_BASE_ADDR + 4*1))
#define USB_EP2R    *((volatile unsigned int *)(USB_BASE_ADDR + 4*2))
#define USB_EP3R    *((volatile unsigned int *)(USB_BASE_ADDR + 4*3))
#define USB_EP4R    *((volatile unsigned int *)(USB_BASE_ADDR + 4*4))
#define USB_EP5R    *((volatile unsigned int *)(USB_BASE_ADDR + 4*5))
#define USB_EP6R    *((volatile unsigned int *)(USB_BASE_ADDR + 4*6))
#define USB_EP7R    *((volatile unsigned int *)(USB_BASE_ADDR + 4*7))

#define USB_CONTROL_TX_ADDR ((void*)(USB_PMA_ADDR + 3*16 + 0))
#define USB_CONTROL_RX_ADDR ((void*)(USB_PMA_ADDR + 3*16 + E0_MAX_PACKET_SIZE * 2))

#define USB_E0_TX_SIZE_ADDR ((void*)(USB_PMA_ADDR + 4))
#define USB_E0_RX_SIZE_ADDR ((void*)(USB_PMA_ADDR + 8 + 4))

#define USB_E1_TX_SIZE_ADDR ((void*)(USB_PMA_ADDR + 16 + 4))
#define USB_E1_RX_SIZE_ADDR ((void*)(USB_PMA_ADDR + 16 + 8 + 4))
#define USB_E1_TX_ADDR      ((void*)(USB_PMA_ADDR + 3*16 + E0_MAX_PACKET_SIZE * 4 + 0)) 
#define USB_E1_RX_ADDR      ((void*)(USB_PMA_ADDR + 3*16 + E0_MAX_PACKET_SIZE * 4 + E0_MAX_PACKET_SIZE * 2))


const uint8_t USB_StringSupportedLanguages[] = 
{
	4, USB_STRING_DESCRIPTOR_TYPE,
	0x09, 0x4
};

const uint8_t USB_StringManufacturer[] =
{
	12,
	USB_STRING_DESCRIPTOR_TYPE,
	'A',0,'l',0,'m',0,'a',0,'z',0
};

const uint8_t USB_StringProduct[] =
{
	12,
	USB_STRING_DESCRIPTOR_TYPE,
	'S',0,'T',0,'M',0,'3',0,'2',0
};

const uint8_t USB_StringSerial[] =
{
	14,
	USB_STRING_DESCRIPTOR_TYPE,
	'M',0,'Y',0,'0',0,'0',0,'0',0,'1',0
};

const uint8_t USB_AlternateSetting[1] = { 0 };

/* USB Standard Device Descriptor */
const uint8_t HID_DeviceDescriptor[HID_SIZE_DEVICE_DESC] =
{
	HID_SIZE_DEVICE_DESC,
	USB_DEVICE_DESCRIPTOR_TYPE, // bDescriptorType - Device descriptor
	0x00, 0x02,                 // bcdUSB

	0x00,                       // bDeviceClass
	0x00,                       // bDeviceSubClass
	0x00,                       // bDeviceProtocol

	E0_MAX_PACKET_SIZE,         // bMaxPacketSize - Endpoint 0

  0x83, 0x04,                 // idVendor (0x0483)
	0x11, 0x57,                 // idProduct (0x5711)

	0, 1,                       // bcdDevice rel. DEVICE_VER_H.DEVICE_VER_L

	1,                          // Index of string descriptor describing manufacturer
	2,                          // Index of string descriptor describing product
	3,                          // Index of string descriptor describing the device serial number

	1                           // bNumConfigurations
};

/* USB Configuration Descriptor */
/*   All Descriptors (Configuration, Interface, Endpoint, Class, Vendor */
const uint8_t RHID_ConfigDescriptor[RHID_SIZ_CONFIG_DESC] =
  {
		    0x09, 			// bLength: ????? ??????????? ????????????
		    USB_CONFIGURATION_DESCRIPTOR_TYPE, // bDescriptorType: ??? ??????????? - ????????????
		    RHID_SIZ_CONFIG_DESC, 0x00, // wTotalLength: ????? ?????? ????? ?????? ??? ?????? ????????????? ? ??????

		    0x01,         // bNumInterfaces: ? ???????????? ????? ???? ?????????
		    0x01,         // bConfigurationValue: ?????? ?????? ????????????
		    0x00,         // iConfiguration: ?????? ??????, ??????? ????????? ??? ????????????
		    0xE0,         // bmAttributes: ??????? ????, ??? ?????????? ????? ???????? ?? ???? USB
		    0x32,         // MaxPower 100 mA: ? ??? ?????? 100 ??

				/************** ?????????? ?????????? ****************/
				0x09,         // bLength: ?????? ??????????? ??????????
				USB_INTERFACE_DESCRIPTOR_TYPE, // bDescriptorType: ??? ??????????? - ?????????
				0x00,         // bInterfaceNumber: ?????????? ????? ?????????? - 0
				0x00,         // bAlternateSetting: ??????? ??????????????? ??????????, ? ??? ?? ????????????
				0x02,         // bNumEndpoints - ?????????? ??????????.

				0x03,         // bInterfaceClass: ????? ????????? - HID
				// ???? ?? ?? ?????? ??? ??????????? ??????????, ???????? ?????????? ??? ????, ?? ???? ???? ?? ??????? ????????? ????? ? ????????
				// ? ??? ? ??? ????? HID-??????????
				0x00,         // bInterfaceSubClass : ???????? ??????????.
				0x00,         // nInterfaceProtocol : ???????? ??????????

				0,            // iInterface: ?????? ??????, ??????????? ?????????

					// ?????? ????????? ?????????? ??? ????????? ????, ??? ?????? ????????? - ??? HID ??????????
					/******************** HID ?????????? ********************/
					0x09,         // bLength: ????? HID-???????????
					HID_HID_DESCRIPTOR_TYPE, // bDescriptorType: ??? ??????????? - HID
					0x01, 0x01,   // bcdHID: ????? ?????? HID 1.1
					0x00,         // bCountryCode: ??? ?????? (???? ?????)
					0x01,         // bNumDescriptors: ??????? ?????? ????? report ????????????
						HID_REPORT_DESCRIPTOR_TYPE,         // bDescriptorType: ??? ??????????? - report
						RHID_SIZ_REPORT_DESC,	0x00, // wItemLength: ????? report-???????????


					/******************** ?????????? ???????? ????? (endpoints) ********************/
					0x07,          // bLength: ????? ???????????
					USB_ENDPOINT_DESCRIPTOR_TYPE, // ??? ??????????? - endpoints

					0x81,          // bEndpointAddress: ????? ???????? ????? ? ??????????? 1(IN)
					0x03,          // bmAttributes: ??? ???????? ????? - Interrupt endpoint
					wMaxPacketSize, 0x00,    // wMaxPacketSize:  Bytes max
					0x20,          // bInterval: Polling Interval (32 ms)

          0x07,	/* bLength: Endpoint Descriptor size */
          USB_ENDPOINT_DESCRIPTOR_TYPE,	/* bDescriptorType: */
            /*	Endpoint descriptor type */
          0x01,	/* bEndpointAddress: */
            /*	Endpoint Address (OUT) */
          0x03,	/* bmAttributes: Interrupt endpoint */
          wMaxPacketSize,	/* wMaxPacketSize:  Bytes max  */
          0x00,
          0x20,	/* bInterval: Polling Interval (32 ms) */
}
  ; /* RHID_ConfigDescriptor */

#define RPT3_COUNT 0x01 //PC->STM32
#define RPT4_COUNT 0x04 //STM32->PC 
const uint8_t RHID_ReportDescriptor[RHID_SIZ_REPORT_DESC] =
  {
    0x06, 0x00, 0xff,              // USAGE_PAGE (Generic Desktop)
    0x09, 0x01,                    // USAGE (Vendor Usage 1)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x85, 0x01,                    //   REPORT_ID (1)
    0x09, 0x01,                    //   USAGE (Vendor Usage 1)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0xb1, 0x82,                    //   FEATURE (Data,Var,Abs,Vol)
    0x85, 0x01,                    //   REPORT_ID (1)
    0x09, 0x01,                    //   USAGE (Vendor Usage 1)
    0x91, 0x82,                    //   OUTPUT (Data,Var,Abs,Vol)

    0x85, 0x02,                    //   REPORT_ID (2)
    0x09, 0x02,                    //   USAGE (Vendor Usage 2)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0xb1, 0x82,                    //   FEATURE (Data,Var,Abs,Vol)
    0x85, 0x02,                    //   REPORT_ID (2)
    0x09, 0x02,                    //   USAGE (Vendor Usage 2)
    0x91, 0x82,                    //   OUTPUT (Data,Var,Abs,Vol)

    0x85, 0x03,                    //   REPORT_ID (3)
    0x09, 0x03,                    //   USAGE (Vendor Usage 3)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x26, 0xff, 0x00,              //   LOGICAL_MAXIMUM (255)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x95, RPT3_COUNT,                    //   REPORT_COUNT (N)
    0xb1, 0x82,                    //   FEATURE (Data,Var,Abs,Vol)
    0x85, 0x03,                    //   REPORT_ID (3)
    0x09, 0x03,                    //   USAGE (Vendor Usage 3)
    0x91, 0x82,                    //   OUTPUT (Data,Var,Abs,Vol)

    0x85, 0x04,                    //   REPORT_ID (4)
    0x09, 0x04,                    //   USAGE (Vendor Usage 4)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x95, RPT4_COUNT,                    //   REPORT_COUNT (N)
    0x81, 0x82,                    //   INPUT (Data,Var,Abs,Vol)
    0xc0                           // END_COLLECTION
};
	
enum USB_State
{
	USB_StateDefault = 0,
	USB_StateAddress,
	USB_StateConfigured,
};

enum USB_State g_usbState = USB_StateDefault;
	
void sup_copy(uint8_t *dst, const uint8_t *src, uint32_t size)
{
	for (; size > 1; size -= 2, dst += 4, src += 2)
		*(uint16_t*)dst = *(uint16_t*)src;
	
	if (size == 1)
	{
		uint16_t tmp = *src;
		*(uint16_t*)dst = tmp;
	}
}

void SendStatusZero()
{
		// fix size
		*(uint16_t*)USB_E0_TX_SIZE_ADDR = 0;
		// send
		history[historyCntr % HISTORY_MAX].EP_before = USB_EP0R;
		USB_EP0R = (USB_EP0R ^ USB_EP0R_STAT_TX) & (USB_EP0R_STAT_TX | USB_EP0R_EP_TYPE | USB_EP0R_EP_KIND | USB_EP0R_EA);
		history[historyCntr % HISTORY_MAX].EP_after = USB_EP0R;
		history[historyCntr++ % HISTORY_MAX].cmd = 'Z';
}

void ProcessData(void)
{
	if (USB_EP1R & USB_EP1R_CTR_TX)
	{
		uint8_t buf[5] = { 4 };
		g_cntr++;
		buf[1] = g_cntr % 255;
		buf[2] = g_cntr / 255 % 255;
		buf[3] = g_cntr / 10 & 3;
//		*(uint32_t*)&buf[1] = historyCntr;

		*(uint16_t*)USB_E1_TX_SIZE_ADDR = 5;
		sup_copy(USB_E1_TX_ADDR, buf, 5);
		USB_EP1R = (USB_EP1R ^ USB_EP1R_STAT_TX) & (USB_EP1R_STAT_TX | USB_EP1R_EP_TYPE | USB_EP1R_EP_KIND | USB_EP1R_EA);
	}
}

void ProcessControl(void)
{
	g_controls++;
	
	if (USB_EP0R & USB_EP0R_CTR_RX)
	{
		if (USB_EP0R & USB_EP0R_SETUP)
		{
			uint8_t *setup = (uint8_t*)USB_CONTROL_RX_ADDR;
			
			uint16_t wValue, /*wIndex,*/ wLength;
			
			USB_REQUEST_TYPE bmRequestType = *(USB_REQUEST_TYPE*)(setup++);
			uint8_t bRequest = *(setup++);
			setup += 2;
			wValue = *(uint16_t*)setup;
			setup += 4;
			setup += 4;
			wLength = *(uint16_t*)setup;
			setup += 4;
			
			history[historyCntr % HISTORY_MAX].EP_before = history[historyCntr % HISTORY_MAX].EP_after = USB_EP0R;
			history[historyCntr % HISTORY_MAX].signal = bRequest;
			history[historyCntr % HISTORY_MAX].value = HIGHBYTE(wValue);
			history[historyCntr % HISTORY_MAX].sz = (*(uint16_t*)USB_E0_RX_SIZE_ADDR) & USB_COUNT0_RX_COUNT0_RX;
			history[historyCntr++ % HISTORY_MAX].cmd = 'D';
			
			if (bRequest == USB_REQUEST_GET_DESCRIPTOR)
			{
				if (HIGHBYTE(wValue) == USB_DEVICE_DESCRIPTOR_TYPE)
				{
					g_controlBuf = HID_DeviceDescriptor;
					g_controlSize = wLength > sizeof(HID_DeviceDescriptor) ? sizeof(HID_DeviceDescriptor) : wLength;
				}
				else if (HIGHBYTE(wValue) == USB_CONFIGURATION_DESCRIPTOR_TYPE)
				{
					g_controlBuf = RHID_ConfigDescriptor;
					g_controlSize = wLength > sizeof(RHID_ConfigDescriptor) ? sizeof(RHID_ConfigDescriptor) : wLength;
				}
				else if (HIGHBYTE(wValue) == USB_STRING_DESCRIPTOR_TYPE)
				{
					if (LOWBYTE(wValue) == 0)
					{
						g_controlBuf = USB_StringSupportedLanguages;
						g_controlSize = wLength > sizeof(USB_StringSupportedLanguages) ? sizeof(USB_StringSupportedLanguages) : wLength;
					}
					else if (LOWBYTE(wValue) == 1)
					{
						g_controlBuf = USB_StringManufacturer;
						g_controlSize = wLength > sizeof(USB_StringManufacturer) ? sizeof(USB_StringManufacturer) : wLength;
					}
					else if (LOWBYTE(wValue) == 2)
					{
						g_controlBuf = USB_StringProduct;
						g_controlSize = wLength > sizeof(USB_StringProduct) ? sizeof(USB_StringProduct) : wLength;
					}
					else if (LOWBYTE(wValue) == 3)
					{
						g_controlBuf = USB_StringSerial;
						g_controlSize = wLength > sizeof(USB_StringSerial) ? sizeof(USB_StringSerial) : wLength;
					}
					else
					{
						g_controlSize = 0;
						return;
					}
				}
				else if (HIGHBYTE(wValue) == USB_DEVICE_QUALIFIER_DESCRIPTOR_TYPE)
				{
					history[historyCntr % HISTORY_MAX].EP_before = USB_EP0R;
					history[historyCntr % HISTORY_MAX].signal = bRequest;
					history[historyCntr % HISTORY_MAX].value = HIGHBYTE(wValue);
					history[historyCntr % HISTORY_MAX].sz = (*(uint16_t*)USB_E0_RX_SIZE_ADDR) & USB_COUNT0_RX_COUNT0_RX;

					// send stall
					USB_EP0R = (USB_EP0R ^ USB_EP0R_STAT_TX_0) & (USB_EP0R_STAT_TX | USB_EP0R_EP_TYPE | USB_EP0R_EP_KIND | USB_EP0R_EA);
					history[historyCntr % HISTORY_MAX].EP_after = USB_EP0R;
					history[historyCntr++ % HISTORY_MAX].cmd = 'S';
					return;
				}
				else if (HIGHBYTE(wValue) == HID_REPORT_DESCRIPTOR_TYPE)
				{
					g_controlBuf = RHID_ReportDescriptor;
					g_controlSize = wLength > sizeof(RHID_ReportDescriptor) ? sizeof(RHID_ReportDescriptor) : wLength;
				}
				else
				{
					g_controlSize = 0;
					return;
				}
			}
			else if (bRequest == USB_REQUEST_SET_ADDRESS)
			{
				g_newAddr = wValue;
				SendStatusZero();
				//USB_DADDR = wValue | USB_DADDR_EF;
				g_controlSize = 0;
				return;
			}
			else if (bRequest == USB_REQUEST_SET_CONFIGURATION)
			{
				SendStatusZero();
				g_controlSize = 0;
				g_usbState = USB_StateConfigured;
				return;
			}
			else if (bRequest == USB_REQUEST_GET_INTERFACE)
			{
				g_controlBuf = USB_AlternateSetting;
				g_controlSize = wLength > sizeof(USB_AlternateSetting) ? sizeof(USB_AlternateSetting) : wLength;
			}
			else if (bRequest == USB_REQUEST_CLEAR_FEATURE)
			{
				SendStatusZero();
				g_controlSize = 0;
				return;
			}
			else
			{
				g_controlSize = 0;
				return;
			}
		}
		else
		{
			// status zero or data
			g_controlSize = 0;
		}
	}
	// TX or TX after RX
	if (g_controlSize > 0)
	{
		uint32_t send = g_controlSize > E0_MAX_PACKET_SIZE ? E0_MAX_PACKET_SIZE : g_controlSize;
		// fix size
		*(uint16_t*)USB_E0_TX_SIZE_ADDR = send;
		sup_copy(USB_CONTROL_TX_ADDR, g_controlBuf, send);
		g_controlBuf += send;
		g_controlSize -= send;
		// send
		history[historyCntr % HISTORY_MAX].EP_before = USB_EP0R;
		USB_EP0R = (USB_EP0R ^ USB_EP0R_STAT_TX) & (USB_EP0R_STAT_TX | USB_EP0R_EP_TYPE | USB_EP0R_EP_KIND | USB_EP0R_EA);
		history[historyCntr % HISTORY_MAX].EP_after = USB_EP0R;
		history[historyCntr++ % HISTORY_MAX].cmd = 'I';
	}
	else
	{
		if (g_newAddr > 0)
		{
			USB_DADDR = g_newAddr | USB_DADDR_EF;
			g_usbState = USB_StateAddress;
			g_newAddr = 0;
		}
		// force RX
		// receive
		history[historyCntr % HISTORY_MAX].EP_before = USB_EP0R;
		USB_EP0R = (USB_EP0R ^ USB_EP0R_STAT_RX) & (USB_EP0R_STAT_RX | USB_EP0R_EP_TYPE | USB_EP0R_EP_KIND | USB_EP0R_EA);
		history[historyCntr % HISTORY_MAX].EP_after = USB_EP0R;
		history[historyCntr++ % HISTORY_MAX].cmd = 'O';
	}
}

void ResetUSB()
{
	// memory for buffers
	uint16_t *usbBufs = (uint16_t*)USB_PMA_ADDR;
	uint8_t buf[5] = { 4 };
	g_cntr++;
	buf[1] = g_cntr % 2 ? 1 : 0;
	buf[2] = g_cntr % 2 ? 0 : 1;
	buf[3] = 3;
//	*(uint32_t*)&buf[1] = historyCntr;

	g_usbState = USB_StateDefault;
	g_newAddr = 0;

	memset(usbBufs, 0, 1024); // remove
	*usbBufs = 8 * 3;
	usbBufs += 2;
	*usbBufs = E0_MAX_PACKET_SIZE;
	usbBufs += 2;
	*usbBufs = 8 * 3 + E0_MAX_PACKET_SIZE;
	usbBufs += 2;
	*usbBufs = E0_MAX_PACKET_SIZE / 2 << 10; // 8 byte size
	usbBufs += 2;
	
	*usbBufs = 8 * 3 + 2 * E0_MAX_PACKET_SIZE;
	usbBufs += 2;
	*usbBufs = E1_MAX_PACKET_SIZE;
	usbBufs += 2;
	*usbBufs = 8 * 3 + 2 * E0_MAX_PACKET_SIZE + E1_MAX_PACKET_SIZE;
	usbBufs += 2;
	*usbBufs = E1_MAX_PACKET_SIZE / 2 << 10; // 8 byte size
	usbBufs += 2;

	*usbBufs = 8 * 3 + 2 * E0_MAX_PACKET_SIZE + 2 * E1_MAX_PACKET_SIZE;
	usbBufs += 2;
	*usbBufs = E2_MAX_PACKET_SIZE;
	usbBufs += 2;
	*usbBufs = 8 * 3 + 2 * E0_MAX_PACKET_SIZE + 2 * E1_MAX_PACKET_SIZE + E2_MAX_PACKET_SIZE;
	usbBufs += 2;
	*usbBufs = E2_MAX_PACKET_SIZE / 2 << 10; // 8 byte size
	
	*(uint16_t*)USB_E1_TX_SIZE_ADDR = 5;
	sup_copy(USB_E1_TX_ADDR, buf, 5);

	USB_EP0R = USB_EP0R_EP_TYPE_0 | USB_EP0R_STAT_RX_0 | USB_EP0R_STAT_RX_1;
	USB_EP1R = USB_EP1R_EP_TYPE | USB_EP1R_STAT_TX_0 | USB_EP1R_STAT_TX_1 | 1;
	USB_EP2R = USB_EP2R_EP_TYPE | 2;
	USB_DADDR = USB_DADDR_EF;
	
	g_resets++;
}

void USB_HP_CAN1_TX_IRQHandler(void)
{
}

void USB_LP_CAN1_RX0_IRQHandler(void)
{
	if (USB_ISTR & USB_ISTR_RESET)
	{
		history[historyCntr++ % HISTORY_MAX].cmd = 'R';
		// function
		ResetUSB();
		// clear all interrupt status
		USB_ISTR &= ~USB_ISTR_RESET;
		// no more interrupts
		return;
	}
	if (USB_ISTR & USB_ISTR_CTR)
	{
		// control endpoint
		if (USB_EP0R & (USB_EP0R_CTR_RX | USB_EP0R_CTR_TX))
			ProcessControl();
		if (USB_EP1R & (USB_EP1R_CTR_RX | USB_EP1R_CTR_TX))
			ProcessData();
	}
}
	
// SysTick interrupt Handler.
void SysTick_Handler(void)
{
  g_msTicks++;
}

void sleep(uint32_t amount)
{
	uint32_t need = amount;
	g_msTicks = 0;

	while (g_msTicks < need)
  {
		// Wait for next SysTick Interrupt
    __WFE(); // Power-Down until next Event/Interrupt
  }
}

int main(void)
{
	uint32_t sum = 0;
	uint32_t waiter = 500;
	bool minus = true;

	SysTick_Config(SystemCoreClock / 1000);
	
	RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
	
	GPIOA->CRL &= ~(GPIO_CRL_MODE0 | GPIO_CRL_CNF0 | GPIO_CRL_MODE1 | GPIO_CRL_CNF1);
	GPIOA->CRL |= GPIO_CRL_MODE0_1 | GPIO_CRL_CNF0_0 | GPIO_CRL_MODE1_1;
	
//	printf("Hello, world!");

//	GPIOA->BSRR = GPIO_BSRR_BR1;
	sleep(2000);
	GPIOA->BSRR = GPIO_BSRR_BS1;
	
//	NVIC_SetPriority(USB_LP_CAN1_RX0_IRQn, 
	NVIC_EnableIRQ(USB_HP_CAN1_TX_IRQn);
	NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn);
	
	RCC->APB1ENR |= RCC_APB1ENR_USBEN;
	
	// reset power down alone
	USB_CNTR &= ~USB_CNTR_PDWN;
	sleep(100);
	// clear interrupt status
	USB_ISTR = 0;
	// remove force reset
	USB_CNTR = USB_CNTR_RESETM;
//	sleep(100);

	while(1)
	{
		sleep(waiter);
		GPIOA->BSRR = GPIO_BSRR_BS0;

		sleep(waiter);
		GPIOA->BSRR = GPIO_BSRR_BR0;
		
		sum += 2 * waiter;

		if (sum >= 1000)
		{
			sum = 0;
			
			if (minus)
			{
				if (waiter <= 20) waiter = 1, minus = false;
				else waiter -= 20;
			}
			else
			{
				if (waiter >= 500) waiter = 500, minus = true;
				else waiter += 20;
			}
		}
	}
}
Аватара пользователя
B@R5uk
Собутыльник Кота
Сообщения: 2896
Зарегистрирован: Сб ноя 13, 2010 12:53:25
Откуда: приходит весна?

Re: Ручная настройка USB

Сообщение B@R5uk »

IfoR писал(а):...пока не понял, что нужно поставить задержку на FLASH, а быстроисполняемый код переписывать в ОЗУ и от туда запускать.
Если код очень разветвлённый, то да, можно в ОЗУ, а если линейный, то что из флэша, что из ОЗУ будет одинаково. Контроллер осуществляет чтение флэша блоками по несколько команд, поэтому пока флэш готовится к выдаче следующего блока, ядро занято исполнением текущего. Причём всякие сишные "ветвления" типа

Код: Выделить всё

if ( {условие} )
{
  {делать раз}
}
else
{
  {делать два}
}
на арме суть линейный код с префиксным условием. Это сделано для более простого прогнозирования времени выполнения (которое при такой компиляции не зависит от истинности или ложности условия), хотя опять же, многое зависит от компилятора.

Асло, помещайте, пожалуйста килобайты листингов программ под спойлеры.
Аватара пользователя
YS
Друг Кота
Сообщения: 7518
Зарегистрирован: Вс мар 29, 2009 22:09:05
Контактная информация:

Re: Ручная настройка USB

Сообщение YS »

Контроллер осуществляет чтение флэша блоками по несколько команд, поэтому пока флэш готовится к выдаче следующего блока, ядро занято исполнением текущего.
ST называет это ART accelerator.
Разница между теорией и практикой на практике гораздо больше, чем в теории.
Аватара пользователя
B@R5uk
Собутыльник Кота
Сообщения: 2896
Зарегистрирован: Сб ноя 13, 2010 12:53:25
Откуда: приходит весна?

Re: Ручная настройка USB

Сообщение B@R5uk »

Кстати, а реально ли сделать виртуальный последовательный порт, которому не нужны будут никакие драйвера в любой операционке (стандартные чтобы подходили)? Типа дешёвой замены всяким FT232. Вроде же существует класс устройств связи (USB Communications Device Class).
alexf58
Нашел транзистор. Понюхал.
Сообщения: 183
Зарегистрирован: Сб фев 09, 2013 23:00:23

Re: Ручная настройка USB

Сообщение alexf58 »

Виртуальный порт и не требует драйверов. Под Линуксом - совсем ничего не надо. Под Windows только текстовый файл чтобы сказать системе использовать стандартный драйвер для этого VID/PID.
Аватара пользователя
Siarzhuk
Потрогал лапой паяльник
Сообщения: 353
Зарегистрирован: Вс янв 19, 2014 22:41:55

Re: Ручная настройка USB

Сообщение Siarzhuk »

B@R5uk писал(а): ... виртуальный последовательный порт, которому не нужны будут никакие драйвера в любой операционке (стандартные чтобы подходили)?
Насчёт термина "виртуальный" можно долго спорить, конечно, чем реально воткнутый в комп USB-COM виртуальнее нежели посконный COM on ISA bus. ;) Если USB стек операционной системы поддерживает данный класс устройств - должна работать со штатными драйверами. Другой вопрос что "совместимoсть со стандартами" и "совместимость с windows" - немножко разные вещи и, как показывает практика, производители железа выбирают второе - по понятным причинам. Достаточно посмотреть на таблицы т.н. quircks в опен-сорцных реализациях тех-же USB CDC, USB mass storage. ;)
B@R5uk писал(а): Типа дешёвой замены всяким FT232. Вроде же существует класс устройств связи (USB Communications Device Class).
Да, USB CDC должен поддерживаться всеми уважаюшими себя операциоными системами из коробки.
alexf58 писал(а):Под Windows только текстовый файл чтобы сказать системе использовать стандартный драйвер для этого VID/PID.
ЕМНИП если Interface descriptor объявил стандартные класс/подкласс/протокол, то система выбирает стандартный драйвер не взирая на идентификаторы. В противном случае теряется смысл стандартности - например та-же флешка не увиделась-бы пока систему с её VID/PID-ами не познакомили.
Одновременным нажатием LIGHT и POWER, РП Sangean ATS-909X (ver 1.29) превращается в ATS-909XR! ;-)
Аватара пользователя
B@R5uk
Собутыльник Кота
Сообщения: 2896
Зарегистрирован: Сб ноя 13, 2010 12:53:25
Откуда: приходит весна?

Re: Ручная настройка USB

Сообщение B@R5uk »

Спасибо. Всё стало предельно ясно и понятно. :music: Осталось только найти готовую прошивку. :))) Не думаю, что такая естественная вещь пришла только мне в голову. Может кто-нибудь уже знает такие решения?

Я видел довольно извращённый вариант на 8-ой меге с ногодрыгом, но он страдает нарушением стандартов: мега не способна держать никакой режим, кроме самого медленного, а в нём не поддерживается (по стандарту) Bulk-отправка. Поэтому изобретатели этого чудесного решения нашли в дровах винды дыру и заэксплойтили её. Разумеется, для других систем/более новых версий винды такое решение не прокатит. Хотелось бы что-нибудь грамотное либо с аппаратным USB, либо на более быстром контроллере (хотя за счёт быстроты МК ногодрыг извратом быть не перестанет).
alexf58
Нашел транзистор. Понюхал.
Сообщения: 183
Зарегистрирован: Сб фев 09, 2013 23:00:23

Re: Ручная настройка USB

Сообщение alexf58 »

ATMega 16U2 стоит копейки и имеет настоящий USB. Прошивка для USB to Serial: ишите по слову LUFA.
Ответить

Вернуться в «ARM»