打印

一个最简的 USB HID(Keyboard) 示例 [使用STM32F3 Discovry]

[复制链接]
8069|8
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
lxyppc|  楼主 | 2013-6-13 17:15 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
使用 USB 协议栈框架做的最简洁的 HID 示例,基于Lee老师的这个例子修改而来 。
hid.zip (3.28 KB) 附件中是生成的 hex 文件,可以运行在STM32F3 Discovry上,烧写使用ST-Link Utility。使用USER按键启动“我的电脑”。
代码空间开销:
   text    data     bss     dec     hex filename
   2948       0     324    3272     cc8 out/hid.elf
flash 占用 3.2K。上程序:
#include <usbd/hal/stmfsmicro.hpp>                              // USB HAL(Hardware Abstract Layer),STM32
#include <usbd/hid.hpp>                                         // USB HID Class
#include <stm32/stm32f30x/flash.hpp>
#include <stm32/stm32f30x/exti.hpp>

using namespace usbd;                                        // 使用 usb 名空间
using namespace hid;                                        // 使用 hid 名空间
using namespace sfr::exti;
using namespace sfr::flash;

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

template<typename PARENT, uint32_t PARAM>                   // 定式
class if_t : public if_impl_t<                              // 定义 Interface 类
            if_t, PARENT, PARAM,                            // 定式
            0,                                              // 指定 bInterfaceSubClass
            1,                                              // 指定 bInterfaceProtocol
            0,                                              // 指定 String ID
            0x110,                                          // 指定 bcdHID
            0,                                              // 指定 bCountryCode
            optional_t<                                     // 定义 hid optional descriptor
                report_t<                                   // 定义 hid report descriptor
                    usage_page_t<CONSUMER_DEVICE>,          // 0x05, 0x0C,      //  Usage Page (Consumer Devices),
                    usage_t<1>,                             // 0x09, 0x06,      //  Usage (Consumer Control),
                    collection_t<APPLICATION,               // 0xA1, 0x01,      //  Collection (Application),
                        logical_extremum_t<0, 1>,           // 0x15, 0x00,      //      Logical Minimum (0),
                                                            // 0x25, 0x65,      //      Logical Maximum (101),
                        report_size_t<1>,                   // 0x75, 0x08,      //      Report Size (1),
                        report_count_t<2>,                  // 0x95, 0x01,      //      Report Count (2),
                        usage_t<0x23, 2>,                   // 0x0A, 0x23, 0x02,//      USAGE (0x223),      // WWW Browser
                        usage_t<0x94, 1>,                   // 0x0A, 0x94, 0x01,//      USAGE (0x194),      // My Computer
                        input_t<DATA, VARIABLE, ABSOLUTE>,  // 0x81, 0x02,      //      Input (Data, Variable, Absolute),
                        report_count_t<6>,                  // 0x95, 0x07,      //      Report Count (6),
                        input_t<CONSTANT>                   // 0x81, 0x01,      //      Input (Constant)
                    >                                       // 0xC0         //  End Collection
                >
            >,
            ep_t                                            // 指定本 Interface 包含的 Endpoint
        > {
public:
    __INLINE void config()          // Interface 初始化,当 Set Configuration 时被调用
    {
        if_t::if_impl_t::config();  // 使用默认的 config 处理
        // TODO
        RCC.AHBENR().IOPAEN(1);
        // Set GPIOA.0 as input
        GPIOA.MODER().MODER0(GPIO_Mode_IN);
        GPIOA.PUPDR().PUPDR0(GPIO_PuPd_NOPULL);
        
        // Enable interrupt of EXTI0
        NVIC->IP[EXTI0_IRQn] = (3<<2) | 0;
        NVIC->ISER[EXTI0_IRQn >> 0x05] =
        (uint32_t)0x01 << (EXTI0_IRQn & (uint8_t)0x1F);
        // Enable EXTI0 rising and falling edge interrupt
        EXTI.IMR1().MR0(1);
        EXTI.RTSR1().TR0(1);
        EXTI.FTSR1().TR0(1);
    }
};

class usbd_t : public core::usbd_impl_t<        // 定义 USB 类
        usbd_t,                                 // 定式
        0x110,                  // bcdUSB
        0,                      // bDeviceClass
        0,                      // bDeviceSubClass
        0,                      // bDeviceProtocol
        0x0416,                 // idVendor
        0x5011,                 // idProduct
        0x100,                  // bcdDevice
        1,                      // iManufacture
        2,                      // iProduct
        3,                      // iSerialNumber
        true,                   // bmAttributes, Bus Powered
        false,                  // bmAttributes, Self Powered
        false,                  // bmAttributes, Remote Wakeup
        10_mA,                  // bMaxPower
        0,                      // iConfiguration
        if_t> {                 // 指定 usb 包含的 Interface,可连续加入多个 Function 和 Interface

public:
    __INLINE usbd_t() { }

#if 1
    __INLINE bool data_out(uint_fast8_t type, uint_fast8_t request, uint_fast16_t value, uint_fast16_t index, uint_fast16_t length)
    {
        out();
        return true;
    }
    __INLINE bool data_in(uint_fast8_t type, uint_fast8_t request, uint_fast16_t value, uint_fast16_t index, uint_fast16_t length)
    {
        in();
        return true;
    }
#endif

    __INLINE const uint8_t* get_string_descriptor(uint_fast8_t index, uint_fast16_t lang_id)    // GetDescriptor(String) 处理
    {
        static const string_langid_t<langid_t::English_UnitedStates> desc0;
        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;
        static const string_t<u'U', u'S', u'B', u' ', u'渭', u'K', u'e', u'y'> desc2;
        static const string_t<u'0', u'0', u'0', u'0'> desc3;
        static const uint8_t* const descriptor[] {
            reinterpret_cast<const uint8_t*>(&desc0),
            reinterpret_cast<const uint8_t*>(&desc1),
            reinterpret_cast<const uint8_t*>(&desc2),
            reinterpret_cast<const uint8_t*>(&desc3)
        };
        return index < sizeof(descriptor) / sizeof(descriptor[0]) ? descriptor[index] : nullptr;
    }
};

usbd_t usbdx;                // 定义 USB 类对象

extern "C"  void USB_LP_CAN1_RX0_IRQHandler(void){
        usbdx.isr();
}

extern "C" void SystemInitHpp(void)
{
    // set system clock to 72MHz
    RCC.CR().HSEBYP(1).HSEON(1);
    while(! RCC.CR().HSERDY );
    FLASH.ACR().PRFTBE(1).LATENCY(2);
    RCC.CFGR().HPRE(0).PPRE2(0).PPRE1(1).PLLSRC(1).PLLMUL(9-2).PLLXTPRE(0);
    RCC.CR().PLLON(1);
    while(! RCC.CR().PLLRDY);
    RCC.CFGR().SW(2);
    while( RCC.CFGR().SWS != 2 );
}

extern "C" void EXTI0_IRQHandler(void)
{
    static uint8_t code;
    EXTI.PR1().PR0(1);
    if(GPIOA.IDR().IDR0){
        code = 2;
    }else{
        code = 0;
    }
    get_if<0>(usbdx).write(&code, sizeof(code));
}

extern "C" int main()
{
    usbdx.open(true);        // 初始化 usb 对象
    while (true);
}

extern "C" {
    void abort(void){
        
    }
}
   
   
   


沙发
lxyppc|  楼主 | 2013-6-13 17:22 | 只看该作者
沙发自己坐

使用特权

评论回复
板凳
mmuuss586| | 2013-6-13 17:38 | 只看该作者

使用特权

评论回复
地板
chen282220981| | 2013-6-13 17:41 | 只看该作者
先下来看看

使用特权

评论回复
5
缥缈九哥| | 2013-6-13 19:29 | 只看该作者
顶起

使用特权

评论回复
6
elec921| | 2013-6-13 19:57 | 只看该作者
牛X。USB 是个好东西

使用特权

评论回复
7
cjhk| | 2013-6-13 21:59 | 只看该作者
学习一下   谢谢了   楼主  顶一个   很不错哦     顶一个

使用特权

评论回复
8
john_lee| | 2013-6-13 22:20 | 只看该作者
get_if<0>(usbdx).write(&code, sizeof(code));
这里给出的模板参数 0,是直接按序号取得 usb 对象中的 interface 的引用的访问方式,觉得不友好,可以改成按 interface 的名字来访问:
static_cast<usbd_t::if_t>(usbdx).write(&code, sizeof(code));
是不是要好些?

使用特权

评论回复
9
cailantu| | 2013-8-30 14:27 | 只看该作者
感谢楼主分享如此好东西

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:代码发BBS不好看?你需要它 代码着色https://bbs.21ic.com/icview-135254-1-1.html

27

主题

2249

帖子

19

粉丝