Raspberry Pi ROS car based on STM32F103 - PS2 remote control program analysis

Preface

The PS2 controller is the remote control controller of Sony's PlayStation2 game console, and the microcontroller is the STM32F103ZET6 of Punctual Atomic.

1. PS controller introduction

The PS2 controller consists of two parts: the controller and the receiver. The controller is mainly responsible for sending button information; the receiver is connected to the microcontroller to receive information from the controller and pass it to the microcontroller. The microcontroller can also send commands to the controller through the receiver. Configure the sending mode of the controller.

2. Instructions for use

2.1 Pin description

Receiver pin output:
1.DI/DAT: signal flow direction, from the handle to the host. This signal is an 8-bit serial data, which is transmitted synchronously on the falling edge of the clock. The reading of the signal is completed during the change of the clock from high to low.
2. DO/CMD: Signal flow direction, from host to handle. This signal is opposite to DI. The signal is an 8-bit serial data, which is transmitted synchronously on the falling edge of the clock.
3.NC: empty port;
4.GND: power ground;
5.VDD: receiver working power supply, power range 3~5V;
6.CS/SEL: used to provide handle trigger signal. During communication, it is at low level;
7.CLK: clock signal, sent by the host, used to maintain data synchronization;
8.NC: empty port;
9.ACK: response signal from the handle to the host. This signal goes low in the last cycle of each 8bits data transmission and CS remains low. If the CS signal does not go low, the PS host will try another peripheral in about 60 microseconds. The ACK port is not used during programming.

2.2 Timing diagram analysis

Timing diagram

1.CS can only transmit data when it is low level, which is a sign of data transmission. When transmitting data, we first pull CS high to indicate that we are ready to transmit data, then pull CS low to start data transmission, and then pull CS high after all the data transmission is completed. Here, the high and low levels of CS can be understood as a switch. Pulling low is a switch that turns on data transmission, and pulling high is a switch that turns off data transmission.
2. DI and DO are completed at the same time, which is full-duplex communication.
3. At the rising edge of the clock, the data of DI and DO intersect, which means that the data is exchanged (the data is only 0 and 1). At this time, the data is not stable, and we cannot read or write data. When the clock is on the falling edge, the data is already stable, and we only start reading and writing data at this time.
4. These 8-bit data are read and written from low to high. We can put the data into an array. One clock transfers one data bit.

Data meaning comparison table:
Data meaning comparison table

When the microcontroller wants to read the controller data or send a command to the controller, it will pull down the CS level and issue a command "0x01"; the controller will reply with its ID "0x41=green light mode, 0x73=red light mode"; in the controller While sending the ID, the microcontroller will transmit 0x42 to request data; then the handle sends 0x5A to tell the microcontroller "data is coming". There are 9 bytes (8 bits) in one communication cycle, and these data are transmitted bit by bit in sequence. After a string of data transmission is completed, the CS level is raised.
idle: The data line is idle, indicating that there is no data transmission on the data line at this time.

3. Handle test

The handle requires two AAA 1.5V batteries to power the receiver, and the receiver is powered by a microcontroller with a power supply range of 3~5V. In the unpaired state, the red light (POWER) of the receiver is always on and the green light (RX) flashes once every second; the red light and green light of the remote control handle flash at the same time. Within a certain period of time, if pairing has not been completed, the handle will enter standby mode and the handle's light will go out. At this time, the "START" key must be used to wake up the handle. Under normal circumstances, the handle and receiver will pair automatically. At this time, the lights of the handle and receiver will all be on, indicating that the handle is paired successfully.
The following are the wiring instructions for the receiver signal line and STM32F103ZET6:
for power supply, VDD is connected to 3.3~5V, and GND is connected to GND.
DI——>PB12
DO——>PB13
CS——>PB14
CLK——>
Serial port wiring (TTL) instructions during PB15 simulation:
RXD——>PA9
TXD——>PA10
Note: How to determine whether the controller can be paired normally? The receiver is only connected to VCC and GND, and no other data lines are connected. When both are powered on, the receiver light keeps flashing, indicating that the pairing is unsuccessful. If the light does not flash, it means that the handle receiver is paired successfully, indicating that the handle and receiver are good.

4. Program analysis

main.c file

#include "stm32f10x.h"
#include "sys.h"
#include "usart.h"		
#include "delay.h"
#include "led.h"	
#include "pstwo.h"
#include "motor.h"
#include "math.h"
#include "stdlib.h"	 	
int PS2_LX,PS2_LY,PS2_RX,PS2_RY,PS2_KEY;	
int main(void)
{
    
    	
	Stm32_Clock_Init(9); //系统时钟设置
	delay_init(72);	     //延时初始化
	uart_init(72,115200);  //串口1初始化 
	TIM3_PWM_Init(900,0); //arr设定计数器自动重装值   
	PS2_SetInit();//配置红绿灯模式                                   
	LED_Init();
	PS2_Init();
	M_Init();	   //电机旋转方向控制信号端口初始化	
		while(1)
		{
    
    		
			PS2_LX=PS2_AnologData(PSS_LX);      
			PS2_LY=PS2_AnologData(PSS_LY);
			PS2_RX=PS2_AnologData(PSS_RX);
			PS2_RY=PS2_AnologData(PSS_RY);
			PS2_KEY=PS2_DataKey();	
			printf("PS2_LX: %d    ",PS2_LX);
			printf("PS2_LY: %d    ",PS2_LY);
			printf("PS2_RX: %d    ",PS2_RX);
			printf("PS2_RY: %d    ",PS2_RY);
			printf("PS2_KE: %d\r\n",PS2_KEY);
			delay_ms(100);
		}
}

PS2.c file

#include "pstwo.h"
#include "usart.h"
u16 Handkey;
u8 Comd[2]={
    
    0x01,0x42};	//开始命令。请求数据
u8 Data[9]={
    
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; //数据存储数组
u16 MASK[]={
    
    
    PSB_SELECT,
    PSB_L3,
    PSB_R3 ,
    PSB_START,
    PSB_PAD_UP,
    PSB_PAD_RIGHT,
    PSB_PAD_DOWN,
    PSB_PAD_LEFT,
    PSB_L2,
    PSB_R2,
    PSB_L1,
    PSB_R1 ,
    PSB_GREEN,
    PSB_RED,
    PSB_BLUE,
    PSB_PINK
	};	//按键值与按键明

//手柄接口初始化    输入  DI->PB12 
//                  输出  DO->PB13    CS->PB14  CLK->PB15
void PS2_Init(void)
{
    
    
    //输入  DI->PB12
	RCC->APB2ENR|=1<<3;     //使能PORTB时钟
	GPIOB->CRH&=0XFFF0FFFF;//PB12设置成输入	默认下拉  
	GPIOB->CRH|=0X00080000;   
    //  DO->PB13    CS->PB14  CLK->PB15
	RCC->APB2ENR|=1<<3;    //使能PORTB时钟  	   	  	 
	GPIOB->CRH&=0X000FFFFF; 
	GPIOB->CRH|=0X33300000;//PB13、PB14、PB15 推挽输出   	 											  
}
//向手柄发送命令
void PS2_Cmd(u8 CMD)
{
    
    
	volatile u16 ref=0x01;
	Data[1] = 0;
	for(ref=0x01;ref<0x0100;ref<<=1)
	{
    
    
		if(ref&CMD)
		{
    
    
			DO_H;                   //输出以为控制位
		}
		else DO_L;
		CLK_H;                        //时钟拉高
		delay_us(50);
		CLK_L;
		delay_us(50);
		CLK_H;
		if(DI)
			Data[1] = ref|Data[1];
	}
}
//判断是否为红灯模式
//返回值;0,红灯模式
//		  其他,其他模式
u8 PS2_RedLight(void)
{
    
    
	CS_L;
	PS2_Cmd(Comd[0]);  //开始命令
	PS2_Cmd(Comd[1]);  //请求数据
	CS_H;
	if( Data[1] == 0X73)   return 0 ;
	else return 1;
}
//读取手柄数据
void PS2_ReadData(void)
{
    
    
	volatile u8 byte=0;
	volatile u16 ref=0x01;
	CS_L;
	PS2_Cmd(Comd[0]);  //开始命令
	PS2_Cmd(Comd[1]);  //请求数据
	for(byte=2;byte<9;byte++)          //开始接受数据
	{
    
    
		for(ref=0x01;ref<0x100;ref<<=1)
		{
    
    
			CLK_H;
			CLK_L;
			delay_us(50);
			CLK_H;
		      if(DI)
		      Data[byte] = ref|Data[byte];
		}
        delay_us(50);
	}
	CS_H;	
}
//对读出来的PS2的数据进行处理   只处理了按键部分   默认数据是红灯模式  只有一个按键按下时
//按下为0, 未按下为1
u8 PS2_DataKey()
{
    
    
	u8 index;
	PS2_ClearData();
	PS2_ReadData();
	Handkey=(Data[4]<<8)|Data[3];     //这是16个按键  按下为0, 未按下为1
	for(index=0;index<16;index++)
	{
    
    	    
		if((Handkey&(1<<(MASK[index]-1)))==0)
		return index+1;
	}
	return 0;          //没有任何按键按下
}
//得到一个摇杆的模拟量	 范围0~256
u8 PS2_AnologData(u8 button)
{
    
    
	return Data[button];
}
//清除数据缓冲区
void PS2_ClearData()
{
    
    
	u8 a;
	for(a=0;a<9;a++)
		Data[a]=0x00;
}
//short poll
void PS2_ShortPoll()
{
    
    
	CS_L;
	delay_us(16);
	PS2_Cmd(0x01);
	PS2_Cmd(0x42);
	PS2_Cmd(0x00);
	PS2_Cmd(0x00);
	PS2_Cmd(0x00);
  CS_H;
	delay_us(16);
}
void PS2_EnterConfing()
{
    
    
	CS_L;
	delay_us(16);
	PS2_Cmd(0x01);
	PS2_Cmd(0x43);
	PS2_Cmd(0x00);
	PS2_Cmd(0x01);
	PS2_Cmd(0x00);
	PS2_Cmd(0x00);
	PS2_Cmd(0x00);
	PS2_Cmd(0x00);
	PS2_Cmd(0x00);
	CS_H;
	delay_us(16);
}
void PS2_TurnOnAnalogMode()
{
    
    
	CS_L;
	PS2_Cmd(0x01);
	PS2_Cmd(0x44);
	PS2_Cmd(0x00);
	PS2_Cmd(0x01);//analog=0x01;digital=0x00 软件设置发送模式
	PS2_Cmd(0xEE);//0X03 锁存设置  0xee 不锁存设置
	PS2_Cmd(0x00);
	PS2_Cmd(0x00);
	PS2_Cmd(0x00);
	PS2_Cmd(0x00);
	CS_H;
	delay_us(16);
}
 void PS2_VibrationMode()
 {
    
    
	 CS_L;
	 delay_us(16);
	 PS2_Cmd(0x01);
	 PS2_Cmd(0x4D);
	 PS2_Cmd(0x00);
	 PS2_Cmd(0x00);
	 PS2_Cmd(0x01);
	 CS_H;
	 delay_us(16);
 }
 void PS2_ExitConfing()
 {
    
    
	 CS_L;
	 delay_us(16);
	 PS2_Cmd(0x01);
	 PS2_Cmd(0x43);
	 PS2_Cmd(0x00);
	 PS2_Cmd(0x00);
	 PS2_Cmd(0x5A);
	 PS2_Cmd(0x5A);
	 PS2_Cmd(0x5A);
	 PS2_Cmd(0x5A);
	 PS2_Cmd(0x5A);
	 CS_H;
	 delay_us(16);
 }
 void PS2_SetInit()
 {
    
    
	 PS2_ShortPoll();
	 PS2_ShortPoll();
	 PS2_ShortPoll();
	 PS2_EnterConfing();
	 PS2_TurnOnAnalogMode();
	 PS2_VibrationMode();
	 PS2_ExitConfing();
 }
 void PS2_Vibration(u8 motor1,u8 motor2)
 {
    
    
	 CS_L;
	 delay_us(16);
	 PS2_Cmd(0x01);
	 PS2_Cmd(0x42);
	 PS2_Cmd(0x00);
	 PS2_Cmd(motor1);
	 PS2_Cmd(motor2);
	 PS2_Cmd(0x00);
	 PS2_Cmd(0x00);
	 PS2_Cmd(0x00);
	 PS2_Cmd(0x00);
	 CS_H;
	 delay_us(16);
 }  

PS2.h file

#ifndef __PSTWO_H
#define __PSTWO_H
#include "delay.h"
#include "sys.h"
#define DI   PBin(12)           //PB12  输入
#define DO_H PBout(13)=1        //命令位高
#define DO_L PBout(13)=0        //命令位低
#define CS_H PBout(14)=1       //CS拉高
#define CS_L PBout(14)=0       //CS拉低
#define CLK_H PBout(15)=1      //时钟拉高
#define CLK_L PBout(15)=0      //时钟拉低
//These are our button constants
#define PSB_SELECT      1
#define PSB_L3          2
#define PSB_R3          3
#define PSB_START       4
#define PSB_PAD_UP      5
#define PSB_PAD_RIGHT   6
#define PSB_PAD_DOWN    7
#define PSB_PAD_LEFT    8
#define PSB_L2         9
#define PSB_R2          10
#define PSB_L1          11
#define PSB_R1          12
#define PSB_GREEN       13
#define PSB_RED         14
#define PSB_BLUE        15
#define PSB_PINK        16
#define PSB_TRIANGLE    13
#define PSB_CIRCLE      14
#define PSB_CROSS       15
#define PSB_SQUARE      26
//#define WHAMMY_BAR		8
//These are stick values
#define PSS_RX 5                //右摇杆X轴数据
#define PSS_RY 6
#define PSS_LX 7
#define PSS_LY 8
extern u8 Data[9];
extern u16 MASK[16];
extern u16 Handkey;
void PS2_Init(void);
u8 PS2_RedLight(void);//判断是否为红灯模式
void PS2_ReadData(void);
void PS2_Cmd(u8 CMD);		  //
u8 PS2_DataKey(void);		  //键值读取
u8 PS2_AnologData(u8 button); //得到一个摇杆的模拟量
void PS2_ClearData(void);	  //清除数据缓冲区
#endif

5. Simulation

Output description: The X-axis is the forward and backward movement parameters of the joystick; the Y-axis is the left and right movement parameters of the joystick. The activity parameter range is 0~255.
a. In the initial state, the analog output of the joystick is 128 and the key value is 0.
initial state
b. Push the left joystick forward, the LY output is 0, and the car moves forward.
The car moves forward

c. Push the right joystick left, the RX output is 0, and the car turns left.
Car turns left

c. Press the handle button, KE outputs the corresponding button value, and the car performs various functions such as acceleration and deceleration.
Output key value

6. Source code link

https://pan.baidu.com/s/1jZF7WUw5WI9UNytRMPHsrA?pwd=TUTE
extraction code: TUTE

Guess you like

Origin blog.csdn.net/weixin_63861197/article/details/123834764