stm32F407-PS手柄代码,20240424

一、硬件接线

手柄接收器—stm32板子
GND—GND
VCC—5V
DAT–F3
CMD–F5
CS–F6
CLK–F4

二、PStwo.h

#ifndef __PSTWO_H
#define __PSTWO_H

//F3 F5 F6 F4 F10
//
#include "delay.h"
#include "sys.h"

#define DI   PFin(3)         //PB12  输入

#define DO_H PFout(5)=1      //命令位高
#define DO_L PFout(5)=0      //命令位低

#define CS_H PFout(6)=1       //CS拉高
#define CS_L PFout(6)=0       //CS拉低

#define CLK_H PFout(4)=1      //时钟拉高
#define CLK_L PFout(4)=0      //时钟拉低

//#define LED0 PFout(7)  // DS0 
//#define LED1 PFout(8)// DS1 

//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


三、PStwo.c

#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->PF3 
//                  输出  DO->PB13    CS->PB0  CLK->PB1
void PS2_Init(void)
{
	 GPIO_InitTypeDef      GPIO_InitStructure;
	
	 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);
	 
	 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
	 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
	 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
	 GPIO_Init(GPIOF, &GPIO_InitStructure);
	
  	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);//
	 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6| GPIO_Pin_4;//
	 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//
	 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//
	 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//100MHz
	 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//
	 GPIO_Init(GPIOF, &GPIO_InitStructure);//GPIO
	
	GPIO_SetBits(GPIOF,GPIO_Pin_5);  
  GPIO_SetBits(GPIOF,GPIO_Pin_6); 
  GPIO_SetBits(GPIOF,GPIO_Pin_4); 
}

//向手柄发送命令
void PS2_Cmd(u8 CMD)
{
	//volatile是易变型变量,是防止编译器优化代码时假设这个变量的值,保证每次小心地重新读取值。
	volatile u16 ref=0x01;
	u8 i=0,j=0;
	Data[1] = 0;
	//ref的变化是一个八位二进制数中唯一一个1的位置变化,从最低位到最高位移动,从0000 0001到1000 0000。
	for(ref=0x01;ref<0x0100;ref<<=1)//
	{
	//当ref中1的位置对应CMD中得位置上也为1时,结果为1;当ref中1的位置对应CMD中得位置上为0时,结果为0。CMD的其他位则不影响此结果。
		if(ref&CMD)
		{
			DO_H;  //	delay_us(12);
                 //输出以为控制位
		}
		else DO_L;
		//这个结果为1时,DO_H即输出1,这个结果为0时,DO_L即输出0。因此for循环八次,DO的结果就是将CMD的每一位传送了过去。
		
		CLK_H;                        //时钟拉高
		delay_us(100);
		CLK_L;
		delay_us(100);
		CLK_H;    //手动拉出一个下降沿使DO和DI得以同时传送
		/*
		接下来又对接收到的DI进行判断:当DI为1时,运用按位或操作,根据Data[1]初始值为0000 0000,
		以及按位或的定义,不难理解ref | Data[1]得到新的Data[1]的过程是:ref里的唯一的1以值不变
		位置不变的形式给到结果的二进制数中,比如某一次循环Data[1] = 0000 0010,ref = 0000 1000,
		且DI=1,则ref | Data[1]=0000 1010。而这个给1的操作,只有这一bit的DI=1时才会进行;若DI=0,
		则ref只进行1的移位,不给予,但其实也就相当于这一位ref是给予了0给Data[1]。所以其实判断DI并
		执行从句的这一步在整个for循环后的结果即是将8 bit的DI按位保存到Data[1]。		
		*/
		if(DI)  Data[1] = ref|Data[1];//运用或运算按位存入Data[1]的8位
		
		CLK_H;                        //时钟拉高
		delay_us(100);
	}
	
	
	
}
//判断是否为红灯模式
//返回值;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是易变型变量,是防止编译器优化代码时假设这个变量的值,保证每次小心地重新读取值。
	volatile u8 byte=0;
	volatile u16 ref=0x01;

	CS_L;		
	//delay_us(12);


	PS2_Cmd(Comd[0]);  //开始命令
	PS2_Cmd(Comd[1]);  //请求数据

	for(byte=2;byte<9;byte++)          //开始接受数据
	{
		for(ref=0x01;ref<0x0100;ref<<=1)
		{
			CLK_H; delay_us(5);
			CLK_L;
			delay_us(50);
			
		    if(DI)Data[byte] = ref|Data[byte];
			CLK_H; //delay_us(5);
		}
		
        delay_us(50);
	}
	CS_H;			

}
//清除数据缓冲区
void PS2_ClearData()
{
	u8 a;
	for(a=0;a<9;a++)
		Data[a]=0x00;
}

//对读出来的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++)//循环16次,看哪一个按键被按下
	{	    
		if((Handkey&(1<<(MASK[index]-1)))==0)
		return index+1;
	}
	return 0;          //没有任何按键按下
}

//得到一个摇杆的模拟量	 范围0~256
u8 PS2_AnologData(u8 button)
{
	return Data[button];
}






在这里插入图片描述
四、实物图
在这里插入图片描述
在这里插入图片描述

### STM32F407 实现 PS/2 接口 #### 1. 硬件准备 为了使STM32F407能够通过PS/2接口与设备通信,需先准备好硬件连接。通常情况下,PS/2接口有四个针脚:VCC、GND、DATA和CLK。其中,DATA和CLK分别对应着数据线和时钟线。 对于STM32系列单片机而言,可以任意选择两个GPIO引脚作为PS/2的数据线(DATA)与时钟线(CLK),但推荐使用具备外部中断功能的引脚以便于检测信号变化[^1]。 #### 2. 软件环境搭建 建议采用STM32CubeIDE进行开发工作,该集成开发环境中包含了丰富的外设驱动库和支持多种编程语言的选择。安装完成后,创建一个新的工程并选择对应的MCU型号——STM32F407VG。 #### 3. 初始化设置 在`main.c`文件内初始化必要的资源之前,需要导入HAL库中的相关头文件,并定义全局变量来保存接收到的状态信息: ```c #include "stm32f4xx_hal.h" #define PS2_CLK_PIN GPIO_PIN_x /* Replace with actual pin */ #define PS2_DAT_PIN GPIO_PIN_y /* Replace with actual pin */ UART_HandleTypeDef huart1; TIM_HandleTypeDef htim2; uint8_t ps2_data; // Store received byte from keyboard/mouse volatile uint8_t flag_newdata = 0;// Flag indicating new data available ``` 接着,在`MX_GPIO_Init()`函数里配置上述指定的GPIO端口模式为输入浮空状态;同时开启定时器用于超时判断防止程序陷入无限等待循环之中。 #### 4. 中断服务例程(ISR) 编写一个处理PS/2协议特性的ISR非常重要。当检测到CLK线上下降沿触发事件时进入此函数内部执行相应操作,比如读取当前时刻DAT线上电平高低从而获取一位二进制数直至累积满一字节为止。 ```c void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){ static uint8_t bit_count=0; if(GPIO_Pin==PS2_CLK_PIN){ while(HAL_GetTick()-start_time<5);/* Debounce delay */ start_time = HAL_GetTick(); if(bit_count<=9){ ps2_data<<=1; if(HAL_GPIO_ReadPin(PS2_PORT, PS2_DAT_PIN)!=RESET) ps2_data|=0x01; ++bit_count; if(bit_count==9 && (ps2_data&0x01)==0){ flag_newdata=1; bit_count=0; } }else{ bit_count=0; } } } ``` 以上代码片段展示了如何利用STM32的EXTI特性捕捉来自PS/2设备的数据流,并将其转换成可识别的信息格式存储起来供后续应用层调用解析。 #### 5. 数据接收与发送流程控制逻辑设计 最后一步就是构建一套完整的收发机制了。考虑到不同类型的外围设备可能遵循略有差异的具体交互规则(如键盘会返回扫描码而鼠标则报告相对位移),因此有必要针对特定应用场景定制化这部分算法实现细节。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值