基于STM32F103RCT6的PS2手柄控制舵机转向小车

news2025/1/20 3:43:20

一、PS2

        (1)当接收器上的绿灯常亮时,证明手柄和接收器配对成功,可以正常进行数据通讯。如果手柄和接收器断开了,按手柄上的START键即可恢复配对;

        (2)当手柄上的MODE指示灯没有点亮的时候,游戏摇杆四个方向输出按键键码值;当点击MODE按键后,手柄上的MODE指示灯变亮(红色),此时游戏摇杆四个方向输出AD值。

        (3)PS2 由手柄与接收器两部分组成,手柄主要负责发送按键信息。接通电源并打开手柄开关时,手柄与接收器自动配对连接,在未配对成功的状态下,接收器绿灯闪烁,手柄上的灯也会闪烁,配对成功后,接收器上绿灯常亮,手柄上灯也常亮,这时可以按“MODE”键,选择手柄发送模式

        (4)红灯模式:遥杆输出模拟值; 绿灯模式:遥杆对应上面四个按键,只有四个极限方向对应。

1.亮灯模式

        即手柄MODE指示灯亮时,手柄ID输出为0x73(网上所谓的红灯模式),左右摇杆才会输出模拟量。L3、R3按键有效

2.无灯模式

        即MODE指示灯不亮灯时,手柄输出为0x41(网上所谓的绿灯模式),左右摇杆不会输出模拟量,其极限值“化身”为左右两边四个方向的按键。L3、R3按键无效

【注意】

        不论在亮灯模式还是无灯模式里,除了L3、R3的其他所有按键均有效

        (在本次项目里我还没有使用过L3、R3,所以不知其工作模式)

3.硬件连接

(1)PS2接收器
(2)PS2接收器转接板

        麻烦一定要用转接板,别想着直接用杜邦线把接收器与单片机连在一起!!!(本人之前一天PS2与单片机没有通讯成功,就是因为没有接转接板)(这个我没有试过,后续可以试一下看看不用转接板是不是真的不行)

注意:

 (1) 使用的时候只需要把接收器插在转接板上面,然后使用杜邦线或者商家给的线连在单片机上即可

(2)注意信号线一定不要接反或者接错。

        用好的代码测新接的硬件就可以知道新接的硬件有没有问题,所以每次要有一个外设测试的基础代码(舵机、电机、PS2、超声波、九轴等这些外设的基础代码,然后CubeMx里面配置都是配置一样的输出口,不然还要一直换引脚)

        这个可以通过自己写好的底层示例代码(就是一个工程只创建单片机与PS2通信的测试代码,通过串口来显示出PS2的数据是否传输到了单片机上)如果原来的代码是正确的,那么就直接压缩文件,再测试硬件是不是有问题的时候,就直接把测试代码烧入新的硬件,如果有反应说明新的硬件连接正确,如果没有成功,说明硬件有问题

        硬件可能出现的问题:

        (1)PS2的信号线和VCC、GND接在单片机上可能接错了,这个是最常见的问题

        (2)单片机的板子有问题:STM32F103RCT6可能出现了问题,换一块板子试一下

        (3)有可能ST-Link接到单片机的杜邦线有问题:可以换新的杜邦线试一下,因为有些老的杜邦线可能接触不良之类出现问题了。(我之前就是换了好多块RCT6的板子接电之后那个LED灯时亮时不亮的,我一直以为是板子的问题,没想到是杜邦线坏了。虽然板子也容易坏,但是一下坏很多块也是没太可能的,就看看是不是ST-Link或者杜邦线有问题,就是一点点排查)

        (4)还有要特别注意的是,接收器的VCC和GND的给电情况(这个可以后续再好好学学PS2的硬件补充一下相关知识)

        如果电压给得很大的话,有可能会出现程序运行到一半就重启的情况(当时省赛的时候就是硬件软件都没问题,但就是走到一快结束的时候突然整个车都重启了,就是因为当时电压给的是11伏,太大了,所以相当于复位了(之前CBT6复位有个小方法就是VCC和GND短接一下,就能很起到复位的效果))

         所以当时我的阿克曼小车一按MODE键过一会儿就重启有可能是我稳压上面是12伏,电压太大直接复位了,后面把电压调小了,还是这种问题就是我的PS2接的是5伏的VCC,我给他给到3.3的VCC就没有出现这种情况了,所以有可能是电压的问题。(后续学一下TB6612给单片机供电,单片机给外设供电的知识,这块儿每次都是靠蒙)

        (5)还有一种可能性很低的情况出现:ST-Link和PS2的硬件坏了,但是这种一般不会出现,一般都是板子有问题(因为很多板子都用了很久了,所以也不知道之前有没有出现什么毛病)

        (6)有一种可能性很高的情况:就是软件输入进去,硬件还没有反应过来,过一会儿就好了        

        (之前省赛的时候,在软件里面加一个延时的Delay函数,硬件的OLED就可以显示C8T6和RCT6的超声波的数据了,而且C8T6也不需要复位了,这种情况就是软件准备好了,但是硬件还没有准备好,所以加一个Delay函数,等待硬件准备好)

        (我记得我的阿克曼小车当时调试的时候就是可以后退但不能前进,不过前进有扭矩,就说明信号是接收到的,但是把硬件放一会儿了就好了,所以有时候可能是代码烧入太多,然后硬件卡住了,所以等一会儿,以后可以多留心一下这种情况,看看到底是哪里出现了问题)

(3)PS2接收器转接板与单片机的引脚连接

使用PA7-->CMD\DO将数据从单片机发送给PS2手柄(单片机发、PS2接)

                 MOSI(Master Output Slave Input):主机输出,从机输入

使用PA6-->DAT\DI将数据从PS2手柄接收到单片机(单片机接、PS2发)

                 MISO(Master Input Slave Output):主机输入,从机输出      

使用PA4-->CS作为片选信号

使用PA5-->CLK作为时钟信号

使用GND与3V3给PS2接收器供电
 

二、CubeMx的配置

(1)配置好一个串口,不需要打开中断和DMA

首先配置CubeMx里面的串口 

 然后配置Keil5里面的串口
(1)放在int 外面

头文件里面要包含这两个,方便打印(C语言里面的知识) 

(可以试试没有这个会发生什么,补充一下C语言的理论知识,忘了)

#include "stdio.h"
#include "string.h"

 这个是串口的重定向

(可以试试没有这个会发生什么,补充一下串口的理论知识,忘了)

int fputc(int ch,FILE *f)
{
	HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,HAL_MAX_DELAY);
	return ch;
}

int fgetc(FILE *f)
{
	uint8_t ch;
	HAL_UART_Receive( &huart1,(uint8_t*)&ch,1, HAL_MAX_DELAY );
	return ch;
}
(2) 放在while里面

        (int函数里面while函数外面没有什么需要配置的,因为没有开串口中断,如果开了中断的话需要写一下)

printf ("%d",ButtonValue[i]);//printf("Hello world");
//这个是打印的东西
HAL_Delay (10);
//这个是延时,就不会打印的那么快    
printf ("\r\n");
//这个是换行符,跟C语言有点区别
(3)最重要的 

        如果串口的keil5的代码写的没问题,串口助手的波特率没有问题,端口没有选错的情况下,还是没有办法显示串口打印的信息,就看看配置里面串口的这个开没开,同时可以检测串口上除了红色的灯亮了,看看紫色的灯亮没亮闪没闪,一般紫色的灯在闪的话(这个我也记不清了,记得是在闪)就说明串口在工作,代码没问题,硬件没问题,CubeMx和串口助手的配置都没有问题的情况下还没有反应,那就只能是串口助手有问题,换一个串口助手

        (这种都是上电之后都会亮的灯,如果没亮要么就是VCC、GND接反了,要么就是硬件坏了,要么就是杜邦线接触不良。像RCT6、C8T6、ESP这种开发板,上电之后指示灯一闪一闪的就说明没有代码烧入进去还是新的板子,如果同时这些开发板还接了其它外设比如PS2、TB6612可以看看这些外设的指示灯亮没亮,没亮就有问题,要检查一下) 

 

(2)配置PS2

        配置完成后在connectivity下找到SPI1        

        这里我们选择Full-Duplex Master

        First Bit 选择 LSB,预分频Prescaler选择256,CPOL选择High,CPHA选择1 Edge,片选信号NSS选择软件方式(我也不知道这个为什么这么选,后续学学)

        CubeMx配置好了之后Keil里面不需要再配置什么了,只需要把PS2的库移植过来,就可以直接使用PS2了,上面配置串口是为了检查PS2的程序是不是正常执行

        打开任意SPI即可,配置严格遵循上图,可能你会发现你的Baud Rate不是250KBit/s,这就需要你调整你的时钟频率,试凑法给调到250KBit/s!这很重要!!!!

        你需要一个输出引脚来连接接收器的CS,因为在配置中使用了标签,所以在写代码的时候就要用标签的代码(具体知识点我也忘了,可以后面补补)

HAL_GPIO_WritePin(CSS_GPIO_Port, CSS_Pin, GPIO_PIN_RESET);//拉低片选CSS,代表选中设备可以开始通信

(3)时钟树配置

三、ps2库.c和.h以及delay(微秒级).c和.h和main.c的代码

       PS2 手柄由手柄与接收器两部分组成,手柄主要负责发送按键信息;接收器与单片机(也可叫作主机)相连,接收器用于接收手柄发来的信息,并传递给单片机,单片机也可通过接收器,向手柄发送命令,配置手柄的发送模式。

(1)DAT\DI代表单片机接收的数据,将数据从PS2手柄接收到单片机

(2)CMD\DO代表单片机发送的数据,将数据从单片机发送给PS2手柄

(3)数据在时钟CLK下降沿的时候才可以正常传输。

(4)数据的接收需要在CS端口为低电平时传输,且在通信时,只有一串数据传输完,CS才由低拉到高,而不是传输完一个字节后就由低拉到高

        当主机想读手柄数据时,将会拉低 CS 线电平,并发出一个命令“0x01”;手柄会回复它的 ID“0x41=模拟绿灯,0x73=模拟红灯”;在手柄发送 ID 的同时,主机将传送 0x42,请求数据;随后手柄发送出 0x5A,告诉主机“数据来了” 

       

        单片机通过PC6和PC7模拟上面那个时序,向PS2发送0x01,再发送一个0x42,PS2会传输回单片机一个ID(0x73代表亮灯模式,0x41代表无灯模式),接着你再随便发一个数,PS2会回一个0x5A,说我要开始传输你需要的按键信息了

HAL_GPIO_WritePin(CSS_GPIO_Port, CSS_Pin, GPIO_PIN_RESET);

                        //拉低片选CSS,代表选中设备可以开始通信

HAL_SPI_TransmitReceive(&hspi1, &CMD[0], &PS2OriginalValue[0], 1, HAL_MAX_DELAY);

                                                           0x01         0x00
    Delay_us(10);
    HAL_SPI_TransmitReceive(&hspi1, &CMD[1], &PS2OriginalValue[1], 1, HAL_MAX_DELAY);

                                                            0x42            0x41/0x73
    Delay_us(10);
    HAL_SPI_TransmitReceive(&hspi1, &CMD[2], &PS2OriginalValue[2], 1, HAL_MAX_DELAY);

                                                                0x01          0x5A
    Delay_us(10);

        接着你按照之前的方式发6个数,同时也会接着收到PS2发回来的6个数,这六个数就是你需要的按键信息,下面这个表讲的就是这个事:

for (i = 3; i < 9; i++)//接下来持续发送0x00,可以接收到按键信息以及摇杆信息
{
      HAL_SPI_TransmitReceive(&hspi1, &CMD[2], &PS2OriginalValue[i], 1, HAL_MAX_DELAY);

                                                                   0x00        

PS2OriginalValue[3]第一组按键:选择键、开始键、左侧按键

PS2OriginalValue[4]第二组按键:前面按键、右侧按键

PS2OriginalValue[5]右摇杆左右

PS2OriginalValue[6]右摇杆上下

PS2OriginalValue[7]左摇杆左右

PS2OriginalValue[8]左摇杆上下
        Delay_us(10);


}

 

(1)没有用过但是想要尝试版

https://blog.csdn.net/m0_62524451/article/details/136681842?fromshare=blogdetail&sharetype=blogdetail&sharerId=136681842&sharerefer=PC&sharesource=2301_81764359&sharefrom=from_link

 工程链接可以参考这篇博文

#ifndef PS2_PS2_H_
#define PS2_PS2_H_
 
#include "main.h"
#include "delay.h"
/*
硬件spi
*/
 
typedef struct
{
    uint8_t A_D;                                           //模拟(红灯)为1 数字(无灯)为0
    int8_t Rocker_RX, Rocker_RY, Rocker_LX, Rocker_LY;     //摇杆值(模拟状态为实际值0-0xFF)(数字态为等效的值0,0x80,0xFF)
                                                           //按键值0为未触发,1为触发态
    uint8_t Key_L1, Key_L2, Key_R1, Key_R2;                //后侧大按键
    uint8_t Key_L_Right, Key_L_Left, Key_L_Up, Key_L_Down; //左侧按键
    uint8_t Key_R_Right, Key_R_Left, Key_R_Up, Key_R_Down; //右侧按键
    uint8_t Key_Select;                                    //选择键
    uint8_t Key_Start;                                     //开始键
    uint8_t Key_Rocker_Left, Key_Rocker_Right;             //摇杆按键
 
} PS2_TypeDef;
 
extern PS2_TypeDef PS2_Data;
 
void PS2_read_data(void);
 
#endif

         这里的CS使用的是PA4而且在CubeMx里面配置的是Output模式,所以这里写的就是HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);

  • 发送接收函数

    • HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size, uint32_t Timeout);

        采用HAL_SPI_TransmitReceive(&hspi1, &CMD[1], &PS2OriginalValue[1], 1, HAL_MAX_DELAY);函数,可以同时进行收发,其中第一个参数是spi句柄,第二个参数是要发送信息的地址,第三个参数是接收到的信息存储的地址,第四个参数是发送和接收的数据大小(注意:只有一个数据大小的参数说明在调用该函数时只能同时接受和发送相同大小的数据),最后一个参数时超时时长,这里设置为无限等待即可

       PS2_TypeDef PS2_Data = {0};  //存储手柄返回数据//PS2_TypeDef这个是使用的结构体

#include "ps2.h"
#include "spi.h"
 
PS2_TypeDef PS2_Data = {0};
uint8_t cmd[3] = {0x01,0x42,0x00};                                     // 请求接受数据
uint8_t PS2data[9] = {0};   //存储手柄返回数据
 
void PS2_Get(void)//硬件SPI通信函数,这里要求通讯速率必须在250KHZ,函数中的延时可微调不可去
{
	uint8_t i = 0;
	
	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);                 //拉低,开始通讯
		
	HAL_SPI_TransmitReceive(&hspi1,&cmd[0],&PS2data[0],1,100);          // 发送0x01,请求接受数据
	delay_us(5);
	HAL_SPI_TransmitReceive(&hspi1,&cmd[1],&PS2data[1],1,100);          // 发送0x42,接受0x01(PS2表示开始通信)
	delay_us(5);
	HAL_SPI_TransmitReceive(&hspi1,&cmd[2],&PS2data[2],1,100);          // 发送0x00,接受ID(红绿灯模式)
	delay_us(5);
	for(i = 3;i <9;i++)
	{
		HAL_SPI_TransmitReceive(&hspi1,&cmd[2],&PS2data[i],1,0xffff);     // 接受数据
		delay_us(1);
		
	}
	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);                    //拉低,准备下次通讯
}
 
 
void PS2_Decode11(void)//接收数据处理函数
{
    if (PS2data[2] == 0x5A)
    {
        PS2_Data.Key_Select = (~PS2data[3] >> 0) & 0x01;                //选择键
        PS2_Data.Key_Start = (~PS2data[3] >> 3) & 0x01;                 //开始键
        //左侧按键
        PS2_Data.Key_L_Up = (~PS2data[3] >> 4) & 0x01;
        PS2_Data.Key_L_Right = (~PS2data[3] >> 5) & 0x01;
        PS2_Data.Key_L_Down = (~PS2data[3] >> 6) & 0x01;
        PS2_Data.Key_L_Left = (~PS2data[3] >> 7) & 0x01;
        //后侧按键
        PS2_Data.Key_L2 = (~PS2data[4] >> 0) & 0x01;
        PS2_Data.Key_R2 = (~PS2data[4] >> 1) & 0x01;
        PS2_Data.Key_L1 = (~PS2data[4] >> 2) & 0x01;
        PS2_Data.Key_R1 = (~PS2data[4] >> 3) & 0x01;
        //右侧按键
        PS2_Data.Key_R_Up = (~PS2data[4] >> 4) & 0x01;
        PS2_Data.Key_R_Right = (~PS2data[4] >> 5) & 0x01;
        PS2_Data.Key_R_Down = (~PS2data[4] >> 6) & 0x01;
        PS2_Data.Key_R_Left = (~PS2data[4] >> 7) & 0x01;
        if (PS2data[1] == 0x41)//无灯模式(摇杆值八向)
        { 
            PS2_Data.Rocker_LX = 127 * (PS2_Data.Key_L_Right - PS2_Data.Key_L_Left);
            PS2_Data.Rocker_LY = 127 * (PS2_Data.Key_L_Up - PS2_Data.Key_L_Down);
 
            PS2_Data.Rocker_RX = 127 * (PS2_Data.Key_R_Right - PS2_Data.Key_R_Left);
            PS2_Data.Rocker_RY = 127 * (PS2_Data.Key_R_Up - PS2_Data.Key_R_Down);
        }
        else if (PS2data[1] == 0x73)//红灯模式(摇杆值模拟)
        { 
				//摇杆按键
				PS2_Data.Key_Rocker_Left = (~PS2data[3] >> 1) & 0x01;
				PS2_Data.Key_Rocker_Right = (~PS2data[3] >> 2) & 0x01;
				//摇杆值
				PS2_Data.Rocker_LX = PS2data[7] - 0x80;
				PS2_Data.Rocker_LY = -1 - (PS2data[8] - 0x80);
				PS2_Data.Rocker_RX = PS2data[5] - 0x80;
				PS2_Data.Rocker_RY = -1 - (PS2data[6] - 0x80);
        }
    }
}
 
void PS2_read_data(void)//在需要的地方调用该函数,不可调用速率太快,up这里在主循环中调用给予10ms延时效果较好
{
	PS2_Get();//这个是PS2通信的底层
	PS2_Decode11();//这个是PS2传输过来的数据
}
	
#ifndef DELAY_DELAY_H_
#define DELAY_DELAY_H_
 
#include "stm32f1xx_hal.h" //HAL库文件声明
void delay_us(uint32_t us); //C文件中的函数声明
 
#endif /* DELAY_DELAY_H_ */
 
#include "delay.h"
 
void delay_us(uint32_t us) //利用CPU循环实现的非精准应用的微秒延时函数
{
    uint32_t delay = (HAL_RCC_GetHCLKFreq() / 8000000 * us); //使用HAL_RCC_GetHCLKFreq()函数获取主频值,经算法得到1微秒的循环次数
    while (delay--); //循环delay次,达到1微秒延时
}
 

         这个main.c文件里面就是包含了一下ps2.h和delay.h的头文件,然后在int外面写了一下PS2_read_data();这个函数里面需要的两个函数void PS2_Get(void);void PS2_Decode11(void);,最后在主函数里面调用了PS2_read_data();这个函数,并且延时了1ms(我写的主函数里面也有一个延时函数,因为手柄上电之后会发送一串乱码,好像是上电就会发送,然后才是正常的数据,然后再配对,再切换模式,这个可以后续再开个串口看一下传输过来的数据哪些时候是乱码,哪些时候是配对的数据,哪些时候是切换模式的数据)

        注意!!!!!!!!!!!!!!
读取函数不要太快运行,在我这里我给了1ms的延时,太快了会导致读取数据乱码(你没按按键,但是在程序里你会发现你按下了按键),还有就是按键状态都在结构体PS2_Data中,直接引用此结构体你所需的变量即可。

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 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 "spi.h"
#include "gpio.h"
 
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "delay.h"
#include "ps2.h"
/* USER CODE END Includes */
 
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
void PS2_Get(void);
void PS2_Decode11(void);
/* 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 */
 
/* 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 */
  uint8_t ps = 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_SPI1_Init();
  /* USER CODE BEGIN 2 */
 
  /* USER CODE END 2 */
 
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	 PS2_read_data();
	 HAL_Delay(1);
    /* 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_MUL8;
  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 */
 
/* 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 */

         参考一下https://blog.csdn.net/weixin_43002939/article/details/130319973?fromshare=blogdetail&sharetype=blogdetail&sharerId=130319973&sharerefer=PC&sharesource=2301_81764359&sharefrom=from_link

这篇文章,跟上面的PS2.c和,h的底层很像,看看在主函数里面这样调用结构体可不可以(应该是可以的)

/* USER CODE BEGIN Header */
/**
 ******************************************************************************
 * @file           : main.c
 * @brief          : Main program body
 ******************************************************************************
 * @attention
 *
 * Copyright (c) 2023 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 "i2c.h"
#include "rtc.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "../../icode/motor/motor.h"
#include "../inc/retarget.h"//用于printf函数的串口重映射
#include "../../icode/oled/oled.h"
#include "../../icode/ps2/ps2.h"
/* 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 */

/* 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_RTC_Init();
  MX_TIM1_Init();
  MX_USART1_UART_Init();
  MX_I2C1_Init();
  /* USER CODE BEGIN 2 */
	HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_4);
	HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
	RetargetInit(&huart1);//将printf函数映射到uart1串口上
	OLED_Init();                           //OLED初始
	OLED_Clear();                         //清屏

  /* USER CODE END 2 */

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

    /* USER CODE BEGIN 3 */

		PS2_Read_Data();

		if(PS2_Data.Key_L_Up){
			MotorControl(0,600,600);
		}
		else if(PS2_Data.Key_L_Down){
			MotorControl(1,600,600);
		}

		else if(PS2_Data.Key_L_Left){
			MotorControl(0,300,0);
		}
		else if(PS2_Data.Key_L_Right){
			MotorControl(0,0,300);
		}
		else {
			MotorControl(2, 0, 0);
		}


		HAL_Delay(2);





		printf("LX:%d LY:%d RX:%d RY:%d\r\n", PS2_Data.Rocker_LX, PS2_Data.Rocker_LY, PS2_Data.Rocker_RX, PS2_Data.Rocker_RY);
		HAL_Delay(10);

//		printf("hellow 510\r");
//		OLED_ShowNum(0, 1, 3, 1, 16, 1);
//		OLED_ShowString(0, 5, "hellow world", 16, 1);



	}
  /* USER CODE END 3 */
}

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

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI|RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.LSIState = RCC_LSI_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();
  }
  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_RTC;
  PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSI;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */

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

(2)成功过的PS2底层移植代码

        参考文章https://blog.csdn.net/Rick0725/article/details/136604217?fromshare=blogdetail&sharetype=blogdetail&sharerId=136604217&sharerefer=PC&sharesource=2301_81764359&sharefrom=from_link

        但是这个文章的底层我是真的看不懂,也看不懂PS2收发数据底层的那个顺序 

(1)CubeMx的配置

                 由于选择的是软件片选,所以我们需要配置一个GPIO用作片选信号,我们直接点击PA4引脚,将其设置为GPIO_Output

         接着在system core中点击GPIO,为其设置CSS的用户标签

(2)keil的代码

PS2.c

#include "PS2.h"
#include "main.h"

uint8_t CMD[3] = { 0x01,0x42,0x00 };  // 请求接受数据
uint8_t PS2OriginalValue[9] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 };   //存储手柄返回数据
uint8_t RockerValue[4] = { 0x00,0x00,0x00,0x00 };  //摇杆模拟值
uint8_t ButtonValue[16] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 };  //所有按键状态值
uint8_t i,mode;

void PS2OriginalValueGet(void)
{
	short i = 0;
 
	HAL_GPIO_WritePin(CSS_GPIO_Port, CSS_Pin, GPIO_PIN_RESET);//拉低片选CSS,代表选中设备可以开始通信
	
 //HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size, uint32_t Timeout);
	
	HAL_SPI_TransmitReceive(&hspi1, &CMD[0], &PS2OriginalValue[0], 1, HAL_MAX_DELAY);//根据数据手册,首先需要向接收器发送0x01请求接收数据
	Delay_us(10);
	HAL_SPI_TransmitReceive(&hspi1, &CMD[1], &PS2OriginalValue[1], 1, HAL_MAX_DELAY);//接下来需要向接收器发送0x42,请求开始通信,接收到0x01表示通信正式开始
	Delay_us(10);
	HAL_SPI_TransmitReceive(&hspi1, &CMD[2], &PS2OriginalValue[2], 1, HAL_MAX_DELAY);//然后发送0x00,可以接收到PS2ID,此ID可以代表PS2手柄目前的模式,如果为0x73,则为红灯模式,如果为0x41,则为绿灯模式,此状态下摇杆返回模拟值
	Delay_us(10);
	for (i = 3; i < 9; i++)//接下来持续发送0x00,可以接收到按键信息以及摇杆信息
	{
		HAL_SPI_TransmitReceive(&hspi1, &CMD[2], &PS2OriginalValue[i], 1, HAL_MAX_DELAY);
		Delay_us(10);
	}
 
	HAL_GPIO_WritePin(CSS_GPIO_Port, CSS_Pin, GPIO_PIN_SET);//完成后将片选拉高
 
}

void ButtonValueGet(void)
{
	uint8_t bit = 1;
	uint8_t button = 0;
	for (bit = 8; bit > 0; bit--)
	{
		bit -= 1;
		ButtonValue[button] = (PS2OriginalValue[3] & (1 << bit)) >> bit;
		bit += 1;
		button++;
	}
	for (bit = 8; bit > 0; bit--)
	{
		bit -= 1;
		ButtonValue[button] = (PS2OriginalValue[4] & (1 << bit)) >> bit;
		bit += 1;
		button++;
	}
	for (button = 0; button < 16; button++)
	{
		if (ButtonValue[button] == 1)  ButtonValue[button] = 0;
		else  ButtonValue[button] = 1;
	}
}

void RockerValueGet(void)
{
	int i;
	 
	for (i = 5; i < 9; i++)
	{
		PS2OriginalValue[i] = (int)PS2OriginalValue[i];
		RockerValue[i - 5] = PS2OriginalValue[i];
    
	}
}

int PS2RedLight(void)
{
	if (mode == 0X73)
		return 1;
	else
		return 0;
}

void PS2OriginalValueClear(void)
{
	for (i = 0; i < 9; i++)
	{
		 PS2OriginalValue[i] = 0x00; 
	}
}

void PS2AllValueUpdate(void)
{
	PS2OriginalValueGet(); 
	RockerValueGet();
	ButtonValueGet();
	mode = PS2OriginalValue[1];
	PS2OriginalValueClear();
}

 PS2.h的代码

#ifndef __PS2_H
#define __PS2_H

#include "delay.h"
#include "spi.h"

#define PSS_Rx 0 
#define PSS_Ry 1
#define PSS_Lx 2
#define PSS_Ly 3
 
#define PSB_Left        0
#define PSB_Down        1
#define PSB_Right       2
#define PSB_Up          Left_Front
#define PSB_Start       4
#define PSB_RightRocker	5
#define PSB_LeftRocker  6
#define PSB_Select      7
#define PSB_Square      8
#define PSB_Cross       9
#define PSB_Circle      10
#define PSB_Triangle    11
#define PSB_X           8
#define PSB_A			9
#define PSB_B			10
#define PSB_Y			11
#define PSB_R1          12
#define PSB_L1          13
#define PSB_R2          14
#define PSB_L2          15

void PS2OriginalValueGet(void);
void ButtonValueGet(void);
void RockerValueGet(void);
int PS2RedLight(void);
void PS2OriginalValueClear(void);
void PS2AllValueUpdate(void);

#endif

main.c函数里的代码

 

 (3)调试结果

        编译下载并打开串口调试助手,按动手柄上的按键,摇动摇杆,出现如下结果说明解码成功,其中1表示被按下的按键,后面四个则是摇杆模拟值 

(3)大概总结

        以上两个代码的PS2初始化是一样的,只是在读取按键的值和摇杆的值的代码书写会不一样一些,虽然我还是看不懂到底是怎么来计算的传输的代码,但是不管是ButtonValue[button]还是 PS2_Data.Key_Select这种,都相当于是名字(PS2按键摇杆传过来的数据取的一个名字)

        现在的PS2的读取都是在主函数里面,我不知道开中断能不能读取,然后就是ButtonValue[0]像这种要在main函数上面声明一下这个变量。

        这个函数的逻辑就是,我先在主函数里面调用一下读取按键的函数,然后按键传过来的数据就放在ButtonValue[button]这个里面的,所以我可以使用这个名字去做一些相应的功能。所以我猜测,我在主函数里面使用PS2_Data.Key_Select这种结构体也能实现相应的功能,可以尝试一下

四、工程代码

五、参考文章

https://blog.csdn.net/Rick0725/article/details/136604217?fromshare=blogdetail&sharetype=blogdetail&sharerId=136604217&sharerefer=PC&sharesource=hxnwhsh&sharefrom=from_link

https://blog.csdn.net/qq_61568285/article/details/134831115?fromshare=blogdetail&sharetype=blogdetail&sharerId=134831115&sharerefer=PC&sharesource=2301_81764359&sharefrom=from_link

http://【STM32 CUBEMX HAL PS2数据读取并解析 硬件SPI - CSDN App】https://blog.csdn.net/m0_62524451/article/details/136681842?sharetype=blogdetail&shareId=136681842&sharerefer=APP&sharesource=2401_88973651&sharefrom=link

http://【STM32 HAL库 PS2手柄控制电机转动 - CSDN App】https://blog.csdn.net/weixin_43002939/article/details/130319973?sharetype=blogdetail&shareId=130319973&sharerefer=APP&sharesource=2401_88973651&sharefrom=link

http://【基于STM32HAL库的PS2遥控手柄 - CSDN App】https://blog.csdn.net/2201_75475731/article/details/132760068?sharetype=blogdetail&shareId=132760068&sharerefer=APP&sharesource=2401_88973651&sharefrom=link

下面这一篇文章一点儿也看不懂

https://blog.csdn.net/yuaner_cxy/article/details/96650089?fromshare=blogdetail&sharetype=blogdetail&sharerId=96650089&sharerefer=PC&sharesource=2301_81764359&sharefrom=from_link

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2255839.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

电脑投屏到电脑:Windows,macOS及Linux系统可以相互投屏!

本篇其实是电脑远程投屏到另一台电脑的操作介绍。本篇文章的方法可用于Windows&#xff0c;macOS及Linux系统的相互投屏。 为了避免介绍过程中出现“这台电脑”投屏到“那台电脑”的混乱表述&#xff0c;假定当前屏幕投出端是Windows系统电脑&#xff0c;屏幕接收端是Linux系统…

软件测试环境搭建与测试流程

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 1.软件测试环境搭建 思考&#xff1a; 在什么条件下做软件测试&#xff1f;怎么做软件测试&#xff1f; 1.1 搭建测试环境前 确定测试目的 功能测试&#xff…

实战 | C# 中使用YOLOv11实现实例分割 (步骤 + 源码)

导 读 本文主要介绍在C#中使用YOLOv11实现实例分割,并给详细步骤和源码。 C# YOLO11实例分割——本文实现效果:

C#窗体程序学生管理

代码如下&#xff1a; public static string constr "Data SourceFUSHUAI;Initial Catalogproduct;Integrated SecurityTrue"; public static SqlConnection con new SqlConnection(constr); private void Form1_Load(object sender, EventArgs e) { gettable…

特朗普画像

任务内容 Description 特朗普当选了&#xff0c;网上流传着很多段子&#xff0c;也出了特朗普的头像。有人说&#xff0c;特朗普 的头像像一团云。所以今年马云去了美国和特朗普谈中美企业的发展。那么你能帮 忙打印出特朗普的头像吗&#xff1f; 抽象派认为&#xff0c;特朗普…

【Linux 篇】Docker 启动和停止的精准掌舵:操控指南

文章目录 【Linux篇】Docker 启动和停止的精准掌舵&#xff1a;操控指南前言docker基本命令1. 帮助手册 2. 指令介绍 常用命令1. 查看镜像2. 搜索镜像3. 拉取镜像4. 删除镜像5. 从Docker Hub拉取 容器的相关命令1. 查看容器2. 创建与启动容器3. 查看镜像4. 启动容器5. 查看容器…

Android环境搭建

Android环境搭建 第一步&#xff1a;安装 Homebrew 执行以下命令来安装 Homebrew&#xff1a; /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)"检测是否安装成功&#xff1a; brew --version第二步&#xff1a;安装 No…

【调试工具】USB 转 UART 适配器(USB 转 TTL)

「USB 转 TTL 转换器」是错误的叫法&#xff0c;正确的叫法应该为 「USB 转 UART 适配器」。 Device connection 注意端口的交叉连接&#xff0c;Device1_TX<---->Device2_RX USB-to-UART adapter GND 记得接地。 使用&#xff1a; 当 TX,RX 需要电平为 0-3.3V 时&am…

Stable Diffusion核心网络结构——U-Net

本文详细详细介绍Stable Diffusion核心网络结构——U-Net&#xff0c;作用&#xff0c;架构&#xff0c;加噪去噪过程损失函数等。 目录 Stable Diffusion核心网络结构 SD模型整体架构初识 U-Net模型 【1】U-Net的核心作用 【2】U-Net模型的完整结构图 &#xff08;1&#xff0…

利用【AOP+自定义注解】实现项目中【日志记录】

利用AOP自定义注解实现日志记录 需求: 日志记录 操作日志记录,类似如下 思路:AOP自定义注解 AOP面向切面编程,利用 一种称为"横切"的技术&#xff0c;剖开封装的对象内部&#xff0c;并将那些影响了 多个类的公共行为抽取出封装到一个可重用模块&#xff0c;并将其…

JAVA-二叉树的概念和性质

目录 一.树形结构 1.1 概念 1.2 树的概念(重要)​编辑 补充&#xff1a;高度和深度的区别 1.3 树的应用 二. 二叉树&#xff08;重点&#xff09; 2.1 概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 选择题 一.树形结构 1.1 概念 树是一种 非线性 的数据结构&…

SSM虾米音乐项目2--分页查询

1.分页查询的底层逻辑 首先根据用户输入的流派&#xff0c;进行模糊查询根据查询的数据进行分页需要前端用户提供pageNo(当前页数)和pageSize(每页的数据量)并且要从后端计算count(总数据量)和totalPage(总页数)&#xff0c;以及startNum(每页开始的记录)从而将对应的页面数据…

debian编译失败

A、缘由和分析 debian的代码在删除该路径下的2个包后&#xff0c; 重新全编&#xff0c;编译不过的问题。 至于我为什么删除这2个包&#xff0c;这是因为在sdk第一次编译时一些文件已经打包进去了&#xff0c;我现在的修改无法更新进img中&#xff0c;而现在我的项目中不需要…

Thonny IDE + MicroPython + ESP32 + A9G 发短信打电话

A9G模块的使用说明 详见该博客&#xff1a;a9gdfgdfhguyiuh-CSDN博客 接线 ESP32 DEVKIT_C A9G GND GND D23 RX A9G开发板用板载MiniUSB&#xff08;安卓口&#xff09;供电 代码 from machine import UART # 导入串口模块 # import timeUART0 UART1 UART2 TX …

在GITHUB上传本地文件指南(详细图文版)

这份笔记简述了如何在GITHUB上上传文件夹的详细策略。 既是对自己未来的一个参考&#xff0c;又希望能给各位读者带来帮助。 详细步骤 打开目标文件夹&#xff08;想要上传的文件夹&#xff09; 右击点击git bash打开 GitHub创立新的仓库后&#xff0c;点击右上方CODE绿色按…

沈阳工业大学《2024年827自动控制原理真题》 (完整版)

本文内容&#xff0c;全部选自自动化考研联盟的&#xff1a;《沈阳工业大学827自控考研资料》的真题篇。后续会持续更新更多学校&#xff0c;更多年份的真题&#xff0c;记得关注哦~ 目录 2024年真题 Part1&#xff1a;2024年完整版真题 2024年真题

初始化webpack应用示例

1、初始化npm mkdir webpack_test cd webpack_test npm init 2、安装webpack依赖 npm install webpack webpack-cli -D 3、添加文件夹&#xff0c;入口文件 mkdir src touch index.js touch add-content.js 文件夹结构 index.js import addContent from "./add-cont…

重邮+数字信号处理实验三:z变换及离散LTI系统的z域分析

实验目的&#xff1a; &#xff08; 1 &#xff09;学会运用 Matlab 求离散时间信号的有理函数 z 变换的部分分式展开&#xff1b; &#xff08; 2 &#xff09;学会运用 Matlab 分析离散时间系统的系统函数的零极点&#xff1b; &#xff08; 3 &#xff09;学会运用 …

前端开发底层逻辑全解析

前端开发就像是构建一座数字大厦的外表装饰与交互系统&#xff0c;而理解其底层逻辑则是打好坚实基础的关键。今天&#xff0c;我们就来深入剖析前端开发的底层逻辑。 一、浏览器的工作机制&#xff1a;幕后的魔法手 当我们在浏览器中打开一个网页时&#xff0c;一系列复杂的操…

MySQL -- CURD(下)

1. Update 修改 1.1 语法 对符合条件的结果进⾏列值更新 UPDATE [LOW_PRIORITY] [IGNORE] table_referenceSET assignment [, assignment] ...[WHERE where_condition][ORDER BY ...][LIMIT row_count]1.2 示例 将吕布的数学成绩改为99分 update exam set math 99 where …