本文主要是介绍S3C2440之USB,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
S3C2440 USB 设备控制器
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是我自己编写的打印函数
- }
- void UsbdMain(void)
- {
- InitDescriptorTable();
- ConfigUsbd();
- PrepareEp1Fifo();
- }
- void ConfigUsbd(void)
- {
- struct s3c24x0_interrupt * intregs = s3c24x0_get_base_interrupt();
- ReconfigUsbd();
- writel((readl(&intregs->INTMSK) & ~(BIT_USBD)), &intregs->INTMSK);
- }
- 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;
- }
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( "<SUS]");
- }
- if(usbdIntpnd&RESUME_INT)
- {
- writeb(RESUME_INT, &usbdevregs->USB_INT_REG);
- // printk("<RSM]");
- }
- if(usbdIntpnd&RESET_INT)
- {
- // printk( "<RST] ReconfigUsbd");
- //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("Ep3hander\n");
- 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);
- }
这个函数比较长,我就不贴代码了。这个函数主要就是针对中断的类型类进行相应的处理:
writeb(0, &usbdevregs->INDEX_REG);
ep0_csr = readb(&usbdevregs->EP0_CSR_IN_CSR1_REG);
EP0_CSR_IN_CSR1_REG寄存器里面有相应端点的相应中断类型。其中有两种中断是不正常状态,EP0_SETUP_END这个标志表示,控制传输,SETUP阶段在数据包到来前,传输就结束了。造成这个原因有可能是总线超时,如果主机在送出SETUP包以及数据包之后,USB设备迟迟不发送应答包。EP0_SENT_STALL 当设备不正常的时候这个标志会置位。
EP0_OUT_PKT_READY 这个标志是正常的状态,在USB设备接收到了一个正确的Token以及数据包是,这个标志就置位,从而程序可以从FIFO中读取数据根据setup包中根据请求类型来进行相应的处理。然后清除这个标志。USB设备控制器就会自动发送ACK包给主机,从而结束一个事务。类似,程序用到用于下载的OUT批量传输端点3的中断处理程序Ep3Handler也是这样一个流程。但是不同的是这里还用到了DMA传输。
- void Ep3Handler(void)
- {
- struct s3c24x0_interrupt * intregs = s3c24x0_get_base_interrupt();
- struct s3c24x0_usb_device * const usbdevregs = s3c24x0_get_base_usb_device();
- U8 out_csr3;
- int fifoCnt;
- writeb(3, &usbdevregs->INDEX_REG);
- out_csr3 = readb(&usbdevregs->OUT_CSR1_REG);
- printk("<3:%x]",out_csr3);
- if(out_csr3 & EPO_OUT_PKT_READY)
- {
- printk("EPO_OUT_PKT_READ\n");
- fifoCnt = readb(&usbdevregs->OUT_FIFO_CNT1_REG);
- #if 0
- RdPktEp3(ep3Buf,fifoCnt);
- PrintEpoPkt(ep3Buf,fifoCnt);
- #else
- if(downloadFileSize==0)
- {
- RdPktEp3((U8 *)downPt,8);
- if(download_run==0)
- {
- downloadAddress=tempDownloadAddress;
- }
- else
- {
- downloadAddress=
- *((U8 *)(downPt+0))+
- (*((U8 *)(downPt+1))<<8)+
- (*((U8 *)(downPt+2))<<16)+
- (*((U8 *)(downPt+3))<<24);
- dwUSBBufReadPtr = downloadAddress;
- dwUSBBufWritePtr = downloadAddress;
- }
- downloadFileSize=
- *((U8 *)(downPt+4))+
- (*((U8 *)(downPt+5))<<8)+
- (*((U8 *)(downPt+6))<<16)+
- (*((U8 *)(downPt+7))<<24);
- checkSum=0;
- downPt=(U8 *)downloadAddress;
- RdPktEp3_CheckSum((U8 *)downPt,fifoCnt-8); //The first 8-bytes are deleted.
- downPt+=fifoCnt-8;
- #if USBDMA
- //CLR_EP3_OUT_PKT_READY() is not executed.
- //So, USBD may generate NAK until DMA2 is configured for USB_EP3;
- writel((readl(&intregs->INTMSK) | BIT_USBD), &intregs->INTMSK);
- return;
- #endif
- }
- else
- {
- #if USBDMA
- printk("<ERROR>");
- #endif
- RdPktEp3_CheckSum((U8 *)downPt,fifoCnt);
- downPt+=fifoCnt; //fifoCnt=64
- }
- #endif
- CLR_EP3_OUT_PKT_READY();
- return;
- }
- //I think that EPO_SENT_STALL will not be set to 1.
- if(out_csr3 & EPO_SENT_STALL)
- {
- printk("[STALL]");
- CLR_EP3_SENT_STALL();
- return;
- }
- }
- u32 usb_receive(char *buf, size_t len, U32 wait)
- {
- int first=1;
- U8 tempMem[16];
- U32 j;
- unsigned int dwRecvTimeSec = 0;
- char c;
- struct s3c24x0_interrupt * intregs = s3c24x0_get_base_interrupt();
- dwUSBBufReadPtr = dwUSBBufBase; // USB_BUF_BASE; thiswa.diy, 2006.06.21
- dwUSBBufWritePtr = dwUSBBufBase; // USB_BUF_BASE; thiswa.diy, 2006.06.21
- bDMAPending = 0;
- /* add by thisway.diy */
- tempDownloadAddress = dwUSBBufBase; // USB_BUF_BASE; thiswa.diy, 2006.06.21 // RAM_BASE, changed by thisway.diy for wince, 2006.06.18
- downloadAddress=(U32)tempMem; //_RAM_STARTADDRESS;
- downPt=(unsigned char *)downloadAddress;
- //This address is used for receiving first 8 byte.
- downloadFileSize=0;
- /*******************************/
- /* File download */
- /*******************************/
- if(isUsbdSetConfiguration==0)
- {
- printk("USB host is not connected yet.\n");
- }
- while(downloadFileSize==0) /* wait until send a file */
- {
- if(first==1 && isUsbdSetConfiguration!=0)
- {
- printk("USB host is connected. Waiting a download.\n");
- first=0;
- }
- }
- printk("get downloadFileSize = %d !!\n",downloadFileSize);
- /* add by thisway.diy */
- if (downloadFileSize - 10 > len)
- {
- printk("Length of file is too big : %d > %d\n", downloadFileSize - 10, len);
- return 0;
- }
- Timer_InitEx();
- Timer_StartEx();
- #if USBDMA
- writel((readl(&intregs->INTMSK) & ~(BIT_DMA2)), &intregs->INTMSK);
- ClearEp3OutPktReady();
- // indicate the first packit is processed.
- // has been delayed for DMA2 cofiguration.
- if(downloadFileSize>EP3_PKT_SIZE) //如果下载文件的大小比端点FIFO大,得开启DMA功能
- {
- if(downloadFileSize - EP3_PKT_SIZE<=(0x80000)) //DMA每次传输的最大数据为1023kb,所以如果大于这个数就得分多次DMA传输
- {
- /* set the source and length */
- dwUSBBufWritePtr = downloadAddress + EP3_PKT_SIZE-8;
- dwWillDMACnt = downloadFileSize - EP3_PKT_SIZE;
- }
- else //多次DMA传输
- {
- dwUSBBufWritePtr = downloadAddress + EP3_PKT_SIZE - 8;
- // dwWillDMACnt = 0x80000 - EP3_PKT_SIZE;
- /* Changed by thisway.diy, 2006.06.22
- * We want When the first DMA interrupt happened,
- * it has received (0x80000 + 8) bytes data from PC
- * The format of data PC send out is: <ADDR(DATA):4>+<SIZE(n+10):4>+<DATA:n>+<CS:2>
- * So, the first 8 bytes isn't the real data we want
- * We want the dwUSBBufWritePtr is always 0x80000 aligin
- */
- dwWillDMACnt = 0x80000 + 8 - EP3_PKT_SIZE;
- }
- totalDmaCount = 0; //DMA传输的次数
- ConfigEp3DmaMode(dwUSBBufWritePtr, dwWillDMACnt); //配置端点3DMA功能,然后设置地址,传输大小
- }
- else //如果下载文件的大小还没有端点FIFO大,那么没有必要开DMA了,加之第一个包已经处理了,所以直接更新写指针吧
- {
- dwUSBBufWritePtr = downloadAddress + downloadFileSize - 8;
- totalDmaCount = downloadFileSize;
- }
- #endif
- printk("\nNow, Downloading [ADDRESS:%xh,TOTAL:%d]\n",
- downloadAddress,downloadFileSize);
- if (wait)
- {
- printk("RECEIVED FILE SIZE:%8d",0);
- j = totalDmaCount + 0x10000;
- while (totalDmaCount != downloadFileSize)
- {
- if (totalDmaCount > j)
- {
- printk("\b\b\b\b\b\b\b\b%8d", j);
- j = totalDmaCount + 0x10000;
- }
- }
- printk("\b\b\b\b\b\b\b\b%8d ", totalDmaCount);
- dwRecvTimeSec = Timer_StopEx();
- if (dwRecvTimeSec == 0)
- {
- dwRecvTimeSec = 1;
- }
- printk("(%dKB/S, %dS)\n", (downloadFileSize/dwRecvTimeSec/1024), dwRecvTimeSec);
- }
- return downloadFileSize - 10;
- }
这段代码的主要作用就是针对传输文件的大小来决定DMA的使用情况,如果使用DMA,则配置端点3为DMA模式,设置DMA传输的终点地址以及每次DMA传输的大小,开启DMA中断。当设置好DMA时,并打开了DMA中断,每传完一个设置的大小,就会进入DMA中断。所以如果传输文件的大小小于1023KB,那么只会进入一次DMA中断。下面就是DMA中断处理程序:
- void IsrDma2(void)
- {
- struct s3c24x0_interrupt * intregs = s3c24x0_get_base_interrupt();
- struct s3c24x0_usb_device * const usbdevregs = s3c24x0_get_base_usb_device();
- U8 out_csr3;
- U32 dwEmptyCnt;
- U8 saveIndexReg = readb(&usbdevregs->INDEX_REG);
- writeb(3, &usbdevregs->INDEX_REG);
- out_csr3 = readb(&usbdevregs->OUT_CSR1_REG);
- ClearPending_my((int)BIT_DMA2);
- /* thisway.diy, 2006.06.22
- * When the first DMA interrupt happened, it has received max (0x80000 + EP3_PKT_SIZE) bytes data from PC
- */
- if (!totalDmaCount)
- totalDmaCount = dwWillDMACnt + EP3_PKT_SIZE; //第一次中断的时候,我们已经接收到了一个包才进入的DMA传输,所以要加一个包的大小
- else
- totalDmaCount+=dwWillDMACnt;
- dwUSBBufWritePtr = ((dwUSBBufWritePtr + dwWillDMACnt - dwUSBBufBase) % dwUSBBufSize) + dwUSBBufBase;
- //更新内存写指针
- if(totalDmaCount>=downloadFileSize)// 最后一次DMA传输完成
- {
- totalDmaCount=downloadFileSize;
- ConfigEp3IntMode(); //恢复端点3的设置
- if(out_csr3& EPO_OUT_PKT_READY)
- {
- CLR_EP3_OUT_PKT_READY();
- }
- writel(((readl(&intregs->INTMSK) | BIT_DMA2) & ~(BIT_USBD)), &intregs->INTMSK);
- //禁止DMA中断,打开USBD中断
- }
- else
- {
- if((totalDmaCount+0x80000)<downloadFileSize) //下一次DMA不能传完文件
- {
- dwWillDMACnt = 0x80000;
- }
- else //下一次DMA能够传完文件,那么就设置DMA传输的大小问剩下的字节数
- {
- dwWillDMACnt = downloadFileSize - totalDmaCount;
- }
- dwEmptyCnt = (dwUSBBufReadPtr - dwUSBBufWritePtr - 1 + dwUSBBufSize) % dwUSBBufSize;
- if (dwEmptyCnt >= dwWillDMACnt)
- {
- ConfigEp3DmaMode(dwUSBBufWritePtr, dwWillDMACnt); //重新配置一下DMA传输大小以及地址
- }
- else
- {
- bDMAPending = 1;
- }
- }
- writeb(saveIndexReg, &usbdevregs->INDEX_REG);
- }
DMA中断处理程序在每次DMA传输完成后进入,根据还剩文件的字节数来决定是否继续传输,以及怎样传输。当传输完成后,禁止DMA中断,将端点3配置成中断模式,好进行下一次的批量传输。
程序源代码在 http://download.csdn.net/detail/yaozhenguo2006/4182352
这篇关于S3C2440之USB的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!