打印
[应用相关]

在万利的STM32学习板上实现USB多媒体键盘

[复制链接]
6033|15
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
computer00|  楼主 | 2008-7-30 16:29 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

   圈圈通过对原来的USB摇杆实例进行修改,做了一个USB HID多媒体键盘,可以调节音量、启动Windows Media
播放器、停止/暂停播放、打开网页、打开计算器等等功能。由于摇杆和多媒体键盘都是HID设备,因而改动的工作量很
少,只需要修改一下VID、PID,字符串描述符、报告描述符、返回报告的格式即可。
  板上各按键的功能分配如下:摇杆往上音量增加;摇杆往下音量降低;摇杆往左静音;摇杆往右启动Windows
Media播放器;摇杆中键(确认键)为播放/暂停(对千千静听等播放器也有效);按键KEY2为打开网页;按键KEY3
为打开计算器。
   从这里下载整个代码包:http://blog.**/computer00/141572/message.aspx
下载该压缩包,解压到ManleyEKBoardEKSTM32FUSBDemo(8M osc)USBDemoUSBLibdemos目录下,
然后编译,烧写调试即可。
    运行后可以看到弹出的新硬件,以及设备管理器中的设备,一个为HID设备,一个为标准用户控制设备,
如下图所示:



报告描述符的代码如下:
const u8 ReportDescriptor[SIZ_REPORT_DESC] =
{
 0x05, 0x0c, // USAGE_PAGE (Consumer Page)
 0x09, 0x01, // USAGE (Consumer Control)
 0xa1, 0x01, // COLLECTION (Application)
 0x09, 0xea, //   USAGE (Volume Decrement) 
 0x09, 0xe9, //   USAGE (Volume Increment) 
 0x09, 0xe2, //   USAGE (Mute)
 0x09, 0xcd, //   USAGE (Play/Pause)
 0x0a, 0x83, 0x01, //USAGE (Windows Media Player(0x183))
 0x0a, 0x23, 0x02, //USAGE (AC Home(0x0223))
 0x0a, 0x92, 0x01, //USAGE (AL Calculator(0x192))
 0x15, 0x00, //     LOGICAL_MINIMUM (0)
 0x25, 0x01, //     LOGICAL_MAXIMUM (1)
 0x95, 0x07, //     REPORT_COUNT (7)
 0x75, 0x01, //     REPORT_SIZE (1)
 0x81, 0x02, //     INPUT (Data,Var,Abs)
 0x75, 0x01, //     REPORT_SIZE (1)
 0x95, 0x01, //     REPORT_COUNT (1)
 0x81, 0x03, //     INPUT (Cnst,Var,Abs)
 0xc0        // END_COLLECTION
}; /* ReportDescriptor */
 
沙发
香水城| | 2008-7-30 17:15 | 只看该作者

圈圈已经成精了,STM32的USB开发包已经玩熟了

说实话,这个USB开发包非常好用,我们已经用了近十年。

使用特权

评论回复
板凳
computer00|  楼主 | 2008-7-30 20:44 | 只看该作者

说实话,我对这个程序的结构并不了解……但是我知道改什

这个程序具体是怎样工作的我不关心,我只修改描述符、返回数据和接收数据就行了,哈哈……

使用特权

评论回复
地板
lomeisi_99| | 2008-7-31 09:24 | 只看该作者

香主还能把程序结构做个简单介绍

使用特权

评论回复
5
walnutcy| | 2008-7-31 09:41 | 只看该作者

00是做项目前期预研非常不错,至于做产品还不知道,呵呵,

使用特权

评论回复
6
香水城| | 2008-7-31 10:03 | 只看该作者

能用就好

介绍程序结构就免了,ST不是教育机构,ST的程序也不是最好的,只是个Demo,如果为了学习研究,还是要靠自己分析。

使用特权

评论回复
7
computer00|  楼主 | 2008-7-31 10:15 | 只看该作者

呵呵,虽然我没有仔细看过代码,但我可以按我的理解来说

应该是在端点0输出中断回调函数中处理建立过程的标准请求,然后根据不同的请求,
选择需要返回不同的数据(主要是各种描述符)或者处理输出的数据。而在端点0输入
中断的回调函数中,返回剩余的(一个数据包不能发送完)数据。主程序则是
等待设置配置,
一旦设置配置之后,那么非0端点就可以启用了,然后根据需要对这些端点操作即可。

对于枚举过程的各种描述符的请求以及设置地址、设置配置等等,原来的程序模板都
已经实现了,所以我们只需要修改描述符中的数据以及数据即可。除非你要增加新的
描述符,才需要去修改这些代码。对于非0端点,需要在中断中处理数据的话,只需要
自己增加对应端点的回调函数即可,当然还要记得事先对相应的端点做初始化。

使用特权

评论回复
8
computer00|  楼主 | 2008-7-31 10:52 | 只看该作者

原例程中的按键处理并没有消抖处理,俺重新改了按键的驱

unsigned char KeyCurrent,KeyOld,KeyNoChangedTime;
unsigned char KeyPress;
unsigned char KeyDown,KeyUp,KeyLast;

#define KEY_SEL    0x01
#define KEY_RIGHT  0x02
#define KEY_LEFT   0x04
#define KEY_DOWN   0x10
#define KEY_UP     0x08
#define KEY_2      0x20
#define KEY_3      0x40

#define KeyIO ((((GPIOD->IDR)>>11)&0x1F)|((((GPIOD->IDR)>>3)&0x03)<<5))

void KeyScan(void);
/********************************************************************
函数功能:键盘扫描。每5ms调用一次
入口参数:无。
返    回:无。
备    注:无。
********************************************************************/
void KeyScan(void)
{
 //开始键盘扫描
 //保存按键状态到当前按键情况
 //KeyCurrent总共有8个bit
 //当某个开关按下时,对应的bit为1
      
 KeyCurrent=~KeyIO;    

 if(KeyCurrent!=KeyOld)  //说明按键情况发生了改变
  {
   KeyNoChangedTime=0;       //键盘按下时间为0
   KeyOld=KeyCurrent;        //保存当前按键情况
   return;
  }
 else
  {
   KeyNoChangedTime++;         //按下时间累计
   if(KeyNoChangedTime>=1)     //如果按下时间足够
   {
    KeyNoChangedTime=1;
    KeyPress=KeyOld;      //保存按键
    KeyDown|=(~KeyLast)&(KeyPress); //求出新按下的键
    KeyUp|=KeyLast&(~KeyPress);     //求出新释放的键
    KeyLast=KeyPress;            //保存当前按键情况
   }
  }
}


当某个键按下时,在KeyDown中对应的位被设置为1;某个键被释放时,KeyUp中对应的位为1;
KeyPress中保存的是当前按键的按住情况,某位按住时位为1。

KeyDown和KeyUp中的值使用后要手动清除,表示已经处理了这个事件,而KeyPress不用手动清除,
它一直反映按键的按住情况。

实际使用方法范例如下:

if(KeyDown & KEY_UP)   //摇杆往上移动键按下
if(KeyDown & KEY_RIGHT) //摇杆往右移动键按下
if(KeyPress & KEY_DOWN) //摇杆往下移动被按住了
if(KeyUp & KEY_LEFT) //摇杆往左移动由原来的按住变成了松开
if(KeyUp || KEY_DOWN) //有按键状态变动

万利的板子上有两个按键KEY2和KEY3,另外还有一个摇杆:KEY_SEL,KEY_LEFT,KEY_RIGHT,KEY_UP,KEY_DOWN.
当初为了统一名称,都叫成KEY了,后面的摇杆改成叫Joystick似乎更好一些~~~~
不然两个KEY_UP和KEY_DOWN会让人混饶...

使用特权

评论回复
9
armmcu| | 2008-8-23 05:41 | 只看该作者

Re

版主,改过的代码能支持 SET REPORT 吗?既然你增加了一个端口来控制LED,那如何和电脑的键盘同步控制LED呢

使用特权

评论回复
10
香水城| | 2008-8-23 08:41 | 只看该作者

USB键盘和电脑的键盘同步控制LED是由PC端的USB键盘驱动完成

USB设备端不管也不知道这个同步的问题。

使用特权

评论回复
11
computer00|  楼主 | 2008-8-23 10:22 | 只看该作者

不支持SET REPORT,我的是通过中断端点输出报告的

主机在检测到HID设备有中断输出端点时,将使用中断输出端点发送报告。
我在中断输出端点中读出数据,控制LED即可。主机会自动同步各块键盘之间的LED灯。

使用特权

评论回复
12
香水城| | 2008-8-23 11:32 | 只看该作者

哈哈,圈圈你这个就是SET REPORT

只不过这个SET REPORT走的是中断端点而不是控制端点。

使用特权

评论回复
13
computer00|  楼主 | 2008-8-23 12:07 | 只看该作者

不一样的,SET REPORT是通过端点0的控制传输,

而通过中断端点,则是中断传输,使用WriteFile API函数来操作的。

使用特权

评论回复
14
armmcu| | 2008-8-23 15:12 | 只看该作者

Re:

我这里的应用 一定要加SET REPORT 啊!  很佩服ST写USB 库的人,强大的数据结构和编程风格,我搞了一天 也没有把SET REPORT 加上,也搞过 LPC2300和C8051F326 的USB驱动 ,很是惭愧

使用特权

评论回复
15
computer00|  楼主 | 2008-8-24 11:42 | 只看该作者

应该是在usb_core.c里面修改吧,

有个void DataStageOut(void)函数,在函数末尾,增加对数据的处理:

#define SET_REPORT 0x09

if(pInformation->ControlState == WAIT_STATUS_IN) //如果数据已经接收完毕
{
 //则对数据处理
 if ((Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT))
           && RequestNo == SET_REPORT)  //如果是SET REPORT请求
 {
  //则处理SET REPORT请求
  
 }
}


前面的接收数据可能你还要自己改下,不知道他这个缓冲区是从哪里获取来的。最简单的办法就是自己开辟一个缓冲区来代替那里的Buffer来接收数据。

使用特权

评论回复
16
15113484470| | 2017-1-4 01:11 | 只看该作者
楼主,下载链接无效了

使用特权

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

本版积分规则

246

主题

14693

帖子

210

粉丝