关于stm32的USB学习笔记之USB_HW.c

来源:互联网 发布:vb int函数的使用方法 编辑:程序博客网 时间:2024/06/04 18:50

作者:findstr

转自:http://blog.csdn.net/findstr/article/details/7354391


[cpp] view plaincopyprint?
  1. #include <stm32f10x_lib.h>  
  2. #include <stm32f10x_map.h>  
  3. #include "usbreg.h"  
  4. #include "usbuser.h"  
  5. #include "usbcore.h"  
  6. #include "usb_hw.h"  
  7. #define _DEBUG_  
  8. #include "debug.h"  
  9.   
  10. #define USB_EP_NUM  4  
  11.   
  12. /*端点缓冲区的开始地址 
  13.  *因为每个缓冲块都需要一个端点描术表 
  14.  *而所有的端点描述表放在,USB缓冲区的首部 
  15.  *此地址是相对于USB缓冲区的地址,我认为加上Offset更好些 
  16.  *这里使用2个端点 
  17.  *端点0与端点1 
  18.  *此时EP_BUF_ADDR指向缓冲区的内容 
  19.  */  
  20. #define EP_BUF_ADDR (sizeof(EP_BUF_DSCR)*USB_EP_NUM)  
  21.   
  22. /*USB缓冲区首地址包括缓冲区描述表,绝对地址*/  
  23. EP_BUF_DSCR * pBUF_DSCR = (EP_BUF_DSCR *) USB_PMA_ADDR;  
  24.   
  25. /*端点空闲缓冲区地址  
  26.  *用于指示目前为止USB缓冲区中还没有分配的空闲地址的首地址*/  
  27. WORD    FreeBufAddr;  
  28.   
  29.       
  30. /*功能:用于初始化USB的时钟等部分 
  31.  *参数:无 
  32.  *返回值:无 
  33.  */  
  34. void USB_Init(void)  
  35. {  
  36.     printf("进入USB_Init,进行初始化\r\n");  
  37.     //使能USB时钟  
  38.     RCC->APB1ENR |= (1<<23);  
  39.   
  40.     //使能USB中断  
  41.     /*因为USB低优先级中断的中断号为20,而NVIC——IPRX 
  42.      *寄存器用四位来存储中断优先级,所以20/4=5 , 
  43.      *然后使能第20位中断*/  
  44.     NVIC->IPR[5] |=0x10;  
  45.     NVIC->ISER[0]|=(1<<20);  
  46. }  
  47. /*功能:用于复位USB模块     
  48.  *参数:无 
  49.  *返回值:无 
  50.  */  
  51. /*现在以我的水平还搞不懂双缓冲为何物,所以先不搞^-^*/  
  52. /*一些资料: 
  53.   *USB低优先级中断(通道20):可由所有USB事件触发(正确传输,USB复位等). 
  54.   *USB高优先级中断(通道19):仅能由同步和双缓冲批量传输事件触发,目的是保证最大的传输速率. 
  55.   *USB唤醒中断(通道42):由USB挂起模式的唤醒事件触发.  OTG_FS_WKUP唤醒 
  56.   * 
  57.   *复位要执行的内容可以参见rm0008 21.4.2节 
  58.   */  
  59. void USB_Reset(void)  
  60. {  
  61.     PrintS("USB_Reset\r\n");  
  62.     /*复位了嘛,那所有以前产生的中断都没有用了,清了完事!*/  
  63.     ISTR=0;  
  64.   
  65.     /*通过设置CNTR来控制stm32的USB模块的工作方式 
  66.      *所有的USB事件中断都是在低优先级中断(通道20)上处理的 
  67.      *好吧就先使能这么多吧,先跑起来再说! 
  68.      */  
  69.     CNTR=   CNTR_CTRM       |   // 使能正确传输中断  
  70.             CNTR_RESETM     |   //使能复位中断  
  71.             CNTR_SUSPM      |   //使能挂起中断  
  72.             CNTR_WKUPM      ;   //使能唤醒中断  
  73.       
  74.     FreeBufAddr =   EP_BUF_ADDR; //此时FreeBuff指向第一个缓冲区首地址(不包括描述符表),相对地址  
  75.   
  76.     BTABLE  = 0x00;              //设置缓冲区描述表的位置仍是相对地址  
  77.       
  78.     /*为端点0设置缓冲区及各种控制位*/  
  79.     pBUF_DSCR->ADDR_TX   =   FreeBufAddr;  
  80.     FreeBufAddr+=8;     //端点0设置为8个字节,一般控制数据为8个字节  
  81.     pBUF_DSCR->ADDR_RX   =   FreeBufAddr;  
  82.     FreeBufAddr+=8;  
  83.     /*在count_Rx字段中10~14bit用来表示缓冲区字节的快数 
  84.      *而15bit用来表示块的大小 
  85.      *0---2byte 
  86.      *1---1byte 
  87.      *我们这里使用了8个字节,bit15为0,所以应该((8<<10)>>1)即8<<9; 
  88.      *至于count_Rx我们在发送时再来赋值 
  89.      */  
  90.     pBUF_DSCR->COUNT_RX= 8 << 9;     
  91.     /*设置端点0为控制端点,接收缓冲区有效 
  92.      *低四位代表端点地址 
  93.      */  
  94.     EPxREG(0) = EP_CONTROL | EP_RX_VALID;  
  95.   
  96.     /*使能USB模块,并设置USB地址为0,以响应枚举*/  
  97.     DADDR = DADDR_EF | 0;  
  98. }  
  99.   
  100. /*功能:复位一个端点 
  101.  *参数:   端点号 
  102.  *              EPNum:bit3~bit0 ----> 端点号 
  103.  *              EPNum:bit7      ----> 端点方向 
  104.  * 
  105.  *返回值:无 
  106.  */  
  107.  /*其实就是将下一个要发送的数据包变成DATA0*/  
  108.  void   EP_Reset(DWORD  EPNum)  
  109.  {  
  110.     DWORD   num,var;  
  111.     PrintS("EP_Reset\r\n");  
  112.     /*获得端点号,低四位为端点号*/  
  113.     num = EPNum & 0x0F;  
  114.     var = EPxREG(num);  
  115.     /*如果bit7为1则将此端点的发送toggle置为0, 
  116.      *否则将此端点的接收toggle置为0 
  117.      *因为数据总是从data0数据包开始发送的 
  118.      */  
  119.      if(EPNum & 0x80)  
  120.         EPxREG(num) = var & (EP_MASK | EP_DTOG_TX);/*输入端点*/  
  121.      else  
  122.         EPxREG(num) = var & (EP_MASK | EP_DTOG_RX);/*输出端点*/  
  123.        
  124.  }  
  125.  /*功能:连接或断开USB功能 
  126.   *参数:true -->连接USB 
  127.   *      false-->关闭USB 
  128.   *返回值:无 
  129.   */  
  130. void USB_Connect(BOOL turnon)  
  131. {  
  132.     /*需要注意一点的事,所有的USB寄存器尽量用=而不要用与或非 
  133.      *在编程手册上有明确表明,这样可能会导至出一些问题*/  
  134.     printf("进入连接USB程序\r\n");  
  135.      /*将USB强制复位*/  
  136.     CNTR = CNTR_FRES;  
  137. //  printf("test1\r\n");  
  138.     /*因为刚连接所以应该跟才启动一样,将所有没有处理的中断给清理掉*/  
  139.     ISTR=0;  
  140. //  printf("test2\r\n");  
  141.     if(turnon)  
  142.     {  
  143. //      printf("test3\r\n");  
  144.         /*使能GPIOA,然后将PA.8置低,使USB 
  145.          *的D+加1.5K上接电阻,使USB集线器识别了高速设备 
  146.          *这样才能让USB识别 
  147.          */  
  148.           RCC->APB2ENR |= (1 << 2);                     /* enable clock for GPIOA */  
  149.           GPIOA->CRH &= ~(0x0f << 0 * 4);                /* clear port PA8 */  
  150.           GPIOA->CRH |=  (0x03 << 0 * 4);                /* PA6 General purpose output open-drain, max speed 50 MHz */  
  151.           GPIOA->BRR  =  (   1 << 8    );                /* reset PA8  (set to low) */       
  152.         /*经过调试发现,这个语句的本意是:复位USB模块 
  153.          *然后在此使能CNTR_RESETM即复位中断标志 
  154.          *至于端点0的初始化留在USB低优先级中进行处理 
  155.          *当然,我们也只开了低优先级中断^_^!*/  
  156.           CNTR = CNTR_RESETM;    /*此处只使能了复位中断,*/       
  157.     }  
  158.     else  
  159.         CNTR = CNTR_FRES | CNTR_PDWN ;/*复位并关闭*/  
  160.   
  161. }  
  162. /*功能:设置端点状态 
  163.  *参数:EPnum --->端点号 
  164.  *     stat  --->要设置的状态值 
  165.  *返回值:无 
  166.  */  
  167.  void USB_ConfigEP (USB_ENDPOINT_DESCRIPTOR * pEPD)  
  168.  {  
  169.     DWORD num,val;  
  170.       
  171.     //取得端点号  
  172.     num = pEPD->bEndpointAddress & 0xf;  
  173.   
  174.     val = pEPD->wMaxPacketSize;  
  175.     //如果是IN端点  
  176.     if(pEPD->bEndpointAddress & USB_ENDPOINT_DIRECTION_MASK)  
  177.     {  
  178.         //此处我只想说pBUF_DSCR是指针,剩下的就是语法问题了  
  179.         (pBUF_DSCR + num)->ADDR_TX = FreeBufAddr;  
  180.         /*取2的倍数,因为缓冲区都是字对齐的,注意此处如果大于1023会出现浪费现象 
  181.          *因为USB_COUNTn_TX只能接收bit0~bit9 
  182.          */  
  183.         val = (val + 1)& ~1;  
  184.     }  
  185.     //输出端点  
  186.     else  
  187.     {  
  188.         (pBUF_DSCR + num)->ADDR_RX = FreeBufAddr;  
  189.         /*因为USB_COUNTn_RX中存储只用bit10~bit14,如果BLSIZE=0(即块大小为2字节),那么只能是0~62个字节 
  190.          *所以如果大于62,则应将块大小设置为BLSIZE=1(即32个字节) 
  191.          */  
  192.         if(val > 62  )  
  193.         {  
  194.             //块大小为32,则大小应该为32的倍数  
  195.             val = (val +31)&~31;  
  196.             /*关于此计算公式,参见rm0008,21,5.3节 
  197.              *(val >> 5)<<10 == val <<5 
  198.              */  
  199.             (pBUF_DSCR + num)->COUNT_RX = ((val << 5)-1) | 0x8000;  
  200.         }  
  201.         else  
  202.         {  
  203.             val = (val + 1) & ~1;  
  204.             (pBUF_DSCR + num)->COUNT_RX = val << 9;  
  205.         }  
  206.     }  
  207.     //修正空闲区域的起始地址  
  208.     FreeBufAddr += val ;  
  209.   
  210.     switch(pEPD->bmAttributes & USB_ENDPOINT_TYPE_MASK)  
  211.     {  
  212.         //控制端点  
  213.         case USB_ENDPOINT_TYPE_CONTROL:  
  214.             val = EP_CONTROL;  
  215.             break;  
  216.         //同步端点  
  217.         case USB_ENDPOINT_TYPE_ISOCHRONOUS:  
  218.             val = EP_ISOCHRONOUS;  
  219.             break;  
  220.         //块传输  
  221.         case USB_ENDPOINT_TYPE_INTERRUPT:  
  222.             val = EP_INTERRUPT;  
  223.             break;  
  224.         default:  
  225.             printf("出错了,未识别的端点类型\r\n");  
  226.             break;  
  227.     }  
  228.     val |= num;  
  229.     //设置端点寄存器  
  230.     EPxREG(num) = val;  
  231.  }  
  232. /*功能:设置端点状态 
  233.  *参数:EPnum --->端点号 
  234.  *     stat  --->要设置的状态值 
  235.  *返回值:无 
  236.  */  
  237.  void EP_Status(DWORD EPNum,DWORD stat)  
  238.  {  
  239.     DWORD num,var;  
  240.       
  241.     /*取得端点号*/  
  242.     num = EPNum & 0x0f;  
  243.     var = EPxREG(num);  
  244.     /*此处用了一点小技巧,因为端点寄存器是写1反转,所以想设置相应的值只有使用异或*/  
  245.     if(EPNum & 0x80)    //输入端点  
  246.         EPxREG(num)=(var ^ (stat & EP_STAT_TX)) & (EP_MASK | EP_STAT_TX);  
  247.     else                //输出端点  
  248.         EPxREG(num)=(var ^ (stat & EP_STAT_RX)) & (EP_MASK | EP_STAT_RX);  
  249.   
  250.  }  
  251. /*功能:复位端点 
  252.  *参数:EPNum  bit0~bit3为端点号 
  253.                 bit7     为端点方向 
  254.  *返回值:无 
  255.  */  
  256. void USB_ResetEP(DWORD EPNum)  
  257. {  
  258.     EP_Reset(EPNum);  
  259. }  
  260. /*功能:设置端点为stall 
  261.  *参数:EPNum  bit0~bit3为端点号 
  262.                 bit7     为端点方向 
  263.  *返回值:无 
  264.  */  
  265. void USB_SetStallEP(DWORD   EPNum)  
  266. {  
  267.     EP_Status(EPNum,EP_TX_STALL | EP_RX_STALL);  
  268. }  
  269. /*功能:设置端点为enable 
  270.  *参数:EPNum  bit0~bit3为端点号 
  271.                 bit7     为端点方向 
  272.  *返回值:无 
  273.  */  
  274. void USB_EnableEP(DWORD EPNum)  
  275. {  
  276.     EP_Status(EPNum,EP_TX_VALID | EP_RX_VALID);  
  277. }  
  278.   
  279. /*功能:设置端点为disable 
  280.  *参数:EPNum  bit0~bit3为端点号 
  281.                 bit7     为端点方向 
  282.  *返回值:无 
  283.  */  
  284. void USB_DisableEP(DWORD EPNum)  
  285. {  
  286.     EP_Status(EPNum,EP_TX_DIS | EP_RX_DIS);  
  287. }  
  288.   
  289. /*功能:清除端点的stall状态 
  290.  *参数:EPNum  bit0~bit3为端点号 
  291.                 bit7     为端点方向 
  292.  *返回值:无 
  293.  */  
  294. void USB_ClrStallEP(DWORD   EPNum)  
  295. {  
  296.     EP_Status(EPNum,EP_TX_VALID | EP_RX_VALID);  
  297. }  
  298. /*功能:设置USB地址 
  299.  *参数:addr要设置的地址 
  300.  *返回值:无 
  301.  */  
  302. void USB_SetAddress(DWORD   addr)  
  303. {  
  304.     //DADDR的高1位是用来使能USB模块的  
  305.     DADDR = DADDR_EF | addr;  
  306. }  
  307. /*功能:用于读端点缓冲区 
  308.  *参数:EPnum --->端点号 
  309.  *     pData --->用于接收从端点缓冲区中读到的数据 
  310.  *      此函数有些问题,应该加入一个参数显示缓冲区有多大 
  311.  *返回值:读到的字节数 
  312.  */  
  313. DWORD USB_ReadEP(DWORD EPnum,BYTE * pData)  
  314. {  
  315.   
  316.     DWORD num,cnt,*pv,n;  
  317.       
  318.     //得到端点号      
  319.     num = EPnum & 0xf;  
  320.   
  321.     //取得些端点的缓冲区地址  
  322.     pv=(DWORD *)(USB_PMA_ADDR + 2* ((pBUF_DSCR + num)->ADDR_RX));  
  323.     //COUNT_RX低10位存放的是缓冲区的数据  
  324.     cnt=(pBUF_DSCR + num)->COUNT_RX & EP_COUNT_MASK;  
  325.     for(n=0;n<(cnt+1)/2;n++)  
  326.     {  
  327.         /*pakced关键字用于单字节对齐,这在USB数据结构中的结构体中尤为重要 
  328.          *因为stm32访问时使用32位,而USB访问时使用16位,所以pData为WORD,而 
  329.          *pv为DWORD型指针 
  330.          */  
  331.         *((__packed WORD *)pData)=*pv++;  
  332.         /*这里pData为单字节指针所以,还是加2而不是加4*/  
  333.         pData+=2;  
  334.     }  
  335.     /*OK,现在我们的端点又可以接收数据了,设置为VALID*/  
  336.     EP_Status (EPnum,EP_RX_VALID);  
  337.     return cnt;  
  338. }  
  339.   
  340. /*功能:用于写端点缓冲区 
  341.  *参数:EPNum --->端点号 
  342.  *     pData --->指向要发送的数据缓冲区 
  343.  *     cnt   --->要写入的字节数 
  344.  *返回值:写入的字节数 
  345.  */  
  346.  DWORD USB_WriteEP(DWORD EPNum , BYTE * pData,DWORD cnt)  
  347.  {  
  348.     DWORD num,*pv,n;  
  349.   
  350.     num = EPNum & 0x0f;  
  351.   
  352.     pv=(DWORD *)(USB_PMA_ADDR + 2*((pBUF_DSCR+num)->ADDR_TX));  
  353.     /*此处应该判断要写入的数据是否超量了,可能会产一个隐藏bug*/  
  354.     for(n=0;n<(cnt + 1)/2;n++)  
  355.     {  
  356.         *pv++=*((__packed WORD*)pData);  
  357.         pData+=2;  
  358.     }  
  359.     //OK,现在USB发送缓冲区中已经有东西了,可以响应主机了  
  360.     (pBUF_DSCR+num)->COUNT_TX=cnt;  
  361.     EP_Status(EPNum,EP_TX_VALID);  
  362.     return  cnt;  
  363.  }  
  364.   
  365. /*功能:USB挂起 
  366.  *参数:无 
  367.  *返回值:无 
  368.  */  
  369. void USB_Suspend(void)  
  370. {  
  371.     GPIOE->BSRR |=1 ;            /* Turn Off Suspend LED */  
  372.     printf("进入挂起中断\r\n");  
  373.     //强制挂起  
  374.     CNTR |= CNTR_FSUSP;   
  375.     //进入低功耗模式  
  376.     CNTR |= CNTR_LPMODE;  
  377. }  
  378.   
  379. /*功能:USB唤醒 
  380.  *参数:无 
  381.  *返回值:无 
  382.  */  
  383. void USB_WakeUp(void)  
  384. {  
  385.     GPIOE->BRR |=1 ;            /* Turn On Suspend LED */  
  386.     printf("进入唤醒中断\r\n");  
  387.     //唤醒了,当然得把这一位给清了  
  388.     CNTR &=  ~CNTR_FSUSP;     
  389.     //USB的唤醒事件会复位此位,我们这里不用管  
  390.     //CNTR &= ~CNTR_LPMODE;  
  391. }  
  392. /*功能:USB低优先级中断服务程序 
  393.  *参数:无 
  394.  *返回值:无 
  395.  */  
  396.  void USB_LP_CAN_RX0_IRQHandler(void)  
  397.  {  
  398.     DWORD   istr;  
  399.     DWORD   num,var;  
  400.   
  401.     istr=ISTR;  //取得中断标志位  
  402.     /*USB复位中断的处理*/  
  403.     if(istr & ISTR_RESET)  
  404.     {  
  405.         //复位设备  
  406.         USB_Reset();  
  407.         //复位与USB协议有关的数据  
  408.         USB_ResetCore();  
  409.         ISTR = ~ISTR_RESET; /*已经处理完复位中断了,清楚复位中断标志*/  
  410.     }  
  411.     /*USB挂起中断*/  
  412. /*  if(istr &  
  413. ******************************************************************** 
  414. */  
  415.   
  416.   if (istr & ISTR_SUSP) {  
  417.     USB_Suspend();  
  418.     ISTR = ~ISTR_SUSP;  
  419.   }  
  420.   
  421.   /* USB Wakeup */  
  422.   if (istr & ISTR_WKUP) {  
  423.     USB_WakeUp();  
  424.     ISTR = ~ISTR_WKUP;  
  425.   }  
  426.   
  427. //******************************************************************  
  428.     /*端点接中发送中断处理*/  
  429.     while((istr = ISTR) & ISTR_CTR)   
  430.     {  
  431.         //清楚中断标志位  
  432.         ISTR = ~ISTR_CTR;  
  433.         //取得发生中断的端点号  
  434.         num=istr & ISTR_EP_ID;  
  435.         //取得端点寄存器的值       
  436.         var = EPxREG(num);  
  437.         //正确的接收传输  
  438.         if(var & EP_CTR_RX )  
  439.         {  
  440.             //清0正确接收标志位  
  441.         //  printf("端点号为:");  
  442.         //  printhex((u8)num);  
  443.             EPxREG(num) = var & ~EP_CTR_RX & EP_MASK;  
  444.             //调用相应的端点进行处理  
  445.             if(USB_P_EP[num])  
  446.             {  
  447.                 //如果是控制传输,则使有USG_EVT_SETUP  
  448.                 if(var & EP_SETUP)  
  449.                     USB_P_EP[num](USB_EVT_SETUP);  
  450.                 else  
  451.                     //否则就是普通的输出包  
  452.                     USB_P_EP[num](USB_EVT_OUT);  
  453.             }  
  454.         }  
  455.         //产生的是发送成功中断  
  456.         if(var & EP_CTR_TX)  
  457.         {  
  458.             //清楚中断标志位  
  459.             EPxREG(num) = var & ~EP_CTR_TX & EP_MASK;  
  460.             //调用对应的中断函数进行处理  
  461.             if(USB_P_EP[num])  
  462.             {  
  463.                 //USB发送成功  
  464.                 USB_P_EP[num](USB_EVT_IN);    
  465.             }  
  466.         }  
  467.   
  468.     }  
  469.   
  470.  }  
如有错误还请指正!!!

原创粉丝点击