【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创建一个空文件

【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闪烁
cpp 复制代码
  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

按下按键为亮,松开为灭

cpp 复制代码
  /* 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

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

cpp 复制代码
		if(HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_13)==GPIO_PIN_RESET){
			//如果按下,将led点亮
				HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_7);
		}
4.按键消抖
cpp 复制代码
		//按键消抖
		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.代码编写

cpp 复制代码
	//电机使能
	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.函数封装

cpp 复制代码
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.按键设置

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

3.goto语句

cpp 复制代码
  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.完整代码

cpp 复制代码
/* 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.代码编写

cpp 复制代码
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不会亮【正常工作】

cpp 复制代码
  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.代码编写

cpp 复制代码
//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.代码编写

cpp 复制代码
/**
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.代码编写

cpp 复制代码
//前进
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.完整代码

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

cpp 复制代码
/* 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.代码编写

cpp 复制代码
  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.代码编写

cpp 复制代码
  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都灭

cpp 复制代码
  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.前方有障碍物代码

cpp 复制代码
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.编写定时器中断处理函数

cpp 复制代码
//定时器的中断处理回调函数
void HAL_TIM_IC_CaptureCallback(){

}

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

1.HAL_TIM_ReadCapturedValue: 从Capture Compare单元读取捕获值
2.__HAL_TIM_SET_CAPTUREPOLARITY:触发捕获事件的信号边沿

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

3.代码
cpp 复制代码
//定时器的中断处理回调函数
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)当填充完数据,记得将指针设置为初始状态

cpp 复制代码
//上升沿
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.串口测试代码

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

5.使能中断

开启定时器的IT中断

开启输入捕获(IC_IT)

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

6.获取数据并打印

cpp 复制代码
  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

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

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

cpp 复制代码
			//将获取到的遥控数值转换为逻辑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.完整代码

cpp 复制代码
/* 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.串口测试

cpp 复制代码
	//串口测试
	HAL_UART_Transmit(&huart1,"go!",3,HAL_MAX_DELAY);

2.开启中断

cpp 复制代码
	//开启定时器输入捕获
	HAL_TIM_IC_Start_IT(&htim17,TIM_CHANNEL_1);

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

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

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

cpp 复制代码
//定时器输入捕获(回调函数)-->传输一次数据后得到的数值
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.计算传输距离

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

5.设置上升沿触发

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

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

cpp 复制代码
  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.完整代码

cpp 复制代码
/* 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.初始化函数

cpp 复制代码
//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:表示要开始发送指令

cpp 复制代码
//这个是我们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】:表示要开始发送数据的指令

cpp 复制代码
//测试函数
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.清屏函数

cpp 复制代码
//存储一页数据
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上

cpp 复制代码
//将显存中的数据,显示到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.显示一个像素

cpp 复制代码
//显示一个像素
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.其他函数

cpp 复制代码
//画直线
//***************************************
// * @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

cpp 复制代码
#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

cpp 复制代码
/* 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)电机循迹,避障部分--->左右单独无法进行正常显示

相关推荐
温柔的男孩像海洋丶21 分钟前
vscode的keil assistant 中搜索不到全局变量
ide·vscode·单片机
沐欣工作室_lvyiyi36 分钟前
基于单片机的多功能智能小车(论文+源码)
stm32·单片机·嵌入式硬件·毕业设计·单片机毕业设计
鸿喵小仙女1 小时前
C# WPF读写STM32/GD32单片机Flash数据
stm32·单片机·c#·wpf
lucy153027510792 小时前
MCU 功耗基准测试
科技·单片机·嵌入式硬件·智能家居·信号处理·工控主板
m0_748240912 小时前
OpenMV与STM32通信全面指南
stm32·单片机·嵌入式硬件
Cchengzu4 小时前
阿里巴巴2017实习生笔试题(二)
stm32·单片机·嵌入式硬件
重生之我是数学王子8 小时前
单片机 STM32入门
stm32·单片机·嵌入式硬件
qq_4597300311 小时前
4-3 MCU中ARM存储器的作用
arm开发·单片机·嵌入式硬件
嵌入式科普14 小时前
嵌入式科普(24)从SPI和CAN通信重新理解“全双工”
c语言·stm32·can·spi·全双工·ra6m5
重生之我是数学王子15 小时前
点亮核心板小灯 STM32U575
stm32·单片机·嵌入式硬件