小车介绍
本博文将会从0到1实现一个智能小车,该小车实现功能:1. 摇头避障模式、2. 跟随模式、3. 循迹模式、4. 小车测速并显示在OLED屏幕、5. 语音控制小车等等。
硬件组成
STM32F103开发板、小车套件、L9110S电机模块、超声波模块(HC-SR04)、sg90舵机、测速模块、循迹模块、红外避障模块等等(下面有详细介绍)
模块化编程
小车采用模块化编程循序渐进,即每实现新的功能小车需要复制前一种功能小车的文件夹来进行修改,否则会因为丢失某些文件导致小车功能缺失。
移动小车
让小车动起来。
硬件组成
L9110S电机模块
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-1.png)
L9110S电机模块用来驱动两个电机,如果需要控制四个电机则需要两个L9110S电机模块。
-
当B-1A为高电平,B-2A为低电平时,电机反转或正转。
-
当B-1A为低电平,B-2A为高电平时,电机正转或反转。
-
当B-1A为低电平,B-2A为低电平时,电机不转。
-
A-1A、A-1B同理。
-
电机的正转和反转与跟电机的接线不同而不同,注意自己调试。
L9110S电机模块与STM32F103板子接线
-
B-1A <-> PB0
-
B-2A <-> PA0
-
A-1A <-> PB2
-
A-1B <-> PA1
STM32CubeMX相关配置
配置SYS
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-2.png)
配置RCC
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-3.png)
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-4.png)
配置GPIO
配置PA0引脚、PA1引脚、PB0引脚、PB2引脚输出高电平。
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-5.png)
文件编写
添加文件car.c
#include "gpio.h"
void car_goForward()
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);
}
void car_goBack()
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET);
}
void car_goStop()
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET);
}
void car_goRight()
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET);
}
void car_goLeft()
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);
}
添加文件car.h
void car_goForward(void);
void car_goBack(void);
void car_goStop(void);
void car_goRight(void);
void car_goLeft(void);
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 "gpio.h"
#include "car.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 */
/* 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)
{
/* USER CODE END WHILE */
car_goForward();
HAL_Delay(1000);
car_goStop();
HAL_Delay(1000);
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* 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 */
USART小车
利用USART接收串口信息,实现蓝牙遥控小车等功能。
STM32CubeMX相关配置
配置USART1
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-6.png)
配置NVIC
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-7.png)
使用Micro库
只要映射了printf用来发送数据去串口都要使用这个库。
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-8.png)
文件编写
修改文件usart.c
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file usart.c
* @brief This file provides code for the configuration
* of the USART instances.
******************************************************************************
* @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 "usart.h"
/* USER CODE BEGIN 0 */
#include <stdio.h>
#include <string.h>
#include "car.h"
#define USART_REC_LEN 200
// 串口接收缓存(1字节)
uint8_t buf = 0;
uint8_t UART1_RX_Buffer[USART_REC_LEN]; // 接收缓冲,串口接收的数据存放地点
// 串口接收状态,16位
uint16_t UART1_RX_STA = 0;
// bit15: 如果是1表示接收完成
// bit14: 如果是1表示接收到回车(0x0d)
// bit13~bit0: 接收到的有效字节数目
/* USER CODE END 0 */
UART_HandleTypeDef huart1;
/* USART1 init function */
void MX_USART1_UART_Init(void)
{
/* USER CODE BEGIN USART1_Init 0 */
/* USER CODE END USART1_Init 0 */
/* USER CODE BEGIN USART1_Init 1 */
/* USER CODE END USART1_Init 1 */
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART1_Init 2 */
/* 开启串口1的接收中断 */
HAL_UART_Receive_IT(&huart1, &buf, 1); /* 每接收一个串口数据调用一次串口接收完成回调函数 */
/* USER CODE END USART1_Init 2 */
}
void HAL_UART_MspInit(UART_HandleTypeDef *uartHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if (uartHandle->Instance == USART1)
{
/* USER CODE BEGIN USART1_MspInit 0 */
/* USER CODE END USART1_MspInit 0 */
/* USART1 clock enable */
__HAL_RCC_USART1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/**USART1 GPIO Configuration
PA9 ------> USART1_TX
PA10 ------> USART1_RX
*/
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USART1 interrupt Init */
HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(USART1_IRQn);
/* USER CODE BEGIN USART1_MspInit 1 */
/* USER CODE END USART1_MspInit 1 */
}
}
void HAL_UART_MspDeInit(UART_HandleTypeDef *uartHandle)
{
if (uartHandle->Instance == USART1)
{
/* USER CODE BEGIN USART1_MspDeInit 0 */
/* USER CODE END USART1_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_USART1_CLK_DISABLE();
/**USART1 GPIO Configuration
PA9 ------> USART1_TX
PA10 ------> USART1_RX
*/
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9 | GPIO_PIN_10);
/* USART1 interrupt Deinit */
HAL_NVIC_DisableIRQ(USART1_IRQn);
/* USER CODE BEGIN USART1_MspDeInit 1 */
/* USER CODE END USART1_MspDeInit 1 */
}
}
/* USER CODE BEGIN 1 */
/* 重写stdio.h文件中的prinft()里的fputc()函数 */
int fputc(int my_data, FILE *p)
{
unsigned char temp = my_data;
// 改写后,使用printf()函数会将数据通过串口一发送出去
HAL_UART_Transmit(&huart1, &temp, 1, 0xffff); // 0xfffff为最大超时时间
return my_data;
}
/* 串口接收完成回调函数 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
// 判断中断是哪个串口触发的
if (huart->Instance == USART1)
{
// 判断接收是否完成,即判断UART1_RX_STA的bit15是否为1
if (!(UART1_RX_STA & 0x8000))
{ // 如果没接收完成就进入接收流程
// 判断是否接收到回车0x0d
if (UART1_RX_STA & 0x4000)
{
// 判断是否接收到换行0x0a
if (buf == 0x0a)
{
// 如果回车和换行都接收到了,则表示接收完成,即把bit15拉高
UART1_RX_STA |= 0x8000;
}
else
{ // 如果接收到回车0x0d没有接收到换行0x0a
// 则认为接收错误,重新开始接收
UART1_RX_STA = 0;
}
}
else
{ // 如果没有接收到回车0x0d
// 则判断收到的这个字符是否是回车0x0d
if (buf == 0x0d)
{
// 如果这个字符是回车,则将将bit14拉高,表示接收到回车
UART1_RX_STA |= 0x4000;
}
else
{ // 如果不是回车
// 则将这个字符存放到缓存数组中
UART1_RX_Buffer[UART1_RX_STA & 0x3ffff] = buf;
UART1_RX_STA++;
// 如果接收数据大于UART1_REC_LEN(200字节),则重新开始接收
if (UART1_RX_STA > USART_REC_LEN - 1)
{
UART1_RX_STA = 0;
}
}
}
}
// 如果接收完成则重新开启串口1的接收中断
HAL_UART_Receive_IT(&huart1, &buf, 1);
}
}
/* 对串口接收数据的处理 */
void usart1_receive_data_handle()
{
/* 判断判断串口是否接收完成 */
if (UART1_RX_STA & 0x8000)
{
printf("接收完成\r\n");
// 串口接收完数据后,对串口数据进行处理
if (!strcmp((const char *)UART1_RX_Buffer, "forward"))
{
printf("前进\r\n");
car_goForward();
}
else if (!strcmp((const char *)UART1_RX_Buffer, "back"))
{
printf("后退\r\n");
car_goBack();
}
else if (!strcmp((const char *)UART1_RX_Buffer, "left"))
{
printf("左转\r\n");
car_goLeft();
}
else if (!strcmp((const char *)UART1_RX_Buffer, "right"))
{
printf("右转\r\n");
car_goRight();
}
else if (!strcmp((const char *)UART1_RX_Buffer, "stop"))
{
printf("停止\r\n");
car_goStop();
}
// 接收到其他数据,进行报错
else
{
if (UART1_RX_Buffer[0] != '\0')
{
printf("%s\r\n", "输入错误,请重新输入");
}
}
// 换行,重新开始下一次接收
memset(UART1_RX_Buffer, 0, USART_REC_LEN);
// printf("\r\n");
UART1_RX_STA = 0;
}
}
/* USER CODE END 1 */
修改文件usart.h
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file usart.h
* @brief This file contains all the function prototypes for
* the usart.c file
******************************************************************************
* @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 */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __USART_H__
#define __USART_H__
#ifdef __cplusplus
extern "C"
{
#endif
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
extern UART_HandleTypeDef huart1;
/* USER CODE BEGIN Private defines */
/* USER CODE END Private defines */
void MX_USART1_UART_Init(void);
/* USER CODE BEGIN Prototypes */
void usart1_receive_data_handle(void);
/* USER CODE END Prototypes */
#ifdef __cplusplus
}
#endif
#endif /* __USART_H__ */
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 "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include "car.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_USART1_UART_Init();
/* USER CODE BEGIN 2 */
printf("haozige\r\n");
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
usart1_receive_data_handle(); /* 对串口读取的数据进行处理 */
HAL_Delay(40);
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* 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 */
调速并测速小车
使用TIM2的通道1、通道2给左右电机输入不同占空比的PWM来达到轮子调速的目的,并使用测速模块将轮子当前速度显示在OLED屏,OLED屏使用软件模拟的I2C来驱动。
硬件组成
测速模块
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-9.png)
-
发射的红外线被物体遮挡时,输出高电平,发射的红外线没被物体遮挡时,输出低电平。
-
即有物体高电平,没物体低电平。
-
当搭配小车测速盘,会形成下降沿(有遮挡高电平,没遮挡低电平)。
模块与STM32F103板子接线
-
测速模块DO引脚 <-> PB13
-
OLED屏SCL引脚 <-> PB6
-
OLED屏SDA引脚 <-> PB7
STM32CubeMX相关配置
配置GPIO
-
配置PB13引脚为引脚中断。
-
配置PB6引脚输出高电平。
-
重置PA0、PA1引脚的状态,防止待会配置定时器2通道输出PWM时映射到其他引脚。
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-10.png)
配置定时器2和定时器3
配置定时器2的通道1和通道2输出PWM,用来给电机调速。
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-11.png)
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-12.png)
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-13.png)
配置定时器3定时时间为1s。
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-14.png)
配置NVIC
-
打开定时器3和PB13引脚的中断。
-
修改定时器3响应中断优先级为4,PB13引脚中断响应优先级为3。
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-15.png)
文件编写
修改文件car.c
利用PWM修改在一个调速周期内低电平占用的时间来达到轮子调速的效果。
#include "gpio.h"
#include "tim.h"
/* B_1A、B-2A控制左边电机,A-1A、A-1B控制右边电机 */
#define B_1A_LOW HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET) /* PB0 */
#define B_1A_HIGH HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET)
#define A_1A_LOW HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_RESET) /* PB2 */
#define A_1A_HIGH HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_SET)
/* 轮子速度:0~19 */
void car_goForward()
{
B_1A_HIGH;
__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1, 19); // B_2A_LOW,PA0
A_1A_HIGH;
__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2, 19); // A_1B_LOW,PA1
}
void car_goBack()
{
B_1A_LOW;
__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1, 0); // B_2A_HIGH;
A_1A_LOW;
__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2, 0); // A_1B_HIGH;
}
void car_goStop()
{
B_1A_HIGH;
__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1, 0); // B_2A_HIGH;
A_1A_HIGH;
__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2, 0); // A_1B_HIGH;
}
void car_goRight()
{
B_1A_HIGH;
__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1, 19); // B_2A_LOW;
A_1A_HIGH;
__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2, 0); // A_1B_HIGH;
}
void car_goLeft()
{
B_1A_HIGH;
__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1, 0); // B_2A_HIGH;
A_1A_HIGH;
__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2, 19); // A_1B_LOW;
}
修改文件tim.c
启动TIM2的通道1、通道2的PWM,启动TIM3并使能中断。
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file tim.c
* @brief This file provides code for the configuration
* of the TIM instances.
******************************************************************************
* @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 "tim.h"
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
TIM_HandleTypeDef htim2;
TIM_HandleTypeDef htim3;
/* TIM2 init function */
void MX_TIM2_Init(void)
{
/* USER CODE BEGIN TIM2_Init 0 */
/* USER CODE END TIM2_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
/* USER CODE BEGIN TIM2_Init 1 */
/* USER CODE END TIM2_Init 1 */
htim2.Instance = TIM2;
htim2.Init.Prescaler = 71;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 19;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_PWM_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_LOW;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM2_Init 2 */
/* 启动B-2A、A-1B引脚的PWM */
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2);
/* USER CODE END TIM2_Init 2 */
HAL_TIM_MspPostInit(&htim2);
}
/* TIM3 init function */
void MX_TIM3_Init(void)
{
/* USER CODE BEGIN TIM3_Init 0 */
/* USER CODE END TIM3_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
/* USER CODE BEGIN TIM3_Init 1 */
/* USER CODE END TIM3_Init 1 */
htim3.Instance = TIM3;
htim3.Init.Prescaler = 7199;
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 9999;
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM3_Init 2 */
HAL_TIM_Base_Start_IT(&htim3); /* 启动定时器3,并使能中断 */
/* USER CODE END TIM3_Init 2 */
}
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *tim_baseHandle)
{
if (tim_baseHandle->Instance == TIM2)
{
/* USER CODE BEGIN TIM2_MspInit 0 */
/* USER CODE END TIM2_MspInit 0 */
/* TIM2 clock enable */
__HAL_RCC_TIM2_CLK_ENABLE();
/* USER CODE BEGIN TIM2_MspInit 1 */
/* USER CODE END TIM2_MspInit 1 */
}
else if (tim_baseHandle->Instance == TIM3)
{
/* USER CODE BEGIN TIM3_MspInit 0 */
/* USER CODE END TIM3_MspInit 0 */
/* TIM3 clock enable */
__HAL_RCC_TIM3_CLK_ENABLE();
/* TIM3 interrupt Init */
HAL_NVIC_SetPriority(TIM3_IRQn, 4, 0);
HAL_NVIC_EnableIRQ(TIM3_IRQn);
/* USER CODE BEGIN TIM3_MspInit 1 */
/* USER CODE END TIM3_MspInit 1 */
}
}
void HAL_TIM_MspPostInit(TIM_HandleTypeDef *timHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if (timHandle->Instance == TIM2)
{
/* USER CODE BEGIN TIM2_MspPostInit 0 */
/* USER CODE END TIM2_MspPostInit 0 */
__HAL_RCC_GPIOA_CLK_ENABLE();
/**TIM2 GPIO Configuration
PA0-WKUP ------> TIM2_CH1
PA1 ------> TIM2_CH2
*/
GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USER CODE BEGIN TIM2_MspPostInit 1 */
/* USER CODE END TIM2_MspPostInit 1 */
}
}
void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef *tim_baseHandle)
{
if (tim_baseHandle->Instance == TIM2)
{
/* USER CODE BEGIN TIM2_MspDeInit 0 */
/* USER CODE END TIM2_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_TIM2_CLK_DISABLE();
/* USER CODE BEGIN TIM2_MspDeInit 1 */
/* USER CODE END TIM2_MspDeInit 1 */
}
else if (tim_baseHandle->Instance == TIM3)
{
/* USER CODE BEGIN TIM3_MspDeInit 0 */
/* USER CODE END TIM3_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_TIM3_CLK_DISABLE();
/* TIM3 interrupt Deinit */
HAL_NVIC_DisableIRQ(TIM3_IRQn);
/* USER CODE BEGIN TIM3_MspDeInit 1 */
/* USER CODE END TIM3_MspDeInit 1 */
}
}
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
添加文件my_i2c.c
软件模拟I2C来驱动OLED屏显示,也可以使用硬件I2C,就不用此文件了。
#include "gpio.h"
#define SCL_HIGH HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET)
#define SCL_LOW HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_RESET)
#define SDA_INPUT SDA_GPIO_PB7_INIT(GPIO_MODE_INPUT)
#define SDA_OUTPUT SDA_GPIO_PB7_INIT(GPIO_MODE_OUTPUT_PP)
#define SDA_HIGH HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET)
#define SDA_LOW HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET)
#define SDA_READ HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7)
/* 微秒延时函数:只适用F1系列72M主频 */
void delay_us(uint32_t us)
{
uint32_t delay = (HAL_RCC_GetHCLKFreq() / 4000000 * us);
while (delay--)
{
;
}
}
/* 配置PB7为输入引脚或者输出引脚 */
void SDA_GPIO_PB7_INIT(uint32_t mode)
{
// 打开时钟
__HAL_RCC_GPIOB_CLK_ENABLE();
// 配置引脚
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_7;
GPIO_InitStruct.Mode = mode; // 配置为输入(GPIO_MODE_INPUT)或输出(GPIO_MODE_OUTPUT_PP)
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
/* 起始信号 */
void IIC_start()
{
SDA_OUTPUT; /* 配置SDA引脚为输出引脚 */
SCL_HIGH;
SDA_HIGH;
delay_us(5);
SDA_LOW;
delay_us(5);
SCL_LOW;
delay_us(5);
}
/* 终止信号 */
void IIC_stop()
{
SDA_OUTPUT; /* 配置SDA引脚为输出引脚 */
SDA_LOW;
delay_us(5);
SCL_HIGH;
delay_us(5);
SDA_HIGH;
delay_us(5);
}
/* 检测应答信号:ACK返回0,NACK返回1 */
uint8_t IIC_wait_ack()
{
SDA_OUTPUT; /* 配置SDA引脚为输出引脚 */
SDA_HIGH; /* 释放数据线 */
delay_us(5);
SCL_HIGH; /* 从机返回ACK */
delay_us(5);
SDA_INPUT; /* 配置SDA引脚为输入引脚 */
/* 读取SDA的电平 */
if (SDA_READ == GPIO_PIN_SET)
{
/* 如果是高电平则为NACK */
IIC_stop();
return 1;
}
SCL_LOW; /* 结束应答信号的检测 */
delay_us(5);
return 0;
}
/* 发送一个字节数据 */
void IIC_send_byte(uint8_t data)
{
SDA_OUTPUT; /* 配置SDA引脚为输出引脚 */
for (uint8_t i = 0; i < 8; i++)
{
/* 从最高位开始发送 */
if ((data & 0x80) >> 7)
{
SDA_HIGH;
}
else
{
SDA_LOW;
}
delay_us(5);
SCL_HIGH;
delay_us(5);
SCL_LOW;
data <<= 1; /* 将下一位移至最高位 */
}
SCL_HIGH; /* 发送完成,释放数据线*/
}
添加文件my_i2c.h
#include "main.h"
void delay_us(uint32_t us);
void SDA_GPIO_PB7_INIT(uint32_t mode);
void IIC_start(void);
void IIC_stop(void);
uint8_t IIC_wait_ack(void);
void IIC_send_byte(uint8_t data);
添加文件oled.c
#include "my_i2c.h"
#include <string.h>
// OLED的字符构造点阵
unsigned char oledFont[] =
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0
0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x30, 0x00, 0x00, 0x00, //! 1
0x00, 0x10, 0x0C, 0x06, 0x10, 0x0C, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //" 2
0x40, 0xC0, 0x78, 0x40, 0xC0, 0x78, 0x40, 0x00, 0x04, 0x3F, 0x04, 0x04, 0x3F, 0x04, 0x04, 0x00, // # 3
0x00, 0x70, 0x88, 0xFC, 0x08, 0x30, 0x00, 0x00, 0x00, 0x18, 0x20, 0xFF, 0x21, 0x1E, 0x00, 0x00, //$ 4
0xF0, 0x08, 0xF0, 0x00, 0xE0, 0x18, 0x00, 0x00, 0x00, 0x21, 0x1C, 0x03, 0x1E, 0x21, 0x1E, 0x00, //% 5
0x00, 0xF0, 0x08, 0x88, 0x70, 0x00, 0x00, 0x00, 0x1E, 0x21, 0x23, 0x24, 0x19, 0x27, 0x21, 0x10, //& 6
0x10, 0x16, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //' 7
0x00, 0x00, 0x00, 0xE0, 0x18, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00, 0x07, 0x18, 0x20, 0x40, 0x00, //( 8
0x00, 0x02, 0x04, 0x18, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x40, 0x20, 0x18, 0x07, 0x00, 0x00, 0x00, //) 9
0x40, 0x40, 0x80, 0xF0, 0x80, 0x40, 0x40, 0x00, 0x02, 0x02, 0x01, 0x0F, 0x01, 0x02, 0x02, 0x00, //* 10
0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x1F, 0x01, 0x01, 0x01, 0x00, //+ 11
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xB0, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, //, 12
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, //- 13
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, //. 14
0x00, 0x00, 0x00, 0x00, 0x80, 0x60, 0x18, 0x04, 0x00, 0x60, 0x18, 0x06, 0x01, 0x00, 0x00, 0x00, /// 15
0x00, 0xE0, 0x10, 0x08, 0x08, 0x10, 0xE0, 0x00, 0x00, 0x0F, 0x10, 0x20, 0x20, 0x10, 0x0F, 0x00, // 0 16
0x00, 0x10, 0x10, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00, // 1 17
0x00, 0x70, 0x08, 0x08, 0x08, 0x88, 0x70, 0x00, 0x00, 0x30, 0x28, 0x24, 0x22, 0x21, 0x30, 0x00, // 2 18
0x00, 0x30, 0x08, 0x88, 0x88, 0x48, 0x30, 0x00, 0x00, 0x18, 0x20, 0x20, 0x20, 0x11, 0x0E, 0x00, // 3 19
0x00, 0x00, 0xC0, 0x20, 0x10, 0xF8, 0x00, 0x00, 0x00, 0x07, 0x04, 0x24, 0x24, 0x3F, 0x24, 0x00, // 4 20
0x00, 0xF8, 0x08, 0x88, 0x88, 0x08, 0x08, 0x00, 0x00, 0x19, 0x21, 0x20, 0x20, 0x11, 0x0E, 0x00, // 5 21
0x00, 0xE0, 0x10, 0x88, 0x88, 0x18, 0x00, 0x00, 0x00, 0x0F, 0x11, 0x20, 0x20, 0x11, 0x0E, 0x00, // 6 22
0x00, 0x38, 0x08, 0x08, 0xC8, 0x38, 0x08, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, // 7 23
0x00, 0x70, 0x88, 0x08, 0x08, 0x88, 0x70, 0x00, 0x00, 0x1C, 0x22, 0x21, 0x21, 0x22, 0x1C, 0x00, // 8 24
0x00, 0xE0, 0x10, 0x08, 0x08, 0x10, 0xE0, 0x00, 0x00, 0x00, 0x31, 0x22, 0x22, 0x11, 0x0F, 0x00, // 9 25
0x00, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, //: 26
0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x60, 0x00, 0x00, 0x00, 0x00, //; 27
0x00, 0x00, 0x80, 0x40, 0x20, 0x10, 0x08, 0x00, 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, //< 28
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, //= 29
0x00, 0x08, 0x10, 0x20, 0x40, 0x80, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, 0x00, //> 30
0x00, 0x70, 0x48, 0x08, 0x08, 0x08, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x30, 0x36, 0x01, 0x00, 0x00, //? 31
0xC0, 0x30, 0xC8, 0x28, 0xE8, 0x10, 0xE0, 0x00, 0x07, 0x18, 0x27, 0x24, 0x23, 0x14, 0x0B, 0x00, //@ 32
0x00, 0x00, 0xC0, 0x38, 0xE0, 0x00, 0x00, 0x00, 0x20, 0x3C, 0x23, 0x02, 0x02, 0x27, 0x38, 0x20, // A 33
0x08, 0xF8, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00, 0x20, 0x3F, 0x20, 0x20, 0x20, 0x11, 0x0E, 0x00, // B 34
0xC0, 0x30, 0x08, 0x08, 0x08, 0x08, 0x38, 0x00, 0x07, 0x18, 0x20, 0x20, 0x20, 0x10, 0x08, 0x00, // C 35
0x08, 0xF8, 0x08, 0x08, 0x08, 0x10, 0xE0, 0x00, 0x20, 0x3F, 0x20, 0x20, 0x20, 0x10, 0x0F, 0x00, // D 36
0x08, 0xF8, 0x88, 0x88, 0xE8, 0x08, 0x10, 0x00, 0x20, 0x3F, 0x20, 0x20, 0x23, 0x20, 0x18, 0x00, // E 37
0x08, 0xF8, 0x88, 0x88, 0xE8, 0x08, 0x10, 0x00, 0x20, 0x3F, 0x20, 0x00, 0x03, 0x00, 0x00, 0x00, // F 38
0xC0, 0x30, 0x08, 0x08, 0x08, 0x38, 0x00, 0x00, 0x07, 0x18, 0x20, 0x20, 0x22, 0x1E, 0x02, 0x00, // G 39
0x08, 0xF8, 0x08, 0x00, 0x00, 0x08, 0xF8, 0x08, 0x20, 0x3F, 0x21, 0x01, 0x01, 0x21, 0x3F, 0x20, // H 40
0x00, 0x08, 0x08, 0xF8, 0x08, 0x08, 0x00, 0x00, 0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00, // I 41
0x00, 0x00, 0x08, 0x08, 0xF8, 0x08, 0x08, 0x00, 0xC0, 0x80, 0x80, 0x80, 0x7F, 0x00, 0x00, 0x00, // J 42
0x08, 0xF8, 0x88, 0xC0, 0x28, 0x18, 0x08, 0x00, 0x20, 0x3F, 0x20, 0x01, 0x26, 0x38, 0x20, 0x00, // K 43
0x08, 0xF8, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x3F, 0x20, 0x20, 0x20, 0x20, 0x30, 0x00, // L 44
0x08, 0xF8, 0xF8, 0x00, 0xF8, 0xF8, 0x08, 0x00, 0x20, 0x3F, 0x00, 0x3F, 0x00, 0x3F, 0x20, 0x00, // M 45
0x08, 0xF8, 0x30, 0xC0, 0x00, 0x08, 0xF8, 0x08, 0x20, 0x3F, 0x20, 0x00, 0x07, 0x18, 0x3F, 0x00, // N 46
0xE0, 0x10, 0x08, 0x08, 0x08, 0x10, 0xE0, 0x00, 0x0F, 0x10, 0x20, 0x20, 0x20, 0x10, 0x0F, 0x00, // O 47
0x08, 0xF8, 0x08, 0x08, 0x08, 0x08, 0xF0, 0x00, 0x20, 0x3F, 0x21, 0x01, 0x01, 0x01, 0x00, 0x00, // P 48
0xE0, 0x10, 0x08, 0x08, 0x08, 0x10, 0xE0, 0x00, 0x0F, 0x18, 0x24, 0x24, 0x38, 0x50, 0x4F, 0x00, // Q 49
0x08, 0xF8, 0x88, 0x88, 0x88, 0x88, 0x70, 0x00, 0x20, 0x3F, 0x20, 0x00, 0x03, 0x0C, 0x30, 0x20, // R 50
0x00, 0x70, 0x88, 0x08, 0x08, 0x08, 0x38, 0x00, 0x00, 0x38, 0x20, 0x21, 0x21, 0x22, 0x1C, 0x00, // S 51
0x18, 0x08, 0x08, 0xF8, 0x08, 0x08, 0x18, 0x00, 0x00, 0x00, 0x20, 0x3F, 0x20, 0x00, 0x00, 0x00, // T 52
0x08, 0xF8, 0x08, 0x00, 0x00, 0x08, 0xF8, 0x08, 0x00, 0x1F, 0x20, 0x20, 0x20, 0x20, 0x1F, 0x00, // U 53
0x08, 0x78, 0x88, 0x00, 0x00, 0xC8, 0x38, 0x08, 0x00, 0x00, 0x07, 0x38, 0x0E, 0x01, 0x00, 0x00, // V 54
0xF8, 0x08, 0x00, 0xF8, 0x00, 0x08, 0xF8, 0x00, 0x03, 0x3C, 0x07, 0x00, 0x07, 0x3C, 0x03, 0x00, // W 55
0x08, 0x18, 0x68, 0x80, 0x80, 0x68, 0x18, 0x08, 0x20, 0x30, 0x2C, 0x03, 0x03, 0x2C, 0x30, 0x20, // X 56
0x08, 0x38, 0xC8, 0x00, 0xC8, 0x38, 0x08, 0x00, 0x00, 0x00, 0x20, 0x3F, 0x20, 0x00, 0x00, 0x00, // Y 57
0x10, 0x08, 0x08, 0x08, 0xC8, 0x38, 0x08, 0x00, 0x20, 0x38, 0x26, 0x21, 0x20, 0x20, 0x18, 0x00, // Z 58
0x00, 0x00, 0x00, 0xFE, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x40, 0x40, 0x40, 0x00, //[ 59
0x00, 0x0C, 0x30, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x38, 0xC0, 0x00, //\ 60
0x00, 0x02, 0x02, 0x02, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0x7F, 0x00, 0x00, 0x00, //] 61
0x00, 0x00, 0x04, 0x02, 0x02, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //^ 62
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, //_ 63
0x00, 0x02, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //` 64
0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x19, 0x24, 0x22, 0x22, 0x22, 0x3F, 0x20, // a 65
0x08, 0xF8, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x11, 0x20, 0x20, 0x11, 0x0E, 0x00, // b 66
0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x0E, 0x11, 0x20, 0x20, 0x20, 0x11, 0x00, // c 67
0x00, 0x00, 0x00, 0x80, 0x80, 0x88, 0xF8, 0x00, 0x00, 0x0E, 0x11, 0x20, 0x20, 0x10, 0x3F, 0x20, // d 68
0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x1F, 0x22, 0x22, 0x22, 0x22, 0x13, 0x00, // e 69
0x00, 0x80, 0x80, 0xF0, 0x88, 0x88, 0x88, 0x18, 0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00, // f 70
0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x6B, 0x94, 0x94, 0x94, 0x93, 0x60, 0x00, // g 71
0x08, 0xF8, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x20, 0x3F, 0x21, 0x00, 0x00, 0x20, 0x3F, 0x20, // h 72
0x00, 0x80, 0x98, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00, // i 73
0x00, 0x00, 0x00, 0x80, 0x98, 0x98, 0x00, 0x00, 0x00, 0xC0, 0x80, 0x80, 0x80, 0x7F, 0x00, 0x00, // j 74
0x08, 0xF8, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0x20, 0x3F, 0x24, 0x02, 0x2D, 0x30, 0x20, 0x00, // k 75
0x00, 0x08, 0x08, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00, // l 76
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x3F, 0x20, 0x00, 0x3F, 0x20, 0x00, 0x3F, // m 77
0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x20, 0x3F, 0x21, 0x00, 0x00, 0x20, 0x3F, 0x20, // n 78
0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x1F, 0x20, 0x20, 0x20, 0x20, 0x1F, 0x00, // o 79
0x80, 0x80, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xA1, 0x20, 0x20, 0x11, 0x0E, 0x00, // p 80
0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x0E, 0x11, 0x20, 0x20, 0xA0, 0xFF, 0x80, // q 81
0x80, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x00, 0x20, 0x20, 0x3F, 0x21, 0x20, 0x00, 0x01, 0x00, // r 82
0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x33, 0x24, 0x24, 0x24, 0x24, 0x19, 0x00, // s 83
0x00, 0x80, 0x80, 0xE0, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x20, 0x20, 0x00, 0x00, // t 84
0x80, 0x80, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x1F, 0x20, 0x20, 0x20, 0x10, 0x3F, 0x20, // u 85
0x80, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0x01, 0x0E, 0x30, 0x08, 0x06, 0x01, 0x00, // v 86
0x80, 0x80, 0x00, 0x80, 0x00, 0x80, 0x80, 0x80, 0x0F, 0x30, 0x0C, 0x03, 0x0C, 0x30, 0x0F, 0x00, // w 87
0x00, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x20, 0x31, 0x2E, 0x0E, 0x31, 0x20, 0x00, // x 88
0x80, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x81, 0x8E, 0x70, 0x18, 0x06, 0x01, 0x00, // y 89
0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x21, 0x30, 0x2C, 0x22, 0x21, 0x30, 0x00, // z 90
0x00, 0x00, 0x00, 0x00, 0x80, 0x7C, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x40, 0x40, //{ 91
0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, //| 92
0x00, 0x02, 0x02, 0x7C, 0x80, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x3F, 0x00, 0x00, 0x00, 0x00, //} 93
0x00, 0x06, 0x01, 0x01, 0x02, 0x02, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //~ 94
};
/* OLED写入一条指令 */
void oledWriteCmd(char writeCmd)
{
IIC_start();
IIC_send_byte(0x78); // 选择一个OLED屏,写模式
IIC_wait_ack();
IIC_send_byte(0x00); // 写入命令,D/C位为0
IIC_wait_ack();
IIC_send_byte(writeCmd);
IIC_wait_ack();
IIC_stop();
}
/* OLED写入一个数据 */
void oledWriteData(char writeData)
{
IIC_start();
IIC_send_byte(0x78); // 选择一个OLED屏,写模式
IIC_wait_ack();
IIC_send_byte(0x40); // 写入命令,D/C位为1
IIC_wait_ack();
IIC_send_byte(writeData);
IIC_wait_ack();
IIC_stop();
}
// OLCD初始化
void oledInit()
{
oledWriteCmd(0xAE);
oledWriteCmd(0x00);
oledWriteCmd(0x10);
oledWriteCmd(0x40);
oledWriteCmd(0xB0);
oledWriteCmd(0x81);
oledWriteCmd(0xFF);
oledWriteCmd(0xA1);
oledWriteCmd(0xA6);
oledWriteCmd(0xA8);
oledWriteCmd(0x3F);
oledWriteCmd(0xC8);
oledWriteCmd(0xD3);
oledWriteCmd(0x00);
oledWriteCmd(0xD5);
oledWriteCmd(0x80);
oledWriteCmd(0xD8);
oledWriteCmd(0x05);
oledWriteCmd(0xD9);
oledWriteCmd(0xF1);
oledWriteCmd(0xDA);
oledWriteCmd(0x12);
oledWriteCmd(0xDB);
oledWriteCmd(0x30);
oledWriteCmd(0x8D);
oledWriteCmd(0x14);
oledWriteCmd(0xAF);
}
// OLED全屏清屏
void oledClean()
{
int i, j;
for (i = 0; i < 8; i++)
{
oledWriteCmd(0xB0 + i); // 选择PAGE
// 选择PAGE的第0列开始显示
oledWriteCmd(0x00);
oledWriteCmd(0x10);
for (j = 0; j < 128; j++)
{
oledWriteData(0); // 写入字符0
}
}
}
// OLED行清屏
void oled_rowClean(char rows)
{
unsigned char i, j;
for (i = 0; i < 2; i++)
{
oledWriteCmd(0xB0 + (rows * 2 - (2 - i))); // 选择PAGE
// 选择PAGE的第0列开始显示
oledWriteCmd(0x00);
oledWriteCmd(0x10);
for (j = 0; j < 128; j++)
{
oledWriteData(0); // 写入字符0
}
}
}
// OLED显示一个字符
void oledShowByte(char rows, char columns, char oledByte)
{
unsigned int i;
// 显示字符的上半部分
oledWriteCmd(0xb0 + (rows * 2 - 2)); // 选择行
// 选择列
oledWriteCmd(0x00 + (columns & 0x0f));
oledWriteCmd(0x10 + (columns >> 4));
// 显示数据
for (i = ((oledByte - 32) * 16); i < ((oledByte - 32) * 16 + 8); i++)
{
oledWriteData(oledFont[i]);
}
// 显示字符的上半部分
oledWriteCmd(0xb0 + (rows * 2 - 1)); // 选择行
// 选择列
oledWriteCmd(0x00 + (columns & 0x0f));
oledWriteCmd(0x10 + (columns >> 4));
// 显示数据
for (i = ((oledByte - 32) * 16 + 8); i < ((oledByte - 32) * 16 + 8 + 8); i++)
{
oledWriteData(oledFont[i]);
}
}
// OLED显示一个字符串
void oledShowString(char rows, char columns, char *str)
{
while (*str != '\0')
{
oledShowByte(rows, columns, *str);
str++;
columns += 8;
}
}
添加文件oled.h
#include "main.h"
// OLED写入一条指令
void oledWriteCmd(uint8_t writeCmd);
// OLED写入一个数据
void oledWriteData(uint8_t writeData);
// OLCD初始化
void oledInit(void);
// OLED清屏
void oledClean(void);
// OLED行清屏
void oled_rowClean(char rows);
// OLED显示一个字符
void oledShowByte(char rows, char columns, char oledByte);
// OLED显示一个字符串
void oledShowString(char rows, char columns, char *str);
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 "tim.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <string.h>
#include "car.h"
#include "oled.h"
uint8_t speed_cnt = 0;
uint8_t speedMsg[24] = {0};
/* 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 */
/* PB13引脚中断的回调函数 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if (GPIO_Pin == GPIO_PIN_13)
{ /* 如果是PB13引脚产生的中断 */
// HAL_Delay(50); /* 按键防抖,这个delay函数不知道为什么引起OLED显示失败 */
if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_13) == GPIO_PIN_RESET)
{
speed_cnt++;
}
}
}
/* 定时器3中断的回调函数 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
// 判断是否是定时器3产生的中断
if (htim3.Instance == TIM3)
{
if (speed_cnt < 10)
{
sprintf((char *)speedMsg, "speed: %d cm/s ", speed_cnt);
}
else
{
sprintf((char *)speedMsg, "speed: %d cm/s ", speed_cnt);
}
printf("%s\r\n", speedMsg);
oledShowString(3, 1, (char *)speedMsg);
speed_cnt = 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_USART1_UART_Init();
MX_TIM2_Init();
MX_TIM3_Init();
/* USER CODE BEGIN 2 */
oledInit(); // OLED初始化
oledClean(); /* 清屏函数 */
// 设置寻址模式
oledWriteCmd(0x20); // 设置内存
oledWriteCmd(0x02); // 选择页寻址模式
oledShowString(1, 1, "diedie_car");
oledShowString(2, 1, "move: stop");
oledShowString(4, 1, "mode: bizhang");
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
usart1_receive_data_handle(); /* 对串口读取的数据进行处理 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* 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 */
摇头避障小车
利用sg90舵机转动不同的角度配合超声波获取小车前方的障碍物的距离来达到避障的效果。
硬件组成
超声波模块HC-SR04
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-16.png)
-
发送超声波:当Trig引脚接收到一个10微秒以上的高电平后开始发送超声波,当开始发送超声波后,Echo引脚会从低电平跳转到高电平。
-
接收超声波:当发出去的超声波返回来并被接收后,Echo引脚会从高电平跳转到低电平。
-
超声波从发出到被接收的时间:Echo持续高电平的时间,当超声波发出去的瞬间启动定时器,超声波被接收的瞬间停止定时器,获取中间经过的时间。
-
测距:距离 = 声音速度(340m/s)* 时间 / 2,除以2是因为超声波经过了两倍距离。
sg90舵机
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-17.png)
驱动sg90舵机时,PWM信号的频率不能太高,大概为50HZ,即设置一个周期为20ms左右的PWM。
-
周期内0.5ms高电平 --> 舵机转动45°,即占空比为2.5%。
-
周期内1.0ms高电平 --> 舵机转动90°,即占空比为5.0%。
-
周期内1.5ms高电平 --> 舵机转动135°,即占空比为7.5%。
-
周期内2.0ms高电平 --> 舵机转动45°,即占空比为10.0%。
-
周期内2.5ms高电平 --> 舵机转动180°,即占空比为12.5%。
模块与STM32F103板子接线
-
超声波Trig引脚 <-> PB3
-
超声波Echo引脚 <-> PB4
-
舵机PWM信号引脚 <-> PB9
STM32CubeMX相关配置
配置GPIO
-
配置PB3引脚为输出高电平。
-
配置PB4引脚为输入引脚。
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-18.png)
配置定时器1和定时器4
配置定时器1作为计数器,PSC值为71,ARR值为65535,这样定时器每计数一下就经过1us。
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-19.png)
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-20.png)
配置定时器4的通道4输出PWM,用来驱动舵机转动不同的角度。
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-21.png)
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-22.png)
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-23.png)
文件编写
修改文件tim.c
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file tim.c
* @brief This file provides code for the configuration
* of the TIM instances.
******************************************************************************
* @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 "tim.h"
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
TIM_HandleTypeDef htim1;
TIM_HandleTypeDef htim2;
TIM_HandleTypeDef htim3;
TIM_HandleTypeDef htim4;
/* TIM1 init function */
void MX_TIM1_Init(void)
{
/* USER CODE BEGIN TIM1_Init 0 */
/* USER CODE END TIM1_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
/* USER CODE BEGIN TIM1_Init 1 */
/* USER CODE END TIM1_Init 1 */
htim1.Instance = TIM1;
htim1.Init.Prescaler = 71;
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 65535;
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = 0;
htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM1_Init 2 */
/* USER CODE END TIM1_Init 2 */
}
/* TIM2 init function */
void MX_TIM2_Init(void)
{
/* USER CODE BEGIN TIM2_Init 0 */
/* USER CODE END TIM2_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
/* USER CODE BEGIN TIM2_Init 1 */
/* USER CODE END TIM2_Init 1 */
htim2.Instance = TIM2;
htim2.Init.Prescaler = 71;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 19;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_PWM_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_LOW;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM2_Init 2 */
/* 启动B-2A、A-1B引脚的PWM */
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2);
/* USER CODE END TIM2_Init 2 */
HAL_TIM_MspPostInit(&htim2);
}
/* TIM3 init function */
void MX_TIM3_Init(void)
{
/* USER CODE BEGIN TIM3_Init 0 */
/* USER CODE END TIM3_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
/* USER CODE BEGIN TIM3_Init 1 */
/* USER CODE END TIM3_Init 1 */
htim3.Instance = TIM3;
htim3.Init.Prescaler = 7199;
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 9999;
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM3_Init 2 */
HAL_TIM_Base_Start_IT(&htim3);
/* USER CODE END TIM3_Init 2 */
}
/* TIM4 init function */
void MX_TIM4_Init(void)
{
/* USER CODE BEGIN TIM4_Init 0 */
/* USER CODE END TIM4_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
/* USER CODE BEGIN TIM4_Init 1 */
/* USER CODE END TIM4_Init 1 */
htim4.Instance = TIM4;
htim4.Init.Prescaler = 7199;
htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
htim4.Init.Period = 199;
htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_Base_Init(&htim4) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim4, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_PWM_Init(&htim4) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL_4) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM4_Init 2 */
/* 启动sg90引脚的PWM */
HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_4);
/* USER CODE END TIM4_Init 2 */
HAL_TIM_MspPostInit(&htim4);
}
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *tim_baseHandle)
{
if (tim_baseHandle->Instance == TIM1)
{
/* USER CODE BEGIN TIM1_MspInit 0 */
/* USER CODE END TIM1_MspInit 0 */
/* TIM1 clock enable */
__HAL_RCC_TIM1_CLK_ENABLE();
/* USER CODE BEGIN TIM1_MspInit 1 */
/* USER CODE END TIM1_MspInit 1 */
}
else if (tim_baseHandle->Instance == TIM2)
{
/* USER CODE BEGIN TIM2_MspInit 0 */
/* USER CODE END TIM2_MspInit 0 */
/* TIM2 clock enable */
__HAL_RCC_TIM2_CLK_ENABLE();
/* USER CODE BEGIN TIM2_MspInit 1 */
/* USER CODE END TIM2_MspInit 1 */
}
else if (tim_baseHandle->Instance == TIM3)
{
/* USER CODE BEGIN TIM3_MspInit 0 */
/* USER CODE END TIM3_MspInit 0 */
/* TIM3 clock enable */
__HAL_RCC_TIM3_CLK_ENABLE();
/* TIM3 interrupt Init */
HAL_NVIC_SetPriority(TIM3_IRQn, 4, 0);
HAL_NVIC_EnableIRQ(TIM3_IRQn);
/* USER CODE BEGIN TIM3_MspInit 1 */
/* USER CODE END TIM3_MspInit 1 */
}
else if (tim_baseHandle->Instance == TIM4)
{
/* USER CODE BEGIN TIM4_MspInit 0 */
/* USER CODE END TIM4_MspInit 0 */
/* TIM4 clock enable */
__HAL_RCC_TIM4_CLK_ENABLE();
/* USER CODE BEGIN TIM4_MspInit 1 */
/* USER CODE END TIM4_MspInit 1 */
}
}
void HAL_TIM_MspPostInit(TIM_HandleTypeDef *timHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if (timHandle->Instance == TIM2)
{
/* USER CODE BEGIN TIM2_MspPostInit 0 */
/* USER CODE END TIM2_MspPostInit 0 */
__HAL_RCC_GPIOA_CLK_ENABLE();
/**TIM2 GPIO Configuration
PA0-WKUP ------> TIM2_CH1
PA1 ------> TIM2_CH2
*/
GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USER CODE BEGIN TIM2_MspPostInit 1 */
/* USER CODE END TIM2_MspPostInit 1 */
}
else if (timHandle->Instance == TIM4)
{
/* USER CODE BEGIN TIM4_MspPostInit 0 */
/* USER CODE END TIM4_MspPostInit 0 */
__HAL_RCC_GPIOB_CLK_ENABLE();
/**TIM4 GPIO Configuration
PB9 ------> TIM4_CH4
*/
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* USER CODE BEGIN TIM4_MspPostInit 1 */
/* USER CODE END TIM4_MspPostInit 1 */
}
}
void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef *tim_baseHandle)
{
if (tim_baseHandle->Instance == TIM1)
{
/* USER CODE BEGIN TIM1_MspDeInit 0 */
/* USER CODE END TIM1_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_TIM1_CLK_DISABLE();
/* USER CODE BEGIN TIM1_MspDeInit 1 */
/* USER CODE END TIM1_MspDeInit 1 */
}
else if (tim_baseHandle->Instance == TIM2)
{
/* USER CODE BEGIN TIM2_MspDeInit 0 */
/* USER CODE END TIM2_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_TIM2_CLK_DISABLE();
/* USER CODE BEGIN TIM2_MspDeInit 1 */
/* USER CODE END TIM2_MspDeInit 1 */
}
else if (tim_baseHandle->Instance == TIM3)
{
/* USER CODE BEGIN TIM3_MspDeInit 0 */
/* USER CODE END TIM3_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_TIM3_CLK_DISABLE();
/* TIM3 interrupt Deinit */
HAL_NVIC_DisableIRQ(TIM3_IRQn);
/* USER CODE BEGIN TIM3_MspDeInit 1 */
/* USER CODE END TIM3_MspDeInit 1 */
}
else if (tim_baseHandle->Instance == TIM4)
{
/* USER CODE BEGIN TIM4_MspDeInit 0 */
/* USER CODE END TIM4_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_TIM4_CLK_DISABLE();
/* USER CODE BEGIN TIM4_MspDeInit 1 */
/* USER CODE END TIM4_MspDeInit 1 */
}
}
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
添加文件hc_sr04.c
#include "tim.h"
#include "gpio.h"
#include "my_i2c.h"
/* 定时器1 */
// 获取超声波的距离
double getDistance()
{
int cnt = 0;
// 给Trig端口至少10us的高电平
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_SET);
delay_us(15);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_RESET);
__HAL_TIM_SetCounter(&htim1, 0); // 将定时器1的计数值设置为0
// echo端口从低电平跳到高电平,开始发波,启动定时器1
while (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_4) == GPIO_PIN_RESET);
__HAL_TIM_ENABLE(&htim1); // 启动定时器1
// echo端口从高电平跳到低电平,停止发波,关闭定时器1
while (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_4) == GPIO_PIN_SET);
__HAL_TIM_DISABLE(&htim1); // 关闭定时器1
// 计算时间
cnt = __HAL_TIM_GetCounter(&htim1); // 读取定时器1的计数值
// 计算距离
return cnt * 340 / 2 * 0.000001 * 100;
}
添加文件hc_sr04.h
//获取超声波的距离
double getDistance(void);
添加文件sg90.c
#include "sg90.h"
#include "main.h"
#include "tim.h"
#define LEFT 1
#define MIDDLE 2
#define RIGHT 3
uint8_t flag = LEFT;
void sg90Left()
{
if (flag != LEFT)
{
flag = LEFT;
__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_4, 25); // 设置舵机转动角度为180°
}
}
void sg90Middle()
{
if (flag != MIDDLE)
{
flag = MIDDLE;
__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_4, 15); // 设置舵机转动角度为90°
}
}
void sg90Right()
{
if (flag != RIGHT)
{
flag = RIGHT;
__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_4, 5); // 设置舵机转动角度为0°
}
}
添加文件sg90.h
void sg90Left(void);
void sg90Middle(void);
void sg90Right(void);
添加文件bizhang.c
#include "tim.h"
#include "car.h"
#include "sg90.h"
#include "hc_sr04.h"
extern double leftDistance;
extern double rightDistance;
extern double middleDistance;
/* 避障模式 */
void bizhang_mode()
{
sg90Middle();
HAL_Delay(400);
middleDistance = getDistance();
if (middleDistance > 35)
{
car_goForward();
}
else
{
car_goStop();
HAL_Delay(50);
sg90Left();
HAL_Delay(400);
leftDistance = getDistance();
sg90Middle();
HAL_Delay(400);
sg90Right();
HAL_Delay(400);
rightDistance = getDistance();
if (leftDistance < 15 && rightDistance < 15)
{
car_goBack();
HAL_Delay(500);
car_goStop();
HAL_Delay(50);
}
else
{
if (leftDistance > rightDistance)
{
car_goLeft();
HAL_Delay(500);
car_goStop();
HAL_Delay(50);
}
if (rightDistance > leftDistance)
{
car_goRight();
HAL_Delay(500);
car_goStop();
HAL_Delay(50);
}
}
}
}
添加文件bizhang.h
/* 避障模式 */
void bizhang_mode(void);
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 "tim.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <string.h>
#include "car.h"
#include "oled.h"
#include "sg90.h"
#include "hc_sr04.h"
#include "my_i2c.h"
#include "bizhang.h"
uint8_t speed_cnt = 0;
uint8_t speedMsg[24] = {0};
double leftDistance = 0;
double rightDistance = 0;
double middleDistance = 0;
/* 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 */
/* 引脚中断的回调函数 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if (GPIO_Pin == GPIO_PIN_13)
{ /* 如果是PB13引脚产生的中断 */
// HAL_Delay(50); /* 按键防抖,这个delay函数不知道为什么引起OLED显示失败 */
if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_13) == GPIO_PIN_RESET)
{
speed_cnt++;
}
}
}
/* 定时器中断的回调函数 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
// 判断是否是定时器3产生的中断
if (htim3.Instance == TIM3)
{
if (speed_cnt < 10)
{
sprintf((char *)speedMsg, "speed: %d cm/s ", speed_cnt);
}
else
{
sprintf((char *)speedMsg, "speed: %d cm/s ", speed_cnt);
}
// printf("%s\r\n",speedMsg);
oledShowString(3, 1, (char *)speedMsg);
speed_cnt = 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_USART1_UART_Init();
MX_TIM2_Init();
MX_TIM3_Init();
MX_TIM1_Init();
MX_TIM4_Init();
/* USER CODE BEGIN 2 */
oledInit(); // OLED初始化
oledClean(); /* 清屏函数 */
// 设置寻址模式
oledWriteCmd(0x20); // 设置内存
oledWriteCmd(0x02); // 选择页寻址模式
oledShowString(1, 1, "diedie_car");
oledShowString(2, 1, "move: stop");
oledShowString(4, 1, "mode: bizhang");
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
usart1_receive_data_handle(); /* 对串口读取的数据进行处理 */
bizhang_mode();
;
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* 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 */
跟随小车
硬件组成
红外避障模块
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-24.png)
-
当发射出的红外线没有被反射回来或被反射回来但强度不够大,DO输出高电平,灯灭。没有障碍物。
-
当发射出的红外线被反射回来,DO输出低电平,灯亮。有障碍物。
-
即有障碍物DO输出低电平,红外避障模块灯亮,没有障碍物DO输出高电平,红外避障模块灯灭。
模块与STM32F103板子接线
-
左红外避障模块 <-> PA4
-
右红外避障模块 <-> PA5
STM32CubeMX相关配置
GPIO配置
配置PA4引脚、PA5引脚为输入引脚。
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-25.png)
文件编写
添加文件gensui.c
#include "gpio.h"
#include "car.h"
#include "oled.h"
/* PA4:左红外避障模块,PA5:右红外避障模块 */
void followMode()
{
oledShowString(4, 1, "mode: gensui");
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_4) == GPIO_PIN_RESET && HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5) == GPIO_PIN_RESET)
{ //
car_goForward();
}
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_4) == GPIO_PIN_RESET && HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5) == GPIO_PIN_SET)
{
car_goLeft();
}
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_4) == GPIO_PIN_SET && HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5) == GPIO_PIN_RESET)
{
car_goRight();
}
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_4) == GPIO_PIN_SET && HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5) == GPIO_PIN_SET)
{
car_goStop();
}
}
添加文件gensui.h
void followMode(void);
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 "tim.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <string.h>
#include "car.h"
#include "oled.h"
#include "sg90.h"
#include "hc-08.h"
#include "my_i2c.h"
#include "bizhang.h"
#include "gensui.h"
uint8_t speed_cnt = 0;
uint8_t speedMsg[24] = {0};
double leftDistance = 0;
double rightDistance = 0;
double middleDistance = 0;
/* 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 */
/* 引脚中断的回调函数 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if (GPIO_Pin == GPIO_PIN_13)
{ /* 如果是PB13引脚产生的中断 */
// HAL_Delay(50); /* 按键防抖,这个delay函数不知道为什么引起OLED显示失败 */
if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_13) == GPIO_PIN_RESET)
{
speed_cnt++;
}
}
}
/* 定时器中断的回调函数 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
// 判断是否是定时器3产生的中断
if (htim3.Instance == TIM3)
{
memset(speedMsg, 0, 24);
sprintf((char *)speedMsg, "speed:%d cm/s", speed_cnt);
printf("%s\r\n", speedMsg);
oledShowString(3, 1, (char *)speedMsg);
speed_cnt = 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_USART1_UART_Init();
MX_TIM2_Init();
MX_TIM3_Init();
MX_TIM1_Init();
MX_TIM4_Init();
/* USER CODE BEGIN 2 */
oledInit(); // OLED初始化
oledClean(); /* 清屏函数 */
// 设置寻址模式
oledWriteCmd(0x20); // 设置内存
oledWriteCmd(0x02); // 选择页寻址模式
oledShowString(1, 1, "diedie_car");
oledShowString(2, 1, "move: stop");
oledShowString(4, 1, "mode: gensui");
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
// printf("jiangxiaoya\r\n");
// HAL_Delay(1000);
// followMode();
usart1_receive_data_handle(); /* 对串口读取的数据进行处理 */
// bizhang_mode();
// printf("middle:%f\r\n",middleDistance);
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* 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 */
循迹小车
硬件组成
循迹模块(TCRT5000传感器)
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-26.png)
-
当发射出的红外线没有被反射回来或被反射回来但强度不够大时,DO输出高电平,灯灭。 黑色吸收红外线,DO输出高电平,灯亮。
-
当发射出的红外线被反射回来或被反射回来且强度足够大,DO输出低电平,灯亮。 白色反射红外线,DO输出低电平,灯亮。
-
即黑色DO输出高电平,模块灯灭,白色DO输出低电平,模块灯亮。
模块与STM32F103板子接线
-
左循迹模块 <-> PA6
-
右循迹模块 <-> PA7
STM32CubeMX相关配置
GPIO配置
配置PA6引脚、PA7引脚为输入引脚。
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-27.png)
文件编写
添加文件xunji.c
#include "gpio.h"
#include "car.h"
/* PA6:左红外循迹模块,PA7:右红外循迹模块 */
void tracingMode()
{
// oledShowString(4, 1, "mode: gensui");
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_6) == GPIO_PIN_RESET && HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_7) == GPIO_PIN_RESET)
{ //
car_goForward();
}
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_6) == GPIO_PIN_RESET && HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_7) == GPIO_PIN_SET)
{
car_goLeft();
}
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_6) == GPIO_PIN_SET && HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_7) == GPIO_PIN_RESET)
{
car_goRight();
}
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_6) == GPIO_PIN_SET && HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_7) == GPIO_PIN_SET)
{
car_goStop();
}
}
添加文件xunji.h
void tracingMode(void);
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 "tim.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <string.h>
#include "car.h"
#include "oled.h"
#include "sg90.h"
#include "hc_sr04.h"
#include "my_i2c.h"
#include "bizhang.h"
#include "gensui.h"
#include "xunji.h"
uint8_t speed_cnt = 0;
uint8_t speedMsg[24] = {0};
double leftDistance = 0;
double rightDistance = 0;
double middleDistance = 0;
/* 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 */
/* 引脚中断的回调函数 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if (GPIO_Pin == GPIO_PIN_13)
{ /* 如果是PB13引脚产生的中断 */
// HAL_Delay(50); /* 按键防抖,这个delay函数不知道为什么引起OLED显示失败 */
if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_13) == GPIO_PIN_RESET)
{
speed_cnt++;
}
}
}
/* 定时器中断的回调函数 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
// 判断是否是定时器3产生的中断
if (htim3.Instance == TIM3)
{
if (speed_cnt < 10)
{
sprintf((char *)speedMsg, "speed: %d cm/s ", speed_cnt);
}
else
{
sprintf((char *)speedMsg, "speed: %d cm/s ", speed_cnt);
}
// printf("%s\r\n",speedMsg);
oledShowString(3, 1, (char *)speedMsg);
speed_cnt = 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_USART1_UART_Init();
MX_TIM2_Init();
MX_TIM3_Init();
MX_TIM1_Init();
MX_TIM4_Init();
/* USER CODE BEGIN 2 */
oledInit(); // OLED初始化
oledClean(); /* 清屏函数 */
// 设置寻址模式
oledWriteCmd(0x20); // 设置内存
oledWriteCmd(0x02); // 选择页寻址模式
oledShowString(1, 1, "diedie_car");
oledShowString(2, 1, "move: stop");
oledShowString(4, 1, "mode: xunji");
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
tracingMode();
usart1_receive_data_handle(); /* 对串口读取的数据进行处理 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* 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 */
语音控制小车,循迹、跟随、避障三种功能切换
硬件组成
语音模块SU-03T
可以通过智能公元平台配置语音词条。
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-28.png)
智能公元产品使用方法
-
创建产品
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-29.png)
-
选择对应的模块
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-30.png)
-
配置相应的信息
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-31.png)
-
根据需要将某些引脚配置成串口或GPIO口
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-32.png)
-
添加SU-03T语音模块的唤醒词
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-33.png)
-
添加命令词
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-34.png)
-
为某个命令词添加控制,通过某个引脚输出电平或者通过串口输出信息
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-35.png)
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-36.png)
-
配置其他信息
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-37.png)
-
生成SDK
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-38.png)
-
下载固件压缩包
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-39.png)
-
将固件包解压到没中文路径的文件夹
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-40.png)
-
去智能公元官方下载烧录软件
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-41.png)
-
打开烧录软件,烧录固件
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-42.png)
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-43.png)
-
选择刚刚生成的固件烧录到语音
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-44.png)
![STM32之智能小车,手把手从0到1,模块化编程](https://imgs.yssmx.com/Uploads/2023/04/428407-45.png)
模块与STM32F103板子接线
-
SU-03T的PB6引脚 <-> PA9(USART1_TX引脚)文章来源:https://www.toymoban.com/news/detail-428407.html
-
SU-03T的PB7引脚 <-> PA10(USART1_RX引脚)文章来源地址https://www.toymoban.com/news/detail-428407.html
文件编写
修改usart.c文件
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file usart.c
* @brief This file provides code for the configuration
* of the USART instances.
******************************************************************************
* @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 "usart.h"
/* USER CODE BEGIN 0 */
#include <stdio.h>
#include <string.h>
#include "car.h"
#include "oled.h"
#define USART_REC_LEN 200
extern uint8_t mode_mark;
// 串口接收缓存(1字节)
uint8_t buf = 0;
uint8_t UART1_RX_Buffer[USART_REC_LEN]; // 接收缓冲,串口接收的数据存放地点
// 串口接收状态,16位
uint16_t UART1_RX_STA = 0;
// bit15: 如果是1表示接收完成
// bit14: 如果是1表示接收到回车(0x0d)
// bit13~bit0: 接收到的有效字节数目
/* USER CODE END 0 */
UART_HandleTypeDef huart1;
/* USART1 init function */
void MX_USART1_UART_Init(void)
{
/* USER CODE BEGIN USART1_Init 0 */
/* USER CODE END USART1_Init 0 */
/* USER CODE BEGIN USART1_Init 1 */
/* USER CODE END USART1_Init 1 */
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART1_Init 2 */
/* 开启串口1的接收中断 */
HAL_UART_Receive_IT(&huart1, &buf, 1); /* 每接收一个串口数据调用一次串口接收完成回调函数 */
/* USER CODE END USART1_Init 2 */
}
void HAL_UART_MspInit(UART_HandleTypeDef *uartHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if (uartHandle->Instance == USART1)
{
/* USER CODE BEGIN USART1_MspInit 0 */
/* USER CODE END USART1_MspInit 0 */
/* USART1 clock enable */
__HAL_RCC_USART1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/**USART1 GPIO Configuration
PA9 ------> USART1_TX
PA10 ------> USART1_RX
*/
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USART1 interrupt Init */
HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(USART1_IRQn);
/* USER CODE BEGIN USART1_MspInit 1 */
/* USER CODE END USART1_MspInit 1 */
}
}
void HAL_UART_MspDeInit(UART_HandleTypeDef *uartHandle)
{
if (uartHandle->Instance == USART1)
{
/* USER CODE BEGIN USART1_MspDeInit 0 */
/* USER CODE END USART1_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_USART1_CLK_DISABLE();
/**USART1 GPIO Configuration
PA9 ------> USART1_TX
PA10 ------> USART1_RX
*/
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9 | GPIO_PIN_10);
/* USART1 interrupt Deinit */
HAL_NVIC_DisableIRQ(USART1_IRQn);
/* USER CODE BEGIN USART1_MspDeInit 1 */
HAL_UART_Receive_IT(&huart1, &buf, 1);
/* USER CODE END USART1_MspDeInit 1 */
}
}
/* USER CODE BEGIN 1 */
/* 重写stdio.h文件中的prinft()里的fputc()函数 */
int fputc(int my_data, FILE *p)
{
unsigned char temp = my_data;
// 改写后,使用printf()函数会将数据通过串口一发送出去
HAL_UART_Transmit(&huart1, &temp, 1, 0xffff); // 0xfffff为最大超时时间
return my_data;
}
/* 串口接收完成回调函数 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
// 判断中断是哪个串口触发的
if (huart->Instance == USART1)
{
// 判断接收是否完成,即判断UART1_RX_STA的bit15是否为1
if (!(UART1_RX_STA & 0x8000))
{ // 如果没接收完成就进入接收流程
// 判断是否接收到回车0x0d
if (UART1_RX_STA & 0x4000)
{
// 判断是否接收到换行0x0a
if (buf == 0x0a)
{
// 如果回车和换行都接收到了,则表示接收完成,即把bit15拉高
UART1_RX_STA |= 0x8000;
}
else
{ // 如果接收到回车0x0d没有接收到换行0x0a
// 则认为接收错误,重新开始接收
UART1_RX_STA = 0;
}
}
else
{ // 如果没有接收到回车0x0d
// 则判断收到的这个字符是否是回车0x0d
if (buf == 0x0d)
{
// 如果这个字符是回车,则将将bit14拉高,表示接收到回车
UART1_RX_STA |= 0x4000;
}
else
{ // 如果不是回车
// 则将这个字符存放到缓存数组中
UART1_RX_Buffer[UART1_RX_STA & 0x3ffff] = buf;
UART1_RX_STA++;
// 如果接收数据大于UART1_REC_LEN(200字节),则重新开始接收
if (UART1_RX_STA > USART_REC_LEN - 1)
{
UART1_RX_STA = 0;
}
}
}
}
// 如果接收完成则重新开启串口1的接收中断
HAL_UART_Receive_IT(&huart1, &buf, 1);
}
}
/* 对串口接收数据的处理 */
void usart1_receive_data_handle()
{
/* 判断判断串口是否接收完成 */
if (UART1_RX_STA & 0x8000)
{
printf("接收完成\r\n");
// 串口接收完数据后,对串口数据进行处理
if (!strcmp((const char *)UART1_RX_Buffer, "bizhang"))
{
printf("避障模式\r\n");
mode_mark = 0;
}
else if (!strcmp((const char *)UART1_RX_Buffer, "gensui"))
{
printf("跟随模式\r\n");
mode_mark = 1;
}
else if (!strcmp((const char *)UART1_RX_Buffer, "xunji"))
{
printf("循迹模式\r\n");
mode_mark = 2;
}
// 接收到其他数据,进行报错
else
{
if (UART1_RX_Buffer[0] != '\0')
{
printf("%s\r\n", "输入错误,请重新输入");
}
}
// 换行,重新开始下一次接收
memset(UART1_RX_Buffer, 0, USART_REC_LEN);
// printf("\r\n");
UART1_RX_STA = 0;
}
}
/* USER CODE END 1 */
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 "tim.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <string.h>
#include "car.h"
#include "oled.h"
#include "sg90.h"
#include "hc-08.h"
#include "my_i2c.h"
#include "bizhang.h"
#include "gensui.h"
#include "xunji.h"
#define BZ 0
#define GS 1
#define XJ 2
uint8_t speed_cnt = 0;
uint8_t speedMsg[24] = {0};
uint8_t mode_mark = 0;
uint8_t mark = 0;
double leftDistance = 0;
double rightDistance = 0;
double middleDistance = 0;
/* 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 */
/* 引脚中断的回调函数 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if (GPIO_Pin == GPIO_PIN_13)
{ /* 如果是PB13引脚产生的中断 */
// HAL_Delay(50); /* 按键防抖,这个delay函数不知道为什么引起OLED显示失败 */
if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_13) == GPIO_PIN_RESET)
{
speed_cnt++;
}
}
}
/* 定时器中断的回调函数 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
// 判断是否是定时器3产生的中断
if (htim3.Instance == TIM3)
{
if (speed_cnt < 10)
{
sprintf((char *)speedMsg, "speed: %d cm/s ", speed_cnt);
}
else
{
sprintf((char *)speedMsg, "speed: %d cm/s ", speed_cnt);
}
// printf("%s\r\n",speedMsg);
oledShowString(3, 1, (char *)speedMsg);
speed_cnt = 0;
}
}
void voice_handle()
{
if (mode_mark == 0) /* 避障模式 */
{
if (mark != BZ)
{
oled_rowClean(4);
oledShowString(4, 1, "mode: bizhang");
}
mark = BZ;
avoidMode();
}
else if (mode_mark == 1) /* 跟随模式 */
{
if (mark != GS)
{
oled_rowClean(4);
oledShowString(4, 1, "mode: gensui");
}
mark = GS;
followMode();
}
else if (mode_mark == 2) /* 循迹模式 */
{
if (mark != XJ)
{
oled_rowClean(4);
oledShowString(4, 1, "mode: xunji");
}
mark = XJ;
tracingMode();
}
else
{
oled_rowClean(4);
oledShowString(4, 1, "voice error");
}
}
/* 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_USART1_UART_Init();
MX_TIM2_Init();
MX_TIM3_Init();
MX_TIM1_Init();
MX_TIM4_Init();
/* USER CODE BEGIN 2 */
oledInit(); // OLED初始化
oledClean(); /* 清屏函数 */
// 设置寻址模式
oledWriteCmd(0x20); // 设置内存
oledWriteCmd(0x02); // 选择页寻址模式
oledShowString(1, 1, "diedie_car");
oledShowString(2, 1, "move: stop");
oledShowString(4, 1, "mode: bizhang");
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
voice_handle();
usart1_receive_data_handle(); /* 对串口读取的数据进行处理 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* 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 */
到了这里,关于STM32之智能小车,手把手从0到1,模块化编程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!