STM32f1 + freeRTOS + libOpenCM3 - USB Causes Failure

Hello!

I have recently begun learning freeRTOS. Everything has been going quite good except the following:

When I try to use USB provided by libopencm3, my program hangs without explanation. I have seen discussion of removing or adding “-fstack-protector-all”

When I add this, my compiling crashes on ‘flash.o’.

Attempts to trace the crash have failed so far, but this is probably due to my inexperience. The same HID code works fine when run on it’s own.

Any advice getting a USB HID composite device working in freeRTOS would be great.

One last note: I also have not been able to get USART code to work alongside the libopenCM3 HID firmware without FreeRTOS. It also fails without any warnings.

Pasted below is my failing code. Huge thank you in advance to anyone with some advice for me!

I would switch to cube, but there is no working composite USB examples. I am open to switching to the ST HAL or CMSIS platform, but again finding good USB examples has been challenging.

/* Simple LED task demo, using timed delays:
 *
 * The LED on PC13 is toggled in task1.
 */
#include "FreeRTOS.h"
#include "task.h"
#include <libopencm3/cm3/nvic.h>
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/usb/usbd.h>
#include <libopencm3/usb/hid.h>
#include "myGPIO.h"
#include "usbtimer.h"

static usbd_device *usbd_dev;


// The device descriptor of a USB device represents the entire device. As a result a USB device can only have one device 
// descriptor. It specifies some basic, yet important information about the device such as the supported USB version, 
// maximum packet size, vendor and product IDs and the number of possible configurations the device can have. The format 
// of the device descriptor is shown below.
const struct usb_device_descriptor dev_descr = {
	.bLength = USB_DT_DEVICE_SIZE, 					  	// Size of the Descriptor in Bytes (18 bytes)
	.bDescriptorType = USB_DT_DEVICE,					// Device Descriptor (0x01)
	.bcdUSB = 0x0200,									// USB Specification Number which device complies too.
	.bDeviceClass = 0,							        // Class Code (Assigned by USB Org), If equal to Zero, each interface specifies it’s own class code, If equal to 0xFF, the class code is vendor specified. Otherwise field is valid Class Code.
	.bDeviceSubClass = 0,								// Subclass Code (Assigned by USB Org)
	.bDeviceProtocol = 0,								// Protocol Code (Assigned by USB Org)
	.bMaxPacketSize0 = 64,								// Maximum Packet Size for Zero Endpoint. Valid Sizes are 8, 16, 32, 64
	.idVendor = 0x0483,									// Vendor ID (Assigned by USB Org)
	.idProduct = 0x5710,								// Product ID (Assigned by Manufacturer)
	.bcdDevice = 0x0200,								// Device Release Number
	.iManufacturer = 1,									// Index of Manufacturer String Descriptor
	.iProduct = 2,										// Index of Product String Descriptor
	.iSerialNumber = 3,									// Index of Serial Number String Descriptor
	.bNumConfigurations = 1,							// Number of Possible Configurations
};


static const uint8_t hid_report_descriptor[] = {
     0x05, 0x01,  // USAGE_PAGE (Generic Desktop)
     0x09, 0x04,  // USAGE (Joystick)

     0xa1, 0x01,  // COLLECTION (Application)
     0xa1, 0x00,  // COLLECTION (Physical)

     0x05, 0x09,  // USAGE_PAGE (Button)
     0x19, 0x01,  // USAGE_MINIMUM (Button 1)
     0x29, 0x08,  // USAGE_MAXIMUM (Button 8)
     0x15, 0x00,  // LOGICAL_MINIMUM (0)
     0x25, 0x01,  // LOGICAL_MAXIMUM (1)
     0x95, 0x08,  // REPORT_COUNT (8)
     0x75, 0x01,  // REPORT_SIZE (1)
     0x81, 0x02,  // INPUT (Data,Var,Abs)

     0x05, 0x01,  // USAGE_PAGE (Generic Desktop)
     0x15, 0x00,  // LOGICAL_MINIMUM (0)
     0x26, 0x00, 0x19,  // LOGICAL_MAXIMUM (6400 = 0x1900)
     0x75, 0x10,  // REPORT_SIZE

     0x09, 0x30,  // USAGE (X)
     0x09, 0x31,  // USAGE (Y)
     0x09, 0x32,  // USAGE (Z)
     0x09, 0x33,  // USAGE (Rx)
     0x09, 0x34,  // USAGE (Ry)
     0x09, 0x35,  // USAGE (Rz)
     0x09, 0x36,  // USAGE (Throttle)
     0x09, 0x37,  // USAGE (Rudder)
     0x95, 0x08,  // REPORT_COUNT
     0x81, 0x82,  // INPUT (Data,Var,Abs,Vol)

     0xc0,  // END_COLLECTION
     0xc0  // END_COLLECTION
};


static const struct {
	struct usb_hid_descriptor hid_descriptor;
	struct {
		uint8_t bReportDescriptorType;
		uint16_t wDescriptorLength;
	} __attribute__((packed)) hid_report;
} __attribute__((packed)) hid_function = {
	.hid_descriptor = {
		.bLength = sizeof(hid_function),
		.bDescriptorType = USB_DT_HID,
		.bcdHID = 0x0100,
		.bCountryCode = 0,
		.bNumDescriptors = 1,
	},
	.hid_report = {
		.bReportDescriptorType = USB_DT_REPORT,
		.wDescriptorLength = sizeof(hid_report_descriptor),
	}
};

// Endpoint Address
// Bits 0..3b Endpoint Number.
// Bits 4..6b Reserved. Set to Zero
// Bits 7 Direction 0 = Out, 1 = In (Ignored for Control Endpoints)

const struct usb_endpoint_descriptor hid_endpoint = {
	.bLength = USB_DT_ENDPOINT_SIZE,				// Size of Descriptor in Bytes (7 bytes)
	.bDescriptorType = USB_DT_ENDPOINT,				// Endpoint Descriptor (0x05)
	.bEndpointAddress = 0x81,						// 10000001
	.bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT,
	.wMaxPacketSize = 17,							// Maximum Packet Size this endpoint is capable of sending or receiving
	.bInterval = 0x02,			// Interval for polling endpoint data transfers. Value in frame counts. Ignored for Bulk & Control Endpoints. Isochronous must equal 1 and field may range from 1 to 255 for interrupt endpoints.
};


const struct usb_interface_descriptor hid_iface = {
	.bLength = USB_DT_INTERFACE_SIZE,					// Size of Descriptor in Bytes (9 Bytes)
	.bDescriptorType = USB_DT_INTERFACE,				// Interface Descriptor (0x04)
	.bInterfaceNumber = 0,								// Number of Interface
	.bAlternateSetting = 0,								// Value used to select alternative setting
	.bNumEndpoints = 1,									// Number of Endpoints used for this interface
	.bInterfaceClass = USB_CLASS_HID,					// Class Code (Assigned by USB Org)
	.bInterfaceSubClass = 1, /* boot */					// Subclass Code (Assigned by USB Org)
	.bInterfaceProtocol = 0, /* mouse */				// Protocol Code (Assigned by USB Org)
	.iInterface = 0,									// Index of String Descriptor Describing this interface
	.endpoint = &hid_endpoint,
	.extra = &hid_function,
	.extralen = sizeof(hid_function),
};


const struct usb_interface ifaces[] = {{
	.num_altsetting = 1,
	.altsetting = &hid_iface,
}};


const struct usb_config_descriptor config = {
	.bLength = USB_DT_CONFIGURATION_SIZE,				// 	Size of Descriptor in Bytes
	.bDescriptorType = USB_DT_CONFIGURATION,			// Configuration Descriptor (0x02)
	.wTotalLength = 0,									// Total length in bytes of data returned
	.bNumInterfaces = 1,								// Number of Interfaces
	.bConfigurationValue = 1,							// Value to use as an argument to select this configuration
	.iConfiguration = 0,								// Index of String Descriptor describing this configuration
	.bmAttributes = 0xC0,								// 11000000
	.bMaxPower = 0x32,									// Maximum Power Consumption in 2mA units
	.interface = ifaces,
};


static const char *usb_strings[] = {
	"Black Sphere Technologies",
	"HID Demo",
	"DEMO",
};


/* Buffer to be used for control requests. */
uint8_t usbd_control_buffer[128];


static enum usbd_request_return_codes hid_control_request(usbd_device *dev, struct usb_setup_data *req, uint8_t **buf, uint16_t *len,
			void (**complete)(usbd_device *, struct usb_setup_data *))
{
	(void)complete;
	(void)dev;

	if((req->bmRequestType != 0x81) ||
	   (req->bRequest != USB_REQ_GET_DESCRIPTOR) ||
	   (req->wValue != 0x2200))
		return USBD_REQ_NOTSUPP;

	/* Handle the HID report descriptor. */
	*buf = (uint8_t *)hid_report_descriptor;
	*len = sizeof(hid_report_descriptor);

	return USBD_REQ_HANDLED;
}


static void hid_set_config(usbd_device *dev, uint16_t wValue)
{
	(void)wValue;
	(void)dev;

	usbd_ep_setup(dev, 0x81, USB_ENDPOINT_ATTR_INTERRUPT, 4, NULL);

	usbd_register_control_callback(
				dev,
				USB_REQ_TYPE_STANDARD | USB_REQ_TYPE_INTERFACE,
				USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT,
				hid_control_request);

	}

extern void vApplicationStackOverflowHook(xTaskHandle *pxTask,signed portCHAR *pcTaskName);

void vApplicationStackOverflowHook(xTaskHandle *pxTask,signed portCHAR *pcTaskName) {
	(void)pxTask;
	(void)pcTaskName;
	for(;;);
}

static void task1(void *args __attribute((unused))) 
{
	for (;;) {
		toggleLED();
		vTaskDelay(pdMS_TO_TICKS(500));
	}
}

void tim2_isr(void)
{
	TIM_SR(TIM2) &= ~TIM_SR_UIF; /* Clear interrrupt flag. */
	//toggleLED();
}

int main(void) {

	rcc_clock_setup_in_hse_8mhz_out_72mhz(); // For "blue pill"

	gpio_setup();
	timer_setup();

	rcc_periph_clock_enable(RCC_GPIOA);
	gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ,
		GPIO_CNF_OUTPUT_PUSHPULL, GPIO12);
	gpio_clear(GPIOA, GPIO12);


	usbd_dev = usbd_init(&st_usbfs_v1_usb_driver, &dev_descr, &config, usb_strings, 3, usbd_control_buffer, sizeof(usbd_control_buffer));
	usbd_register_set_config_callback(usbd_dev, hid_set_config);


	xTaskCreate(task1,"LED",100,NULL,configMAX_PRIORITIES-1,NULL);
	vTaskStartScheduler();

	for (;;)
		usbd_poll(usbd_dev);
	return 0;
}

Libopencm3 is third party software I’m not familiar with or responsible for, so I’m afraid I don’t have specific suggestions, just generic once.

I would definitely leave “-fstack-protector-all” off. FreeRTOS has its own stack overflow detection (https://www.freertos.org/Stacks-and-stack-overflow-checking.html
)
and I’m not sure if GCC adding to stack frames will break the context switching or not (probably not, just don’t know).

Next thing to do would be read through this list:
https://www.freertos.org/FAQHelp.html

and if libopencm3 uses interrupt pay particular attention to this page
https://www.freertos.org/RTOS-Cortex-M3-M4.html, which is unfortunately complex but is trying to explain something that is complex.

Once you have tried everything on those links (stack overflow checking, adding asserts, using an up to date version of FreeRTOS so you have more assert points,
checking interrupt priorities, etc.) then try and report back with as much details about what you observe as possible - for example if you stop on the debugger what is executing? I’m afraid we can’t help with information such as “When I add this, my compiling
crashes on ‘flash.o’.” unless you tell us what ‘crashes’ means - if there is a compiler or linker error then past it into the post.

Hi!

Thank you for taking the time to respond to my somewhat nebulous question. I totally understand this is a FreeRTOS website, but I posted hoping my question might catch the eye of someone familiar with LibopenCM3. LibopenCM3 has no dedicated forum on the internet, so we must go around begging for help anywhere we can find it!

Maybe your experience can allow a quick comment on my goals.
image

This is what I am trying to make. I would love to simply switch to a different framework than libopencm3, but I have found, by far, the best example code and existing information on my goal with STMF103 chips seems to be for libopenCM3 - the IAR compiler is out of my price range, and the CubeMX Hid library states it does not support composite HID. It is certainly possible I do not understand the limitations of CubeMX though.

I would like to stick with STM32 chips, but I am open to any suggestion that might help me at this point!

This example code found here is close to what I want, but so far I have not managed to get it to compile for STM32F103.

Thank you again for your input.