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

小车介绍

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

硬件组成

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

模块化编程

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

移动小车

让小车动起来。

硬件组成

L9110S电机模块

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

    配置RCC

    配置GPIO

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

    文件编写

    添加文件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

    配置NVIC

    使用Micro库

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

    文件编写

    修改文件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来驱动。

    硬件组成

    测速模块

  • 发射的红外线被物体遮挡时,输出高电平,发射的红外线没被物体遮挡时,输出低电平。

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

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

  • 模块与STM32F103板子接线

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

  • OLED屏SCL引脚 <-> PB6

  • OLED屏SDA引脚 <-> PB7

  • STM32CubeMX相关配置

    配置GPIO

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

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

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

  • 配置定时器2和定时器3

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

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

    配置NVIC

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

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

  • 文件编写

    修改文件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

  • 发送超声波:当Trig引脚接收到一个10微秒以上的高电平后开始发送超声波,当开始发送超声波后,Echo引脚会从低电平跳转到高电平。

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

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

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

  • sg90舵机

    驱动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引脚为输入引脚。

  • 配置定时器1和定时器4

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

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

    文件编写

    修改文件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 */
    

    跟随小车

    硬件组成

    红外避障模块

  • 当发射出的红外线没有被反射回来或被反射回来但强度不够大,DO输出高电平,灯灭。没有障碍物。

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

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

  • 模块与STM32F103板子接线

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

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

  • STM32CubeMX相关配置

    GPIO配置

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

    文件编写

    添加文件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传感器)

  • 当发射出的红外线没有被反射回来或被反射回来但强度不够大时,DO输出高电平,灯灭。 黑色吸收红外线,DO输出高电平,灯亮。

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

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

  • 模块与STM32F103板子接线

  • 左循迹模块 <-> PA6

  • 右循迹模块 <-> PA7

  • STM32CubeMX相关配置

    GPIO配置

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

    文件编写

    添加文件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

    可以通过智能公元平台配置语音词条。

    智能公元产品使用方法

    1. 创建产品

    1. 选择对应的模块

    1. 配置相应的信息

    1. 根据需要将某些引脚配置成串口或GPIO口

    1. 添加SU-03T语音模块的唤醒词

    1. 添加命令词

    1. 为某个命令词添加控制,通过某个引脚输出电平或者通过串口输出信息

    1. 配置其他信息

    1. 生成SDK

    1. 下载固件压缩包

    1. 将固件包解压到没中文路径的文件夹

    1. 去智能公元官方下载烧录软件

    1. 打开烧录软件,烧录固件

    1. 选择刚刚生成的固件烧录到语音

    模块与STM32F103板子接线

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

  • SU-03T的PB7引脚 <-> PA10(USART1_RX引脚)

  • 文件编写

    修改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 */
    
    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32之智能小车,手把手从0到1,模块化编程

    发表评论