USB (CH9350) note using HID device card reader

Posted by bagnallc on Fri, 04 Mar 2022 11:45:39 +0100

  • The project needs to add a card reader device without transplanting USB routines. An external chip (CH9350) is directly used, which is very stable without driving;
  • I only use one of the functions of this chip: the conversion of HID data from USB to serial port data, the lower computer mode used, and many other functions. See the manual for details. The inconvenient place in the development process is that the output serial port data is added with a protocol, which is not completely transparent. Just analyze the data.
  • If the MCU does not have USB peripherals or does not want to use the USB inside the MCU, the relevant USB processing scheme should be found in Nanjing qinheng, and individual chips and test boards can be applied.
  • Relevant USB documents can be downloaded from the official website, which is very convenient. Most of the supporting resources of other USB are 51 routines (for the reason of age), anyway c file and modify it in combination with your own environment. It is OK to modify it (it is recommended to open the project with VS Code).

1, Overview


2, Principle circuit

  • It requires few peripheral circuits. The lower computer mode I use here only uses one USB (DM\DP),

3, Phenomenon

  • 1. I use a random card swiping machine purchased online. To be honest, the card swiping sensitivity is not very high, or the card I use is degaussed.

  • 2. CH9350 will frequently send request frame data after power on, and the rate is very fast. Pay attention to turn it off when using. After power on, send the following data frames to stop it.

  • 3. The data from swiping the card is as follows, and only a part of it is intercepted (after all, it is a bank card, which should be hidden well to prevent criminals from knowing my card number and want to remit money to me, "=" before is the card number, "=" after that, I guess it is the password through encryption algorithm).

  • The data frames that need to be parsed are the middle part. The rest can be parsed according to the manual. At the beginning, I was still confused. This is not a standard ASCII code. After verification, it is the "USB keyboard common code value table". Once I search online, I will be OK. Later, I will show some of the data frames I use. Just discard some 0x00 received in the red box.

4, Handle

  • The main code is the serial port processing part, which is easy to analyze. Let me record how I process this data and precautions:
  • 1. Swiping the card once will bring in hundreds of bytes, and these bytes are regular, starting with 0x57, but containing 0x00, so you can't simply use the C library function strtok();
  • The data that comes in suddenly after swiping the card has no data frame header (referring to the parsed number 622225210183 ******** and I don't know which is the beginning). Therefore, I use the timeout judgment here as a frame, and empty the parsed receiving buffer after timeout, so as to ensure that the data that comes in each time is all the card numbers and is not stuck or missing from the last time.
  • Directly filter out other data in the serial port interrupt, and save the required card number data into the array; The code is as follows. Although there are problems with this filter writing method (0x57 appears twice at the beginning, and this frame of data will be lost), it is easy to save trouble and there is no verification, but the reliability requirement is not high. In case of extreme packet loss, it is OK to swipe the card again.
#define UART4_RxbufSize 128
uint8_t UART4_Rxbuf[UART4_RxbufSize]={0};		//Serial port 4 receive buffer
uint8_t UART4_RxbufCount=0;  					//Serial port 4 receiving buffer data storage location

#Redefine / / wait for Rev1
#Define revhead2 / / wait for receiving
#Define revhead3 / / wait to receive
#Define revhead4 / / wait for receiving
#Define revhead5 / / wait to receive
#Define revhead6 / / wait to receive
#Define revhead7 / / wait to receive
#Define revhead8 / / wait to receive

uint8_t RevState5=revhead1;         	// Receiving status
uint16_t Uart4TimeOut=0;				//Count in the software timer, clear 0 every time the interrupt data is received, and the timeout is judged as one frame
uint8_t Uart4GetIDFlag=0;


// Serial port interrupt service function
void UART4_IRQHandler(void)
{
  	uint8_t ucTemp;
	if(USART_GetFlagStatus(UART4,USART_IT_RXNE)!=RESET)//Non null interrupt
	{		
			ucTemp = USART_ReceiveData(UART4);		//The flag bit is automatically cleared after receiving
			Uart4TimeOut=0;							//Count in software timer
			Uart4GetIDFlag=0;
			switch(RevState5)
			{
				//Waiting to receive the first synchronization header 0x57
				case  revhead1:
						if (ucTemp==0x57)
						{
							RevState5=revhead2;
						}
				break;
				//Waiting to receive the second synchronization header 0xAB
				case  revhead2:
						if (ucTemp==0xAB)
						{
								RevState5=revhead3;
						}
						else
						{
								RevState5=revhead1;      //State machine reset
						}
						break;   
				//Waiting to receive the third synchronization header 0x88
				case  revhead3:
						if (ucTemp==0x88)
						{
								RevState5=revhead4;
						}
						else
						{
								RevState5=revhead1;      //State machine reset
						}
						break; 
				//Waiting to receive the 4th synchronization header 0x0B
				case  revhead4:
						if (ucTemp==0x0B)
						{
								RevState5=revhead5;
						}
						else
						{
								RevState5=revhead1;      //State machine reset
						}
						break;   		
				//Waiting to receive the 5th synchronization header 0x10
				case  revhead5:	//The reason for the comment is that it becomes 0x11 when it is inserted into another USB port of CH9350, so it is simply skipped
//						if (ucTemp==0x10)
//						{
								RevState5=revhead6;
//						}
//						else
//						{
//								RevState5=revhead1;      // State machine reset
//						}
						break;  
				//Waiting to receive the 6th synchronization header 0x01
				case  revhead6:
						if (ucTemp==0x01)
						{
								RevState5=revhead7;
						}
						else
						{
								RevState5=revhead1;      //State machine reset
						}
						break;  
				//Waiting to receive the 7th synchronization header 0x00
				case  revhead7:
						if (ucTemp==0x00)
						{
								RevState5=revhead8;
						}
						else
						{
								RevState5=revhead1;      //State machine reset
						}
						break;  
				//Waiting to receive the 8th synchronization header 0x00
				case  revhead8:
						if (ucTemp==0x00)
						{
								RevState5=revpakage;
						}
						else
						{
								RevState5=revhead1;      //State machine reset
						}
						break;  						
				//Waiting to receive data content
				case revpakage:
						if(UART4_RxbufCount<UART4_RxbufSize&&ucTemp!=0)
						{
							UART4_Rxbuf[UART4_RxbufCount++]=USBDataToASCII(ucTemp);
						}
						RevState5=revhead1;
						break;
			default:
						RevState5=revhead1;              //State machine reset
				break;
			}
	}	




/* Serial port 4 data receiving and processing thread entry function, which is used to process the card reader thread */
void uart4recv_thread_entry(void *parameter)
{	
	while(1)
	{
		if(Uart4TimeOut>2&&Uart4GetIDFlag==0)	
		{
			Uart4GetIDFlag=1;			//It has timed out. It is judged that one frame has been received			
			memcpy(Vision_t.ID,UART4_Rxbuf,strlen((char*)UART4_Rxbuf));
			memset(UART4_Rxbuf,0,UART4_RxbufSize);
			UART4_RxbufCount=0;
		}
		rt_thread_mdelay(50);
	}
}



Topics: stm32