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)
{
U32 cnt, n;
LPC_USB->USBCtrl = ((EPNum & 0x0F) << 2) | CTRL_RD_EN;//写入端点号和读取使能
do
{
cnt = LPC_USB->USBRxPLen;
}
while ((cnt & PKT_RDY) == 0);//等待数据包准备就绪并获得数据包长度
cnt &= PKT_LNGTH_MASK;
for (n = 0; n < (cnt + 3) / 4; n++)//依次读取
{
*((__packed U32 *)pData) = LPC_USB->USBRxData;
pData += 4;
}
LPC_USB->USBCtrl = 0;//读取清零
if (((0x1248 >> EPNum) & 1) == 0) //如果不是同步端点,那么清零缓冲区,是同步端点不能清零0x1234说明3 6 9 12是同步端点
{
WrCmdEP(EPNum, CMD_CLR_BUF);
}
return (cnt);
}
//USB写入端点缓冲区,端点逻辑地址
U32 USB_WriteEP (U32 EPNum, U8 *pData, U32 cnt)
{
U32 n;
LPC_USB->USBCtrl = ((EPNum & 0x0F) << 2) | CTRL_WR_EN;//端点写使能
LPC_USB->USBTxPLen = cnt;//设置写入长度
for (n = 0; n < (cnt + 3) / 4; n++) //循环写入
{
LPC_USB->USBTxData = *((__packed U32 *)pData);
pData += 4;
}
LPC_USB->USBCtrl = 0;
WrCmdEP(EPNum, CMD_VALID_BUF);//使能缓冲区,下一次in包来的时候端点内部数据由SIE(串行引擎)发出
return (cnt); //返回实际写入数据长度
}
//usb获取当前帧计数
U32 USB_GetFrame (void)
{
U32 val;
WrCmd(CMD_RD_FRAME);//读取帧计数命令
val = RdCmdDat(DAT_RD_FRAME);
val = val | (RdCmdDat(DAT_RD_FRAME) << 8);
return (val);
}
//USB枚举过程的核心
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;//最后将端点中断的慢中断清除
}
}
LPC1768的usb使用--硬件篇的更多相关文章
- GSM Sniffing入门之硬件篇
3个月前,听朋友介绍得知OsmocomBB项目.此前一直以为GSM Sniffing需要价格昂贵的专用设备,但osmocomBB的上手成本:一个25元左右的手机,外加一根USB转TTL的串口线,着实让 ...
- 智能家居-2.基于esp8266的语音控制系统(硬件篇)
智能家居-1.基于esp8266的语音控制系统(开篇) 智能家居-2.基于esp8266的语音控制系统(硬件篇) 智能家居-3.基于esp8266的语音控制系统(软件篇) 赞赏支持 QQ:505645 ...
- Linux笔记(固定USB摄像头硬件端口,绑定前后置摄像头)
在Android的系统会有前置摄像头和后置摄像头的定义,摄像头分为SOC类型的摄像头和USB这一类的摄像头,接下要分析就是USB摄像头这一类 . 一般在android或者linux系统中分析一个模块, ...
- 硬件篇-04-SLAM移动底盘机械设计
这篇比较水,发出来主要是为了呼应专栏主题,既然是实现,那各个方面都得讲一下不是. 底盘SW模型 淘的,主要是看上了它有弹簧阻尼器,适合野外,抗震,但是这种底盘结构转向起来比较吃力.是再有个全轮 ...
- 硬件篇-03-SLAM移动底盘电气设计
最近因为在忙毕设,专栏已经1个多月没更,对于托更我很抱歉.不过这几周真的没什么时间,Rick&Morty的最新集我到现在都还没看哈哈. 现在毕设已经搞得差不多了,水专栏文章的快乐生 ...
- usb驱动开发篇简易介绍
我这里重点的介绍如何写驱动程序,对于一些应用程序我就不做介绍了,因为我对于那些高层的东西写得很少.倘若再讲,有班门弄斧之嫌,呵呵! 作为WIN98和WIN2K推荐的一项新技术来说,USB的驱动程序和以 ...
- 程序员必备PC维修法(硬件篇)
自检自修不是万能,但不会自检自修却万万不能.程序员修电脑还是有必要的. 情景:通电情况下,电脑主机无法启动,所有主机内部硬件无反应. 原因:1.CPU.主板.电源积尘.(出现率30%) 2.CPU.主 ...
- #2020征文-开发板# 用鸿蒙开发AI应用(一)硬件篇
目录: 前言 开发板简介 产品特色及功能 产品参数 各个主板功能简介 Hi3516DV300 芯片手册 前言鸿蒙2.0的系统刚开源出来,华为志在打造1+8+N万物互联的全场景智慧生活,不仅是国产操作系 ...
- 硬件篇-02-TX2刷机Jetpack4.3
Jetson TX2收货将近一周了,期间趁摄像头还没到,预先给TX2配置了环境.由于学校要求写毕设日志,故干脆一起在知乎写了,顺带帮助想要给TX2配环境却苦于网络上关于TX2的教程很少的同学. 本期内 ...
随机推荐
- 内存管理 & 内存优化技巧 浅析
内存管理 浅析 下列行为都会增加一个app的内存占用: 1.创建一个OC对象: 2.定义一个变量: 3.调用一个函数或者方法. 如果app占用内存过大,系统可能会强制关闭app,造成闪退现象,影响用户 ...
- AI 人工智能 探索 (八)
绑定下,用来释放内存 布局框架.链接:http://pan.baidu.com/s/1eQzSXZO 密码:25ir 这次 我采用 ngui 来设定界面.除工具栏模块外,其他各类ui模块都是 内存池动 ...
- thinkPHP16---伪静态
url伪静态通常是为了 满足更好的SEO效果,thinkPHP支持伪静态url设置,可以通过设置URL_HTML_SUFFIX的参数 随意在URL的最后添加你想要的静态后缀,而不会影响当前操作的正常执 ...
- System services not available to Activities before onCreate()
应用中涉及到系统的mac地址获取,应该是不能够在oncreate()以前使用
- PHP 常识
PHP常识 1.谈谈你对MVC的认识,介绍几种目前比较流行的框架 由Model(M),View(V),Cntroller(C) 组成的一种设计模式,Model 主要处理一些业务处理类,View是页面显 ...
- [学习opencv]高斯、中值、均值、双边滤波
http://www.cnblogs.com/tiandsp/archive/2013/04/20/3031862.html [学习opencv]高斯.中值.均值.双边滤波 四种经典滤波算法,在ope ...
- 高性能web开发:如何加载js,,js的存放位置
外部JS的阻塞下载 所有浏览器在下载JS的时候,会阻止一切其他活动,比如其他资源的下载,内容的呈现等等.至到JS下载.解析.执行完毕后才开始继续并行下载其他资源并呈现内容. 有人会问:为什么JS不能像 ...
- java.lang.ClassCastException: com.sun.proxy.$Proxy8 cannot be cast to com.bjsxt.service.UserServiceImpl01_AOP.
对于Spring AOP 采用两种代理方法,一种是常规JDK,一种是CGLIB,我的UserDao了一个接口IUserDao,当代理对象实现了至少一个接口时,默认使用 JDK动态创建代理对象,当代理对 ...
- hql 链接查询
第一部分.连接查询 一.内连接 内连接查询操作列出与连接条件匹配的数据行,它使用比较运算符比较被连接列的列值.内连接分三种: 1.等值连接:在连接条件中使用等于号(=)运算符比较被连接列的列值,其查询 ...
- Tree of Life (easy)
Tree of Life (easy) Heidi has finally found the mythical Tree of Life – a legendary combinatorial st ...