s3c2440 soc集成了一个usb1.1设备控制器,可以进行全速/低速的控制,中断与批量传输。除了端点0,具有四个端点,每个端点都可以作为中断与批量的端点,每个端点具有128 byte的FIFO,所以端点最大packet可以设置成128byte。并且支持DMA传输。任何一种设备控制器对于软件来说都是一组寄存器:数据,状态,控制。usb 设备控制器也不例外。设置好相应的控制寄存器,并且在数据来时读取数据寄存器,需要发送数据的时候将数据写入输出寄存器。而这种数据的通信建立在对状态寄存器的读取上,往往还会有中断与DMA的操作。s3c2440 usb设备控制器的寄存器分为以下几组:
(1):电源管理寄存器
PWR_REG 负责USB设备挂起等电源设置
(2):地址寄存器
存储USB设备的地址,当主机枚举设备设备的时候设置
(3):中断控制寄存器
EP_INT_REG 端点中断状态寄存器,每当一个端点事件发生的时候,相应的位就会置1
USB_INT_REG 设备中断状态寄存器,主要有三个中断:唤醒,复位,挂起
EP_INT_EN_REG 端点中断使能寄存器
EP_INT_EN_REG 设备中断使能寄存器
(4):编号寄存器
因为USB 设备控制器有五个端点,并且五个端点寄存器大同小异,所以硬件设计上使用了编号寄存器:名字相同但物理寄存器不同。有一个INDEX_REG寄存器,它里面的值指示了具体的哪组物理寄存器。这样的寄存器有七个分别是:
MAXP_REG: 端点最大信息包大小
IN_CSR1_REG
IN_CSR2_REG
OUT_CSR1_REG
OUT_CSR2_REG
OUT_FIFO_CNT1_REG
OUT_FIFO_CNT2_REG
(5):FIFO寄存器
EPO_FIFO_REG
EP1_FIFO_REG
EP2_FIFO_REG
EP3_FIFO_REG
EP4_FIFO_REG
(6):DMA寄存器
端点1~4,每个端点六个,用于设置端点的DMA传输。
USB设备控制器处理了大部分的USB传输细节,并产生相应的中断。所以对USB设备控制器的编程,最关键的部分就是中断处理的部分。比如端点每收到一个token以及后面的数据包,就会产生相应的中断。软件只需要在中断处理程序中读取数据,并且清除中断标志。USB设备控制器就会自动发送应答包。现在网上比较流行的操作s3c2440 usb 设备控制器的程序就是移植到u-boot里面的usb下载程序。因为在u-boot的环境下所以调试起来没有在裸机上来的方便,所以我将这个程序移植到了裸机上,编译环境arm-linux-gcc。下面大体介绍以下这个程序的流程:
这个程序主要是完成了USB设备的枚举,与批量OUT传输,并且开启了DMA,OUT传输用的端点是端点3。这个程序首先从init_usb_slave() 开始:这个函数主要是设置usb slave的引脚以及控制寄存器,设置中断处理程序的入口,以及开启中断。
void usb_init_slave(void)
{
struct s3c24x0_gpio * const gpioregs = s3c24x0_get_base_gpio();
char *mode;
Delay(10);
Usb_Isr_Init();
//设置中断处理程序的入口,需要两个中断USBD与DMA2,USBD用于处理USB事务,DMA2用于处理DMA传输
writel((readl(&gpioregs->MISCCR) & ~((1<<3) | (1<<13))), &gpioregs->MISCCR);
// USBD is selected instead of USBH1
// USB port 1 is enabled.
//
// USBD should be initialized first of all.
//
isUsbdSetConfiguration=0;
UsbdMain(); //主要配置函数
Delay(10);
writel((readl(&gpioregs->GPCDAT) | (1<<5)), &gpioregs->GPCDAT);
//这个操作和Mini2440开发板有关,在USB 设备的信号线上D+上接上了一个GPC5的一个上拉电阻,当GPC5输出高电平的时候,主机的集线器才能检测到设备,从而给复位信号
/* enable USB Device, thisway.diy */
#if USBDMA
mode="DMA";
#else
mode="Int";
#endif
download_run=0; //The default menu is the Download & Run mode.
printk("USB slave is enable!n");
//printk是我自己编写的打印函数
}
在配置完设备后,给GPC5一个高电平,主机就开始枚举设备的过程了。能不能开始这个过程关键是看设备是否配置正确。这就是要看UsbMain()这个函数了。他在usbmain.c中,如下:
void UsbdMain(void)
{
InitDescriptorTable();
ConfigUsbd();
PrepareEp1Fifo();
}
这个函数挺简单,但是调用了三个函数可不简单。第一个就是初始化一些USB描述符,比如设备描述符,配置描述符等,在usb控制传输的时候返回给设备。这个函数在usbsetup.c中。PrepareEp1Fifo()是配置端点1的,这里没有用到。关键的是ConfigUsbd()函数,这个函数是最主要的配置函数。在usblib.c中:
void ConfigUsbd(void)
{
struct s3c24x0_interrupt * intregs = s3c24x0_get_base_interrupt();
ReconfigUsbd();
writel((readl(&intregs->INTMSK) & ~(BIT_USBD)), &intregs->INTMSK);
}
这个函数是个封装函数,第一次配置调用他,并且开启中断。在重置USB的过程中调用的是ReconfigUsbd(),这个函数也在usblib.c中:
void ReconfigUsbd(void)
{
// *** End point information ***
// EP0: control
// EP1: bulk in end point
// EP2: not used
// EP3: bulk out end point
// EP4: not used
struct s3c24x0_usb_device * const usbdevregs = s3c24x0_get_base_usb_device();
writeb(PWR_REG_DEFAULT_VALUE, &usbdevregs->PWR_REG); //disable suspend mode
writeb(0, &usbdevregs->INDEX_REG);
writeb(FIFO_SIZE_8, &usbdevregs->MAXP_REG); //EP0 max packit size = 8
writeb((EP0_SERVICED_OUT_PKT_RDY | EP0_SERVICED_SETUP_END), & usbdevregs->EP0_CSR_IN_CSR1_REG); //EP0:clear OUT_PKT_RDY & SETUP_END
writeb(1, &usbdevregs->INDEX_REG);
#if (EP1_PKT_SIZE==32)
writeb(FIFO_SIZE_32, &usbdevregs->MAXP_REG); //EP1:max packit size = 32
#else
writeb(FIFO_SIZE_64, &usbdevregs->MAXP_REG); //EP1:max packit size = 64
#endif
writeb((EPI_FIFO_FLUSH | EPI_CDT), &usbdevregs->EP0_CSR_IN_CSR1_REG);
writeb((EPI_MODE_IN | EPI_IN_DMA_INT_MASK | EPI_BULK), &usbdevregs->IN_CSR2_REG); //IN mode, IN_DMA_INT=masked
writeb(EPO_CDT, &usbdevregs->OUT_CSR1_REG);
writeb((EPO_BULK | EPO_OUT_DMA_INT_MASK), &usbdevregs->OUT_CSR2_REG);
writeb(2, &usbdevregs->INDEX_REG);
writeb(FIFO_SIZE_64, &usbdevregs->MAXP_REG); //EP2:max packit size = 64
writeb((EPI_FIFO_FLUSH | EPI_CDT | EPI_BULK), &usbdevregs->EP0_CSR_IN_CSR1_REG);
writeb((EPI_MODE_IN | EPI_IN_DMA_INT_MASK), &usbdevregs->IN_CSR2_REG); //IN mode, IN_DMA_INT=masked
writeb(EPO_CDT, &usbdevregs->OUT_CSR1_REG);
writeb((EPO_BULK | EPO_OUT_DMA_INT_MASK), &usbdevregs->OUT_CSR2_REG);
writeb(3, &usbdevregs->INDEX_REG);
#if (EP3_PKT_SIZE==32)
writeb(FIFO_SIZE_32, &usbdevregs->MAXP_REG); //EP3:max packit size = 32
#else
writeb(FIFO_SIZE_64, &usbdevregs->MAXP_REG); //EP3:max packit size = 64
#endif
writeb((EPI_FIFO_FLUSH | EPI_CDT | EPI_BULK), &usbdevregs->EP0_CSR_IN_CSR1_REG);
writeb((EPI_MODE_OUT | EPI_IN_DMA_INT_MASK), &usbdevregs->IN_CSR2_REG); //OUT mode, IN_DMA_INT=masked
writeb(EPO_CDT, &usbdevregs->OUT_CSR1_REG);
//clear OUT_PKT_RDY, data_toggle_bit.
//The data toggle bit should be cleared when initialization.
writeb((EPO_BULK | EPO_OUT_DMA_INT_MASK), &usbdevregs->OUT_CSR2_REG);
writeb(4, &usbdevregs->INDEX_REG);
writeb(FIFO_SIZE_64, &usbdevregs->MAXP_REG); //EP4:max packit size = 64
writeb((EPI_FIFO_FLUSH | EPI_CDT | EPI_BULK), &usbdevregs->EP0_CSR_IN_CSR1_REG);
writeb((EPI_MODE_OUT | EPI_IN_DMA_INT_MASK), &usbdevregs->IN_CSR2_REG); //OUT mode, IN_DMA_INT=masked
writeb(EPO_CDT, &usbdevregs->OUT_CSR1_REG);
//clear OUT_PKT_RDY, data_toggle_bit.
//The data toggle bit should be cleared when initialization.
writeb((EPO_BULK | EPO_OUT_DMA_INT_MASK), &usbdevregs->OUT_CSR2_REG);
writeb((EP0_INT | EP1_INT | EP2_INT | EP3_INT | EP4_INT), &usbdevregs->EP_INT_REG);
writeb((RESET_INT | SUSPEND_INT | RESUME_INT), &usbdevregs->USB_INT_REG);
//Clear all usbd pending bits
writeb((EP0_INT | EP1_INT | EP3_INT), &usbdevregs->EP_INT_EN_REG);
writeb(RESET_INT, &usbdevregs->USB_INT_EN_REG);
ep0State = EP0_STATE_INIT;
}
可以看出首先是操作电源管理寄存器关闭自动挂起功能,然后就是针对每个端点来配置,主要设置端点的最大信息包的大小,端点类型,传输方向。以及是否支持DMA。最主要的是端点0与端点3因为其他三个端点没有用到。最后清除所有的中断状态寄存器的标志。
usb 设备控制器最主要的部分就是中断处理程序,本程序中的USB中断处理程序是void IsrUsbd(void),在usbmain.c中:
void IsrUsbd(void)
{
struct s3c24x0_usb_device * const usbdevregs = s3c24x0_get_base_usb_device();
U8 usbdIntpnd,epIntpnd;
U8 saveIndexReg = readb(&usbdevregs->INDEX_REG);
usbdIntpnd = readb(&usbdevregs->USB_INT_REG);
epIntpnd = readb(&usbdevregs->EP_INT_REG);
// printk( "[INT:EP_I=%x,USBI=%x]",epIntpnd,usbdIntpnd );
if(usbdIntpnd&SUSPEND_INT)
{
writeb(SUSPEND_INT, &usbdevregs->USB_INT_REG);
// printk( " if(usbdIntpnd&RESUME_INT) { writeb(RESUME_INT, &usbdevregs->USB_INT_REG); // printk(" if(usbdIntpnd&RESET_INT) { // printk( " //ResetUsbd(); ReconfigUsbd(); writeb(RESET_INT, &usbdevregs->USB_INT_REG); //RESET_INT should be cleared after ResetUsbd(). PrepareEp1Fifo(); } //以上三个是USB设备中断,主要用到的是复位中断,当GPC5引脚为高时,USB 插入主机,就会产生这个中断 if(epIntpnd&EP0_INT) { writeb(EP0_INT, &usbdevregs->EP_INT_REG); Ep0Handler(); } //对于每个端点中断都有不同的处理程序,这个是端点0的处理程序。在设备枚举的过程中使用。 if(epIntpnd&EP1_INT) { writeb(EP1_INT, &usbdevregs->EP_INT_REG); Ep1Handler(); } if(epIntpnd&EP2_INT) { writeb(EP2_INT, &usbdevregs->EP_INT_REG); // printk("<2:TBD]n"); //not implemented yet //Ep2Handler(); } if(epIntpnd&EP3_INT) { writeb(EP3_INT, &usbdevregs->EP_INT_REG); printk("Ep3handern"); Ep3Handler(); } if(epIntpnd&EP4_INT) { writeb(EP4_INT, &usbdevregs->EP_INT_REG); // printk("<4:TBD]n"); //not implemented yet //Ep4Handler(); } ClearPending_my((int)BIT_USBD); writeb(saveIndexReg, &usbdevregs->INDEX_REG);
上一篇:基于mini2440的USB视频采集
下一篇:mini2440 IIC 裸机程序记录
推荐阅读最新更新时间:2024-11-19 23:59
设计资源 培训 开发板 精华推荐
- Openpilot:一个Github上星标49.2k、开源的高级驾驶员辅助系统
- ZRC500 精密 5.0 伏低拐点电流电压基准的典型应用
- ADP2107-BL1-EVZ,用于 ADP2107-BL1 2A 同步降压 DC 至 DC 转换器的评估板
- 便携式 3 节镍氢/镍镉电池充电器
- LTC3624EMSE-3.3 3.3V 输出电压、2A 同步降压型稳压器和 2.25MHz 的典型应用
- DK-AS-5SGXEA7NES,开发板自带两个Stratix V GX FPGA器件,提供一个硬件平台
- 1.3寸(240*240)ips屏st7789驱动板
- 用于带有在线数字微调功能的SEA01恒压和电流控制器的评估板
- REF43 +2.5V 低功耗对数放大器参考电压的典型应用
- 为建筑物内保护解决方案保护 Legerity 用户线路接口电路