cancel
Showing results for 
Search instead for 
Did you mean: 

Nucleo-n657x0Q: USB Device CDC ACM for n657x0q

TienNHxz04
Associate II

Hello,

I currently need to transfer ADC data from a nucleo-n657x0Q to a PC. I've configured the stm32n6 using ST's example on GitHub, but I still don't know how to use the `ux_device_class_cdc_acm_write` function to transfer the data.

Can anyone guide me on how to transfer data using this USB HS?

1 ACCEPTED SOLUTION

Accepted Solutions
Gyessine
ST Employee

This is a guide that will help you  build your own USB CDC ACM application to transmit data using the "ux_device_class_cdc_acm_write" it just need few tweaks to transmit your own data. its based on STM32H5 but you can tailor it easy to the STM32N6 since they are both USBX native :

1.1. Project configuration

Start by creating a new project using the STM32CubeMX by clicking [File -> New -> STM32 Project].

Once the project creation is done, enable the USB peripheral [USB] peripheral in device only mode, located in the [Connectivity] section. 

Gyessine_1-1776356326459.png

Enable the [CRS SYNC SOURCE USB] option in CRS SYNC, located in the [RCC] section. 

Gyessine_2-1776356326491.png

Enable the [CORE] in THREADX, located in the [Middleware and software packages] section.

Increment the [TX_MINIMUM_STACK] to 800 bytes.

Gyessine_3-1776356327006.png

Enable the [Core system] [Device CoreStack FS] [Device Controllers FS] [CDC ACM] in USBX, located in the [Middleware and software packages] section.
Change [UXDevice memory pool size] to 12*1024.
Change [USBX Device system Stack size] to 6*1024.

Gyessine_4-1776356326921.png

 

In the [CLOCK CONFIGURATION], just ensure that the USB is getting a 48MHZ signal.

Gyessine_5-1776356326678.png

In the project manager, select advanced settings and fill the options like this.

Gyessine_6-1776356326882.png

Now, name your project and generate your code.

Gyessine_7-1776356326677.png

Now, we just need to add these parts of code in our project:

In app_usbx_device.c:

/* USER CODE BEGIN Includes */
#include "main.h"

/* USER CODE END Includes */

/* USER CODE BEGIN PV */

extern PCD_HandleTypeDef hpcd_USB_OTG_HS;
static TX_THREAD ux_cdc_write_thread;
TX_SEMAPHORE semaphore;
/* USER CODE END PV */
 

static VOID app_ux_device_thread_entry(ULONG thread_input)
{
  /* USER CODE BEGIN app_ux_device_thread_entry */
  /* Initialize the USB OTG HS  Peripheral */
  MX_USB_OTG_FS_PCD_Init();
  /* Allocate the RX and TX FIFOs */
  HAL_PCDEx_SetRxFiFo(&hpcd_USB_OTG_HS, 0x200);
  HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS, 0, 0x100);
  HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS, 1, 0x100);
  HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS, 2, 0x100);
  HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS, 3, 0x100);
  /* Link the drivers using to the USBX */
  ux_dcd_stm32_initialize((ULONG)USB_OTG_HS, (ULONG)&hpcd_USB_OTG_HS);
  /* Start the Peripheral */
  HAL_PCD_Start(&hpcd_USB_OTG_HS);
  /* USER CODE END app_ux_device_thread_entry */
}

In ux_device_cdc_acm.c:

/* USER CODE BEGIN PV */
#define APP_RX_DATA_SIZE   2048
#define APP_TX_DATA_SIZE   2048
#define RX_NEW_RECEIVED_DATA      0x01
#define TX_NEW_TRANSMITTED_DATA   0x02
UX_SLAVE_CLASS_CDC_ACM  *cdc_acm;
extern TX_SEMAPHORE semaphore;
UX_SLAVE_CLASS_CDC_ACM_LINE_CODING_PARAMETER CDC_VCP_LineCoding =
{
  115200, /* baud rate */
  0x00,   /* stop bits-1 */
  0x00,   /* parity - none */
  0x08    /* nb. of bits 8 */
};
/* USER CODE END PV */
VOID USBD_CDC_ACM_Activate(VOID *cdc_acm_instance)
{
  /* USER CODE BEGIN USBD_CDC_ACM_Activate */
	  cdc_acm = (UX_SLAVE_CLASS_CDC_ACM*) cdc_acm_instance;

	  /* Set device class_cdc_acm with default parameters */
	  if (ux_device_class_cdc_acm_ioctl(cdc_acm, UX_SLAVE_CLASS_CDC_ACM_IOCTL_SET_LINE_CODING,
	                                    &CDC_VCP_LineCoding) != UX_SUCCESS)
	  {
	    Error_Handler();
	  }
  /* USER CODE END USBD_CDC_ACM_Activate */

  return;
}
VOID USBD_CDC_ACM_ParameterChange(VOID *cdc_acm_instance)
{
  /* USER CODE BEGIN USBD_CDC_ACM_ParameterChange */
	UX_PARAMETER_NOT_USED(cdc_acm_instance);

	  ULONG request;
	  UX_SLAVE_TRANSFER *transfer_request;
	  UX_SLAVE_DEVICE *device;

	  /* Get the pointer to the device.  */
	  device = &_ux_system_slave -> ux_system_slave_device;

	  /* Get the pointer to the transfer request associated with the control endpoint. */
	  transfer_request = &device -> ux_slave_device_control_endpoint.ux_slave_endpoint_transfer_request;

	  request = *(transfer_request -> ux_slave_transfer_request_setup + UX_SETUP_REQUEST);

	  switch (request)
	  {
	    case UX_SLAVE_CLASS_CDC_ACM_SET_LINE_CODING :

	      /* Get the Line Coding parameters */
	      if (ux_device_class_cdc_acm_ioctl(cdc_acm, UX_SLAVE_CLASS_CDC_ACM_IOCTL_GET_LINE_CODING,
	                                        &CDC_VCP_LineCoding) != UX_SUCCESS)
	      {
	        Error_Handler();
	      }
	      break;

	    case UX_SLAVE_CLASS_CDC_ACM_GET_LINE_CODING :

	      /* Set the Line Coding parameters */
	      if (ux_device_class_cdc_acm_ioctl(cdc_acm, UX_SLAVE_CLASS_CDC_ACM_IOCTL_SET_LINE_CODING,
	                                        &CDC_VCP_LineCoding) != UX_SUCCESS)
	      {
	        Error_Handler();
	      }
	      break;

	    case UX_SLAVE_CLASS_CDC_ACM_SET_CONTROL_LINE_STATE :
	    default :
	      break;
	  }
  /* USER CODE END USBD_CDC_ACM_ParameterChange */

  return;
}
VOID usbx_cdc_acm_write_thread_entry(ULONG thread_input)
{
    UX_PARAMETER_NOT_USED(thread_input);
    ULONG actual_length;
    unsigned int counter = 1;

    /* Buffer must be large enough for base_msg + number + CRLF + '\0' */
    char Tx_Buffer[16];

    /* Base message without number */
    const char base_msg[] ="helloworld";

    while (1)
    {
        /* Format: base message + incrementing number + CRLF */
        int len = snprintf(Tx_Buffer, sizeof(Tx_Buffer), "%s%u\r\n", base_msg, counter);

        /* Check for truncation or error (optional but good practice) */

            /* Formatting error or truncated – handle as needed, e.g. clip len */
            len = sizeof(Tx_Buffer);

        /* Transmit the string over USB CDC ACM */
        ux_device_class_cdc_acm_write(cdc_acm, (UCHAR *)Tx_Buffer, (ULONG)len, &actual_length);

        /* Increment counter so next message has next number */
        counter++;

        /* Delay between transmissions */
        tx_thread_sleep(10UL);
    }
}

In ux_device_cdc_acm.h:

/* USER CODE BEGIN 1 */
VOID usbx_cdc_acm_write_thread_entry(ULONG thread_input);
/* USER CODE END 1 */

Here all the code implementation is done! You can build and flash the code into your device and try it!

2. Results

After flashing the code, connect a USB Type-C® cable into the user USB connector and connect it to a computer. Open a virtual COM port terminal in your computer.

Gyessine_8-1776356403833.png

An incrementing “Hello world” message must appear in the serial monitor view.

Gyessine_9-1776356403869.png

BR
Gyessine

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

View solution in original post

5 REPLIES 5
Andrew Neil
Super User

As it's on a Nucleo board, why not just use the Virtual COM Port (VCP) built into the ST-Link ?

A complex system that works is invariably found to have evolved from a simple system that worked.
A complex system designed from scratch never works and cannot be patched up to make it work.
Gyessine
ST Employee

This is a guide that will help you  build your own USB CDC ACM application to transmit data using the "ux_device_class_cdc_acm_write" it just need few tweaks to transmit your own data. its based on STM32H5 but you can tailor it easy to the STM32N6 since they are both USBX native :

1.1. Project configuration

Start by creating a new project using the STM32CubeMX by clicking [File -> New -> STM32 Project].

Once the project creation is done, enable the USB peripheral [USB] peripheral in device only mode, located in the [Connectivity] section. 

Gyessine_1-1776356326459.png

Enable the [CRS SYNC SOURCE USB] option in CRS SYNC, located in the [RCC] section. 

Gyessine_2-1776356326491.png

Enable the [CORE] in THREADX, located in the [Middleware and software packages] section.

Increment the [TX_MINIMUM_STACK] to 800 bytes.

Gyessine_3-1776356327006.png

Enable the [Core system] [Device CoreStack FS] [Device Controllers FS] [CDC ACM] in USBX, located in the [Middleware and software packages] section.
Change [UXDevice memory pool size] to 12*1024.
Change [USBX Device system Stack size] to 6*1024.

Gyessine_4-1776356326921.png

 

In the [CLOCK CONFIGURATION], just ensure that the USB is getting a 48MHZ signal.

Gyessine_5-1776356326678.png

In the project manager, select advanced settings and fill the options like this.

Gyessine_6-1776356326882.png

Now, name your project and generate your code.

Gyessine_7-1776356326677.png

Now, we just need to add these parts of code in our project:

In app_usbx_device.c:

/* USER CODE BEGIN Includes */
#include "main.h"

/* USER CODE END Includes */

/* USER CODE BEGIN PV */

extern PCD_HandleTypeDef hpcd_USB_OTG_HS;
static TX_THREAD ux_cdc_write_thread;
TX_SEMAPHORE semaphore;
/* USER CODE END PV */
 

static VOID app_ux_device_thread_entry(ULONG thread_input)
{
  /* USER CODE BEGIN app_ux_device_thread_entry */
  /* Initialize the USB OTG HS  Peripheral */
  MX_USB_OTG_FS_PCD_Init();
  /* Allocate the RX and TX FIFOs */
  HAL_PCDEx_SetRxFiFo(&hpcd_USB_OTG_HS, 0x200);
  HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS, 0, 0x100);
  HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS, 1, 0x100);
  HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS, 2, 0x100);
  HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS, 3, 0x100);
  /* Link the drivers using to the USBX */
  ux_dcd_stm32_initialize((ULONG)USB_OTG_HS, (ULONG)&hpcd_USB_OTG_HS);
  /* Start the Peripheral */
  HAL_PCD_Start(&hpcd_USB_OTG_HS);
  /* USER CODE END app_ux_device_thread_entry */
}

In ux_device_cdc_acm.c:

/* USER CODE BEGIN PV */
#define APP_RX_DATA_SIZE   2048
#define APP_TX_DATA_SIZE   2048
#define RX_NEW_RECEIVED_DATA      0x01
#define TX_NEW_TRANSMITTED_DATA   0x02
UX_SLAVE_CLASS_CDC_ACM  *cdc_acm;
extern TX_SEMAPHORE semaphore;
UX_SLAVE_CLASS_CDC_ACM_LINE_CODING_PARAMETER CDC_VCP_LineCoding =
{
  115200, /* baud rate */
  0x00,   /* stop bits-1 */
  0x00,   /* parity - none */
  0x08    /* nb. of bits 8 */
};
/* USER CODE END PV */
VOID USBD_CDC_ACM_Activate(VOID *cdc_acm_instance)
{
  /* USER CODE BEGIN USBD_CDC_ACM_Activate */
	  cdc_acm = (UX_SLAVE_CLASS_CDC_ACM*) cdc_acm_instance;

	  /* Set device class_cdc_acm with default parameters */
	  if (ux_device_class_cdc_acm_ioctl(cdc_acm, UX_SLAVE_CLASS_CDC_ACM_IOCTL_SET_LINE_CODING,
	                                    &CDC_VCP_LineCoding) != UX_SUCCESS)
	  {
	    Error_Handler();
	  }
  /* USER CODE END USBD_CDC_ACM_Activate */

  return;
}
VOID USBD_CDC_ACM_ParameterChange(VOID *cdc_acm_instance)
{
  /* USER CODE BEGIN USBD_CDC_ACM_ParameterChange */
	UX_PARAMETER_NOT_USED(cdc_acm_instance);

	  ULONG request;
	  UX_SLAVE_TRANSFER *transfer_request;
	  UX_SLAVE_DEVICE *device;

	  /* Get the pointer to the device.  */
	  device = &_ux_system_slave -> ux_system_slave_device;

	  /* Get the pointer to the transfer request associated with the control endpoint. */
	  transfer_request = &device -> ux_slave_device_control_endpoint.ux_slave_endpoint_transfer_request;

	  request = *(transfer_request -> ux_slave_transfer_request_setup + UX_SETUP_REQUEST);

	  switch (request)
	  {
	    case UX_SLAVE_CLASS_CDC_ACM_SET_LINE_CODING :

	      /* Get the Line Coding parameters */
	      if (ux_device_class_cdc_acm_ioctl(cdc_acm, UX_SLAVE_CLASS_CDC_ACM_IOCTL_GET_LINE_CODING,
	                                        &CDC_VCP_LineCoding) != UX_SUCCESS)
	      {
	        Error_Handler();
	      }
	      break;

	    case UX_SLAVE_CLASS_CDC_ACM_GET_LINE_CODING :

	      /* Set the Line Coding parameters */
	      if (ux_device_class_cdc_acm_ioctl(cdc_acm, UX_SLAVE_CLASS_CDC_ACM_IOCTL_SET_LINE_CODING,
	                                        &CDC_VCP_LineCoding) != UX_SUCCESS)
	      {
	        Error_Handler();
	      }
	      break;

	    case UX_SLAVE_CLASS_CDC_ACM_SET_CONTROL_LINE_STATE :
	    default :
	      break;
	  }
  /* USER CODE END USBD_CDC_ACM_ParameterChange */

  return;
}
VOID usbx_cdc_acm_write_thread_entry(ULONG thread_input)
{
    UX_PARAMETER_NOT_USED(thread_input);
    ULONG actual_length;
    unsigned int counter = 1;

    /* Buffer must be large enough for base_msg + number + CRLF + '\0' */
    char Tx_Buffer[16];

    /* Base message without number */
    const char base_msg[] ="helloworld";

    while (1)
    {
        /* Format: base message + incrementing number + CRLF */
        int len = snprintf(Tx_Buffer, sizeof(Tx_Buffer), "%s%u\r\n", base_msg, counter);

        /* Check for truncation or error (optional but good practice) */

            /* Formatting error or truncated – handle as needed, e.g. clip len */
            len = sizeof(Tx_Buffer);

        /* Transmit the string over USB CDC ACM */
        ux_device_class_cdc_acm_write(cdc_acm, (UCHAR *)Tx_Buffer, (ULONG)len, &actual_length);

        /* Increment counter so next message has next number */
        counter++;

        /* Delay between transmissions */
        tx_thread_sleep(10UL);
    }
}

In ux_device_cdc_acm.h:

/* USER CODE BEGIN 1 */
VOID usbx_cdc_acm_write_thread_entry(ULONG thread_input);
/* USER CODE END 1 */

Here all the code implementation is done! You can build and flash the code into your device and try it!

2. Results

After flashing the code, connect a USB Type-C® cable into the user USB connector and connect it to a computer. Open a virtual COM port terminal in your computer.

Gyessine_8-1776356403833.png

An incrementing “Hello world” message must appear in the serial monitor view.

Gyessine_9-1776356403869.png

BR
Gyessine

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

Thank @Gyessine , I am working with FSBL, so I need change code of which file?

I don't know how to use ST LINK to transfer data to PC, because it's work is supply power for STM.


@TienNHxz04 wrote:

I don't know how to use ST LINK to transfer data to PC


It provides a Virtual COM Port (VCP) connected to one of the UARTs on the Target STM32N657x0q.

It is the simplest way to get data to a PC - no need to mess about with implementing USB and CDC yourself in your code.

Details are in the User Manual for the board:

AndrewNeil_0-1776417322507.png

AndrewNeil_1-1776417347398.png

https://www.st.com/resource/en/user_manual/um3417-stm32n6-nucleo144-board-mb1940-stmicroelectronics.pdf

via: https://www.st.com/en/evaluation-tools/nucleo-n657x0-q.html#documentation

A complex system that works is invariably found to have evolved from a simple system that worked.
A complex system designed from scratch never works and cannot be patched up to make it work.