这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » 手势翻页笔第四篇-结果帖

共7条 1/1 1 跳转至

手势翻页笔第四篇-结果帖

菜鸟
2024-06-15 17:23:12     打赏

结果帖

论坛里由很多大佬们都添加成功的添加HID,本文就不赘述了,就节选个人认为重要的点给大家分享一下我的做法和过程

1、Cube MX HID配置

image.png

必须勾选以上两个,不然会缺少相关的代码文件。

image.png

以上需要配置为USB

image.png

电流设置为0ma,相关的内存分配都是16k 8k,但是实际的代码还是会编译不过,后续会详细提及。

image.png


Tread X配置相对简单,只要打开应用后,勾选上图的初始化 并 选择创建任务即可。

完成以上步骤之后直接生成代码即可,注:时钟是配置为250MHz的主频

 

2、代码调试

生成后的代码时可以以直接编译通过的,但是直接烧录之后,调试模式会发现代码连while 1都进不去,就卡死再某个地方了,而且没有触发hard fault,可以说是非常绝望啊,必须要一行一行的仔细查看代码了,最简单的调试方法就是静茹仿真模式,不断的打断点,开代码,理解代码。

再经过不断的调试后发现,代码时运行到初始化创建USBx相关的代码后卡死的,以下时发现流程:

 

main.C可以看见多了一个 “MX_ThreadX_Init”的函数,跳转过去文件是app_threadx.c,可以看见有一个创建任务的初始化函数(下文),所以可以猜测相对应的 USB X也会由一个,并且他们应该在同一个地方被调用。

以下创建名为 tx_app_thread 的任务的代码。

UINT App_ThreadX_Init(VOID *memory_ptr)
{
  UINT ret = TX_SUCCESS;
  TX_BYTE_POOL *byte_pool = (TX_BYTE_POOL*)memory_ptr;
 
  /* USER CODE BEGIN App_ThreadX_MEM_POOL */
 
  /* USER CODE END App_ThreadX_MEM_POOL */
  CHAR *pointer;
 
  /* Allocate the stack for tx app thread  */
  if (tx_byte_allocate(byte_pool, (VOID**) &pointer,
                       TX_APP_STACK_SIZE, TX_NO_WAIT) != TX_SUCCESS)
  {
    return TX_POOL_ERROR;
  }
  /* Create tx app thread.  */
  if (tx_thread_create(&tx_app_thread, "tx app thread", tx_app_thread_entry, 0, pointer,
                       TX_APP_STACK_SIZE, TX_APP_THREAD_PRIO, TX_APP_THREAD_PREEMPTION_THRESHOLD,
                       TX_APP_THREAD_TIME_SLICE, TX_APP_THREAD_AUTO_START) != TX_SUCCESS)
  {
    return TX_THREAD_ERROR;
  }
 
  /* USER CODE BEGIN App_ThreadX_Init */
  /* USER CODE END App_ThreadX_Init */
 
  return ret;
}

于是全文搜索 App_ThreadX_Init,可以发现只有一处调用,其他都是注释,跳转后能看见该函数,调用了App_ThreadX_Init MX_USBX_Device_Init。

并且tx_application_define是由最开始的MX_ThreadX_Init函数调用的,所以这就是我们想找的地方。

VOID tx_application_define(VOID *first_unused_memory)
{
  /* USER CODE BEGIN  tx_application_define_1*/
 
  /* USER CODE END  tx_application_define_1 */
#if (USE_STATIC_ALLOCATION == 1)
  UINT status = TX_SUCCESS;
  VOID *memory_ptr;
 
  if (tx_byte_pool_create(&tx_app_byte_pool, "Tx App memory pool", tx_byte_pool_buffer, TX_APP_MEM_POOL_SIZE) != TX_SUCCESS)
  {
    /* USER CODE BEGIN TX_Byte_Pool_Error */
 
    /* USER CODE END TX_Byte_Pool_Error */
  }
  else
  {
    /* USER CODE BEGIN TX_Byte_Pool_Success */
 
    /* USER CODE END TX_Byte_Pool_Success */
 
    memory_ptr = (VOID *)&tx_app_byte_pool;
    status = App_ThreadX_Init(memory_ptr);
    if (status != TX_SUCCESS)
    {
      /* USER CODE BEGIN  App_ThreadX_Init_Error */
      while(1)
      {
      }
      /* USER CODE END  App_ThreadX_Init_Error */
    }
    /* USER CODE BEGIN  App_ThreadX_Init_Success */
 
    /* USER CODE END  App_ThreadX_Init_Success */
 
  }
    if (status != TX_SUCCESS)
    {
      /* USER CODE BEGIN  App_ThreadX_Init_Error */
      while(1)
      {
      }
      /* USER CODE END  App_ThreadX_Init_Error */
    }
    /* USER CODE BEGIN  App_ThreadX_Init_Success */
 
    /* USER CODE END  App_ThreadX_Init_Success */
 
  }
  if (tx_byte_pool_create(&ux_device_app_byte_pool, "Ux App memory pool", ux_device_byte_pool_buffer, UX_DEVICE_APP_MEM_POOL_SIZE) != TX_SUCCESS)
  {
    /* USER CODE BEGIN UX_Device_Byte_Pool_Error */
 
    /* USER CODE END UX_Device_Byte_Pool_Error */
  }
  else
  {
    /* USER CODE BEGIN UX_Device_Byte_Pool_Success */
 
    /* USER CODE END UX_Device_Byte_Pool_Success */
 
    memory_ptr = (VOID *)&ux_device_app_byte_pool;
    status = MX_USBX_Device_Init(memory_ptr);
    if (status != UX_SUCCESS)
    {
      /* USER CODE BEGIN  MX_USBX_Device_Init_Error */
      while(1)
      {
      }
      /* USER CODE END  MX_USBX_Device_Init_Error */
    }
    /* USER CODE BEGIN  MX_USBX_Device_Init_Success */
 
    /* USER CODE END  MX_USBX_Device_Init_Success */
  }

而且能看见

memory_ptr = (VOID *)&tx_app_byte_pool;
memory_ptr = (VOID *)&ux_device_app_byte_pool;

这两行像是申请空间大小的赋值。所以直接开始实验:

直接看现象:在代码在烧录后连While 1 里的灯都不会翻转了(后面发现了使用Tread X后不在mainWhile 1里面循环了,但是当时不清楚)。

调试模式:打断点可以发现代码是可以运行到

memory_ptr = (VOID *)&ux_device_app_byte_pool;

这一行之后就卡死了,所以直接屏蔽后,代码是可以跑起来的,判断依据是断点可以进入到,我们创建的任务里面了,即tx_app_thread_entry里。

所以将USB X的空间给大就可以了,几重跳转之后可以发现,UX_DEVICE_CLASS_HID_MAX_EVENTS_QUEUE 着宏是8,改为16之后代码就不会卡死了。

image.png

紧接着在app_ux_device_thread_entry这个函数中添加初始化的代码,就可以正确的被电脑识别为HID设备了。

并且发现这个函数有且只会在 MX_USBX_Device_Init 中调用,所以必须在复位 或 开发板上电前,先将USB口接到电脑后再上电,不然不会识别设备。

 

3、实验

再修改一部分代码确定是插入了我的设备,所以我小改了一下制造商的名字,如下win 10上可以完美的显示我们的修改!

image.png

以上就完成了HID键盘的初始化。下面会结合手势传感器,实现手势翻页笔的功能。

3.1、键值发送

在上面得知创建的任务会不断的进入tx_app_thread_entry这个函数中,于是有了以下代码:

void tx_app_thread_entry(ULONG thread_input)
{
  /* USER CODE BEGIN tx_app_thread_entry */
    uint16_t paj7620u2_state=0;
    uint8_t  win_state=0;
    uint8_t  key=0;
    
      while(1)  
      {  
        tx_thread_sleep(300);  
 
        HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);  
        get_paj7620u2_state();
        HID_keyboard_send(0x04);  
      }  
  /* USER CODE END tx_app_thread_entry */
}

以上的代码实现每间隔300ms翻转一次IO电平闪灯,获取一次手势,并且发送“a”给电脑,0x04就是a”。

键盘发送函数封装

void  HID_keyboard_send(uint8_t win_en){
    send_keyboard_win_data(&hid_event,win_en);
    ux_device_class_hid_event_set(hid_keyboard,&hid_event);
    clear_keyboard_data(&hid_event);              
    ux_device_class_hid_event_set(hid_keyboard,&hid_event);           
}
 
 
void send_keyboard_win_data(UX_SLAVE_CLASS_HID_EVENT *param,uint8_t key_value){   
    memset(keyboard_value,0,sizeof(keyboard_value));
    
    param->ux_device_class_hid_event_length = 8; 
    keyboard_value[2] = key_value;        
    memcpy((uint8_t*)¶m->ux_device_class_hid_event_buffer[0],(uint8_t*)&keyboard_value[0],sizeof(keyboard_value));
}

以上的代码实现“脑残化”填键值就可以发。

后面要实现翻页笔就需要将a”替换为左、右箭头,所以要确认对应的键值。

image.png

由上图可以确认,上下左右按键对应的键值了,所以对应手势发送键值即可实现翻页笔了!

所以进一步的将应用代码改为,获取传感器的返回值后直接,发送出去

void tx_app_thread_entry(ULONG thread_input)
{
  /* USER CODE BEGIN tx_app_thread_entry */
    uint16_t paj7620u2_state=0;
    
      while(1)  
      {  
            tx_thread_sleep(300);  
 
            HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);  
            paj7620u2_state=get_paj7620u2_state();               
            HID_keyboard_send(paj7620u2_state);  
      }  
  /* USER CODE END tx_app_thread_entry */
}

原本的传感器驱动肯定不会返回键值的,所以还要修改传感器的返回值。(PS:目前先不考虑应用分层,先跑着,所有先“恶心”一下下)

uint16_t get_paj7620u2_state ( void )
{
    uint16_t  Gesture_Data;
    Gesture_Data = DEV_I2C_ReadWord( PAJ_INT_FLAG1 );
    if ( Gesture_Data ) {
        switch ( Gesture_Data ) {
            case PAJ_UP:
                LOG( "Up " );
                return 0x0052;
                break;
            
            case PAJ_DOWN:
                LOG( "Down " );
                return 0x0051;
                break;
            
            case PAJ_LEFT:
                LOG( "Left " );
                return 0x0050;
                break;
            
            case PAJ_RIGHT:
                LOG( "Right " );
                return 0x004f;
                break;
            
            case PAJ_FORWARD:
                LOG( "Forward " );
                return 0x0000;
                break;
            
            case PAJ_BACKWARD:
                LOG( "Backward " );
                return 0x0000;
                break;
            
            case PAJ_CLOCKWISE:
                LOG( "Clockwise " );
                return 0x0000;
                break;
            
            case PAJ_COUNT_CLOCKWISE:
                LOG( "AntiClockwise " );
                return 0x0000;
                break;
            
            case PAJ_WAVE:
                LOG( "Wave " );
                return 0x0000;
                break;
            
            default:                
                return 0x0000;
                break;
        }
        Gesture_Data = 0;
    }
}

直接使用return 值来传参即,返回的值就是我在该手势下想要发送的键值。比如识别到向上的手势我就发送箭头向上的键值。


实验效果:

221212.gif












关键词: 手势     ThreadX    

菜鸟
2024-06-15 17:46:48     打赏
2楼

谢谢分享


专家
2024-06-15 17:51:59     打赏
3楼

思路很清晰,感谢分享


专家
2024-06-15 18:33:16     打赏
4楼

感谢分享


高工
2024-06-16 15:04:19     打赏
5楼

谢谢分享


院士
2024-06-18 10:26:21     打赏
6楼

就是这个样子的

赞一下


专家
2024-06-21 08:30:57     打赏
7楼

谢谢分享


共7条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]