STM32之智能小车,手把手从0到1,模块化编程

这篇具有很好参考价值的文章主要介绍了STM32之智能小车,手把手从0到1,模块化编程。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

小车介绍

本博文将会从0到1实现一个智能小车,该小车实现功能:1. 摇头避障模式、2. 跟随模式、3. 循迹模式、4. 小车测速并显示在OLED屏幕、5. 语音控制小车等等。

硬件组成

STM32F103开发板、小车套件、L9110S电机模块、超声波模块(HC-SR04)、sg90舵机、测速模块、循迹模块、红外避障模块等等(下面有详细介绍)

模块化编程

小车采用模块化编程循序渐进,即每实现新的功能小车需要复制前一种功能小车的文件夹来进行修改,否则会因为丢失某些文件导致小车功能缺失。

移动小车

让小车动起来。

硬件组成

L9110S电机模块

STM32之智能小车,手把手从0到1,模块化编程

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,模块化编程

配置RCC

STM32之智能小车,手把手从0到1,模块化编程
STM32之智能小车,手把手从0到1,模块化编程

配置GPIO

配置PA0引脚、PA1引脚、PB0引脚、PB2引脚输出高电平。

STM32之智能小车,手把手从0到1,模块化编程

文件编写

添加文件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,模块化编程

配置NVIC

STM32之智能小车,手把手从0到1,模块化编程

使用Micro库

只要映射了printf用来发送数据去串口都要使用这个库。

STM32之智能小车,手把手从0到1,模块化编程

文件编写

修改文件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,模块化编程
  • 发射的红外线被物体遮挡时,输出高电平,发射的红外线没被物体遮挡时,输出低电平。

  • 即有物体高电平,没物体低电平。

  • 当搭配小车测速盘,会形成下降沿(有遮挡高电平,没遮挡低电平)。

模块与STM32F103板子接线

  • 测速模块DO引脚 <-> PB13

  • OLED屏SCL引脚 <-> PB6

  • OLED屏SDA引脚 <-> PB7

STM32CubeMX相关配置

配置GPIO

  • 配置PB13引脚为引脚中断。

  • 配置PB6引脚输出高电平。

  • 重置PA0、PA1引脚的状态,防止待会配置定时器2通道输出PWM时映射到其他引脚。

STM32之智能小车,手把手从0到1,模块化编程

配置定时器2和定时器3

配置定时器2的通道1和通道2输出PWM,用来给电机调速。

STM32之智能小车,手把手从0到1,模块化编程
STM32之智能小车,手把手从0到1,模块化编程
STM32之智能小车,手把手从0到1,模块化编程

配置定时器3定时时间为1s。

STM32之智能小车,手把手从0到1,模块化编程

配置NVIC

  • 打开定时器3和PB13引脚的中断。

  • 修改定时器3响应中断优先级为4,PB13引脚中断响应优先级为3。

STM32之智能小车,手把手从0到1,模块化编程

文件编写

修改文件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,模块化编程
  • 发送超声波:当Trig引脚接收到一个10微秒以上的高电平后开始发送超声波,当开始发送超声波后,Echo引脚会从低电平跳转到高电平。

  • 接收超声波:当发出去的超声波返回来并被接收后,Echo引脚会从高电平跳转到低电平。

  • 超声波从发出到被接收的时间:Echo持续高电平的时间,当超声波发出去的瞬间启动定时器,超声波被接收的瞬间停止定时器,获取中间经过的时间。

  • 测距:距离 = 声音速度(340m/s)* 时间 / 2,除以2是因为超声波经过了两倍距离。

sg90舵机

STM32之智能小车,手把手从0到1,模块化编程

驱动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,模块化编程

配置定时器1和定时器4

配置定时器1作为计数器,PSC值为71,ARR值为65535,这样定时器每计数一下就经过1us。

STM32之智能小车,手把手从0到1,模块化编程
STM32之智能小车,手把手从0到1,模块化编程

配置定时器4的通道4输出PWM,用来驱动舵机转动不同的角度。

STM32之智能小车,手把手从0到1,模块化编程
STM32之智能小车,手把手从0到1,模块化编程
STM32之智能小车,手把手从0到1,模块化编程

文件编写

修改文件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,模块化编程
  • 当发射出的红外线没有被反射回来或被反射回来但强度不够大,DO输出高电平,灯灭。没有障碍物。

  • 当发射出的红外线被反射回来,DO输出低电平,灯亮。有障碍物。

  • 即有障碍物DO输出低电平,红外避障模块灯亮,没有障碍物DO输出高电平,红外避障模块灯灭。

模块与STM32F103板子接线

  • 左红外避障模块 <-> PA4

  • 右红外避障模块 <-> PA5

STM32CubeMX相关配置

GPIO配置

配置PA4引脚、PA5引脚为输入引脚。

STM32之智能小车,手把手从0到1,模块化编程

文件编写

添加文件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,模块化编程
  • 当发射出的红外线没有被反射回来或被反射回来但强度不够大时,DO输出高电平,灯灭。 黑色吸收红外线,DO输出高电平,灯亮。

  • 当发射出的红外线被反射回来或被反射回来且强度足够大,DO输出低电平,灯亮。 白色反射红外线,DO输出低电平,灯亮。

  • 即黑色DO输出高电平,模块灯灭,白色DO输出低电平,模块灯亮。

模块与STM32F103板子接线

  • 左循迹模块 <-> PA6

  • 右循迹模块 <-> PA7

STM32CubeMX相关配置

GPIO配置

配置PA6引脚、PA7引脚为输入引脚。

STM32之智能小车,手把手从0到1,模块化编程

文件编写

添加文件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,模块化编程

智能公元产品使用方法

  1. 创建产品

STM32之智能小车,手把手从0到1,模块化编程
  1. 选择对应的模块

STM32之智能小车,手把手从0到1,模块化编程
  1. 配置相应的信息

STM32之智能小车,手把手从0到1,模块化编程
  1. 根据需要将某些引脚配置成串口或GPIO口

STM32之智能小车,手把手从0到1,模块化编程
  1. 添加SU-03T语音模块的唤醒词

STM32之智能小车,手把手从0到1,模块化编程
  1. 添加命令词

STM32之智能小车,手把手从0到1,模块化编程
  1. 为某个命令词添加控制,通过某个引脚输出电平或者通过串口输出信息

STM32之智能小车,手把手从0到1,模块化编程
STM32之智能小车,手把手从0到1,模块化编程
  1. 配置其他信息

STM32之智能小车,手把手从0到1,模块化编程
  1. 生成SDK

STM32之智能小车,手把手从0到1,模块化编程
  1. 下载固件压缩包

STM32之智能小车,手把手从0到1,模块化编程
  1. 将固件包解压到没中文路径的文件夹

STM32之智能小车,手把手从0到1,模块化编程
  1. 去智能公元官方下载烧录软件

STM32之智能小车,手把手从0到1,模块化编程
  1. 打开烧录软件,烧录固件

STM32之智能小车,手把手从0到1,模块化编程
STM32之智能小车,手把手从0到1,模块化编程
  1. 选择刚刚生成的固件烧录到语音

STM32之智能小车,手把手从0到1,模块化编程
STM32之智能小车,手把手从0到1,模块化编程

模块与STM32F103板子接线

  • SU-03T的PB6引脚 <-> PA9(USART1_TX引脚)

  • 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模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • STM32系列——手把手教你蓝牙模块HC05、HC06的使用,重在“用起来”(HAL库)

    不论是HC05还是HC06,我们用到的都是蓝牙模块的透传功能,只需要用到4个引脚:RXD、TXD、VCC(5V)、GND。 1、HC-05有6个引脚,但是我们只用到4个。 2、HC-05正面有一个按键。与HC-06的区别是, 它上电之前必须要按住此按键再插入电脑中,才能够进入AT模式 。HC-06没有按键,直接

    2024年02月05日
    浏览(50)
  • 手把手教你使用--常用模块--HC05蓝牙模块,无线蓝牙串口透传模块,(实例:手机蓝牙控制STM32单片机点亮LED灯)

    最近在学STM32,基本的学完了,想学几个模块来巩固一下知识,就想到了蓝牙模块。玩啥好难过有很多博客教怎么连的,但自己看起来还是有点糊涂。模块的原理和知识点我就不讲解了,这里我主要 手把手 记录一下我是如何对蓝牙模块进行学习和使用的。 所使用的资料和工

    2024年02月02日
    浏览(46)
  • 基于STM32F103RCT6之手把手教你写智能家居项目(2)

            上一节我们简述了智能家居项目,实现了点灯的相关代码编写,还有WIFI模块的固件烧录。 连接什么平台:         我们想要远程控制家具的开关和获取家中的状态,少不了一个可以传输数据的云平台。我认为易监控是一个简单好用的云平台。 怎么连接平台:

    2024年02月20日
    浏览(34)
  • 手把手教你编写跑马灯——STM32

    新建一个文件夹 ,打开KeiL,projiece-----new vision projection  给文件命名(随便取)  根据自己开发板的信号选择对应的型号  在刚才创建的文件夹里面 新建一个main.c文件  双击source group 1,点击main.c,点击add  添加头文件 led.c main.c led就可以全亮然后全灭一直循环

    2024年02月08日
    浏览(30)
  • 手把手教你STM32入门教程(标准库)

    目录 前言 步骤一:准备工作 步骤二:安装Keil MDK-ARM开发工具 步骤三:创建工程 步骤四:配置工程 步骤六:编写代码 步骤七:下载程序   总结 在物联网时代,嵌入式系统的应用越来越广泛,而STM32作为业内领先的嵌入式芯片之一,受到了越来越多开发者的关注和喜爱。然

    2024年02月12日
    浏览(33)
  • STM32手把手点灯学习笔记(大疆robomasterA板)

    建议大家先在b站看正点原子或者野火的stm32视频,本贴仅适用于刚刚接触stm32并且使用大疆robomasterA型开发板的同学,我会分享实现的代码和学习的方法,供大家后续学习A板时可以参考,帖主也是正在学习,欢迎大家一起讨论 此教程建立在你会使用keil5, 虽然说正点原子和野

    2023年04月08日
    浏览(71)
  • 零基础快速上手STM32开发(手把手保姆级教程)

    作为一名嵌入式工程师,STM32 是必须要学习的一款单片机,同时这款单片机资料足够多,而且比较简单,非常适合初学者入门。 STM32 是一款由 STMicroelectronics 公司开发的 32 位微控制器,由于其强大的处理能力和广泛的应用领域,如嵌入式系统、物联网设备、机器人等,而受到

    2024年02月05日
    浏览(36)
  • STM32--GPIO点亮LED灯(手把手,超详细)

    写在前面: 在前面的学习中,我们学习了 STM32的编译环境(MDK)、时钟树以及GPIO的8种工作模式 ;这节我们学习正式入门STM32--- 点亮第一个LED灯 ;即利用GPIO进行电灯,尽管是一个十分简单的实现,但是其步骤也是一个完整的STM32项目,可以说是“麻雀虽小,五脏俱全”;因

    2024年02月03日
    浏览(35)
  • 手把手带你使用VSCode 搭建 STM32开发环境!

    首先附上一张VS Code图一直都喜欢这种,黑色主题感觉高大上。 下载最新版VS Code: 安装好插件,具有良好的代码补全与调试功能。 “ VS Code下载地址:https://code.visualstudio.com/ ” 下载 LLVM:用于代码补全,其实可以理解为 Clang。因为VS Code 中“C/C++”插件的自动补全功能不太好

    2024年02月07日
    浏览(40)
  • 手把手教你开发stm32——系统时钟(基于hal库)

    概念: 时钟系统是由振荡器(信号源)、定时唤醒器、倍频器、分频器等组成的电路。常用的信号源有晶体振荡器和RC振荡器。 意义: 时钟是嵌入式系统的脉搏,处理器啮合在时钟的驱动下完成指令执行,状态转换等等动作,外设部件在时钟的驱动下完成各种工作,比如串

    2024年02月06日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包