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

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

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

Сообщение IfoR »

Люди, помогите, пожалуйста, разобраться с модулем USB на примере stm32f103 на уровне регистров. Пересмотрел весь интернет, но практически везде все объяснение обходится словами "возьмем вот эту вот библиотеку, поменяем пару параметров в дескрипторах и теперь мы можем мигать светодиодами с компа! Все просто!" Это, конечно, круто, но такой уровень абстракции лично для меня тяжол для изучения. В документации описание есть, но мало и на трудном для вникания английском. В сети нигде ничего нет, кто бы описывал работу с USB на примитивном уровне. Только везде драйвера, взятые из примера или ещё откуда. Хотя видел один сверхполезный коммент, где один человек в шутку описывал кратко что нужно сделать и как это нудно, когда есть драйвера, но я из этого единственного коммента хоть немного понял в каком порядке что происходит. Смотрел исходники библиотек, сначала там идёт тоже, что написано в документации: включение модуля, настройка прерываний. Все это я сделал и у себя в коде. Но как дело дошло до работы с регистрами BTABLE и EPxR, тут я и встал. Знания английского уже не хватает осилить написанное в документации, да и не все детали там описаны. Может кто знает хоть какую нибудь информацию об этом, где можно почерпнуть или кто может знает?
Изображение
/dev/urandom - гигабайты информации.

OS: openSUSE 13.2 (x86_64)
Реклама
Аватара пользователя
balmer
Это не хвост, это антенна
Сообщения: 1433
Зарегистрирован: Вс дек 02, 2012 03:13:48
Откуда: Калининград

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

Сообщение balmer »

"Для себя" изучать тщательно USB модуль не имеет смысла. Чтобы более-менее разобраться, придется прочитать книжку на 300-400 страниц. А потом для Windows придется еще писать драйвера... Пытался прочитать "избранные главы" по аудиоинтерфейсу - ну его нафиг, только голова кругом пошла.
Реклама
Аватара пользователя
IfoR
Поставщик валерьянки для Кота
Сообщения: 2029
Зарегистрирован: Сб ноя 15, 2008 10:09:56
Откуда: г. Тула
Контактная информация:

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

Сообщение IfoR »

Вот так вот потом мир и скатывается, когда все твердят "для себя изучать тщательно что-то не имеет сысла", а как оно работает никто объяснить не может. :cry:
Я пока не ставлю для себя цели делать какое-то сложно устройство со своими драйверами, мне бы пока просто с любым HID разобраться. Да даже, как довести контроллер до такого состояния, чтобы он был-бы в состоянии принимать пакеты. Пока мне только удалось получить состояние, когда контроллер, при подачи питания, вырабатывает прерывание от USB модуля в котором, по идее, нужно настраивать все endpoint-ы и, в частности, 0-й endpoint для идентификации устройства хостом. И что для меня интересно, при простом подачи питания на контроллер, вырабатывается прерывания SUSP и ESOF (типа не обнаружена передача от хоста в течении 3 мс, вроде логично), а есть подключить его сразу по USB к компу, то ничего не вырабатывается. Вообще нет никаких прерываний. Получается, я и модуль не могу настроить, потому что не знаю о его готовности к этому. :roll: И вот ответы на подобные и другие вещи я и не знаю где найти.
Кстати, а что за книжка? В ней есть описание работы с USB модулем STM32 на регистровом уровне?
Изображение
/dev/urandom - гигабайты информации.

OS: openSUSE 13.2 (x86_64)
Аватара пользователя
balmer
Это не хвост, это антенна
Сообщения: 1433
Зарегистрирован: Вс дек 02, 2012 03:13:48
Откуда: Калининград

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

Сообщение balmer »

Нету :( . Лично мне хватило краткого описания. Длинное описание тоже читал, но после трех-четырех дней оценил объем информации который надо усвоить и забил. Люди с форума cqham/SDR тоже пишут - сложный стандарт USB, освоить можно, но сильно проще поднять 100 МБит сеть, если хочется быстрого обмена между устройством и девайсом.
Реклама
Эиком - электронные компоненты и радиодетали
Аватара пользователя
IfoR
Поставщик валерьянки для Кота
Сообщения: 2029
Зарегистрирован: Сб ноя 15, 2008 10:09:56
Откуда: г. Тула
Контактная информация:

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

Сообщение IfoR »

Спасибо! Интересно почитать. А вот с контроллером видимо придётся разбираться по исходникам.
Изображение
/dev/urandom - гигабайты информации.

OS: openSUSE 13.2 (x86_64)
Реклама
alexf58
Нашел транзистор. Понюхал.
Сообщения: 183
Зарегистрирован: Сб фев 09, 2013 23:00:23

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

Сообщение alexf58 »

Я довольно много возился с USB - делал устройство и дрйвер к нему для Мака, еще до OS X. OS 9 - хрень еще та. Даже свой VID получили :) Лет 14 назад.
И согласен, что протокол не простой для изучения. Потому что сделан чтобы покрыть все что шевелится. Чтобы понять биты в регистрах, надо четко представлять что такое endpoints и т.д.

Если есть желание вникнуть в суть на низком уровне, без толстых книг не обойтись. Еще можно поглядеть код имплементации на AVR чисто программно.
А если надо задействовать USB на STM32, есть примеры от ST. Я буквально вчера на основе generic HID слепил тестовую систему за пол дня.
Реклама
Аватара пользователя
YS
Друг Кота
Сообщения: 7518
Зарегистрирован: Вс мар 29, 2009 22:09:05
Контактная информация:

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

Сообщение YS »

О, приятно видеть единомышленника. Я тоже изумился косоте, запутанности и размеру стандартной USB-библиотеки для STM32 и начал писать свою реализацию. Кстати, в качестве модельного примера взял именно USB Audio Class.

Сейчас я застрял на том, что прерывание USB RESET я отлавливаю, но вот нулевая конечная точка почему-то не хочет принимать данные. Пишу под STM32L152, потому что у меня есть отладка с ним.

Итак, чтобы завести USB, нам надо:

1. четко представлять себе, как работает собственно USB;
2. научиться взаимодействовать с железкой конкретного МК.

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

Для достижения цели №1 можно действовать так:

1.1. почитать USB made simple и/или USB in a nutshell. Первое мне понравилось больше.
1.2 Почитать стандарт на какой-нибудь класс. Например, на USB Audio Device Class.
1.4 Опционально заглянуть в главу 9 общего описания USB (архив, в нем файл usb_20.pdf).

После этого приходит понимание, что сам по себе USB не так страшен, как о нем рассказывают, за одним исключением: если пытаться написать универсально конфигурируемый движок USB, выйдет неудобоваримое чудовище, что и вышло у ST.

Правда, на то, чтобы понять, что USB не так страшен, у меня ушло два полных дня (~20 часов) непрерывного осознания мануалов (включая reference manual на чип) и стандартов (с перерывами только на поесть и сходить в туалет), приведенных выше (именно осознания, с чтением английского проблем нет).

После этого можно переходить к пункту 2 - настройке железа. Что дает блок USB? Он обеспечивает прием и передачу пакетов. То есть, он сам разбирается со всякими SOF, ACK/NAK и прочим, сам считает и проверят CRC и т.д. То есть, когда хост совершает транзакцию OUT, в идеале должно вызваться прерывание, в котором мы увидим, что конечная точка N приняла данные, заберем готовые, уже положенные в буфер, данные и обработаем их. При совершении транзакции IN блок USB сам заберет подготовленные данные из буфера и отдаст их хосту.

Чтобы разобраться с этим, надо читать reference manual на используемый чип. После этого можно начинать писать код с оглядкой на уже прочитанное.

На этом этапе ко мне пришло понимание, что разобраться с тем, что наворотили ST в своем блоке USB, сложнее, чем понять собственно стандарт USB. Самый главный вынос мозга - USB packet memory. Я так до конца пока и не понял, как она организована. То ли так:

[два байта настоящей памяти USB][два байта выравнивания][два байта настоящей памяти USB][два байта выравнивания][два байта настоящей памяти USB][два байта выравнивания]...

то ли так:

[байт памяти USB][три байта выравнивания][байт памяти USB][три байта выравнивания][байт памяти USB][три байта выравнивания]...

Сейчас я склонился к первому варианту, поскольку он больше похож на то, что следует из формул, приведенных в RM. У меня есть смутные подозрения, что моя конечная точка не работает именно потому, что при заполнении таблицы адресов буферов я все же промахнулся... Вот что им стоило нарисовать memory map, а?..

Сейчас сижу и медитирую над мануалами.

Если интересно, можете взглянуть на то, что я написал. Разумеется, это просто некий экспериментальный проект, до решения ему еще далеко... Пишу в IAR 6.30.

Как я уже говорил, сейчас проблема в том, что я не получаю прерывания по корректному приему. Есть подозрения, что это из-за неверной конфигурации таблицы адресов, произошедшей из-за неверного доступа к памяти USB. Понять бы, как в нее писать/читать корректно...

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

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

#include "stm32l1xx.h"

/* USB registers definitions */

#define USB_REG_BASE_ADDR               0x40005C00UL
#define USB_MEM_BASE_ADDR               0x40006000UL
#define USB_MEM_SIZE_B                  512UL

__no_init __root uint32_t USB_EP0R   @ (USB_REG_BASE_ADDR + 0x00);
__no_init __root uint32_t USB_EP1R   @ (USB_REG_BASE_ADDR + 0x04);
__no_init __root uint32_t USB_EP2R   @ (USB_REG_BASE_ADDR + 0x08);
__no_init __root uint32_t USB_EP3R   @ (USB_REG_BASE_ADDR + 0x0C);
__no_init __root uint32_t USB_EP4R   @ (USB_REG_BASE_ADDR + 0x10);
__no_init __root uint32_t USB_EP5R   @ (USB_REG_BASE_ADDR + 0x14);
__no_init __root uint32_t USB_EP6R   @ (USB_REG_BASE_ADDR + 0x18);
__no_init __root uint32_t USB_EP7R   @ (USB_REG_BASE_ADDR + 0x1C);

__no_init __root uint32_t USB_CNTR   @ (USB_REG_BASE_ADDR + 0x40);
__no_init __root uint32_t USB_ISTR   @ (USB_REG_BASE_ADDR + 0x44);
__no_init __root uint32_t USB_FNR    @ (USB_REG_BASE_ADDR + 0x48);
__no_init __root uint32_t USB_DADDR  @ (USB_REG_BASE_ADDR + 0x4C);
__no_init __root uint32_t USB_BTABLE @ (USB_REG_BASE_ADDR + 0x50);

/* Endpoint buffer description pseudo-register table definitions */

//Add other definitions as needed
#define USB_BTABLE_LOC                          0UL
#define USBMEM_OFFSET_RECALC(REC_NUM,REG_NUM)   (USB_MEM_BASE_ADDR + USB_BTABLE_LOC + (REC_NUM)*16UL + (REG_NUM)*4UL)
//The length of a table below; adjust as needed according to actual record count
#define USBMEM_TABLE_LENGTH_BYTES               32UL

//Endpoint 0 description record
__no_init __root uint32_t USB_ADDR0_TX     @ (USBMEM_OFFSET_RECALC(0,0));
__no_init __root uint32_t USB_COUNT0_TX    @ (USBMEM_OFFSET_RECALC(0,1));
__no_init __root uint32_t USB_ADDR0_RX     @ (USBMEM_OFFSET_RECALC(0,2));
__no_init __root uint32_t USB_COUNT0_RX    @ (USBMEM_OFFSET_RECALC(0,3));

//Endpoint 1 description record
__no_init __root uint32_t USB_ADDR1_TX     @ (USBMEM_OFFSET_RECALC(1,0));
__no_init __root uint32_t USB_COUNT1_TX    @ (USBMEM_OFFSET_RECALC(1,1));
__no_init __root uint32_t USB_ADDR1_RX     @ (USBMEM_OFFSET_RECALC(1,2));
__no_init __root uint32_t USB_COUNT1_RX    @ (USBMEM_OFFSET_RECALC(1,3));

//Endpoint 2 description record
__no_init __root uint32_t USB_ADDR2_TX     @ (USBMEM_OFFSET_RECALC(2,0));
__no_init __root uint32_t USB_COUNT2_TX    @ (USBMEM_OFFSET_RECALC(2,1));
__no_init __root uint32_t USB_ADDR2_RX     @ (USBMEM_OFFSET_RECALC(2,2));
__no_init __root uint32_t USB_COUNT2_RX    @ (USBMEM_OFFSET_RECALC(2,3));

//Endpoint 3 description record
__no_init __root uint32_t USB_ADDR3_TX     @ (USBMEM_OFFSET_RECALC(3,0));
__no_init __root uint32_t USB_COUNT3_TX    @ (USBMEM_OFFSET_RECALC(3,1));
__no_init __root uint32_t USB_ADDR3_RX     @ (USBMEM_OFFSET_RECALC(3,2));
__no_init __root uint32_t USB_COUNT3_RX    @ (USBMEM_OFFSET_RECALC(3,3));

//Add more (up to 8 total) endpoint records as needed

#define ENDP_BLOCKSIZE_32B                        (16UL<<10UL)
#define ENDP0_TXBUFFER_SIZE_BYTES                 32
#define ENDP0_TXBUFFER_START_LOC                  (USBMEM_TABLE_LENGTH_BYTES)
#define ENDP0_TXBUFFER_START_ABS                  (USB_MEM_BASE_ADDR + USBMEM_TABLE_LENGTH_BYTES*2UL)

#define ENDP0_RXBUFFER_SIZE_BYTES                 32
#define ENDP0_RXBUFFER_START_LOC                  (USBMEM_TABLE_LENGTH_BYTES + ENDP0_TXBUFFER_SIZE_BYTES)
#define ENDP0_RXBUFFER_START_ABS                  (USB_MEM_BASE_ADDR + (USBMEM_TABLE_LENGTH_BYTES + ENDP0_BUFFER_SIZE_BYTES)*2UL)

volatile uint8_t ENDP0_RAM_RX_buffer[32];
volatile uint8_t ENDP0_RAM_TX_buffer[32];

uint8_t DEVICE_DESCRIPTOR[] = {0x12,0x01,0x01,0x00,0x00,0x00,0x00,0x20,0x12,0x34,0x56,0x78,0x01,0x00,0x00,0x00,0x00,0x01};

/* Copy data from hardware USB buffer to RAM */
void USB_copy_from_RX_memory(uint32_t *HW_buffer,uint16_t size_bytes,uint8_t *RAM_buffer)
{
  uint16_t i,k;
  
  k=0;
  
  for (i=0; i<(size_bytes/2); i++)
  {
    RAM_buffer[k]=(HW_buffer[i] & 0x000000FF);
    RAM_buffer[k+1]=(HW_buffer[i] & 0x0000FF00) >> 8;
    k=+2;
  }
}

/* Copy data to hardware USB buffer from RAM */
void USB_copy_to_TX_memory(uint32_t *HW_buffer,uint16_t size_bytes,uint8_t *RAM_buffer)
{
  uint16_t i,k;
  
  k=0;
  
  for (i=0; i<(size_bytes/2); i++)
  {
    HW_buffer[i]=RAM_buffer[k] | (RAM_buffer[k+1]<<8);
    k+=2;
  }
}

void USB_clock_setup(void)
{
  /* System clock setup with USB module in mind */
  
  /* Switch to HSI to configure PLL */
  
  //Turn HSI on
  RCC->CR|=RCC_CR_HSION;
  while (!(RCC->CR & RCC_CR_HSIRDY))
  {
    //Make sure HSI is active
  }
  //Switch to HSI for core clocks
  RCC->CFGR=RCC_CFGR_SW_0;
  
  //Turn PLL off
  RCC->CR&=~RCC_CR_PLLON;
  while (RCC->CR & RCC_CR_PLLRDY)
  {
    //Make sure PLL is fully stopped
  }
  //Set PLL input to HSE, multiplication by 12 (96/2 = 48 MHz for USB), division to 4 (96/4 = 24 MHz SYSCLK)
  //All prescalers are set to 1
  RCC->CFGR|=RCC_CFGR_PLLDIV_1 | RCC_CFGR_PLLDIV_0 | RCC_CFGR_PLLMUL_2 | RCC_CFGR_PLLSRC;
  
  //Configure memory to work with 1 wait state (core clock above 16 MHz)
  //Enable 64-bit access
  FLASH->ACR |= FLASH_ACR_ACC64;  
  //Enable Prefetch Buffer
  FLASH->ACR |= FLASH_ACR_PRFTEN;
  //Flash 1 wait state 
  FLASH->ACR |= FLASH_ACR_LATENCY;
  
  //Turn PLL on
  RCC->CR|=RCC_CR_PLLON;
  while (!(RCC->CR & RCC_CR_PLLRDY))
  {
    //Make sure PLL is ready
  }
  
  /* Switch system clock to PLL */
  RCC->CFGR|=RCC_CFGR_SW_0 | RCC_CFGR_SW_1;
  
  //Turn on USB clock and port A clock (SYSCFG is used for USB pullup)
  RCC->AHBENR=RCC_AHBENR_GPIOAEN;
  RCC->APB1ENR=RCC_APB1ENR_USBEN;
  RCC->APB2ENR=RCC_APB2ENR_SYSCFGEN;
  
  //Now USB should have proper clock
}

void USB_hw_setup(void)
{
  volatile uint32_t i;
  
  /* General USB module setup */
  
  //Switching ON the analog part
  USB_CNTR&=~USB_CNTR_PDWN;
  for (i=0; i<500UL; i++)
  {
    //Wait for analog part to start-up
  }
  //Remove reset, initialize register
  USB_CNTR=0;
  //Enable interrupts: correct transfer, buffer error, any error, bus reset, interrupt on SOF,
  USB_CNTR|=USB_CNTR_CTRM | USB_CNTR_RESETM | USB_CNTR_SOFM | USB_CNTR_PMAOVRM | USB_CNTR_ERRM;
  //Clear possible spurious interrupts
  USB_ISTR=0;
  
  //Enable low prioruty USB interrupt
  NVIC_EnableIRQ(USB_LP_IRQn);
}

void USB_reset_setup(void)
{
  /* Endpoint 0 memory setup */
  
  //Set relative position of the buffer table
  USB_BTABLE=USB_BTABLE_LOC;
  
  //Endpoint 0 buffer configuration
  USB_ADDR0_TX=ENDP0_TXBUFFER_START_LOC;
  USB_ADDR0_RX=ENDP0_RXBUFFER_START_LOC;
  
  USB_COUNT0_TX=ENDP0_TXBUFFER_SIZE_BYTES;
  USB_COUNT0_RX=ENDP_BLOCKSIZE_32B; //Allocate 1 block of 32 bytes
  
  //Endpoint 0 is a control endpoint
  USB_EP0R=USB_EP0R_EP_TYPE_0 | USB_EP0R_STAT_TX_0 | USB_EP0R_STAT_TX_1;
  
  USB_copy_to_TX_memory((uint32_t*)ENDP0_TXBUFFER_START_ABS,18,DEVICE_DESCRIPTOR);
  
  //Set USB address to 0
  USB_DADDR=USB_DADDR_EF;
  
  //Allow USB pullup
  SYSCFG->PMC|=SYSCFG_PMC_USB_PU;
}

void USB_LP_IRQHandler(void)
{ 
  if (USB_ISTR & USB_ISTR_RESET)
  {
    /* Master issued RESET */
    //Re-initiaze HW
    USB_reset_setup();
    
    GPIOB->ODR|=GPIO_ODR_ODR_6;
  }
  
  if (USB_ISTR & USB_ISTR_CTR)
  {
    /* Some endpoint has received data */
    
    GPIOB->ODR|=GPIO_ODR_ODR_7;
    
    //Refresh endpoint state
    USB_EP0R=USB_EP0R_EP_TYPE_0 | USB_EP0R_STAT_TX;
  }
  
  if (USB_ISTR & USB_ISTR_SOF)
  {
    /* SOF sync detected */
  }
  
  if (USB_ISTR & USB_ISTR_PMAOVR)
  {
    /* Packet memory area buffer overrun/underrun */
  }
  
  if (USB_ISTR & USB_ISTR_ERR)
  {
    /* General reception error */
  }
  
  //All interrupt sources serviced
  USB_ISTR=0;
}

void main(void)
{
  volatile uint32_t i;
  
  USB_clock_setup();
  USB_hw_setup();
  USB_reset_setup();
  
  //Test LEDs
  RCC->AHBENR=RCC_AHBENR_GPIOBEN;
  //PB6 and PB7 are outputs
  GPIOB->MODER=GPIO_MODER_MODER7_0 | GPIO_MODER_MODER6_0;
  //All outputs push-pull
  GPIOB->OTYPER=0;
  //High speed mode
  GPIOB->OSPEEDR=GPIO_OSPEEDER_OSPEEDR7_0 | GPIO_OSPEEDER_OSPEEDR7_1 | GPIO_OSPEEDER_OSPEEDR6_0 | GPIO_OSPEEDER_OSPEEDR6_1;
  //Init to 0
  GPIOB->ODR=0;
  
  __enable_interrupt();
  
  while (1)
  {
    for (i=0; i<200000UL; i++)
    {
    }
    
    GPIOB->ODR=0;
  }
}
Если же обмен пакетами налажен, то дальше вся работа сводится к тому, чтобы на этапе настройки передавать нужные дескрипторы в ответ на соответствующие запросы, а после этого - просто писать и читать нужные буферы конечных точек. Дескрипторы тоже не так страшны - это write-only вещь. Сначала можно потратить часок на их формирование соответственно стандарту, после чего забыть про все и воспринимать их как предопределенные двоичные блоки.
Разница между теорией и практикой на практике гораздо больше, чем в теории.
Аватара пользователя
YS
Друг Кота
Сообщения: 7518
Зарегистрирован: Вс мар 29, 2009 22:09:05
Контактная информация:

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

Сообщение YS »

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

Вы не поверите, в чем была проблема. В контактах на отладке!!!

Перевоткнул хедер и прерывание заработало. Код инициализации, приведенный выше, корректен!

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

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

Сообщение IfoR »

2YS, спасибо большое за информацию! :) Сам я сейчас пока отложил на ненадолго разборки с USB. Но так с самим протоколом USB в общем плане разобрался. Ещё потребовало некоторых усилий разобраться в буферами в USB - оказалось, в блоке USB есть свой блок памяти со своим личным адресным пространством, и адреса должны указываться относительно относительно именно своего адресного пространства. Ох и мудрёно всё это, но да ладно.
Изображение
/dev/urandom - гигабайты информации.

OS: openSUSE 13.2 (x86_64)
Аватара пользователя
YS
Друг Кота
Сообщения: 7518
Зарегистрирован: Вс мар 29, 2009 22:09:05
Контактная информация:

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

Сообщение YS »

оказалось, в блоке USB есть свой блок памяти со своим личным адресным пространством, и адреса должны указываться относительно относительно именно своего адресного пространства.
О да, я тоже долго раскалял голову над этой частью даташита. :) Там, как я уже писал выше, не просто свое адресное пространство - оно еще и организовано хитро. :idea:

Итак, чего я добился на данный момент:

писать свой код обработки стандартных запросов (логику) я пока посчитал нецелесообразным - просто очистил движок USB от ST от наслоений их StdPeriphLib (по сути - заменил код инициализации на свой) и выколупал его из безобразной структуры проекта-примера. На основе своего кода инициализации и их движка я запилил USB-микрофон, который корректно распознается и с которого можно записывать.

На все у меня ушло примерно восемь дней. Как будет время, думаю написать статью. Но это по мере возможности...
Разница между теорией и практикой на практике гораздо больше, чем в теории.
Аватара пользователя
balmer
Это не хвост, это антенна
Сообщения: 1433
Зарегистрирован: Вс дек 02, 2012 03:13:48
Откуда: Калининград

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

Сообщение balmer »

YS писал(а):На основе своего кода инициализации и их движка я запилил USB-микрофон
Круто-круто. Ждем статью. Искал пример для USB Audio Input - так и не нашел хорошего.
Аватара пользователя
YS
Друг Кота
Сообщения: 7518
Зарегистрирован: Вс мар 29, 2009 22:09:05
Контактная информация:

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

Сообщение YS »

Искал пример для USB Audio Input - так и не нашел хорошего.
Я тоже сначала долго искал адекватный пример, надеялся, что не придется пробивать самому. :) В конце концов отчаялся, сел и разобрал сам с нуля. Действительно, в 99% статей все сводится к "поменяйте эти три значения тут".

ОК, если интерес есть, я постараюсь сделать статью. Правда, по причине того, что я пишу диплом, непонятно, когда это будет, так что интересующимся предлагаю задавать вопросы тут. На что смогу - отвечу. Единственно, предупреждаю сразу, что микрофон у меня не настоящий - АЦП я не настраивал, данные берутся из внутреннего буфера. Т.е. это просто тест передачи на компьютер. Но компьютер видит его как аудио-устройство USB и полностью уверен, что это настоящее устройство ввода. :)
Разница между теорией и практикой на практике гораздо больше, чем в теории.
Аватара пользователя
balmer
Это не хвост, это антенна
Сообщения: 1433
Зарегистрирован: Вс дек 02, 2012 03:13:48
Откуда: Калининград

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

Сообщение balmer »

Тут самый интересный вопрос - работает ли 192 КГц 16 бит стерео на USB Full Speed которое 12Mbps.

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

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

Сообщение YS »

Тут самый интересный вопрос - работает ли 192 КГц 16 бит стерео на USB Full Speed которое 12Mbps.
Думаю, ничто не должно этому мешать. Это поток примерно 800 кБ/с, 768 байт на один кадр USB при пределе в 1023 байта.

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

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

Сообщение IfoR »

2YS, да, с организацией то я тоже разобрался. :) Но пока я в таком состоянии, когда компьютер первый раз отправляет пакет в устройство 0 и endpoint 0 с запросом дескриптора, а в ответ мой контроллер в посылает какую-то хрень (т.е. пустой пакет). Ньда... Надо разбираться.
Хах, у самого сейчас последняя сессия и тоже буду делать диплом. :)
Изображение
/dev/urandom - гигабайты информации.

OS: openSUSE 13.2 (x86_64)
Аватара пользователя
YS
Друг Кота
Сообщения: 7518
Зарегистрирован: Вс мар 29, 2009 22:09:05
Контактная информация:

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

Сообщение YS »

Хах, у самого сейчас последняя сессия и тоже буду делать диплом.
О-о-о! Коллега! :) :beer:
Но пока я в таком состоянии, когда компьютер первый раз отправляет пакет в устройство 0 и endpoint 0 с запросом дескриптора, а в ответ мой контроллер в посылает какую-то хрень (т.е. пустой пакет).
Ага, и остановились там же. :) Только если говорить про меня, я не уверен, что конкретно контроллер посылает в ответ.

Скажу, что, дошед до этого этапа, я понял, что для того, чтобы отладить протокольную часть за разумное время, потребуется логический анализатор с декодированием USB, ну или что-то типа MDO4000. Можно и без этого, но тогда дело грозит затянуться, т.к. шину USB нельзя остановить, чтобы подумать, а возможности низкоуровневой отладки со стороны хоста крайне ограничены - например, как я понял, со стороны ОС невозможно простыми средствами просмотреть обмен до момента энумерации. :idea:

Сам по себе протокол не так сложен, но он имеет кучу стадий обмена на этапе регистрации устройства на шине, когда устройства еще по факту не видно в системе, и потому отладка представляет сложности.

Можно бы ограничиться просто DSO, но, учитывая количество передаваемых служебных данных, расшифровывать их руками не улыбается, тем более, учитывая, что у меня отнюдь не Tektronix, запоминающий двадцать миллионов выборок, появляются еще и проблемы с захватом пакета. :)

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

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

Сообщение IfoR »

Сегодня решил продолжить штурм. В общем оказалось, что я немного неправильно настроил тактирование. Ядро работало как и должно на частоте 72 МГц, но там ещё есть делитель частоты на 1.5 для USB модуля (для получения частоты 48 МГц). Я по наявняку думал, что чтобы его включить, нужно установить бит в RCC->CFGR, а оказывается, после уточнения в документации, наоборот - его нужно сбросить для его включения. После этого USB стал работать более адекватно. Теперь, как и должно, появляется сигнал о том что приходит SETUP пакет (В USB->ISTR стоит бит CTR, в USB->EP0R стоят биты CTR_RX и SETUP), который по идеи должен быть запросом дескриптора, но вот только в буфере я не вижу никаких входящих данных. BTABLE установлен в 0, ADDR0_RX = 0x40, COUNT0_RX = 0x40 (длина 64 байта). Предварительно, для наглядности, я обнулил область PMA и в итоге RX буфере (0x4006080), при поступлении прерывания о приёме пакета, остался по прежнему обнулён.
Изображение
Эх, да что же это такое?
Изображение
/dev/urandom - гигабайты информации.

OS: openSUSE 13.2 (x86_64)
Аватара пользователя
YS
Друг Кота
Сообщения: 7518
Зарегистрирован: Вс мар 29, 2009 22:09:05
Контактная информация:

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

Сообщение YS »

Ядро работало как и должно на частоте 72 МГц
В порядке чеклиста: FLASH правильно сконфигурирован? При частотах выше 16 МГц надо включать особый метод чтения из FLASH (дополнительные wait states), иначе память не будет успевать.
Разница между теорией и практикой на практике гораздо больше, чем в теории.
Аватара пользователя
IfoR
Поставщик валерьянки для Кота
Сообщения: 2029
Зарегистрирован: Сб ноя 15, 2008 10:09:56
Откуда: г. Тула
Контактная информация:

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

Сообщение IfoR »

Само собой. Иначе бы у меня было много других проблем. :)
В своё время тоже недоумевал по этому поводу, пока не понял, что нужно поставить задержку на FLASH, а быстроисполняемый код переписывать в ОЗУ и от туда запускать.
Изображение
/dev/urandom - гигабайты информации.

OS: openSUSE 13.2 (x86_64)
Аватара пользователя
IfoR
Поставщик валерьянки для Кота
Сообщения: 2029
Зарегистрирован: Сб ноя 15, 2008 10:09:56
Откуда: г. Тула
Контактная информация:

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

Сообщение IfoR »

В общем, взялся опять за это дело и никак не сдвинулся с места. Единственно что попробовал поменять кварц на 16 МГц (с предварительным делением на 2) - абсолютно тоже самое. Может кто-то заметит что-то? Я ничего неправильного так и не нашёл.

Настройка тактирования и вообще:

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

void config()
{
	// Активируем системные прерывания при ошибках выполнения
	SCB->SHCSR |= SCB_SHCSR_BUSFAULTENA | SCB_SHCSR_USGFAULTENA | SCB_SHCSR_MEMFAULTENA;

	// Установка задержки чтения из памяти FLASH для высокой частоты тактирования
	 FLASH->ACR |= FLASH_ACR_PRFTBE;
	 FLASH->ACR |= FLASH_ACR_LATENCY_2;

	// Настройка частоты тактирования от кварца (8) 16 МГЦ на частоту 72 МГц через PLL x9
	// USB - 48МГц
	// APB2 - 72 МГц
	// APB1 - 36 МГц

	// Перезапускаем генерацию. Остановка генерации от кварца
	RCC->CFGR = 0;
	RCC->CR &= ~(RCC_CR_PLLON | RCC_CR_HSEON);
	while ((RCC->CR & RCC_CR_HSERDY) || (RCC->CR & RCC_CR_PLLRDY)) {};

	// Запуск кварцевого резонатора
	RCC->CR |= RCC_CR_HSEON;
	while (!(RCC->CR & RCC_CR_HSERDY)); // Ожидание включения кварца

	RCC->CR |= RCC_CR_CSSON; // Запускаем систему контроля тактирования

	// Заводим на умножитель частоту с кварца. Делим на 2. Умножаем на 9. Включаем деления частоты тактирования USB на 1.5 (48 МГц).
	RCC->CFGR |= RCC_CFGR_PLLMULL9 | RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLXTPRE_HSE_Div2;
	RCC->CFGR &= ~(RCC_CFGR_USBPRE);
	RCC->CR |= RCC_CR_PLLON; // Запускаем умножитель
	while (!(RCC->CR & RCC_CR_PLLRDY)); // Ожидание включения умножителя

	// Установка тактовой частоты с умножителя. Деление AHB: 1 (72 МГц). Деление APB1: 2 (36 МГц).
	RCC->CFGR |= RCC_CFGR_HPRE_DIV1 | RCC_CFGR_PPRE1_DIV2 | RCC_CFGR_SW_PLL;
	while ((RCC->CFGR&RCC_CFGR_SWS)!=0x08); // Ожидание переключения на PLL.

	// ---------- С этой строки частота тактирования 72 МГц --------------

	// Отключение внутреннего тактового генератора
	// НЕ ОТКЛЮЧАТЬ ПРИ РАБОТЕ С SWD!
	//RCC->CR &= ~RCC_CR_HSION;

	RCC->CFGR |= RCC_CFGR_ADCPRE_DIV8; // Деление частоты для АЦП

	// Активация тактирования
	RCC->APB2ENR =  RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN | RCC_APB2ENR_IOPDEN | // Порты
					RCC_APB2ENR_ADC1EN | // АЦП
					RCC_APB2ENR_AFIOEN; // Альтернативные функции
	// Активация тактирования
	RCC->APB1ENR = RCC_APB1ENR_TIM6EN | RCC_APB1ENR_USBEN;

	// Настройка всех выводов выводов
	GPIOA->CRL = CONF2CRL(PORTACONF);
	GPIOA->CRH = CONF2CRH(PORTACONF);
	GPIOB->CRL = CONF2CRL(PORTBCONF);
	GPIOB->CRH = CONF2CRH(PORTBCONF);
	GPIOC->CRL = CONF2CRL(PORTCCONF);
	GPIOC->CRH = CONF2CRH(PORTCCONF);
	GPIOD->CRL = CONF2CRL(PORTDCONF);

	GPIOA->ODR = 0;
	GPIOB->ODR = 0;
	GPIOC->ODR = (1<<B1_PC) | (1<<B2_PC) | (1<<B3_PC) | (1<<B4_PC) | (1<<B5_PC);
	GPIOD->ODR = 0;
}
Инициализация USB

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

void USBConfig()
{
	// Включаем аналоговые цепи USB
	USB->CNTR = USB_CNTR_FRES;
	USBwait(255); // Ожидание включения модуля

	// Сбрасываем драйвер
	USB->CNTR = 0;

	// Сбрасываем регистр флагов прерываний
	USB->ISTR = 0;

	// Разрешаем прерывания
	//USB->CNTR |= USB_CNTR_RESETM | USB_CNTR_SUSPM | USB_CNTR_WKUPM | USB_CNTR_SOFM | USB_CNTR_ESOFM | USB_CNTR_CTRM | USB_CNTR_ERRM | USB_CNTR_PMAOVRM;
	USB->CNTR = USB_CNTR_RESETM | USB_CNTR_SUSPM | USB_CNTR_WKUPM;

	// Обнуляем буффер
	for(uint16_t* i = USB_PMA_BASE; i <= USB_PMA_BASE+0x3FF; i+=2) *i = 0;

	// Активируем прерывания
	NVIC_EnableIRQ(USBWakeUp_IRQn);
	NVIC_EnableIRQ(USB_HP_CAN1_TX_IRQn);
	NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn);

	LCD_debug("USBConfig OK");
}
Обработчик прерывания
Хедер

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

#define maxPacketSize 64 // Максимальный размер пакета

#define BTABLE_ADDRESS 0 // Адрес таблицы буфферного дескриптора
#define EP0_ADDR_RX 0x40				// Адрес буффера передачи
#define EP0_ADDR_TX (EP0_ADDR_RX + maxPacketSize)	// Адрес буффера передачи

#define bufferDescriptor ((USB_BufferDescriptor *)(USB_PMA_BASE + BTABLE_ADDRESS*2))

// Дескриптор устройства
struct
{
	uint8_t		bLength;			// Размер дескриптора в байтах: 18
	uint8_t		bDescriptorType;	// Тип - DeviceDescriptor: 0x01
	uint16_t	bcdUSB;				// Номер спецификации USB в BCD
	uint8_t		bDeviceClass;		// Код класса
	uint8_t		bDeviceSubClass;	// Код подкласса
	uint8_t		bDeviceProtocol;	// Код протокола
	uint8_t		bMaxPacketSize;		// Максимальный размер пакета
	uint16_t	idVendor;			// Vendor ID (VID)
	uint16_t	idProduct;			// Product ID (PID)
	uint16_t	bcdDevice;			// Номер версии устройства в BCD
	uint8_t		iManufacturer;		// Индекс строки, описывающий производителя
	uint8_t		iProduct;			// Индекс строки, описывающий продукт
	uint8_t		iSerialNumber;		// Индекс строки, содержащий серийный номер
	uint8_t		bNumConfigurations;	// Количество возможных конфигураций

} deviceDescriptorStruct;

// Дескриптор конфигурации
struct
{
	uint8_t		bLength;			// Размер дескриптора в байтах: 9
	uint8_t		bDescriptorType;	// Тип: 0x02
	uint16_t	wTotalLength;		// Полная длинна возвращаемых данных в байтах
	uint8_t		bNumInterfaces;		// Количество интерфейсов
	uint8_t		bConfigurationValue;// Величина, используемая как аргумент для выбора этой конфигурации
	uint8_t		iConfiguration;		// Индекс строкового дескриптора, описывающего эту конфиигурацию
	uint8_t		bmAttributes ;		// D7 = 1, D6 - Самозапитываемое, В5 - удалённое пробуждение. D4-D0=0
	uint8_t		bMaxPower;			// Максимальное энергопотреюление в 2 мА.
} configurationDescriptorStruct;

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

void USB_LP_CAN1_RX0_IRQHandler()
{ // Приём данных


	// Разбор событий
	// Сброс передатчика
	if (USB->ISTR & USB_ISTR_RESET)
	{
		LCD_debug("USBRX Reset");
		// Настраиваем 0-ю конечную точку


		// Устанавливаем адресса буфферов
		USB->BTABLE = BTABLE_ADDRESS; // Установка адреса таблицы буфферов в помяти USB

		// Конечная точка 0
		// Тип: Control
		// RX: Valid
		// TX: Stall
		USB->EP0R = USB_EP0R_EP_TYPE_0 |
				    ((USB->EP0R ^ USB_EP0R_STAT_RX) & USB_EP0R_STAT_RX) |
				    ((USB->EP0R ^ USB_EP0R_STAT_TX_0) & USB_EP0R_STAT_TX);

		bufferDescriptor->ADDR0_RX = EP0_ADDR_RX;
		bufferDescriptor->ADDR0_TX = EP0_ADDR_TX;
		bufferDescriptor->COUNT0_RX = maxPacketSize;
		bufferDescriptor->COUNT0_TX = maxPacketSize;

		USB->DADDR = USB_DADDR_EF;	// Активация устройства
	}
	if (USB->ISTR & USB_ISTR_CTR) // Пришёл пакет
	{
		USB->EP0R = (USB->EP0R & ~(USB_EP0R_STAT_RX | USB_EP0R_STAT_TX)) |
				((USB->EP0R ^ USB_EP0R_STAT_RX_1) & USB_EP0R_STAT_RX) |
				((USB->EP0R ^ USB_EP0R_STAT_TX_1) & USB_EP0R_STAT_TX);

		LCD_debug("USBRX CorrectTR");

		if ((USB->EP0R & USB_EP0R_CTR_RX) && (USB->EP0R & USB_EP0R_SETUP))
		{ // Пришёл SETUP пакет


		 // --- Здесь в PMA нет ничего! ---
		 LCD_debug("setup");

		}

		USB->EP0R = (USB->EP0R & ~(USB_EP0R_STAT_RX | USB_EP0R_STAT_TX)) |
				((USB->EP0R ^ USB_EP0R_STAT_RX) & USB_EP0R_STAT_RX) |
				((USB->EP0R ^ USB_EP0R_STAT_TX_0) & USB_EP0R_STAT_TX);
	}
	if (USB->ISTR & USB_ISTR_PMAOVR)
	{
		LCD_debug("USBRX PMAOver");
	}
	if (USB->ISTR & USB_ISTR_ERR)
	{
		LCD_debug("USBRX Error");
	}
	if (USB->ISTR & USB_ISTR_WKUP)
	{
		LCD_debug("USBRX WakeUp");
	}
	if (USB->ISTR & USB_ISTR_SUSP)
	{
		//LCD_debug("USBRX SuspendReq");
	}
	if (USB->ISTR & USB_ISTR_SOF)
	{
		LCD_debug("USBRX StartOfFra");
	}
	if (USB->ISTR & USB_ISTR_ESOF)
	{
		//LCD_debug("USBRX ExpectSOF");
	}

	USB->ISTR = 0;
}
Изображение
/dev/urandom - гигабайты информации.

OS: openSUSE 13.2 (x86_64)
Ответить

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