Nie takie USB straszne jak je malują, cz.6 - gotowy program

Wszystko co dotyczy płytek z rodziny Discovery firmy STM
ODPOWIEDZ
Awatar użytkownika
elvis
Użytkownik
Posty: 35
Rejestracja: 30 lis 2018, 17:50

Nie takie USB straszne jak je malują, cz.6 - gotowy program

Post autor: elvis » 06 lip 2019, 13:57

Czas na uzupełnienie programu o brakujące elementy. Na początek deskryptory - używałem ich już poprzednio, ale czas podzielić się kodem.
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_ */
Widzimy w nim trochę deklaracji stałych - USB_DESC_TYPE_ są przepisane z dokumentacji. My używamy raptem 3 deskryptorów:
  • 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ę"
Treść samych deskryptorów to uproszczona wersja z przykładów od ST. Wyrzuciłem z nich napisy, zadeklarowałem również tablice jako stałe - niech sobie siedzą w pamięci Flash zamiast RAM marnować (plik usb_desc.c):

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
};
Do dekodowania żądań hosta przyda się również trochę stałych. Ich wartości pochodzą bezpośrednio z dokumentacji protokołu USB. Umieściłem je w pliku usb.h:

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_ */
Nowa funkcja handle_setup będzie sprawdzała, czy żądanie dotyczy urządzenia, czy interfejsu i przekazywała sterowanie dalej:

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;
	}
}
Jeśli dostajemy zapytanie, którego nie potrafimy obsłużyć, odpowiadamy zmianą stanu endpointa na STALL co wykonuje funkcja setup_error:

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);
}
Obsługa żądań skierowanych do interfejsu i urządzenia ma postać:

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;
	}
}
Jak widzimy dekodujemy polecenia:
  • GET_DESCRIPTOR - czyli pobranie deskryptora
  • SET_ADDRESS - ustawienie nowego adresu urządzenia
  • SET_CONFIGURATION - wybór konfiguracji (jest tylko jedna)
Zgodnie ze specyfikacją obsługa USB odbywa się za pomocą automatu stanowego. Definiujemy więc jego stany:

Kod: Zaznacz cały

static enum {
	USB_STATE_DEFAULT,
	USB_STATE_ADDRESSED,
	USB_STATE_CONFIGURED,
}connection_state;
Nazwy też pochodzą ze standardu. DEFAULT jest ustawiany po podłączeniu lub resecie interfejsu. Gdy host przydzieli nam adres, zmieniamy stan na ADDRESSED, a po wyborze konfiguracji na CONFIGURED.
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++) {}
	}
}
Dodając kilka brakujących callback-ów mamy cały i o dziwo działający program:

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++) {}
	}
}
Po uruchomieniu kursor ucieka w prawo, a analizator pokazuje że wszystko działa jak powinno:

Obrazek

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ć :)

Awatar użytkownika
l3n1n
Moderator
Posty: 289
Rejestracja: 28 paź 2017, 8:46
Lokalizacja: 3M

Re: Nie takie USB straszne jak je malują, cz.6 - gotowy program

Post autor: l3n1n » 06 lip 2019, 15:09

elvis pisze:
06 lip 2019, 13:57
...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ć :)
A szkoda bo myślę, że znowu by było ciekawie.

Awatar użytkownika
Zealota
Użytkownik
Posty: 7
Rejestracja: 18 lis 2017, 17:45
Lokalizacja: Gliwice
Kontakt:

Re: Nie takie USB straszne jak je malują, cz.6 - gotowy program

Post autor: Zealota » 16 lip 2019, 9:15

elvis pisze:
06 lip 2019, 13:57
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ć :)
No cóż Elvis po raz kolejny świetna robota. Dziękuję Ci za podzielenie się tą wiedza.
Dla mnie USB ma w sobie jakąś magię i tajemnicę, która staram się posiąść, ale idzie bardzo topornie :)
Niemniej jednak brnę dalej.

Z mojej perspektywy polecam książkę "USB dla niewtajemniczonych w przykładach na mikrokontrolery STM32".
Od strony opisu standardu USB oceniam ją dość wysoko, bo pomimo trudnego tematu, kolejne czytania coraz bardziej odsłaniają tę "tajemnicę".
Wydaje się, że od opisu standardu USB owa książka jest co najmniej poprawna oraz kod do niej dostarczony wydaje się być pisany z zachowaniem zasad dobrego programowania. Książka ma tez jedną zasadniczą wadę - kod oparty jest na przestarzałym bibliotekach SPL, co obecnie bardzo utrudnia jej praktyczne wykorzystanie - na pewno mnie, a dla miłośników CubeMX może być całkowicie niestrawne.

Na szczęście jest internet i od pewnego czasu rozpracowuję tę oto bibliotekę:
https://github.com/dmitrystu/libusb_stm32

Polecam zapoznać się. Jest pisana w "bare metal" zatem nie jest wymagająca pod kątem zasobów.
Działa na sporej liczbie mikrokontrolerów STM32 i kod "wydaje się" bardzo przejrzysty :)

Standardowo kod np. dla urządzenia combo "CDC + mouse" zajmuje 4800 bajtów flash i 1088 bajtów ram (-Os), z czego bufor PMA to 1024.
Mnie z powodzeniem udało się uruchomić CDC+mouse zarówno z dostarczonego pliku makefile jak i po imporcie do Eclipse.
Odpaliłem na STM32F103 oraz F407, w zanadrzu czekają jeszcze F303, F070 oraz F042.
Po własnej modyfikacji deskryptorów z powodzeniem uruchomiłem CDC+keyboard - przydatny okazał się tutorial dla AVR:
https://eleccelerator.com/tutorial-abou ... scriptors/

Jeszcze z mojej perspektywy istotne jest to, że można użyć tej biblioteki do tanich i słabo wyposażonych uK takich jak F070 czy F042 co powoduje, że mamy możliwość budowy bardzo tanich urządzeń ze sprzętową obsługą USB - dla przypomnienia taki F070 w detalu kosztuje ok 7 zł. Niestety w takiej aplikacji na nic się zda CubeMX - dla tych procków nie da się wygenerować biblioteki USB - to był podstawowy bodziec do poszukiwania czegoś innego.

Twój wpis ewidentnie przybliża mnie do lepszego zrozumienia tej "magii" :)

ODPOWIEDZ