基于 STM32C8T6 与 STM32CubeIDE 的 PS2 手柄开发指南

 

目录

 

一、前言

二、硬件准备

1. 核心组件

2. 硬件连接

三、STM32CubeIDE 配置流程

1. 新建工程

2. GPIO 初始化

3. 编写 PS2 通讯驱动

(1)发送命令函数 PS2_Cmd(u8 CMD)

(2)读取手柄数据函数 PS2_ReadData(void)

(3)初始化与模式配置

四、功能实现与测试

1. 按键检测

2. 摇杆模拟值读取(红灯模式)

3. 震动控制

五、测试与调试

1. 串口监控

2. 模式切换测试

六、注意事项

七、总结

八、完整代码


 

一、前言

        PS2 手柄凭借高性价比、丰富按键及可扩展性强等优势,成为嵌入式开发中常用的遥控设备。本文将详细介绍如何利用 STM32C8T6 开发板与 STM32CubeIDE 集成开发环境,实现 PS2 手柄的解码与控制,涵盖硬件连接、软件配置及功能测试等关键环节。

        这里提供PS2手柄控制舵机并打印串口信息的例程。

 

二、硬件准备

1. 核心组件

  • STM32C8T6 开发板:作为主控芯片,具备丰富的 GPIO 资源与稳定的性能。
  • PS2 手柄及接收器:需注意接收器引脚定义,关键引脚包括:
    • DI/DAT:手柄到主机的 8 位串行数据信号,同步于时钟下降沿传输。
    • DO/CMD:主机到手柄的 8 位串行数据信号,与 DI 方向相反。
    • CS/SEL:触发信号,通讯期间保持低电平。
    • CLK:主机发出的时钟信号,用于数据同步。
    • VDD/GND:电源引脚(3-5V),需注意防反接保护。

2. 硬件连接

参考手册中接收器与 STM32 的连接方式,建议采用以下引脚映射(可根据实际需求调整):

接收器引脚STM32C8T6 引脚(GPIO)功能说明
DI/DATPB12数据输入(浮空输入)
DO/CMDPB13数据输出(推挽输出)
CS/SELPB14片选信号(推挽输出)
CLKPB15时钟信号(推挽输出)
VDD3.3V 或 5V 电源接收器电源
GND开发板 GND共地

三、STM32CubeIDE 配置流程

1. 新建工程

  1. 打开 STM32CubeIDE,创建新项目,选择芯片型号STM32F103C8T6
  2. 配置系统时钟:通过 RCC 设置外部时钟(如 8MHz 晶振),配置系统时钟为 72MHz(参考手册中Stm32_Clock_Init(9)的配置逻辑)。
  3. 创建的时候需要勾选生成.c和.h文件。

2. GPIO 初始化

根据硬件连接,在 CubeMX 图形界面中配置引脚:

 

  • PB12(DI):设置为浮空输入(模拟 PS2 通讯中的输入模式)。
  • PB13(DO)PB14(CS)PB15(CLK):设置为推挽输出,默认电平为高电平(需与手册中PS2_Init函数的初始化逻辑一致)。

记住一定要设置RCC和SYS

然后修改外部晶振为72MHz

SYS设置Serial Wire,不然忘记后芯片会锁住

usart1和usart2设置为异步,并给usart1添加串口助手

这里设置PWM为50Hz

3. 编写 PS2 通讯驱动

参考手册中的pstwo.c文件逻辑,在 CubeIDE 中实现以下核心函数:

(1)发送命令函数 PS2_Cmd(u8 CMD)

c

void PS2_Cmd(uint8_t CMD) {
  uint16_t ref;
  for (ref = 0x01; ref < 0x0100; ref <<= 1) {
    if (CMD & ref) HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_SET); // DO_H
    else HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_RESET); // DO_L
    
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_SET); // CLK_H
    HAL_Delay_us(10); // 延时10μs
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_RESET); // CLK_L
    HAL_Delay_us(10);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_SET); // 再次拉高时钟以读取DI
    
    if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_12)) Data[1] |= ref; // 读取DI数据(仅在特定命令中使用)
  }
  HAL_Delay_us(16); // 命令间隔延时
}

(2)读取手柄数据函数 PS2_ReadData(void)

c

void PS2_ReadData(void) {
  uint8_t byte, ref;
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, GPIO_PIN_RESET); // CS_L
  
  PS2_Cmd(0x01); // 开始命令
  PS2_Cmd(0x42); // 请求数据
  
  for (byte = 2; byte < 9; byte++) { // 读取8字节数据(索引2-8)
    for (ref = 0x01; ref < 0x100; ref <<= 1) {
      HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_SET);
      HAL_Delay_us(10);
      HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_RESET);
      HAL_Delay_us(10);
      HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_SET);
      
      if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_12)) Data[byte] |= ref;
    }
    HAL_Delay_us(16);
  }
  
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, GPIO_PIN_SET); // CS_H
}

(3)初始化与模式配置

  • 端口初始化:在main()中调用MX_GPIO_Init()完成 GPIO 初始化。
  • 手柄配置初始化:参考手册中的PS2_SetInit()逻辑,实现短轮询、进入配置模式、设置震动及保存配置等步骤。

四、功能实现与测试

1. 按键检测

  • 原理:手柄按键状态存储于Data[3](低 8 位)和Data[4](高 8 位),按下为0,未按下为1。通过组合这两个字节得到 16 位按键值(Handkey = (Data[4] << 8) | Data[3])。
  • 代码示例

 

c

uint8_t PS2_DataKey(void) {
  uint8_t index;
  PS2_ClearData();
  PS2_ReadData();
  uint16_t Handkey = (Data[4] << 8) | Data[3];
  
  for (index = 0; index < 16; index++) {
    if ((Handkey & (1 << (MASK[index] - 1))) == 0) return index + 1; // 返回按键编号(1-16)
  }
  return 0; // 无按键按下
}

2. 摇杆模拟值读取(红灯模式)

  • 条件:仅在红灯模式下有效(通过PS2_RedLight()判断),摇杆模拟值存储于Data[5]~Data[8](分别对应右摇杆 X/Y、左摇杆 X/Y)。
  • 代码示例

 

c

uint8_t PS2_AnologData(uint8_t button) {
  return Data[button]; // button取值:5=PSS_RX, 6=PSS_RY, 7=PSS_LX, 8=PSS_LY
}

3. 震动控制

  • 初始化:在PS2_SetInit()中调用PS2_VibrationMode()开启震动模式。
  • 控制函数

 

c

void PS2_Vibration(uint8_t motor1, uint8_t motor2) {
  // motor1:右侧小电机(0x00关,其他值开)
  // motor2:左侧大电机(0x40-0xFF开,值越大震动越强)
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, GPIO_PIN_RESET);
  PS2_Cmd(0x01);
  PS2_Cmd(0x42);
  PS2_Cmd(0x00);
  PS2_Cmd(motor1);
  PS2_Cmd(motor2);
  // 后续命令可参考手册填充
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, GPIO_PIN_SET);
}

五、测试与调试

1. 串口监控

通过 STM32CubeIDE 配置 USART1,在main()中使用printf()输出按键值与摇杆模拟值:

 

c

int main(void) {
  // 初始化代码...
  while (1) {
    uint8_t key = PS2_DataKey();
    if (key != 0) printf("Key %d pressed\r\n", key);
    
    // 输出摇杆模拟值(红灯模式有效)
    printf("LX: %d, LY: %d, RX: %d, RY: %d\r\n",
           PS2_AnologData(PSS_LX), PS2_AnologData(PSS_LY),
           PS2_AnologData(PSS_RX), PS2_AnologData(PSS_RY));
    
    HAL_Delay(100);
  }
}

2. 模式切换测试

  • 红灯模式:摇杆输出模拟值(0-255),L3/R3 按键有效。
  • 绿灯模式:摇杆推至极限时输出方向键值(如 UP/DOWN 等),L3/R3 无效,可通过手柄MODE键或软件锁存配置切换。

六、注意事项

  1. 电源保护:接收器与开发板共电源(3-5V),需确保极性正确,避免过压烧毁器件。
  2. 配对问题:若手柄与接收器无法配对,可尝试仅连接电源(不接数据线),配对成功后再检查时序与程序逻辑。
  3. 时序精度:PS2 通讯时钟频率需严格遵循手册(如时钟周期约 20μs),避免因延时误差导致通讯失败。
  4. 模式锁存:通过PS2_TurnOnAnalogMode()0xEE(不锁存)或0x03(锁存)参数,可控制是否允许手柄按键切换模式。

七、总结

        通过 STM32C8T6 与 STM32CubeIDE 开发 PS2 手柄,核心在于精准实现手册中的通讯时序与命令协议。本文基于手册提供了从硬件连接到软件编程的完整流程,开发者可在此基础上扩展应用场景,如机器人控制、工业设备遥控等。如需进一步优化,可尝试调整震动参数、添加无线传输模块或集成更多传感器功能。

八、完整代码

用cubeide配置完成后,按照ide把线连好后将PS2.c,PS2.h,Key.c,Key.h,main.c粘贴就可以使用了

当然这里我会把完整工程上传。

Key.h

/*
 * Key.h
 *
 *  Created on: Apr 11, 2025
 *      Author: 29245
 */

#ifndef INC_KEY_H_
#define INC_KEY_H_

#include "PS2.h"
#include <stdint.h>

uint8_t Key_GetNum(void);  // 获取按键值(带去抖)

#endif /* INC_KEY_H_ */

Key.c

/*
 * Key.c
 *
 *  Created on: Apr 11, 2025
 *      Author: 29245
 */

#include "Key.h"
#include "PS2.h"
#include <stdint.h>

uint8_t PS2_DataKey(void);

// 定义消抖延时次数
#define DEBOUNCE_DELAY 5

// 获取按键值(带去抖)
uint8_t Key_GetNum(void) {
    static uint8_t key_state = 0;
    static uint8_t debounce_count = 0;
    uint8_t key_val = PS2_DataKey();

    if (key_val) {
        if (!key_state) {
            // 检测到按键按下,开始消抖计数
            debounce_count++;
            if (debounce_count >= DEBOUNCE_DELAY) {
                // 消抖延时结束,确认按键按下
                key_state = 1;
                debounce_count = 0;
                return key_val;
            }
        }
    } else {
        // 按键释放,重置状态和计数器
        key_state = 0;
        debounce_count = 0;
    }
    return 0;
}

 

PS2.h

/*
 * PS2.h
 *
 *  Created on: Apr 11, 2025
 *      Author: 29245
 */

#ifndef INC_PS2_H_
#define INC_PS2_H_

#include "main.h"
#include "gpio.h"
#include <stdint.h>

// 硬件引脚定义(手册版本:PB12-PB15)
#define DAT_GPIO_Port   GPIOB
#define DAT_Pin         GPIO_PIN_12
#define CMD_GPIO_Port   GPIOB
#define CMD_Pin         GPIO_PIN_13
#define CS_GPIO_Port    GPIOB
#define CS_Pin          GPIO_PIN_14
#define CLK_GPIO_Port   GPIOB
#define CLK_Pin         GPIO_PIN_15

#define DI              HAL_GPIO_ReadPin(DAT_GPIO_Port, DAT_Pin)
#define DO_H            HAL_GPIO_WritePin(CMD_GPIO_Port, CMD_Pin, GPIO_PIN_SET)
#define DO_L            HAL_GPIO_WritePin(CMD_GPIO_Port, CMD_Pin, GPIO_PIN_RESET)
#define CS_H            HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET)
#define CS_L            HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET)
#define CLK_H           HAL_GPIO_WritePin(CLK_GPIO_Port, CLK_Pin, GPIO_PIN_SET)
#define CLK_L           HAL_GPIO_WritePin(CLK_GPIO_Port, CLK_Pin, GPIO_PIN_RESET)

// 按键定义(与手册一致)
#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_Y           13
#define PSB_X           14
#define PSB_A           15
#define PSB_B           16

// 摇杆定义(与手册一致)
#define PSS_RX          5   // 右摇杆X轴
#define PSS_RY          6   // 右摇杆Y轴
#define PSS_LX          7   // 左摇杆X轴
#define PSS_LY          8   // 左摇杆Y轴

extern uint8_t Data[9];
extern uint16_t MASK[16];
extern uint16_t Handkey;

// 核心函数声明(补充完整)
void PS2_SetInit(void);
uint8_t PS2_DataKey(void);
void PS2_ReadData(void);
void PS2_ClearData(void);
uint8_t PS2_AnologData(uint8_t button);

// 配置相关函数声明(与PS2.c实现一致)
void PS2_ShortPoll(void);// 短轮询(建立连接)
void PS2_EnterConfing(void);// 进入配置模式
void PS2_TurnOnAnalogMode(void);// 开启模拟模式(红灯模式)
void PS2_VibrationMode(void);// 开启震动模式
void PS2_ExitConfing(void);// 退出配置模式并保存



// 震动控制声明
void PS2_Vibration(uint8_t motor1, uint8_t motor2);




#endif /* INC_PS2_H_ */

PS2.c

/*
 * PS2.c
 *
 *  Created on: Apr 11, 2025
 *      Author: 29245
 */
#include "PS2.h"
#include "usart.h"
#include "gpio.h"

#include <stdint.h>

uint16_t Handkey;
uint8_t Comd[2] = {0x01, 0x42};  // 开始命令,请求数据
uint8_t Data[9] = {0};           // 数据存储数组

void delay_us(uint32_t us) {
    uint32_t delay = (HAL_RCC_GetHCLKFreq() / 4000000 * us);
    while (delay--);
}

uint16_t MASK[16] = {
    PSB_SELECT,  PSB_L3,    PSB_R3,     PSB_START,  // 1-4
    PSB_PAD_UP,  PSB_PAD_RIGHT, PSB_PAD_DOWN, PSB_PAD_LEFT,  // 5-8
    PSB_L2,      PSB_R2,    PSB_L1,     PSB_R1,     // 9-12
    PSB_Y,       PSB_X,     PSB_A,      PSB_B       // 13-16(修改后)
};
// 向手柄发送命令
void PS2_Cmd(uint8_t CMD) {
    uint16_t ref;
    Data[1] = 0;
    for (ref = 0x01; ref < 0x100; ref <<= 1) {
        CMD & ref ? DO_H : DO_L;
        CLK_H; delay_us(5);
        CLK_L; delay_us(5);
        CLK_H;
        if (DI) Data[1] |= ref;
    }
    delay_us(16);
}

// 判断红灯模式
uint8_t PS2_RedLight(void) {
    CS_L;
    PS2_Cmd(Comd[0]);
    PS2_Cmd(Comd[1]);
    CS_H;
    return (Data[1] == 0x73) ? 0 : 1;
}

// 读取手柄数据
void PS2_ReadData(void) {
    uint8_t byte;
    uint16_t ref;
    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; delay_us(5);
            CLK_L; delay_us(5);
            CLK_H;
            if (DI) Data[byte] |= ref;
        }
        delay_us(16);
    }
    CS_H;
}

// 按键处理
uint8_t PS2_DataKey(void) {
    uint8_t index;
    PS2_ClearData();
    PS2_ReadData();
    Handkey = (Data[4] << 8) | Data[3];
    for (index = 0; index < 16; index++) {
        if (!(Handkey & (1 << (MASK[index] - 1)))) {
            return index + 1;
        }
    }
    return 0;
}

// 清除数据
void PS2_ClearData(void) {
    for (uint8_t a = 0; a < 9; a++) {
        Data[a] = 0;
    }
}

// 震动控制
void PS2_Vibration(uint8_t motor1, uint8_t 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);
}

// 初始化配置
void PS2_SetInit(void) {
    PS2_ShortPoll(); PS2_ShortPoll(); PS2_ShortPoll();
    PS2_EnterConfing();
    PS2_TurnOnAnalogMode();
    PS2_VibrationMode();
    PS2_ExitConfing();
}

// 短轮询(建立连接)
void PS2_ShortPoll(void) {
    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(void) {
    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(void) {
    CS_L;
    PS2_Cmd(0x01);
    PS2_Cmd(0x44);
    PS2_Cmd(0x00);
    PS2_Cmd(0x01);  // 模拟模式
    PS2_Cmd(0xEE);  // 允许手柄MODE键切换(0x03为锁存)
    PS2_Cmd(0x00);
    PS2_Cmd(0x00);
    PS2_Cmd(0x00);
    PS2_Cmd(0x00);
    CS_H;
    delay_us(16);
}

// 开启震动模式
void PS2_VibrationMode(void) {
    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(void) {
    CS_L;
    delay_us(16);
    PS2_Cmd(0x01);
    PS2_Cmd(0x43);
    PS2_Cmd(0x00);
    PS2_Cmd(0x00);
    PS2_Cmd(0x5A);  // 连续发送0x5A以保存配置
    PS2_Cmd(0x5A);
    PS2_Cmd(0x5A);
    PS2_Cmd(0x5A);
    PS2_Cmd(0x5A);
    CS_H;
    delay_us(16);
}

// 获取摇杆模拟量(关键函数,之前未正确实现)
uint8_t PS2_AnologData(uint8_t button) {
    if (button >= PSS_RX && button <= PSS_LY) {  // 5-8对应摇杆数据
        return Data[button];
    }
    return 0;  // 无效按钮返回0
}

 

Servo.h

/*
 * Servo.h
 *
 *  Created on: Apr 11, 2025
 *      Author: 29245
 */

#ifndef INC_SERVO_H_
#define INC_SERVO_H_


#include <stdint.h>

// 舵机初始化
void Servo_Init(void);

// 设置舵机角度
void Servo_SetAngle(uint8_t angle);


#endif /* INC_SERVO_H_ */

Servo.c

/*
 * Servo.c
 *
 *  Created on: Apr 11, 2025
 *      Author: 29245
 */
#include "Servo.h"
#include "tim.h"

// 舵机角度转换为 PWM 占空比
uint16_t angle_to_pwm(uint8_t angle) {
    // 0.5ms - 2.5ms 对应 0 - 180 度
    // 定时器 72Mhz 720 分频,自动重装载值 2000,周期 20ms
    // 0.5ms 对应 50,2.5ms 对应 250
    return (angle * (250 - 50) / 180) + 50;
}

// 舵机初始化
void Servo_Init(void) {
    HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
}

// 设置舵机角度
void Servo_SetAngle(uint8_t angle) {
    if (angle > 180) {
        angle = 180;
    }
    __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, angle_to_pwm(angle));

}

main.c

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2025 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "dma.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */
uint8_t uart_tx_buffer[64]; // 足够存储按键信息字符串
volatile uint8_t dma_transmit_complete = 1; // DMA传输完成标志(原子操作)

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_TIM3_Init();
  MX_USART1_UART_Init();
  MX_USART2_UART_Init();
  /* USER CODE BEGIN 2 */
  PS2_SetInit(); // 初始化PS2手柄
  HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1); // 启动舵机PWM输出
  PS2_TurnOnAnalogMode(); // 开启模拟模式


  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {


//	  //串口调试
//	  char test[] = "UART Test OK\r\n";
//	  HAL_UART_Transmit_DMA(&huart1, (uint8_t*)test, sizeof(test));
//
//	       // 等待 DMA 传输完成(调试时可阻塞,正式项目用回调)
//	  while (HAL_UART_GetState(&huart1) != HAL_UART_STATE_READY); // 临时阻塞等待
//
//	  HAL_Delay(1000); // 发送间隔 1 秒


	 // 串口调试按键
	  uint8_t key = Key_GetNum();
	    if (key && dma_transmit_complete) {      // 仅在传输完成时处理新按键
	      dma_transmit_complete = 0;              // 标记传输中
	      char buffer[50];
	      snprintf(buffer, sizeof(buffer), "Key %d pressed\r\n", key);
	      HAL_UART_Transmit_DMA(&huart1, (uint8_t*)buffer, strlen(buffer));
	    }
	    HAL_Delay(10);                           // 降低CPU占用,配合按键去抖
//	  }



//	  //遥感器控制舵机
//	  PS2_ReadData();
//	  uint8_t lx, ly, rx, ry;     // PS2摇杆X/Y轴坐标(左/右摇杆)
//	  int angle;// 获取PS2数据
//	    lx = PS2_AnologData(PSS_LX);
//	    ly = PS2_AnologData(PSS_LY);
//	    rx = PS2_AnologData(PSS_RX);
//	    ry = PS2_AnologData(PSS_RY);
//
//	    // 舵机角度计算(映射0-255到0-180,含边界保护)
//	    angle = (int)lx * 180 / 255;             // 类型转换避免溢出
//	    angle = (angle < 0) ? 0 : angle;         // 下限保护(若舵机支持0°)
//	    angle = (angle > 180) ? 180 : angle;     // 上限保护
//
//	    Servo_SetAngle(angle);                   // 控制舵机
//
//	    // 格式化输出(使用局部变量避免重复函数调用)
//	    char buffer[100];
//	    snprintf(buffer, sizeof(buffer), "LX:%3d LY:%3d RX:%3d RY:%3d Angle:%3d\r\n",
//	             lx, ly, rx, ry, angle);
//	    HAL_UART_Transmit_DMA(&huart1, (uint8_t*)buffer, strlen(buffer));
//
//	    PS2_ClearData();                          // 清除PS2数据缓冲区
//	    HAL_Delay(10);                           // 控制循环频率

    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */

  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) {
    if (huart == &huart1) {
        dma_transmit_complete = 1;             // 传输完成后重置标志
     }

}

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值