经典CV键盘树莓派复刻

发布于:2022-12-02 ⋅ 阅读:(199) ⋅ 点赞:(0)

关键词: DIY    CV键盘     树莓派    PICO    微控制器    TinyUSB

0 写在前面

1) CV键盘简介

        顾名思义,只提供 “复制粘贴” 功能的小键盘。

        他们说:高端的程序员,往往采用最朴素的编程方式。

        他们说:顶尖程序员都把自己叫做CV工程师。

        他们说:CV大法是一门正派武功,没几年沉淀学不来。

        他们说:程序员的事不叫抄,这叫代码复用。

        在他们之间,有一款键盘十分受追捧。这款神秘键盘,抛弃了那些冗余花哨的键位,只保留了最纯粹的功能,专为追求效率的CV工程师量身打造。

        搬砖,只要这一个键盘就够了。

2) 主控芯片简介:

        RP2040,一款树莓派mcu

https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdfhttps://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf

        Pico开发板,灵活、易用的树莓派微控制器开发板,基于rp2040芯片       https://pico.org.cnhttps://pico.org.cn

3) 硬件准备

a)开发环境:树莓派4B

b)目标设备:树莓派PICO

c)PCB:CV键盘

d)电子元件:机械按键  0805 1K电阻  100nF电容

4) 术语:

        HID:人机交互设备

        Host/Device:主机/从机(设备)

        TinyUSB:一种USB协议栈

        PHY:外设

1  使用树莓派开发树莓派:

https://datasheets.raspberrypi.com/pico/getting-started-with-pico.pdfhttps://datasheets.raspberrypi.com/pico/getting-started-with-pico.pdf

1)构建开发环境

a) 下载构建脚本

$ wget https://raw.githubusercontent.com/raspberrypi/pico-setup/master/pico_setup.sh

b)获取权限

$ chmod +x pico_setup.sh

c)执行脚本

$ ./pico_setup.sh

d)重新启动

$ sudo reboot

2)获取示例代码和SDK

$ cd ~/

$ mkdir pico

$ cd pico

获取sdk

$ git clone -b master https://github.com/raspberrypi/pico-sdk.git

$ cd pico-sdk

$ git submodule update --init

$ cd ..

$ git clone -b master https://github.com/raspberrypi/pico-examples.git

3)获取编译工具

$ sudo apt update

$ sudo apt install cmake gcc-arm-none-eabi libnewlib-arm-none-eabi build-essential

更新SDK

$ cd pico-sdk
$ git pull
$ git submodule update

4)编译示例代码

$ cd pico-examples

$ mkdir build

$ cd build

导入sdk路径

$ export PICO_SDK_PATH=../../pico-sdk

$ cmake ..

编译代码

$ cd usb/device/dev_hid_composite

$ make -j4

5)下载和运行

        按住booltest按键同时将pcio连接到4B上,弹出大容量设备挂在点击ok,拷贝到.uf2目标盘

$ sudo cp dev_hid_composite.uf2 /media/pi/RPI_RP2 

        此时按下bootest按键可以观察到键盘输入字符A,鼠标移动x和y分别移动5

2 TinyUsb协议栈

        键盘的按键触发基于USB进行传输,因此引入开源的USB协议栈TinyUSB。

        TinyUSB是一种用于嵌入式系统的开放源代码跨平台USB主机/设备堆栈,设计为内存安全,无动态分配和线程安全,所有中断事件都被延迟,然后在非ISR任务功能中处理。

https://docs.tinyusb.org/en/latesthttps://docs.tinyusb.org/en/latest

        USB 设备通过报告形式向主机传递数据,这种报告称为USB HID报告描述符。HID是人机交互设备的缩写。

        USB HID报告描述符是USB主机请求于USB设备的一种描述符。HID设备用报告的形式发送数据到主机,描述符告诉主机如何解释数据。在像键盘、鼠标等低速设备中,每10ms进行一次数据上报。通过数据上报告诉主机哪个按键被按下或者鼠标移动的位置。

3 Demo代码分析

        在路径pico/pico-example/build/usb/device/dev_hid_composite/main.c中。

        main调用了5个函数:

int main(void)

{

  board_init(); //外设初始化

  tusb_init(); //tinyUsb协议栈初始化

  while (1)

  {

    tud_task(); // tinyusb device task

    led_blinking_task();// LED运行指示灯

    hid_task(); //人机交互设备任务

  }

  return 0;

}

关键:hid_task()其实现为:

void hid_task(void)

{

  // Poll every 10ms

  const uint32_t interval_ms = 10;

  static uint32_t start_ms = 0;

//判断时间是否到10ms

  if ( board_millis() - start_ms < interval_ms) return;

  start_ms += interval_ms;

  //按键是否被按下

uint32_t const btn = board_button_read();

​​​​​​​  //当挂起时远端唤醒

  if ( tud_suspended() && btn )

  {

    // Wake up host if we are in suspend mode

    // and REMOTE_WAKEUP feature is enabled by host

    tud_remote_wakeup();

  }

else

  {

    // 发送报告上第一条信息

    send_hid_report(REPORT_ID_KEYBOARD, btn);

  }

}

其中关键函数:send_hid_report()函数实现:

static void send_hid_report(uint8_t report_id, uint32_t btn)

{

  // skip if hid is not ready yet

  if ( !tud_hid_ready() ) return;

//HID 设备类型

  switch(report_id)

  {

    case REPORT_ID_KEYBOARD:

    {

      // use to avoid send multiple consecutive zero report for keyboard

      static bool has_keyboard_key = false;

      if ( btn )

      {

        uint8_t keycode[6] = { 0 };

        keycode[0] = HID_KEY_A; //按键A触发

        tud_hid_keyboard_report(REPORT_ID_KEYBOARD, 0, keycode);

        has_keyboard_key = true;

      }else

      {

        // send empty key report if previously has key pressed

        if (has_keyboard_key) tud_hid_keyboard_report(REPORT_ID_KEYBOARD, 0, NULL);

        has_keyboard_key = false;

      }

    }

    break;

    case REPORT_ID_MOUSE:

    {

      int8_t const delta = 5;

      // no button, right + down, no scroll, no pan

      tud_hid_mouse_report(REPORT_ID_MOUSE, 0x00, delta, delta, 0, 0);

    }

    break;

    case REPORT_ID_CONSUMER_CONTROL:

    {

      // use to avoid send multiple consecutive zero report

      static bool has_consumer_key = false;

      if ( btn )

      {

        // volume down

        uint16_t volume_down = HID_USAGE_CONSUMER_VOLUME_DECREMENT;

        tud_hid_report(REPORT_ID_CONSUMER_CONTROL, &volume_down, 2);

        has_consumer_key = true;

      }else

      {

        // send empty key report (release key) if previously has key pressed

        uint16_t empty_key = 0;

        if (has_consumer_key) tud_hid_report(REPORT_ID_CONSUMER_CONTROL, &empty_key, 2);

        has_consumer_key = false;

      }

    }

    break;

    case REPORT_ID_GAMEPAD:

    {

      // use to avoid send multiple consecutive zero report for keyboard

      static bool has_gamepad_key = false;

      hid_gamepad_report_t report =

      {

        .x   = 0, .y = 0, .z = 0, .rz = 0, .rx = 0, .ry = 0,

        .hat = 0, .buttons = 0

      };

      if ( btn )

      {

        report.hat = GAMEPAD_HAT_UP;

        report.buttons = GAMEPAD_BUTTON_A;

        tud_hid_report(REPORT_ID_GAMEPAD, &report, sizeof(report));

        has_gamepad_key = true;

      }else

      {

        report.hat = GAMEPAD_HAT_CENTERED;

        report.buttons = 0;

        if (has_gamepad_key) tud_hid_report(REPORT_ID_GAMEPAD, &report, sizeof(report));

        has_gamepad_key = false;

      }    }

    break;

    default: break;

  }

}

伪代码逻辑:

函数入口

{

初始化外设

初始化协议栈



进入循环

{

协议栈任务

按键触发协议传输

LED运行灯任务

}



}

代码过程分析:

        进入循环后,每10ms判断一次按键是否触发,触发则触发链式消息中的第一条,发送键盘消息,随后的消息会在协议栈任务中以链式消息顺序发送。

4 修改代码

1)修改按键触发的gpio6.7.8初始化

        文件目录 pico/pico-sdk/lib/tinyusb/hw/rp2040/family.c


#define CV_BOARD

#ifdef CV_BOARD

#define KEY_CTRL 6

#define KEY_C 7

#define KEY_V 8

#endif



#ifdef CV_BOARD

  gpio_init(KEY_CTRL);

  gpio_init(KEY_C);

  gpio_init(KEY_V);

  gpio_set_dir(KEY_CTRL,GPIO_IN);

  gpio_set_dir(KEY_C,GPIO_IN);

  gpio_set_dir(KEY_V,GPIO_IN);

#endif

2)删除链式消息中其他HID设备行为

原枚举

enum

{

  REPORT_ID_KEYBOARD = 1,

  REPORT_ID_MOUSE,

  REPORT_ID_CONSUMER_CONTROL,

  REPORT_ID_GAMEPAD,

  REPORT_ID_COUNT

};

现枚举:

enum

{

  REPORT_ID_KEYBOARD = 1,

  REPORT_ID_COUNT,

  REPORT_ID_MOUSE,

  REPORT_ID_CONSUMER_CONTROL,

  REPORT_ID_GAMEPAD
};

3)修改原来的按键触发

void hid_task(void){

uint32_t const btn = board_button_read();

}

void hid_task(void){

uint32_t const btn = (!gpio_get(KEY_CTRL)) | (!gpio_get(KEY_C)) |(!gpio_get(KEY_V));

}

4)原来的传递的按键码

static void send_hid_report(uint8_t report_id, uint32_t btn)

{

  // skip if hid is not ready yet

  if ( !tud_hid_ready() ) return;



  switch(report_id)

  {

    case REPORT_ID_KEYBOARD:

    {

      // use to avoid send multiple consecutive zero report for keyboard

      static bool has_keyboard_key = false;



      if ( btn )

      {

        uint8_t keycode[6] = { 0 };

        keycode[0] = HID_KEY_A;



        tud_hid_keyboard_report(REPORT_ID_KEYBOARD, 0, keycode);

        has_keyboard_key = true;

      }else

……

……

……

}

修改为



static void send_hid_report(uint8_t report_id, uint32_t btn)

{

  // skip if hid is not ready yet

  if ( !tud_hid_ready() ) return;



  switch(report_id)

  {

    case REPORT_ID_KEYBOARD:

    {

      // use to avoid send multiple consecutive zero report for keyboard

      static bool has_keyboard_key = false;



      if ( btn )

      {

        uint8_t keycode[6] = { 0 };

         if(!gpio_get(KEY_CTRL)) keycode[0] = HID_KEY_CNOTROL_LEFT;

if(!gpio_get(KEY_C)) keycode[1] = HID_KEY_C;

if(!gpio_get(KEY_V)) keycode[2] = HID_KEY_V;



for(int i = 6; i < 6 ; i++)

{

   if(keycode[i] == 0)

   {

memcpy(&keycode[i],&kecode[i+1],6 - i + 1);

   }

}



       tud_hid_keyboard_report(REPORT_ID_KEYBOARD, 0, keycode);

        has_keyboard_key = true;

      }else

……

……

……

}

5)编译与烧录

$ make -j4

$ sudo cp dev_hid_composite.uf2 /media/pi/RPI_RP2

5 硬件电路设计

使用GPIO6-8作为键盘三个按键的输入脚。电路原理图如下:

PCB设计

6 成品测试

(补充视频)

2022/10/3

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到