USB full-speed device interface - это по сути контроллер в контроллере, самостоятельно выполняющий все низкоуровневые операции USB-протокола.
С точки зрения программы в МК, USB представляется как набор буферов (в терминологии USB - endpoints) для обмена данными и регистров для управления этими буферами и процессом в целом.
Обмен данными происходит в режиме мастер-слейв (мастер - хост, слейв - девайс), по своей инициативе девайс никаких данных не передаёт - только по запросу от хоста.
Больше того, МК вообще не передаёт данные, а только готовит их для передачи - заполняет буфер и устанавливает флажки готовности, а хост потом "забирает" эти данные когда ему вздумается.
Весь процесс выглядит примерно так.
Если хост захочет что-то получить от девайса, то он будет делать периодические запросы с указанием из какого конкретно endpoint-буфера он хочет получить данные.
Если МК уже выставил готовность endpoint к передаче, то контроллер USB сам, без участия процессора МК, передаст хосту данные и сбросит флажки готовности endpoint.
Если же готовность enpoint к передаче не была выставлена, то хост будет повторять попытки.
И в обратную сторону.
Если хост захочет что-то передать девайсу, то он будет делать периодические запросы, предлагая принять данные и указывая при этом для какого конкретно endpoint-буфера эти данные предназначены.
Если МК уже выставил готовность endpoint к приёму, то контроллер USB сам, без участия процессора МК, примет от хоста данные и сбросит флажки готовности endpoint.
Если же готовность endpoint к приёму не была выставлена, то хост будет повторять попытки.
Для управления endpoint-буферами предназначены регистры EPnR.
Их восемь штук, они все одинаковые, и поскольку endpoints в USB двунаправленные, то каждый регистр может управлять двумя буферами - на приём (RX) и на передачу (TX).
Я выделил жёлтеньким две почти одинаковые группы битов, отличающиеся только тем, что первая управляет приёмником, а вторая - передатчиком.
То, что я выше называл флажками готовности - это биты STAT_{RX|TX} (status bits), вот их значения:
00 - DISABLED - буфер вообще не работает или просто не существует;
01 - STALL - буфер не работает для текущего запроса, повторно запрашивать бесполезно;
10 - NAK - буфер временно не готов, но можно и нужно запрашивать повторно;
11 - VALID - буфер готов.
Выставить готовность - это установить статус VALID, а по окончании приёма-передачи (в терминологии USB - транзакции) контроллер USB сам сбросит готовность - установит статус NAK.
Биты CTR_{RX|TX} (correct transfer) - признак окончания транзакции, устанавливается контроллером USB когда данные приняты или переданы, и МК может перечитать приёмный буфер или перезаписать передающий.
Биты DTOG_{RX|TX} (data toggle) контроллер USB обычно благополучно разруливает сам, и я не буду затуманивать текст лишними деталями, скажу только, что этот бит надо сбрасывть в начале транзакций.
Важная особенность всех этих битовых полей - спец способы установки, обозначенные на картинке:
rc_w0 - только сброс - запись нуля записывает ноль, запись единицы игнорируется;
t - переключение (toggle) - запись единицы инвертирует бит, запись нуля игнорируется.
IMHO, проще всего устанавливать эти поля исключающим "или".
Вот табличка для toggle-логики:
Было Запись Стало 0 0 0 0 1 1 1 0 1 1 1 0
Видно, что {Запись} == {Было}^{Стало}, т.е. чтобы выставить нужные значения t-битов:
EPnR ^= {нужные значения t-битов}
Биты EA - endpoint адрес.
Endpoints в USB адресуются от нуля до 15.
Казалось бы, зачем указывать адрес, если регистры EPnR уже сами пронумерованы?
Фокус в том, что номер регистра EPnR соответствует не адресу endpoint, а порядковому номеру пары буферов, которыми этот регистр управляет.
Теоретически, восемь регистров EPnR могут быть настроены на работу с любыми восемью адресами из 16, но практически лучше настраивать так, чтобы номера регистров совпадали с адресами.
Биты EP_TYPE - endpoint type.
00 - bulk
01 - control
10 - isochronous
11 - interrupt
Почитать про типы в популярном изложении (да и вообще про USB) можно, например, тут - http://www.usbmadesimple.co.uk/index.html или тут - http://www.beyondlogic.org/usbnutshell/usb1.shtml
Бит SETUP - дополнительный признак окончания транзакции для endpoint с нулевым адресом и типом control.
Endpoint c нулевым адресом используется в USB не только для обмена данными, но и для управления девайсом, в частности для первоначальной конфигурации девайса хостом (enumeration).
Бит SETUP устанавливается контроллером USB, если закончившаяся транзакция была транзакцией управления, а сбрасывается по сбросу бита CTR_RX.
Пример функции приёма данных.
int USBGet (uint16_t* a) { uint32_t r = USB->EP1R ^ 0x3000; if (r & 0xB000) { for (int i = 0; i < 32; i++) a[i] = (uint16_t) (*USBRXD1)[i]; USB->EP1R = (r & ~0xC070) | 0x80; return 0; // ok } else { return 1; // fail } }
Пример функции передачи (а точнее - подготовки передачи) данных.
int USBPut (uint16_t* a) { uint32_t r = USB->EP1R ^ 0x30; if (r & 0xB0) { for (int i = 0; i < 32; i++) (*USBTXD1)[i] = (uint32_t) a[i]; USBTABLE->tx1cnt = 64; USB->EP1R = (r & ~0x70C0) | 0x8000; return 0; // ok } else { return 1; // fail } }
В этих примерах кроме манипуляций с регистром EP1R фигурируют буферы данных (*USBRXD1 и *USBTXD1) и счётчик байтов (USBTABLE->tx1cnt).
Отредактировано vt340 (2017-04-26 08:59:59)