作者:findstr
转自:http://blog.csdn.net/findstr/article/details/7354391
- #include <stm32f10x_lib.h>
- #include <stm32f10x_map.h>
- #include "usbreg.h"
- #include "usbuser.h"
- #include "usbcore.h"
- #include "usb_hw.h"
- #define _DEBUG_
- #include "debug.h"
- #define USB_EP_NUM 4
- /*端点缓冲区的开始地址
- *因为每个缓冲块都需要一个端点描术表
- *而所有的端点描述表放在,USB缓冲区的首部
- *此地址是相对于USB缓冲区的地址,我认为加上Offset更好些
- *这里使用2个端点
- *端点0与端点1
- *此时EP_BUF_ADDR指向缓冲区的内容
- */
- #define EP_BUF_ADDR (sizeof(EP_BUF_DSCR)*USB_EP_NUM)
- /*USB缓冲区首地址包括缓冲区描述表,绝对地址*/
- EP_BUF_DSCR * pBUF_DSCR = (EP_BUF_DSCR *) USB_PMA_ADDR;
- /*端点空闲缓冲区地址
- *用于指示目前为止USB缓冲区中还没有分配的空闲地址的首地址*/
- WORD FreeBufAddr;
- /*功能:用于初始化USB的时钟等部分
- *参数:无
- *返回值:无
- */
- void USB_Init(void)
- {
- printf("进入USB_Init,进行初始化\r\n");
- //使能USB时钟
- RCC->APB1ENR |= (1<<23);
- //使能USB中断
- /*因为USB低优先级中断的中断号为20,而NVIC——IPRX
- *寄存器用四位来存储中断优先级,所以20/4=5 ,
- *然后使能第20位中断*/
- NVIC->IPR[5] |=0x10;
- NVIC->ISER[0]|=(1<<20);
- }
- /*功能:用于复位USB模块
- *参数:无
- *返回值:无
- */
- /*现在以我的水平还搞不懂双缓冲为何物,所以先不搞^-^*/
- /*一些资料:
- *USB低优先级中断(通道20):可由所有USB事件触发(正确传输,USB复位等).
- *USB高优先级中断(通道19):仅能由同步和双缓冲批量传输事件触发,目的是保证最大的传输速率.
- *USB唤醒中断(通道42):由USB挂起模式的唤醒事件触发. OTG_FS_WKUP唤醒
- *
- *复位要执行的内容可以参见rm0008 21.4.2节
- */
- void USB_Reset(void)
- {
- PrintS("USB_Reset\r\n");
- /*复位了嘛,那所有以前产生的中断都没有用了,清了完事!*/
- ISTR=0;
- /*通过设置CNTR来控制stm32的USB模块的工作方式
- *所有的USB事件中断都是在低优先级中断(通道20)上处理的
- *好吧就先使能这么多吧,先跑起来再说!
- */
- CNTR= CNTR_CTRM | // 使能正确传输中断
- CNTR_RESETM | //使能复位中断
- CNTR_SUSPM | //使能挂起中断
- CNTR_WKUPM ; //使能唤醒中断
- FreeBufAddr = EP_BUF_ADDR; //此时FreeBuff指向第一个缓冲区首地址(不包括描述符表),相对地址
- BTABLE = 0x00; //设置缓冲区描述表的位置仍是相对地址
- /*为端点0设置缓冲区及各种控制位*/
- pBUF_DSCR->ADDR_TX = FreeBufAddr;
- FreeBufAddr+=8; //端点0设置为8个字节,一般控制数据为8个字节
- pBUF_DSCR->ADDR_RX = FreeBufAddr;
- FreeBufAddr+=8;
- /*在count_Rx字段中10~14bit用来表示缓冲区字节的快数
- *而15bit用来表示块的大小
- *0---2byte
- *1---1byte
- *我们这里使用了8个字节,bit15为0,所以应该((8<<10)>>1)即8<<9;
- *至于count_Rx我们在发送时再来赋值
- */
- pBUF_DSCR->COUNT_RX= 8 << 9;
- /*设置端点0为控制端点,接收缓冲区有效
- *低四位代表端点地址
- */
- EPxREG(0) = EP_CONTROL | EP_RX_VALID;
- /*使能USB模块,并设置USB地址为0,以响应枚举*/
- DADDR = DADDR_EF | 0;
- }
- /*功能:复位一个端点
- *参数: 端点号
- * EPNum:bit3~bit0 ----> 端点号
- * EPNum:bit7 ----> 端点方向
- *
- *返回值:无
- */
- /*其实就是将下一个要发送的数据包变成DATA0*/
- void EP_Reset(DWORD EPNum)
- {
- DWORD num,var;
- PrintS("EP_Reset\r\n");
- /*获得端点号,低四位为端点号*/
- num = EPNum & 0x0F;
- var = EPxREG(num);
- /*如果bit7为1则将此端点的发送toggle置为0,
- *否则将此端点的接收toggle置为0
- *因为数据总是从data0数据包开始发送的
- */
- if(EPNum & 0x80)
- EPxREG(num) = var & (EP_MASK | EP_DTOG_TX);/*输入端点*/
- else
- EPxREG(num) = var & (EP_MASK | EP_DTOG_RX);/*输出端点*/
- }
- /*功能:连接或断开USB功能
- *参数:true -->连接USB
- * false-->关闭USB
- *返回值:无
- */
- void USB_Connect(BOOL turnon)
- {
- /*需要注意一点的事,所有的USB寄存器尽量用=而不要用与或非
- *在编程手册上有明确表明,这样可能会导至出一些问题*/
- printf("进入连接USB程序\r\n");
- /*将USB强制复位*/
- CNTR = CNTR_FRES;
- // printf("test1\r\n");
- /*因为刚连接所以应该跟才启动一样,将所有没有处理的中断给清理掉*/
- ISTR=0;
- // printf("test2\r\n");
- if(turnon)
- {
- // printf("test3\r\n");
- /*使能GPIOA,然后将PA.8置低,使USB
- *的D+加1.5K上接电阻,使USB集线器识别了高速设备
- *这样才能让USB识别
- */
- RCC->APB2ENR |= (1 << 2); /* enable clock for GPIOA */
- GPIOA->CRH &= ~(0x0f << 0 * 4); /* clear port PA8 */
- GPIOA->CRH |= (0x03 << 0 * 4); /* PA6 General purpose output open-drain, max speed 50 MHz */
- GPIOA->BRR = ( 1 << 8 ); /* reset PA8 (set to low) */
- /*经过调试发现,这个语句的本意是:复位USB模块
- *然后在此使能CNTR_RESETM即复位中断标志
- *至于端点0的初始化留在USB低优先级中进行处理
- *当然,我们也只开了低优先级中断^_^!*/
- CNTR = CNTR_RESETM; /*此处只使能了复位中断,*/
- }
- else
- CNTR = CNTR_FRES | CNTR_PDWN ;/*复位并关闭*/
- }
- /*功能:设置端点状态
- *参数:EPnum --->端点号
- * stat --->要设置的状态值
- *返回值:无
- */
- void USB_ConfigEP (USB_ENDPOINT_DESCRIPTOR * pEPD)
- {
- DWORD num,val;
- //取得端点号
- num = pEPD->bEndpointAddress & 0xf;
- val = pEPD->wMaxPacketSize;
- //如果是IN端点
- if(pEPD->bEndpointAddress & USB_ENDPOINT_DIRECTION_MASK)
- {
- //此处我只想说pBUF_DSCR是指针,剩下的就是语法问题了
- (pBUF_DSCR + num)->ADDR_TX = FreeBufAddr;
- /*取2的倍数,因为缓冲区都是字对齐的,注意此处如果大于1023会出现浪费现象
- *因为USB_COUNTn_TX只能接收bit0~bit9
- */
- val = (val + 1)& ~1;
- }
- //输出端点
- else
- {
- (pBUF_DSCR + num)->ADDR_RX = FreeBufAddr;
- /*因为USB_COUNTn_RX中存储只用bit10~bit14,如果BLSIZE=0(即块大小为2字节),那么只能是0~62个字节
- *所以如果大于62,则应将块大小设置为BLSIZE=1(即32个字节)
- */
- if(val > 62 )
- {
- //块大小为32,则大小应该为32的倍数
- val = (val +31)&~31;
- /*关于此计算公式,参见rm0008,21,5.3节
- *(val >> 5)<<10 == val <<5
- */
- (pBUF_DSCR + num)->COUNT_RX = ((val << 5)-1) | 0x8000;
- }
- else
- {
- val = (val + 1) & ~1;
- (pBUF_DSCR + num)->COUNT_RX = val << 9;
- }
- }
- //修正空闲区域的起始地址
- FreeBufAddr += val ;
- switch(pEPD->bmAttributes & USB_ENDPOINT_TYPE_MASK)
- {
- //控制端点
- case USB_ENDPOINT_TYPE_CONTROL:
- val = EP_CONTROL;
- break;
- //同步端点
- case USB_ENDPOINT_TYPE_ISOCHRONOUS:
- val = EP_ISOCHRONOUS;
- break;
- //块传输
- case USB_ENDPOINT_TYPE_INTERRUPT:
- val = EP_INTERRUPT;
- break;
- default:
- printf("出错了,未识别的端点类型\r\n");
- break;
- }
- val |= num;
- //设置端点寄存器
- EPxREG(num) = val;
- }
- /*功能:设置端点状态
- *参数:EPnum --->端点号
- * stat --->要设置的状态值
- *返回值:无
- */
- void EP_Status(DWORD EPNum,DWORD stat)
- {
- DWORD num,var;
- /*取得端点号*/
- num = EPNum & 0x0f;
- var = EPxREG(num);
- /*此处用了一点小技巧,因为端点寄存器是写1反转,所以想设置相应的值只有使用异或*/
- if(EPNum & 0x80) //输入端点
- EPxREG(num)=(var ^ (stat & EP_STAT_TX)) & (EP_MASK | EP_STAT_TX);
- else //输出端点
- EPxREG(num)=(var ^ (stat & EP_STAT_RX)) & (EP_MASK | EP_STAT_RX);
- }
- /*功能:复位端点
- *参数:EPNum bit0~bit3为端点号
- bit7 为端点方向
- *返回值:无
- */
- void USB_ResetEP(DWORD EPNum)
- {
- EP_Reset(EPNum);
- }
- /*功能:设置端点为stall
- *参数:EPNum bit0~bit3为端点号
- bit7 为端点方向
- *返回值:无
- */
- void USB_SetStallEP(DWORD EPNum)
- {
- EP_Status(EPNum,EP_TX_STALL | EP_RX_STALL);
- }
- /*功能:设置端点为enable
- *参数:EPNum bit0~bit3为端点号
- bit7 为端点方向
- *返回值:无
- */
- void USB_EnableEP(DWORD EPNum)
- {
- EP_Status(EPNum,EP_TX_VALID | EP_RX_VALID);
- }
- /*功能:设置端点为disable
- *参数:EPNum bit0~bit3为端点号
- bit7 为端点方向
- *返回值:无
- */
- void USB_DisableEP(DWORD EPNum)
- {
- EP_Status(EPNum,EP_TX_DIS | EP_RX_DIS);
- }
- /*功能:清除端点的stall状态
- *参数:EPNum bit0~bit3为端点号
- bit7 为端点方向
- *返回值:无
- */
- void USB_ClrStallEP(DWORD EPNum)
- {
- EP_Status(EPNum,EP_TX_VALID | EP_RX_VALID);
- }
- /*功能:设置USB地址
- *参数:addr要设置的地址
- *返回值:无
- */
- void USB_SetAddress(DWORD addr)
- {
- //DADDR的高1位是用来使能USB模块的
- DADDR = DADDR_EF | addr;
- }
- /*功能:用于读端点缓冲区
- *参数:EPnum --->端点号
- * pData --->用于接收从端点缓冲区中读到的数据
- * 此函数有些问题,应该加入一个参数显示缓冲区有多大
- *返回值:读到的字节数
- */
- DWORD USB_ReadEP(DWORD EPnum,BYTE * pData)
- {
- DWORD num,cnt,*pv,n;
- //得到端点号
- num = EPnum & 0xf;
- //取得些端点的缓冲区地址
- pv=(DWORD *)(USB_PMA_ADDR + 2* ((pBUF_DSCR + num)->ADDR_RX));
- //COUNT_RX低10位存放的是缓冲区的数据
- cnt=(pBUF_DSCR + num)->COUNT_RX & EP_COUNT_MASK;
- for(n=0;n<(cnt+1)/2;n++)
- {
- /*pakced关键字用于单字节对齐,这在USB数据结构中的结构体中尤为重要
- *因为stm32访问时使用32位,而USB访问时使用16位,所以pData为WORD,而
- *pv为DWORD型指针
- */
- *((__packed WORD *)pData)=*pv++;
- /*这里pData为单字节指针所以,还是加2而不是加4*/
- pData+=2;
- }
- /*OK,现在我们的端点又可以接收数据了,设置为VALID*/
- EP_Status (EPnum,EP_RX_VALID);
- return cnt;
- }
- /*功能:用于写端点缓冲区
- *参数:EPNum --->端点号
- * pData --->指向要发送的数据缓冲区
- * cnt --->要写入的字节数
- *返回值:写入的字节数
- */
- DWORD USB_WriteEP(DWORD EPNum , BYTE * pData,DWORD cnt)
- {
- DWORD num,*pv,n;
- num = EPNum & 0x0f;
- pv=(DWORD *)(USB_PMA_ADDR + 2*((pBUF_DSCR+num)->ADDR_TX));
- /*此处应该判断要写入的数据是否超量了,可能会产一个隐藏bug*/
- for(n=0;n<(cnt + 1)/2;n++)
- {
- *pv++=*((__packed WORD*)pData);
- pData+=2;
- }
- //OK,现在USB发送缓冲区中已经有东西了,可以响应主机了
- (pBUF_DSCR+num)->COUNT_TX=cnt;
- EP_Status(EPNum,EP_TX_VALID);
- return cnt;
- }
- /*功能:USB挂起
- *参数:无
- *返回值:无
- */
- void USB_Suspend(void)
- {
- GPIOE->BSRR |=1 ; /* Turn Off Suspend LED */
- printf("进入挂起中断\r\n");
- //强制挂起
- CNTR |= CNTR_FSUSP;
- //进入低功耗模式
- CNTR |= CNTR_LPMODE;
- }
- /*功能:USB唤醒
- *参数:无
- *返回值:无
- */
- void USB_WakeUp(void)
- {
- GPIOE->BRR |=1 ; /* Turn On Suspend LED */
- printf("进入唤醒中断\r\n");
- //唤醒了,当然得把这一位给清了
- CNTR &= ~CNTR_FSUSP;
- //USB的唤醒事件会复位此位,我们这里不用管
- //CNTR &= ~CNTR_LPMODE;
- }
- /*功能:USB低优先级中断服务程序
- *参数:无
- *返回值:无
- */
- void USB_LP_CAN_RX0_IRQHandler(void)
- {
- DWORD istr;
- DWORD num,var;
- istr=ISTR; //取得中断标志位
- /*USB复位中断的处理*/
- if(istr & ISTR_RESET)
- {
- //复位设备
- USB_Reset();
- //复位与USB协议有关的数据
- USB_ResetCore();
- ISTR = ~ISTR_RESET; /*已经处理完复位中断了,清楚复位中断标志*/
- }
- /*USB挂起中断*/
- /* if(istr &
- ********************************************************************
- */
- if (istr & ISTR_SUSP) {
- USB_Suspend();
- ISTR = ~ISTR_SUSP;
- }
- /* USB Wakeup */
- if (istr & ISTR_WKUP) {
- USB_WakeUp();
- ISTR = ~ISTR_WKUP;
- }
- //******************************************************************
- /*端点接中发送中断处理*/
- while((istr = ISTR) & ISTR_CTR)
- {
- //清楚中断标志位
- ISTR = ~ISTR_CTR;
- //取得发生中断的端点号
- num=istr & ISTR_EP_ID;
- //取得端点寄存器的值
- var = EPxREG(num);
- //正确的接收传输
- if(var & EP_CTR_RX )
- {
- //清0正确接收标志位
- // printf("端点号为:");
- // printhex((u8)num);
- EPxREG(num) = var & ~EP_CTR_RX & EP_MASK;
- //调用相应的端点进行处理
- if(USB_P_EP[num])
- {
- //如果是控制传输,则使有USG_EVT_SETUP
- if(var & EP_SETUP)
- USB_P_EP[num](USB_EVT_SETUP);
- else
- //否则就是普通的输出包
- USB_P_EP[num](USB_EVT_OUT);
- }
- }
- //产生的是发送成功中断
- if(var & EP_CTR_TX)
- {
- //清楚中断标志位
- EPxREG(num) = var & ~EP_CTR_TX & EP_MASK;
- //调用对应的中断函数进行处理
- if(USB_P_EP[num])
- {
- //USB发送成功
- USB_P_EP[num](USB_EVT_IN);
- }
- }
- }
- }