Ручная настройка USB
- IfoR
- Поставщик валерьянки для Кота
- Сообщения: 2029
- Зарегистрирован: Сб ноя 15, 2008 10:09:56
- Откуда: г. Тула
- Контактная информация:
Ручная настройка USB
Люди, помогите, пожалуйста, разобраться с модулем USB на примере stm32f103 на уровне регистров. Пересмотрел весь интернет, но практически везде все объяснение обходится словами "возьмем вот эту вот библиотеку, поменяем пару параметров в дескрипторах и теперь мы можем мигать светодиодами с компа! Все просто!" Это, конечно, круто, но такой уровень абстракции лично для меня тяжол для изучения. В документации описание есть, но мало и на трудном для вникания английском. В сети нигде ничего нет, кто бы описывал работу с USB на примитивном уровне. Только везде драйвера, взятые из примера или ещё откуда. Хотя видел один сверхполезный коммент, где один человек в шутку описывал кратко что нужно сделать и как это нудно, когда есть драйвера, но я из этого единственного коммента хоть немного понял в каком порядке что происходит. Смотрел исходники библиотек, сначала там идёт тоже, что написано в документации: включение модуля, настройка прерываний. Все это я сделал и у себя в коде. Но как дело дошло до работы с регистрами BTABLE и EPxR, тут я и встал. Знания английского уже не хватает осилить написанное в документации, да и не все детали там описаны. Может кто знает хоть какую нибудь информацию об этом, где можно почерпнуть или кто может знает?
- Реклама
- balmer
- Это не хвост, это антенна
- Сообщения: 1433
- Зарегистрирован: Вс дек 02, 2012 03:13:48
- Откуда: Калининград
Re: Ручная настройка USB
"Для себя" изучать тщательно USB модуль не имеет смысла. Чтобы более-менее разобраться, придется прочитать книжку на 300-400 страниц. А потом для Windows придется еще писать драйвера... Пытался прочитать "избранные главы" по аудиоинтерфейсу - ну его нафиг, только голова кругом пошла.
- IfoR
- Поставщик валерьянки для Кота
- Сообщения: 2029
- Зарегистрирован: Сб ноя 15, 2008 10:09:56
- Откуда: г. Тула
- Контактная информация:
Re: Ручная настройка USB
Вот так вот потом мир и скатывается, когда все твердят "для себя изучать тщательно что-то не имеет сысла", а как оно работает никто объяснить не может.
Я пока не ставлю для себя цели делать какое-то сложно устройство со своими драйверами, мне бы пока просто с любым HID разобраться. Да даже, как довести контроллер до такого состояния, чтобы он был-бы в состоянии принимать пакеты. Пока мне только удалось получить состояние, когда контроллер, при подачи питания, вырабатывает прерывание от USB модуля в котором, по идее, нужно настраивать все endpoint-ы и, в частности, 0-й endpoint для идентификации устройства хостом. И что для меня интересно, при простом подачи питания на контроллер, вырабатывается прерывания SUSP и ESOF (типа не обнаружена передача от хоста в течении 3 мс, вроде логично), а есть подключить его сразу по USB к компу, то ничего не вырабатывается. Вообще нет никаких прерываний. Получается, я и модуль не могу настроить, потому что не знаю о его готовности к этому.
И вот ответы на подобные и другие вещи я и не знаю где найти.
Кстати, а что за книжка? В ней есть описание работы с USB модулем STM32 на регистровом уровне?
Я пока не ставлю для себя цели делать какое-то сложно устройство со своими драйверами, мне бы пока просто с любым HID разобраться. Да даже, как довести контроллер до такого состояния, чтобы он был-бы в состоянии принимать пакеты. Пока мне только удалось получить состояние, когда контроллер, при подачи питания, вырабатывает прерывание от USB модуля в котором, по идее, нужно настраивать все endpoint-ы и, в частности, 0-й endpoint для идентификации устройства хостом. И что для меня интересно, при простом подачи питания на контроллер, вырабатывается прерывания SUSP и ESOF (типа не обнаружена передача от хоста в течении 3 мс, вроде логично), а есть подключить его сразу по USB к компу, то ничего не вырабатывается. Вообще нет никаких прерываний. Получается, я и модуль не могу настроить, потому что не знаю о его готовности к этому.
Кстати, а что за книжка? В ней есть описание работы с USB модулем STM32 на регистровом уровне?
- balmer
- Это не хвост, это антенна
- Сообщения: 1433
- Зарегистрирован: Вс дек 02, 2012 03:13:48
- Откуда: Калининград
Re: Ручная настройка USB
Нету
. Лично мне хватило краткого описания. Длинное описание тоже читал, но после трех-четырех дней оценил объем информации который надо усвоить и забил. Люди с форума cqham/SDR тоже пишут - сложный стандарт USB, освоить можно, но сильно проще поднять 100 МБит сеть, если хочется быстрого обмена между устройством и девайсом.
- IfoR
- Поставщик валерьянки для Кота
- Сообщения: 2029
- Зарегистрирован: Сб ноя 15, 2008 10:09:56
- Откуда: г. Тула
- Контактная информация:
Re: Ручная настройка USB
Спасибо! Интересно почитать. А вот с контроллером видимо придётся разбираться по исходникам.
- Реклама
Re: Ручная настройка USB
Я довольно много возился с USB - делал устройство и дрйвер к нему для Мака, еще до OS X. OS 9 - хрень еще та. Даже свой VID получили
Лет 14 назад.
И согласен, что протокол не простой для изучения. Потому что сделан чтобы покрыть все что шевелится. Чтобы понять биты в регистрах, надо четко представлять что такое endpoints и т.д.
Если есть желание вникнуть в суть на низком уровне, без толстых книг не обойтись. Еще можно поглядеть код имплементации на AVR чисто программно.
А если надо задействовать USB на STM32, есть примеры от ST. Я буквально вчера на основе generic HID слепил тестовую систему за пол дня.
И согласен, что протокол не простой для изучения. Потому что сделан чтобы покрыть все что шевелится. Чтобы понять биты в регистрах, надо четко представлять что такое endpoints и т.д.
Если есть желание вникнуть в суть на низком уровне, без толстых книг не обойтись. Еще можно поглядеть код имплементации на AVR чисто программно.
А если надо задействовать USB на STM32, есть примеры от ST. Я буквально вчера на основе generic HID слепил тестовую систему за пол дня.
Re: Ручная настройка USB
О, приятно видеть единомышленника. Я тоже изумился косоте, запутанности и размеру стандартной 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 запилили из благих побуждений - для большей гибкости. Если бы они сделали четко выделенные буферы для каждой конечной точки, проблем бы не было.
Если же обмен пакетами налажен, то дальше вся работа сводится к тому, чтобы на этапе настройки передавать нужные дескрипторы в ответ на соответствующие запросы, а после этого - просто писать и читать нужные буферы конечных точек. Дескрипторы тоже не так страшны - это write-only вещь. Сначала можно потратить часок на их формирование соответственно стандарту, после чего забыть про все и воспринимать их как предопределенные двоичные блоки.
Сейчас я застрял на том, что прерывание 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;
}
}
Разница между теорией и практикой на практике гораздо больше, чем в теории.
Re: Ручная настройка USB
Надеюсь, модераторы простят мне два сообщения подряд ради такого случая.
Вы не поверите, в чем была проблема. В контактах на отладке!!!
Перевоткнул хедер и прерывание заработало. Код инициализации, приведенный выше, корректен!
Все, буду реализовывать непосредственно протокол.
Вы не поверите, в чем была проблема. В контактах на отладке!!!
Перевоткнул хедер и прерывание заработало. Код инициализации, приведенный выше, корректен!
Все, буду реализовывать непосредственно протокол.
Разница между теорией и практикой на практике гораздо больше, чем в теории.
- IfoR
- Поставщик валерьянки для Кота
- Сообщения: 2029
- Зарегистрирован: Сб ноя 15, 2008 10:09:56
- Откуда: г. Тула
- Контактная информация:
Re: Ручная настройка USB
2YS, спасибо большое за информацию!
Сам я сейчас пока отложил на ненадолго разборки с USB. Но так с самим протоколом USB в общем плане разобрался. Ещё потребовало некоторых усилий разобраться в буферами в USB - оказалось, в блоке USB есть свой блок памяти со своим личным адресным пространством, и адреса должны указываться относительно относительно именно своего адресного пространства. Ох и мудрёно всё это, но да ладно.
Re: Ручная настройка USB
О да, я тоже долго раскалял голову над этой частью даташита.оказалось, в блоке USB есть свой блок памяти со своим личным адресным пространством, и адреса должны указываться относительно относительно именно своего адресного пространства.
Итак, чего я добился на данный момент:
писать свой код обработки стандартных запросов (логику) я пока посчитал нецелесообразным - просто очистил движок USB от ST от наслоений их StdPeriphLib (по сути - заменил код инициализации на свой) и выколупал его из безобразной структуры проекта-примера. На основе своего кода инициализации и их движка я запилил USB-микрофон, который корректно распознается и с которого можно записывать.
На все у меня ушло примерно восемь дней. Как будет время, думаю написать статью. Но это по мере возможности...
Разница между теорией и практикой на практике гораздо больше, чем в теории.
- balmer
- Это не хвост, это антенна
- Сообщения: 1433
- Зарегистрирован: Вс дек 02, 2012 03:13:48
- Откуда: Калининград
Re: Ручная настройка USB
Круто-круто. Ждем статью. Искал пример для USB Audio Input - так и не нашел хорошего.YS писал(а):На основе своего кода инициализации и их движка я запилил USB-микрофон
Re: Ручная настройка USB
Я тоже сначала долго искал адекватный пример, надеялся, что не придется пробивать самому.Искал пример для USB Audio Input - так и не нашел хорошего.
ОК, если интерес есть, я постараюсь сделать статью. Правда, по причине того, что я пишу диплом, непонятно, когда это будет, так что интересующимся предлагаю задавать вопросы тут. На что смогу - отвечу. Единственно, предупреждаю сразу, что микрофон у меня не настоящий - АЦП я не настраивал, данные берутся из внутреннего буфера. Т.е. это просто тест передачи на компьютер. Но компьютер видит его как аудио-устройство USB и полностью уверен, что это настоящее устройство ввода.
Разница между теорией и практикой на практике гораздо больше, чем в теории.
- balmer
- Это не хвост, это антенна
- Сообщения: 1433
- Зарегистрирован: Вс дек 02, 2012 03:13:48
- Откуда: Калининград
Re: Ручная настройка USB
Тут самый интересный вопрос - работает ли 192 КГц 16 бит стерео на USB Full Speed которое 12Mbps.
Впрочем в последнее время мне разонравилась сама идея делать SDR через звуковую карту. Более опытные товарищи используют сетевой интерфейс. И это видимо правильно. Компьютер шумит ужастно, а используя сетевой интерфейс можно разнести девайсы как по питанию так и в пространстве.
Впрочем в последнее время мне разонравилась сама идея делать SDR через звуковую карту. Более опытные товарищи используют сетевой интерфейс. И это видимо правильно. Компьютер шумит ужастно, а используя сетевой интерфейс можно разнести девайсы как по питанию так и в пространстве.
Re: Ручная настройка USB
Думаю, ничто не должно этому мешать. Это поток примерно 800 кБ/с, 768 байт на один кадр USB при пределе в 1023 байта.Тут самый интересный вопрос - работает ли 192 КГц 16 бит стерео на USB Full Speed которое 12Mbps.
Ну, коли уж мы о разнесении, самое время вспомнить про оптический S/PDIF.
Разница между теорией и практикой на практике гораздо больше, чем в теории.
- IfoR
- Поставщик валерьянки для Кота
- Сообщения: 2029
- Зарегистрирован: Сб ноя 15, 2008 10:09:56
- Откуда: г. Тула
- Контактная информация:
Re: Ручная настройка USB
2YS, да, с организацией то я тоже разобрался.
Но пока я в таком состоянии, когда компьютер первый раз отправляет пакет в устройство 0 и endpoint 0 с запросом дескриптора, а в ответ мой контроллер в посылает какую-то хрень (т.е. пустой пакет). Ньда... Надо разбираться.
Хах, у самого сейчас последняя сессия и тоже буду делать диплом.
Хах, у самого сейчас последняя сессия и тоже буду делать диплом.
Re: Ручная настройка USB
О-о-о! Коллега!Хах, у самого сейчас последняя сессия и тоже буду делать диплом.
Ага, и остановились там же.Но пока я в таком состоянии, когда компьютер первый раз отправляет пакет в устройство 0 и endpoint 0 с запросом дескриптора, а в ответ мой контроллер в посылает какую-то хрень (т.е. пустой пакет).
Скажу, что, дошед до этого этапа, я понял, что для того, чтобы отладить протокольную часть за разумное время, потребуется логический анализатор с декодированием USB, ну или что-то типа MDO4000. Можно и без этого, но тогда дело грозит затянуться, т.к. шину USB нельзя остановить, чтобы подумать, а возможности низкоуровневой отладки со стороны хоста крайне ограничены - например, как я понял, со стороны ОС невозможно простыми средствами просмотреть обмен до момента энумерации.
Сам по себе протокол не так сложен, но он имеет кучу стадий обмена на этапе регистрации устройства на шине, когда устройства еще по факту не видно в системе, и потому отладка представляет сложности.
Можно бы ограничиться просто DSO, но, учитывая количество передаваемых служебных данных, расшифровывать их руками не улыбается, тем более, учитывая, что у меня отнюдь не Tektronix, запоминающий двадцать миллионов выборок, появляются еще и проблемы с захватом пакета.
В общем, оценив все это, я взял движок протокола от ST.
Разница между теорией и практикой на практике гораздо больше, чем в теории.
- IfoR
- Поставщик валерьянки для Кота
- Сообщения: 2029
- Зарегистрирован: Сб ноя 15, 2008 10:09:56
- Откуда: г. Тула
- Контактная информация:
Re: Ручная настройка USB
Сегодня решил продолжить штурм. В общем оказалось, что я немного неправильно настроил тактирование. Ядро работало как и должно на частоте 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), при поступлении прерывания о приёме пакета, остался по прежнему обнулён.

Эх, да что же это такое?
Эх, да что же это такое?
Re: Ручная настройка USB
В порядке чеклиста: FLASH правильно сконфигурирован? При частотах выше 16 МГц надо включать особый метод чтения из FLASH (дополнительные wait states), иначе память не будет успевать.Ядро работало как и должно на частоте 72 МГц
Разница между теорией и практикой на практике гораздо больше, чем в теории.
- IfoR
- Поставщик валерьянки для Кота
- Сообщения: 2029
- Зарегистрирован: Сб ноя 15, 2008 10:09:56
- Откуда: г. Тула
- Контактная информация:
Re: Ручная настройка USB
Само собой. Иначе бы у меня было много других проблем. 
В своё время тоже недоумевал по этому поводу, пока не понял, что нужно поставить задержку на FLASH, а быстроисполняемый код переписывать в ОЗУ и от туда запускать.
В своё время тоже недоумевал по этому поводу, пока не понял, что нужно поставить задержку на FLASH, а быстроисполняемый код переписывать в ОЗУ и от туда запускать.
- IfoR
- Поставщик валерьянки для Кота
- Сообщения: 2029
- Зарегистрирован: Сб ноя 15, 2008 10:09:56
- Откуда: г. Тула
- Контактная информация:
Re: Ручная настройка USB
В общем, взялся опять за это дело и никак не сдвинулся с места. Единственно что попробовал поменять кварц на 16 МГц (с предварительным делением на 2) - абсолютно тоже самое. Может кто-то заметит что-то? Я ничего неправильного так и не нашёл.
Настройка тактирования и вообще:
Инициализация USB
Обработчик прерывания
Хедер
Настройка тактирования и вообще:
Код: Выделить всё
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;
}Код: Выделить всё
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;
}


