[Cortex-M0技术交流] 一个最简的 USB HID(Keyboard) 示例

[复制链接]
 楼主| john_lee 发表于 2013-5-19 03:15 | 显示全部楼层 |阅读模式
本帖最后由 john_lee 于 2013-5-19 11:54 编辑

使用 USB 协议栈框架做的最简洁的 HID 示例,仍然是基于新唐 M0。
附件中是生成的 hex 文件,可以运行在菜农助学板上,烧写时需要配置 Config0 中的时钟选择为 external 12Mhz。两个按键,一个启动“浏览器”,一个启动“我的电脑”。
代码空间开销:
  1.    text    data     bss     dec     hex filename
  2.    1452     196      40    1688     698 .\default\ukey.elf
flash 占用 1.6K。上程序:
  1. #include <usb/hal/numicro>                                  // USB HAL(Hardware Abstract Layer),新唐 Numicro
  2. #include <usb/hid>                                          // USB HID Class
  3. #include <numicro/nuc/sfr/gpio>                             // GPIO Register
  4. using namespace usb;                                        // 使用 usb 名空间
  5. using namespace hid;                                        // 使用 hid 名空间

  6. template<typename PARENT, uint32_t PARAM>                   // 定式
  7. class ep_t : public in::ep_impl_t<                          // 定义 Endpoint 类
  8.             ep_t, PARENT, PARAM,                            // 定式
  9.             10                                              // 指定 bInterval
  10.         > { };

  11. template<typename PARENT, uint32_t PARAM>                   // 定式
  12. class if_t : public if_impl_t<                              // 定义 Interface 类
  13.             if_t, PARENT, PARAM,                            // 定式
  14.             0,                                              // 指定 bInterfaceSubClass
  15.             1,                                              // 指定 bInterfaceProtocol
  16.             0,                                              // 指定 String ID
  17.             0x110,                                          // 指定 bcdHID
  18.             0,                                              // 指定 bCountryCode
  19.             optional_t<                                     // 定义 hid optional descriptor
  20.                 report_t<                                   // 定义 hid report descriptor
  21.                     usage_page_t<CONSUMER_DEVICE>,          // 0x05, 0x0C,      //  Usage Page (Consumer Devices),
  22.                     usage_t<1>,                             // 0x09, 0x06,      //  Usage (Consumer Control),
  23.                     collection_t<APPLICATION,               // 0xA1, 0x01,      //  Collection (Application),
  24.                         logical_extremum_t<0, 1>,           // 0x15, 0x00,      //      Logical Minimum (0),
  25.                                                             // 0x25, 0x65,      //      Logical Maximum (101),
  26.                         report_size_t<1>,                   // 0x75, 0x08,      //      Report Size (1),
  27.                         report_count_t<2>,                  // 0x95, 0x01,      //      Report Count (2),
  28.                         usage_t<0x23, 2>,                   // 0x0A, 0x23, 0x02,//      USAGE (0x223),      // WWW Browser
  29.                         usage_t<0x94, 1>,                   // 0x0A, 0x94, 0x01,//      USAGE (0x194),      // My Computer
  30.                         input_t<DATA, VARIABLE, ABSOLUTE>,  // 0x81, 0x02,      //      Input (Data, Variable, Absolute),
  31.                         report_count_t<6>,                  // 0x95, 0x07,      //      Report Count (6),
  32.                         input_t<CONSTANT>                   // 0x81, 0x01,      //      Input (Constant)
  33.                     >                                       // 0xC0         //  End Collection
  34.                 >
  35.             >,
  36.             ep_t                                            // 指定本 Interface 包含的 Endpoint
  37.         > {
  38. public:
  39.     __INLINE void config()          // Interface 初始化,当 Set Configuration 时被调用
  40.     {
  41.         if_t::if_impl_t::config();  // 使用默认的 config 处理
  42.         using namespace sfr::gpio;
  43.         *reinterpret_cast<volatile uint32_t*>(0xe000e100) = (1 << EINT0_IRQn) | (1 << EINT1_IRQn);  // NVIC 使能 EINT0、EINT1 中断
  44.         GPIOB.IEN()
  45.             .IF_EN14(1)             // 使能 EINT0 下降沿中断
  46.             .IR_EN14(1)             // 使能 EINT0 上升沿中断
  47.             .IF_EN15(1)             // 使能 EINT1 下降沿中断
  48.             .IR_EN15(1);            // 使能 EINT1 上升沿中断
  49.     }
  50. };

  51. class usbd_t : public core::usbd_impl_t<        // 定义 USB 类
  52.         usbd_t,                                 // 定式
  53.         0x110,                  // bcdUSB
  54.         0,                      // bDeviceClass
  55.         0,                      // bDeviceSubClass
  56.         0,                      // bDeviceProtocol
  57.         0x0416,                 // idVendor
  58.         0x5011,                 // idProduct
  59.         0x100,                  // bcdDevice
  60.         1,                      // iManufacture
  61.         2,                      // iProduct
  62.         3,                      // iSerialNumber
  63.         true,                   // bmAttributes, Bus Powered
  64.         false,                  // bmAttributes, Self Powered
  65.         false,                  // bmAttributes, Remote Wakeup
  66.         10_mA,                  // bMaxPower
  67.         0,                      // iConfiguration
  68.         if_t> {                 // 指定 usb 包含的 Interface,可连续加入多个 Function 和 Interface

  69. public:
  70.     __INLINE usbd_t() { }

  71. #if 1
  72.     __INLINE bool data_out(uint_fast8_t type, uint_fast8_t request, uint_fast16_t value, uint_fast16_t index, uint_fast16_t length)
  73.     {
  74.         out();
  75.         return true;
  76.     }
  77.     __INLINE bool data_in(uint_fast8_t type, uint_fast8_t request, uint_fast16_t value, uint_fast16_t index, uint_fast16_t length)
  78.     {
  79.         in();
  80.         return true;
  81.     }
  82. #endif

  83.     __INLINE const uint8_t* get_string_descriptor(uint_fast8_t index, uint_fast16_t lang_id)    // GetDescriptor(String) 处理
  84.     {
  85.         static const string_langid_t<langid_t::English_UnitedStates> desc0;
  86.         static const string_t<u'j', u'.', u'y', u'.', u'l', u'e', u'e', u'@', u'y', u'e', u'a', u'h', u'.', u'n', u'e', u't'> desc1;
  87.         static const string_t<u'U', u'S', u'B', u' ', u'渭', u'K', u'e', u'y'> desc2;
  88.         static const string_t<u'0', u'0', u'0', u'0'> desc3;
  89.         static const uint8_t* const descriptor[] {
  90.             reinterpret_cast<const uint8_t*>(&desc0),
  91.             reinterpret_cast<const uint8_t*>(&desc1),
  92.             reinterpret_cast<const uint8_t*>(&desc2),
  93.             reinterpret_cast<const uint8_t*>(&desc3)
  94.         };
  95.         return index < sizeof(descriptor) / sizeof(descriptor[0]) ? descriptor[index] : nullptr;
  96.     }
  97. };

  98. usbd_t usbd;                // 定义 USB 类对象

  99. void usbd_isr()             // USBD_IRQn 中断向量 handler
  100. {
  101.     usbd.isr();             // 调用 USB 类的中断处理
  102. }

  103. void eint0_isr()            // EINT0_IRQn 中断向量 handler
  104. {
  105.     static uint8_t code;
  106.     using namespace sfr::gpio;
  107.     auto pin = GPIOB.PIN();
  108.     auto isrc = GPIOB.ISRC();
  109.     GPIOB.ISRC = isrc;
  110.     if (isrc.ISRC14)
  111.         code = pin.PIN14 == 0 ? 1 : 0;      // WWW Browser
  112.     else
  113.         code = pin.PIN15 == 0 ? 2 : 0;      // My Computer
  114.     get_if<0>(usbd).write(&code, sizeof(code));     // 使用 usbd 的 interface 0 发送数据
  115. }

  116. void eint1_isr()            // EINT1_IRQn 中断向量 handler
  117. {
  118.     eint0_isr();
  119. }

  120. int main()
  121. {
  122.     usbd.open(true);        // 初始化 usb 对象
  123.     while (true);
  124. }

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×

评分

参与人数 1威望 +8 收起 理由
xyz549040622 + 8

查看全部评分

 楼主| john_lee 发表于 2013-5-19 03:16 | 显示全部楼层
抢沙发,看我快还是9G快!!!
缥缈九哥 发表于 2013-5-19 03:23 | 显示全部楼层
我都 没有上来。所以 给你抢着了。
缥缈九哥 发表于 2013-5-19 11:56 | 显示全部楼层
我再顶。
 楼主| john_lee 发表于 2013-5-19 12:05 | 显示全部楼层
这个示例中的用户代码(函数部分)不超过 30 行,真的非常少。
乡村男孩 发表于 2013-5-19 12:13 | 显示全部楼层
必须顶起呀....
dong_abc 发表于 2013-5-21 00:09 | 显示全部楼层
get_if<0>(usbd).write(&code, sizeof(code));     // 使用 usbd 的 interface 0 发送数据

HID键盘不是向端点发数据吗? 怎么用interface发数据? get_if<0>神马意思啊?
 楼主| john_lee 发表于 2013-5-21 00:53 | 显示全部楼层
本帖最后由 john_lee 于 2013-5-21 00:55 编辑
dong_abc 发表于 2013-5-21 00:09
get_if(usbd).write(&code, sizeof(code));     // 使用 usbd 的 interface 0 发送数据

HID键盘不是向端 ...


不错,这些回复里,就属你的贴子有技术含量,他们都是打酱油的。

USB 收发数据,物理上是用的 Endpoint 没错,但逻辑功能却都集中在 Interface 或 Function 里,所以,usbd 对象外部的程序逻辑,要访问 usbd 对象,访问的层次应该是 Interface,而不是 Endpoint,虽然最终数据是从 Endpoint 走的。这个协议栈的层次关系是非常明确的,就像其他很多有层次的协议栈一样,比如 TCP/IP。

get_if是个模板函数,尖括号里的 0 是模板参数,圆括号里的 usbd 是函数实参,整个 get_if<0>(usbd) 的意思,就是取得 usbd 对象中的 第 0 号 Interface 的引用。不知我说明白没?
缥缈九哥 发表于 2013-5-21 13:07 | 显示全部楼层
我看你说的明白。可是实质是不明白。
dong_abc 发表于 2013-5-21 19:02 | 显示全部楼层
缥缈九哥 发表于 2013-5-21 13:07
我看你说的明白。可是实质是不明白。

是的,只看见表象了,实质的东西根本不清楚。

hotpower 发表于 2013-5-22 12:11 来自手机 | 显示全部楼层
abin0415 发表于 2013-5-22 21:30 | 显示全部楼层
顶起
jstele 发表于 2013-5-24 07:59 | 显示全部楼层

顶!强烈支持
zifan 发表于 2013-5-29 11:15 | 显示全部楼层
刚好要做这方面。记录,后续细看。
chaled 发表于 2013-6-3 10:55 | 显示全部楼层
收藏,准备使用这个芯片。
liumingxing 发表于 2013-6-8 10:01 | 显示全部楼层
最近才知道LOOK-RTOS,就是不会用。要是有一本手册,介绍LOOK-RTOS如何使用,或者有一本书能系统地介绍LOOK,就太好了。
江枫渔火 发表于 2013-6-8 10:06 | 显示全部楼层
马克~顶,好贴~俺也是打酱油的
Ryanhsiung 发表于 2013-6-8 17:58 | 显示全部楼层
没有项目么。。。
jstele 发表于 2013-6-14 07:33 | 显示全部楼层
usb傳輸都是以endpoint去做in或out的工作,當你不是主機而是設備時;usb   hid在transection過程中會在主機與設備間建立一個通道就是所謂的pipe。所以此時usb主機和設備都會知道對方的端點號(usb2.0有1~15組in和out的端點號所以共有30個,端點號0是給usb在init時與主機聯絡的起始端點)所以我想版主所說的interface應該是設備在與主機連線上後所建立完成pipe後所確定的主機和設備的in和out端點號才是。
hesun 发表于 2013-6-28 13:45 | 显示全部楼层
向高手学习。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:坚持使用 GCC 一百年不动摇!

33

主题

1466

帖子

21

粉丝
快速回复 在线客服 返回列表 返回顶部