nrf52840usb-hid-keyboard之shift/ctrl..释放顺序

nrf52840是一款低功耗的蓝牙芯片,它支持USB HID,可以作为一个HID Keyboard进行使用。

USB HID(Human Interface Device)是一种USB设备类别,它定义了常见的人机界面设备,如键盘、鼠标、游戏手柄等。HID Keyboard是指通过USB接口连接的键盘设备,与普通的键盘相比,它可以通过程序控制模拟键盘输出。

在nrf52840上实现USB HID Keyboard的关键是使用USB Device Stack,该协议栈提供了对USB HID协议的支持。实现步骤包括:初始化USB协议栈、注册USB HID设备、注册USB endpoint以及设置和处理USB HID Report。

需要注意的是,nrf52840的USB接口只支持USB 2.0 Full Speed(12 Mbps),如果需要更高的传输速率,则需要使用蓝牙连接。

我使用Nordic 52840的usb hid keyboard实现数据输出,实际键盘上的每个按键按下和释放都会是8个字节的数据。

Byte0reserveByte2Byte3Byte4Byte5Byte6Byte7

下表描述的是特殊功能键Byte0里面的每个bit所代表的含义,也就是平常所说的modifier

bit位按键名

按下

释放Byte0值
bit0Left Control100x01
bit1Left Shift100x02
bit2Left Alt100x04
bit3Left GUI100x08
bit4Right Control100x10
bit5Right Shift100x20
bit6Right Alt100x40
bit7Right GUI100x80

Byte1保留,普通按键定义在Byte2–Byte7内

举个简单的例子:在CapsLock没有打开的情况下,需要输出字符大写A,那么该按键按下时候发出的数据结构为:

0x02 0x00 0x04 0x00 0x00 0x00 0x00 0x00(A)

按键释放的时候:

0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00(全部弹起)

这就是1个A发送时按键按下和弹起的时候主机所收到的数据。(keyboard表可以参照下面这位大佬发的)

USB协议中HID设备描述符以及键盘按键值对应编码表_usb hid键盘码表的完整列表。-CSDN博客

那么我需要实现的由两种按下和释放的形式

如:1asKW93(接收到数据字符的ASCII转换成keyboar对应的modifier和scan_key之后存放在send_data[MAX_SIZE]数组里面)

由于八个字节里面每个按键都有1个对应的Modifier和键值,我就把这个的modifier和键值存放在1个结构体里面

typedef unsigned char uint8_t;
#define MAX_SIZE 1024

typedef struct
{

  uint8_t modifier;//特殊键 byte0
  uint8_t keynum;//普通键

}single_keyboard;

//定义了1个存放由ASCII码值转换成hid_keyboard的需要发送的所有键的数组
single_keyboard send_data[MAX_SIZE];

//设置modifier的状态的函数 norid例程官方提供
ret_code_t app_usbd_hid_kbd_modifier_state_set(app_usbd_hid_kbd_t const  * p_kbd,
                                               app_usbd_hid_kbd_modifier_t modifier,
                                               bool                        state)
{
    app_usbd_hid_kbd_ctx_t * p_kbd_ctx    = hid_kbd_ctx_get(p_kbd);
    bool                     actual_state = (p_kbd_ctx->rep.modifier & modifier) != 0;

    if (actual_state == state)
    {
        /*Modifier has already the same state*/
        return NRF_SUCCESS;
    }

    app_usbd_hid_access_lock(&p_kbd_ctx->hid_ctx);

    if (state)
    {
        p_kbd_ctx->rep.modifier |= modifier;
    }
    else
    {
        p_kbd_ctx->rep.modifier &= ~modifier;
    }
    app_usbd_hid_access_unlock(&p_kbd_ctx->hid_ctx);

    if (app_usbd_hid_trans_required(&p_kbd_ctx->hid_ctx))
    {
        /*New transfer need to be triggered*/
        return hid_kbd_transfer_set(p_kbd);
    }

    return NRF_SUCCESS;
}


//发送scan_key的函数 Nordic例程官方提供
ret_code_t app_usbd_hid_kbd_key_control(app_usbd_hid_kbd_t const * p_kbd,
                                        app_usbd_hid_kbd_codes_t   key,
                                        bool                       press)
{
    app_usbd_hid_kbd_ctx_t * p_kbd_ctx   = hid_kbd_ctx_get(p_kbd);
    uint8_t                * destination = NULL;

    if (press)
    {
        for (size_t i = 0; i < ARRAY_SIZE(p_kbd_ctx->rep.key_table); ++i)
        {
            if (p_kbd_ctx->rep.key_table[i] == key)
            {
                /*Already pressed*/
                return NRF_SUCCESS;
            }

            if ((destination == NULL) && (p_kbd_ctx->rep.key_table[i] == 0))
            {
                destination = &p_kbd_ctx->rep.key_table[i];
            }
        }

        if (destination == NULL)
        {
            return NRF_ERROR_BUSY;
        }
    }
    else
    {
        /*Find if key is pressed*/
        for (size_t i = 0; i < ARRAY_SIZE(p_kbd_ctx->rep.key_table); ++i)
        {
            if (p_kbd_ctx->rep.key_table[i] == key)
            {
                destination = &p_kbd_ctx->rep.key_table[i];
                break;
            }
        }

        if (destination == NULL)
        {
            /*Key hasn't been pressed*/
            return NRF_SUCCESS;
        }
    }

    /*Save destination*/
    app_usbd_hid_access_lock(&p_kbd_ctx->hid_ctx);
    *destination = press ? key : 0;
    app_usbd_hid_access_unlock(&p_kbd_ctx->hid_ctx);

    if (app_usbd_hid_trans_required(&p_kbd_ctx->hid_ctx))
    {
        /*New transfer need to be triggered*/
        return hid_kbd_transfer_set(p_kbd);
    }

    return NRF_SUCCESS;
}

//按下时发送键值和modifier/释放时释放键值不释放modifier
ret_code_t usbd_hid_signal_key_send(app_usbd_hid_kbd_t const * p_kbd,
                                    single_keyboard            key,
                                    bool                       press)
{
   app_usbd_hid_kbd_ctx_t * p_kbd_ctx   = hid_kbd_ctx_get(p_kbd);
   memset(p_kbd_ctx->rep.key_table,0,sizeof(p_kbd_ctx->rep.key_table));

   /*Save destination*/
   app_usbd_hid_access_lock(&p_kbd_ctx->hid_ctx);

   if(press)
   {
     p_kbd_ctx->rep.key_table[0] = key.keynum;
     p_kbd_ctx->rep.modifier = key.modifier;
   }
   else
   {
     p_kbd_ctx->rep.modifier = key.modifier;//按键释放的时候保存其键值
   }
   app_usbd_hid_access_unlock(&p_kbd_ctx->hid_ctx);
   
   if (app_usbd_hid_trans_required(&p_kbd_ctx->hid_ctx))
   {
      //一般除了第一次发送,其他情况不会走这,而是走hid中断的那边
       /*New transfer need to be triggered*/
       return hid_kbd_transfer_set(p_kbd);
   }

   return NRF_SUCCESS;
}


/*方案一:无论需要实现什么字符发送,都是按下后立刻释放,如在CapsLock关闭的情况下,需要显示XY,那么按下和释放的顺序就是shift⬇x⬇shift⬆x⬆shift⬇y⬇shift⬆y⬆*/
void main()
{
   int data_length = 0;
   data_length = ascii_to_hidkbd("1asKW93",7);//data_length = 7
    do
    {
        if(可以按下按键)
        {
   app_usbd_hid_kbd_modifier_state_set(&hid_keboard_instance,send_data[i].modifier,true);
   app_usbd_hid_kbd_key_control(&hid_keboard_instance,send_data[i].keynum,true);
        }
        else if(可以释放按键)
        {
            
 app_usbd_hid_kbd_modifier_state_set(&hid_keboard_instance,send_data[i].modifier,false);
 app_usbd_hid_kbd_key_control(&hid_keboard_instance,send_data[i].keynum,false);
        }    
    }
    while(i<data_length)
}


/*方案二:如果下一个需要发送的数据modifier和当前发送的数据的modifier相同,那么这个特殊键不释放,只释放普通键,如在CapsLock关闭的情况下,需要显示XY,那么按下和释放的顺序就是shift⬇x⬇x⬆y⬇shift⬆y⬆*/
void main()
{
   int data_length = 0;
   data_length = ascii_to_hidkbd("1asKW93",7);//data_length = 7
    do
    {
        if(可以按下按键)
        {           
   app_usbd_hid_kbd_modifier_state_set(&hid_keboard_instance,send_data[i].modifier,true);
   app_usbd_hid_kbd_key_control(&hid_keboard_instance,send_data[i].keynum,true);
        }
        else if(可以释放按键)
        {
           usbd_hid_signal_key_send(&hid_keboard_instance,send_data[i],false);
        }    
    }
    while(i<data_length)
    
    /*释放最后一个键*/
app_usbd_hid_kbd_modifier_state_set(&hid_keboard_instance,send_data[i-1].modifier,false);
app_usbd_hid_kbd_key_control(&hid_keboard_instance,0x00,false);//发送1个空包
   
}

A字符:

按下时发送:

0x02 0x00 0x00 0x00 0x00 0x00 0x00 0x00

0x02 0x00 0x04 0x00 0x00 0x00 0x00 0x00

释放时发送:

0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

a字符:

按下时发送:0x00 0x00 0x04 0x00 0x00 0x00 0x00 0x00

释放时发送:0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

在CTRL+X模式打开的情况下CTRL+F:

按下时发送:0x01 0x00 0x09 0x00 0x00 0x00 0x00 0x00

释放时发送:0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

数字1:

按下时候发送:0x00 0x00 0x1E 0x00 0x00 0x00 0x00 0x00

释放时发送:0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

疑惑:为什么发送一串字符串"adAoFWx81"的时候只有在发送A的时候会先单独发送shift的键值包0x02 0x00 0x00 0x00 0x00 0x00 0x00 0x00之后再发送0x02 0x00 0x00 0x00 0x00 0x00 0x00 0x00,但是在发送F字符的时候只发送0x02 0x00 0x09 0x00 0x00 0x00 0x00 0x00,希望由看到的大佬可以指点一下,万分感谢!!!!!!

  • 7
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

fighting787

一起加油,一起成功!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值