基于STM32的USB程序开发笔记(三)
STM32 USB固件函数的一些介绍
接着上篇,详细情况可以查看usb_core(.c/.h),STM32 USB中断事件为以下几种:
void ISTR_CTR(void);
void ISTR_SOF(void);
void ISTR_ESOF(void);
void ISTR_DOVR(void);
void ISTR_ERROR(void);
void ISTR_RESET(void);
void ISTR_WAKEUP(void);
void ISTR_SUSPEND(void);
这些处理函数使能由定义CNTR_MASK决定:
// CNTR mask control
#define CNTR_MASK CNTR_CTRM | CNTR_WKUPM | CNTR_SUSPM | CNTR_ERRM | \
CNTR_SOFM | CNTR_ESOFM | CNTR_RESETM | CNTR_DOVRM \
其中着重说明的是ISTR_RESET()和ISTR_CTR()函数,ISTR_RESET()主要处理USB复位后进行一些初始化任务,ISTR_CTR()则是处理数据正确传输后控制,比如说响应主机。
// *****************************************************************************
// Function Name : INT_ISTR_RESET
// Description : ISTR Reset Interrupt service routines.
// Input :
// Output :
// Return :
// *****************************************************************************
void INT_ISTR_RESET(void)
{
// Set the buffer table address
SetBTABLE(BASEADDR_BTABLE);
// Set the endpoint type: ENDP0
SetEPR_Type(ENDP0, EP_CONTROL);
Clr_StateOut(ENDP0);
// Set the endpoint data buffer address: ENDP0 RX
SetBuffDescTable_RXCount(ENDP0, ENDP0_PACKETSIZE);
SetBuffDescTable_RXAddr(ENDP0, ENDP0_RXADDR);
// Set the endpoint data buffer address: ENDP0 TX
SetBuffDescTable_TXCount(ENDP0, 0);
SetBuffDescTable_TXAddr(ENDP0, ENDP0_TXADDR);
// Initialize the RX/TX status: ENDP0
SetEPR_RXStatus(ENDP0, EP_RX_VALID);
SetEPR_TXStatus(ENDP0, EP_TX_NAK);
// Set the endpoint address: ENDP0
SetEPR_Address(ENDP0, ENDP0);
// ---------------------------------------------------------------------
// TODO: Add you code here
// ---------------------------------------------------------------------
// Set the endpoint type: ENDP1
SetEPR_Type(ENDP1, EP_INTERRUPT);
Clr_StateOut(ENDP1);
// Set the endpoint data buffer address: ENDP1 RX
SetBuffDescTable_RXCount(ENDP1, ENDP1_PACKETSIZE);
SetBuffDescTable_RXAddr(ENDP1, ENDP1_RXADDR);
// Set the endpoint data buffer address: ENDP1 TX
SetBuffDescTable_TXCount(ENDP1, 0);
SetBuffDescTable_TXAddr(ENDP1, ENDP1_TXADDR);
// Initialize the RX/TX status: ENDP1
SetEPR_RXStatus(ENDP1, EP_RX_VALID);
SetEPR_TXStatus(ENDP1, EP_TX_DIS);
// Set the endpoint address: ENDP1
SetEPR_Address(ENDP1, ENDP1);
SetEPR_Type(ENDP2, EP_INTERRUPT);
Clr_StateOut(ENDP2);
// Set the endpoint data buffer address: ENDP2 RX
SetBuffDescTable_RXCount(ENDP2, ENDP2_PACKETSIZE);
SetBuffDescTable_RXAddr(ENDP2, ENDP2_RXADDR);
// Set the endpoint data buffer address: ENDP2 TX
SetBuffDescTable_TXCount(ENDP2, 0);
SetBuffDescTable_TXAddr(ENDP2, ENDP2_TXADDR);
// Initialize the RX/TX status: ENDP2
SetEPR_RXStatus(ENDP2, EP_RX_DIS);
SetEPR_TXStatus(ENDP2, EP_TX_VALID);
// Set the endpoint address: ENDP2
SetEPR_Address(ENDP2, ENDP2);
// ---------------------------------------------------------------------
// End of you code
// ---------------------------------------------------------------------
SetDADDR(0x0080 | vsDeviceInfo.bDeviceAddress);
vsDeviceInfo.eDeviceState = DS_DEFAULT;
vsDeviceInfo.bCurrentFeature = 0x00;
vsDeviceInfo.bCurrentConfiguration = 0x00;
vsDeviceInfo.bCurrentInterface = 0x00;
vsDeviceInfo.bCurrentAlternateSetting = 0x00;
vsDeviceInfo.uStatusInfo.w = 0x0000;
}
在这个ISTR_CTR()函数中,定义了EP0、1、2的传输方式以及各自的缓冲描述符,其中EP0是默认端口,负责完成USB设备的枚举,一般情况是不需要更改的。其他端点配置则需根据实际应用而决定,如何设置请仔细理解STM32的参考手册。
值得说明的是STM32的端点RX/TX缓冲描述表是定义在PMA中的,他是基于分组缓冲区描述报表寄存器(BTABLE)而定位的,各端点RX/TX缓冲描述表说明是数据存储地址以及大小,这个概念需要了解,ST提供的固件很含糊,为此,我在usb_regs.h文件中进行了重新定义,如下:
// USB_IP Packet Memory Area base address
#define PMAAddr (0x40006000L)
// Buffer Table address register
#define BTABLE ((volatile unsigned *)(RegBase + 0x50))
// *****************************************************************************
// Packet memory area: Total 512Bytes
// *****************************************************************************
#define BASEADDR_BTABLE 0x0000
// *****************************************************************************
// PMAAddr + BASEADDR_BTABLE + 0x00000000 : EP0_TX_ADDR
// PMAAddr + BASEADDR_BTABLE + 0x00000002 : EP0_TX_COUNT
// PMAAddr + BASEADDR_BTABLE + 0x00000004 : EP0_RX_ADDR
// PMAAddr + BASEADDR_BTABLE + 0x00000006 : EP0_RX_COUNT
//
// PMAAddr + BASEADDR_BTABLE + 0x00000008 : EP1_TX_ADDR
// PMAAddr + BASEADDR_BTABLE + 0x0000000A : EP1_TX_COUNT
// PMAAddr + BASEADDR_BTABLE + 0x0000000C : EP1_RX_ADDR
// PMAAddr + BASEADDR_BTABLE + 0x0000000E : EP1_RX_COUNT
//
// PMAAddr + BASEADDR_BTABLE + 0x00000010 : EP2_TX_ADDR
// PMAAddr + BASEADDR_BTABLE + 0x00000012 : EP2_TX_COUNT
// PMAAddr + BASEADDR_BTABLE + 0x00000014 : EP2_RX_ADDR
// PMAAddr + BASEADDR_BTABLE + 0x00000016 : EP2_RX_COUNT
//
// PMAAddr + BASEADDR_BTABLE + 0x00000018 : EP3_TX_ADDR
// PMAAddr + BASEADDR_BTABLE + 0x0000001A : EP3_TX_COUNT
// PMAAddr + BASEADDR_BTABLE + 0x0000001C : EP3_RX_ADDR
// PMAAddr + BASEADDR_BTABLE + 0x0000001E : EP3_RX_COUNT
//
// PMAAddr + BASEADDR_BTABLE + 0x00000020 : EP4_TX_ADDR
// PMAAddr + BASEADDR_BTABLE + 0x00000022 : EP4_TX_COUNT
// PMAAddr + BASEADDR_BTABLE + 0x00000024 : EP4_RX_ADDR
// PMAAddr + BASEADDR_BTABLE + 0x00000026 : EP4_RX_COUNT
//
// PMAAddr + BASEADDR_BTABLE + 0x00000028 : EP5_TX_ADDR
// PMAAddr + BASEADDR_BTABLE + 0x0000002A : EP5_TX_COUNT
// PMAAddr + BASEADDR_BTABLE + 0x0000002C : EP5_RX_ADDR
// PMAAddr + BASEADDR_BTABLE + 0x0000002E : EP5_RX_COUNT
//
// PMAAddr + BASEADDR_BTABLE + 0x00000030 : EP6_TX_ADDR
// PMAAddr + BASEADDR_BTABLE + 0x00000032 : EP6_TX_COUNT
// PMAAddr + BASEADDR_BTABLE + 0x00000034 : EP6_RX_ADDR
// PMAAddr + BASEADDR_BTABLE + 0x00000036 : EP6_RX_COUNT
//
// PMAAddr + BASEADDR_BTABLE + 0x00000038 : EP7_TX_ADDR
// PMAAddr + BASEADDR_BTABLE + 0x0000003A : EP7_TX_COUNT
// PMAAddr + BASEADDR_BTABLE + 0x0000003C : EP7_RX_ADDR
// PMAAddr + BASEADDR_BTABLE + 0x0000003E : EP7_RX_COUNT
// *****************************************************************************
//
// PMAAddr + BASEADDR_BTABLE + (0x00000040 - 0x000001FF) : assigned to data buffer
//
// *****************************************************************************
#define BASEADDR_DATA (BASEADDR_BTABLE + 0x00000040)
// ENP0
#define ENDP0_PACKETSIZE 0x40
#define ENDP0_RXADDR BASEADDR_DATA
#define ENDP0_TXADDR (ENDP0_RXADDR + ENDP0_PACKETSIZE)
// ENP1
#define ENDP1_PACKETSIZE 0x40
#define ENDP1_RXADDR (ENDP0_TXADDR + ENDP0_PACKETSIZE)
#define ENDP1_TXADDR (ENDP1_RXADDR + ENDP1_PACKETSIZE)
// ENP2
#define ENDP2_PACKETSIZE 0x40
#define ENDP2_RXADDR (ENDP1_TXADDR + ENDP1_PACKETSIZE)
#define ENDP2_TXADDR (ENDP2_RXADDR + ENDP2_PACKETSIZE)
// ENP3
#define ENDP3_PACKETSIZE 0x40
#define ENDP3_RXADDR (ENDP2_TXADDR + ENDP2_PACKETSIZE)
#define ENDP3_TXADDR (ENDP3_RXADDR + ENDP3_PACKETSIZE)
// ENP4
#define ENDP4_PACKETSIZE 0x40
#define ENDP4_RXADDR (ENDP3_TXADDR + ENDP3_PACKETSIZE)
#define ENDP4_TXADDR (ENDP4_RXADDR + ENDP4_PACKETSIZE)
// ENP5
#define ENDP5_PACKETSIZE 0x40
#define ENDP5_RXADDR (ENDP4_TXADDR + ENDP4_PACKETSIZE)
#define ENDP5_TXADDR (ENDP5_RXADDR + ENDP5_PACKETSIZE)
// ENP6
#define ENDP6_PACKETSIZE 0x40
#define ENDP6_RXADDR (ENDP5_TXADDR + ENDP5_PACKETSIZE)
#define ENDP6_TXADDR (ENDP6_RXADDR + ENDP6_PACKETSIZE)
// ENP7
#define ENDP7_PACKETSIZE 0x40
#define ENDP7_RXADDR (ENDP6_TXADDR + ENDP6_PACKETSIZE)
#define ENDP7_TXADDR (ENDP7_RXADDR + ENDP7_PACKETSIZE)
这样,一般只要在PMA的大小区域内(512Bytes),修改端点EPnR的数据包大小就可以了,当然,实际情况可以根据需要进行更改。
// *****************************************************************************
// Function Name : INT_ISTR_CTR
// Description : ISTR Correct Transfer Interrupt service routine.
// Input :
// Output :
// Return :
// *****************************************************************************
void INT_ISTR_CTR(void)
{
unsigned short wEPIndex;
unsigned short wValISTR;
unsigned short wValENDP;
while( ((wValISTR=GetISTR()) & ISTR_CTR) != 0 )
{
// Get the index number of the endpoints
wEPIndex = wValISTR & ISTR_EP_ID;
if(wEPIndex == 0)
{
// Set endpoint0 RX/TX status: NAK (Negative-Acknowlegment)
SetEPR_RXStatus(ENDP0, EP_RX_NAK);
SetEPR_TXStatus(ENDP0, EP_TX_NAK);
// Transfer direction
if((wValISTR & ISTR_DIR) == 0)
{
// DIR=0: IN
// DIR=0 implies that EP_CTR_TX always 1
ClrEPR_CTR_TX(ENDP0);
CTR_IN0();
return;
}
else
{
// DIR=1: SETUP or OUT
// DIR=1 implies that CTR_TX or CTR_RX always 1
wValENDP = GetEPR(ENDP0);
if((wValENDP & EP_CTR_TX) != 0)
{
ClrEPR_CTR_TX(ENDP0);
CTR_IN0();
return;
}
else if((wValENDP & EP_SETUP) != 0)
{
ClrEPR_CTR_RX(ENDP0);
CTR_SETUP0();
return;
}
else if((wValENDP & EP_CTR_RX) != 0)
{
ClrEPR_CTR_RX(ENDP0);
CTR_OUT0();
return;
}
}
}
// Other endpoints
else
{
wValENDP = GetEPR(wEPIndex);
SetEPR_RXStatus(wEPIndex, EP_RX_NAK);
SetEPR_TXStatus(wEPIndex, EP_TX_NAK);
if((wValENDP & EP_CTR_TX) != 0)
{
ClrEPR_CTR_TX(wEPIndex);
switch(wEPIndex)
{
case ENDP1: CTR_IN1(); break;
case ENDP2: CTR_IN2(); break;
case ENDP3: CTR_IN3(); break;
case ENDP4: CTR_IN4(); break;
case ENDP5: CTR_IN5(); break;
case ENDP6: CTR_IN6(); break;
case ENDP7: CTR_IN7(); break;
default: break;
}
}
if((wValENDP & EP_CTR_RX) != 0)
{
ClrEPR_CTR_RX(wEPIndex);
switch(wEPIndex)
{
case ENDP1: CTR_OUT1(); break;
case ENDP2: CTR_OUT2(); break;
case ENDP3: CTR_OUT3(); break;
case ENDP4: CTR_OUT4(); break;
case ENDP5: CTR_OUT5(); break;
case ENDP6: CTR_OUT6(); break;
case ENDP7: CTR_OUT7(); break;
default: break;
}
}
}
}
}
INT_ISTR_CTR()函数将各自响应事件提取出来,默认端点EP0也是最为复杂的,这个需要查看STM32的参考手册以及USB协议才能更好了解为何如此。到这里STM32 USB里数据传输事件就指向了各个对应的端点。下篇着重说明USB设备的枚举。
管理员 头像是你的本人吗?
可以参考,感谢分享
参考下,谢谢大神