Plik nagłówkowy usb_desc.h
Kod: Zaznacz cały
#ifndef USB_DESC_H_
#define USB_DESC_H_
#include <stdint.h>
#define USB_DESC_TYPE_DEVICE 1
#define USB_DESC_TYPE_CONFIGURATION 2
#define USB_DESC_TYPE_STRING 3
#define USB_DESC_TYPE_INTERFACE 4
#define USB_DESC_TYPE_ENDPOINT 5
#define USB_DESC_TYPE_DEVICE_QUALIFIER 6
#define USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION 7
#define USB_DESC_TYPE_INTERFACE_POWER 8
#define USB_DESC_TYPE_BOS 15
#define HID_REPORT_DESC 34
#define DEVICE_DESC_SIZE 18
#define CONFIGURATION_DESC_SIZE 34
#define MOUSE_REPORT_DESC_SIZE 74
extern const uint8_t device_desc[DEVICE_DESC_SIZE];
extern const uint8_t configuration_desc[CONFIGURATION_DESC_SIZE];
extern const uint8_t mouse_report_desc[MOUSE_REPORT_DESC_SIZE];
#endif /* USB_DESC_H_ */
- device_desc - to deskryptor urządzenia
- configuration_desc - deskryptor konfiguracji, interfejsów i endpointów
- mouse_report_desc - definiuje pakiety używane przez naszą "myszę"
Kod: Zaznacz cały
#include "usb_desc.h"
const uint8_t device_desc[DEVICE_DESC_SIZE] = {
0x12, /* bLength */
USB_DESC_TYPE_DEVICE, /* bDescriptorType */
0x00, /* bcdUSB */
0x02,
0x00, /* bDeviceClass */
0x00, /* bDeviceSubClass */
0x00, /* bDeviceProtocol */
0x40, /* bMaxPacketSize */
0x83, /* idVendor */
0x04, /* idVendor */
0x40, /* idProduct */
0x57, /* idProduct */
0x00, /* bcdDevice rel. 2.00 */
0x02,
0x00, /* Index of manufacturer string */
0x00, /* Index of product string */
0x00, /* Index of serial number string */
0x01 /* bNumConfigurations */
};
const uint8_t configuration_desc[CONFIGURATION_DESC_SIZE] = {
0x09, /* bLength */
USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType */
CONFIGURATION_DESC_SIZE, /* wTotalLength */
0x00,
0x01, /* bNumInterfaces */
0x01, /* bConfigurationValue */
0x00, /* iConfiguration */
0xE0, /* bmAttributes */
0x32, /* MaxPower 100 mA */
/* Interface Descriptor */
0x09, /* bLength */
USB_DESC_TYPE_INTERFACE, /* bDescriptorType */
0x00, /* bInterfaceNumber */
0x00, /* bAlternateSetting */
0x01, /* bNumEndpoints */
0x03, /* bInterfaceClass: HID */
0x01, /* bInterfaceSubClass: 1=BOOT, 0=no boot */
0x02, /* nInterfaceProtocol: 0=none, 1=keyboard, 2=mouse */
0, /* iInterface: Index of string descriptor */
/* HID Descriptor */
0x09, /* bLength */
0x21, /* bDescriptorType */
0x11, /* bcdHID: HID Class Spec release number */
0x01,
0x00, /* bCountryCode */
0x01, /* bNumDescriptors: Number of HID class descriptors to follow*/
0x22, /* bDescriptorType */
MOUSE_REPORT_DESC_SIZE, /* wItemLength: Total length of Report descriptor */
0x00,
/* Endpoint Descriptor */
0x07, /* bLength */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType */
0x81, /* bEndpointAddress */
0x03, /* bmAttributes: Interrupt endpoint */
0x04, /* wMaxPacketSize */
0x00,
0x0a, /* bInterval */
/* 34 */
};
const uint8_t mouse_report_desc[MOUSE_REPORT_DESC_SIZE] =
{
0x05, 0x01,
0x09, 0x02,
0xA1, 0x01,
0x09, 0x01,
0xA1, 0x00,
0x05, 0x09,
0x19, 0x01,
0x29, 0x03,
0x15, 0x00,
0x25, 0x01,
0x95, 0x03,
0x75, 0x01,
0x81, 0x02,
0x95, 0x01,
0x75, 0x05,
0x81, 0x01,
0x05, 0x01,
0x09, 0x30,
0x09, 0x31,
0x09, 0x38,
0x15, 0x81,
0x25, 0x7F,
0x75, 0x08,
0x95, 0x03,
0x81, 0x06,
0xC0, 0x09,
0x3c, 0x05,
0xff, 0x09,
0x01, 0x15,
0x00, 0x25,
0x01, 0x75,
0x01, 0x95,
0x02, 0xb1,
0x22, 0x75,
0x06, 0x95,
0x01, 0xb1,
0x01, 0xc0
};
Kod: Zaznacz cały
#ifndef USB_H_
#define USB_H_
/* bmRequestType */
#define USB_REQTYPE_DIR_MASK 0x80
#define USB_REQTYPE_TYPE_MASK 0x60
#define USB_REQTYPE_RECIPENT_MASK 0x1f
#define USB_REQTYPE_TYPE_STANDARD 0x00U
#define USB_REQTYPE_TYPE_CLASS 0x20U
#define USB_REQTYPE_TYPE_VENDOR 0x40U
#define USB_REQTYPE_RECIPIENT_DEVICE 0x00
#define USB_REQTYPE_RECIPIENT_INTERFACE 0x01
#define USB_REQTYPE_RECIPIENT_ENDPOINT 0x02
/* bRequest */
#define USB_REQUEST_GET_STATUS 0
#define USB_REQUEST_CLEAR_FEATURE 1
#define USB_REQUEST_SET_FEATURE 3
#define USB_REQUEST_SET_ADDRESS 5
#define USB_REQUEST_GET_DESCRIPTOR 6
#define USB_REQUEST_SET_DESCRIPTOR 7
#define USB_REQUEST_GET_CONFIGURATION 8
#define USB_REQUEST_SET_CONFIGURATION 9
#define USB_REQUEST_GET_INTERFACE 10
#define USB_REQUEST_SET_INTERFACE 11
#define USB_REQUEST_SYNCH_FRAME 12
#endif /* USB_H_ */
Kod: Zaznacz cały
static void handle_setup(const struct setup_request *req)
{
switch (req->bmRequestType & USB_REQTYPE_RECIPENT_MASK) {
case USB_REQTYPE_RECIPIENT_DEVICE:
handle_setup_device(req);
break;
case USB_REQTYPE_RECIPIENT_INTERFACE:
handle_setup_interface(req);
break;
default:
setup_error();
break;
}
}
Kod: Zaznacz cały
static void setup_error(void)
{
endpoint_set_rx_status(0, USB_EP_RX_STALL);
endpoint_set_tx_status(0, USB_EP_TX_STALL);
}
Kod: Zaznacz cały
static void handle_setup_device(const struct setup_request *req)
{
switch (req->bmRequestType & USB_REQTYPE_TYPE_MASK) {
case USB_REQTYPE_TYPE_STANDARD:
switch (req->bRequest) {
case USB_REQUEST_GET_DESCRIPTOR:
get_descriptor(req);
break;
case USB_REQUEST_SET_ADDRESS:
set_address(req);
break;
case USB_REQUEST_SET_CONFIGURATION:
set_configuration(req);
break;
default:
setup_error();
break;
}
break;
default:
setup_error();
break;
}
}
static void handle_setup_interface(const struct setup_request *req)
{
switch (req->bmRequestType & USB_REQTYPE_TYPE_MASK) {
case USB_REQTYPE_TYPE_STANDARD:
switch (req->bRequest) {
case USB_REQUEST_GET_DESCRIPTOR:
get_descriptor(req);
break;
default:
setup_error();
break;
}
break;
default:
setup_error();
break;
}
}
- GET_DESCRIPTOR - czyli pobranie deskryptora
- SET_ADDRESS - ustawienie nowego adresu urządzenia
- SET_CONFIGURATION - wybór konfiguracji (jest tylko jedna)
Kod: Zaznacz cały
static enum {
USB_STATE_DEFAULT,
USB_STATE_ADDRESSED,
USB_STATE_CONFIGURED,
}connection_state;
W momencie wyboru konfiguracji otwieramy również nowy endpoint - typu INTERROUT, przez który będziemy wysyłać dane o zachowaniu naszej myszki.
W programie głównym będziemy wysyłać co pewien czas raporty o przesunięciu kursora o 10 pikseli w prawo - tak jak w pierwszym programie, który używał biblioteki od ST:
Kod: Zaznacz cały
struct mouse_report {
uint8_t buttons;
int8_t xrel;
int8_t yrel;
int8_t wrel;
};
int main(void)
{
clocks_config();
usb_init();
while (1) {
struct mouse_report rpt = {0};
rpt.xrel = 10;
endpoint_send(1, &rpt, sizeof(rpt), NULL);
for (volatile uint32_t dly = 0; dly < 2000000; dly++) {}
}
}
Kod: Zaznacz cały
#include <stdio.h>
#include <string.h>
#include "stm32l053xx.h"
#include "endpoint.h"
#include "usb.h"
#include "usb_desc.h"
struct setup_request {
uint8_t bmRequestType;
uint8_t bRequest;
uint16_t wValue;
uint16_t wIndex;
uint16_t wLength;
};
static enum {
USB_STATE_DEFAULT,
USB_STATE_ADDRESSED,
USB_STATE_CONFIGURED,
}connection_state;
static uint8_t usb_address;
typedef void (*comm_callback)(uint8_t);
struct {
const void *tx_buffer;
int32_t tx_length;
comm_callback tx_handler;
void *rx_buffer;
int32_t rx_length;
comm_callback rx_handler;
}endpoint[8];
static void usb_reset(void)
{
endpoint_init(0, 0x040, 0x080);
endpoint_init(1, 0x0c0, 0x100);
connection_state = USB_STATE_DEFAULT;
usb_address = 0;
USB->DADDR = USB_DADDR_EF;
endpoint_use_rx_data0(0);
endpoint_set_type(0, USB_EP_CONTROL);
endpoint_set_rx_status(0, USB_EP_RX_VALID);
endpoint_set_rx_status(1, USB_EP_RX_DIS);
endpoint_set_tx_status(1, USB_EP_TX_DIS);
}
void endpoint_send(uint8_t ep, const void *data, uint16_t length, comm_callback next)
{
endpoint[ep].tx_handler = next;
if (length < ENDPOINT_SIZE) {
endpoint[ep].tx_buffer = NULL;
endpoint[ep].tx_length = -1;
} else {
endpoint[ep].tx_length = length - ENDPOINT_SIZE;
endpoint[ep].tx_buffer = data + ENDPOINT_SIZE;
length = ENDPOINT_SIZE;
}
endpoint_copy_to(ep, data, length);
endpoint_set_tx_status(ep, USB_EP_TX_VALID);
}
void endpoint_receive(uint8_t ep, void *data, uint16_t length, comm_callback next)
{
endpoint[ep].rx_handler = next;
endpoint[ep].rx_buffer = data;
endpoint[ep].rx_length = length;
endpoint_set_rx_status(ep, USB_EP_RX_VALID);
}
static void send_zlp_done(uint8_t ep)
{
endpoint_set_rx_status(ep, USB_EP_RX_VALID);
}
static void send_zlp(uint8_t ep)
{
endpoint_send(ep, NULL, 0, send_zlp_done);
}
static void ack_received_handler(uint8_t ep)
{
endpoint_set_rx_status(0, USB_EP_RX_VALID);
}
static void data_sent_handler(uint8_t ep)
{
endpoint_receive(ep, NULL, 0, ack_received_handler);
}
static void setup_error(void)
{
endpoint_set_rx_status(0, USB_EP_RX_STALL);
endpoint_set_tx_status(0, USB_EP_TX_STALL);
}
static void get_descriptor(const struct setup_request *req)
{
uint16_t len;
const uint8_t *pbuf;
switch (req->wValue >> 8) {
case USB_DESC_TYPE_DEVICE:
pbuf = device_desc;
len = sizeof(device_desc);
break;
case USB_DESC_TYPE_CONFIGURATION:
pbuf = configuration_desc;
len = sizeof(configuration_desc);
break;
case HID_REPORT_DESC:
pbuf = mouse_report_desc;
len = sizeof(mouse_report_desc);
break;
default:
setup_error();
return;
}
if (len > req->wLength)
len = req->wLength;
endpoint_send(0, pbuf, len, data_sent_handler);
}
static void set_address_done(uint8_t ep)
{
USB->DADDR = USB_DADDR_EF | usb_address;
endpoint_set_rx_status(0, USB_EP_RX_VALID);
}
static void set_address(const struct setup_request *req)
{
uint8_t addr;
if ((req->wIndex == 0U) && (req->wLength == 0U) && (req->wValue < 128U)) {
addr = (uint8_t)(req->wValue) & 0x7FU;
if (connection_state == USB_STATE_CONFIGURED) {
setup_error();
} else {
endpoint_send(0, NULL, 0, set_address_done);
usb_address = addr;
if (addr != 0U)
connection_state = USB_STATE_ADDRESSED;
else
connection_state = USB_STATE_DEFAULT;
}
} else {
setup_error();
}
}
static void hid_open(void)
{
endpoint_set_type(1, USB_EP_INTERRUPT);
endpoint[1].tx_buffer = NULL;
endpoint[1].tx_length = -1;
endpoint[1].tx_handler = NULL;
endpoint_use_tx_data0(1);
endpoint_set_tx_status(1, USB_EP_TX_NAK);
}
static void hid_close(void)
{
endpoint_set_tx_status(1, USB_EP_TX_DIS);
}
static void set_configuration(const struct setup_request *req)
{
static uint8_t cfgidx;
cfgidx = (uint8_t)(req->wValue);
if (cfgidx > 1) {
setup_error();
} else {
switch (connection_state) {
case USB_STATE_ADDRESSED:
if (cfgidx) {
connection_state = USB_STATE_CONFIGURED;
hid_open();
send_zlp(0);
} else {
send_zlp(0);
}
break;
case USB_STATE_CONFIGURED:
if (cfgidx == 0U) {
connection_state = USB_STATE_ADDRESSED;
hid_close();
send_zlp(0);
} else {
send_zlp(0);
}
break;
default:
setup_error();
hid_close();
break;
}
}
}
static void handle_setup_device(const struct setup_request *req)
{
switch (req->bmRequestType & USB_REQTYPE_TYPE_MASK) {
case USB_REQTYPE_TYPE_STANDARD:
switch (req->bRequest) {
case USB_REQUEST_GET_DESCRIPTOR:
get_descriptor(req);
break;
case USB_REQUEST_SET_ADDRESS:
set_address(req);
break;
case USB_REQUEST_SET_CONFIGURATION:
set_configuration(req);
break;
default:
setup_error();
break;
}
break;
default:
setup_error();
break;
}
}
static void handle_setup_interface(const struct setup_request *req)
{
switch (req->bmRequestType & USB_REQTYPE_TYPE_MASK) {
case USB_REQTYPE_TYPE_STANDARD:
switch (req->bRequest) {
case USB_REQUEST_GET_DESCRIPTOR:
get_descriptor(req);
break;
default:
setup_error();
break;
}
break;
default:
setup_error();
break;
}
}
static void handle_setup_ep(const struct setup_request *req)
{
setup_error();
}
static void handle_setup(const struct setup_request *req)
{
switch (req->bmRequestType & USB_REQTYPE_RECIPENT_MASK) {
case USB_REQTYPE_RECIPIENT_DEVICE:
handle_setup_device(req);
break;
case USB_REQTYPE_RECIPIENT_INTERFACE:
handle_setup_interface(req);
break;
case USB_REQTYPE_RECIPIENT_ENDPOINT:
handle_setup_ep(req);
break;
default:
setup_error();
break;
}
}
static void use_callback(uint8_t ep, comm_callback *handler)
{
comm_callback callback = *handler;
if (callback == NULL)
return;
*handler = NULL;
callback(ep);
}
void USB_IRQHandler(void)
{
volatile uint32_t status = USB->ISTR;
struct setup_request req;
if (status & USB_ISTR_RESET) {
USB->ISTR = 0;
usb_reset();
} else if (status & USB_ISTR_CTR) {
uint8_t ep = status & 0xf;
if (status & USB_ISTR_DIR) {
endpoint_clear_rx(ep);
if (endpoint_is_setup_req(ep)) {
// transaction SETUP (RX)
endpoint_copy_from(0, &req, sizeof(req));
handle_setup(&req);
} else {
// transaction OUT (RX)
uint16_t length = endpoint_get_rx_length(ep);
if (length > endpoint[ep].rx_length)
length = endpoint[ep].rx_length;
endpoint_copy_from(ep, endpoint[ep].rx_buffer, length);
endpoint[ep].rx_buffer += length;
endpoint[ep].rx_length -= length;
if (length == ENDPOINT_SIZE)
endpoint_receive(ep, endpoint[ep].rx_buffer, endpoint[ep].rx_length, endpoint[ep].rx_handler);
else
use_callback(ep, &endpoint[ep].rx_handler);
}
} else {
// transaction IN (TX)
endpoint_clear_tx(ep);
if (endpoint[ep].tx_length >= 0)
endpoint_send(ep, endpoint[ep].tx_buffer, endpoint[ep].tx_length, endpoint[ep].tx_handler);
else
use_callback(ep, &endpoint[ep].tx_handler);
}
}
}
static void clocks_config(void)
{
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN;
PWR->CR = (PWR->CR & ~PWR_CR_VOS_Msk) | PWR_CR_VOS_0;
RCC->CR |= RCC_CR_HSEBYP | RCC_CR_HSEON;
while ((RCC->CR & RCC_CR_HSERDY) == 0) {}
RCC->CFGR = (RCC->CFGR & ~(RCC_CFGR_PLLSRC_Msk | RCC_CFGR_PLLMUL_Msk | RCC_CFGR_PLLDIV_Msk))
| RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMUL12 | RCC_CFGR_PLLDIV3;
RCC->CR |= RCC_CR_PLLON;
while ((RCC->CR & RCC_CR_PLLRDY) == 0) {}
RCC->CFGR = (RCC->CFGR & ~(RCC_CFGR_HPRE_Msk | RCC_CFGR_PPRE1_Msk | RCC_CFGR_PPRE2_Msk))
| RCC_CFGR_HPRE_DIV1 | RCC_CFGR_PPRE1_DIV1 | RCC_CFGR_PPRE2_DIV1 | RCC_CFGR_SW_PLL;
RCC->CRRCR |= RCC_CRRCR_HSI48ON;
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN;
SYSCFG->CFGR3 |= SYSCFG_CFGR3_ENREF_HSI48;
while ((RCC->CRRCR & RCC_CRRCR_HSI48RDY) == 0) {}
RCC->CCIPR |= RCC_CCIPR_HSI48MSEL;
RCC->IOPENR |= RCC_IOPENR_GPIOAEN | RCC_IOPENR_GPIOBEN | RCC_IOPENR_GPIOCEN | RCC_IOPENR_GPIODEN | RCC_IOPENR_GPIOHEN;
RCC->APB1ENR |= RCC_APB1ENR_USBEN;
}
static void usb_init(void)
{
USB->CNTR = 0;
USB->CNTR = USB_CNTR_FRES;
USB->ISTR = 0;
USB->CNTR = USB_CNTR_CTRM | USB_CNTR_RESETM;
USB->BTABLE = 0;
USB->BCDR |= USB_BCDR_DPPU;
usb_reset();
NVIC_SetPriority(USB_IRQn, 0);
NVIC_EnableIRQ(USB_IRQn);
}
struct mouse_report {
uint8_t buttons;
int8_t xrel;
int8_t yrel;
int8_t wrel;
};
int main(void)
{
clocks_config();
usb_init();
while (1) {
struct mouse_report rpt = {0};
rpt.xrel = 10;
endpoint_send(1, &rpt, sizeof(rpt), NULL);
for (volatile uint32_t dly = 0; dly < 2000000; dly++) {}
}
}

Gotowy program zamiast 17KB zajmuje teraz niecałe 5KB, ale co najważniejsze pozwala lepiej zrozumieć jaka magia była ukryta w bibliotece.
Zanim zaimplementowałem HID testowałem też CDC, więc zmieniając deskryptor oraz dodają dwa nowe endpointy moglibyśmy mieć w naszym programie działając port szergowy... ale ja już nie mam siły tego opisywać
