STM32CubeMX USB CDC VCP? - usb

I've found large number of examples, but nothing on how to do it "properly" from STM32MXCube.
How do I create skeleton code from STM32CubeMX for USB CDC virtual COM port communications (if possible STM32F4 Discovery)?

A STM32CubeMX project for Discovery F4 with CDC as USB device should work out of the box. Assuming you use an up-to-date STM32CubeMX and library:
Start STM32CubeMX
Select the board Discovery F4
Enable peripheral UBS_OTG_FS device only (leave over stuff uncheck)
Enable midlleware USB_Device Communication .. .aka CDC
In the clock tab check the clock source is HSE HCLK. It shall give 168 MHz HLCK and 48 MHz in the 48 MHz (USB). Check there is no red anywhere.
Save the project
Generate code (I used SW4STM32 toolchains)
Build (you may need to switch to internal CDT builder vs. GNU make).
Now add some code to send data over the COM port and voila it should work.
Actually, the tricky part is not try to make any "CDC" access until the host USB connects (no CDC setup yet)
Here is how I did it for quick emit test:
In file usbd_cdc_if.c
uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len)
{
uint8_t result = USBD_OK;
/* USER CODE BEGIN 7 */
if (hUsbDevice_0 == NULL)
return -1;
USBD_CDC_SetTxBuffer(hUsbDevice_0, Buf, Len);
result = USBD_CDC_TransmitPacket(hUsbDevice_0);
/* USER CODE END 7 */
return result;
}
static int8_t CDC_DeInit_FS(void)
{
/* USER CODE BEGIN 4 */
hUsbDevice_0 = NULL;
return (USBD_OK);
/* USER CODE END 4 */
}
In file main.c
/* USER CODE BEGIN Includes */
#include "usbd_cdc_if.h"
/* USER CODE END Includes */
....
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
uint8_t HiMsg[] = "hello\r\n";
CDC_Transmit_FS(HiMsg, strlen(HiMsg));
HAL_Delay(200);
}
As soon you plug the micro USB (CN5) CDC data will start to show on the host terminal.
That works. I can see "hello" on the terminal (you may need to install a driver, http://www.st.com/web/en/catalog/tools/PF257938).
For reception, it needs to be first armed, say, started by a first call to USBD_CDC_ReceivePacket() in a good place. For that it can be CDC_Init_FS.
Then you can handle data as it arrives in CDC_Receive_FS and rearming reception again from here.
That works for me.
static int8_t CDC_Receive_FS (uint8_t* Buf, uint32_t *Len)
{
/* USER CODE BEGIN 6 */
USBD_CDC_ReceivePacket(hUsbDevice_0);
return (USBD_OK);
/* USER CODE END 6 */
}
static int8_t CDC_Init_FS(void)
{
hUsbDevice_0 = &hUsbDeviceFS;
/* USER CODE BEGIN 3 */
/* Set Application Buffers */
USBD_CDC_SetTxBuffer(hUsbDevice_0, UserTxBufferFS, 0);
USBD_CDC_SetRxBuffer(hUsbDevice_0, UserRxBufferFS);
USBD_CDC_ReceivePacket(hUsbDevice_0);
return (USBD_OK);
/* USER CODE END 3 */
}

There are a number of STM32F4 Discovery boards supported by the STM32Cube software, and you haven’t said which you’re using, but I’ve had exactly the same issue with the Discovery board with the F401VCT MCU.
After installing the STM virtual COM port driver, Windows Device Manager showed a STMicroelectronics virtual COM port, but with a yellow warning mark. The COM port was not accessible with a terminal application (PuTTY).
I eventually found that there is a problem with source code output from the STMCube program. But there is a simple fix:
Open a new STM32Cube project and enable the USB_OTG_FS as Device
Only and select CDC Virtual Port COM from the MiddleWares
USB_Device drop-down.
Generate the source code with no other changes needed to any USB settings.
In file usbd_cdc_if.c, change #define USB_HS_MAX_PACKET_SIZE from 512 to 256.
In file usbd_cdc.c, change the #define CDC_DATA_HS_MAX_PACKET_SIZE from 512 to 256.
After doing this, the yellow warning disappeared from Device Manager, and I could receive data at the CDC_Receive_FS function (in usbd_cdc_if.c file) when using PuTTY. Be aware that these definitions return to their incorrect values each time the STM32Cube generates code, and I haven’t found a way around this yet.
I hope this helps.

iChal's fix worked to remove the yellow warning mark.
I would like to mention that USB_HS_MAX_PACKET_SIZE is now in usbd_def.h and CDC_DATA_HS_MAX_PACKET_SIZE is in usbd_cdc.h
I am using STM32CubeMX v4.11.0 STM32Cube v1.0 and the STM32F401C-DISCO.
On further work, I now only have to set the heap size to a larger value.
I am setting it to 0x600 as I also have FreeRTOS enabled. I am using IAR EWARM, so the change is made in linker script stm32f401xc_flash.icf.

Related

flash write efm32zg fails with while (DMA->CHENS & DMA_CHENS_CH0ENS)

I am attempting to create a boot loader which allows me to update a processor's software remotely.
I am using keil uvision compiler (V5.20.0.0).
Flash.c, startup_efm32zg.s, startup_efm32zg.c and em_dma.c configured to execute from RAM (code, Zero init data, other data) via their options/properties tabs.
Stack size configured at 0x0000 0800 via the startup_efm32zg.s Configuration Wizard tab.
Using Silicon Labs flash.c and flash.h, removed RAMFUNC as this is redundant to Keil configuration, above.
I modified the flash.c code slightly so it stays in the FLASH_write function (supposedly in RAM) until the DMA is done doing its thing.
I moved the
while (DMA->CHENS & DMA_CHENS_CH0ENS);
line down to the end of the function and added a little wrapper around it like this:
/* Activate channel 0 */
DMA->CHENS = DMA_CHENS_CH0ENS;
if (DMA->CHENS & DMA_CHENS_CH0ENS)
{
/* Start the transfer */
MSC->WRITECMD = MSC_WRITECMD_WRITETRIG;
/* Wait until transfer is done */
while (DMA->CHENS & DMA_CHENS_CH0ENS)
{
//do nothing here
}
}
FLASH_init() is called as part of the initial setup prior to entering my infinite loop.
When called upon to update the flash.....
(1): I disable interrupts.
(2): I call FLASH_erasePage starting at 0x0000 2400. This works.
(3): I call FLASH_write.
FLASH_write(&startAddress, (uint32_t *)flashBuffer, (BLOCK_SIZE/4));
Where:
startAddress = 0x00002400,
flashBuffer = a buffer of type uint8_t flashBuffer[256],
#define BLOCK_SIZE = 256.
It gets stuck here in the function:
while (DMA->CHENS & DMA_CHENS_CH0ENS)
Eventually the debugger execution stops and the Call Stack clears to be left with 0x00000000 and ALL of memory is displayed as 0xAA.
I have set aside 9K of flash for the bootloader. After a build I am told:
Program size: Code=7524 RO-data=304 RW-data=664 ZI-data=3432
Target Memory Options for Target1:
IROM1: Start[0x0] Size[0x2400]
IRAM1: Start[0x20000000] Size:[0x1000]
So .... what on earth is going on? Any help?
One of my other concerns is that it is supposed to be executing from RAM. When I look in the in the Call Stack for the Location/Value for FLASH_write after having stepped into the FLASH_write function I see 0x000008A4. This is flash!(?)
I've tried the whole RAM_FUNC thing, too with the same results.

STM32 flashing disabled after flashing a code without R/W protection

I have some experience of StdPeriph libraries usage for programming stm32. But now I tried STM32Cube HAL with STM32CubeMX code generator. I generated a project with this options:
Middleware: FreeRTOS and FatFS via SDIO
Compiler is GCC
stm32f103ret6 MCU
I imported generated code to Eclipse environment. I made a binary and flashed it with "st-flash write ..." as usual. My test program successfuly wrote to USART1 "Hello" in cycle - this is no problem. But then, when I tried to flash another code, it failed with "unknown chip id". If I manually connect NRST to GND, st-flash gives:
...Flash: 0 bytes (0 KiB) in pages of 2048 bytes
Full output:
2015-06-14T16:07:29 INFO src/stlink-common.c: Loading device parameters....
2015-06-14T16:07:29 INFO src/stlink-common.c: Device connected is: F1 High-density device, id 0x10036414
2015-06-14T16:07:29 INFO src/stlink-common.c: SRAM size: 0x10000 bytes (64 KiB), Flash: 0 bytes (0 KiB) in pages of 2048 bytes
I tried to use ST-Link Utility from Windows, but it cannot connect to this MCU to change option bytes (connection to another devices with stm32 works well).
I tried to flash through USART1, but it failed.
Source code I flashed, of course, does not contain any read/write protection enabling. I tried 2 another MCU, but this error was reproduced.
How can I unbrick by MCUs and flash anything?
I found a root cause!
This is a HAL initialization function, generated by STM32CubeMX:
void HAL_MspInit(void)
{
/* USER CODE BEGIN MspInit 0 */
/* USER CODE END MspInit 0 */
__HAL_RCC_AFIO_CLK_ENABLE();
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
/* System interrupt init*/
/* SysTick_IRQn interrupt configuration */
HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
/**DISABLE: JTAG-DP Disabled and SW-DP Disabled
*/
__HAL_AFIO_REMAP_SWJ_DISABLE();
/* USER CODE BEGIN MspInit 1 */
/* USER CODE END MspInit 1 */
}
I didn't notice this simple lines!
/**DISABLE: JTAG-DP Disabled and SW-DP Disabled
*/
__HAL_AFIO_REMAP_SWJ_DISABLE();
This macros totally disables SWD and JTAG programming, look at stm321xx_hal_gpio_ex.h:
#define __HAL_AFIO_REMAP_SWJ_DISABLE() MODIFY_REG(AFIO->MAPR, AFIO_MAPR_SWJ_CFG, AFIO_MAPR_SWJ_CFG_DISABLE)
I didn't found any checkbox in CubeMX to disable/enable SWD/JTAG, so this is the only behavior of code generator! Pay attention to this point when using STM32CubeMX!
If you set the pin assignments for the JTAG/SWD pins correctly (e.g. SYS_JTDI, SYS_JTDO-TRACESWO, etc.) on the pinout tab of STM32CubeMX, the generated code will not disable JTAG/SWD.
It's (BURIED) under Pinout | SYS | Debug of STM32CubeMX...set to Serial Wire or whatever.

UART on discovery cortex F3 to putty terminal

/* USER CODE BEGIN 2 */
uint8_t a;
/* USER CODE END 2 */
/* USER CODE BEGIN 3 */
/* Infinite loop */
while (1)
{
}
HAL_UART_Receive(&huart2, &a, 1, 1);
while (a== 'b') {
HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_8);
HAL_Delay(100);
}
}
/* USER CODE END 3 */
Used CubeMX to create project, then changed in Keil (it is a piece of that)
Code was send to Discovery F3 Cortex Board.
Transmitting worked with function HAL_UART_Transmit I can see on putty terminal the data i've wrote (similar program).
Receiving does'nt work... I m trying to send the letter by putty, but nothing happens.
Please help.
Once you received a 'b' it will remain unchanged till another character is received. So your pin will toggle every 100 ms. To avoid this, before Delay insert a change to a, like a=0.

atmel sensor using printf

I have an atmel UC3-L0 and compass sensor. Now I install AtmelStudio and download some demo code into the board. But I have no idea where the function printf in demo code will appear the data. How should I do to get the data?
The printf function outputs to stdout.
Usually on a "naked" processor with no operating system you need to define how a character is sent or received from a physical interface (usually an USART, console port, USB port, 4-port LCD interface, etc.). So typically you may want to use the USART port of your processor board to connect to a PC running Hyperterm, PuTTY or similar using a serial cable.
In essence you will need to
create FILE streams using the fdev_setup_stream() macro and
provide pointers to functions get() and put() that tell the printf() function how exactly to read and write from/to that stream (e.g. read/write to a USART, an LCD display, etc.).
you may have libraries - depending on your hardware - that already contain such functions (plus the correct port initialisation functions), like e.g. uart.c/.h, lcd.c/.h, etc.
In the documentation of stdio.h (e.g. here) look for the following:
printf(), fdev_setup_stream()
If you have downloaded Atmel Studio you may look into the stdiodemo.c code for further insight.
In order to use printf in ATMEL studio you should check the following things:
Add and Apply the Standard serial I/O module from Project->ASF Wizard.
Also add the USART module from the ASF Wizard.
Include the following code snippet before the main function.
static struct usart_module usart_instance;
static void configure_console(void)
{
struct usart_config usart_conf;
usart_get_config_defaults(&usart_conf);
usart_conf.mux_setting = EDBG_CDC_SERCOM_MUX_SETTING;
usart_conf.pinmux_pad0 = EDBG_CDC_SERCOM_PINMUX_PAD0;
usart_conf.pinmux_pad1 = EDBG_CDC_SERCOM_PINMUX_PAD1;
usart_conf.pinmux_pad2 = EDBG_CDC_SERCOM_PINMUX_PAD2;
usart_conf.pinmux_pad3 = EDBG_CDC_SERCOM_PINMUX_PAD3;
usart_conf.baudrate = 115200;
stdio_serial_init(&usart_instance, EDBG_CDC_MODULE, &usart_conf);
usart_enable(&usart_instance);
}
Make Sure you call the configure_console after system_init() from the main function.
Now go to tools->extension manager. Add the terminal window extension.
Build and Run your program and open the terminal window from view-> terminal window. put the correct com port to which your device is running on and set the baud to 115200 and hit connect on the terminal window.
You should see the printf statements now. (Float doesn't get printed in Atmel studio)
I was recently puzzling over this myself. I has installed Atmel Studio 7.0 and was using the SAMD21 Dev Board via an example project in which a call to printf was made.
In the sample code I saw that there was a configuration section:
/*!
* \brief Initialize USART to communicate with on board EDBG - SERCOM
* with the following settings.
* - 8-bit asynchronous USART
* - No parity
* - One stop bit
* - 115200 baud
*/
static void configure_usart(void)
{
struct usart_config config_usart;
// Get the default USART configuration
usart_get_config_defaults(&config_usart);
// Configure the baudrate
config_usart.baudrate = 115200;
// Configure the pin multiplexing for USART
config_usart.mux_setting = EDBG_CDC_SERCOM_MUX_SETTING;
config_usart.pinmux_pad0 = EDBG_CDC_SERCOM_PINMUX_PAD0;
config_usart.pinmux_pad1 = EDBG_CDC_SERCOM_PINMUX_PAD1;
config_usart.pinmux_pad2 = EDBG_CDC_SERCOM_PINMUX_PAD2;
config_usart.pinmux_pad3 = EDBG_CDC_SERCOM_PINMUX_PAD3;
// route the printf output to the USART
stdio_serial_init(&usart_instance, EDBG_CDC_MODULE, &config_usart);
// enable USART
usart_enable(&usart_instance);
}
In windows device manager I saw that there was an "Atmel Corp. EDBG USB Port (COM3)" listed under "Ports". However, the one of the "Properties" of this port was listed as 9600 Bits per second. I changed this from 9600 to 115200 to be consistent with the config section above.
Finally, I ran PuTTY.exe and set the Connection-->Serial setting to COM3 and 115200 baud. Then I went to Session, then clicked the Serial Connection Type, then clicked the Open button. And, BAM, there's my printf output via PuTTY.

Detecting open PC COM port from USB Virtual Com Port device

I am using an STM32F105 microcontroller with the STM32_USB-FS-Device_Lib_V3.2.1 USB library and have adapted the VCP example for our purposes (integration with RTOS and serial API).
The problem is that if the USB cable is attached, but the port is not open on the Windows host, after a few minutes the device ends up permanently re-entering the USB ISR until the port is opened and then it all starts working normally.
I have instrumented interrupt handler and can see that when the fault occurs, the ISR handler exits and then immediately re-enters. This occurs because on exit from the interrupt the IEPINT flag in OTG_FS_GINTSTS is not clear. The OTG_FS_DAINT at this time contains 0x00000002 (IEPINT1 set), while DIEPINT1 has 0x00000080 (TXFE). The line in OTGD_FS_Handle_InEP_ISR() that clears TXFE is called, but the bit either does not clear or becomes immediately reasserted. When the COM port on the host is reopened, the state of OTG_FS_GINTSTS and OTG_FS_DAINT at the end of the interrupt is always zero, and further interrupts occur at the normal rate. Note that the problem only occurs if data is being output but the host has no port open. If either the port is open or no data is output, the system runs indefinitely. I believe that the more data that is output the sooner the problem occurs, but that is anecdotal at present.
The VCP code has a state variable that takes the following enumerated values:
UNCONNECTED,
ATTACHED,
POWERED,
SUSPENDED,
ADDRESSED,
CONFIGURED
and we use the CONFIGURED state to determine whether to put data into the driver buffer for sending. However the CONFIGURED state is set when the cable is attached not when the host has the port open and an application connected. I see that when Windows does open the port, there is a burst of interrupts so it seems that some communication occurs on this event; I wonder if it is possible therefore to detect whether the host has the port open,.
I need one of two things perhaps:
To prevent the USB code from getting stuck in the ISR in the first instance
To determine whether the host has the port open from the device end, and only push data for sending when open.
Part (1) - preventing the interrupt lock-up - was facilitated by a USB library bug fix from ST support; it was not correctly clearing the TxEmpty interrupt.
After some research and assistance from ST Support, I have determined a solution to part (2) - detecting whether the host port is open. Conventionally, when a port is opened the DTR modem control line is asserted. This information is passed to a CDC class device, so I can use this to achieve my aim. It is possible for an application to change the behaviour of DTR, but this should not happen in any of the client applications that are likely to connect to this device in this case. However there is a back-up plan that implicitly assumes the port to be open if the line-coding (baud, framing) are set. In this case there is no means of detecting closure but at least it will not prevent an unconventional application from working with my device, even if it then causes it to crash when it disconnects.
Regarding ST's VCP example code specifically I have made the following changes to usb_prop.c:
1) Added the following function:
#include <stdbool.h>
static bool host_port_open = false ;
bool Virtual_Com_Port_IsHostPortOpen()
{
return bDeviceState == CONFIGURED && host_port_open ;
}
2) Modified Virtual_Com_Port_NoData_Setup() handling of SET_CONTROL_LINE_STATE thus:
else if (RequestNo == SET_CONTROL_LINE_STATE)
{
// Test DTR state to determine if host port is open
host_port_open = (pInformation->USBwValues.bw.bb0 & 0x01) != 0 ;
return USB_SUCCESS;
}
3) To allow use with applications that do not operate DTR conventionally I have also modified Virtual_Com_Port_Data_Setup() handling of SET_LINE_CODING thus:
else if (RequestNo == SET_LINE_CODING)
{
if (Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT))
{
CopyRoutine = Virtual_Com_Port_SetLineCoding;
// If line coding is set the port is implicitly open
// regardless of host's DTR control. Note: if this is
// the only indicator of port open, there will be no indication
// of closure, but this will at least allow applications that
// do not assert DTR to connect.
host_port_open = true ;
}
Request = SET_LINE_CODING;
}
I found another solution by adopting CDC_Transmit_FS.
It can now be used as output for printf by overwriting _write function.
First it checks the connection state, then it tries to send over USB endport in a busy loop, which repeats sending if USB is busy.
I found out if dev_state is not USBD_STATE_CONFIGURED the USB plug is disconnected. If the plug is connected but no VCP port is open via PuTTY or termite, the second check fails.
This implementation works fine for me for RTOS and CubeMX HAL application. The busy loop is not blocking low priority threads anymore.
uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len)
{
uint8_t result = USBD_OK;
// Check if USB interface is online and VCP connection is open.
// prior to send:
if ((hUsbDevice_0->dev_state != USBD_STATE_CONFIGURED)
|| (hUsbDevice_0->ep0_state == USBD_EP0_STATUS_IN))
{
// The physical connection fails.
// Or: The phycical connection is open, but no VCP link up.
result = USBD_FAIL;
}
else
{
USBD_CDC_SetTxBuffer(hUsbDevice_0, Buf, Len);
// Busy wait if USB is busy or exit on success or disconnection happens
while(1)
{
//Check if USB went offline while retrying
if ((hUsbDevice_0->dev_state != USBD_STATE_CONFIGURED)
|| (hUsbDevice_0->ep0_state == USBD_EP0_STATUS_IN))
{
result = USBD_FAIL;
break;
}
// Try send
result = USBD_CDC_TransmitPacket(hUsbDevice_0);
if(result == USBD_OK)
{
break;
}
else if(result == USBD_BUSY)
{
// Retry until USB device free.
}
else
{
// Any other failure
result = USBD_FAIL;
break;
}
}
}
return result;
}
CDC_Transmit_FS is used by _write:
// This function is used by printf and puts.
int _write(int file, char *ptr, int len)
{
(void) file; // Ignore file descriptor
uint8_t result;
result = CDC_Transmit_FS((uint8_t*)ptr, len);
if(result == USBD_OK)
{
return (int)len;
}
else
{
return EOF;
}
}
Regards
Bernhard
After so much searching and a kind of reverse engineering I finally found the method for detecting the open terminal and also it's termination. I found that in the CDC class there is three Data nodes , one is a control node and the other two are data In and data Out nodes.Now when you open a terminal a code is sent to the control node and also when you close it. all we need to do is to get those codes and by them start and stop our data transmission tasks. the code that is sent is respectively 0x21 and 0x22 for opening and closing the terminal.In the usb_cdc_if.c there is a function that receive and interpret those codes (there is a switch case and the variable cmd is the code we are talking about).that function is CDC_Control_FS . Here we are, Now all we need to do is to expand that function so that it interpret the 0x22 and 0x21 . there you are , now you know in your application whether the port is open or not.
I need one of two things perhaps:
To prevent the USB code from getting stuck in the ISR in the first instance
To determine whether the host has the port open from the device end, and only push data for sending when open.
You should attempt to do option 1 instead of 2. On Windows and Linux, it is possible to open a COM port and use it without setting the control signals, which means there is no fool-proof, cross-platform way to detect that the COM port is open.
A well programmed device will not let itself stop functioning just because the USB host stopped polling for data; this is a normal thing that should be handled properly. For example, you might change your code so that you only queue up data to be sent to the USB host if there is buffer space available for the endpoint. If there is no free buffer space, you might have some special error handling code.
I have the same requirement to detect PC port open/close. I have seen it implemented it as follows:
Open detected by:
DTR asserted
CDC bulk transfer
Close detected by:
DTR deasserted
USB "unplugged", sleep etc
This seems to be working reasonably well, although more thorough testing will be needed to confirm it works robustly.
Disclaimer: I use code generated by Cube, and as a result it works with HAL drivers. Solutions, proposed here before, don't work for me, so I have found one. It is not good, but works for some purposes.
One of indirect sign of not opened port arises when you try to transmit packet by CDC_Transmit_FS, and then wait till TxState is set to 0. If port is not opened it never happens. So my solution is to fix some timeout:
uint16_t count = 0;
USBD_CDC_HandleTypeDef *hcdc =
(USBD_CDC_HandleTypeDef*) USBD_Device.pClassData;
while (hcdc->TxState != 0) {
if (++count > BUSY_TIMEOUT) { //number of cycles to wait till it makes decision
//here it's clear that port is not opened
}
}
The problem is also, that if one tries to open port, after device has tried to send a packet, it cant be done. Therefore whole routine I use:
uint8_t waitForTransferCompletion(void) {
uint16_t count = 0;
USBD_CDC_HandleTypeDef *hcdc =
(USBD_CDC_HandleTypeDef*) USBD_Device.pClassData;
while (hcdc->TxState != 0) {
if (++count > BUSY_TIMEOUT) { //number of cycles to wait till it makes decision
USBD_Stop(&USBD_Device); // stop and
MX_USB_DEVICE_Init(); // init device again
HAL_Delay(RESET_DELAY); // give a chance to open port
return USBD_FAIL; // return fail, to send last packet again
}
}
return USBD_OK;
}
The question is, how big timeout has to be, not to interrupt transmission while port is opened. I set BUSY_TIMEOUT to 3000, and now it works.
I fixed it by checking of a variable hUsbDeviceFS.ep0_state.
It equal 5 if connected and 4 if do not connected or was disconnected.
But. There are some issue in the HAL. It equal 5 when program started.
Next steps fixed it at the begin of a program
/* USER CODE BEGIN 2 */
HAL_Delay(500);
hUsbDeviceFS.ep0_state = 4;
...
I do not have any wishes to learn the HAL - I hope this post will be seeing by developers and they will fix the HAL.
It helped me to fix my issue.