【STM32】小车

一、安装及基本技能

1.环境安装

MDK的下载与安装:

下载链接:Arm Keil | Keil STM32G0xx_DFP

【STM32】两个版本MDK搭建和三种调试器的使用-CSDN博客

安装完MDK后要导入MDK Keil中

导入下载的MDK【记得保存路径中不要有中文】 

安装DFP包提示SVDConv exied with an error.No uVision SystemViewer file_svdconv exited with an error-CSDN博客

FW的下载与安装

下载地址:STM32CubeG0 - STM32Cube MCU Package for STM32G0 series (HAL, Low-Layer APIs and CMSIS, USB PD, File system, RTOS - and examples running on ST boards) - STMicroelectronics

【STM32】STM32的Cube和HAL生态-CSDN博客

ST_Link驱动安装

2.烧录空代码--STLink的使用

1.先通过CubeMX创建一个空文件

2.配置STLink

【STM32】两个版本MDK搭建和三种调试器的使用-CSDN博客

 此时并没有接收到,表示未上电

上电后发现还是没有识别,则再一次关闭,重新打开即可

注意点:要对应开发板上的设置

3.基本技能--点亮LED

1.原理图分析

LED1:一旦通电就会亮

LED2:一端3.3V,另外一端就要接通低电平才会亮【设置为输出引脚,因为我是直接将其赋值】

2.相关步骤

1)将LED0变为低电平才会点亮(模拟接地)

2)找到芯片对应的引脚(PB7),给他赋高电平或者低电平来改变蓝灯的亮灭

3.使用CubeMX生成相关代码

1.设置GPIO

由上图分析可知将PB7设置为输出引脚

2.设置RCC

4.基本技能--按键控制LED

1.实验目标

点亮STM32板子上的LED灯,并使用按键控制灯的亮灭

2.实验用到知识点

按键原理图

按键消抖

HAL库中的HAL_GPIO_TogglePin和HAL_ReadPin函数

3.分析原理图

分析可知当按键未按下时,3.3V沿着PC13输出为高电平,所以接通时PC13会被拉低

因为默认在3.3V后面加上上拉电阻,所以我们不用额外添加上拉电阻,所以使用输入浮空就可以

4.使用CubeMX生成相关代码

1.设置GPIO

LED还是按照上面的设置:output

KEY在新设置:input【因为是从3.3V流过来的,所以其实我们是检测PC13处的电平】

 

 2.设置RCC

5.代码编写

1.led闪烁
  while (1)
  {
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET);
		HAL_Delay(1000);
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET);
		HAL_Delay(1000);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }

2.检测按键:HAL_GPIO_WritePin

按下按键为亮,松开为灭

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  /* USER CODE BEGIN 2 */
	//读取按键状态
	HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_13);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		//如果读取到按键为低电平,说明按下按键
		if(HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_13)==GPIO_PIN_RESET){
			//如果按下,将led点亮
				HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET);
		}
		else{//反之按下没有反应
			HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET);
		}
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
3.检测按键:HAL_GPIO_TogglePin

每一次按下按键都会转换电平状态

		if(HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_13)==GPIO_PIN_RESET){
			//如果按下,将led点亮
				HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_7);
		}
4.按键消抖

		//按键消抖
		if(HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_13)==GPIO_PIN_RESET){
			//如果按下,将led点亮
				HAL_Delay(100);//按键消抖
				if(HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_13)==GPIO_PIN_RESET){
					HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_7);//反转GPIO电平
				}
		}

6. 步骤

1)查看按键原理图

2)判断对应的GPIO口的电平状态

3)判断GPIO是输入端还是输出端

4)按键需要消抖

二、电机模块

0.直流电机原理

受力不平衡

1.小车前进

1.知识点

直流电机的驱动

STM32引脚电平状态

2.小车底板原理图

3.电机使能

发现我们对应的是H2插座的1和2

分析可以知道对应PB5和PB4

4.电机转动方向

IN-和IN+一定不能同时为1或者为0

一个为0则另外一个为1

5.代码编写


	//电机使能
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4 |GPIO_PIN_5, GPIO_PIN_SET);
		
  while (1)
  {
		//电机前进:则将左右的IN+设置为高电平,左右的IN-设置为低电平
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3 |GPIO_PIN_11, GPIO_PIN_SET);//IN+
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6 |GPIO_PIN_12, GPIO_PIN_RESET);//IN-
  }

6.步骤

1)电机驱动是依靠什么驱动(电流流过此次产生力)

2)找到小车底板上对应GPIO引脚

3)开启左轮和右轮的使能端

4)使两轮对应的IN+设为高电平,IN-设置为对应的低电平

2.小车后退

步骤与小车前进部分差不多

1.函数封装

void backword(){
		//电机后进:则将左右的IN+设置为低电平,左右的IN-设置为高电平
		HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN__Pin,GPIO_PIN_RESET);//left 的IN+设置为低电平
		HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_R_IN__Pin,GPIO_PIN_RESET);//right 的IN+设置为低电平
		HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN_Pin,GPIO_PIN_SET);//left 的IN-设置为高电平
		HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_R_IN_B6_Pin,GPIO_PIN_SET);//right 的IN-设置为高电平
}

void forword(){
		//电机前进:则将左右的IN+设置为高电平,左右的IN-设置为低电平
		HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN__Pin,GPIO_PIN_SET);//left 的IN+设置为高电平
		HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_R_IN__Pin,GPIO_PIN_SET);//right 的IN+设置为高电平
		HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN_Pin,GPIO_PIN_RESET);//left 的IN-设置为低电平
		HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_R_IN_B6_Pin,GPIO_PIN_RESET);//right 的IN-设置为低电平

}

2.按键设置

//按键检测
int key_detection(){
	if(HAL_GPIO_ReadPin(KEY_GPIO_Port,KEY_Pin)==GPIO_PIN_RESET){
		return 1;
	}
	return 0;
}

3.goto语句

  while (1)
  {
		A:while(1){
			forword();
			if(key_detection()==1){
				HAL_Delay(100);//消抖
				if(key_detection()==1){
					goto B;
				}
			}
		}
		B:while(1){
			forword();
			if(key_detection()==1){
				HAL_Delay(100);//消抖
				if(key_detection()==1){
					goto A;
				}
			}
		}
  }

4.完整代码

/* 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 "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 */
void backword(){
		//电机后进:则将左右的IN+设置为低电平,左右的IN-设置为高电平
		HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN__Pin,GPIO_PIN_RESET);//left 的IN+设置为低电平
		HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_R_IN__Pin,GPIO_PIN_RESET);//right 的IN+设置为低电平
		HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN_Pin,GPIO_PIN_SET);//left 的IN-设置为高电平
		HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_R_IN_B6_Pin,GPIO_PIN_SET);//right 的IN-设置为高电平
}

void forword(){
		//电机前进:则将左右的IN+设置为高电平,左右的IN-设置为低电平
		HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN__Pin,GPIO_PIN_SET);//left 的IN+设置为高电平
		HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_R_IN__Pin,GPIO_PIN_SET);//right 的IN+设置为高电平
		HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN_Pin,GPIO_PIN_RESET);//left 的IN-设置为低电平
		HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_R_IN_B6_Pin,GPIO_PIN_RESET);//right 的IN-设置为低电平

}

//按键检测
int key_detection(){
	if(HAL_GPIO_ReadPin(KEY_GPIO_Port,KEY_Pin)==GPIO_PIN_RESET){
		return 1;
	}
	return 0;
}

/* 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();
  /* USER CODE BEGIN 2 */
	//使能电机
  HAL_GPIO_WritePin(GPIOB, MOTOR_L_S_Pin|MOTOR_R_S_Pin,GPIO_PIN_SET);
	//定义按键
	HAL_GPIO_ReadPin(KEY_GPIO_Port,KEY_Pin);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		A:while(1){
			forword();
			if(key_detection()==1){
				HAL_Delay(100);//消抖
				if(key_detection()==1){
					goto B;
				}
			}
		}
		B:while(1){
			forword();
			if(key_detection()==1){
				HAL_Delay(100);//消抖
				if(key_detection()==1){
					goto A;
				}
			}
		}
    /* 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};

  /** Configure the main internal regulator output voltage
  */
  HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);

  /** 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.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV1;
  RCC_OscInitStruct.PLL.PLLN = 16;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
  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_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = 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 */

3.小车完整代码

1.原理图

2.代码编写

void backword(){
		//使能电机
		HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_SET);//使能右轮子
		HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_SET);//使能左轮子
		//电机后进:则将左右的IN+设置为低电平,左右的IN-设置为高电平
		HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN__Pin,GPIO_PIN_RESET);//left 的IN+设置为低电平
		HAL_GPIO_WritePin(MOTOR_R_IN__GPIO_Port,MOTOR_R_IN__Pin,GPIO_PIN_RESET);//right 的IN+设置为低电平
		HAL_GPIO_WritePin(MOTOR_L_IN_B12_GPIO_Port,MOTOR_L_IN_B12_Pin,GPIO_PIN_SET);//left 的IN-设置为高电平
		HAL_GPIO_WritePin(MOTOR_R_IN_B6_GPIO_Port,MOTOR_R_IN_B6_Pin,GPIO_PIN_SET);//right 的IN-设置为高电平
}

void forword(){
		//使能电机
		HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_SET);//使能右轮子
		HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_SET);//使能左轮子
		//电机后进:则将左右的IN+设置为低电平,左右的IN-设置为高电平
		HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN__Pin,GPIO_PIN_SET);//left 的IN+设置为高电平
		HAL_GPIO_WritePin(MOTOR_R_IN__GPIO_Port,MOTOR_R_IN__Pin,GPIO_PIN_SET);//right 的IN+设置为高电平
		HAL_GPIO_WritePin(MOTOR_L_IN_B12_GPIO_Port,MOTOR_L_IN_B12_Pin,GPIO_PIN_RESET);//left 的IN-设置为低电平
		HAL_GPIO_WritePin(MOTOR_R_IN_B6_GPIO_Port,MOTOR_R_IN_B6_Pin,GPIO_PIN_RESET);//right 的IN-设置为低电平

}
//左转前进:左边的轮子不动,右边的轮子动
//左边轮子不使能,右边的轮子使能,并且将IN+设置为高电平,IN-设置为低电平
void left_forward(){
		//使能电机
		HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_SET);//使能右轮子
		HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_RESET);//不使能左轮子
		HAL_GPIO_WritePin(MOTOR_R_IN__GPIO_Port,MOTOR_R_IN__Pin,GPIO_PIN_SET);//right 的IN+设置为高置为高电平
		HAL_GPIO_WritePin(MOTOR_R_IN_B6_GPIO_Port,MOTOR_R_IN_B6_Pin,GPIO_PIN_RESET);//right 的IN-设置为低电平
}

//左转后退:左边的轮子不动,右边的轮子动
//左边轮子不使能,右边的轮子使能,并且将IN+设置为低电平,IN-设置为高电平
void left_back(){
		//使能电机
		HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_SET);//使能右轮子
		HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_RESET);//使能左轮子
		HAL_GPIO_WritePin(MOTOR_R_IN__GPIO_Port,MOTOR_R_IN__Pin,GPIO_PIN_RESET);//right 的IN+设置为高置为低电平
		HAL_GPIO_WritePin(MOTOR_R_IN_B6_GPIO_Port,MOTOR_R_IN_B6_Pin,GPIO_PIN_SET);//right 的IN-设置为高电平
}

//右转前进:右边的轮子不动,左边的轮子动
//右边轮子不使能,左边的轮子使能,并且将IN+设置为高电平,IN-设置为低电平
void right_forward(){
		//使能电机
		HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_RESET);//不使能右轮子
		HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_SET);//使能左轮子
		HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN__Pin,GPIO_PIN_SET);//left 的IN+设置为高电平
		HAL_GPIO_WritePin(MOTOR_L_IN_B12_GPIO_Port,MOTOR_L_IN_B12_Pin,GPIO_PIN_RESET);//left 的IN-设置为低电平
}

//右转后退:右边的轮子不动,左边的轮子动
//右边轮子不使能,左边的轮子使能,并且将IN+设置为高电平,IN-设置为低电平
void right_back(){
		//使能电机
		HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_RESET);//不使能右轮子
		HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_SET);//使能左轮子
		HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN__Pin,GPIO_PIN_RESET);//left 的IN+设置为低电平
		HAL_GPIO_WritePin(MOTOR_L_IN_B12_GPIO_Port,MOTOR_L_IN_B12_Pin,GPIO_PIN_SET);//left 的IN-设置为高电平
}
//停止
void stop(){
		//使能电机
		HAL_GPIO_WritePin(GPIOB, MOTOR_L_S_Pin,GPIO_PIN_RESET);//不使能左轮子
		HAL_GPIO_WritePin(GPIOB, MOTOR_R_S_Pin,GPIO_PIN_RESET);//不使能右轮子
}

三、循迹模块:LM339

1.基本原理

1.原理图

LED3是input引脚

2.红外循迹原理

  1. 红外对管中,其中的白色为发射红外信号的,黑色为接收管,未接收到信号时不会导通(LED1不会亮),接收到红外信号会导通(LED1会亮)。
  2. 接收管的导通内阻大小也与接收到多少有效信号有关,接收的有效信号越多,导通的内阻越小,下图的LED也就会越亮。
  3. 如下图,当红外发射管发出去的信号没有被反弹回来时,红外接收管不会被导通,此时LED1就不会亮,当发射管的信号被反弹回来并被接收管收到后,红外接收管被导通,此时LED1亮起。

3.红外灵敏度

1)由红外接收器和红外发收器离地面的高度有关

2)与负极的电阻大小有关

4.LM339比较器

1)当我们循迹的时候,小车底下的黑色线不会被压倒,则红外发射器发射的红外线可以被红外接收器接收到,则对应的红外模块的小灯不会亮。(因为选择器中的正电压>负电压,所以此时小灯两端都为正电压)

2)在红外循迹模块中红外接收是光线越大,阻值越小

3)

4)判断是否压线--->判断引脚是否为0

2.左边越界后用户led亮起

1.原理图分析

左边越界,表示左边的红外板块上的灯会亮起

2.CubeMX设置

3.代码编写

当小车越界的时候,用户led会亮,红外板块上的灯亮【不正常工作】

当小车没有越界的时候,用户led不会亮【正常工作】

  while (1)
  {
		//如果红外模块上的led亮起,说明未接通,表示不能接收到红外线
		//如果红外模块上的led不亮,说明接收到红外线
		if(HAL_GPIO_ReadPin(tracking_left_GPIO_Port,tracking_left_Pin)==0){//此时引脚上为低电平,则此时红外板块的灯亮
			//表示输出为高电平,说明红外接收器接收到信号,则用户led亮,红外板块led亮起
			HAL_GPIO_WritePin(led_GPIO_Port, led_Pin, GPIO_PIN_RESET);
		}else{//表示接收到红外信号【正常工作】,则用户led不亮,红外板块led不会亮
			HAL_GPIO_WritePin(led_GPIO_Port, led_Pin, GPIO_PIN_SET);
		}
    }

3.右边越界后led闪烁

分析过程与上面类似于

1.原理图

2.代码编写

//led闪烁
void led_start(){
//			HAL_GPIO_WritePin(led_GPIO_Port,led_Pin,GPIO_PIN_SET);
//			HAL_Delay(1000);
//			HAL_GPIO_WritePin(led_GPIO_Port,led_Pin,GPIO_PIN_RESET);
			HAL_GPIO_TogglePin(led_GPIO_Port,led_Pin);
			HAL_Delay(1000);
}
int main(void)
{
  while (1)
  {
		//如果led亮说明没有接收到红外线,如果led不亮说明接收到红外线
		if(HAL_GPIO_ReadPin(tracking_right_GPIO_Port,tracking_right_Pin)==1){
			//此时检测为高电平,说明与红外连接的led不会亮,说明红外接收不到红外线
			//此时将led的指示灯设置为闪烁
			led_start();
			led_start();
		}else{
			//说明红外接收器正常的接收到了红外,此时指示灯led不会亮
			HAL_GPIO_WritePin(led_GPIO_Port,led_Pin,GPIO_PIN_SET);
		}
  }
}

4.车头越界后led改变状态

1.实验现象

1)如果左边越界,则用户指示灯闪烁减慢【HAL_Delay(1000)】,红外模块上的led熄灭

2)如果右边越界,则用户指示灯闪烁一般速度【HAL_Delay(500)】,红外模块上的led熄灭

3)如果正常行驶,用户指示灯熄灭【HAL_GPIO_WritePin(SET)-->输入高电平,熄灭】,红外模块上的led都熄灭

4)如果左右都越界(整个车头越界),则用户指示灯闪烁加快【HAL_Delay(50)】

2.代码编写

/**
1)如果左边越界,则用户指示灯闪烁减慢【HAL_Delay(1000)】,红外模块上的led熄灭

2)如果右边越界,则用户指示灯闪烁一般速度【HAL_Delay(500)】,红外模块上的led熄灭

3)如果正常行驶,用户指示灯熄灭【HAL_GPIO_WritePin(SET)-->输入高电平,熄灭】,红外模块上的led都熄灭

4)如果左右都越界(整个车头越界),则用户指示灯闪烁加快【HAL_Delay(50)】
*/

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

/* 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 */
//越界判断
int cross(){
	//【注意点】
	//判断整个车头是否越界应该放在第一个进行判断,因为如果先将单个判断放在第一个则后面无法进行判断
	if(HAL_GPIO_ReadPin(GPIOB,IR_TRACK_L_Pin) && HAL_GPIO_ReadPin(GPIOB,IR_TRACK_R_Pin)==0){//表示两边都越界【整个车头】
		return 1;
	}else if(HAL_GPIO_ReadPin(GPIOB,IR_TRACK_L_Pin)==0){//表示左边越界
		return 2;
	}else if(HAL_GPIO_ReadPin(GPIOB,IR_TRACK_R_Pin)==0){//表示右边越界
		return 3;
	}else{
		return 4;
	}
}
//led闪烁速度转换
void led_speed(unsigned int time){
	HAL_GPIO_TogglePin(user_led_GPIO_Port,user_led_Pin);
	HAL_Delay(time);
}
/* 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();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		switch(cross()){
			//整个车头越界
			case 1:
				led_speed(50);
				break;
			//左边越界
			case 2:
				led_speed(1000);
				break;
			//右边越界
			case 3:
				led_speed(500);
				break;
			//正常行驶时,用户指示灯熄灭
			case 4:
				HAL_GPIO_WritePin(user_led_GPIO_Port,user_led_Pin,GPIO_PIN_SET);
		}
    /* 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};

  /** Configure the main internal regulator output voltage
  */
  HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);

  /** 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.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV1;
  RCC_OscInitStruct.PLL.PLLN = 16;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
  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_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = 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 */

5.左边越界后小车右转

1.设置电机模块

2.代码编写

//前进
void forward(){
	//使能左右电机
	HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port,MOTOR_R_S_Pin,GPIO_PIN_SET);
	HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port,MOTOR_L_S_Pin,GPIO_PIN_SET);
	
	//IN+:设置为高电平
	HAL_GPIO_WritePin(MOTOR_R_IN__GPIO_Port,MOTOR_R_IN__Pin,GPIO_PIN_SET);
	HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN__Pin,GPIO_PIN_SET);
	
	//IN-:设置为低电平
	HAL_GPIO_WritePin(MOTOR_L_IN_B12_GPIO_Port,MOTOR_L_IN_B12_Pin,GPIO_PIN_RESET);
	HAL_GPIO_WritePin(MOTOR_R_IN_B6_GPIO_Port,MOTOR_R_IN_B6_Pin,GPIO_PIN_RESET);
	
	
}
//右转
void go_right(){
	//失能右电机(right)
	HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port,MOTOR_R_S_Pin,GPIO_PIN_RESET);
	//使能左电机(left)
	HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port,MOTOR_L_S_Pin,GPIO_PIN_SET);
	
	//IN+:设置为高电平
	HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN__Pin,GPIO_PIN_SET);
	
	//IN-:设置为低电平
	HAL_GPIO_WritePin(MOTOR_L_IN_B12_GPIO_Port,MOTOR_L_IN_B12_Pin,GPIO_PIN_RESET);
	
	
}

//左转
void go_left(){
	//使能右电机(right)
	HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port,MOTOR_R_S_Pin,GPIO_PIN_SET);
	//失能左电机(left)
	HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port,MOTOR_L_S_Pin,GPIO_PIN_RESET);
	
	//IN+:设置为高电平
	HAL_GPIO_WritePin(MOTOR_R_IN__GPIO_Port,MOTOR_R_IN__Pin,GPIO_PIN_SET);
	
	//IN-:设置为低电平
	HAL_GPIO_WritePin(MOTOR_R_IN_B6_GPIO_Port,MOTOR_R_IN_B6_Pin,GPIO_PIN_RESET);
	
	
}
/* 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();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		if(HAL_GPIO_ReadPin(IR_TRACK_L_GPIO_Port,IR_TRACK_L_Pin)==0){
			//表示左边此时越界,则应该往右走
			go_right();
		}else if(HAL_GPIO_ReadPin(IR_TRACK_R_GPIO_Port,IR_TRACK_R_Pin)==0){
			//表示右边此时越界,则应该往左走
			go_left();
		}
		else{//说明此时为不越界
			forward();
		}
    /* USER CODE END WHILE */
 
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

6.完整代码

当整个车头越界时,会后退,然后再重新规划路线

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void backword(){
		//使能电机
		HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_SET);//使能右轮子
		HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_SET);//使能左轮子
		//电机后进:则将左右的IN+设置为低电平,左右的IN-设置为高电平
		HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN__Pin,GPIO_PIN_RESET);//left 的IN+设置为低电平
		HAL_GPIO_WritePin(MOTOR_R_IN__GPIO_Port,MOTOR_R_IN__Pin,GPIO_PIN_RESET);//right 的IN+设置为低电平
		HAL_GPIO_WritePin(MOTOR_L_IN_B12_GPIO_Port,MOTOR_L_IN_B12_Pin,GPIO_PIN_SET);//left 的IN-设置为高电平
		HAL_GPIO_WritePin(MOTOR_R_IN_B6_GPIO_Port,MOTOR_R_IN_B6_Pin,GPIO_PIN_SET);//right 的IN-设置为高电平
}

void forword(){
		//使能电机
		HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_SET);//使能右轮子
		HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_SET);//使能左轮子
		//电机后进:则将左右的IN+设置为低电平,左右的IN-设置为高电平
		HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN__Pin,GPIO_PIN_SET);//left 的IN+设置为高电平
		HAL_GPIO_WritePin(MOTOR_R_IN__GPIO_Port,MOTOR_R_IN__Pin,GPIO_PIN_SET);//right 的IN+设置为高电平
		HAL_GPIO_WritePin(MOTOR_L_IN_B12_GPIO_Port,MOTOR_L_IN_B12_Pin,GPIO_PIN_RESET);//left 的IN-设置为低电平
		HAL_GPIO_WritePin(MOTOR_R_IN_B6_GPIO_Port,MOTOR_R_IN_B6_Pin,GPIO_PIN_RESET);//right 的IN-设置为低电平

}
//左转前进:左边的轮子不动,右边的轮子动
//左边轮子不使能,右边的轮子使能,并且将IN+设置为高电平,IN-设置为低电平
void left_forward(){
		//使能电机
		HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_SET);//使能右轮子
		HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_RESET);//不使能左轮子
		HAL_GPIO_WritePin(MOTOR_R_IN__GPIO_Port,MOTOR_R_IN__Pin,GPIO_PIN_SET);//right 的IN+设置为高置为高电平
		HAL_GPIO_WritePin(MOTOR_R_IN_B6_GPIO_Port,MOTOR_R_IN_B6_Pin,GPIO_PIN_RESET);//right 的IN-设置为低电平
}

//左转后退:左边的轮子不动,右边的轮子动
//左边轮子不使能,右边的轮子使能,并且将IN+设置为低电平,IN-设置为高电平
void left_back(){
		//使能电机
		HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_SET);//使能右轮子
		HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_RESET);//不使能左轮子
		HAL_GPIO_WritePin(MOTOR_R_IN__GPIO_Port,MOTOR_R_IN__Pin,GPIO_PIN_RESET);//right 的IN+设置为高置为低电平
		HAL_GPIO_WritePin(MOTOR_R_IN_B6_GPIO_Port,MOTOR_R_IN_B6_Pin,GPIO_PIN_SET);//right 的IN-设置为高电平
}

//右转前进:右边的轮子不动,左边的轮子动
//右边轮子不使能,左边的轮子使能,并且将IN+设置为高电平,IN-设置为低电平
void right_forward(){
		//使能电机
		HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_RESET);//不使能右轮子
		HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_SET);//使能左轮子
		HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN__Pin,GPIO_PIN_SET);//left 的IN+设置为高电平
		HAL_GPIO_WritePin(MOTOR_L_IN_B12_GPIO_Port,MOTOR_L_IN_B12_Pin,GPIO_PIN_RESET);//left 的IN-设置为低电平
}

//右转后退:右边的轮子不动,左边的轮子动
//右边轮子不使能,左边的轮子使能,并且将IN+设置为高电平,IN-设置为低电平
void right_back(){
		//使能电机
		HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_RESET);//不使能右轮子
		HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_SET);//使能左轮子
		HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN__Pin,GPIO_PIN_RESET);//left 的IN+设置为低电平
		HAL_GPIO_WritePin(MOTOR_L_IN_B12_GPIO_Port,MOTOR_L_IN_B12_Pin,GPIO_PIN_SET);//left 的IN-设置为高电平
}
//停止
void stop(){
		//使能电机
		HAL_GPIO_WritePin(GPIOB, MOTOR_L_S_Pin,GPIO_PIN_RESET);//不使能左轮子
		HAL_GPIO_WritePin(GPIOB, MOTOR_R_S_Pin,GPIO_PIN_RESET);//不使能右轮子
}
/* 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();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		//这里还有问题:待解决~~~~~~~~~~~~【但是前后退模块是正确的】
		if(HAL_GPIO_ReadPin(IR_TRACK_L_GPIO_Port,IR_TRACK_L_Pin) && HAL_GPIO_ReadPin(IR_TRACK_R_GPIO_Port,IR_TRACK_R_Pin)==0){
			//说明此时整个车头越界
			//先让其后退
			backword();
			//在让其延时一会(后退一会)
			HAL_Delay(500);
			//在让其左/右转,在循环多次之后,肯定会找到一个合适的路线继续行驶
			right_forward();
		}else if(HAL_GPIO_ReadPin(IR_TRACK_L_GPIO_Port,IR_TRACK_L_Pin)==0){
			//说明此时左边越界,应该往右边走
			right_forward();
		}else if(HAL_GPIO_ReadPin(IR_TRACK_R_GPIO_Port,IR_TRACK_R_Pin)==0){
			//说明此时右边越界,应该往左边走
			left_forward();
		}else{
			//都没有越界,则往前走
			forword();
		}
    /* USER CODE END WHILE */
 
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

四、避障模块:LM324

0.红外避障基本原理

1.红外避障实现方案

  1. 红外避障实现采用红外对管实现,红外对管是一个总称,包含红外发射管和红外接收管,有不同的封装形式和内部结构稍有不同,但原理都一样。
  2. 我们的小车红外发射管和红外接收管采用的封装形式为5mm圆头直插封装。

2.红外对管原理

  1. 红外对管中,其中的白色为发射红外信号的,黑色为接收管,未接收到信号时不会导通,接收到红外信号会导通。
  2. 接收管的导通内阻大小也与接收到多少有效信号有关,接收的有效信号越多,导通的内阻越小,下图的LED也就会越亮。(当出现障碍物时,接收到的光变多,阻值变大,正电压变小,此时正电压<负电压。则输出负电压,则led1亮)-->出现障碍物时,led1会亮【则判断是否遇到障碍物,就对引脚进行判断,如果引脚==0,表示遇到,如果不为0,则表示没有遇到】
  3. 如下图,当红外发射管发出去的信号没有被反弹回来时,红外接收管不会被导通,此时LED1就不会亮,当发射管的信号被反弹回来并被接收管收到后,红外接收管被导通,此时LED1亮起。

U4的位置你可以理解为存在一个光敏电阻,电阻随着光照强度增大而增大,所以这个LM339+的位置是不是电流就小了,而我们的LM339-是一个滑动变阻器这是一个可调节的,当成为一个默认值的时候是不是只需要看+的电压大小,那光照强度高,电阻大,是不是+电压就小了,那进行比较的时候比较器会输出一个逻辑0,所以LED1就亮了,而离障碍物远的时候,因为光照强度弱了电阻小了,5的电压大,+大于-,所以led不亮

1.左边有障碍物用户led亮起

当左边遇到障碍物时,板上的led亮(因为障碍物会反射,如果物体离得越近,表示接收到的光越多),我们设置用户led灯亮

注意点:避障引脚为input(PA11和PA12)--->因为我们是要读取该引脚的电平【如果是设置引脚电平则为output】

1.原理图

分析可得:如果要判断是否有障碍物,则判断引脚上是否为0(因为障碍物反射,则U4光照强度增加,阻值增大,电压减少,正电压<负电压,则输出0),如果为0则表示遇到障碍物,红外板子灯亮起

引脚为0---》有障碍物---》亮

引脚为1--》无障碍物---》不亮

2.CubeMX使用

注意点:避障引脚为input(PA11和PA12)--->因为我们是要读取该引脚的电平【如果是设置引脚电平则为output】

3.代码编写

  while (1)
  {
		if(HAL_GPIO_ReadPin(IR_OA_L_GPIO_Port,IR_OA_L_Pin)==0){//此时表示遇到障碍物,led1亮
			//此时我们点亮用户led
			HAL_GPIO_WritePin(user_led_GPIO_Port,user_led_Pin,GPIO_PIN_RESET);
		}else{//此时表示遇到障碍物,led1不亮
			//此时我们不点亮用户led	
			HAL_GPIO_WritePin(user_led_GPIO_Port,user_led_Pin,GPIO_PIN_SET);
		}
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }

2.右边有障碍物用户led亮起

原理同上面一致

1.代码编写

  while (1)
  {
		if(HAL_GPIO_ReadPin(IR_OA_R_GPIO_Port,IR_OA_R_Pin==0){//此时表示遇到障碍物,led1亮
			//此时我们点亮用户led
			HAL_GPIO_WritePin(user_led_GPIO_Port,user_led_Pin,GPIO_PIN_RESET);
		}else{//此时表示遇到障碍物,led1不亮
			//此时我们不点亮用户led	
			HAL_GPIO_WritePin(user_led_GPIO_Port,user_led_Pin,GPIO_PIN_SET);
		}
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }

3..前方有障碍物用户led亮起

前面两个同时越界--->led闪烁

左边有障碍物--->led会亮,设置用户led也亮

右边有障碍物----->led会亮,设置用户led也亮

都无障碍物-->两个led都灭

  while (1)
  {
		if(HAL_GPIO_ReadPin(IR_OA_L_GPIO_Port,IR_OA_L_Pin) && HAL_GPIO_ReadPin(IR_OA_R_GPIO_Port,IR_OA_R_Pin)==0){
			//表示前面两个都遇到障碍物
			led_speed();
		}
		else if(HAL_GPIO_ReadPin(IR_OA_L_GPIO_Port,IR_OA_L_Pin)==0){//此时表示遇到障碍物,led1亮
			HAL_GPIO_WritePin(user_led_GPIO_Port,user_led_Pin,GPIO_PIN_RESET);
		}else if(HAL_GPIO_ReadPin(IR_OA_R_GPIO_Port,IR_OA_R_Pin)==0){
			//遇到障碍物用户led亮起来
			HAL_GPIO_WritePin(user_led_GPIO_Port,user_led_Pin,GPIO_PIN_RESET);
		}else{//此时表示没有遇到障碍物,led1不亮
			//此时我们不点亮用户led	
			HAL_GPIO_WritePin(user_led_GPIO_Port,user_led_Pin,GPIO_PIN_SET);
		}
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }

4.前方有障碍物代码

void backword(){
		//使能电机
		HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_SET);//使能右轮子
		HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_SET);//使能左轮子
		//电机后进:则将左右的IN+设置为低电平,左右的IN-设置为高电平
		HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN__Pin,GPIO_PIN_RESET);//left 的IN+设置为低电平
		HAL_GPIO_WritePin(MOTOR_R_IN__GPIO_Port,MOTOR_R_IN__Pin,GPIO_PIN_RESET);//right 的IN+设置为低电平
		HAL_GPIO_WritePin(MOTOR_L_IN_B12_GPIO_Port,MOTOR_L_IN_B12_Pin,GPIO_PIN_SET);//left 的IN-设置为高电平
		HAL_GPIO_WritePin(MOTOR_R_IN_B6_GPIO_Port,MOTOR_R_IN_B6_Pin,GPIO_PIN_SET);//right 的IN-设置为高电平
}

void forword(){
		//使能电机
		HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_SET);//使能右轮子
		HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_SET);//使能左轮子
		//电机后进:则将左右的IN+设置为低电平,左右的IN-设置为高电平
		HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN__Pin,GPIO_PIN_SET);//left 的IN+设置为高电平
		HAL_GPIO_WritePin(MOTOR_R_IN__GPIO_Port,MOTOR_R_IN__Pin,GPIO_PIN_SET);//right 的IN+设置为高电平
		HAL_GPIO_WritePin(MOTOR_L_IN_B12_GPIO_Port,MOTOR_L_IN_B12_Pin,GPIO_PIN_RESET);//left 的IN-设置为低电平
		HAL_GPIO_WritePin(MOTOR_R_IN_B6_GPIO_Port,MOTOR_R_IN_B6_Pin,GPIO_PIN_RESET);//right 的IN-设置为低电平

}
//左转前进:左边的轮子不动,右边的轮子动
//左边轮子不使能,右边的轮子使能,并且将IN+设置为高电平,IN-设置为低电平
void left_forward(){
		//使能电机
		HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_SET);//使能右轮子
		HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_RESET);//不使能左轮子
		HAL_GPIO_WritePin(MOTOR_R_IN__GPIO_Port,MOTOR_R_IN__Pin,GPIO_PIN_SET);//right 的IN+设置为高置为高电平
		HAL_GPIO_WritePin(MOTOR_R_IN_B6_GPIO_Port,MOTOR_R_IN_B6_Pin,GPIO_PIN_RESET);//right 的IN-设置为低电平
}

//左转后退:左边的轮子不动,右边的轮子动
//左边轮子不使能,右边的轮子使能,并且将IN+设置为低电平,IN-设置为高电平
void left_back(){
		//使能电机
		HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_SET);//使能右轮子
		HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_RESET);//使能左轮子
		HAL_GPIO_WritePin(MOTOR_R_IN__GPIO_Port,MOTOR_R_IN__Pin,GPIO_PIN_RESET);//right 的IN+设置为高置为低电平
		HAL_GPIO_WritePin(MOTOR_R_IN_B6_GPIO_Port,MOTOR_R_IN_B6_Pin,GPIO_PIN_SET);//right 的IN-设置为高电平
}

//右转前进:右边的轮子不动,左边的轮子动
//右边轮子不使能,左边的轮子使能,并且将IN+设置为高电平,IN-设置为低电平
void right_forward(){
		//使能电机
		HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_RESET);//不使能右轮子
		HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_SET);//使能左轮子
		HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN__Pin,GPIO_PIN_SET);//left 的IN+设置为高电平
		HAL_GPIO_WritePin(MOTOR_L_IN_B12_GPIO_Port,MOTOR_L_IN_B12_Pin,GPIO_PIN_RESET);//left 的IN-设置为低电平
}

//右转后退:右边的轮子不动,左边的轮子动
//右边轮子不使能,左边的轮子使能,并且将IN+设置为高电平,IN-设置为低电平
void right_back(){
		//使能电机
		HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_RESET);//不使能右轮子
		HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_SET);//使能左轮子
		HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN__Pin,GPIO_PIN_RESET);//left 的IN+设置为低电平
		HAL_GPIO_WritePin(MOTOR_L_IN_B12_GPIO_Port,MOTOR_L_IN_B12_Pin,GPIO_PIN_SET);//left 的IN-设置为高电平
}
//停止
void stop(){
		//使能电机
		HAL_GPIO_WritePin(GPIOB, MOTOR_L_S_Pin,GPIO_PIN_RESET);//不使能左轮子
		HAL_GPIO_WritePin(GPIOB, MOTOR_R_S_Pin,GPIO_PIN_RESET);//不使能右轮子
}
/* 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();
  /* USER CODE BEGIN 2 */

		HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_SET);//使能右轮子
		HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_SET);//使能左轮子
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		if((HAL_GPIO_ReadPin(IR_OA_L_GPIO_Port,IR_OA_L_Pin)==0)&& (HAL_GPIO_ReadPin(IR_OA_R_GPIO_Port,IR_OA_R_Pin)==0)){
			backword();
			HAL_Delay(500);
			right_forward();
		}
		else if(HAL_GPIO_ReadPin(IR_OA_L_GPIO_Port,IR_OA_L_Pin)==0){
			//此时表示左边遇到障碍物,则led1亮起
			//我们应该想右转
			right_forward();
		}else if(HAL_GPIO_ReadPin(IR_OA_R_GPIO_Port,IR_OA_R_Pin)==0){
			//此时表示右边遇到障碍物,则led1亮起
			//我们应该想左转
			left_forward();
		}else{
			//没有遇到障碍物向前
			forword();
		}
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

五、红外遥控模块

1.红外收发的应用场景

2.红外遥控的编码协议(NEC PROTOCOL脉冲宽度调制)

第一步:查看脉冲时间

接收端:区别在于脉冲时间的长短

脉冲时间是一个范围

实际上数据有33位=1位同步码头+32位数据位

第二步:输入捕获功能(识别PWM)

脉冲的时间=value1和value2之间的差值

就要使用到上升沿和下降沿的读取【主要看tim.c文件中的函数】

第三步:硬件电路引脚(红外接收&&串口)

因为我们是要通过判断引脚上的值来进行分析,所以应该使用输入引脚,输入捕获(查看PWM)最为合适--->定时器模块

【STM32】串口和printf-CSDN博客

3.CubeMX生成

1.TIME-->输入捕获(PWM)

2.USART--串口输出

3.时钟设置

4.代码编写

1.编写定时器中断处理函数

//定时器的中断处理回调函数
void HAL_TIM_IC_CaptureCallback(){

}

2.判断是上升沿还是下降沿

1.HAL_TIM_ReadCapturedValue:  从Capture Compare单元读取捕获值

2.__HAL_TIM_SET_CAPTUREPOLARITY:触发捕获事件的信号边沿

在运行时设置 TIM Capture x 输入极性。【设置捕获模式(上升沿,下降沿)

3.代码
//定时器的中断处理回调函数
void HAL_TIM_IC_CaptureCallback(){
	//判断是否为上升沿捕获
	if(isUpcat){
		//将捕获的定时器的值赋值给valueUp
			valueUp=HAL_TIM_ReadCapturedValue(&htim1,TIM_CHANNEL_1);
		//将上升沿置为0
		isUpcat=0;//是否为上升沿的标志位
		//上升沿捕获数据
		__HAL_TIM_SET_CAPTUREPOLARITY(&htim1,TIM_CHANNEL_1,TIM_ICPOLARITY_FALLING);
	}else{//捕获下降沿
		//将捕获的定时器的值赋值给valueDown
		valueDown=HAL_TIM_ReadCapturedValue(&htim1,TIM_CHANNEL_1);
		//将上升沿置为1
		isUpcat=1;//是否为上升沿的标志位
		//下升沿捕获数据
		__HAL_TIM_SET_CAPTUREPOLARITY(&htim1,TIM_CHANNEL_1,TIM_ICPOLARITY_RISING);
	}
}

此时一个脉冲宽度已经设置好了

脉冲宽度=下降沿捕获-上升沿捕获

3.脉冲数据填充

1)判断是否在脉冲频率范围内【判断接收码头是否正确】

2)判断buffer中是否已经满

3)当填充完数据,记得将指针设置为初始状态

//上升沿
uint32_t isUpcat=1;//表示默认为上升沿
uint32_t valueUp=0;
uint32_t valueDown=0;
//脉冲宽度
uint32_t kuandu=0;
uint16_t buffer[128]={0};
uint16_t buffer_ID=0;
//接收标志位:判断此时buffer中的数据是否已经填满
uint16_t rvcFlag=0;


//定时器的中断处理回调函数
void HAL_TIM_IC_CaptureCallback(){
	//判断是否为上升沿捕获
	if(isUpcat){
		//将捕获的定时器的值赋值给valueUp
			valueUp=HAL_TIM_ReadCapturedValue(&htim1,TIM_CHANNEL_1);
		//将上升沿置为0
		isUpcat=0;//是否为上升沿的标志位
		//触发捕获事件的信号边沿
		__HAL_TIM_SET_CAPTUREPOLARITY(&htim1,TIM_CHANNEL_1,TIM_ICPOLARITY_FALLING);
	}else{//捕获下降沿
		//将捕获的定时器的值赋值给valueDown
		valueDown=HAL_TIM_ReadCapturedValue(&htim1,TIM_CHANNEL_1);
		//将上升沿置为1
		isUpcat=1;//是否为上升沿的标志位
		//触发捕获事件的信号边沿
		__HAL_TIM_SET_CAPTUREPOLARITY(&htim1,TIM_CHANNEL_1,TIM_ICPOLARITY_RISING);
		
		//计算差值
		kuandu=valueDown-valueUp;//取到了脉冲宽度
		
		//判断输入进来的数据是否是我们想要的
		if(kuandu>=4400 && kuandu <=4600){//接收码头的范围
			buffer_ID=0;//这个值用来判断我遍历到数组中的哪一个位置
			buffer[buffer_ID++]=kuandu;//将我们获得的数据填入
		}else if(buffer_ID>0){//表示此时我们还没有填充完整
			//接着往下填充
			buffer[buffer_ID++]=kuandu;
			if(buffer_ID>32){//表示接收完毕
				rvcFlag=1;//红外接收标志位
				//将指针设置回开头
				buffer_ID=0;
			}
		}
	}
}

4.串口测试代码

  /* USER CODE BEGIN 2 */
	//发生测试串口代码
	HAL_UART_Transmit(&huart1,(uint8_t)*ceshi,strlen(ceshi),100);

5.使能中断

开启定时器的IT中断

开启输入捕获(IC_IT)

	//使能输入捕获中断【定时器中的】
	HAL_TIM_IC_Start_IT(&htim1,TIM_CHANNEL_1);
	//使能定时器中断
	HAL_TIM_Base_Start_IT(&htim1);

6.获取数据并打印

  while (1)
  {
		if(rvcFlag){
			for(int i=0;i<33;i++){
				//将从红外中获取的数据打印给printfbuffer
				sprintf(printfbuffer," %u",buffer[i]);
				//显示测试代码
				//HAL_MAX_DELAY:最大超时时间
				HAL_UART_Transmit(&huart1,(uint8_t*)printfbuffer,strlen(printfbuffer),HAL_MAX_DELAY);
				//换行
				if(i%8==0){
					HAL_UART_Transmit(&huart1,"\n",2,HAL_MAX_DELAY);
				}
			}
			//将标志位清零
			rvcFlag=0;
		}
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }

点击按键0 

7.将获取到的数值转换为逻辑1或者逻辑0

修改为逻辑表示,方便我们编写代码

我们在设置时将范围给大一点 

			//将获取到的遥控数值转换为逻辑1或者逻辑0
			for(int i=0;i<8;i++){
				if(buffer[16+i]>=1400 && buffer[16+i]<1700){//这里16+i表示第17位开始才为控制码
					yaokong |=0x01;//将其设置为逻辑1
				}else if(buffer[16+i]>=400 && buffer[16+i]<=600){
					yaokong |=0x00;//将其设置为逻辑0
				}else {
					//错误处理
				}
				yaokong<<=1;//存放数值
			}

注意点:

yaokong要左移动1位 

8设置对应的按键控制值

利用串口助手进行设置

5.完整代码

/* 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 "tim.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "string.h"
#include "stdio.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 */
//上升沿
uint32_t isUpcat=1;//表示默认为上升沿
uint32_t valueUp=0;
uint32_t valueDown=0;
//脉冲宽度
uint32_t kuandu=0;
uint16_t buffer[128]={0};
uint16_t buffer_ID=0;
//接收标志位:判断此时buffer中的数据是否已经填满
uint16_t rvcFlag=0;
char ceshi[]="start";
char yaokong;
/* 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 */
void backword(){
		//使能电机
		HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_SET);//使能右轮子
		HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_SET);//使能左轮子
		//电机后进:则将左右的IN+设置为低电平,左右的IN-设置为高电平
		HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN__Pin,GPIO_PIN_RESET);//left 的IN+设置为低电平
		HAL_GPIO_WritePin(MOTOR_R_IN__GPIO_Port,MOTOR_R_IN__Pin,GPIO_PIN_RESET);//right 的IN+设置为低电平
		HAL_GPIO_WritePin(MOTOR_L_IN_B12_GPIO_Port,MOTOR_L_IN_B12_Pin,GPIO_PIN_SET);//left 的IN-设置为高电平
		HAL_GPIO_WritePin(MOTOR_R_IN_B6_GPIO_Port,MOTOR_R_IN_B6_Pin,GPIO_PIN_SET);//right 的IN-设置为高电平
}

void forword(){
		//使能电机
		HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_SET);//使能右轮子
		HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_SET);//使能左轮子
		//电机后进:则将左右的IN+设置为低电平,左右的IN-设置为高电平
		HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN__Pin,GPIO_PIN_SET);//left 的IN+设置为高电平
		HAL_GPIO_WritePin(MOTOR_R_IN__GPIO_Port,MOTOR_R_IN__Pin,GPIO_PIN_SET);//right 的IN+设置为高电平
		HAL_GPIO_WritePin(MOTOR_L_IN_B12_GPIO_Port,MOTOR_L_IN_B12_Pin,GPIO_PIN_RESET);//left 的IN-设置为低电平
		HAL_GPIO_WritePin(MOTOR_R_IN_B6_GPIO_Port,MOTOR_R_IN_B6_Pin,GPIO_PIN_RESET);//right 的IN-设置为低电平

}
//左转前进:左边的轮子不动,右边的轮子动
//左边轮子不使能,右边的轮子使能,并且将IN+设置为高电平,IN-设置为低电平
void left_forward(){
		//使能电机
		HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_SET);//使能右轮子
		HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_RESET);//不使能左轮子
		HAL_GPIO_WritePin(MOTOR_R_IN__GPIO_Port,MOTOR_R_IN__Pin,GPIO_PIN_SET);//right 的IN+设置为高置为高电平
		HAL_GPIO_WritePin(MOTOR_R_IN_B6_GPIO_Port,MOTOR_R_IN_B6_Pin,GPIO_PIN_RESET);//right 的IN-设置为低电平
}

//左转后退:左边的轮子不动,右边的轮子动
//左边轮子不使能,右边的轮子使能,并且将IN+设置为低电平,IN-设置为高电平
void left_back(){
		//使能电机
		HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_SET);//使能右轮子
		HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_RESET);//使能左轮子
		HAL_GPIO_WritePin(MOTOR_R_IN__GPIO_Port,MOTOR_R_IN__Pin,GPIO_PIN_RESET);//right 的IN+设置为高置为低电平
		HAL_GPIO_WritePin(MOTOR_R_IN_B6_GPIO_Port,MOTOR_R_IN_B6_Pin,GPIO_PIN_SET);//right 的IN-设置为高电平
}

//右转前进:右边的轮子不动,左边的轮子动
//右边轮子不使能,左边的轮子使能,并且将IN+设置为高电平,IN-设置为低电平
void right_forward(){
		//使能电机
		HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_RESET);//不使能右轮子
		HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_SET);//使能左轮子
		HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN__Pin,GPIO_PIN_SET);//left 的IN+设置为高电平
		HAL_GPIO_WritePin(MOTOR_L_IN_B12_GPIO_Port,MOTOR_L_IN_B12_Pin,GPIO_PIN_RESET);//left 的IN-设置为低电平
}

//右转后退:右边的轮子不动,左边的轮子动
//右边轮子不使能,左边的轮子使能,并且将IN+设置为高电平,IN-设置为低电平
void right_back(){
		//使能电机
		HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_RESET);//不使能右轮子
		HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_SET);//使能左轮子
		HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN__Pin,GPIO_PIN_RESET);//left 的IN+设置为低电平
		HAL_GPIO_WritePin(MOTOR_L_IN_B12_GPIO_Port,MOTOR_L_IN_B12_Pin,GPIO_PIN_SET);//left 的IN-设置为高电平
}
//停止
void stop(){
		//使能电机
		HAL_GPIO_WritePin(GPIOB, MOTOR_L_S_Pin,GPIO_PIN_RESET);//不使能左轮子
		HAL_GPIO_WritePin(GPIOB, MOTOR_R_S_Pin,GPIO_PIN_RESET);//不使能右轮子
}
/* 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_TIM1_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
	//发生测试串口代码
	HAL_UART_Transmit(&huart1,(uint8_t*)ceshi,strlen(ceshi),HAL_MAX_DELAY);
	//使能输入捕获中断【定时器中的】
	HAL_TIM_IC_Start_IT(&htim1,TIM_CHANNEL_1);
	//使能定时器中断
	HAL_TIM_Base_Start_IT(&htim1);
	char printfbuffer[128]={0};
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		if(rvcFlag){//接收到红外标志信号,表示数据接收完成
			for(int i=0;i<33;i++){
				//将从红外中获取的数据打印给printfbuffer
				sprintf(printfbuffer," %u",buffer[i]);
				//显示测试代码
				//HAL_MAX_DELAY:最大超时时间
				HAL_UART_Transmit(&huart1,(uint8_t*)printfbuffer,strlen(printfbuffer),HAL_MAX_DELAY);
				//换行
				if(i%8==0){
					HAL_UART_Transmit(&huart1,"\n",2,HAL_MAX_DELAY);
				}
			}
			//将标志位清零,等待下一次传输的数据
			rvcFlag=0;
			//将获取到的遥控数值转换为逻辑1或者逻辑0
			for(int i=0;i<8;i++){
				if(buffer[16+i]>=1400 && buffer[16+i]<1700){
					yaokong |=0x01;//将其设置为逻辑1
				}else if(buffer[16+i]>=400 && buffer[16+i]<=600){
					yaokong |=0x00;//将其设置为逻辑0
				}else {
					//错误处理
				}
				yaokong<<=1;
			}
			switch(yaokong){
				case 0x18://对应按键2
							forword();
							yaokong=0;
							break;
				case 0x4a://对应按键8
							backword();
							yaokong=0;
							break;
				case 0x10://对应按键4
							left_forward();
							yaokong=0;
							break;
				case 0x5a://对应按键6
							right_forward();
							yaokong=0;
							break;
				case 0x42://对应按键7
							left_back();
							yaokong=0;
							break;
				case 0x52://对应按键9
							right_back();
							yaokong=0;
							break;
				case 0x38://对应按键8
							stop();
							yaokong=0;
							break;
			}
		}
    /* 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};

  /** Configure the main internal regulator output voltage
  */
  HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);

  /** 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.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV1;
  RCC_OscInitStruct.PLL.PLLN = 16;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
  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_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;

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

/* USER CODE BEGIN 4 */
//定时器的中断处理回调函数
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim){
	//判断是否为上升沿捕获
	if(isUpcat){
		//将捕获的定时器的值赋值给valueUp
			valueUp=HAL_TIM_ReadCapturedValue(&htim1,TIM_CHANNEL_1);
		//将上升沿置为0
		isUpcat=0;//是否为上升沿的标志位
		//触发捕获事件的信号边沿
		__HAL_TIM_SET_CAPTUREPOLARITY(&htim1,TIM_CHANNEL_1,TIM_ICPOLARITY_FALLING);
	}else{//捕获下降沿
		//将捕获的定时器的值赋值给valueDown
		valueDown=HAL_TIM_ReadCapturedValue(&htim1,TIM_CHANNEL_1);
		//将上升沿置为1
		isUpcat=1;//是否为上升沿的标志位
		//触发捕获事件的信号边沿
		__HAL_TIM_SET_CAPTUREPOLARITY(&htim1,TIM_CHANNEL_1,TIM_ICPOLARITY_RISING);
		
		//计算差值
		kuandu=valueDown-valueUp;//取到了脉冲宽度
		
		//判断输入进来的数据是否是我们想要的【判断同步码头】
		if(kuandu>=4400 && kuandu <=4600){//接收码头的范围
			buffer_ID=0;//这个值用来判断我遍历到数组中的哪一个位置
			buffer[buffer_ID++]=kuandu;//将我们获得的数据填入
		}else if(buffer_ID>0){//表示此时我们还没有填充完整
			//接着往下填充
			buffer[buffer_ID++]=kuandu;
			if(buffer_ID>32){//表示接收完毕
				rvcFlag=1;//红外接收标志位,表示数据接收完成
				//将指针设置回开头
				buffer_ID=0;
			}
		}
	}
}
/* 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 */

六、超声波模块:HC-SR04

1.产品特点

测试距离=高电平时间*声速340/2

2.工作原理

发送触发信号

发出8个40KHZ的脉冲

接收输出回响信号(输入捕获)

3.实物图

4.硬件接线

ECHO--->PD1:是接收输出回响信号--->使用定时器照中的输入捕获模块(通过接收上升沿和下降沿的事件,进行事件的捕获)

5.CubeMX的设置

1.将ECHO设置为输入捕获模式

由原理图分析,知道ECHO是对应PD1,所以我们将PD1设置为输入捕获(定时器板块)

2.设置串口

3.设置用户时钟

当有物体靠近时,用户指示灯亮起

当没有物体靠近时,用户指示灯熄灭

6.代码编写

1.串口测试

	//串口测试
	HAL_UART_Transmit(&huart1,"go!",3,HAL_MAX_DELAY);

2.开启中断

	//开启定时器输入捕获
	HAL_TIM_IC_Start_IT(&htim17,TIM_CHANNEL_1);

3.编写中断处理回调函数

这里的上升沿和下降沿捕获与前面红外遥控一样

计算出一次数据接收用到的时间长度

//定时器输入捕获(回调函数)-->传输一次数据后得到的数值
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim){
	if(isUpcat){
		//获取上升沿的值
		valueUp=HAL_TIM_ReadCapturedValue(&htim17,TIM_CHANNEL_1);
		//将输入捕获模式改为下降沿触发
		__HAL_TIM_SET_CAPTUREPOLARITY(&htim17,TIM_CHANNEL_1,TIM_INPUTCHANNELPOLARITY_FALLING);
		
		isUpcat=0;
	}else{
		//获取下降沿的值
		valueDown=HAL_TIM_ReadCapturedValue(&htim17,TIM_CHANNEL_1);
		//将输入捕获模式改为上升沿触发
		__HAL_TIM_SET_CAPTUREPOLARITY(&htim17,TIM_CHANNEL_1,TIM_INPUTCHANNELPOLARITY_RISING);
		
		isUpcat=1;
		
		//计算宽度(输出回响信号)
		kuandu=valueDown-valueUp;
		
		//表示数据接收完成
		ready=1;
	}
}

4.计算传输距离

uint32_t jisuan(uint16_t kuandu){
	//此处我们/1000,是将长度单位改为毫米
	//计算公式=高电平时间*340m/s(光速)/2
	return kuandu*340/2/1000;
}

5.设置上升沿触发

	//先将电平拉低,在拉高制造一个上升沿
	HAL_GPIO_WritePin(TRIG_GPIO_Port,TRIG_Pin,GPIO_PIN_RESET);
	HAL_GPIO_WritePin(TRIG_GPIO_Port,TRIG_Pin,GPIO_PIN_SET);

6.将获取到的数据通过串口打印出来

  while (1)
  {
		//判断一次数据传输接收是否完成
		if(ready){
			juli=jisuan(kuandu);//计算出距离
			//将距离打印出来
			sprintf(printfJuli," %u",juli);
			//通过串口打印
			HAL_UART_Transmit(&huart1,(uint8_t*)printfJuli,strlen(printfJuli),HAL_MAX_DELAY);
			
			//记得将ready复位,才可以进行下一次数据采样
			ready=0;
		}
		//因为我们需要先制造一次上升沿的触发信号才可以进行数据接收,所以这里一定要写这个
		//先将电平拉低,在拉高制造一个上升沿
		HAL_GPIO_WritePin(TRIG_GPIO_Port,TRIG_Pin,GPIO_PIN_RESET);
		HAL_GPIO_WritePin(TRIG_GPIO_Port,TRIG_Pin,GPIO_PIN_SET);
		//设置用户指示灯
		//当物体靠近设定值,则灯亮起
		if(juli<=200){
			HAL_GPIO_WritePin(user_led_GPIO_Port,user_led_Pin,GPIO_PIN_RESET);
		}else{
			HAL_GPIO_WritePin(user_led_GPIO_Port,user_led_Pin,GPIO_PIN_SET);
		}
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }

7.完整代码

/* 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 "tim.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "string.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 */
uint8_t isUpcat=1;//默认输入高电平
uint16_t valueUp=0;
uint16_t valueDown=0;
//输出回响信号
uint32_t kuandu=0;
//数据接收完毕状态位
uint16_t ready=0;
//距离计算结果
uint32_t juli=0;
//将存放获得的数据
char printfJuli[124]={0};
uint32_t jisuan(uint16_t kuandu);
/* 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 */
	//串口测试
	HAL_UART_Transmit(&huart1,"go!",3,HAL_MAX_DELAY);
	//开启定时器输入捕获
	HAL_TIM_IC_Start_IT(&htim17,TIM_CHANNEL_1);
  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_TIM17_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
	//先将电平拉低,在拉高制造一个上升沿
	HAL_GPIO_WritePin(TRIG_GPIO_Port,TRIG_Pin,GPIO_PIN_RESET);
	HAL_GPIO_WritePin(TRIG_GPIO_Port,TRIG_Pin,GPIO_PIN_SET);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		//判断一次数据传输接收是否完成
		if(ready){
			juli=jisuan(kuandu);//计算出距离
			//将距离打印出来
			sprintf(printfJuli," %u",juli);
			//通过串口打印
			HAL_UART_Transmit(&huart1,(uint8_t*)printfJuli,strlen(printfJuli),HAL_MAX_DELAY);
			
			//记得将ready复位,才可以进行下一次数据采样
			ready=0;
		}
		//因为我们需要先制造一次上升沿的触发信号才可以进行数据接收,所以这里一定要写这个
		//先将电平拉低,在拉高制造一个上升沿
		HAL_GPIO_WritePin(TRIG_GPIO_Port,TRIG_Pin,GPIO_PIN_RESET);
		HAL_GPIO_WritePin(TRIG_GPIO_Port,TRIG_Pin,GPIO_PIN_SET);
		//设置用户指示灯
		//当物体靠近设定值,则灯亮起
		if(juli<=200){
			HAL_GPIO_WritePin(user_led_GPIO_Port,user_led_Pin,GPIO_PIN_RESET);
		}else{
			HAL_GPIO_WritePin(user_led_GPIO_Port,user_led_Pin,GPIO_PIN_SET);
		}
    /* 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};

  /** Configure the main internal regulator output voltage
  */
  HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);

  /** 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.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV1;
  RCC_OscInitStruct.PLL.PLLN = 16;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
  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_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;

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

/* USER CODE BEGIN 4 */
//定时器输入捕获(回调函数)-->传输一次数据后得到的数值
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim){
	if(isUpcat){
		//获取上升沿的值
		valueUp=HAL_TIM_ReadCapturedValue(&htim17,TIM_CHANNEL_1);
		//将输入捕获模式改为下降沿触发
		__HAL_TIM_SET_CAPTUREPOLARITY(&htim17,TIM_CHANNEL_1,TIM_INPUTCHANNELPOLARITY_FALLING);
		
		isUpcat=0;
	}else{
		//获取下降沿的值
		valueDown=HAL_TIM_ReadCapturedValue(&htim17,TIM_CHANNEL_1);
		//将输入捕获模式改为上升沿触发
		__HAL_TIM_SET_CAPTUREPOLARITY(&htim17,TIM_CHANNEL_1,TIM_INPUTCHANNELPOLARITY_RISING);
		
		isUpcat=1;
		
		//计算宽度(输出回响信号)
		kuandu=valueDown-valueUp;
		
		//表示数据接收完成
		ready=1;
	}
}
uint32_t jisuan(uint16_t kuandu){
	//此处我们/1000,是将长度单位改为毫米
	//计算公式=高电平时间*340m/s(光速)/2
	return kuandu*340/2/1000;
}
/* 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 */

七、OLED模块:SSD1306

1.基本介绍

在强光下还是可以看得清楚

背光

2.IIC

屏幕使用IIC进行通信

【STM32】IIC(Inter Integrated Cirruit--集成电路总线)-CSDN博客

 3.通信过程

4.芯片驱动:SSD1306

屏幕:128*64

是以列进行填充的

当8行数据填充满后,会自动转换到下一列

5.填充数据方法

方法1:

指令由3个数值组成=页地址(1个)+列地址(由2个组成)

2.方法二:

固定发送三个数据:第一个是页地址(行数)+第二个是固定为0x00+第三列地址(列数)

6.CubeMX设置

1.原理图分析

2.设置IIC

7.代码编写

0.指令集

【0x00】:表示要开始发送命令的指令

【0x40】:表示要开始发送数据的指令

1.初始化函数

//OLED初始化
void OLED_Init()
{
  OLED_SendCmd(0xAE); /*关闭显示 display off*/

  OLED_SendCmd(0x20);
  OLED_SendCmd(0x10);

  OLED_SendCmd(0xB0);

  OLED_SendCmd(0xC8);

  OLED_SendCmd(0x00);
  OLED_SendCmd(0x10);

  OLED_SendCmd(0x40);

  OLED_SendCmd(0x81);

  OLED_SendCmd(0xDF);
  OLED_SendCmd(0xA1);

  OLED_SendCmd(0xA6);
  OLED_SendCmd(0xA8);

  OLED_SendCmd(0x3F);

  OLED_SendCmd(0xA4);

  OLED_SendCmd(0xD3);
  OLED_SendCmd(0x00);

  OLED_SendCmd(0xD5);
  OLED_SendCmd(0xF0);

  OLED_SendCmd(0xD9);
  OLED_SendCmd(0x22);

  OLED_SendCmd(0xDA);
  OLED_SendCmd(0x12);

  OLED_SendCmd(0xDB);
  OLED_SendCmd(0x20);

  OLED_SendCmd(0x8D);
  OLED_SendCmd(0x14);

  OLED_SendCmd(0xAF); /*开启显示 display ON*/
}

2.显示数据

0x00:表示要开始发送指令

//这个是我们OLED的地址【开发方给的】
#define OLED_ADDRESS 0x78

//在显示屏上显示数据
void OLED_SendCmd(uint8_t cmd){
	
	uint8_t SendBuffer[2];
	SendBuffer[0]=0x00;//告诉他,我们要开始发送数据了【固定的】
	SendBuffer[1]=cmd;
	
	//IIC发送信息
	//OLED_ADDRESS:发送者的地址
	HAL_I2C_Master_Transmit(&hi2c1,OLED_ADDRESS,SendBuffer,strlen(SendBuffer),HAL_MAX_DELAY);
}

3.测试代码

【0x40】:表示要开始发送数据的指令

//测试函数
void OLED_test(){
	//我们发送的显示的位置为:第一列的第一行
	OLED_SendCmd(0xB0);//页地址(一共分为8块,1块8行)
	
	OLED_SendCmd(0x00);//列地址(分为2份表示)
	OLED_SendCmd(0x10);
	
	//要发送的数据
	//0x40:表示我们要开始发送数据【发送标志位】
	//0xaa:发送的数据
	char sendBuffer[]={0x40,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa};
	
	//IIC发送信息
	HAL_I2C_Master_Transmit(&hi2c1,OLED_ADDRESS,sendBuffer,strlen(sendBuffer),HAL_MAX_DELAY);
	
}

4.清屏函数

//存储一页数据
uint16_t GRAM[8][128]={0};

//因为我们在显示数据前应该先清屏
//要不然会出现问题
void Clear_OLED(){
	for(uint8_t i=0;i<8;i++){
		for(uint8_t j=0;j<128;j++){
			GRAM[i][j]=0;
		}
	}	
}

5.将显存中的数据显示到OLED上

//将显存中的数据,显示到OLED上
void OLED_ShowFrams(){
	//第一个是发送的指令【表示我们要发送的是数据】
	uint8_t SendSGRM[129];//存储发送的数据
	SendSGRM[0]=0x40;//告诉是发送给屏幕的数据(指令)
	for(uint8_t i=0;i<8;i++){
		for(uint8_t j=0;j<128;j++){
			//j+1:因为第一个存放了指令,后面才是存放数据
			SendSGRM[j+1]=GRAM[i][j];
		}
		//设置页地址
	OLED_SendCmd(0xb0+i);
	//设置列地址(由于列地址会自己换列,所以不需要自己手动换)
	OLED_SendCmd(0x00);//表示第一行
	OLED_SendCmd(0x10);//表示第一列
	//将数据发送给IIC
	HAL_I2C_Master_Transmit(&hi2c1,OLED_ADDRESS,SendSGRM,strlen(SendSGRM),HAL_MAX_DELAY);
	}
}

6.显示一个像素

//显示一个像素
void OLED_Setpixel(uint16_t x,uint16_t y){//x:列 y:表示行【页】
	//判断是否越界
	if(x>=128 || y>=64){
		return;
	}
	//y/8:表示第几页
	//y%8:表示在该页的第几行
	//0x01 << (y%8):将该行置为1,其他置为0
	GRAM[y/8][x] |= 0x01 << (y%8);
}

7.其他函数

//画直线
//***************************************
// * @param x1 起始点横坐标
// * @param y1 起始点纵坐标
// * @param x2 终止点横坐标
// * @param y2 终止点纵坐标
//****************************************
void OLED_DrawLine(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2)
{
  static uint8_t temp = 0;
  if (x1 == x2)
  {
    if (y1 > y2)
    {
      temp = y1;
      y1 = y2;
      y2 = temp;
    }
    for (uint8_t y = y1; y <= y2; y++)
    {
      OLED_SetPixel(x1, y);
    }
  }
  else if (y1 == y2)
  {
    if (x1 > x2)
    {
      temp = x1;
      x1 = x2;
      x2 = temp;
    }
    for (uint8_t x = x1; x <= x2; x++)
    {
      OLED_SetPixel(x, y1);
    }
  }
  else
  {
    // Bresenham直线算法
    int16_t dx = x2 - x1;
    int16_t dy = y2 - y1;
    int16_t ux = ((dx > 0) << 1) - 1;
    int16_t uy = ((dy > 0) << 1) - 1;
    int16_t x = x1, y = y1, eps = 0;
    dx = abs(dx);
    dy = abs(dy);
    if (dx > dy)
    {
      for (x = x1; x != x2; x += ux)
      {
        OLED_SetPixel(x, y);
        eps += dy;
        if ((eps << 1) >= dx)
        {
          y += uy;
          eps -= dx;
        }
      }
    }
    else
    {
      for (y = y1; y != y2; y += uy)
      {
        OLED_SetPixel(x, y);
        eps += dx;
        if ((eps << 1) >= dy)
        {
          x += ux;
          eps -= dy;
        }
      }
    }
  }
}

//绘制矩形
//********************
// * @param x 起始点横坐标
// * @param y 起始点纵坐标
// * @param w 矩形宽度
// * @param h 矩形高度
//*********************
void OLED_DrawRectangle(uint8_t x, uint8_t y, uint8_t w, uint8_t h)
{
  OLED_DrawLine(x, y, x + w, y);
  OLED_DrawLine(x, y + h, x + w, y + h);
  OLED_DrawLine(x, y, x, y + h);
  OLED_DrawLine(x + w, y, x + w, y + h);
}


//绘制圆形
//********************
// * @param x 圆心横坐标
// * @param y 圆心纵坐标
// * @param r 圆半径
//********************
void OLED_DrawCircle(uint8_t x, uint8_t y, uint8_t r)
{
  int16_t a = 0, b = r, di = 3 - (r << 1);
  while (a <= b)
  {
    OLED_SetPixel(x - b, y - a);
    OLED_SetPixel(x + b, y - a);
    OLED_SetPixel(x - a, y + b);
    OLED_SetPixel(x - b, y - a);
    OLED_SetPixel(x - a, y - b);
    OLED_SetPixel(x + b, y + a);
    OLED_SetPixel(x + a, y - b);
    OLED_SetPixel(x + a, y + b);
    OLED_SetPixel(x - b, y + a);
    a++;
    if (di < 0)
    {
      di += 4 * a + 6;
    }
    else
    {
      di += 10 + 4 * (a - b);
      b--;
    }
    OLED_SetPixel(x + a, y + b);
  }
}


//绘制填充三角形
//************************
// * @param x1 第一个点横坐标
// * @param y1 第一个点纵坐标
// * @param x2 第二个点横坐标
// * @param y2 第二个点纵坐标
// * @param x3 第三个点横坐标
// * @param y3 第三个点纵坐标
//*************************
void OLED_DrawFilledTriangle(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t x3, uint8_t y3)
{
  uint8_t a = 0, b = 0, y = 0, last = 0;
  if (y1 > y2)
  {
    a = y2;
    b = y1;
  }
  else
  {
    a = y1;
    b = y2;
  }
  y = a;
  for (; y <= b; y++)
  {
    if (y <= y3)
    {
      OLED_DrawLine(x1 + (y - y1) * (x2 - x1) / (y2 - y1), y, x1 + (y - y1) * (x3 - x1) / (y3 - y1), y);
    }
    else
    {
      last = y - 1;
      break;
    }
  }
  for (; y <= b; y++)
  {
    OLED_DrawLine(x2 + (y - y2) * (x3 - x2) / (y3 - y2), y, x1 + (y - last) * (x3 - x1) / (y3 - last), y);
  }
}




//绘制一个填充圆
// * @param x 圆心横坐标
// * @param y 圆心纵坐标
// * @param r 圆半径
void OLED_DrawFilledCircle(uint8_t x, uint8_t y, uint8_t r)
{
  int16_t a = 0, b = r, di = 3 - (r << 1);
  while (a <= b)
  {
    for (int16_t i = x - b; i <= x + b; i++)
    {
      OLED_SetPixel(i, y + a);
      OLED_SetPixel(i, y - a);
    }
    for (int16_t i = x - a; i <= x + a; i++)
    {
      OLED_SetPixel(i, y + b);
      OLED_SetPixel(i, y - b);
    }
    a++;
    if (di < 0)
    {
      di += 4 * a + 6;
    }
    else
    {
      di += 10 + 4 * (a - b);
      b--;
    }
  }
}



//*****************************
// * @brief 绘制一个椭圆
// * @param x 椭圆中心横坐标
// * @param y 椭圆中心纵坐标
// * @param a 椭圆长轴
// * @param b 椭圆短轴
//******************************
void OLED_DrawEllipse(uint8_t x, uint8_t y, uint8_t a, uint8_t b)
{
  int xpos = 0, ypos = b;
  int a2 = a * a, b2 = b * b;
  int d = b2 + a2 * (0.25 - b);
  while (a2 * ypos > b2 * xpos)
  {
    OLED_SetPixel(x + xpos, y + ypos);
    OLED_SetPixel(x - xpos, y + ypos);
    OLED_SetPixel(x + xpos, y - ypos);
    OLED_SetPixel(x - xpos, y - ypos);
    if (d < 0)
    {
      d = d + b2 * ((xpos << 1) + 3);
      xpos += 1;
    }
    else
    {
      d = d + b2 * ((xpos << 1) + 3) + a2 * (-(ypos << 1) + 2);
      xpos += 1, ypos -= 1;
    }
  }
  d = b2 * (xpos + 0.5) * (xpos + 0.5) + a2 * (ypos - 1) * (ypos - 1) - a2 * b2;
  while (ypos > 0)
  {
    OLED_SetPixel(x + xpos, y + ypos);
    OLED_SetPixel(x - xpos, y + ypos);
    OLED_SetPixel(x + xpos, y - ypos);
    OLED_SetPixel(x - xpos, y - ypos);
    if (d < 0)
    {
      d = d + b2 * ((xpos << 1) + 2) + a2 * (-(ypos << 1) + 3);
      xpos += 1, ypos -= 1;
    }
    else
    {
      d = d + a2 * (-(ypos << 1) + 3);
      ypos -= 1;
    }
  }
}

8.完整代码

1.oled.c

#include "oled.h"
#include "string.h"
//这个是我们OLED的地址【开发方给的】
#define OLED_ADDRESS 0x78
//存储一页数据
uint16_t GRAM[8][128]={0};


//在显示屏上显示数据
void OLED_SendCmd(uint8_t cmd){
	
	uint8_t SendBuffer[2];
	SendBuffer[0]=0x00;//告诉他,我们要开始发送数据了【固定的】
	SendBuffer[1]=cmd;
	
	//IIC发送信息
	//OLED_ADDRESS:发送者的地址
	HAL_I2C_Master_Transmit(&hi2c1,OLED_ADDRESS,SendBuffer,strlen(SendBuffer),HAL_MAX_DELAY);
}

//OLED初始化
void OLED_Init()
{
  OLED_SendCmd(0xAE); /*关闭显示 display off*/

  OLED_SendCmd(0x20);
  OLED_SendCmd(0x10);

  OLED_SendCmd(0xB0);

  OLED_SendCmd(0xC8);

  OLED_SendCmd(0x00);
  OLED_SendCmd(0x10);

  OLED_SendCmd(0x40);

  OLED_SendCmd(0x81);

  OLED_SendCmd(0xDF);
  OLED_SendCmd(0xA1);

  OLED_SendCmd(0xA6);
  OLED_SendCmd(0xA8);

  OLED_SendCmd(0x3F);

  OLED_SendCmd(0xA4);

  OLED_SendCmd(0xD3);
  OLED_SendCmd(0x00);

  OLED_SendCmd(0xD5);
  OLED_SendCmd(0xF0);

  OLED_SendCmd(0xD9);
  OLED_SendCmd(0x22);

  OLED_SendCmd(0xDA);
  OLED_SendCmd(0x12);

  OLED_SendCmd(0xDB);
  OLED_SendCmd(0x20);

  OLED_SendCmd(0x8D);
  OLED_SendCmd(0x14);

  OLED_SendCmd(0xAF); /*开启显示 display ON*/
}

//测试函数
void OLED_test(){
	//我们发送的显示的位置为:第一列的第一行
	OLED_SendCmd(0xB0);//页地址(一共分为8块,1块8行)
	
	OLED_SendCmd(0x00);//列地址(分为2份表示)
	OLED_SendCmd(0x10);
	
	//要发送的数据
	//0x40:表示我们要开始发送数据【发送标志位】
	//0xaa:发送的数据
	uint8_t sendBuffer[]={0x40,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa};
	
	//IIC发送信息
	HAL_I2C_Master_Transmit(&hi2c1,OLED_ADDRESS,sendBuffer,strlen(sendBuffer),HAL_MAX_DELAY);
	
}

//因为我们在显示数据前应该先清屏
//要不然会出现问题
void Clear_OLED(){
	for(uint8_t i=0;i<8;i++){
		for(uint8_t j=0;j<128;j++){
			GRAM[i][j]=0;
		}
	}	
}

//将显存中的数据,显示到OLED上
void OLED_ShowFrams(){
	//第一个是发送的指令【表示我们要发送的是数据】
	uint8_t SendSGRM[129];//存储发送的数据
	SendSGRM[0]=0x40;//告诉是发送给屏幕的数据(指令)
	for(uint8_t i=0;i<8;i++){
		for(uint8_t j=0;j<128;j++){
			//j+1:因为第一个存放了指令,后面才是存放数据
			SendSGRM[j+1]=GRAM[i][j];
		}
		//设置页地址
	OLED_SendCmd(0xb0+i);
	//设置列地址(由于列地址会自己换列,所以不需要自己手动换)
	OLED_SendCmd(0x00);//表示第一行
	OLED_SendCmd(0x10);//表示第一列
	//将数据发送给IIC
	HAL_I2C_Master_Transmit(&hi2c1,OLED_ADDRESS,SendSGRM,strlen(SendSGRM),HAL_MAX_DELAY);
	}
}

//显示一个像素
void OLED_Setpixel(uint16_t x,uint16_t y){//x:列 y:表示行【页】
	//判断是否越界
	if(x>=128 || y>=64){
		return;
	}
	//y/8:表示第几页
	//y%8:表示在该页的第几行
	//0x01 << (y%8):将该行置为1,其他置为0
	GRAM[y/8][x] |= 0x01 << (y%8);
}

//画直线
//***************************************
// * @param x1 起始点横坐标
// * @param y1 起始点纵坐标
// * @param x2 终止点横坐标
// * @param y2 终止点纵坐标
//****************************************
void OLED_DrawLine(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2)
{
  static uint8_t temp = 0;
  if (x1 == x2)
  {
    if (y1 > y2)
    {
      temp = y1;
      y1 = y2;
      y2 = temp;
    }
    for (uint8_t y = y1; y <= y2; y++)
    {
      OLED_SetPixel(x1, y);
    }
  }
  else if (y1 == y2)
  {
    if (x1 > x2)
    {
      temp = x1;
      x1 = x2;
      x2 = temp;
    }
    for (uint8_t x = x1; x <= x2; x++)
    {
      OLED_SetPixel(x, y1);
    }
  }
  else
  {
    // Bresenham直线算法
    int16_t dx = x2 - x1;
    int16_t dy = y2 - y1;
    int16_t ux = ((dx > 0) << 1) - 1;
    int16_t uy = ((dy > 0) << 1) - 1;
    int16_t x = x1, y = y1, eps = 0;
    dx = abs(dx);
    dy = abs(dy);
    if (dx > dy)
    {
      for (x = x1; x != x2; x += ux)
      {
        OLED_SetPixel(x, y);
        eps += dy;
        if ((eps << 1) >= dx)
        {
          y += uy;
          eps -= dx;
        }
      }
    }
    else
    {
      for (y = y1; y != y2; y += uy)
      {
        OLED_SetPixel(x, y);
        eps += dx;
        if ((eps << 1) >= dy)
        {
          x += ux;
          eps -= dy;
        }
      }
    }
  }
}

//绘制矩形
//********************
// * @param x 起始点横坐标
// * @param y 起始点纵坐标
// * @param w 矩形宽度
// * @param h 矩形高度
//*********************
void OLED_DrawRectangle(uint8_t x, uint8_t y, uint8_t w, uint8_t h)
{
  OLED_DrawLine(x, y, x + w, y);
  OLED_DrawLine(x, y + h, x + w, y + h);
  OLED_DrawLine(x, y, x, y + h);
  OLED_DrawLine(x + w, y, x + w, y + h);
}


//绘制圆形
//********************
// * @param x 圆心横坐标
// * @param y 圆心纵坐标
// * @param r 圆半径
//********************
void OLED_DrawCircle(uint8_t x, uint8_t y, uint8_t r)
{
  int16_t a = 0, b = r, di = 3 - (r << 1);
  while (a <= b)
  {
    OLED_SetPixel(x - b, y - a);
    OLED_SetPixel(x + b, y - a);
    OLED_SetPixel(x - a, y + b);
    OLED_SetPixel(x - b, y - a);
    OLED_SetPixel(x - a, y - b);
    OLED_SetPixel(x + b, y + a);
    OLED_SetPixel(x + a, y - b);
    OLED_SetPixel(x + a, y + b);
    OLED_SetPixel(x - b, y + a);
    a++;
    if (di < 0)
    {
      di += 4 * a + 6;
    }
    else
    {
      di += 10 + 4 * (a - b);
      b--;
    }
    OLED_SetPixel(x + a, y + b);
  }
}


//绘制填充三角形
//************************
// * @param x1 第一个点横坐标
// * @param y1 第一个点纵坐标
// * @param x2 第二个点横坐标
// * @param y2 第二个点纵坐标
// * @param x3 第三个点横坐标
// * @param y3 第三个点纵坐标
//*************************
void OLED_DrawFilledTriangle(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t x3, uint8_t y3)
{
  uint8_t a = 0, b = 0, y = 0, last = 0;
  if (y1 > y2)
  {
    a = y2;
    b = y1;
  }
  else
  {
    a = y1;
    b = y2;
  }
  y = a;
  for (; y <= b; y++)
  {
    if (y <= y3)
    {
      OLED_DrawLine(x1 + (y - y1) * (x2 - x1) / (y2 - y1), y, x1 + (y - y1) * (x3 - x1) / (y3 - y1), y);
    }
    else
    {
      last = y - 1;
      break;
    }
  }
  for (; y <= b; y++)
  {
    OLED_DrawLine(x2 + (y - y2) * (x3 - x2) / (y3 - y2), y, x1 + (y - last) * (x3 - x1) / (y3 - last), y);
  }
}




//绘制一个填充圆
// * @param x 圆心横坐标
// * @param y 圆心纵坐标
// * @param r 圆半径
void OLED_DrawFilledCircle(uint8_t x, uint8_t y, uint8_t r)
{
  int16_t a = 0, b = r, di = 3 - (r << 1);
  while (a <= b)
  {
    for (int16_t i = x - b; i <= x + b; i++)
    {
      OLED_SetPixel(i, y + a);
      OLED_SetPixel(i, y - a);
    }
    for (int16_t i = x - a; i <= x + a; i++)
    {
      OLED_SetPixel(i, y + b);
      OLED_SetPixel(i, y - b);
    }
    a++;
    if (di < 0)
    {
      di += 4 * a + 6;
    }
    else
    {
      di += 10 + 4 * (a - b);
      b--;
    }
  }
}



//*****************************
// * @brief 绘制一个椭圆
// * @param x 椭圆中心横坐标
// * @param y 椭圆中心纵坐标
// * @param a 椭圆长轴
// * @param b 椭圆短轴
//******************************
void OLED_DrawEllipse(uint8_t x, uint8_t y, uint8_t a, uint8_t b)
{
  int xpos = 0, ypos = b;
  int a2 = a * a, b2 = b * b;
  int d = b2 + a2 * (0.25 - b);
  while (a2 * ypos > b2 * xpos)
  {
    OLED_SetPixel(x + xpos, y + ypos);
    OLED_SetPixel(x - xpos, y + ypos);
    OLED_SetPixel(x + xpos, y - ypos);
    OLED_SetPixel(x - xpos, y - ypos);
    if (d < 0)
    {
      d = d + b2 * ((xpos << 1) + 3);
      xpos += 1;
    }
    else
    {
      d = d + b2 * ((xpos << 1) + 3) + a2 * (-(ypos << 1) + 2);
      xpos += 1, ypos -= 1;
    }
  }
  d = b2 * (xpos + 0.5) * (xpos + 0.5) + a2 * (ypos - 1) * (ypos - 1) - a2 * b2;
  while (ypos > 0)
  {
    OLED_SetPixel(x + xpos, y + ypos);
    OLED_SetPixel(x - xpos, y + ypos);
    OLED_SetPixel(x + xpos, y - ypos);
    OLED_SetPixel(x - xpos, y - ypos);
    if (d < 0)
    {
      d = d + b2 * ((xpos << 1) + 2) + a2 * (-(ypos << 1) + 3);
      xpos += 1, ypos -= 1;
    }
    else
    {
      d = d + a2 * (-(ypos << 1) + 3);
      ypos -= 1;
    }
  }
}




2.main.c

/* 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 "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "oled.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_I2C1_Init();
  /* USER CODE BEGIN 2 */
	//OLED初始化
	HAL_Delay(20);
	OLED_Init();
//	Clear_OLED();
//	OLED_ShowFrams();
	OLED_test();
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		for(uint8_t i=0;i<8;i++){
				Clear_OLED();
				OLED_Setpixel(64,32);
				OLED_ShowFrams();
		}
    /* 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};

  /** Configure the main internal regulator output voltage
  */
  HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);

  /** 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.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV1;
  RCC_OscInitStruct.PLL.PLLN = 8;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
  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_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != 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 */

八,综合项目

1.模块

红外避障

红外循迹

红外遥控

串口通信

2.CubeMX设置

1.超声波

2.红外遥控部分

3.串口通信

4.红外避障

5.红外循迹

6.电机

7.OLED

OLED使用到IIC通信,所以使用IIC引脚设置

8.串口

9.时钟配置

3.问题解决

当我们添加新的.c或者.h文件记得将其添加进去

本项目源码:

code/project/project · 林何/STM32G0xx小车 - 码云 - 开源中国 (gitee.com)

存在问题

1)电机循迹,避障部分--->左右单独无法进行正常显示

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数字堡垒:上海迅软科技揭示企业数据安全的几大守护方式

在数字化时代&#xff0c;数据已成为许多企业宝贵的资产。对企业而言&#xff0c;如何保护企业的数据安全是至关重要的一件事。但是&#xff0c;数据同时也面临着许多安全威胁&#xff0c;如数据泄露、数据损坏、盗窃等。而企业失去数据或数据遭到未经授权的访问都可能导致严重…

什么是 shell 脚本?

一、什么是 shell&#xff1f; Shell 是一个应用程序&#xff0c;它负责接收用户输入的命令&#xff0c;然后根据命令做出相应的动作&#xff0c; Shell 负责将应用层或者用户输入的命令传递给系统内核&#xff0c;由操作系统内核来完成相应的工作&#xff0c;然后将结果反馈给…

代码随想录算法训练营第38天| 509. 斐波那契数 70. 爬楼梯 746. 使用最小花费爬楼梯

JAVA代码编写 动态规划&#xff08;Dynamic Programming&#xff09; 一个问题可以划分为多个子问题&#xff0c;且子问题之间有关联&#xff0c;就可以使用动态规划。 动态规划问题步骤&#xff1a; 确定dp数组&#xff08;dp table&#xff09;以及下标的含义确定递推公式…

一文秒懂 springsecurity6.2实现自定义登录页

前言 springsecurity原理和基础这里暂时不讲&#xff0c;网上资料太多了&#xff0c;这个大家可以自行查找学习&#xff0c;基本上没什么太大区别&#xff0c;看几篇文章就了解了&#xff0c;这篇文章主要是针对自定义登录页做一个demo&#xff0c;通过这个小 demo&#xff0c…

无线4G电表有哪些优势?

无线4G电表是一种电力管理技术&#xff0c;它具有许多优势&#xff0c;使其成为现代社会中的重要应用。无线4G电表的主要特点是利用4G网络进行数据传输和通信&#xff0c;以取代传统的有线连接方式。那么&#xff0c;无线4G电表有哪些优势呢&#xff1f; 首先&#xff0c;无线4…

提高企业邮箱安全性的策略与技巧

提高企业邮箱的安全性的方法大体相同&#xff0c;每家邮箱供应商也可能会有自己独有防护措施。 为了增强Zoho Mail企业邮箱的安全性&#xff0c;您可参考以下建议&#xff1a; 采用强密码&#xff1a;创建包含大写字母、小写字母、数字和特殊字符的复杂密码&#xff0c;并定期…

vue中实现数字+英文字母组合键盘

完整代码 <template><div class"login"><div click"setFileClick">欢迎使用员工自助终端</div><el-dialog title"初始化设置文件打印消耗品配置密码" :visible.sync"dialogSetFile" width"600px&quo…

Powerbuilder9.0 安装是一直卡在setup is running无法继续

这种情况是安装时&#xff0c;他后面弹出来一个提示框&#xff0c;但是因为其他进程的干扰&#xff0c;我们无法看到也就无法继续了。 我看到这个文章&#xff1a;https://blog.csdn.net/FLORY_/article/details/105244102 使用他说的方法的确有效。过程 1. 打开任务管理器 …

【AI数字人-论文】DINet论文解读

DINet 方法形变修补损失函数perception lossGAN lossLip-sync loss 实现细节参考 如下图所示&#xff0c;人脸视觉配音&#xff08;Face visually dubbing&#xff09;旨在根据输入的驱动音频同步源视频中的嘴型&#xff0c;同时保持身份和头部姿势与源视频帧一致。然而在少样本…

子查询在SQL中的应用和实践

作者&#xff1a;CSDN-川川菜鸟 在SQL中&#xff0c;子查询是一种强大的工具&#xff0c;用于解决复杂的数据查询问题。本文将深入探讨子查询的概念、类型、规则&#xff0c;并通过具体案例展示其在实际应用中的用途。 文章目录 子查询概念子查询的类型子查询的规则实际案例分析…

leetcode 1658. 将 x 减到 0 的最小操作数(优质解法)

代码&#xff1a; class Solution {public int minOperations(int[] nums, int x) {int sum0; // nums 数组中的数据总和int lengthnums.length;for(int i0;i<length;i){sumnums[i];}int targetsum-x; //待查找的子数组的和if(target<0){return -1;}//采用滑动窗口的…

团建策划信息展示服务预约小程序效果如何

团建是中大型企业商家每年举办的员工活动&#xff0c;其形式多样化、具备全部参与的娱乐性。但在实际策划流程及内容时&#xff0c;部分公司便会难以入手&#xff0c;术业有专攻&#xff0c;这个时候团建策划公司便会发挥效果。 如拓展训练、露营、运动会、体育竞技等往往更具…

美格智能亮相中国企业家博鳌论坛:勇立创新潮头,5G+AIoT开启智能新时代

12月2日-5日&#xff0c;由新华社和海南省人民政府、中国品牌建设促进会等联合主办的2023中国企业家博鳌论坛在海南博鳌召开&#xff0c;包括众多世界500强、中国500强和行业领军企业家在内的3500多名社会各界嘉宾汇聚一堂&#xff0c;共谋中国经济高质量发展之道。 会议现场 …

知虾网:帮助卖家优化Shopee店铺运营的数据分析工具

在如今的电商时代&#xff0c;越来越多的卖家选择在Shopee平台上开设店铺。然而&#xff0c;随着竞争的加剧&#xff0c;如何提高店铺的曝光率和销售效果成为了卖家们面临的重要问题。为了帮助Shopee卖家更好地了解市场动态、优化商品策略、提高运营效果&#xff0c;知虾网应运…

LeetCode 每日一题 Day 4

2477. 到达首都的最少油耗 给你一棵 n 个节点的树&#xff08;一个无向、连通、无环图&#xff09;&#xff0c;每个节点表示一个城市&#xff0c;编号从 0 到 n - 1 &#xff0c;且恰好有 n - 1 条路。0 是首都。给你一个二维整数数组 roads &#xff0c;其中 roads[i] [ai,…

二叉树OJ题之三

哈喽伙伴们&#xff0c;有一段时间没更新博客了&#xff0c;主要是这段时间要准备学校的期末考试&#xff0c;所以没有把部分时间分给博客&#xff0c;今天我们一起去接着看二叉树递归有关的OJ题&#xff0c;今天我们要学习的是 判断相同的树&#xff0c;力扣题目--100 &…

2023年度亚太客户中心产业发展论坛——鸿联九五荣获亚太区卓越客服大赛客户运营管理类铂金大奖

11月27-28日&#xff0c; 2023年度亚太客户中心产业发展论坛暨亚太区卓越客服大赛在马来西亚吉隆坡举行。来自中国、澳大利亚、马来西亚、新加坡、中国香港、印度尼西亚和泰国等多个国家及地区的优秀企业代表齐聚吉隆坡。 论坛首日活动以“Experience Excellence, Meet the Cha…

Redis应用-缓存

目录 什么是缓存 使用redis作为缓存 缓存的更新策略 通用的淘汰策略 redis内置的淘汰策略 缓存预热 缓存穿透 缓存雪崩 缓存击穿 什么是缓存 缓存(cache)是计算机中一个经典的概念,在很多的场景中都会涉及到. 核心思路就是把一些常用的数据放到触手可及(访问速度更快…

javascript实现List列表数据结构

书籍推荐 有幸拜读《数据结构与算法Javascript描述》这本书&#xff0c;先强烈安利一波&#xff01;非常感谢作者大大给我们前端领域带来这本书。 全书从javascript的角度出发&#xff0c;简单明了的分析了数据结构在javascript领域的实现过程与实际的应用案例&#xff0c;且…

VA03 凭证流 查看备忘

今天被问到了&#xff0c;为什么这个销售订单 只显示了3 EA 仔细看了一下&#xff0c;前面10 是行项目 看销售订单 最后发现&#xff0c;凭证流跟选择查看的行项目有关系 以前一直没有关注这个细节