【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;"> 【ST NUCLEO-U5A5ZJ-Q开发板测评】7 试跑一个USB-to-UART vcp程序</span></strong></div>
<div> </div>
<div> </div>
<div>这是一个stm32cubeide的例程。使用了操作系统threadx和usb 实现COM端口(VCP)的USB-to-UART桥。</div>
<div> </div>
<div><span style="font-size:18px;"><strong>主要电路</strong></span></div>
<div> </div>
<div> </div>
<div><strong><span style="font-size:16px;">简单程序介绍(目前只是试跑,所以先简单分析)</span></strong></div>
<div> </div>
<div>1,在开始ThreadX调用入口函数tx_application_define()时,在此阶段,所有USBx资源都被初始化,CDC_ACM类驱动程序被注册。</div>
<div> 并且应用程序创建具有相同优先级的3个任务线程:</div>
<div> -app_ux_device_thread_entry(优先级:10;抢占优先级:10)用于初始化USB OTG HAL PCD驱动程序并启动设备。</div>
<div> -usbx_cdc_acm_read_thread_entry(优先级:20;抢占优先级:20),用于从虚拟COM端口读取接收到的数据。</div>
<div> -usbx_cdc_acm_write_thread_entry(优先级:20;抢占优先级:20),用于通过UART发送接收到的数据。</div>
<div>程序截图如下:</div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div> </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> </div>
<div>在枚举阶段,在CDC类实现中声明了三个通信“端点”:</div>
<div>-1个Bulk IN端点,用于从STM32设备接收到PC主机的数据:</div>
<div>当通过UART接收数据时,数据会保存在缓冲区“UserTxBufferFS”中定期,在<strong>usbx_cdc_acm_write_thread_entry</strong>检查缓冲区“UserTxBufferFS”的状态如果有可用的数据,他们是响应于IN令牌而发送的,否则它是NAKed。</div>
<div>-1个Bulk OUT端点,用于将数据从PC主机传输到STM32设备:</div>
<div>当通过该端点接收数据时,它们被保存在缓冲区“UserRxBufferFS”中,然后使用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> </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 = &_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;
}</code></pre>
<pre>
<code class="language-cpp">/* USER CODE BEGIN 1 */
/**
* <a href="home.php?mod=space&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 = &_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));
}
}
}</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(&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),
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;
}
}
}
}
}
</code></pre>
<p> </p>
<pre>
<code class="language-cpp">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();
}
}</code></pre>
<div> </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->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);
}</code></pre>
<p> </p>
</div>
<div> </div>
<div>通过UART接收数据由中断处理,而传输由DMA处理,因此应用程序可以在传输另一个数据的同时接收数据(全双工功能)。</div>
<div>VCP驱动从ST官网下载。</div>
<div> </div>
<div>串口参数配置可以是或其他</div>
<div>-115200 N,8,1</div>
<div>-流量控制=无</div>
<div> </div>
<div>微控制器的PA9和PA10上可用的USART1接口有连接到ST-LINK MCU。</div>
<div>默认情况下,目标MCU和ST-LINK MCU之间的USART1通信已启用,参数配置一样。</div>
<div> </div>
<div>板子编译程序后下载程序,插上stlink口和usb typec口后应该如下出现两个虚拟串口</div>
<div></div>
<p> </p>
<div>具体运行结果如下:</div>
<p> </p>
<div></div>
<div> </div>
<div>演示视频</div>
<div>525fc109c1af1709a92ea4d6feeff5b7<br />
</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> <p>电脑录屏其实可以找一个录屏软件,这样看的更清楚一些~</p>
wangerxian 发表于 2024-3-11 10:22
电脑录屏其实可以找一个录屏软件,这样看的更清楚一些~
<p>谢谢提醒,下次找个试一下。</p>
页:
[1]