在线时间6 小时
UID3637438
注册时间2020-8-17
NXP金币0
TA的每日心情 | 开心 2020-12-11 09:21 |
---|
签到天数: 6 天 连续签到: 1 天 [LV.2]偶尔看看I
注册会员

- 积分
- 104
- 最后登录
- 2024-12-2
|
本帖最后由 dhvf1 于 2020-12-2 17:27 编辑
LPC1768的usb使用--硬件篇
LPC1768芯片带有USB设备控制器,前面写的文章都是在说比较简单的设备驱动,今天来说复杂一点的
首先是硬件层的配置
- #ifndef __USBHW_H__
- #define __USBHW_H__
- #include "debugSerial.h"
- #include "usbreg.h"
- #include "usb.h"
- #include "usbuser.h"
- #include "usbcfg.h"
- #include "usbcore.h"
- #include "usbep1.h"
- U32 EPAdr (U32 EPNum) ;
- //USB硬件寄存器级别的方法
- extern void USB_Init(void);
- //usb连接
- extern void USB_Connect(BOOL con);
- //usb复位
- extern void USB_Reset(void);
- //usb挂起
- extern void USB_Suspend(void);
- //usb挂起恢复
- extern void USB_Resume(void);
- //usb唤醒
- extern void USB_WakeUp(void);
- extern void USB_WakeUpCfg(BOOL cfg);
- //usb设置地址
- extern void USB_SetAddress(U32 adr);
- //usb配置
- extern void USB_Configure(BOOL cfg);
- //usb根据配置描述符配置端点
- extern void USB_ConfigEP(USB_ENDPOINT_DESCRIPTOR *pEPD);
- //使能端点
- extern void USB_EnableEP(U32 EPNum);
- //禁止端点
- extern void USB_DisableEP(U32 EPNum);
- //端点复位
- extern void USB_ResetEP(U32 EPNum);
- //设置端点暂停
- extern void USB_SetStallEP(U32 EPNum);
- //清除端点暂停,设置特性
- extern void USB_ClrStallEP(U32 EPNum);
- //usb清除端点缓存
- extern void USB_ClearEPBuf(U32 EPNum);
- //读取usb缓冲区
- extern U32 USB_ReadEP(U32 EPNum, U8 *pData);
- //写入usb in包的缓冲区
- extern U32 USB_WriteEP(U32 EPNum, U8 *pData, U32 cnt);
- //获取当前usb帧号
- extern U32 USB_GetFrame(void);
- #endif
复制代码 实现如下
- #include "usbhw.h"
- #define EP_MSK_CTRL 0x0001 /* 控制端点逻辑地址,第0端点 */
- #define EP_MSK_BULK 0xC924 /* 批量端点逻辑地址 第2 5 8 11 14 15 */
- #define EP_MSK_INT 0x4492 /* 中断端点逻辑地址 1 4 7 10 13 */
- #define EP_MSK_ISO 0x1248 /* 同步端点逻辑地址 3 6 9 12 */
- //端点的逻辑地址转换为物理地址,比如逻辑地址0x80 转换过来是物理端点1,
- //(usb设置配置的时候发送来的端点号码是逻辑地址,需要转换)
复制代码 U32 EPAdr (U32 EPNum)
- {
- U32 val;
- val = (EPNum & 0x0F) << 1;
- if (EPNum & 0x80) val += 1;//根据输入输出增减
- return (val);
- }
复制代码 //usb写入命令
- void WrCmd (U32 cmd)
- {
- LPC_USB->USBDevIntClr = CCEMTY_INT;//清除命令空中断
- LPC_USB->USBCmdCode = cmd;
- while ((LPC_USB->USBDevIntSt & CCEMTY_INT) == 0);//等待写入的命令被接受,空中断再次产生
- }
复制代码 //usb写入命令+数据,流程相当于上一个函数的重复
- void WrCmdDat (U32 cmd, U32 val)
- {
- LPC_USB->USBDevIntClr = CCEMTY_INT;
- LPC_USB->USBCmdCode = cmd;
- while ((LPC_USB->USBDevIntSt & CCEMTY_INT) == 0);
- LPC_USB->USBDevIntClr = CCEMTY_INT;
- LPC_USB->USBCmdCode = val;
- while ((LPC_USB->USBDevIntSt & CCEMTY_INT) == 0);
- }
复制代码
//向端点写入命令,端点号应当是端点的逻辑地址
- void WrCmdEP (U32 EPNum, U32 cmd)
- {
- LPC_USB->USBDevIntClr = CCEMTY_INT;
- LPC_USB->USBCmdCode = CMD_SEL_EP(EPAdr(EPNum));//选择端点,发送的是命令形式的端点选择
- while ((LPC_USB->USBDevIntSt & CCEMTY_INT) == 0);
- LPC_USB->USBDevIntClr = CCEMTY_INT;
- LPC_USB->USBCmdCode = cmd; //写入数据
- while ((LPC_USB->USBDevIntSt & CCEMTY_INT) == 0);
- }
复制代码 //写入命令并读出数据 命令应当是读取命令 02
U32 RdCmdDat (U32 cmd)
- {
- LPC_USB->USBDevIntClr = CCEMTY_INT | CDFULL_INT;//清除命令为空,数据满中断
- LPC_USB->USBCmdCode = cmd;
- while ((LPC_USB->USBDevIntSt & CDFULL_INT) == 0);//等待数据满
- return (LPC_USB->USBCmdData);
- }
复制代码 //USB总线复位,重新配置端点的寄存器
- void USB_Reset(void)
- {
- LPC_USB->USBEpInd = 0;
- LPC_USB->USBMaxPSize = USB_MAX_PACKET0;
- LPC_USB->USBEpInd = 1;
- LPC_USB->USBMaxPSize = USB_MAX_PACKET0;//两个控制端点都设置为最大包形式
- while ((LPC_USB->USBDevIntSt & EP_RLZED_INT) == 0);//等待端点使用
- LPC_USB->USBEpIntClr = 0xFFFFFFFF;
- LPC_USB->USBEpIntEn = 0xFFFFFFFF ^ USB_DMA_EP;//使能DMA的端点,中断自动触发DMA,所以不开启中断
- LPC_USB->USBDevIntClr = 0xFFFFFFFF;
- //当使用同步端点的时候要打开帧中断
- LPC_USB->USBDevIntEn = DEV_STAT_INT | EP_SLOW_INT ;//打开状态中断(复位挂起等)和慢速中断(默认情况下,端点中断都是慢速中断)
- }
复制代码 //usb设置设备的总线地址
- void USB_SetAddress (U32 adr)
- {
- WrCmdDat(CMD_SET_ADDR, DAT_WR_BYTE(DEV_EN | adr)); //连续写入两次,使能usb对该地址的响应
- WrCmdDat(CMD_SET_ADDR, DAT_WR_BYTE(DEV_EN | adr));
- }
复制代码 //usb初始化
- void USB_Init (void)
- {
- LPC_SC->PCONP |= (1<<15);//打开IO口时钟
- //USB D+
- LPC_PINCON->PINSEL1 &= ~(0X03L<<26);
- LPC_PINCON->PINSEL1 |= (1<<26); //功能 usbd+
- //USB D+
- LPC_PINCON->PINSEL1 &= ~(0X03L<<28);
- LPC_PINCON->PINSEL1 |= (1<<28); //功能 usbd-
- //USB VBUS
- LPC_PINCON->PINSEL3 &= ~(0X03L<<28);
- LPC_PINCON->PINSEL3 |= (2<<28); //功能 usb vbus
- //USB CONNECT
- LPC_PINCON->PINSEL4 &= ~(0X03L<<18);
- LPC_PINCON->PINSEL4 |= (0X01L<<18);
- // LPC_PINCON->PINMODE4 &= ~(0X03L<<18); //使能上拉电阻
- // LPC_PINCON->PINMODE_OD2 &= ~(0X01<<9); //正常推挽模式
- // P2dir(9) = 1; //输出
- // P2high(9) = 1; //初始化设置为0
- LPC_SC->PCONP |= (1UL<<31); /* USB PCLK -> enable USB Per. */
- LPC_USB->USBClkCtrl = 0x1A; /* Dev, PortSel, AHB clock enable */
- while ((LPC_USB->USBClkSt & 0x1A) != 0x1A); //等待时钟状态切换完成
- USB_Reset();
- USB_SetAddress(0); //初始化未识别之前设置设备地址为0
- NVIC_ClearPendingIRQ(USB_IRQn);
- NVIC_SetPriority(USB_IRQn,NVIC_EncodePriority(SYS_NVIC_GROUP,USB_PreemptPriority,USB_SubPriority));//中断优先级别
- NVIC_EnableIRQ(USB_IRQn); /* enable USB interrupt */
- }
复制代码 //usb软连接选择,为0 断开 为1连接
- void USB_Connect (BOOL con)
- {
- WrCmdDat(CMD_SET_DEV_STAT, DAT_WR_BYTE(con ? DEV_CON : 0));
- // if(con)P2low(9) = 1;
- // else P2high(9) = 1;
- }
复制代码 //usb挂起事件发生之后自动调用的函数,处理挂起事务
- void USB_Suspend (void)
- {
- usb_debug_printf("USB_Suspend \r\n");
- mouse_connect = 0;
- }
复制代码
//usb收到恢复指令之后自动调用的函数,处理恢复事务
- void USB_Resume(void)
- {
- usb_debug_printf("USB_Resume \r\n");
- mouse_connect = 0;
- }
复制代码
//usb远程唤醒时间发生的时候自动调用的函数
- void USB_WakeUp (void)
- {
- if (USB_DeviceStatus & USB_GETSTATUS_REMOTE_WAKEUP)
- {
- WrCmdDat(CMD_SET_DEV_STAT, DAT_WR_BYTE(DEV_CON));//根据USB状态设置usb自动连接
- }
- }
复制代码 //usb远程唤醒时需要的配置
- void USB_WakeUpCfg (BOOL cfg)
- {
- //不需要的函数
- }
复制代码 //USB设置配置,设置所有已经使能的端点作出相应或者不响应
- void USB_Configure (BOOL cfg)
- {
- WrCmdDat(CMD_CFG_DEV, DAT_WR_BYTE(cfg ? CONF_DVICE : 0));
- LPC_USB->USBReEp = 0x00000003;//设置控制端点0的输入和输出端点使能
- while ((LPC_USB->USBDevIntSt & EP_RLZED_INT) == 0);
- LPC_USB->USBDevIntClr = EP_RLZED_INT;//这样表示仅仅输入输出端点响应
- }
复制代码 //根据USB端点配置符来配置USB的相应端点
- void USB_ConfigEP (USB_ENDPOINT_DESCRIPTOR *pEPD)
- {
- U32 num;
- num = EPAdr(pEPD->bEndpointAddress);//获取物理端点地址
- LPC_USB->USBReEp |= (1 << num);//使能相应端点
- LPC_USB->USBEpInd = num;//使能相应端点中断
- LPC_USB->USBMaxPSize = pEPD->wMaxPacketSize;//设置相应端点缓冲区大小
- while ((LPC_USB->USBDevIntSt & EP_RLZED_INT) == 0);//等待端点使用
- LPC_USB->USBDevIntClr = EP_RLZED_INT;//清除中断
- }
复制代码 //使能usb相应端点
- void USB_EnableEP (U32 EPNum)
- {
- WrCmdDat(CMD_SET_EP_STAT(EPAdr(EPNum)), DAT_WR_BYTE(0));//将端点相应的状态清零
- }
复制代码 //禁用相应端点
- void USB_DisableEP (U32 EPNum)
- {
- WrCmdDat(CMD_SET_EP_STAT(EPAdr(EPNum)), DAT_WR_BYTE(EP_STAT_DA));//第五位字节禁用相应的端点
- }
复制代码 //复位相应的端点,这些端点都是逻辑端点
- void USB_ResetEP (U32 EPNum)
- {
- WrCmdDat(CMD_SET_EP_STAT(EPAdr(EPNum)), DAT_WR_BYTE(0));//清除端点状态自然就是复位
- }
复制代码 //设置端点暂停
- void USB_SetStallEP (U32 EPNum)
- {
- WrCmdDat(CMD_SET_EP_STAT(EPAdr(EPNum)), DAT_WR_BYTE(EP_STAT_ST));//设置端点暂停响应
- }
- //清除端点的暂停
- void USB_ClrStallEP (U32 EPNum)
- {
- WrCmdDat(CMD_SET_EP_STAT(EPAdr(EPNum)), DAT_WR_BYTE(0));
- }
复制代码 //usb清除端点缓冲区数据
- void USB_ClearEPBuf (U32 EPNum)
- {
- WrCmdEP(EPNum, CMD_CLR_BUF);
- }
复制代码 //usb读取端点缓冲区数据,每次读取四个字节
U32 USB_ReadEP (U32 EPNum, U8 *pData)
- void USB_IRQHandler(void)
- {
- U32 disr, val, n, m;
- U32 episr, episrCur;
- //读取当前设备中断
- disr = LPC_USB->USBDevIntSt;
- //设备状态中断处理
- if (disr & DEV_STAT_INT)
- {
- LPC_USB->USBDevIntClr = DEV_STAT_INT;//清除中断
- WrCmd(CMD_GET_DEV_STAT);
- val = RdCmdDat(DAT_GET_DEV_STAT); //获取设备状态
- if (val & DEV_RST)USB_Reset();复位中断
- if (val & DEV_CON_CH){}//设备连接状态被改变
- if (val & DEV_SUS_CH)
- { /* Suspend/Resume */
- if (val & DEV_SUS)USB_Suspend();//设备挂起
- else USB_Resume();//设备恢复
- }
- return;
- }
- //USB慢速中断,在不修改中断优先级的情况下,所有的中断都是慢速中断(可修改为快速中断)
- if (disr & EP_SLOW_INT)
- {
- episrCur = 0;
- episr = LPC_USB->USBEpIntSt;//获取当前端点中断状态
- for (n = 0; n < 32; n++)//轮询所有中断端点
- {
- if (episr == episrCur)break; //如果中断全部处理了,那么就不用在轮询了
- if (episr & (1 << n))//如果有中断发生,对应端点位为1
- {
- episrCur |= (1 << n);//设置cur的值,这句话的原理是,每处理一个端点,记录已经处理的端点,要是已经处理的端点和中断端点
- //相等,就可以说明所有的端点都已经被处理了
- m = n >> 1;//每两个端点对应一个逻辑端点,处理函数将一个逻辑端点的处理放在一个函数中,所以/2
- LPC_USB->USBEpIntClr = (1 << n);//清除对应中断,对应中断的清除相当于我们向SEI引擎写入了清除端点状态的命令,所以端点 状态信息会存在在cmddata上不需要而外的去读取,所以下面才会有那句直接读取cmddata
- while ((LPC_USB->USBDevIntSt & CDFULL_INT) == 0);//等待命令执行
- val = LPC_USB->USBCmdData;//读取断电状态
- //如果端点号是偶数,说明书输输出端点
复制代码- if ((n & 1) == 0)
- {
- if (n == 0)//0为默认的控制端点
- {
- if (val & EP_SEL_STP)//是否为setup包
- {
- if (USB_P_EP[0])//对端点0的处理程序是否存在
- {
- USB_P_EP[0](USB_EVT_SETUP);//存在则调用处理程序
- continue;
- }
- }
- }
- if (USB_P_EP[m])USB_P_EP[m](USB_EVT_OUT);//其他端点的输出处理,方法类似(包括0端点)
- } else//奇数是输入端点
- {
- if (USB_P_EP[m])USB_P_EP[m](USB_EVT_IN);//输入处理
- }
- }
- }
- LPC_USB->USBDevIntClr = EP_SLOW_INT;//最后将端点中断的慢中断清除
- }
- }
复制代码
|
|