7179 views|3 replies

1305

Posts

0

Resources
The OP
 

[New version CH554 evaluation] --- 4.2, USB HID routine learning and verification - code [Copy link]

This post was last edited by yang_alex on 2018-5-4 19:57 USB data communication is initiated by the host. The host sends a command (or request) to the slave, telling the slave what you want to send to me and what you want to do. All USB devices are required to respond to the control commands sent by the host. In the USB specification, this command (or request) has format requirements. Its format is as follows: The commands (or requests) sent by the USB host to the slave are divided into standard commands (requests) and HID device class specific commands (requests). The standard request sent by the USB host to the slave is as follows: The HID device class specific request sent by the USB host to the slave is as follows: In USB data transmission, relevant information is reflected in layers. Specifically, a [transmission] consists of multiple [transactions]; a [transaction] consists of one or more [packets]. [Transfer] includes 4 different types of transfer modes: Control Transfer, Bulk Transfer, Interrupt Transfer and IsochTransfer. Control transfer and interrupt transfer are used in HID devices. All HID devices communicate with the host through the USB control pipe (default pipe, i.e. endpoint 0) and interrupt pipe. [Transaction] can be divided into three categories: Setup transaction: used by the host to send control commands to the device; Data IN transaction: used by the host to read data from the device; Data OUT transaction: used by the host to send data to the device. [Packet] can be divided into four categories: Token Packet, Data Packet, Hand Shake Packet and Special Packet. USB communication is based on packets, which contain a packet identifier PID (Packet Identifier) to indicate the type of packet. Token Packet: Token Packet is used to start a USB transfer. Including the following 4 types: Output (OUT) token packet: used to notify the device to output a data packet Input (IN) token packet: used to notify the device to return a data packet Setup (SETUP) token packet: only used in control transmission, and the same as the output token packet, it also notifies the device to output a data packet. The difference between the two is: SETUP token packet only uses DATA0 data packet, and can only be sent to the control endpoint of the device, and the device must receive it, while OUT token packet has no such restrictions Frame start (SOF) token packet: sent at the beginning of each frame (or microframe), sent in the form of broadcast, all USB full-speed devices and high-speed devices can receive SOF packets. Data Packet As the name implies, data packets are used to transmit data, which are divided into the following types: DATA0 and DATA1 (defined in USB1.1 specification) DATA2 and MDATA packets (added in USB2.0 specification) Handshake packet (Hand Shake Packet) is used to identify whether the transmission is confirmed by the other party. Including the following 4 types: ACK, NAK, STALL, NYET Special Packet (Special Packet) Packet used in special occasions. Including the following 4 types: PRE, ERR, SPLIT, and PING After understanding the above basic knowledge, we start to analyze the code: When detecting USB bus reset, USB bus suspend or wake-up events, or when USB successfully handles data transmission or data reception, the USB protocol processor will set the corresponding interrupt flag and generate an interrupt request. The application can directly query or query and analyze the interrupt flag register USB_INT_FG in the USB interrupt service program, and perform corresponding processing according to IF_BUS_RST and UIF_SUSPEND; And, if UIF_TRANSFER is valid, it is necessary to continue to analyze the USB interrupt status register USB_INT_ST, and perform corresponding processing according to the current endpoint number MASK_UIS_ENDP and the current transaction token PID identifier MASK_UIS_TOKEN. UIS_TOKEN_IN, UIS_TOKEN_OUT, and UIS_TOKEN_SETUP correspond to the [input (IN) token packet], [output (OUT) token packet], and [establishment (SETUP) token packet] mentioned above respectively. (Note, in order to look clearer, I deleted some codes and left the main framework)
  1. /******************************************************************************* * Function Name : DeviceInterrupt() * Description : CH559USB interrupt processing function***************************************************************************/ void DeviceInterrupt( void ) interrupt INT_NO_USB using 1 //USB interrupt service routine, using register group 1 { UINT8 len,i; if(UIF_TRANSFER) //USB transfer completion flag { switch (USB_INT_ST & (MASK_UIS_TOKEN | MASK_UIS_ENDP)) { case UIS_TOKEN_IN | 2: //endpoint 2# endpoint batch upload case UIS_TOKEN_OUT | 2: //endpoint 2# endpoint batch download case UIS_TOKEN_SETUP | 0: //SETUP transaction case UIS_TOKEN_IN | 0: //endpoint0 IN case UIS_TOKEN_OUT | 0: // endpoint0 OUT default:} UIF_TRANSFER = 0; //Write 0 to clear interrupt } if(UIF_BUS_RST) //Device mode USB bus reset interrupt { UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK; UEP1_CTRL = bUEP_AUTO_TOG | UEP_R_RES_ACK; UEP2_CTRL = bUEP_AUTO_TOG | UEP_R_RES_ACK | UEP_T_RES_NAK; USB_DEV_AD = 0x00; UIF_SUSPEND = 0; UIF_TRANSFER = 0; UIF_BUS_RST = 0; //Clear interrupt flag } if (UIF_SUSPEND) //USB bus suspend/wake up completed { UIF_SUSPEND = 0; if ( USB_MIS_ST & bUMS_SUSPEND ) //Suspend { } } else { //Unexpected interrupt, impossible situation USB_INT_FG = 0xFF; //Clear interrupt flag} }
复制代码
The token packet of non-zero endpoint is relatively simple to handle: If the synchronous trigger bit bUEP_R_TOG of the OUT transaction of each endpoint is set in advance, then U_TOG_OK or bUIS_TOG_OK can be used to determine whether the synchronous trigger bit of the currently received data packet matches the synchronous trigger bit of the endpoint. If the data is synchronized, the data is valid; if the data is not synchronized, the data should be discarded. After each processing of the USB send or receive interrupt, the synchronous trigger bit of the corresponding endpoint should be correctly modified to synchronize the next sent data packet and detect whether the next received data packet is synchronized; in addition, by setting bUEP_AUTO_TOG, the corresponding synchronous trigger bit can be automatically flipped after successful transmission or reception.
  1. case UIS_TOKEN_IN | 2: //endpoint 2# endpoint batch upload UEP2_T_LEN = 0; //The pre-used send length must be cleared// UEP1_CTRL ^= bUEP_T_TOG; //If automatic flip is not set, manual flip is required UEP2_CTRL = UEP2_CTRL & ~ MASK_UEP_T_RES | UEP_T_RES_NAK; //Default response NAK break; case UIS_TOKEN_OUT | 2: //endpoint 2# endpoint batch download if ( U_TOG_OK ) // Unsynchronized data packets will be discarded { len = USB_RX_LEN; //Receive data length, data is stored from the first address of Ep2Buffer for ( i = 0; i < len; i ++ ) { Ep2Buffer[MAX_PACKET_SIZE+i] = Ep2Buffer[i] ^ 0xFF; // OUT data is inverted to IN and verified by the computer } UEP2_T_LEN = len; UEP2_CTRL = UEP2_CTRL & ~ MASK_UEP_T_RES | UEP_T_RES_ACK; // Allow upload} break;
复制代码
The token packet of endpoint 0 is more complicated to process, because the various descriptors of the device and the commands of the host are transmitted through endpoint 0. Let's talk about the establishment (SETUP) token packet processing of endpoint 0 first: after identifying the establishment token packet, obtain the USB receive data length and compare it with the size of the establishment request. If it does not match, it is an error and error processing is performed. If it matches, further processing is performed. By obtaining the request type, it is identified whether it is a HID class command or a standard request. (Note that in order to look more refreshing, I deleted some codes and left the main framework)
  1. case UIS_TOKEN_SETUP | 0://SETUP transaction len = USB_RX_LEN; if(len == (sizeof(USB_SETUP_REQ))) { SetupLen = UsbSetupBuf->wLengthL; len = 0; // Default is success and upload 0 length SetupReq = UsbSetupBuf->bRequest; if ( ( UsbSetupBuf->bRequestType & USB_REQ_TYP_MASK ) != USB_REQ_TYP_STANDARD )/*HID class command*/ { } else //Standard request{ } } else { len = 0xff; //Packet length error } if(len == 0xff) { SetupReq = 0xFF; UEP0_CTRL = bUEP_R_TOG | bUEP_T_TOG | UEP_R_RES_STALL | UEP_T_RES_STALL;//STALL } else if(len <= THIS_ENDP0_SIZE) //Upload data or return a 0-length packet in the status stage { UEP0_T_LEN = len; UEP0_CTRL = bUEP_R_TOG | bUEP_T_TOG | UEP_R_RES_ACK | UEP_T_RES_ACK; //The default data packet is DATA1, and the response ACK is returned } else { UEP0_T_LEN = 0; //Although it has not yet reached the status stage, the upload of a 0-length data packet is preset in advance to prevent the host from entering the status stage in advance UEP0_CTRL = bUEP_R_TOG | bUEP_T_TOG | UEP_R_RES_ACK | UEP_T_RES_ACK; //The default data packet is DATA1, and the response ACK is returned } break;
复制代码
HID class command processing: (As can be seen from the code, the program only simply processes the HID Get_Report request)
  1. if ( ( UsbSetupBuf->bRequestType & USB_REQ_TYP_MASK ) != USB_REQ_TYP_STANDARD )/*HID class command*/ { switch( SetupReq ) { case 0x01: //GetReport pDescr = UserEp2Buf; //Transmit data on the control endpoint if(SetupLen >= THIS_ENDP0_SIZE) //Greater than endpoint 0 size, requires special processing { len = THIS_ENDP0_SIZE; } else { len = SetupLen; } break; case 0x02: //GetIdle break; case 0x03: //GetProtocol break; case 0x09: //SetReport break; case 0x0A: //SetIdle break; case 0x0B://SetProtocol break; default: len = 0xFF; /*Command not supported*/ break; } if ( SetupLen > len ) { SetupLen = len; //Limit total length } len = SetupLen >= THIS_ENDP0_SIZE ? THIS_ENDP0_SIZE : SetupLen;//This transmission length memcpy(Ep0Buffer,pDescr,len); //Load upload data SetupLen -= len; pDescr += len; }
复制代码
Standard request processing: (From the code, we can see that the program processes GET_DESCRIPTOR, SET_ADDRESS, GET_CONFIGURATION, SET_CONFIGURATION, CLEAR_FEATURE, SET_FEATURE, and GET_STATUS7 requests respectively)
  1. else //Standard request { switch(SetupReq) //Request code { case USB_GET_DESCRIPTOR: switch(UsbSetupBuf->wValueH) { len = SetupLen >= THIS_ENDP0_SIZE ? THIS_ENDP0_SIZE : SetupLen;//This transfer length memcpy(Ep0Buffer,pDescr,len); //Load upload data SetupLen -= len; pDescr += len; break; case USB_SET_ADDRESS: SetupLen = UsbSetupBuf->wValueL; //Temporarily store USB device address break; case USB_GET_CONFIGURATION: Ep0Buffer[0] = UsbConfig; if ( SetupLen >= 1 ) { len = 1; } break; case USB_SET_CONFIGURATION: UsbConfig = UsbSetupBuf->wValueL; break; case 0x0A: break; case USB_CLEAR_FEATURE: //Clear Feature if ( ( UsbSetupBuf->bRequestType & USB_REQ_RECIP_MASK ) == USB_REQ_RECIP_ENDP )// Endpoint{ } else { len = 0xFF; // Not endpoint not supported } break; case USB_SET_FEATURE: /* Set Feature */ if( ( UsbSetupBuf->bRequestType & 0x1F ) == 0x00 ) /* Set device*/ { if( ( ( ( UINT16 )UsbSetupBuf->wValueH << 8 ) | UsbSetupBuf->wValueL ) == 0x01 ) { if( CfgDesc[ 7 ] & 0x20 ) { /* Set wake-up enable flag*/ } else { len = 0xFF; /* Operation failed*/ } } else { len = 0xFF; /* Operation failed*/ } } else if( ( UsbSetupBuf->bRequestType & 0x1F ) == 0x02 ) /* Set endpoint */ { if( ( ( ( UINT16 )UsbSetupBuf->wValueH << 8 ) | UsbSetupBuf->wValueL ) == 0x00 ) { switch( ( ( UINT16 )UsbSetupBuf->wIndexH << 8 ) | UsbSetupBuf->wIndexL ) { case 0x82:UEP2_CTRL = UEP2_CTRL & (~bUEP_T_TOG) | UEP_T_RES_STALL;/* Set endpoint 2 IN STALL */ break; case 0x02: UEP2_CTRL = UEP2_CTRL & (~bUEP_R_TOG) | UEP_R_RES_STALL;/* Set endpoint 2 OUT Stall */ break; case 0x81: UEP1_CTRL = UEP1_CTRL & (~bUEP_T_TOG) | UEP_T_RES_STALL;/* Set endpoint 1 IN STALL */ break; default: len = 0xFF; /* Operation failed */ break; } } else { len = 0xFF; /* Operation failed */ } } else { len = 0xFF; /* Operation failed */ } break; case USB_GET_STATUS: Ep0Buffer[0] = 0x00; Ep0Buffer[1] = 0x00; if ( SetupLen >= 2 ) { len = 2; } else { len = SetupLen; } break; default: len = 0xff; // Operation failed, break; } } }
复制代码
Let's take a closer look at the processing of GET_DESCRIPTOR (get descriptor request). The high byte of the wValue field of the GET_DESCRIPTOR request indicates the type of descriptor to be obtained, and the low byte indicates the index value of the descriptor. The types of descriptions are: 1 for device descriptor, 2 for configuration descriptor, 3 for string descriptor, 4 for interface descriptor, 5 for endpoint descriptor, and 0x22 for report descriptor.
  1. case USB_GET_DESCRIPTOR: switch(UsbSetupBuf->wValueH) { case 1: //Device descriptor pDescr = DevDesc; //Send the device descriptor to the buffer to be sent len = sizeof(DevDesc); break; case 2: //Configuration descriptor pDescr = CfgDesc; //Send the device descriptor to the buffer to be sent len = sizeof(CfgDesc); break; case 0x22: //Report descriptor pDescr = HIDRepDesc; //Data is ready to upload len = sizeof(HIDRepDesc); Ready = 1; //If there are more interfaces, this standard bit should be valid after the last interface configuration is completed break; default: len = 0xff; //Unsupported command or error break; } if ( SetupLen > len ) { SetupLen = len; //Limit the total length } len = SetupLen >= THIS_ENDP0_SIZE ? THIS_ENDP0_SIZE : SetupLen;//This transmission length memcpy(Ep0Buffer,pDescr,len); //Load upload data SetupLen -= len; pDescr += len; break;
复制代码
The input and output token packets of endpoint 0 are easier to handle.
  1. case UIS_TOKEN_IN | 0: //endpoint0 IN switch(SetupReq) { case USB_GET_DESCRIPTOR: case HID_GET_REPORT: len = SetupLen >= THIS_ENDP0_SIZE ? THIS_ENDP0_SIZE :SetupLen; //The length of this transfer memcpy( Ep0Buffer, pDescr, len ); //Load upload data SetupLen -= len; pDescr += len; UEP0_T_LEN = len; UEP0_CTRL ^= bUEP_T_TOG; //Synchronous flag bit flip break; case USB_SET_ADDRESS: USB_DEV_AD = USB_DEV_AD & bUDA_GP_BIT | SetupLen; UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK; break; default: UEP0_T_LEN = 0; //Status stage completes interrupt or forces upload of 0-length data packet to end control transmission UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK; break; } break; case UIS_TOKEN_OUT | 0: // endpoint0 OUT len = USB_RX_LEN; if(SetupReq == 0x09) { if(Ep0Buffer[0]) { printf("Light on Num Lock LED!\n"); } else if(Ep0Buffer[0] == 0) { printf("Light off Num Lock LED!\n"); } } UEP0_CTRL ^= bUEP_R_TOG; //Synchronous flag bit flip break;
复制代码
At this point, the USB interrupt function has been analyzed. But do you find it strange that you don't see the processing of data packets, handshake packets, and frame start packets? In fact, the processing of these packets is handled by the MCU hardware. We can set or read their status flags in the program. Burn it to the evaluation board and run it. Find the USB device (it has no name, and it took me a long time to find it). We can find that PID 2007, VID 5131, PVN 0000 are exactly what we set in the device descriptor (0x31, 0x51, 0x07, 0x20, 0x00, 0x00). 32 HEX data are sent through the window, and endpoint 2 returns 64 data.
  1. /*Device descriptor*/ UINT8C DevDesc[18] = {0x12,0x01,0x10,0x01,0x00,0x00,0x00,THIS_ENDP0_SIZE, 0x31,0x51,0x07,0x20,0x00,0x00,0x00,0x00, 0x00,0x01 };
复制代码
By monitoring the HID function, it is found that the sent data and the returned data are complementary. Analyzing the code, it can be seen that the received data is inverted in the following code and then placed in the send buffer.
  1. case UIS_TOKEN_IN | 2: //endpoint 2# endpoint batch upload UEP2_T_LEN = 0; //Pre-use send length must be cleared// UEP1_CTRL ^= bUEP_T_TOG; //If automatic flip is not set, manual flip is required UEP2_CTRL = UEP2_CTRL & ~ MASK_UEP_T_RES | UEP_T_RES_NAK; //Default response NAK break; case UIS_TOKEN_OUT | 2://endpoint 2# Endpoint batch downloadif ( U_TOG_OK ) // Unsynchronized data packets will be discarded{ len = USB_RX_LEN; // Received data length, data is stored from the first address of Ep2Bufferfor ( i = 0; i < len; i ++ ) { Ep2Buffer[MAX_PACKET_SIZE+i] = Ep2Buffer ^ 0xFF; // ★★★OUT data is inverted to IN and verified by computer★★★ } UEP2_T_LEN = len; UEP2_CTRL = UEP2_CTRL & ~ MASK_UEP_T_RES | UEP_T_RES_ACK; // Allow upload} break;
复制代码
Modify the code and no longer invert. Recompile and burn, and find that the returned data is the same as the sent data.
  1. case UIS_TOKEN_IN | 2: //endpoint 2# endpoint batch upload UEP2_T_LEN = 0; //The pre-used sending length must be cleared// UEP1_CTRL ^= bUEP_T_TOG; //If automatic flip is not set, manual flip is required UEP2_CTRL = UEP2_CTRL & ~ MASK_UEP_T_RES | UEP_T_RES_NAK; //Default response NAK break; case UIS_TOKEN_OUT | 2: //endpoint 2# endpoint batch download if ( U_TOG_OK ) // Unsynchronized data packets will be discarded { len = USB_RX_LEN; //Received data length, data is stored from the first address of Ep2Buffer for ( i = 0; i < len; i ++ ) { Ep2Buffer[MAX_PACKET_SIZE+i] = Ep2Buffer; // ★★★OUT data is sent back to the computer for verification ★★★ } UEP2_T_LEN = len; UEP2_CTRL = UEP2_CTRL & ~ MASK_UEP_T_RES | UEP_T_RES_ACK; // Upload allowed} break;
复制代码
This content was created by EEWORLD forum user yang_alex, if you need to reprint or use it for commercial purposes, you must obtain the author's consent and indicate the source //endpoint 2# Endpoint batch downloadif ( U_TOG_OK ) // Unsynchronized data packets will be discarded{ len = USB_RX_LEN; // Received data length, data is stored from the first address of Ep2Bufferfor ( i = 0; i < len; i ++ ) { Ep2Buffer[MAX_PACKET_SIZE+i] = Ep2Buffer; // ★★★OUT data is sent back to the computer for verification★★★ } UEP2_T_LEN = len; UEP2_CTRL = UEP2_CTRL & ~ MASK_UEP_T_RES | UEP_T_RES_ACK; // Upload is allowed} break;[/code] This content is originally created by EEWORLD forum user yang_alex. If you want to reprint or use it for commercial purposes, you must obtain the author's consent and indicate the source //endpoint 2# Endpoint batch downloadif ( U_TOG_OK ) // Unsynchronized data packets will be discarded{ len = USB_RX_LEN; // Received data length, data is stored from the first address of Ep2Bufferfor ( i = 0; i < len; i ++ ) { Ep2Buffer[MAX_PACKET_SIZE+i] = Ep2Buffer; // ★★★OUT data is sent back to the computer for verification★★★ } UEP2_T_LEN = len; UEP2_CTRL = UEP2_CTRL & ~ MASK_UEP_T_RES | UEP_T_RES_ACK; // Upload is allowed} break;[/code] This content is originally created by EEWORLD forum user yang_alex. If you want to reprint or use it for commercial purposes, you must obtain the author's consent and indicate the source


This post is from MCU

Latest reply

I use this tool, but why can't I open the device? The device manager is there, but after refreshing the drop-down box of this software, there is no such device. In addition, its program does not use endpoint 1, why can you use ep1 to send? ? ?   Details Published on 2020-3-24 17:45
 

935

Posts

1

Resources
2
 
This post is from MCU
 
Personal signature存储芯片/MCU/SRAM/PSRAM/DDR/FLASH/MRAM。web.www.sramsun.com  QQ3161422826 TEL:13751192923
 

305

Posts

0

Resources
3
 
This post is from MCU
 
Personal signature单价1元含税的USB和Touchkey单片机CH551G已大批量出货,试样QQ:1258305301
 
 

30

Posts

0

Resources
4
 

I use this tool, but why can't I open the device? The device manager is there, but after refreshing the drop-down box of this software, there is no such device.

In addition, its program does not use endpoint 1, why can you use ep1 to send? ? ?

This post is from MCU
 
 
 

Just looking around
Find a datasheet?

EEWorld Datasheet Technical Support

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号
快速回复 返回顶部 Return list