自定义USB BULK设计(一)——固件程序,LPC2378
来源:互联网 发布:nba2kol安德森数据 编辑:程序博客网 时间:2024/04/28 22:07
前段时间买了块D12+51的开发板来玩,现在公司要求用ARM来做一个USB项目。原来用D12也就做了个LED流水灯,还是HID设备的,现在不能满足要求,你总不能显示公司的设备时,还搞个“人体工学设备”。于是把开发流程又理了一遍,这里是第一部分,讲固件的编写。
使用的MCU是NXP的ARM,LPC2378,自带了USB模块,关于2378的寄存器定义什么的,大家去看手册,ZLG网上还有中文版的下。
固件编写其实不用从头开始,我一般是拿官网的例子改改,就变成自己的代码了,这个USB的也是一样,X:/Keil/ARM/Boards/Keil/MCB2300 路径下有多个2300开发板的例子,我拿USBMem的例子改了一下,因为一会我要用的是BULK传输。
1、打开/USBMem下的Memory.Uv2,查看项目的target Option ,你会发现是LPC2368,这个可以改为你想要的芯片类型,只要USB模块相同的系列都行。
2、查看下项目文件列表,如图1所示,删除不需要的文件,增加main.c和USB_trans.c 2个文件,如图2所示
图1 图2
usbcfg.h——usb配置文件
usbcore.c——USB内核文件
usbdesc.c——USB Descriptors
usbhw.c——USB Hardware Layer Module for Philips LPC23xx/24xx
usbuser.c——USB Custom User Module
2.代码修改
(1)usbcfg.h :
这个文件是定义USB相关代码的宏,其他文件根据这些宏来判断某个函数或某个代码段是否需要编译,这样可以最小的减少代码量。
我只定义了以下事件(函数),也可以根据设计需要定义
#define USB_POWER_EVENT 0
#define USB_RESET_EVENT 1
#define USB_SUSPEND_EVENT 0
#define USB_RESUME_EVENT 0
#define USB_WAKEUP_EVENT 0
#define USB_SOF_EVENT 0
#define USB_ERROR_EVENT 0
#define USB_EP_EVENT 0x0005 //该宏使能了2个端口处理函数,USB_EndPoint0和USB_EndPoint2,
#define USB_CONFIGURE_EVENT 1
#define USB_INTERFACE_EVENT 0
#define USB_FEATURE_EVENT 0
以下宏全部设为0,因为我们是自定义的设备,不是以下类别
#define USB_CLASS 0
#define USB_HID 0
#define USB_HID_IF_NUM 0
#define USB_MSC 0
#define USB_MSC_IF_NUM 0
#define USB_AUDIO 0
#define USB_ADC_CIF_NUM 0
#define USB_ADC_SIF1_NUM 0
#define USB_ADC_SIF2_NUM 0
#define USB_CDC 0
#define USB_CDC_CIF_NUM 0
#define USB_CDC_DIF_NUM 0
#define USB_CDC_BUFSIZE 0
(2)usbcore.c——USB内核文件
这个文件不用做任何修改,它包含了一个0端点函数(控制端点)USB_EndPoint0,用于响应控制传输,其他端点的响应函数都放在usbuser.c中。
(3)usbdesc.c
这里放置的是各种描述符,你可以根据需要,改为你自己的设备描述,至于描述符的定义和写法,参考USB协议(1.0和2.0都可以)的第9章。
(4)usbhw.c
这个文件的函数都与硬件相关,就是和MCU的USB模块有关,包括寄存器初始化,读写缓冲区,中断函数等,
USB_Init配置了USB寄存器,因为这个工程本来就只使能了端口2并使用BULK传输,因此你什么都不用改,如果你想讲其他端口配置为BULK或其他,你就对着手册自己改了。
个人觉得它的ISR那里虽然通用性强但是写得有点啰嗦,自己改了一下,因为只用到端口0和端口2(这里的端口指的是逻辑端点,就是一个逻辑端点有2个物理端点),因此ISR的代码改为如下:
void USB_ISR (void) __irq {
U32 disr, val, n, m;
U32 episr, episrCur;
disr = DEV_INT_STAT; /* Device Interrupt Status 获得设备中断状态*/
/* Device Status Interrupt (Reset, Connect change, Suspend/Resume) */
//如果是总线状态改变:包括设备复位,连接改变,挂起/唤醒
if (disr & DEV_STAT_INT) {
DEV_INT_CLR = DEV_STAT_INT;//清该中断标志
WrCmd(CMD_GET_DEV_STAT); //发送获得设备状态命令
val = RdCmdDat(DAT_GET_DEV_STAT); /* Device Status */
if (val & DEV_RST) { /* Reset */
USB_Reset();
USB_Reset_Event();
}
goto isr_end; //不允许同时2个中断置位
}
/* Endpoint's Slow Interrupt */
if (disr & EP_SLOW_INT) {
episr = EP_INT_STAT; //获得端点中断状态
if(episr & EP0RX)//端口0 OUT
{
EP_INT_CLR = 1; //清端点中断,相当于发送了清端点中断指令
while ((DEV_INT_STAT & CDFULL_INT) == 0); //判断命令寄存器是否可读出数据
val = CMD_DATA;//获得选择端点寄存器的内容
if (val & EP_SEL_STP) { // 如果所选端点上一次接收到的包为 SETUP 包
USB_P_EP[0](USB_EVT_SETUP); // 调用函数USB_EndPoint0
}
else
{
USB_P_EP[0](USB_EVT_OUT);
}
}
else if(episr & EP2RX)//端口2 OUT
{
EP_INT_CLR = 0x10; //清端点中断,相当于发送了清端点中断指令
while ((DEV_INT_STAT & CDFULL_INT) == 0); //判断命令寄存器是否可读出数据
USB_P_EP[2](USB_EVT_OUT);
}
isr_end:
VICVectAddr = 0; /* Acknowledge Interrupt */
}
有人可能奇怪,为什么没有端口0和端口2的IN处理,呵呵,这是USB传输的关键所在,如果希望从上位机读取设备的数据,
程序设计者必须自定义帧格式或帧协议,比如自定义一个帧,它的第一个字节为命令字节,如果该字节为0x11,则表示上位机是请求接收数据,如果是其他,则表示上位机是其他操作。
上位机发送OUT帧(writeFile)—>USB发生端口OUT中断->固件程序检测OUT包的第1个字节是否为0x11,如果是,则将RAM缓冲区的数据写到USB硬件缓冲区,然后发送->上位机再执行一个ReadFile函数,就把在PC缓冲区的数据读出来了。
(5)usbuser.c
这个文件定义了很多端口处理函数。里面有个很酷的写法:
#define P_EP(n) ((USB_EP_EVENT & (1 << (n))) ? USB_EndPoint##n : NULL)
/* USB Endpoint Events Callback Pointers */
void (* const USB_P_EP[16]) (U32 event) = {
P_EP(0),
P_EP(1),
P_EP(2),
P_EP(3),
P_EP(4),
P_EP(5),
P_EP(6),
P_EP(7),
P_EP(8),
P_EP(9),
P_EP(10),
P_EP(11),
P_EP(12),
P_EP(13),
P_EP(14),
P_EP(15),
};
##符号会连接两个符号,从而产生新的符号(词法层次)。
就是说,你调用USB_P_EP[2] ,就是调用函数USB_EndPoint2,而且还可以带参数。
USB_P_EP是一个只读型指针数组,其每个元素都是一个指向函数的指针。
由于我只用到端口2,将USB_EndPoint2 函数改写如下
void USB_EndPoint2 (U32 event) {
USB_BulkOut();
}
(6)
usb_trans.h中
#define EP_BULK_IN 0x82
#define EP_BULK_OUT 0x02
usb_trans.c中
void USB_BulkOut(void)
{
U8 BulkLen;
BulkLen = USB_ReadEP(EP_BULK_OUT, usbWBuffer);//读端口2的OUT端点数据
if(usbWBuffer[0]==0x11)//如果是收到上位机读请求,则将数据放到端口2的IN端点,并发送
{ USB_WriteEP(EP_BULK_IN, usbRBuffer, 64);}
}
(7)
main函数中,只要编写以下代码,固件就编写完了。
USB_Init();
USB_Connect(__TRUE); /* USB Connect */
- 自定义USB BULK设计(一)——固件程序,LPC2378
- STM32 USB HID 自定义设备 bulk 传输
- USB设备的Bulk模式驱动程序设计
- USB设备的Bulk模式驱动程序设计
- USB设备的Bulk模式驱动程序设计
- USB设备的Bulk模式驱动程序设计
- USB设备的Bulk模式驱动程序设计
- USB设备的Bulk模式驱动程序设计
- USB固件开发总结(一)zt
- 通过USB升级程序固件
- 初涉USB,初学者USB入门总结(2) 设备固件程序
- 初涉USB,初学者USB入门总结(2) 设备固件程序
- 初涉USB,初学者USB入门总结(2) 设备固件程序
- 初涉USB,初学者USB入门总结(2) 设备固件程序
- LPC2378烧写程序三种方法(E-2378)
- 如何实现自定义 USB Bulk 批量传输设备
- USB设备的Bulk模式驱动程序设计1
- USB设备的Bulk模式驱动程序设计2
- Linux内核NAPI机制分析
- 封装的一个JS拖拽效果,兼容所有主流浏览器
- Source Insight 经典教程
- 有一种人你伤不起
- [转]我是一个硬盘
- 自定义USB BULK设计(一)——固件程序,LPC2378
- MySQL数据库日志
- hp unix和aix视频资料
- h
- windows系统安装
- 最简便的备份MySQL数据库的方法
- 错误记录
- C++50点
- 优化MySQL数据库查询