九子帝王 发表于 2024-5-19 21:36

【AT-START-F405测评】USB_Host cdc在freertos下速度测试及优化

本帖最后由 九子帝王 于 2024-5-19 21:46 编辑

#申请原创#
拿到板子之后就上手测试了cdc的速度,device端使用的手头另外的ic
打开工程”**\project\at_start_f405\examples\usb_host\cdc_demo“
修改keil中的define,将USB_OTG_HS改为USB_OTG_FS

烧写并运行代码,使用usb抓包工具抓包如图,可以看出每帧发送的数据量在240-360 byte左右,换算成秒即为240k-360k byte之间

但是我需要使用freertos,将usb作为其中的一个任务,那么此时就会产生要在while(1)的循环中增加vTaskDelay()用以任务调度
while(1)
    {
    vTaskDelay(1);
    usbh_loop_handler(&otg_core_struct.host);
    /* if press user key, host send data to device */
    if(at32_button_press() == USER_BUTTON)
    {
      cdc_start_transmission(&otg_core_struct.host, (uint8_t *)tx_data, 60);
      cdc_start_reception(&otg_core_struct.host, (uint8_t *)rx_data, 64);
    }
    }那么在这种情况下速度就会降低至一帧64-128 byte

尝试将
cdc_start_transmission(&otg_core_struct.host, (uint8_t *)tx_data, 60);两句中的数据量增加,并没有什么作用,追踪代码发现是由于实际调用的发送函数会将一次传输的数量限制在 ”out_endpoint_size“之内
进一步追踪发现out_endpoint_size即为设备端端点长度64
而调用 cdc_process_transmission() 的正是上文中的 usbh_loop_handler() 函数。至此,在freertos下速度上限被卡在了任务调度周期上。
查看F405的用户手册,发现有FIFO空中断可以使用,且此中断收寄存器 PTXFEMPLVL 的控制,可以选择半空还是全空中断

围绕半空中段,最终改造demo程序,提升传输速度
一、开启半空中断
usbh_core.c

在函数 void usbh_resume(usbh_core_type *uhost) 中增加 USB_OTG_NPTXFEMP_INT 语句
/* set global interrut mask */
usbx->gintmsk = USB_OTG_SOF_INT| USB_OTG_NPTXFEMP_INT |
                         USB_OTG_USBSUSP_INT | USB_OTG_PRT_INT |
                         USB_OTG_HCH_INT | USB_OTG_INCOMISOIN_INT |
                         USB_OTG_INCOMPIP_INCOMPISOOUT_INT | USB_OTG_WKUP_INT |
                         USB_OTG_DISCON_INT;二、添加半空中断传输入口
usbh_int.cvoid usbh_irq_handler(otg_core_type *otgdev)
{
otg_global_type *usbx = otgdev->usb_reg;
usbh_core_type *uhost = &otgdev->host;
uint32_t intsts = usb_global_get_all_interrupt(usbx);

if(usbx->gintsts_bit.curmode == 1)
{
    if(intsts & USB_OTG_HCH_FLAG)
    {
      usbh_hch_handler(uhost);
      usb_global_clear_interrupt(usbx, USB_OTG_HCH_FLAG);
    }
    if(intsts & USB_OTG_SOF_FLAG)
    {
      usbh_sof_handler(uhost);
      usb_global_clear_interrupt(usbx, USB_OTG_SOF_FLAG);
    }
    if(intsts & USB_OTG_NPTXFEMP_FLAG)
    {
      extern void usbh_txempty_handler(usbh_core_type *uhost);
      usbh_txempty_handler(uhost);
      usb_global_clear_interrupt(usbx, USB_OTG_NPTXFEMP_FLAG);
    }
三、修改传输和中断函数
usbh_cdc_class.c

添加中断函数
/**
* @brief usb host tx empty handler
* @param uhost: to the structure of usbh_core_type
* @retval none
*/
void usbh_txempty_handler(usbh_core_type *uhost)
{
    if(uhost->global_state != USBH_CLASS) {
      uhost->usb_reg->gintmsk_bit.nptxfempmsk = 0;
      return;
    }
    cdc_process_transmission(uhost);
}
修改发送处理函数
/**
* @briefusb host cdc class process transmission handler
* @paramuhost: to the structure of usbh_core_type
* @retval status: usb_sts_type status
*/
static void cdc_process_transmission(usbh_core_type *uhost)
{
usbh_core_type *puhost = (usbh_core_type *)uhost;
usbh_cdc_type *pcdc = (usbh_cdc_type *)puhost->class_handler->pdata;

switch(pcdc->data_tx_state)
{
    case CDC_SEND_DATA:
      if(pcdc->tx_len > pcdc->data_interface.out_endpoint_size)
      {
      usbh_bulk_send(puhost, pcdc->data_interface.out_channel, (uint8_t*)pcdc->tx_data, pcdc->data_interface.out_endpoint_size);
      }
      else
      {
      usbh_bulk_send(puhost, pcdc->data_interface.out_channel, (uint8_t*)pcdc->tx_data, pcdc->tx_len);
      }
      pcdc->data_tx_state = CDC_SEND_DATA_WAIT;
    break;
   
    case CDC_SEND_DATA_WAIT:
      if(uhost->urb_state == URB_DONE)
      {
      if(pcdc->tx_len > pcdc->data_interface.out_endpoint_size)
      {
          pcdc->tx_len -= pcdc->data_interface.out_endpoint_size;
          pcdc->tx_data += pcdc->data_interface.out_endpoint_size;
          pcdc->data_tx_state = CDC_SEND_DATA;
      }
      else if(pcdc->tx_len == pcdc->data_interface.out_endpoint_size)// cdc acm mode An empty packet must be sent to indicate that the transmission is complete
      {
            pcdc->tx_len = 0;
            pcdc->data_tx_state = CDC_SEND_DATA;
      }
      else
      {
          pcdc->tx_len = 0;
          pcdc->data_tx_state = CDC_IDLE;
          puhost->usb_reg->gintmsk_bit.nptxfempmsk = 0;
          cdc_transmit_complete(uhost);
      }
      }
      else if( uhost->urb_state == URB_NOTREADY)
      {
      pcdc->data_tx_state = CDC_SEND_DATA;
      }
    break;
   
    default:
    break;
}
}
修改发送开始函数
/**
* @briefusb host cdc class start transmission handler
* @paramuhost: to the structure of usbh_core_type
* @paramdata: tx data pointer
* @paramlen: tx data len
* @retval status: usb_sts_type status
*/
void cdc_start_transmission(usbh_core_type *uhost, uint8_t *data, uint32_t len)
{
usbh_core_type *puhost = (usbh_core_type *)uhost;
usbh_cdc_type *pcdc = (usbh_cdc_type *)puhost->class_handler->pdata;
if(pcdc->data_tx_state == CDC_IDLE)
{
    pcdc->data_tx_state = CDC_SEND_DATA;
    pcdc->state = CDC_TRANSFER_DATA;
    pcdc->tx_data = data;
    pcdc->tx_len = len;
    puhost->usb_reg->gintmsk_bit.nptxfempmsk = 1;
}
}
修改接收处理函数
uint16_t in_fifo_size = USBH_RX_FIFO_SIZE * 4;
/**
* @briefusb host cdc class process reception handler
* @paramuhost: to the structure of usbh_core_type
* @retval status: usb_sts_type status
*/
static void cdc_process_reception(usbh_core_type *uhost)
{
usbh_core_type *puhost = (usbh_core_type *)uhost;
usbh_cdc_type *pcdc = (usbh_cdc_type *)puhost->class_handler->pdata;
uint32_t len = 0;
   
switch(pcdc->data_rx_state)
{
    case CDC_RECEIVE_DATA:
      if(pcdc->rx_len > in_fifo_size)
          usbh_bulk_recv(puhost, pcdc->data_interface.in_channel, (uint8_t *)pcdc->rx_data, in_fifo_size);
      else
          usbh_bulk_recv(puhost, pcdc->data_interface.in_channel, (uint8_t *)pcdc->rx_data, pcdc->rx_len);
      pcdc->data_rx_state = CDC_RECEIVE_DATA_WAIT;
    break;
   
    case CDC_RECEIVE_DATA_WAIT:
      if(uhost->urb_state == URB_DONE)
      {
      len = uhost->hch.trans_count;
      if(pcdc->rx_len > len)
      {
          pcdc->rx_len -= len;
          pcdc->rx_data += len;
          pcdc->data_rx_state = CDC_RECEIVE_DATA;
      }
      else
      {
          pcdc->rx_len = 0;
          pcdc->data_rx_state = CDC_IDLE;
          cdc_receive_complete(uhost);
         
      }
      }

    break;
   
    default:
    break;
}
}
四、修改传输完成回调函数,添加usb任务和速度测试任务
TaskHandle_t usbh_handler, usbh_speed_print_handler;
otg_core_type otg_core_struct;
void usb_clock48m_select(usb_clk48_s clk_s);
#define CDC_SIZE 2048
uint8_t tx_data = {0};
uint8_t rx_data;
uint8_ttest = 0, cmplt = 0;
uint32_t speed = 0;

void usbh_speed_print_handle(void *pvParameters)
{
    TickType_t xLastWakeTime = xTaskGetTickCount ();
    while(1)
    {
      xTaskDelayUntil( &xLastWakeTime, 1000);
      if(1 == test)
            printf("reception speed:%d bytes/s\r\n", speed);
      else if(2 == test)
            printf("transmission speed:%d bytes/s\r\n", speed);
      speed = 0;
    }
}
void usbh_speed_test_handle(void *pvParameters)
{
    nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);
    /* enable otg clock */
    crm_periph_clock_enable(OTG_CLOCK, TRUE);

    /* select usb 48m clcok source */
    usb_clock48m_select(USB_CLK_HEXT);

    /* enable otg irq */
    nvic_irq_enable(OTG_IRQ, 0, 0);

    /* init usb */
    usbh_init(&otg_core_struct,
                USB_SPEED_CORE_ID,
                USB_ID,
                &uhost_cdc_class_handler,
                &usbh_user_handle);
    for(uint16_t i = 0; i < CDC_SIZE; i++)
      tx_data = i/64;
    while(1)
    {
      vTaskDelay(1);
      usbh_loop_handler(&otg_core_struct.host);
      if(cmplt) {
            cmplt = 0;
            if(1 == test)
                cdc_start_reception(&otg_core_struct.host, (uint8_t *)rx_data, CDC_SIZE);
            if(2 == test)
                cdc_start_transmission(&otg_core_struct.host, (uint8_t *)tx_data, CDC_SIZE);
      }
      
      /* if press user key, host send data to device */
      if(at32_button_state() != RESET)
      {
            if(0 == test){
                test = 1;
                cdc_start_reception(&otg_core_struct.host, (uint8_t *)rx_data, CDC_SIZE);
            } else if(1 == test){
                test = 2;
                cdc_start_transmission(&otg_core_struct.host, (uint8_t *)tx_data, CDC_SIZE);
            }
            else{
                test = 0;
            }
            while(at32_button_state() != RESET)
                vTaskDelay(1);
      }
    }
}

/**
* @briefusb host cdc class transmit complete
* @paramuhost: to the structure of usbh_core_type
* @retval status: usb_sts_type status
*/
void cdc_transmit_complete(usbh_core_type *uhost)
{
    speed += CDC_SIZE;
    cmplt = 1;
}

/**
* @briefusb host cdc class reception complete
* @paramuhost: to the structure of usbh_core_type
* @retval status: usb_sts_type status
*/
void cdc_receive_complete(usbh_core_type *uhost)
{
    speed += CDC_SIZE;
    cmplt = 1;
}

void usbh_test_init(void)
{
    /* creat usb host cdc speed test task*/
    if (xTaskCreate((TaskFunction_t)usbh_speed_test_handle,
                  (const char *)"usbh_speed_test_task",
                  (uint16_t)1024,
                  (void *)NULL,
                  (UBaseType_t)2,
                  (TaskHandle_t *)&usbh_handler) != pdPASS){
      printf("create usbh_speed_test_task failed!\r\n");
    } else {
      printf("create usbh_speed_test_task success!\r\n");
    }
    if (xTaskCreate((TaskFunction_t)usbh_speed_print_handle,
                  (const char *)"usbh_speed_print_task",
                  (uint16_t)512,
                  (void *)NULL,
                  (UBaseType_t)3,
                  (TaskHandle_t *)&usbh_speed_print_handler) != pdPASS){
      printf("create usbh_speed_print_task failed!\r\n");
    } else {
      printf("create usbh_speed_print_task success!\r\n");
    }
}
五、测试结果
由于只针对发送做了半空中断处理,接收功能只做了增加单次传输的限制更改,导致接收速度没有达到较大的提升
测试结果如下



可以看到目前发送速度已经达到1Mbyte/s左右
工程


页: [1]
查看完整版本: 【AT-START-F405测评】USB_Host cdc在freertos下速度测试及优化