查看: 4441|回复: 1

[分享] LPC1768的usb使用--硬件篇

[复制链接]
  • TA的每日心情
    开心
    2020-12-11 09:21
  • 签到天数: 6 天

    连续签到: 1 天

    [LV.2]偶尔看看I

    2

    主题

    46

    帖子

    0

    注册会员

    Rank: 2

    积分
    104
    最后登录
    2024-12-2
    发表于 2020-12-2 17:26:03 | 显示全部楼层 |阅读模式
    本帖最后由 dhvf1 于 2020-12-2 17:27 编辑

    LPC1768的usb使用--硬件篇

    LPC1768芯片带有USB设备控制器,前面写的文章都是在说比较简单的设备驱动,今天来说复杂一点的

             首先是硬件层的配置
    1. #ifndef __USBHW_H__

    2. #define __USBHW_H__

    3. #include "debugSerial.h"

    4. #include "usbreg.h"

    5. #include "usb.h"

    6. #include "usbuser.h"

    7. #include "usbcfg.h"

    8. #include "usbcore.h"

    9. #include "usbep1.h"

    10. U32 EPAdr (U32 EPNum) ;

    11. //USB硬件寄存器级别的方法

    12. extern void USB_Init(void);

    13. //usb连接

    14. extern void USB_Connect(BOOL con);

    15. //usb复位

    16. extern void USB_Reset(void);

    17. //usb挂起

    18. extern void USB_Suspend(void);

    19. //usb挂起恢复

    20. extern void USB_Resume(void);

    21. //usb唤醒

    22. extern void USB_WakeUp(void);

    23. extern void USB_WakeUpCfg(BOOL cfg);

    24. //usb设置地址

    25. extern void USB_SetAddress(U32 adr);

    26. //usb配置

    27. extern void USB_Configure(BOOL cfg);

    28. //usb根据配置描述符配置端点

    29. extern void USB_ConfigEP(USB_ENDPOINT_DESCRIPTOR *pEPD);

    30. //使能端点

    31. extern void USB_EnableEP(U32 EPNum);

    32. //禁止端点

    33. extern void USB_DisableEP(U32 EPNum);

    34. //端点复位

    35. extern void USB_ResetEP(U32 EPNum);

    36. //设置端点暂停

    37. extern void USB_SetStallEP(U32 EPNum);

    38. //清除端点暂停,设置特性

    39. extern void USB_ClrStallEP(U32 EPNum);

    40. //usb清除端点缓存

    41. extern void USB_ClearEPBuf(U32 EPNum);

    42. //读取usb缓冲区

    43. extern U32 USB_ReadEP(U32 EPNum, U8 *pData);

    44. //写入usb in包的缓冲区

    45. extern U32 USB_WriteEP(U32 EPNum, U8 *pData, U32 cnt);

    46. //获取当前usb帧号

    47. extern U32 USB_GetFrame(void);

    48. #endif
    复制代码
    实现如下
    1. #include "usbhw.h"
    2. #define EP_MSK_CTRL 0x0001      /* 控制端点逻辑地址,第0端点 */
    3. #define EP_MSK_BULK 0xC924      /* 批量端点逻辑地址 第2 5 8 11 14 15 */
    4. #define EP_MSK_INT  0x4492      /* 中断端点逻辑地址 1 4  7 10 13 */
    5. #define EP_MSK_ISO  0x1248      /* 同步端点逻辑地址 3 6 9 12 */
    6. //端点的逻辑地址转换为物理地址,比如逻辑地址0x80 转换过来是物理端点1,
    7. //(usb设置配置的时候发送来的端点号码是逻辑地址,需要转换)
    复制代码
    U32 EPAdr (U32 EPNum)
    1. {
    2.     U32 val;
    3.     val = (EPNum & 0x0F) << 1;
    4.     if (EPNum & 0x80) val += 1;//根据输入输出增减
    5.     return (val);
    6. }
    复制代码
    //usb写入命令
    1. void WrCmd (U32 cmd)
    2. {
    3. LPC_USB->USBDevIntClr = CCEMTY_INT;//清除命令空中断
    4. LPC_USB->USBCmdCode = cmd;
    5. while ((LPC_USB->USBDevIntSt & CCEMTY_INT) == 0);//等待写入的命令被接受,空中断再次产生
    6. }
    复制代码
    //usb写入命令+数据,流程相当于上一个函数的重复
    1. void WrCmdDat (U32 cmd, U32 val)
    2. {
    3.     LPC_USB->USBDevIntClr = CCEMTY_INT;
    4.     LPC_USB->USBCmdCode = cmd;
    5.     while ((LPC_USB->USBDevIntSt & CCEMTY_INT) == 0);
    6.     LPC_USB->USBDevIntClr = CCEMTY_INT;
    7.     LPC_USB->USBCmdCode = val;
    8.     while ((LPC_USB->USBDevIntSt & CCEMTY_INT) == 0);
    9. }
    复制代码

    //向端点写入命令,端点号应当是端点的逻辑地址
    1. void WrCmdEP (U32 EPNum, U32 cmd)
    2. {
    3.     LPC_USB->USBDevIntClr = CCEMTY_INT;
    4.     LPC_USB->USBCmdCode = CMD_SEL_EP(EPAdr(EPNum));//选择端点,发送的是命令形式的端点选择
    5. while ((LPC_USB->USBDevIntSt & CCEMTY_INT) == 0);
    6.     LPC_USB->USBDevIntClr = CCEMTY_INT;
    7.     LPC_USB->USBCmdCode = cmd;  //写入数据
    8. while ((LPC_USB->USBDevIntSt & CCEMTY_INT) == 0);
    9. }
    复制代码
    //写入命令并读出数据 命令应当是读取命令 02

    U32 RdCmdDat (U32 cmd)
    1. {
    2. LPC_USB->USBDevIntClr = CCEMTY_INT | CDFULL_INT;//清除命令为空,数据满中断
    3. LPC_USB->USBCmdCode = cmd;
    4. while ((LPC_USB->USBDevIntSt & CDFULL_INT) == 0);//等待数据满
    5. return (LPC_USB->USBCmdData);
    6. }
    复制代码
    //USB总线复位,重新配置端点的寄存器
    1. void USB_Reset(void)
    2. {
    3. LPC_USB->USBEpInd = 0;
    4. LPC_USB->USBMaxPSize = USB_MAX_PACKET0;
    5. LPC_USB->USBEpInd = 1;
    6. LPC_USB->USBMaxPSize = USB_MAX_PACKET0;//两个控制端点都设置为最大包形式
    7. while ((LPC_USB->USBDevIntSt & EP_RLZED_INT) == 0);//等待端点使用
    8. LPC_USB->USBEpIntClr = 0xFFFFFFFF;
    9. LPC_USB->USBEpIntEn = 0xFFFFFFFF ^ USB_DMA_EP;//使能DMA的端点,中断自动触发DMA,所以不开启中断
    10. LPC_USB->USBDevIntClr = 0xFFFFFFFF;
    11. //当使用同步端点的时候要打开帧中断
    12. LPC_USB->USBDevIntEn = DEV_STAT_INT | EP_SLOW_INT ;//打开状态中断(复位挂起等)和慢速中断(默认情况下,端点中断都是慢速中断)               
    13. }
    复制代码
    //usb设置设备的总线地址
    1. void USB_SetAddress (U32 adr)
    2. {
    3. WrCmdDat(CMD_SET_ADDR, DAT_WR_BYTE(DEV_EN | adr)); //连续写入两次,使能usb对该地址的响应
    4. WrCmdDat(CMD_SET_ADDR, DAT_WR_BYTE(DEV_EN | adr));
    5. }
    复制代码
    //usb初始化
    1. void USB_Init (void)



    2. {

    3.     LPC_SC->PCONP |= (1<<15);//打开IO口时钟
    4.     //USB D+
    5.     LPC_PINCON->PINSEL1 &= ~(0X03L<<26);
    6.     LPC_PINCON->PINSEL1 |= (1<<26);         //功能 usbd+
    7.     //USB D+
    8.     LPC_PINCON->PINSEL1 &= ~(0X03L<<28);
    9.     LPC_PINCON->PINSEL1 |= (1<<28);         //功能 usbd-
    10.     //USB VBUS
    11. LPC_PINCON->PINSEL3 &= ~(0X03L<<28);   
    12. LPC_PINCON->PINSEL3 |= (2<<28);         //功能 usb vbus
    13. //USB CONNECT
    14. LPC_PINCON->PINSEL4 &= ~(0X03L<<18);   
    15. LPC_PINCON->PINSEL4 |= (0X01L<<18);
    16. //  LPC_PINCON->PINMODE4 &= ~(0X03L<<18);   //使能上拉电阻
    17. //  LPC_PINCON->PINMODE_OD2 &= ~(0X01<<9);  //正常推挽模式
    18. //  P2dir(9) = 1;                           //输出
    19. //  P2high(9) = 1;                          //初始化设置为0
    20. LPC_SC->PCONP   |= (1UL<<31);                /* USB PCLK -> enable USB Per.       */
    21. LPC_USB->USBClkCtrl = 0x1A;                  /* Dev, PortSel, AHB clock enable */
    22. while ((LPC_USB->USBClkSt & 0x1A) != 0x1A);     //等待时钟状态切换完成
    23. USB_Reset();
    24. USB_SetAddress(0);                              //初始化未识别之前设置设备地址为0
    25. NVIC_ClearPendingIRQ(USB_IRQn);
    26. NVIC_SetPriority(USB_IRQn,NVIC_EncodePriority(SYS_NVIC_GROUP,USB_PreemptPriority,USB_SubPriority));//中断优先级别
    27. NVIC_EnableIRQ(USB_IRQn);               /* enable USB interrupt */
    28. }
    复制代码
    //usb软连接选择,为0 断开 为1连接
    1. void USB_Connect (BOOL con)
    2. {
    3.     WrCmdDat(CMD_SET_DEV_STAT, DAT_WR_BYTE(con ? DEV_CON : 0));
    4. //  if(con)P2low(9) = 1;
    5. //  else P2high(9) = 1;
    6. }

    复制代码
    //usb挂起事件发生之后自动调用的函数,处理挂起事务
    1. void USB_Suspend (void)
    2. {
    3. usb_debug_printf("USB_Suspend \r\n");
    4. mouse_connect = 0;
    5. }

    复制代码


    //usb收到恢复指令之后自动调用的函数,处理恢复事务
    1. void USB_Resume(void)

    2. {
    3. usb_debug_printf("USB_Resume \r\n");
    4. mouse_connect = 0;
    5. }
    复制代码

    //usb远程唤醒时间发生的时候自动调用的函数
    1. void USB_WakeUp (void)
    2. {
    3. if (USB_DeviceStatus & USB_GETSTATUS_REMOTE_WAKEUP)
    4. {
    5. WrCmdDat(CMD_SET_DEV_STAT, DAT_WR_BYTE(DEV_CON));//根据USB状态设置usb自动连接
    6. }
    7. }

    复制代码
    //usb远程唤醒时需要的配置
    1. void USB_WakeUpCfg (BOOL cfg)
    2. {
    3. //不需要的函数
    4. }

    复制代码
    //USB设置配置,设置所有已经使能的端点作出相应或者不响应
    1. void USB_Configure (BOOL cfg)
    2. {
    3. WrCmdDat(CMD_CFG_DEV, DAT_WR_BYTE(cfg ? CONF_DVICE : 0));
    4. LPC_USB->USBReEp = 0x00000003;//设置控制端点0的输入和输出端点使能
    5. while ((LPC_USB->USBDevIntSt & EP_RLZED_INT) == 0);
    6. LPC_USB->USBDevIntClr = EP_RLZED_INT;//这样表示仅仅输入输出端点响应
    7. }
    复制代码
    //根据USB端点配置符来配置USB的相应端点
    1. void USB_ConfigEP (USB_ENDPOINT_DESCRIPTOR *pEPD)
    2. {
    3. U32 num;
    4. num = EPAdr(pEPD->bEndpointAddress);//获取物理端点地址
    5. LPC_USB->USBReEp |= (1 << num);//使能相应端点
    6. LPC_USB->USBEpInd = num;//使能相应端点中断
    7. LPC_USB->USBMaxPSize = pEPD->wMaxPacketSize;//设置相应端点缓冲区大小
    8. while ((LPC_USB->USBDevIntSt & EP_RLZED_INT) == 0);//等待端点使用
    9. LPC_USB->USBDevIntClr = EP_RLZED_INT;//清除中断
    10. }

    复制代码
    //使能usb相应端点
    1. void USB_EnableEP (U32 EPNum)
    2. {
    3. WrCmdDat(CMD_SET_EP_STAT(EPAdr(EPNum)), DAT_WR_BYTE(0));//将端点相应的状态清零
    4. }
    复制代码
    //禁用相应端点
    1. void USB_DisableEP (U32 EPNum)
    2. {
    3. WrCmdDat(CMD_SET_EP_STAT(EPAdr(EPNum)), DAT_WR_BYTE(EP_STAT_DA));//第五位字节禁用相应的端点
    4. }
    复制代码
    //复位相应的端点,这些端点都是逻辑端点
    1. void USB_ResetEP (U32 EPNum)
    2. {
    3. WrCmdDat(CMD_SET_EP_STAT(EPAdr(EPNum)), DAT_WR_BYTE(0));//清除端点状态自然就是复位
    4. }

    复制代码
    //设置端点暂停
    1. void USB_SetStallEP (U32 EPNum)
    2. {
    3. WrCmdDat(CMD_SET_EP_STAT(EPAdr(EPNum)), DAT_WR_BYTE(EP_STAT_ST));//设置端点暂停响应
    4. }
    5. //清除端点的暂停
    6. void USB_ClrStallEP (U32 EPNum)
    7. {
    8. WrCmdDat(CMD_SET_EP_STAT(EPAdr(EPNum)), DAT_WR_BYTE(0));
    9. }
    复制代码
    //usb清除端点缓冲区数据
    1. void USB_ClearEPBuf (U32 EPNum)
    2. {
    3. WrCmdEP(EPNum, CMD_CLR_BUF);
    4. }
    复制代码
    //usb读取端点缓冲区数据,每次读取四个字节

    U32 USB_ReadEP (U32 EPNum, U8 *pData)

    1. {

    2.     U32 cnt, n;

    3.     LPC_USB->USBCtrl = ((EPNum & 0x0F) << 2) | CTRL_RD_EN;//写入端点号和读取使能



    4.     do

    5.     {

    6.         cnt = LPC_USB->USBRxPLen;

    7.     }

    8.     while ((cnt & PKT_RDY) == 0);//等待数据包准备就绪并获得数据包长度



    9.     cnt &= PKT_LNGTH_MASK;

    10.     for (n = 0; n < (cnt + 3) / 4; n++)//依次读取

    11.     {

    12.         *((__packed U32 *)pData) = LPC_USB->USBRxData;

    13.         pData += 4;

    14.     }



    15.     LPC_USB->USBCtrl = 0;//读取清零



    16.     if (((0x1248 >> EPNum) & 1) == 0) //如果不是同步端点,那么清零缓冲区,是同步端点不能清零0x1234说明3 6 9 12是同步端点

    17.     {  

    18.         WrCmdEP(EPNum, CMD_CLR_BUF);

    19.     }



    20.     return (cnt);

    21. }



    22. //USB写入端点缓冲区,端点逻辑地址

    23. U32 USB_WriteEP (U32 EPNum, U8 *pData, U32 cnt)

    24. {

    25.     U32 n;

    26.     LPC_USB->USBCtrl = ((EPNum & 0x0F) << 2) | CTRL_WR_EN;//端点写使能



    27.     LPC_USB->USBTxPLen = cnt;//设置写入长度

    28.     for (n = 0; n < (cnt + 3) / 4; n++) //循环写入

    29.     {

    30.         LPC_USB->USBTxData = *((__packed U32 *)pData);

    31.         pData += 4;

    32.     }



    33.     LPC_USB->USBCtrl = 0;

    34.     WrCmdEP(EPNum, CMD_VALID_BUF);//使能缓冲区,下一次in包来的时候端点内部数据由SIE(串行引擎)发出

    35.     return (cnt);                   //返回实际写入数据长度

    36. }







    37. //usb获取当前帧计数

    38. U32 USB_GetFrame (void)

    39. {

    40.     U32 val;

    41.     WrCmd(CMD_RD_FRAME);//读取帧计数命令

    42.     val = RdCmdDat(DAT_RD_FRAME);

    43.     val = val | (RdCmdDat(DAT_RD_FRAME) << 8);

    44.     return (val);

    45. }



    46. //USB枚举过程的核心
    复制代码

    1. void USB_IRQHandler(void)

    2. {

    3.     U32 disr, val, n, m;

    4.     U32 episr, episrCur;

    5.     //读取当前设备中断

    6.     disr = LPC_USB->USBDevIntSt;

    7.     //设备状态中断处理

    8.     if (disr & DEV_STAT_INT)

    9.     {

    10.         LPC_USB->USBDevIntClr = DEV_STAT_INT;//清除中断

    11.         WrCmd(CMD_GET_DEV_STAT);

    12.         val = RdCmdDat(DAT_GET_DEV_STAT); //获取设备状态

    13.         if (val & DEV_RST)USB_Reset();复位中断

    14.         if (val & DEV_CON_CH){}//设备连接状态被改变

    15.         if (val & DEV_SUS_CH)

    16.         { /* Suspend/Resume */

    17.             if (val & DEV_SUS)USB_Suspend();//设备挂起

    18.             else USB_Resume();//设备恢复

    19.         }

    20.         return;

    21.     }

    22.     //USB慢速中断,在不修改中断优先级的情况下,所有的中断都是慢速中断(可修改为快速中断)

    23.     if (disr & EP_SLOW_INT)

    24.     {

    25.         episrCur = 0;

    26.         episr = LPC_USB->USBEpIntSt;//获取当前端点中断状态

    27.         for (n = 0; n < 32; n++)//轮询所有中断端点

    28.         {

    29.             if (episr == episrCur)break; //如果中断全部处理了,那么就不用在轮询了

    30.             if (episr & (1 << n))//如果有中断发生,对应端点位为1

    31.             {

    32.                 episrCur |= (1 << n);//设置cur的值,这句话的原理是,每处理一个端点,记录已经处理的端点,要是已经处理的端点和中断端点

    33.                     //相等,就可以说明所有的端点都已经被处理了

    34.                 m = n >> 1;//每两个端点对应一个逻辑端点,处理函数将一个逻辑端点的处理放在一个函数中,所以/2

    35.                 LPC_USB->USBEpIntClr = (1 << n);//清除对应中断,对应中断的清除相当于我们向SEI引擎写入了清除端点状态的命令,所以端点                                  状态信息会存在在cmddata上不需要而外的去读取,所以下面才会有那句直接读取cmddata

    36.                 while ((LPC_USB->USBDevIntSt & CDFULL_INT) == 0);//等待命令执行

    37.                 val = LPC_USB->USBCmdData;//读取断电状态

    38.                 //如果端点号是偶数,说明书输输出端点
    复制代码
    1.   if ((n & 1) == 0)

    2.                 {

    3.                     if (n == 0)//0为默认的控制端点

    4.                     {

    5.                         if (val & EP_SEL_STP)//是否为setup包

    6.                         {

    7.                             if (USB_P_EP[0])//对端点0的处理程序是否存在

    8.                             {

    9.                                 USB_P_EP[0](USB_EVT_SETUP);//存在则调用处理程序

    10.                                 continue;

    11.                             }

    12.                         }

    13.                     }

    14.                     if (USB_P_EP[m])USB_P_EP[m](USB_EVT_OUT);//其他端点的输出处理,方法类似(包括0端点)

    15.                 } else//奇数是输入端点

    16.                 {

    17.                     if (USB_P_EP[m])USB_P_EP[m](USB_EVT_IN);//输入处理

    18.                 }

    19.             }

    20.         }

    21.         LPC_USB->USBDevIntClr = EP_SLOW_INT;//最后将端点中断的慢中断清除

    22.     }

    23. }
    复制代码

                  
    签到
    回复

    使用道具 举报

  • TA的每日心情
    开心
    2025-6-10 23:03
  • 签到天数: 1502 天

    连续签到: 1 天

    [LV.Master]伴坛终老

    97

    主题

    4688

    帖子

    12

    版主

    Rank: 7Rank: 7Rank: 7

    积分
    10080
    最后登录
    2025-7-2
    发表于 2020-12-2 17:33:56 | 显示全部楼层
    好长的帖子啊~~
    需要一些时间来消化
    该会员没有填写今日想说内容.
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /3 下一条

    Archiver|手机版|小黑屋|恩智浦技术社区

    GMT+8, 2025-7-22 12:14 , Processed in 0.087274 second(s), 20 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.

    快速回复 返回顶部 返回列表