955|2

6108

帖子

4

TA的资源

版主

楼主
 

【ST NUCLEO-U5A5ZJ-Q开发板测评】7 试跑一个USB-to-UART vcp程序 [复制链接]

本帖最后由 damiaa 于 2024-3-10 22:51 编辑
  【ST NUCLEO-U5A5ZJ-Q开发板测评】7 试跑一个USB-to-UART vcp程序
 
 
这是一个stm32cubeide的例程。使用了操作系统threadx和usb 实现COM端口(VCP)的USB-to-UART桥。
 
主要电路
 
 
简单程序介绍(目前只是试跑,所以先简单分析)
 
1,在开始ThreadX调用入口函数tx_application_define()时,在此阶段,所有USBx资源都被初始化,CDC_ACM类驱动程序被注册。
     并且应用程序创建具有相同优先级的3个任务线程:
     -app_ux_device_thread_entry(优先级:10;抢占优先级:10)用于初始化USB OTG HAL PCD驱动程序并启动设备。
     -usbx_cdc_acm_read_thread_entry(优先级:20;抢占优先级:20),用于从虚拟COM端口读取接收到的数据。
     -usbx_cdc_acm_write_thread_entry(优先级:20;抢占优先级:20),用于通过UART发送接收到的数据。
程序截图如下:
 
线程app_ux_device_thread_entry负责启动或停止USB设备。
运行模式下,线程将等待来自USB_PD接口的消息队列,当USB设备插入主机PC时,USB_PD接口的回调函数将发送消息到app_ux_device_thread_entry接口去启动USB设备
同样,当USB设备从主机拔下时USB_PD接口的回调函数将发送消息到 app_ux_device_thread_entry去停止USB设备
 
在枚举阶段,在CDC类实现中声明了三个通信“端点”:
-1个Bulk IN端点,用于从STM32设备接收到PC主机的数据:
当通过UART接收数据时,数据会保存在缓冲区“UserTxBufferFS”中定期,在usbx_cdc_acm_write_thread_entry检查缓冲区“UserTxBufferFS”的状态如果有可用的数据,他们是响应于IN令牌而发送的,否则它是NAKed。
-1个Bulk OUT端点,用于将数据从PC主机传输到STM32设备:
当通过该端点接收数据时,它们被保存在缓冲区“UserRxBufferFS”中,然后使用DMA模式通过UART进行传输,同时OUT端点为NAKed。
传输结束后,OUT端点准备在HAL_UART_RxCpltCallback()中接收下一个数据包。
-1 x中断IN端点,用于设置和获取串行端口参数:
当接收到控制设置时,在ux_app_parameters_change()中执行相应的请求。
 
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();
}
/* Check if baudrate < 9600) then set it to 9600 */
if (CDC_VCP_LineCoding.ux_slave_class_cdc_acm_parameter_baudrate < MIN_BAUDRATE)
{
CDC_VCP_LineCoding.ux_slave_class_cdc_acm_parameter_baudrate = MIN_BAUDRATE;
/* Set the new configuration of ComPort */
USBD_CDC_VCP_Config(&CDC_VCP_LineCoding);
}
else
{
/* Set the new configuration of ComPort */
USBD_CDC_VCP_Config(&CDC_VCP_LineCoding);
}
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 BEGIN 1 */

/**
  * @brief   Function implementing usbx_cdc_acm_thread_entry.
  * @param  thread_input: Not used
  * @retval none
  */
VOID usbx_cdc_acm_read_thread_entry(ULONG thread_input)
{
  ULONG actual_length;
  ULONG senddataflag = 0;
  UX_SLAVE_DEVICE *device;

  UX_PARAMETER_NOT_USED(thread_input);

  device = &_ux_system_slave->ux_system_slave_device;

  while (1)
  {
    /* Check if device is configured */
    if ((device->ux_slave_device_state == UX_DEVICE_CONFIGURED) && (cdc_acm != UX_NULL))
    {

#ifndef UX_DEVICE_CLASS_CDC_ACM_TRANSMISSION_DISABLE

      /* Set transmission_status to UX_FALSE for the first time */
      cdc_acm -> ux_slave_class_cdc_acm_transmission_status = UX_FALSE;

#endif /* UX_DEVICE_CLASS_CDC_ACM_TRANSMISSION_DISABLE */

      /* Read the received data in blocking mode */
      ux_device_class_cdc_acm_read(cdc_acm, (UCHAR *)UserRxBufferFS, 64,
                                   &actual_length);
      if (actual_length != 0)
      {
        /* Send the data via UART */
        if (HAL_UART_Transmit_DMA(uart_handler, (uint8_t *)UserRxBufferFS, actual_length) != HAL_OK)
        {
          Error_Handler();
        }

        /* Wait until the requested flag TX_NEW_TRANSMITTED_DATA is received */
        if (tx_event_flags_get(&EventFlag, TX_NEW_TRANSMITTED_DATA, TX_OR_CLEAR,
                               &senddataflag, TX_WAIT_FOREVER) != TX_SUCCESS)
        {
          Error_Handler();
        }
      }
      else
      {
        /* Sleep thread for 10ms if no data received */
        tx_thread_sleep(MS_TO_TICK(10));
      }
    }
    else
    {
      /* Sleep thread for 10ms */
      tx_thread_sleep(MS_TO_TICK(10));
    }
  }
}
/**
  * @brief  Function implementing usbx_cdc_acm_write_thread_entry.
  * @param  thread_input: Not used
  * @retval none
  */
VOID usbx_cdc_acm_write_thread_entry(ULONG thread_input)
{
  ULONG receivedataflag = 0;
  ULONG actual_length, buffptr, buffsize;

  UX_PARAMETER_NOT_USED(thread_input);

  while (1)
  {
    /* Wait until the requested flag RX_NEW_RECEIVED_DATA is received */
    if (tx_event_flags_get(&EventFlag, RX_NEW_RECEIVED_DATA, TX_OR_CLEAR,
                           &receivedataflag, TX_WAIT_FOREVER) != TX_SUCCESS)
    {
      Error_Handler();
    }

#ifndef UX_DEVICE_CLASS_CDC_ACM_TRANSMISSION_DISABLE

    /* Set transmission_status to UX_FALSE for the first time */
    cdc_acm -> ux_slave_class_cdc_acm_transmission_status = UX_FALSE;

#endif

    /* Check if there is a new data to send */
    if (UserTxBufPtrOut != UserTxBufPtrIn)
    {
      /* Check buffer overflow and Rollback */
      if (UserTxBufPtrOut > UserTxBufPtrIn)
      {
        buffsize = APP_RX_DATA_SIZE - UserTxBufPtrOut;
      }
      else
      {
        /* Calculate data size */
        buffsize = UserTxBufPtrIn - UserTxBufPtrOut;
      }

      /* Copy UserTxBufPtrOut in buffptr */
      buffptr = UserTxBufPtrOut;

      /* Send data over the class cdc_acm_write */
      if (ux_device_class_cdc_acm_write(cdc_acm, (UCHAR *)(&UserTxBufferFS[buffptr]),
                                        buffsize, &actual_length) == UX_SUCCESS)
      {
        /* Increment the UserTxBufPtrOut pointer */
        UserTxBufPtrOut += buffsize;

        /* Rollback UserTxBufPtrOut if it equal to APP_TX_DATA_SIZE */
        if (UserTxBufPtrOut == APP_TX_DATA_SIZE)
        {
          UserTxBufPtrOut = 0;
        }
      }
    }
  }
}

 

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  /* Set RX_NEW_RECEIVED_DATA flag */
  if (tx_event_flags_set(&EventFlag, RX_NEW_RECEIVED_DATA, TX_OR) != TX_SUCCESS)
  {
    Error_Handler();
  }

  /* Increment the UserTxBufPtrIn pointer */
  UserTxBufPtrIn++;

  /* Rollback the UserTxBufPtrIn if it equal to APP_TX_DATA_SIZE */
  if (UserTxBufPtrIn == APP_TX_DATA_SIZE)
  {
    UserTxBufPtrIn = 0;
  }

  /* Start another reception: provide the buffer pointer with offset and the buffer size */
  if (HAL_UART_Receive_IT(uart_handler, (uint8_t *)UserTxBufferFS + UserTxBufPtrIn, 1) != HAL_OK)
  {
    /* Transfer error in reception process */
    Error_Handler();
  }

}
 
在CDC_ACM应用程序中,实现了两个请求:
-设置行:设置比特率、停止比特数、奇偶校验和数据比特数
-获取行:获取比特率、停止比特数、奇偶校验和数据比特数
其他请求(发送中断、控制线状态)没有实现
/**
  * @brief  USBD_CDC_VCP_Config
            Configure the COM Port with the parameters received from host.
  * @param  UX_SLAVE_CLASS_CDC_ACM_LINE_CODING_PARAMETER: linecoding struct.
  * @param  CDC_VCP_LineCoding: CDC VCP line coding.
  * @retval none
  */
static VOID USBD_CDC_VCP_Config(UX_SLAVE_CLASS_CDC_ACM_LINE_CODING_PARAMETER
                                *CDC_VCP_LineCoding)
{
  /* Deinitialization UART */
  if (HAL_UART_DeInit(uart_handler) != HAL_OK)
  {
    /* Deinitialization Error */
    Error_Handler();
  }

  /* Check stop bit parameter */
  switch (CDC_VCP_LineCoding->ux_slave_class_cdc_acm_parameter_stop_bit)
  {
    case 0:

      /* Set the UART Stop bit to 1 */
      uart_handler->Init.StopBits = UART_STOPBITS_1;

      break;

    case 2:

      /* Set the UART Stop bit to 2 */
      uart_handler->Init.StopBits = UART_STOPBITS_2;

      break;

    default :

      /* By default set the UART Stop bit to 1 */
      uart_handler->Init.StopBits = UART_STOPBITS_1;

      break;
  }

  /* Check parity parameter */
  switch (CDC_VCP_LineCoding->ux_slave_class_cdc_acm_parameter_parity)
  {
    case 0:

      /* Set the UART parity bit to none */
      uart_handler->Init.Parity = UART_PARITY_NONE;

      break;

    case 1:

      /* Set the UART parity bit to ODD */
      uart_handler->Init.Parity = UART_PARITY_ODD;

      break;

    case 2:

      /* Set the UART parity bit to even */
      uart_handler->Init.Parity = UART_PARITY_EVEN;

      break;

    default :

      /* By default set the UART parity bit to none */
      uart_handler->Init.Parity = UART_PARITY_NONE;

      break;
  }

  /* Set the UART data type : only 8bits and 9bits is supported */
  switch (CDC_VCP_LineCoding->ux_slave_class_cdc_acm_parameter_data_bit)
  {
    case 0x07:

      /* With this configuration a parity (Even or Odd) must be set */
      uart_handler->Init.WordLength = UART_WORDLENGTH_8B;

      break;

    case 0x08:

      if (uart_handler->Init.Parity == UART_PARITY_NONE)
      {
        uart_handler->Init.WordLength = UART_WORDLENGTH_8B;
      }
      else
      {
        uart_handler->Init.WordLength = UART_WORDLENGTH_9B;
      }

      break;

    default :

      uart_handler->Init.WordLength = UART_WORDLENGTH_8B;

      break;
  }

  /* Get the UART baudrate from vcp */
  uart_handler->Init.BaudRate = CDC_VCP_LineCoding->ux_slave_class_cdc_acm_parameter_baudrate;

  /* Set the UART Hw flow control to none */
  uart_handler->Init.HwFlowCtl = UART_HWCONTROL_NONE;

  /* Set the UART mode */
  uart_handler->Init.Mode = UART_MODE_TX_RX;

  /* Set the UART sampling */
  uart_handler->Init.OverSampling = UART_OVERSAMPLING_16;

  /* Initialization UART */
  if (HAL_UART_Init(uart_handler) != HAL_OK)
  {
    /* Initialization Error */
    Error_Handler();
  }

  /* Start reception: provide the buffer pointer with offset and the buffer size */
  HAL_UART_Receive_IT(uart_handler, (uint8_t *)(UserTxBufferFS + UserTxBufPtrIn), 1);
}

 

 
通过UART接收数据由中断处理,而传输由DMA处理,因此应用程序可以在传输另一个数据的同时接收数据(全双工功能)。
VCP驱动从ST官网下载。
 
串口参数配置可以是或其他
-115200 N,8,1
-流量控制=无
 
微控制器的PA9和PA10上可用的USART1接口有连接到ST-LINK MCU。
默认情况下,目标MCU和ST-LINK MCU之间的USART1通信已启用,参数配置一样。
 
板子编译程序后下载程序,插上stlink口和usb typec口后应该如下出现两个虚拟串口

 

具体运行结果如下:

 

 
演示视频
1

 
谢谢支持!
此帖出自stm32/stm8论坛

最新回复

电脑录屏其实可以找一个录屏软件,这样看的更清楚一些~   详情 回复 发表于 2024-3-11 10:22
点赞 关注
 

回复
举报

7244

帖子

2

TA的资源

版主

沙发
 

电脑录屏其实可以找一个录屏软件,这样看的更清楚一些~

此帖出自stm32/stm8论坛

点评

谢谢提醒,下次找个试一下。  详情 回复 发表于 2024-3-11 12:04
 
 

回复

6108

帖子

4

TA的资源

版主

板凳
 
wangerxian 发表于 2024-3-11 10:22 电脑录屏其实可以找一个录屏软件,这样看的更清楚一些~

谢谢提醒,下次找个试一下。

此帖出自stm32/stm8论坛
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/6 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表