首页 新闻 会员 周边 捐助

杰理AC6951C重启USB功能后无法正常使用

0
悬赏园豆:200 [已解决问题] 解决于 2023-09-18 23:41

楼主想用该MCU做一个支持PS3和PC的手柄,是两个不同的USB主机平台,所以USB设备描述符上用的也是不同的。根据杰理官方的demo所给的例程,与描述符相匹配的端点配置,是在配置并启用后才向USB主机发送描述符(这点我认为没什么问题),但这样需要整个 USBstack 都 free 了以后重启 USBstack 才能够完成切换另一套设备所用的USB描述符与其相应的端点配置。现在问题来了,当我重启后它短时间内还是正常的,端点也完成了相应的配置,但是在获取设备描述符的时候,由于USB主机第一次并不清楚从机是个什么设备,会获取两次USB设备描述符,当获取第二次设备描述符的时候,MCU就莫名其妙的重启了,无论怎么试都是卡在这个地方,去杰理的钉钉开源群问官方工作人员,官方工作人员也没有给出任何的回复,故此来园子向诸位求救了🙏

https://github.com/PING020903/AC695N-demo-GamePad/
这是我上传到GitHub的源码,各路大神可自取试验

让USBstack重启用的是同样的函数,但是由USB主机多次发起复位信号后我控制从机重启USBstack能正常使用,USB从机识别USB主机发送索要描述符的命令重启就会有以上我所描述的问题

该代码我放置在 app_dongle.c 中

static void app_start()
{
    log_info("=======================================");
    log_info("--------------dongle demo--------------");
    log_info("=======================================");

    /* clock_idle(BT_IDLE_CLOCK); */
    //u32 sys_clk =  clk_get("sys");
    //bt_pll_para(TCFG_CLOCK_OSC_HZ, sys_clk, 0, 0);
    /* audio_dev_init(); */
    //test_function();

#if 0
    log_info("App Start - USB");
    hci_interface_init();
#else
    //log_info("App Start - HCI");
    //btstack_demo_init();
    //usbstack_init();
    //usb_start();

#if TCFG_PC_ENABLE
    //otg mode
    usbstack_init();
    //no otg mode
    usb_start();
#endif
    /* 按键消息使能 */
    //sys_key_event_enable();
    /* hci_interface_init(); */
#endif

/* 最好不要修改函数的位置, 可能会无法正常运行 */
/*********************** user task **************************/
    my_task_init();
/************************************************************/
#if 1
    extern unsigned char reques_list[2][8];
    extern u8 usb_connect_timeout_flag;
    extern unsigned char timer_send_flag;
    if (reques_list[0][6] == 0x40)
    {
        usb_stop();     // 停止
        usbstack_exit();// 退出
        //usb_iomode(0);  // 關閉
        printf("turn off USB stack");
        usb_connect_timeout_flag = 1;

        

        //usb_iomode(1);
        usb_io_reset(0);    // 該API會將所有配置好的USB配置都清空並復位
        usbstack_init();
        //usb_iomode(1);
        usb_start();

#if 0
        gpio_set_direction(IO_PORT_DP, 0);  // 設置輸出
        gpio_set_direction(IO_PORT_DM, 0);
        gpio_set_pull_up(IO_PORT_DP, 0);    // 非上拉
        gpio_set_pull_up(IO_PORT_DM, 0);
        gpio_set_pull_down(IO_PORT_DP, 1);  // 下拉
        gpio_set_pull_down(IO_PORT_DM, 1);
        gpio_write(IO_PORT_DP, 1);          // 輸出1
        gpio_write(IO_PORT_DM, 1);
        delay(250);
#endif // 無需軟件模擬USB拔出, 模擬了反而連配置PC_Xbox360的描述符這一階段都未到

        
    }
#endif // 目前描述符配置切換后, 在獲取第二次設備描述符的時候一直重啓

    
}
问题补充:

这一段是我使用该demo的FreeRTOS创建超时定时器去执行重启 USBstack,结果依旧一样,始终在发送了第二次设备描述符后MCU重启
该代码放置在 my_put_test.c

extern void usbstack_init();
extern void usbstack_exit();
extern void usb_stop();
extern void usb_start();
extern unsigned char reques_list[2][8];
extern u8 usb_connect_timeout_flag;    // 描述符選擇, 默認PS3平臺
extern u8 usb_reset_count;
static void* USB_connect_timeout_task(void* p_arg)
{
    int ret;
    printf("in the timeout task");
    if (reques_list[0][6] == 0x40)
    {
        printf(" RUN USB release ! ! !");
        usb_reset_count = 0;

        usb_stop();     // 停止
        usbstack_exit();// 退出
        /*我都唔知道此處是否有用*/
        usb_iomode(0);  // 關閉
        printf("turn off USB PHY");
        //os_time_dly(100);
        gpio_set_direction(IO_PORT_DP, 0);  // 設置輸出
        gpio_set_direction(IO_PORT_DM, 0);
        gpio_set_pull_up(IO_PORT_DP, 0);    // 非上拉
        gpio_set_pull_up(IO_PORT_DM, 0);
        gpio_set_pull_down(IO_PORT_DP, 1);  // 下拉
        gpio_set_pull_down(IO_PORT_DM, 1);
        gpio_write(IO_PORT_DP, 1);          // 輸出1
        gpio_write(IO_PORT_DM, 1);
        delay(250);

        /************************/

        usb_connect_timeout_flag = 1;   // 切換平臺所用的描述符
        printf("file:%s, line:%d", __FILE__, __LINE__);




        usbstack_init();
        usb_iomode(1);
        usb_start();
    }

    return &ret;
}
void my_task_init(void)
{
    printf("__________ %s __________, %x\n", __func__, JL_USB->CON0);
    int err;
    extern void* USB_connect_timeout_task(void* p_arg);
    //usr_timeout_add(NULL, USB_connect_timeout_task, 300, 1);    // OS 2s不到就復位了
    // gpio_direction_output(IO_PORTA_03, 1);//set this IO output

    my_button_init();
    my_PWM_output_init();
    gpio_direction_output(IO_PORTA_03, 0);
    gpio_direction_output(IO_PORTA_01, 0);
    /* PC */
    data_send_to_host[1] = 0x14;    // 长度是固定的
    buf_point = records_keys_point; // 指向要被写入VM的数据


    printf(" %x %x %x %x %x %x %x %x", reques_list[0][0], reques_list[0][1], reques_list[0][2], reques_list[0][3], reques_list[0][4], reques_list[0][5], reques_list[0][6], reques_list[0][7]);
    printf(" %x %x %x %x %x %x %x %x", reques_list[1][0], reques_list[1][1], reques_list[1][2], reques_list[1][3], reques_list[1][4], reques_list[1][5], reques_list[1][6], reques_list[1][7]);
    if (reques_list[0][6] == 0x40)
    {
        //usb_stop();     // 停止
        //usbstack_exit();// 退出
        //usb_iomode(0);  // 關閉
        //printf("turn off USB PHY");
        //usbstack_init();
        //usb_iomode(1);
        //usb_start();
    }

    /* PS3 */
    ps3_data_send_to_host[0] = 0x01;
    ps3_data_send_to_host[29] = 0x03;
    ps3_data_send_to_host[31] = 0x14;
    ps3_data_send_to_host[32] = 0x00;
    ps3_data_send_to_host[33] = 0x00;
    ps3_data_send_to_host[34] = 0x00;
    ps3_data_send_to_host[35] = 0x00;
    ps3_data_send_to_host[36] = 0x23;
    ps3_data_send_to_host[37] = 0x6d;
    ps3_data_send_to_host[38] = 0x77;
    ps3_data_send_to_host[39] = 0x01;
    ps3_data_send_to_host[40] = 0x80;
    ps3_data_send_to_host[45] = 0x01;
    ps3_data_send_to_host[47] = 0x02;
    ps3_data_send_to_host[48] = 0x00;


#if SUCCESSIVE_PRESS
    float temp = frequency / 2.0;
    frequency = temp;
#endif

#if VM_TEST

    for(int i = 0; i < NUM_FOR_VM_LEN; i++) // clear read VM buffer
        *((unsigned int*)buf_point + i) = 0;

    //int ret_w = syscfg_write(1, buf_point, NUM_FOR_VM_LEN * 4);
    //printf("VM write ret: %d", ret_w);


    // 除非重新烧写, 否则即使掉电数据仍然存储在VM区域
    int ret_r = syscfg_read(1, buf_point, (NUM_FOR_VM_LEN + LEN_RECORD_VM) * 4);
    printf(" VM READ RET: %d, WDT addr: %x", ret_r, p33_rx_1byte(P3_WDT_CON));
    if (ret_r < 0)
        printf(" NO RECORD ! ! !");
    records_length_temp = *(records_keys_point + MAX_RECORD_ARRAY_LEN); // 将从VM读取的数据, 保存当前按键编程记录的长度
    for (int i = 0; i < records_length_temp; i++)                       // 将时间记录赋值
        reappear_times_temp[i] = (*((unsigned short*)records_keys_point + 1 + (SHORT_SIZE_NEXT * i)));
#endif

#if RECORD_MOVEMENT
#if MY_LIST
    /* 初始化头节点, init node head */
    key_info_head.keys_data[0] = 0;
    key_info_head.keys_data[1] = 0;
    key_info_head.record_times = 0;
    my_node_init(key_info_head);
#endif
    unsigned int ep_temp = JL_USB->EP0_ADR;
    printf("[INFO] JL_USB->EP0_ADR: %X", ep_temp);


#endif

#if THREAD_CREATE
    /* create my task, remember do not set stack-size(stksize) too short. if send task queue ,do not set the queue-size(qsize) is zero */
    err = os_task_create(my_task, NULL, 1, 4096, 32, MY_TASK_NAME);
    log_print(__LOG_INFO, NULL, "create my task ! ! !    ret = %d\n", err);

#if MAIN_TIMER
    /* create user timer */
    ret_id_timer = sys_hi_timer_add(NULL, my_timer_task, MAIN_TCC_TIMER);
    log_print(__LOG_INFO, NULL, "Main timer task ID : %d\n", ret_id_timer);
#endif

#if PWM_TIEMR
    /*同一定时器中最好不要连续发送两次任务消息, 发送任务消息的时间要错开, 否则会因为发送了任务消息, 而任务收到后尚未处理完成, 又收到任务导致队列溢出*/
    /*定时器发送事件时间内未完成任务会导致任务队列溢出*/
    /* Failure to complete a task within the timer send event time causes the task queue to overflow. */
    ret_id_timer_led = sys_hi_timer_add(NULL, led_timer_task, LED_TCC_TIMER);
    log_print(__LOG_INFO, NULL, "LED timer task ID : %d\n", ret_id_timer_led);
#endif

#if SPECIAL_FUNC_TIMER
    ret_id_timer_SpecialFunctions = sys_hi_timer_add(NULL, SpecialFunc_timer_task, SPECIAL_FUNC_TCC);
    log_print(__LOG_INFO, NULL, "Special_Functions timer task ID : %d\n", ret_id_timer_SpecialFunctions);
#endif
#endif
    /* printf pwm model info */
    // log_pwm_led_info();

#if FUNC_TIMESTAMP
    for (int i = 0; i < 10; i++)
    {
        if (i >= START_FUNC && i < (END_FUNC + 1))
            count_all_func[i] = RESET_VAL;
        else
            count_all_func[i] = 0;
    }
#endif
}

楼主也试过通过索要配置描述符的指令来判断主机类型去重启 USBstack 切换描述符,问题依旧如出一辙

#if 1
    extern unsigned char reques_list[2][8];
    extern u8 usb_connect_timeout_flag;
    extern unsigned char timer_send_flag;
    if (reques_list[1][6] == 0xff && reques_list[1][3] == 0x02)
    {
        usb_stop();     // 停止
        usbstack_exit();// 退出
        //usb_iomode(0);  // 關閉
        printf("turn off USB stack");
        usb_connect_timeout_flag = 1;

        

        //usb_iomode(1);
        usb_io_reset(0);    // 該API會將所有配置好的USB配置都清空並復位
        usbstack_init();
        //usb_iomode(1);
        usb_start();

#if 0
        gpio_set_direction(IO_PORT_DP, 0);  // 設置輸出
        gpio_set_direction(IO_PORT_DM, 0);
        gpio_set_pull_up(IO_PORT_DP, 0);    // 非上拉
        gpio_set_pull_up(IO_PORT_DM, 0);
        gpio_set_pull_down(IO_PORT_DP, 1);  // 下拉
        gpio_set_pull_down(IO_PORT_DM, 1);
        gpio_write(IO_PORT_DP, 1);          // 輸出1
        gpio_write(IO_PORT_DM, 1);
        delay(250);
#endif // 無需軟件模擬USB拔出, 模擬了反而連配置PC_Xbox360的描述符這一階段都未到
我是只平平的主页 我是只平平 | 菜鸟二级 | 园豆:226
提问于:2023-09-18 10:27
< >
分享
最佳答案
0

根据你提供的信息,问题似乎与 USB 描述符切换和 USB 栈的重启有关,可能存在一些同步问题。为了解决这个问题,你可以考虑以下几点:

延迟 USB 栈的重启:在切换 USB 描述符后,尝试延迟 USB 栈的重启。这样可以确保 USB 主机已经完成了描述符的切换操作,然后再重启 USB 栈。
c

usb_connect_timeout_flag = 1;

// 延迟一段时间,以确保USB主机已经完成描述符切换
delay(1000); // 1秒延迟,根据实际情况调整

usb_io_reset(0); // 重置USB IO
usbstack_init(); // 初始化USB栈
usb_start(); // 启动USB
检查 USB 主机的请求:在获取设备描述符之前,可以检查 USB 主机的请求。如果请求不是获取设备描述符的操作,可以不执行 USB 栈的重启。
c

if (usb_reset_count == 0 && (reques_list[1][6] == 0xff) && (reques_list[1][3] == 0x02))
{
// 延迟一段时间,以确保USB主机已经完成描述符切换
delay(1000); // 1秒延迟,根据实际情况调整

usb_stop();     // 停止USB
usbstack_exit(); // 退出USB栈
usb_io_reset(0); // 重置USB IO
usbstack_init();  // 初始化USB栈
usb_start();      // 启动USB

}
增加日志记录:在你的代码中增加详细的日志记录,以便在问题发生时能够更容易地排查问题所在。记录关键的变量值和操作,以便在调试时更容易理解程序的执行流程。

检查 USB 描述符的配置:确保 USB 描述符的配置是正确的,包括端点和其他参数。任何配置错误都可能导致问题。

考虑使用更稳定的 USB 栈库:杰理的 USB 栈库可能存在一些问题,尤其是在复杂的应用中。你可以考虑使用其他稳定的 USB 栈库,如STM32 USB库(针对STM32微控制器)或者LibUSB等。

通过以上方法,你可以尝试解决 USB 描述符切换和 USB 栈重启时可能出现的问题。请确保在每个操作之间添加足够的延迟以确保 USB 主机和设备之间的同步。如果问题仍然存在,可能需要更深入地调试和分析 USB 栈的行为以找出根本原因。

收获园豆:200
Technologyforgood | 大侠五级 |园豆:7535 | 2023-09-18 23:13

你说得对,我也觉得杰理的USB栈有点问题,但是他们把这部分的代码给封锁住了,目前我也还不会如何移植USB栈到杰理的MCU上
我尝试每隔10秒就把两个不同设备的配置描述符切换一次,确保不会出现短时间切换USB配置引发MCU复位,我端点是初始化成功的,但是切换到PS3设备描述符的时候,我用busHound查看确实也枚举成功了,但就是没有输入,但是切换到PC_Xbox360的配置输入一切正常。
由于我只能控制的只有描述符与输入输出的处理,对于杰理的USB栈的源码,我是一点也看不了,造成debug困难的窘境,而且还会因为执行速度快导致打印跟不上导致MCU复位的情况。AC695N用户手册上,对于MCU的USB功能,更是只有对USB状态寄存器bit位的介绍,更多的描述真的是一点都没有

我是只平平 | 园豆:226 (菜鸟二级) | 2023-09-18 23:39

@我是只平平: 最後,樓主更改了判斷主機平臺的方式,由判斷設備描述符更改為判斷兩種設備枚舉后的通信差別。傑理的這個USB棧,只要我在其枚舉過程判斷索要設備描述符指令——就必定會導致重啓MCU重啓,準確的說在將要上傳設備描述符的時候調用了這個獲取endpoint_0發送的信息,就會導致這種情況。但我在枚舉成功后,根據通訊差別去判斷USB主機平臺,再去重啓 USBstack 就不會有這種問題。。。由於我無法得知傑理 USBstack 的結構,我不能夠尋本溯源找到原因所在,只是得到了這麽一個無厘頭的結果。

我是只平平 | 园豆:226 (菜鸟二级) | 2023-09-19 14:48
其他回答(1)
0

怕是难搞哦,杰理的技术支持不太好用。

-ssdq- | 园豆:205 (菜鸟二级) | 2023-09-18 17:22

确实,问官方工作人员没人回

支持(0) 反对(0) 我是只平平 | 园豆:226 (菜鸟二级) | 2023-09-18 17:36
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册