当前位置:首页 >> 信息与通信 >>

如何修改STM32的USB例程为自己所用


如何修改 STM32 的 USB 例程为自己所用 computer00 电脑圈圈 http://ourdev.cn/bbs/bbs_content_all.jsp?bbs_sn=1387906 在万利学习板自带的演示例程中,有几个 USB 的例程。如果我们想实现一个 USB 功能,可以拿里面的例子 来改。 标 范 那么具体要改哪些地方呢?首先要改各种描述符,然后是具体的数据处理。

我们拿 USB 摇杆鼠 例 来 修 改 , 把 它 改 成 USB 键 盘 。 该 范 例 下 在 目 , 录 将

\Manley\EKBoard\EKSTM32F\USBDemo(8M osc)\USBDemo\USBLib\demos\JoyStickMouse JoyStickMouse 复制一份,改名为 USBKeyboard,以用来修改。

描述符在文件 usb_desc.c 中。第一个要改的是设备描述符。设备描述符的结构都标准的,长度也是固 定的。范例中的 USB 设备描述符如下: /* USB Standard Device Descriptor */ const u8 Joystick_DeviceDescriptor[JOYSTICK_SIZ_DEVICE_DESC]= { 0x12, 0x00, 0x02, 0x00, 0x00, 0x00, 0x40, 0x83, 0x04, 0x10, 0x57, 0x00, 0x02, 1, 2, 3, 0x01 /*Index of string descriptor describing manufacturer */ /*Index of string descriptor describing product*/ /*Index of string descriptor describing the device serial number */ /*bNumConfigurations*/ }; /* Joystick_DeviceDescriptor */ 我们只需要修改这里的 idVendor(即 VID)和 idProduct(即 PID)即可。它们是用来供电脑端识别设备以 加载驱动用的,所以必须不能跟现有的设备相冲突。VID 和 PID 都是两字节,低字节在前,高字节在后。 例如这里的 VID 为 0x0483,写在里面就是 0x83,0x04。我们将 VID 改成 0x1234,将 PID 改成 0x4321, 即: 0x34, 0x12, 0x21, 0x43。 然后再修改配置描述符集合。配置描述符集合包括配置描述符、接口描述符、类特殊描述符(这里是 HID 描述符)、以及端点描述符。如果你需要增加端点,那么在最后增加就行了,注意要记得修改 JOYSTICK_SIZ_CONFIG_DESC 的值为配置描述符集合的长度。 第一部分为配置描述符。 通常这里不需要修改, 除非你要改成该配置有多个接口(USB 复合设备),那么应该修改 bNumInterfaces,需要多少个就改成多 少个,这里只有一个接口,所以值为 1。第二部分为接口描述符,在接口描述符中决定该接口所实现的功 /*bcdDevice rel. 2.00*/ /*idProduct = 0x5710*/ /*bDeviceClass*/ /*bDeviceSubClass*/ /*bDeviceProtocol*/ /*bMaxPacketSize40*/ /*idVendor (0x0483)*/ /*bLength */ /*bcdUSB */ USB_DEVICE_DESCRIPTOR_TYPE, /*bDescriptorType*/

能,例如 HID 设备,或者是大容量存储设备等等。其中 bInterfaceNumber 为该接口的编号,从 0 开始。这 里只有一个接口,所以它的值为 0,如果又更多的接口,则依次编号。注意一个接口完整结束(包括该接 口下的类特殊描述符和端点描述符)后,才开始一个新的接口。bNumEndpoints 为该接口所使用的端点数 目(不包括端点 0),原来的程序是实现鼠标功能的,所以只有一个输入端点。我们这里增加一个输出端 点,用来控制 LED(键盘上有大写字母锁定、小键盘数字键锁定等指示灯),因此将 bNumEndpoints 改为 2。 bInterfaceClass 为接口所使用的类, 这里指定为 HID 设备, 键盘和鼠标都是 HID 设备, USB 这里不用修改, 如果你要实现其它设备,请根据 USB 协议所规定的类来修改。bInterfaceSubClass 为接口所使用的子类, 在 HID 设备类下规定了两种子类,系统引导时能用的和不能用的,这里为 1,表示系统引导时能使用。 bInterfaceProtocol 为接口的协议,原来为鼠标,这里改为 1,键盘。第三部分为 HID 描述符,只有 HID 设备才有,如果你要修改成其它设备,则用其它设备的类特殊描述符代替或者没有,在这里不用做修改。 第四部分为输入端点 1 的端点描述符,原来代码中,设置的端点最大包长度(wMaxPacketSize)为 4 字节, 我们将其改成 8 字节。另外,我们再增加一个输出端点 1,将最后的输入端点 1 描述符复制一份,然后修 改地址(bEndpointAddress)为 0x01,这表示该端点为输出端点,地址为 1。由 bEndpointAddress 的最高 位表示方向,1 为输入,0 为输出,最后 4 位表示地址。最后,要记得在 usb_desc.h 文件中修改 JOYSTICK_SIZ_CONFIG_DESC 的长度为 41,因为我们增加了 7 字节。实际修改好的配置描述符集合如下: /* USB Configuration Descriptor */ /* All Descriptors (Configuration, Interface, Endpoint, Class, Vendor */ const u8 Joystick_ConfigDescriptor[JOYSTICK_SIZ_CONFIG_DESC] = { //以下为配置描述符 0x09, /* bLength: Configuation Descriptor size */ USB_CONFIGURATION_DESCRIPTOR_TYPE, /* bDescriptorType: Configuration */ JOYSTICK_SIZ_CONFIG_DESC, /* wTotalLength: Bytes returned */ 0x00, 0x01, 0x01, 0x00, 0xC0, 0x32, /*bNumInterfaces: 1 interface*/ /*bConfigurationValue: Configuration value*/ /*iConfiguration: Index of string descriptor describing the configuration*/ /*bmAttributes: self powered */ /*MaxPower 100 mA: this current is used for detecting Vbus*/

//以下为接口描述符 /************** Descriptor of Joystick Mouse interface ****************/ /* 09 */ 0x09, 0x00, 0x00, 0x02, 0x03, 0x01, 0x01, 0, /*bLength: Interface Descriptor size*/ /*bInterfaceNumber: Number of Interface*/ /*bAlternateSetting: Alternate setting*/ /*bNumEndpoints*/ /*bInterfaceClass: HID*/ /*bInterfaceSubClass : 1=BOOT, 0=no boot*/ /*bInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/ /*iInterface: Index of string descriptor*/ USB_INTERFACE_DESCRIPTOR_TYPE,/*bDescriptorType: Interface descriptor type*/

//以下为 HID 描述符

/******************** Descriptor of Joystick Mouse HID ********************/ /* 18 */ 0x09, 0x00, 0x01, 0x00, 0x01, 0x22, 0x00, //以下为输入端点 1 描述符 /******************** Descriptor of Joystick Mouse endpoint ********************/ /* 27 */ 0x07, 0x81, 0x03, 0x08, 0x00, 0x20, /* 34 */ 0x07, 0x01, 0x03, 0x08, 0x00, 0x20, /* 41 */ }; 接下来,还需要修改报告描述符,报告描述符比较复杂,这里就不详述了,直接给出修改好的报告描述符 如下: const u8 Joystick_ReportDescriptor[JOYSTICK_SIZ_REPORT_DESC] = { 0x05, 0x01, // USAGE_PAGE (Generic Desktop) 0x09, 0x06, // USAGE (Keyboard) 0xa1, 0x01, // COLLECTION (Application) 0x05, 0x07, // 0x19, 0xe0, // 0x29, 0xe7, // 0x15, 0x00, // USAGE_PAGE (Keyboard/Keypad) USAGE_MINIMUM (Keyboard LeftControl) USAGE_MAXIMUM (Keyboard Right GUI) LOGICAL_MINIMUM (0) /*bInterval: Polling Interval (32 ms)*/ /*bLength: Endpoint Descriptor size*/ /*bEndpointAddress: Endpoint Address (OUT)*/ /*bmAttributes: Interrupt endpoint*/ /*wMaxPacketSize: 8 Byte max */ USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/ /*bInterval: Polling Interval (32 ms)*/ //以下为输出端但 1 描述符 /*bLength: Endpoint Descriptor size*/ /*bEndpointAddress: Endpoint Address (IN)*/ /*bmAttributes: Interrupt endpoint*/ /*wMaxPacketSize: 8 Byte max */ USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/ /*bCountryCode: Hardware target country*/ /*bNumDescriptors: Number of HID class descriptors to follow*/ /*bDescriptorType*/ /*bLength: HID Descriptor size*/ /*bcdHID: HID Class Spec release number*/ HID_DESCRIPTOR_TYPE, /*bDescriptorType: HID*/

JOYSTICK_SIZ_REPORT_DESC,/*wItemLength: Total length of Report descriptor*/

0x25, 0x01, // 0x95, 0x08, // 0x75, 0x01, // 0x81, 0x02, // 0x95, 0x01, // 0x75, 0x08, // 0x81, 0x03, // 0x95, 0x06, // 0x75, 0x08, // 0x25, 0xFF, // 0x19, 0x00, // 0x29, 0x65, // 0x81, 0x00, // 0x25, 0x01, // 0x95, 0x05, // 0x75, 0x01, // 0x05, 0x08, // 0x19, 0x01, // 0x29, 0x02, // 0x91, 0x02, // 0x95, 0x01, // 0x75, 0x06, // 0x91, 0x03, // 0xc0 };

LOGICAL_MAXIMUM (1) REPORT_COUNT (8) REPORT_SIZE (1) INPUT (Data,Var,Abs) REPORT_COUNT (1) REPORT_SIZE (8) INPUT (Cnst,Var,Abs) REPORT_COUNT (6) REPORT_SIZE (8) LOGICAL_MAXIMUM (255) USAGE_MINIMUM (Reserved (no event indicated)) USAGE_MAXIMUM (Keyboard Application) INPUT (Data,Ary,Abs) LOGICAL_MAXIMUM (1) REPORT_COUNT (5) REPORT_SIZE (1) USAGE_PAGE (LEDs) USAGE_MINIMUM (Num Lock) USAGE_MAXIMUM (Caps Lock) OUTPUT (Data,Var,Abs) REPORT_COUNT (1) REPORT_SIZE (6) OUTPUT (Cnst,Var,Abs)

// END_COLLECTION

该报告描述符说明输入报告为 8 字节,第一字节为特殊键,用位图表示,第二字节保留,第三至第八字节 为普通按键。我们将原来的摇杆功能改成键盘上的 4 个方向键,中键选择键为回车键,另外 KEY2 和 KEY3 分别做大写字母锁定键和数字锁锁定键。 输出报告为 1 字节, 其中最低两位分别为 Num Lock 灯和 Caps Lock 灯。 Joystick_StringLangID 描述符不用修改,Joystick_StringVendor、Joystick_StringProduct 分别为厂商 字符串和设备字符串,不改也可以,但是显示出来就是原来的内容,最好还是自己修改下。这里使用的是 Unicode 编 码 , 可 以 直 接 使 用 圈 圈 以 前 写 小 程 序 自 动 生 成 该 描 述 符 , 该 工 具 的 地 址 为 : http://computer00.***.org/user1/2198/archives/2007/42769.html。Joystick_StringSerial 为产品序 列号,它也是 Unicode 编码,这里可以不用修改,当然你修改也可以。这里我将厂商字符串改成“电脑圈 圈的家当”,产品字符串改成“电脑圈圈修改的简易 USB 键盘”。 好了,描述符改完了,就需要去修改数据处理了。我们启用了一个新的端点,端点 1 输出,原来的程序中 并未对它进行初始化,所以我们需要先增加对端点 1 输出的初始化。在 usb_prop.c 文件中,找到 void Joystick_Reset(void)函数, 该函数是负责初始化端点的。 原来对端点 1 输入的初始化设置为 4 字节, 我们将它改成 8 字节。并增加对端点输出的初始化,最终修改的代码部分如下: /* Initialize Endpoint In 1 */ SetEPType(ENDP1, EP_INTERRUPT); //初始化为中断端点类型 SetEPTxAddr(ENDP1, ENDP1_TXADDR); //设置发送数据的地址 SetEPTxCount(ENDP1, 8); //设置发送的长度 // SetEPRxStatus(ENDP1, EP_RX_DIS);

SetEPTxStatus(ENDP1, EP_TX_NAK); //设置端点处于忙状态 /* Initialize Endpoint Out 1 */ SetEPRxAddr(ENDP1, ENDP1_RXADDR); //设置接收数据的地址 SetEPRxCount(ENDP1, 1); //设置接收长度 SetEPRxStatus(ENDP1, EP_RX_VALID); //设置端点有效,可以接收数据 需要在 usb_conf.h 中增加对 ENDP1_RXADDR 的定义: #define ENDP1_RXADDR (0xD8) 然后,修改原来在 main 函数中发送数据的处理。这里我们使用圈圈前几天写的按键及摇杆驱动(见 http://blog.ednchina.com/computer00/142610/message.aspx)。 修改主循环中的内容如下: while (1) { DelayXms(5); //延时 5ms KeyScan(); //扫描一次键盘 if (KeyUp||KeyDown) { Joystick_Send(KeyPress); //发送按键 KeyUp="0"; KeyDown="0"; } } 然后,在 hw_config.c 中修改 Joystick_Send 函数,根据不同的按键来发送按键情况,具体怎么修改这里 就不说了,最后使用函数 UserToPMABufferCopy 将缓冲区中的数据复制到端点 1 的输出缓冲中,再使用 函数 SetEPTxValid(ENDP1)使端点 1 数据有效,从而发送出去。 对于输出,我们还需要增加一个回调函数来处理,因为原来的输出端点 1 的回调函数是个空函数。在 usb_conf.h 中找到#define EP1_OUT_Callback NOP_Process 一行,它将端点 1 输出回调函数定义为空 处理函数。 我们将它删除, 换成我们自己的回调处理函数: void EP1_OUT_Callback(void);。 然后回到 main.c 中增加该函数的实际代码,它主要用来控制 LED 的状态。在使用 LED 之前,当然要记得初始化这些 IO 口为 输出状态,以及使能 PC 口的时钟,还有前面的键盘扫描也要增加对相应的 IO 口初始化,这些初始化代码 在 void Set_System(void) 函 数 中 处 理 。 LED 连 接 在 PC 口 上 , 在 stm32f10x_conf.h 文 件 中 , 将 #define _GPIOC 宏使能,原本该宏是被注释掉的,这样会提示 GPIOC 没有定义。 处理接收数据的回调函数和发送数据的函数代码分别如下: void EP1_OUT_Callback(void) { u8 DataLen; //保存接收数据的长度 u8 DataBuffer[64]; //保存接收数据的缓冲区 DataLen = GetEPRxCount(ENDP1); //获取收到的长度 PMAToUserBufferCopy(DataBuffer, ENDP1_RXADDR, DataLen); //复制数据 SetEPRxValid(ENDP1); //设置端点有效,以接收下一次数据 if(DataLen==1) //收到一字节的输出报告 { //清除事件

//D0 位表示数字键盘灯,D1 位表示大写字母锁定灯 if(DataBuffer[0]&0x01) { GPIOC->BSRR=(1<<6); //亮 LED3 } else { GPIOC->BRR=(1<<6); //灭 LED3 } if(DataBuffer[0]&0x02) //大写字母锁定键 { GPIOC->BSRR=(1<<7); //亮 LED2 } else { GPIOC->BRR=(1<<7); //灭 LED2 } } } void Joystick_Send(u8 Keys) { u8 Buffer[8] = {0, 0, 0, 0, 0, 0, 0, 0}; u8 i; i="2"; //对各个按键进行处理。注意,由于这里的摇杆 5 个按键 //不可能同时按下,所以返回的普通键数量不会超过 6 个。 //如果你的键盘同时按下的普通键能够超过 6 个的话,就需要做 //点特殊处理了,将后面 6 字节全部设置为 0xFF,表示按键无法识别。 if(Keys&KEY_UP) { Buffer[i]=0x52; //Keyboard UpArrow i++; } if(Keys&KEY_DOWN) { Buffer[i]=0x51; //Keyboard DownArrow i++; } if(Keys&KEY_LEFT) { Buffer[i]=0x50; //Keyboard LeftArrow i++; //数字键盘灯亮

} if(Keys&KEY_RIGHT) { Buffer[i]=0x4F; //Keyboard RightArrow i++; } if(Keys&KEY_2) { Buffer[i]=0x39; //Keyboard Caps Lock i++; } if(Keys&KEY_3) { Buffer[i]=0x53; //Keypad Num Lock and Clear i++; } if(Keys&KEY_SEL) { Buffer[i]=0x28; //Keyboard Return (ENTER) } /*copy mouse position info in ENDP1 Tx Packet Memory Area*/ UserToPMABufferCopy(Buffer, GetEPTxAddr(ENDP1), 8); /* enable endpoint for transmission */ SetEPTxValid(ENDP1); } 程序运行后,可在设备管理器中看到新增加的 USB 人体学输入设备和一个键盘设备。如下图:

(原文件名:KeyBoard.GIF)


相关文章:
如何修改stm32的USB例程为自己所用
如何修改 STM32 的 USB 例程为自己所用技术分类: 微处理器与 DSP 作者:computer00 EDN 博客精华文章 作者:computer00 | 2008-08-06 痛下决心一定要把 USB ...
手把手教你STM32笔记
是为 48MHz 时钟配置用的,CLK48= HSE/PLLM*PLL...(STM32F429xx/439xx devices) 定时器中断服务程序...比如,硬件设备的终端更改了它,现在硬件设备往往也有...
STM32_深入浅出(新手必看)
正好 2148 还没上手,就直接转了这款 STM32F103...购买后所需的改造:打开壳体,将 USB 的+5V 供电跟...调试程序,如果需要使用 RAM 调试则为 lnkarm_RAM...
手把手教嵌入式
60 手把手教嵌入式 7.5 修改 STM32 的 USB 例程为自己所用 ......= 0; nCount--); } 思考题 在这里, 同学们可以尝试自己再编一下如何设置延时,使得 ...
HID--学习心得
根据数据特性再决定该不该回复该 如何回复、该不该接收该如何接收这些动作。 例程为自己所用】 【修改 STM32 的 USB 例程为自己所用】 1. USB 设备和主机的...
菜鸟如何开始学习STM32单片机软件编程
菜鸟如何开始学习STM32单片机软件编程_计算机软件及应用...稍加修改就可以开发自己的东西了,那么菜鸟如 何开始...字体替换——不学就用·... 各取所需话“抠图”...
STM32入门基本知识
今天先来说说为什么是它——选择 STM32 的原因。...购买后所需的改造:打开壳体,将 USB 的+5V 供电跟...调试程序,如果需要使用 RAM 调试则为 lnkarm_RAM...
[STM32入门]---利用ST提供的USB例程实现USB IAP功能
用 STM3210EVB 来演示这 个功能。 阅读前请下...因此我们修改该代码为: /* Set the Vector Table ...如何修改STM32的USB例程... 8页 免费 STM32官方...
STM32开发平台实验指导书
5.1 STM32 自检程序 第二部分:硬件篇(修改) 1....这样容易控制给锡量,从而不用浪费焊锡和吸锡的麻 ...所造成的管脚短路现象, 现在来说下如何处理掉这多...
基于STM32的USB程序开发笔记
基于STM32的USB程序开发笔记_信息与通信_工程科技_专业...朋友自己 翻译的, 却不是很全面, 如果你在为寻找...负责完成 USB 设备的枚举,一般情况是不需要更改的。...
更多相关标签:
stm32 usb例程 | stm32f103 usb例程 | stm32 usb cdc例程 | stm32 usb hid 例程 | stm32 usb官方例程 | stm32f105 usb例程 | stm32 usb键盘例程 | stm32f4 usb hid 例程 |