damiaa 发表于 2024-3-10 22:07

【ST NUCLEO-U5A5ZJ-Q开发板测评】7 试跑一个USB-to-UART vcp程序

<div class='showpostmsg'> 本帖最后由 damiaa 于 2024-3-10 22:51 编辑

<div><strong><span style="font-size:20px;">&nbsp; 【ST NUCLEO-U5A5ZJ-Q开发板测评】7 试跑一个USB-to-UART vcp程序</span></strong></div>

<div>&nbsp;</div>

<div>&nbsp;</div>

<div>这是一个stm32cubeide的例程。使用了操作系统threadx和usb 实现COM端口(VCP)的USB-to-UART桥。</div>

<div>&nbsp;</div>

<div><span style="font-size:18px;"><strong>主要电路</strong></span></div>

<div> &nbsp;</div>

<div> &nbsp;</div>

<div><strong><span style="font-size:16px;">简单程序介绍(目前只是试跑,所以先简单分析)</span></strong></div>

<div>&nbsp;</div>

<div>1,在开始ThreadX调用入口函数tx_application_define()时,在此阶段,所有USBx资源都被初始化,CDC_ACM类驱动程序被注册。</div>

<div>&nbsp;&nbsp;&nbsp;&nbsp; 并且应用程序创建具有相同优先级的3个任务线程:</div>

<div>&nbsp;&nbsp;&nbsp;&nbsp; -app_ux_device_thread_entry(优先级:10;抢占优先级:10)用于初始化USB OTG HAL PCD驱动程序并启动设备。</div>

<div>&nbsp;&nbsp;&nbsp;&nbsp; -usbx_cdc_acm_read_thread_entry(优先级:20;抢占优先级:20),用于从虚拟COM端口读取接收到的数据。</div>

<div>&nbsp;&nbsp;&nbsp;&nbsp; -usbx_cdc_acm_write_thread_entry(优先级:20;抢占优先级:20),用于通过UART发送接收到的数据。</div>

<div>程序截图如下:</div>

<div></div>

<div></div>

<div></div>

<div></div>

<div></div>

<div>&nbsp;</div>

<div><strong>线程app_ux_device_thread_entry</strong>负责启动或停止USB设备。</div>

<div>运行模式下,线程将等待来自USB_PD接口的消息队列,当USB设备插入主机PC时,USB_PD接口的回调函数将发送消息到app_ux_device_thread_entry接口去启动USB设备</div>

<div>同样,当USB设备从主机拔下时USB_PD接口的回调函数将发送消息到 app_ux_device_thread_entry去停止USB设备</div>

<div>&nbsp;</div>

<div>在枚举阶段,在CDC类实现中声明了三个通信&ldquo;端点&rdquo;:</div>

<div>-1个Bulk IN端点,用于从STM32设备接收到PC主机的数据:</div>

<div>当通过UART接收数据时,数据会保存在缓冲区&ldquo;UserTxBufferFS&rdquo;中定期,在<strong>usbx_cdc_acm_write_thread_entry</strong>检查缓冲区&ldquo;UserTxBufferFS&rdquo;的状态如果有可用的数据,他们是响应于IN令牌而发送的,否则它是NAKed。</div>

<div>-1个Bulk OUT端点,用于将数据从PC主机传输到STM32设备:</div>

<div>当通过该端点接收数据时,它们被保存在缓冲区&ldquo;UserRxBufferFS&rdquo;中,然后使用DMA模式通过UART进行传输,同时OUT端点为NAKed。</div>

<div>传输结束后,OUT端点准备在<strong>HAL_UART_RxCpltCallback</strong>()中接收下一个数据包。</div>

<div>-1 x中断IN端点,用于设置和获取串行端口参数:</div>

<div>当接收到控制设置时,在ux_app_parameters_change()中执行相应的请求。</div>

<div>&nbsp;</div>

<div>
<pre>
<code class="language-cpp">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 = &amp;_ux_system_slave -&gt; ux_system_slave_device;
/* Get the pointer to the transfer request associated with the control endpoint. */
transfer_request = &amp;device -&gt; ux_slave_device_control_endpoint.ux_slave_endpoint_transfer_request;
request = *(transfer_request -&gt; 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,
&amp;CDC_VCP_LineCoding) != UX_SUCCESS)
{
Error_Handler();
}
/* Check if baudrate &lt; 9600) then set it to 9600 */
if (CDC_VCP_LineCoding.ux_slave_class_cdc_acm_parameter_baudrate &lt; MIN_BAUDRATE)
{
CDC_VCP_LineCoding.ux_slave_class_cdc_acm_parameter_baudrate = MIN_BAUDRATE;
/* Set the new configuration of ComPort */
USBD_CDC_VCP_Config(&amp;CDC_VCP_LineCoding);
}
else
{
/* Set the new configuration of ComPort */
USBD_CDC_VCP_Config(&amp;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,
&amp;CDC_VCP_LineCoding) != UX_SUCCESS)
{
Error_Handler();
}
break;
case UX_SLAVE_CLASS_CDC_ACM_SET_CONTROL_LINE_STATE :
default :
break;
}</code></pre>

<pre>
<code class="language-cpp">/* USER CODE BEGIN 1 */

/**
* <a href="home.php?mod=space&amp;uid=159083" target="_blank">@brief </a>Function implementing usbx_cdc_acm_thread_entry.
* @paramthread_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 = &amp;_ux_system_slave-&gt;ux_system_slave_device;

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

#ifndef UX_DEVICE_CLASS_CDC_ACM_TRANSMISSION_DISABLE

      /* Set transmission_status to UX_FALSE for the first time */
      cdc_acm -&gt; 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,
                                 &amp;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(&amp;EventFlag, TX_NEW_TRANSMITTED_DATA, TX_OR_CLEAR,
                               &amp;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));
    }
}
}</code></pre>

<pre>
<code class="language-cpp">/**
* @briefFunction implementing usbx_cdc_acm_write_thread_entry.
* @paramthread_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(&amp;EventFlag, RX_NEW_RECEIVED_DATA, TX_OR_CLEAR,
                           &amp;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 -&gt; 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 &gt; 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 *)(&amp;UserTxBufferFS),
                                        buffsize, &amp;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;
      }
      }
    }
}
}
</code></pre>

<p>&nbsp;</p>

<pre>
<code class="language-cpp">void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
/* Set RX_NEW_RECEIVED_DATA flag */
if (tx_event_flags_set(&amp;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();
}

}</code></pre>

<div>&nbsp;</div>

<div>在CDC_ACM应用程序中,实现了两个请求:</div>

<div>-设置行:设置比特率、停止比特数、奇偶校验和数据比特数</div>

<div>-获取行:获取比特率、停止比特数、奇偶校验和数据比特数</div>

<div>其他请求(发送中断、控制线状态)没有实现</div>

<pre>
<code class="language-cpp">/**
* @briefUSBD_CDC_VCP_Config
            Configure the COM Port with the parameters received from host.
* @paramUX_SLAVE_CLASS_CDC_ACM_LINE_CODING_PARAMETER: linecoding struct.
* @paramCDC_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-&gt;ux_slave_class_cdc_acm_parameter_stop_bit)
{
    case 0:

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

      break;

    case 2:

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

      break;

    default :

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

      break;
}

/* Check parity parameter */
switch (CDC_VCP_LineCoding-&gt;ux_slave_class_cdc_acm_parameter_parity)
{
    case 0:

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

      break;

    case 1:

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

      break;

    case 2:

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

      break;

    default :

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

      break;
}

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

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

      break;

    case 0x08:

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

      break;

    default :

      uart_handler-&gt;Init.WordLength = UART_WORDLENGTH_8B;

      break;
}

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

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

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

/* Set the UART sampling */
uart_handler-&gt;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);
}</code></pre>

<p>&nbsp;</p>
</div>

<div>&nbsp;</div>

<div>通过UART接收数据由中断处理,而传输由DMA处理,因此应用程序可以在传输另一个数据的同时接收数据(全双工功能)。</div>

<div>VCP驱动从ST官网下载。</div>

<div>&nbsp;</div>

<div>串口参数配置可以是或其他</div>

<div>-115200 N,8,1</div>

<div>-流量控制=无</div>

<div>&nbsp;</div>

<div>微控制器的PA9和PA10上可用的USART1接口有连接到ST-LINK MCU。</div>

<div>默认情况下,目标MCU和ST-LINK MCU之间的USART1通信已启用,参数配置一样。</div>

<div>&nbsp;</div>

<div>板子编译程序后下载程序,插上stlink口和usb typec口后应该如下出现两个虚拟串口</div>

<div></div>

<p>&nbsp;</p>

<div>具体运行结果如下:</div>

<p>&nbsp;</p>

<div></div>

<div>&nbsp;</div>

<div>演示视频</div>

<div>525fc109c1af1709a92ea4d6feeff5b7<br />
&nbsp;</div>

<div>谢谢支持!</div>
</div><script>                                        var loginstr = '<div class="locked">查看本帖全部内容,请<a href="javascript:;"   style="color:#e60000" class="loginf">登录</a>或者<a href="https://bbs.eeworld.com.cn/member.php?mod=register_eeworld.php&action=wechat" style="color:#e60000" target="_blank">注册</a></div>';
                                       
                                        if(parseInt(discuz_uid)==0){
                                                                                                (function($){
                                                        var postHeight = getTextHeight(400);
                                                        $(".showpostmsg").html($(".showpostmsg").html());
                                                        $(".showpostmsg").after(loginstr);
                                                        $(".showpostmsg").css({height:postHeight,overflow:"hidden"});
                                                })(jQuery);
                                        }                </script><script type="text/javascript">(function(d,c){var a=d.createElement("script"),m=d.getElementsByTagName("script"),eewurl="//counter.eeworld.com.cn/pv/count/";a.src=eewurl+c;m.parentNode.insertBefore(a,m)})(document,523)</script>

wangerxian 发表于 2024-3-11 10:22

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

damiaa 发表于 2024-3-11 12:04

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

<p>谢谢提醒,下次找个试一下。</p>
页: [1]
查看完整版本: 【ST NUCLEO-U5A5ZJ-Q开发板测评】7 试跑一个USB-to-UART vcp程序