[RISC-V MCU CH32V103 Evaluation] 3. USB HID routine operation
[Copy link]
I have been studying USB for a while. After much research, I think HID is the most suitable one for me.
Because HID does not require any driver, any computer with a USB port should be able to recognize it.
Good USB information found on the Internet:
USB Device Descriptors - Overview
When a USB device is inserted, the host will request various descriptors from the device to identify the device. So what are device descriptors?
Descriptor is a complete data structure that can be implemented through programming in C language and stored in USB devices. It is used to describe all the properties of a USB device. The USB host requires the device to send this information through a series of commands.
The role of the descriptor is to pass information to the host through command operations, so that the host knows what functions the device has, what type of device it belongs to, how much bandwidth it will occupy, what type of transmission method it uses, and the amount of data. Only after the host determines this information can the device actually start working.
What are the standard descriptors for USB?
USB has 5 standard descriptors: device descriptor, configuration descriptor, character descriptor, interface descriptor, and endpoint descriptor.
There is a certain relationship between descriptors. A device has only one device descriptor, while a device descriptor can contain multiple configuration descriptors, and a configuration descriptor can contain multiple interface descriptors. If an interface uses several endpoints, there will be several endpoint descriptors. From this we can see that the relationship between USB descriptors is layered. The top layer is the device descriptor, the configuration descriptor is below, the interface descriptor is below, and the endpoint descriptor is below. When obtaining descriptors, first obtain the device descriptor, and then obtain the configuration descriptor. According to the configuration set length in the configuration descriptor, read back the configuration descriptor, interface descriptor, and endpoint descriptor at a time. It may also be necessary to obtain the device serial number, manufacturer string, product string, etc.
Then, all data exchanges between the host and the microcontroller are completed through interrupts.
The README in the example program explains the functions of the example program:
I just used a tool I found on the Internet to verify it:
The tool is as follows:
Debug.rar
(46.84 KB, downloads: 20)
I sent 1,1,2,2,3,3,4,4,5,5,6,6,7,7
The returned numbers are all taken and returned: as shown in the above picture.
It can be seen that the program is completely correct.
Briefly explain the operating principle:
First initialize the USB:
void USB_DeviceInit( void )
{
R8_USB_CTRL = 0x00;
R8_UEP4_1_MOD = RB_UEP4_RX_EN|RB_UEP4_TX_EN|RB_UEP1_RX_EN|RB_UEP1_TX_EN;
R8_UEP2_3_MOD = RB_UEP2_RX_EN|RB_UEP2_TX_EN|RB_UEP3_RX_EN|RB_UEP3_TX_EN;
R8_UEP5_6_MOD = RB_UEP5_RX_EN|RB_UEP5_TX_EN|RB_UEP6_RX_EN|RB_UEP6_TX_EN;
R8_UEP7_MOD = RB_UEP7_RX_EN|RB_UEP7_TX_EN;
R16_UEP0_DMA = (UINT16)(UINT32)pEP0_RAM_Addr;
R16_UEP1_DMA = (UINT16)(UINT32)pEP1_RAM_Addr;
R16_UEP2_DMA = (UINT16)(UINT32)pEP2_RAM_Addr;
R16_UEP3_DMA = (UINT16)(UINT32)pEP3_RAM_Addr;
R16_UEP4_DMA = (UINT16)(UINT32)pEP4_RAM_Addr;
R16_UEP5_DMA = (UINT16)(UINT32)pEP5_RAM_Addr;
R16_UEP6_DMA = (UINT16)(UINT32)pEP6_RAM_Addr;
R16_UEP7_DMA = (UINT16)(UINT32)pEP7_RAM_Addr;
R8_UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
R8_UEP1_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
R8_UEP2_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
R8_UEP3_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
R8_UEP4_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
R8_UEP5_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
R8_UEP6_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
R8_UEP7_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
R8_USB_INT_FG = 0xFF;
R8_USB_INT_EN = RB_UIE_SUSPEND | RB_UIE_BUS_RST | RB_UIE_TRANSFER;
R8_USB_DEV_AD = 0x00;
R8_USB_CTRL = RB_UC_DEV_PU_EN | RB_UC_INT_BUSY | RB_UC_DMA_EN;
R8_UDEV_CTRL = RB_UD_PD_DIS|RB_UD_PORT_EN;
}
Then in the interrupt, process the host's commands: there are three types, initialization, output, and input
void USB_DevTransProcess( void )
{
UINT8 len, chtype;
UINT8 intflag, errflag = 0;
intflag = R8_USB_INT_FG;
if( intflag & RB_UIF_TRANSFER )
{
switch ( R8_USB_INT_ST & MASK_UIS_TOKEN)
{
case UIS_TOKEN_SETUP:
R8_UEP0_CTRL = RB_UEP_R_TOG | RB_UEP_T_TOG | UEP_R_RES_ACK | UEP_T_RES_NAK;
len = R8_USB_RX_LEN;
if ( len == sizeof( USB_SETUP_REQ ) )
{
SetupReqLen = pSetupReqPak->wLength;
SetupReqCode = pSetupReqPak->bRequest;
chtype = pSetupReqPak->bRequestType;
len = 0;
errflag = 0;
if ( ( pSetupReqPak->bRequestType & USB_REQ_TYP_MASK ) != USB_REQ_TYP_STANDARD )
{
errflag = 0xFF;
}
else
{
switch( SetupReqCode )
{
case USB_GET_DESCRIPTOR:
{
switch( ((pSetupReqPak->wValue)>>8) )
{
case USB_DESCR_TYP_DEVICE:
pDescr = MyDevDescr;
len = MyDevDescr[0];
break;
case USB_DESCR_TYP_CONFIG:
pDescr = MyCfgDescr;
len = MyCfgDescr[2];
break;
case USB_DESCR_TYP_STRING:
switch( (pSetupReqPak->wValue)&0xff )
{
case 1:
pDescr = MyManuInfo;
len = MyManuInfo[0];
break;
case 2:
pDescr = MyProdInfo;
len = MyProdInfo[0];
break;
case 0:
pDescr = MyLangDescr;
len = MyLangDescr[0];
break;
default:
errflag = 0xFF;
break;
}
break;
default :
errflag = 0xff;
break;
}
if( SetupReqLen>len ) SetupReqLen = len;
len = (SetupReqLen >= DevEP0SIZE) ? DevEP0SIZE : SetupReqLen;
memcpy( pEP0_DataBuf, pDescr, len );
pDescr += len;
}
break;
case USB_SET_ADDRESS:
SetupReqLen = (pSetupReqPak->wValue)&0xff;
break;
case USB_GET_CONFIGURATION:
pEP0_DataBuf[0] = DevConfig;
if ( SetupReqLen > 1 ) SetupReqLen = 1;
break;
case USB_SET_CONFIGURATION:
DevConfig = (pSetupReqPak->wValue)&0xff;
break;
case USB_CLEAR_FEATURE:
if ( ( pSetupReqPak->bRequestType & USB_REQ_RECIP_MASK ) == USB_REQ_RECIP_ENDP )
{
switch( (pSetupReqPak->wIndex)&0xff )
{
case 0x82:
R8_UEP2_CTRL = (R8_UEP2_CTRL & ~( RB_UEP_T_TOG|MASK_UEP_T_RES )) | UEP_T_RES_NAK;
break;
case 0x02:
R8_UEP2_CTRL = (R8_UEP2_CTRL & ~( RB_UEP_R_TOG|MASK_UEP_R_RES )) | UEP_R_RES_ACK;
break;
case 0x81:
R8_UEP1_CTRL = (R8_UEP1_CTRL & ~( RB_UEP_T_TOG|MASK_UEP_T_RES )) | UEP_T_RES_NAK;
break;
case 0x01:
R8_UEP1_CTRL = (R8_UEP1_CTRL & ~( RB_UEP_R_TOG|MASK_UEP_R_RES )) | UEP_R_RES_ACK;
break;
default:
errflag = 0xFF;
break;
}
}
else errflag = 0xFF;
break;
case USB_GET_INTERFACE:
pEP0_DataBuf[0] = 0x00;
if ( SetupReqLen > 1 ) SetupReqLen = 1;
break;
case USB_GET_STATUS:
pEP0_DataBuf[0] = 0x00;
pEP0_DataBuf[1] = 0x00;
if ( SetupReqLen > 2 ) SetupReqLen = 2;
break;
default:
errflag = 0xff;
break;
}
}
}
else errflag = 0xff;
if( errflag == 0xff)
{
R8_UEP0_CTRL = RB_UEP_R_TOG | RB_UEP_T_TOG | UEP_R_RES_STALL | UEP_T_RES_STALL;
}
else
{
if( chtype & 0x80 )
{
len = (SetupReqLen>DevEP0SIZE) ? DevEP0SIZE : SetupReqLen;
SetupReqLen -= len;
}
else len = 0;
R8_UEP0_T_LEN = len;
R8_UEP0_CTRL = RB_UEP_R_TOG | RB_UEP_T_TOG | UEP_R_RES_ACK | UEP_T_RES_ACK;
}
break;
case UIS_TOKEN_IN:
switch ( R8_USB_INT_ST & ( MASK_UIS_TOKEN | MASK_UIS_ENDP ) )
{
case UIS_TOKEN_IN:
switch( SetupReqCode )
{
case USB_GET_DESCRIPTOR:
len = SetupReqLen >= DevEP0SIZE ? DevEP0SIZE : SetupReqLen;
memcpy( pEP0_DataBuf, pDescr, len );
SetupReqLen -= len;
pDescr += len;
R8_UEP0_T_LEN = len;
R8_UEP0_CTRL ^= RB_UEP_T_TOG;
break;
case USB_SET_ADDRESS:
R8_USB_DEV_AD = (R8_USB_DEV_AD&RB_UDA_GP_BIT) | SetupReqLen;
R8_UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
break;
default:
R8_UEP0_T_LEN = 0;
R8_UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
break;
}
break;
case UIS_TOKEN_IN | 1:
R8_UEP1_CTRL ^= RB_UEP_T_TOG;
R8_UEP1_CTRL = (R8_UEP1_CTRL & ~MASK_UEP_T_RES) | UEP_T_RES_NAK;
break;
case UIS_TOKEN_IN | 2:
R8_UEP2_CTRL ^= RB_UEP_T_TOG;
R8_UEP2_CTRL = (R8_UEP2_CTRL & ~MASK_UEP_T_RES) | UEP_T_RES_NAK;
break;
case UIS_TOKEN_IN | 3:
R8_UEP3_CTRL ^= RB_UEP_T_TOG;
R8_UEP3_CTRL = (R8_UEP3_CTRL & ~MASK_UEP_T_RES) | UEP_T_RES_NAK;
break;
case UIS_TOKEN_IN | 4:
R8_UEP4_CTRL ^= RB_UEP_T_TOG;
R8_UEP4_CTRL = (R8_UEP4_CTRL & ~MASK_UEP_T_RES) | UEP_T_RES_NAK;
break;
case UIS_TOKEN_IN | 5:
R8_UEP5_CTRL ^= RB_UEP_T_TOG;
R8_UEP5_CTRL = (R8_UEP5_CTRL & ~MASK_UEP_T_RES) | UEP_T_RES_NAK;
break;
case UIS_TOKEN_IN | 6:
R8_UEP6_CTRL ^= RB_UEP_T_TOG;
R8_UEP6_CTRL = (R8_UEP6_CTRL & ~MASK_UEP_T_RES) | UEP_T_RES_NAK;
break;
case UIS_TOKEN_IN | 7:
R8_UEP7_CTRL ^= RB_UEP_T_TOG;
R8_UEP7_CTRL = (R8_UEP7_CTRL & ~MASK_UEP_T_RES) | UEP_T_RES_NAK;
break;
default :
break;
}
break;
case UIS_TOKEN_OUT:
switch ( R8_USB_INT_ST & ( MASK_UIS_TOKEN | MASK_UIS_ENDP ) )
{
case UIS_TOKEN_OUT:
len = R8_USB_RX_LEN;
break;
case UIS_TOKEN_OUT | 1:
if ( R8_USB_INT_ST & RB_UIS_TOG_OK )
{
R8_UEP1_CTRL ^= RB_UEP_R_TOG;
len = R8_USB_RX_LEN;
DevEP1_OUT_Deal( len );
}
break;
case UIS_TOKEN_OUT | 2:
if ( R8_USB_INT_ST & RB_UIS_TOG_OK )
{
R8_UEP2_CTRL ^= RB_UEP_R_TOG;
len = R8_USB_RX_LEN;
DevEP2_OUT_Deal( len );
}
break;
case UIS_TOKEN_OUT | 3:
if ( R8_USB_INT_ST & RB_UIS_TOG_OK )
{
R8_UEP3_CTRL ^= RB_UEP_R_TOG;
len = R8_USB_RX_LEN;
DevEP3_OUT_Deal( len );
}
break;
case UIS_TOKEN_OUT | 4:
if ( R8_USB_INT_ST & RB_UIS_TOG_OK )
{
R8_UEP4_CTRL ^= RB_UEP_R_TOG;
len = R8_USB_RX_LEN;
DevEP4_OUT_Deal( len );
}
break;
case UIS_TOKEN_OUT | 5:
if ( R8_USB_INT_ST & RB_UIS_TOG_OK )
{
R8_UEP5_CTRL ^= RB_UEP_R_TOG;
len = R8_USB_RX_LEN;
DevEP5_OUT_Deal( len );
}
break;
case UIS_TOKEN_OUT | 6:
if ( R8_USB_INT_ST & RB_UIS_TOG_OK )
{
R8_UEP6_CTRL ^= RB_UEP_R_TOG;
len = R8_USB_RX_LEN;
DevEP6_OUT_Deal( len );
}
break;
case UIS_TOKEN_OUT | 7:
if ( R8_USB_INT_ST & RB_UIS_TOG_OK )
{
R8_UEP7_CTRL ^= RB_UEP_R_TOG;
len = R8_USB_RX_LEN;
DevEP7_OUT_Deal( len );
}
break;
}
break;
case UIS_TOKEN_SOF:
break;
default :
break;
}
R8_USB_INT_FG = RB_UIF_TRANSFER;
}
else if( intflag & RB_UIF_BUS_RST )
{
R8_USB_DEV_AD = 0;
R8_UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
R8_UEP1_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
R8_UEP2_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
R8_UEP3_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
R8_UEP4_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
R8_UEP5_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
R8_UEP6_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
R8_UEP7_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
R8_USB_INT_FG |= RB_UIF_BUS_RST;
}
else if( intflag & RB_UIF_SUSPEND )
{
if ( R8_USB_MIS_ST & RB_UMS_SUSPEND ) {;}
else{;}
R8_USB_INT_FG = RB_UIF_SUSPEND;
}
else
{
R8_USB_INT_FG = intflag;
}
}
|