原创 STM32学习笔记USB HID ―STM32 USB HID固件学习分析

2009-8-16 17:38 10201 8 8 分类: MCU/ 嵌入式
先来个广告:试验板子
http://item.taobao.com/auction/item_detail.jhtml?item_id=af46787150a8e26f29546dccfbed3ab6&x_id=0db2
点击看大图


STM32学习笔记USB HID


―――――STM32 USB HID固件学习分析


1.系统复位和上电复位


发生系统复位或者上电复位时,应用程序首先需要做的是提供USB 模块所需要的


时钟信号,然后清除复位信号,使程序可以访问USB 模块的寄存器。复位之后的


初始化流程如下所述:


首先,由应用程序激活寄存器单元的时钟,再配置设备时钟管理逻辑单元的相关


控制位,清除复位信号.


void USB_Init (void) {


 


  RCC->APB1ENR |= (1 << 23);                /* enable clock for USB时钟信号*/


  /* Enable USB Interrupts */


  NVIC->IPR [5] |= 0x00000010;              /* set priority lower than SVC */


  NVIC->ISER[0] |= (1 << (USB_LP_CAN_RX0_IRQChannel & 0x1F));


 


  /* Control USB connecting via SW */


  RCC->APB2ENR |= (1 << 5);                 /* enable clock for GPIOD */


}


2. 开启模拟单元,使USB处于复位状态,直到模拟但愿准备完毕


    其次,必须配置CNTR 寄存器的PDWN 位用以开启USB 收发器相关的模拟部


分,程序体现CNTR = CNTR_FRES;模拟单元准备完毕后,程序复位撤销,即一次复位操作。然后使能复位中断,那么主机将会检测到USB设备的复位,给出复位事件给USB设备。


void USB_Connect (BOOL con) {


 


 //==============================================================


  GPIO_InitTypeDef m_GPIO_InitTypeDef;


  m_GPIO_InitTypeDef.GPIO_Pin   = GPIO_Pin_2;


  m_GPIO_InitTypeDef.GPIO_Mode  = GPIO_Mode_Out_PP;//推挽输出


  m_GPIO_InitTypeDef.GPIO_Speed = GPIO_Speed_2MHz;//速度2M 


  GPIO_Init(GPIOD,&m_GPIO_InitTypeDef);


  //===============================================================


  //usb处于复位状态,让USB模拟电路准备


  CNTR = CNTR_FRES;                         /* Force USB Reset */


 


  ISTR = 0;                                 /* Clear Interrupt Status */


  if (con)   //:连接USB


  {//清除USB复位信号,向USB主机发送"恢复请求",并且使能复位中断请求


   //则当USB主机向本机发送“恢复请求”后,相应中断,并调用USB_Reset函数.


   //说明:如果此操作在115ms有效,USB主机将对USB模块发出"唤醒操作"


    CNTR = CNTR_RESETM;                     /* USB Reset Interrupt Mask */


    GPIO_SetBits(GPIOD,GPIO_Pin_2);


   


  }


  else     //:断开USB


  {//清除USB复位信号,关闭USB电源


    CNTR = CNTR_FRES | CNTR_PDWN;           /* Switch Off USB Device */


    GPIO_ResetBits(GPIOD,GPIO_Pin_2);


  }


}


3USB主机发送给USB device复位事件,STM32的复位事件


 void USB_Reset (void) {


/* Double Buffering is not yet supported              */


 


  ISTR = 0;                                 /* Clear Interrupt Status */


  //:CNTR_RESETM在使能连接时被中断使能


  //:CNTR_CTRM此处添加(正确传输中断使能==>某个端点成功完成一次传输.)


//:下一次事件中断将检测CNTR_CTRM中断


  //  究竟是哪个端点成功传输(ISTR:EP_ID[3:0]),数据方向(ISTR:DIR)


  CNTR = CNTR_CTRM | CNTR_RESETM |


         (USB_SUSPEND_EVENT ? CNTR_SUSPM   : 0) |


         (USB_WAKEUP_EVENT  ? CNTR_WKUPM   : 0) |


         (USB_ERROR_EVENT   ? CNTR_ERRM    : 0) |


         (USB_ERROR_EVENT   ? CNTR_PMAOVRM : 0) |


         (USB_SOF_EVENT     ? CNTR_SOFM    : 0) |


         (USB_SOF_EVENT     ? CNTR_ESOFM   : 0);


 


  FreeBufAddr = EP_BUF_ADDR;                //自由缓冲区地址


  BTABLE = 0x00;                            /* set BTABLE Address */


 


  /* Setup Control Endpoint 0 */


 


  pBUF_DSCR->ADDR_TX = FreeBufAddr;        //:


  FreeBufAddr += USB_MAX_PACKET0;     //:


  pBUF_DSCR->ADDR_RX = FreeBufAddr;        //:


  FreeBufAddr += USB_MAX_PACKET0;     //:


  if (USB_MAX_PACKET0 > 62)


  {


    pBUF_DSCR->COUNT_RX = ((USB_MAX_PACKET0 << 5) - 1) | 0x8000;


  }


  else


  {


    pBUF_DSCR->COUNT_RX =   USB_MAX_PACKET0 << 9;


  }


 


  //配置端点0


  //1   EP_CONTROL(控制端点) bit[10:9](EP_TYPE[1:0])


  //2:  EP_RX_VALID(数据接受状态位)bit[13:12](STAT_RX[1:0])       ==>端点可用于接受


  EPxREG(0) = EP_CONTROL | EP_RX_VALID;    


 


  //设置USB地址为0


  DADDR = DADDR_EF | 0;                     /* Enable USB Default Address */


}


4.  STM32的中断函数


  void USB_LP_CAN_RX0_IRQHandler (void)


{


  DWORD istr, num, val;


  istr = ISTR;


/*==========USB Reset Request=============*/


  if (istr & ISTR_RESET)


  {


    USB_Reset();


    #if USB_RESET_EVENT


       USB_Reset_Event();


    #endif


    ISTR = ~ISTR_RESET;


       #ifdef Debug_Uart


       printf("USB_RESET_EVENT\n");


       #endif


  }


//……………略过一些程序………………


/*==========EndpointInterrupts===========================================*/


  while ((istr = ISTR) & ISTR_CTR)


  {//istr = ISTR:表示不是前面用到的中断


   //:CNTR_CTRM表示某个端点成功完成一次传输.在复位中使能此中断


//:第一次进入这个中断后,开始枚举,枚举一般都是端点0


//USB_EVT_INUSB_EVT_SETUP事件。USB_EVT_INUSB_EVT_SETUP事件需要//返回相应数据而使用的。枚举事务几乎都是USB_EVT_SETUP事务。


  //  究竟是哪个端点成功传输(ISTR:EP_ID[3:0]),数据方向(ISTR:DIR)


    ISTR = ~ISTR_CTR;


    num = istr & ISTR_EP_ID;  //num端点号


//========================


//#define EPxREG(x)       REG(USB_BASE_ADDR + 4*(x))   /* EndPoint Registers */


//#define REG(x)          (*((volatile unsigned int *)(x)))


//:通过前面两句知道val = EPxREG(num);就是读取端点num寄存器的值。


    val = EPxREG(num);


    if (val & EP_CTR_RX)


       {//:端口num  EP_CTR_RX正确接受标志


       //bit[10:9]=EP_TYPE[1:0]:端点类型


      EPxREG(num) = val & ~EP_CTR_RX & EP_MASK;     //清除相关标志位


      if (USB_P_EP[num])


         {


        if (val & EP_SETUP)


              {//如果使SETUP             


          USB_P_EP[num](USB_EVT_SETUP);


        }


              else


              {//否则为OUT


          USB_P_EP[num](USB_EVT_OUT);


        }


      }


    }


    if (val & EP_CTR_TX)


       {//::端口num  EP_CTR_TX正确发送标志


      EPxREG(num) = val & ~EP_CTR_TX & EP_MASK;


      if (USB_P_EP[num])


         {//IN


        USB_P_EP[num](USB_EVT_IN);


      }


    }


  }


}


5.枚举过程分析。


   51 说明:枚举使用端点0,在void USB_EndPoint0 (DWORD event)函数处理。


        STM32USB枚举串口调试打印输出:


        USB_RESET_EVENT


        USB_RESET_EVENT


        USB_Suspend


        USB_RESET_EVENT


        USB_WakeUp


        USB_EVT_SETUP


        ...REQUEST_STANDARD


        ......USB_REQUEST_GET_DESCRIPTOR


        USB_EVT_IN


        USB_RESET_EVENT


        USB_EVT_SETUP


        ...REQUEST_STANDARD


        ......USB_REQUEST_SET_ADDRESS


        USB_EVT_SETUP


        ...REQUEST_STANDARD


        ......USB_REQUEST_GET_DESCRIPTOR


        USB_EVT_IN


        USB_EVT_IN


        USB_EVT_IN


        USB_EVT_SETUP


        ...REQUEST_STANDARD


        ......USB_REQUEST_GET_DESCRIPTOR


        USB_EVT_IN


        USB_EVT_IN


        USB_EVT_SETUP


        ...REQUEST_STANDARD


        ......USB_REQUEST_GET_DESCRIPTOR


        USB_EVT_IN


        USB_EVT_RUUSBEUUUSB_EVT


        UUSBUUSUSBCUUSB.USUSB_EVT_SETUP


        ...REQUEST_STUUUSB.SUSB_PUNUSB__USB_EVT_SETUP


        ...REQUEST_CLASS


        ......REQUEST_TO_INTERFACE


        USB_EVT_SETUP


        ...REQUEST_STANDARD


        ......USB_REQUEST_GET_DESCRIPTOR


        USB_EVUE


  52 端点0 USB_EVT_SETUP事件函数分析说明


       5.2.1 void USB_EndPoint0 (DWORD event)程序结构:


<!--[if !vml]-->点击看大图
<!--[endif]-->


        在枚举时,USB_EVT_OUT包没有使用,只使用了


USB_EVT_INUSB_EVT_SETUP


5.2.2  USB_EVT_IN分析


<!--[if !vml]-->点击看大图
<!--[endif]-->


可查看函数USB_DataInStage()功能为传送通道EndPoint0的数据。在USB_EVT_IN事件中只需要传输在枚举时需要的数据到USB主机,需要传输的数据都时在枚举事务中与USB主机商量好的长度。


       5.2.3 USB_EVT_SETUP分析 


           A.


<!--[if !vml]-->点击看大图
<!--[endif]-->


分析:USB_SetupStage()函数为将端点0的数据读到SetupPacket变量。SetupPacket定义格式如下:


typedef __packed struct _USB_SETUP_PACKET {


  REQUEST_TYPE   bmRequestType;


  BYTE            bRequest;


  WORD_BYTE     wValue;


  WORD_BYTE     wIndex;


  WORD           wLength;


} USB_SETUP_PACKET;


可以看到这为一个USB总线Setup包格式,具体可参照USB协议第9章。


SetupPacket.wLength这个域表明第二阶段的数据传输长度。传输方向由bmRequstType域的Direction位指出。wLength域为0则表明无数据传输。在输入请求下,设备返回的数据长度不应多于wLength,但可以少于。在输出请求下,wLength指出主机发出的确切数据量。如果主机发送多于wLength的数据,设备做出的响应是无定义的。


比如响应一个GET_DESCRIPTORsetup包,则下一阶段需要传回


GET_DESCRIPTOR的内容,而这个内容时固定的,这时长度就为


SetupPacket.wLengt说明下一个阶段为GET_DESCRIPTOR数据传入USB主机(一个USB_EVT_IN事务实现),那在GET_DESCRIPTOR的内容的地址给EP0Data的地址(后面说明会指明在哪儿),而长度在这儿指定。则下一个IN事务直接传输这个EP0Data的内容,直到将EP0Data.Count数据传输完毕。


B.下面内容均可参考USB协议第9章内容(将已附件格式打包)


<!--[if !vml]-->点击看大图
<!--[endif]-->


分析:bmRequestType(D6..5: 种类)00=标准请求:用于枚举


                              01=  :指定类 OUT/IN事务


                              02=厂商


C.由于在枚举时几乎都是用标准请求的。所以只是分析REQUEST_STANDARD请求


点击看大图


点击看大图


<!--[if !vml]-->
<!--[endif]-->


下面为第9章的标准请求列表,和上面吻合。


<!--[if !vml]-->点击看大图


USB协议第9章:


rar


文档说明的PDFrar


keil程序
<!--[endif]-->

PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
8
关闭 站长推荐上一条 /3 下一条