STM32智能小车(循迹、跟随、避障、测速、蓝牙、wife、4g、语音识别)总结

STM32智能小车(循迹、跟随、避障、测速、蓝牙、wife、4g、语音识别)总结

码农世界 2024-05-27 前端 74 次浏览 0个评论

目录

1.电机模块开发

1.1 让小车动起来

1.2 串口控制小车方向

1.3 如何进行小车PWM调速

1.4 PWM方式实现小车转向

2.循迹小车 

2.1 循迹模块使用

2.2 循迹小车原理

2.3 循迹小车核心代码

2.4 循迹小车解决转弯平滑问题

3.跟随/避障小车

3.1 红外壁障模块分析​编辑

3.2 跟随小车的原理

3.3 跟随小车开发和调试代码

3.4 超声波模块介绍

3.5 舵机模块介绍

3.6 摇头避障小车开发和调试代码

4.测速小车

4.1 测速模块

4.2 测试原理和单位换算

4.3 定时器和中断实现测速开发和调试代码

4.4 小车速度显示在OLED屏

5.远程控制小车

5.1 蓝牙控制小车

5.2 蓝牙控制并测速小车

5.3 wifi控制测速小车

5.4 4g控制小车

6.语音控制小车

6.1语音模块配置:

6.2 语音控制小车开发和调试代码


1.电机模块开发

L9110s概述

接通VCC,GND 模块电源指示灯亮, 以下资料来源官方,具体根据实际调试

IA1输入高电平,IA1输入低电平,【OA1 OB1】电机正转;

IA1输入低电平,IA1输入高电平,【OA1 OB1】电机反转;

IA2输入高电平,IA2输入低电平,【OA2 OB2】电机正转;

IA2输入低电平,IA2输入高电平,【OA2 OB2】电机反转;

接线参考:

B-1A -- PA0

B-1B -- PB1

A-1A -- PA1

A-1B -- PB10 

1.1 让小车动起来

代码实现:

motor.c

#include "motor.h"
void goForward(void)
{
    // 左轮
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET);
    // 右轮
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);
}
void goBack(void)
{
    // 左轮
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_SET);
    // 右轮
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET);
}
void goLeft(void)
{
    // 左轮
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET);
    // 右轮
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);
}
void goRight(void)
{
    // 左轮
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET);
    // 右轮
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);
}
void stop(void)
{
    // 左轮
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_SET);
    // 右轮
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);
}

motor.h

#ifndef __MOTOR_H__
#define __MOTOR_H__
#include "main.h"
void goForward(void);
void goBack(void);
void goLeft(void);
void goRight(void);
void stop(void);
#endif

main.c

#include "motor.h"
//main函数的while循环部分:
while (1)
{
    goForward();
    HAL_Delay(1000);
    goBack();
    HAL_Delay(1000);
    goLeft();
    HAL_Delay(1000);
    goRight();
    HAL_Delay(1000);
    stop();
    HAL_Delay(1000);
}
1.2 串口控制小车方向
  • 串口分文件编程进行代码整合——通过现象来改代码
  • 接入蓝牙模块,通过蓝牙控制小车
  • 添加点动控制,如果APP支持按下一直发数据,松开就停止发数据(蓝牙调试助手的自定义按键不 能实现),就能实现前进按键按下后小车一直往前走的功能

    代码实现:

    usart.c

    #include "usart.h"
    #include "string.h"
    #include "stdio.h"
    #include "motor.h"
    //串口接收缓存(1字节)
    uint8_t buf=0;
    //定义最大接收字节数 200,可根据需求调整
    #define UART1_REC_LEN 200
    // 接收缓冲, 串口接收到的数据放在这个数组里,最大UART1_REC_LEN个字节
    uint8_t UART1_RX_Buffer[UART1_REC_LEN];
    //  接收状态
    //  bit15,      接收完成标志
    //  bit14,      接收到0x0d
    //  bit13~0,    接收到的有效字节数目
    uint16_t UART1_RX_STA=0;
    #define SIZE 12
    char buffer[SIZE];
    // 接收完成回调函数,收到一个数据后,在这里处理
    void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
    {
    	// 判断中断是由哪个串口触发的
    	if(huart->Instance == USART1)
    	{
    		// 判断接收是否完成(UART1_RX_STA bit15 位是否为1)
    		if((UART1_RX_STA & 0x8000) == 0)
    		{
    			// 如果已经收到了 0x0d (回车),
    			if(UART1_RX_STA & 0x4000)
    			{
    				// 则接着判断是否收到 0x0a (换行)
    				if(buf == 0x0a)
    				{
    					// 如果 0x0a 和 0x0d 都收到,则将 bit15 位置为1
    					UART1_RX_STA |= 0x8000;
    					// 灯控指令
    					if(!strcmp(UART1_RX_Buffer, "M1"))
    						goForward();
    					else if(!strcmp(UART1_RX_Buffer, "M2"))
    						goBack();
    					else if(!strcmp(UART1_RX_Buffer, "M3"))
    						goLeft();
    					else if(!strcmp(UART1_RX_Buffer, "M4"))
    						goRight();
    					else
    						stop();
    					
    					memset(UART1_RX_Buffer, 0, UART1_REC_LEN);
    					UART1_RX_STA = 0;
    				}
    				else
    					// 否则认为接收错误,重新开始
    					UART1_RX_STA = 0;
    			}
    			else	// 如果没有收到了 0x0d (回车)
    			{
    				//则先判断收到的这个字符是否是 0x0d (回车)
    				if(buf == 0x0d)
    				{
    					// 是的话则将 bit14 位置为1
    					UART1_RX_STA |= 0x4000;
    				}
    				else
    				{
    					// 否则将接收到的数据保存在缓存数组里
    					UART1_RX_Buffer[UART1_RX_STA & 0X3FFF] = buf;
    					UART1_RX_STA++;
    					
    					// 如果接收数据大于UART1_REC_LEN(200字节),则重新开始接收
    					if(UART1_RX_STA > UART1_REC_LEN - 1)
    						UART1_RX_STA = 0;
    				}
    			}
    		}
    		// 重新开启中断
    		HAL_UART_Receive_IT(&huart1, &buf, 1);
    	}
    }
    int fputc(int ch, FILE *f)
    {      
    	unsigned char temp[1]={ch};
    	HAL_UART_Transmit(&huart1,temp,1,0xffff);  
    	return ch;
    }
    1.3 如何进行小车PWM调速

    原理:

    全速前进是LeftCon1A = 0; LeftCon1B = 1;

    完全停止是LeftCon1A = 0;LeftCon1B = 0;

    那么单位时间内,比如20ms, 有15ms是全速前进,5ms是完全停止, 速度就会比5ms全速前进,15ms完全停止获得的功率多,相应的速度更快!

    开发:借用PWM的舵机控制代码

    将控制车轮的4个 GPIO 口配置修改如下,否则小车动不起来。

    原因:L9110每个控制口需要一高一低才可以动起来,如果PWM有效电平为高电平,则另一个 GPIO口则需要输出低电平才可以驱动轮子。

    代码实现:

    main.c

    // main函数里
    HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_1);
    HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);
    while (1)
    {
        __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1, 8);
        __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2, 8);
        HAL_Delay(1000);
        __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1, 10);
        __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2, 10);
        HAL_Delay(1000);
        __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1, 15);
        __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2, 15);
        HAL_Delay(1000);
    }
    1.4 PWM方式实现小车转向

    右转原理: 左轮速度大于右轮

    左转原理: 右轮速度大于左轮

    左右轮各自调速代码实现:

    // main函数里
    while (1)
    {
        __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1,8);
        __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2,15);
        HAL_Delay(1000);
        __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1,15);
        __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2,8);
        HAL_Delay(1000);
    }

    2.循迹小车 

    2.1 循迹模块介绍
    • TCRT5000传感器的红外发射二极管不断发射红外线
    • 当发射出的红外线没有被反射回来或被反射回来但强度不够大时
    • 红外接收管一直处于关断状态,此时模块的输出端为高电平,指示二极管一直处于熄灭状态
    • 被检测物体出现在检测范围内时,红外线被反射回来且强度足够大,红外接收管饱和
    • 此时模块的输出端为低电平,指示二极管被点亮
    • 总结就是一句话,没反射回来,D0输出高电平,灭灯

      接线方式

      • VCC:接电源正极(3-5V)
      • GND:接电源负极 DO:TTL开关信号输出0、1
      • AO:模拟信号输出(不同距离输出不同的电压,此脚一般可以不接)
        2.2 循迹小车原理

        由于黑色具有较强的吸收能力,当循迹模块发射的红外线照射到黑线时,红外线将会被黑线吸收,导致 循迹模块上光敏三极管处于关闭状态,此时模块上一个LED熄灭。在没有检测到黑线时,模块上两个LED常亮

        总结就是一句话,有感应到黑线,D0输出高电平 ,灭灯

        2.3 循迹小车核心代码

        硬件接线

        • B-1A -- PA0
        • B-1B -- PB1
        • A-1A -- PA1
        • A-1B -- PB10
        • 循迹模块(左)--  PB3
        • 循迹模块(右) -- PB4

          代码示例:

          #define LeftWheel_Value HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_3)
          #define RightWheel_Value HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_4)
          // main函数里
          while (1)
          {
              if (LeftWheel_Value == GPIO_PIN_RESET && RightWheel_Value == GPIO_PIN_RESET)
                  goForward();
              if (LeftWheel_Value == GPIO_PIN_SET && RightWheel_Value == GPIO_PIN_RESET)
                  goLeft();
              if (LeftWheel_Value == GPIO_PIN_RESET && RightWheel_Value == GPIO_PIN_SET)
                  goRight();
              if (LeftWheel_Value == GPIO_PIN_SET && RightWheel_Value == GPIO_PIN_SET)
                  stop();
          }
          2.4 循迹小车解决转弯平滑问题

          原理:两轮都有速度且一轮速度大于另一轮

          代码实现:

          #define LeftWheel_Value HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_3)
          #define RightWheel_Value HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_4)
          // main函数里
          while (1)
          {
              if(LeftWheel_Value == GPIO_PIN_RESET && RightWheel_Value == GPIO_PIN_RESET)
              {
                  __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1,19);
                  __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2,19);
              }
              if(LeftWheel_Value == GPIO_PIN_SET && RightWheel_Value == GPIO_PIN_RESET)
              {
                  __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1,15);
                  __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2,8);
              }
              if(LeftWheel_Value == GPIO_PIN_RESET && RightWheel_Value == GPIO_PIN_SET)
              {
                  __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1,8);
                  __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2,15);
              }
              if(LeftWheel_Value == GPIO_PIN_SET && RightWheel_Value == GPIO_PIN_SET)
              {
                  __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1,0);
                  __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2,0);
              }
          }

          3.跟随/避障小车

          3.1 红外壁障模块分析

          原理和循迹是一样的,循迹红外观朝下,跟随朝前

          3.2 跟随小车的原理
          • 左边跟随模块能返回红外,输出低电平,右边不能返回,输出高电平,说明物体在左边,需要左转
          • 右边跟随模块能返回红外,输出低电平,左边不能返回,输出高电平,说明物体在右边,需要右转
            3.3 跟随小车开发和调试代码

            硬件接线

            • B-1A -- PB0
            • B-1B -- PB1
            • A-1A -- PB2
            • A-1B -- PB10
            • 跟随模块(左) -- PB5
            • 跟随模块(右) -- PB6

              代码示例:

              #define LeftWheel_Value HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_5)
              #define RightWheel_Value HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_6)
              // main函数里
              while (1)
              {
                  if(LeftWheel_Value == GPIO_PIN_RESET && RightWheel_Value == GPIO_PIN_RESET)
                      goForward();
                  if(LeftWheel_Value == GPIO_PIN_SET && RightWheel_Value == GPIO_PIN_RESET)
                      goRight();
                  if(LeftWheel_Value == GPIO_PIN_RESET && RightWheel_Value == GPIO_PIN_SET)
                      goLeft();
                  if(LeftWheel_Value == GPIO_PIN_SET && RightWheel_Value == GPIO_PIN_SET)
                      stop();
              }
              
              3.4 超声波模块介绍

              使用超声波模块,型号:HC-SR04

              • 怎么让它发送波 Trig ,给Trig端口至少10us的高电平
              • 怎么知道它开始发了 Echo信号,由低电平跳转到高电平,表示开始发送波
              • 怎么知道接收了返回波 Echo,由高电平跳转回低电平,表示波回来了
              • 怎么算时间 Echo引脚维持高电平的时间! 波发出去的那一下,开始启动定时器 波回来的拿一下,我们开始停止定时器,计算出中间经过多少时间
              • 怎么算距离 距离 = 速度 (340m/s)* 时间/2

                时序图:

                3.5 舵机模块介绍

                 1. 什么是舵机

                如下图所示,最便宜的舵机sg90,常用三根或者四根接线,黄色为PWM信号控制 用处:垃圾桶项目开盖用、智能小车的全比例转向、摄像头云台、机械臂等 常见的有0-90°、0-180°、0-360°

                2. 怎么控制舵机

                向黄色信号线“灌入”PWM信号

                PWM波的频率不能太高,大约50HZ,即周期=1/频率=1/50=0.02s,20ms左右

                确定周期/频率:

                如果周期为20ms,则 PSC=7199,ARR=199

                角度控制

                0.5ms-------------0度; 2.5% 对应函数中CCRx为5

                1.0ms------------45度; 5.0% 对应函数中CCRx为10

                1.5ms------------90度; 7.5% 对应函数中CCRx为15

                2.0ms-----------135度; 10.0% 对应函数中CCRx为20

                2.5ms-----------180度; 12.5% 对应函数中CCRx为25

                3.6 摇头避障小车开发和调试代码

                硬件接线

                • sg90 -- PB9

                  cubeMX配置

                  代码实现

                  sg90.c

                  #include "sg90.h"
                  #include "gpio.h"
                  #include "tim.h"
                  void initSG90(void)
                  {
                      HAL_TIM_PWM_Start(&htim4,TIM_CHANNEL_4); //启动定时器4
                      __HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_4, 17); //将舵机置为90度
                  }
                  void sgMiddle(void)
                  {
                      __HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_4, 17); //将舵机置为90度
                  }
                  void sgRight(void)
                  {
                      __HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_4, 5); //将舵机置为0度
                  }
                  void sgLeft(void)
                  {
                      __HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_4, 25); //将舵机置为180度
                  }

                  sg90.h

                  #ifndef __SG90_H__
                  #define __SG90_H__
                  void initSG90(void);
                  void sgMiddle(void);
                  void sgRight(void);
                  void sgLeft(void);
                  #endif

                  main.c

                  initSG90();
                  HAL_Delay(1000);
                  while (1)
                  {
                      sgLeft();
                      HAL_Delay(1000);
                      sgMiddle();
                      HAL_Delay(1000);
                      sgRight();
                      HAL_Delay(1000);
                      sgMiddle();
                      HAL_Delay(1000);
                  }

                  封装超声波传感器

                  超声波模块接线:

                  • Trig       --    PB7
                  • Echo     -- PB8

                    cubeMX配置

                    代码实现

                    sr04.c

                    #include "sr04.h"
                    #include "gpio.h"
                    #include "tim.h"
                    //使用TIM2来做us级延时函数
                    void TIM2_Delay_us(uint16_t n_us)
                    {
                        /* 使能定时器2计数 */
                        __HAL_TIM_ENABLE(&htim2);
                        __HAL_TIM_SetCounter(&htim2, 0);
                        while(__HAL_TIM_GetCounter(&htim2) < ((1 * n_us)-1) );
                        /* 关闭定时器2计数 */
                        __HAL_TIM_DISABLE(&htim2);
                    }
                    double get_distance(void)
                    {
                        int cnt=0;
                        //1. Trig ,给Trig端口至少10us的高电平
                        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET);//拉高
                        TIM2_Delay_us(20);
                        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET);//拉低
                        //2. echo由低电平跳转到高电平,表示开始发送波
                        //波发出去的那一下,开始启动定时器
                        while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_8) == GPIO_PIN_RESET);//等待输入电平拉高
                        HAL_TIM_Base_Start(&htim2);
                        __HAL_TIM_SetCounter(&htim2,0);
                        //3. 由高电平跳转回低电平,表示波回来了
                        while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_8) == GPIO_PIN_SET);//等待输入电平变低
                        //波回来的那一下,我们开始停止定时器
                        HAL_TIM_Base_Stop(&htim2);
                        //4. 计算出中间经过多少时间
                        cnt = __HAL_TIM_GetCounter(&htim2);
                        //5. 距离 = 速度 (340m/s)* 时间/2(计数1次表示1us)
                        return (cnt*340/2*0.000001*100); //单位:cm
                    }

                    sr04.h

                    #ifndef __SR04_H__
                    #define __SR04_H__
                    double get_distance(void);
                    #endif
                    

                    main.c

                    while (1)
                    {
                        if(dir != MIDDLE){
                            sgMiddle();
                            dir = MIDDLE;
                            HAL_Delay(300);
                        }
                        disMiddle = get_distance();
                        if(disMiddle > 35){
                        //前进
                        }
                        else
                        {
                            //停止
                            //测左边距离
                            sgLeft();
                            HAL_Delay(300);
                            disLeft = get_distance();
                            sgMiddle();
                            HAL_Delay(300);
                            sgRight();
                            dir = RIGHT;
                            HAL_Delay(300);
                            disRight = get_distance();
                        }
                    }

                    封装电机驱动

                    代码实现:

                    while (1)
                    {
                        if(dir != MIDDLE){
                            sgMiddle();
                            dir = MIDDLE;
                            HAL_Delay(300);
                        }
                        disMiddle = get_distance();
                        if(disMiddle > 35){
                            //前进
                            goForward();
                        }else if(disMiddle < 10){
                            goBack();
                        }else
                        {
                            //停止
                            stop();
                            //测左边距离
                            sgLeft();
                            HAL_Delay(300);
                            disLeft = get_distance();
                            sgMiddle();
                            HAL_Delay(300);
                            sgRight();
                            dir = RIGHT;
                            HAL_Delay(300);
                            disRight = get_distance();
                            if(disLeft < disRight){
                                goRight();
                                HAL_Delay(150);
                                stop();
                            }
                            if(disRight < disLeft){
                                goLeft();
                                HAL_Delay(150);
                            stop();
                            }
                        }
                        HAL_Delay(50);
                    }
                    

                    4.测速小车

                    4.1 测速模块
                    • 用途:广泛用于电机转速检测,脉冲计数,位置限位等。
                    • 有遮挡,输出高电平;无遮挡,输出低电平
                    • 接线 :VCC 接电源正极3.3-5V
                    • GND 接电源负极 DO TTL开关信号输出
                    • AO 此模块不起作用
                      4.2 测试原理和单位换算
                      • 轮子走一圈,经过一个周长,C = 2x3.14x半径= 3.14 x 直径(6.5cm)
                      • 对应的码盘也转了一圈,码盘有20个格子,每经过一个格子,会遮挡(高电平)和不遮挡(低电平), 那么一个脉冲就是走了 3.14 * 6.5 cm /20 = 1.0205CM
                      • 定时器可以设计成一秒,统计脉冲数,一个脉冲就是1cm
                      • 假设一秒有80脉冲,那么就是80cm/s
                        4.3 定时器和中断实现测速开发和调试代码

                        测试数据通过串口发送到上位机

                        硬件接线

                        测速模块:

                        • VCC -- 3.3V 不能接5V,否则遮挡一次会触发3次中断
                        • OUT -- PB14

                          cubeMX配置

                          代码实现:

                          unsigned int speedCnt;
                          void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
                          {
                              if (GPIO_Pin == GPIO_PIN_14)
                                  if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_14) == GPIO_PIN_RESET)
                                      speedCnt++;
                          }
                          void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
                          {
                              printf("speed: %d\r\n", speedCnt);
                              speedCnt = 0;
                          }
                          main函数里:
                          HAL_TIM_Base_Start_IT(&htim2);
                          4.4 小车速度显示在OLED屏

                          OLED模块介绍:STM32 OLED屏幕显示详解

                          硬件接线

                          • SCL -- PB6
                          • SDA -- PB7

                            代码示例:

                            oled.c

                            #include "oled.h"
                            #include "i2c.h"
                            #include "oledfont.h"
                            void Oled_Write_Cmd(uint8_t dataCmd)
                            {
                            	
                            	HAL_I2C_Mem_Write(&hi2c1, 0x78, 0x00, I2C_MEMADD_SIZE_8BIT,
                            										&dataCmd, 1, 0xff);
                            }
                            void Oled_Write_Data(uint8_t dataData)
                            {
                            	HAL_I2C_Mem_Write(&hi2c1, 0x78, 0x40, I2C_MEMADD_SIZE_8BIT,
                            										&dataData, 1, 0xff);
                            }
                            void Oled_Init(void){
                            	Oled_Write_Cmd(0xAE);//--display off
                            	Oled_Write_Cmd(0x00);//---set low column address
                            	Oled_Write_Cmd(0x10);//---set high column address
                            	Oled_Write_Cmd(0x40);//--set start line address  
                            	Oled_Write_Cmd(0xB0);//--set page address
                            	Oled_Write_Cmd(0x81); // contract control
                            	Oled_Write_Cmd(0xFF);//--128   
                            	Oled_Write_Cmd(0xA1);//set segment remap 
                            	Oled_Write_Cmd(0xA6);//--normal / reverse
                            	Oled_Write_Cmd(0xA8);//--set multiplex ratio(1 to 64)
                            	Oled_Write_Cmd(0x3F);//--1/32 duty
                            	Oled_Write_Cmd(0xC8);//Com scan direction
                            	Oled_Write_Cmd(0xD3);//-set display offset
                            	Oled_Write_Cmd(0x00);//
                            	
                            	Oled_Write_Cmd(0xD5);//set osc division
                            	Oled_Write_Cmd(0x80);//
                            	
                            	Oled_Write_Cmd(0xD8);//set area color mode off
                            	Oled_Write_Cmd(0x05);//
                            	
                            	Oled_Write_Cmd(0xD9);//Set Pre-Charge Period
                            	Oled_Write_Cmd(0xF1);//
                            	
                            	Oled_Write_Cmd(0xDA);//set com pin configuartion
                            	Oled_Write_Cmd(0x12);//
                            	
                            	Oled_Write_Cmd(0xDB);//set Vcomh
                            	Oled_Write_Cmd(0x30);//
                            	
                            	Oled_Write_Cmd(0x8D);//set charge pump enable
                            	Oled_Write_Cmd(0x14);//
                            	
                            	Oled_Write_Cmd(0xAF);//--turn on oled panel		
                            }
                            void Oled_Screen_Clear(void){
                            	char i,n;
                            	Oled_Write_Cmd (0x20);                    //set memory addressing mode
                            	Oled_Write_Cmd (0x02);                    //page addressing mode
                            	for(i=0;i<8;i++){
                            		Oled_Write_Cmd(0xb0+i);               
                            		Oled_Write_Cmd(0x00);                 
                            		Oled_Write_Cmd(0x10);                 
                            		for(n=0;n<128;n++)Oled_Write_Data(0x00); 			
                            	}	
                            }
                            void Oled_Show_Char(char row,char col,char oledChar){ //row*2-2
                            	unsigned int  i;
                            	Oled_Write_Cmd(0xb0+(row*2-2));                           //page 0
                            	Oled_Write_Cmd(0x00+(col&0x0f));                          //low
                            	Oled_Write_Cmd(0x10+(col>>4));                            //high	
                            	for(i=((oledChar-32)*16);i<((oledChar-32)*16+8);i++){
                            		Oled_Write_Data(F8X16[i]);                            //写数据oledTable1
                            	}
                            	Oled_Write_Cmd(0xb0+(row*2-1));                           //page 1
                            	Oled_Write_Cmd(0x00+(col&0x0f));                          //low
                            	Oled_Write_Cmd(0x10+(col>>4));                            //high
                            	for(i=((oledChar-32)*16+8);i<((oledChar-32)*16+8+8);i++){
                            		Oled_Write_Data(F8X16[i]);                            //写数据oledTable1
                            	}		
                            }
                            /******************************************************************************/
                            // 函数名称:Oled_Show_Char 
                            // 输入参数:oledChar 
                            // 输出参数:无 
                            // 函数功能:OLED显示单个字符
                            /******************************************************************************/
                            void Oled_Show_Str(char row,char col,char *str){
                            	while(*str!=0){
                            		Oled_Show_Char(row,col,*str);
                            		str++;
                            		col += 8;	
                            	}		
                            }
                            

                            main.c

                            extern uint8_t buf;
                            unsigned int speedCnt = 0;
                            char speedMes[24];  //主程序发送速度数据的字符串缓冲区
                            void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
                            {
                            	if (GPIO_Pin == GPIO_PIN_14)
                            		if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_14) == GPIO_PIN_RESET)
                            			speedCnt++;
                            }
                            void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
                            {
                            	printf("speed: %d\r\n", speedCnt);
                            	sprintf(speedMes,"speed:%2d cm/s",speedCnt);//串口数据的字符串拼装,speed是格子,每个格子1cm
                            	Oled_Show_Str(2,2,speedMes);
                            	speedCnt = 0;
                            }

                            5.远程控制小车

                            5.1 蓝牙控制小车
                            • 使用蓝牙模块,串口透传
                            • 蓝牙模块,又叫做蓝牙串口模块

                              串口透传技术:

                              • 透传即透明传送,是指在数据的传输过程中,通过无线的方式这组数据不发生任何形式的改变,仿 佛传输过程是透明的一样,同时保证传输的质量,原封不动地到了最终接收者手里。
                              • 以太网,蓝牙,Zigbee, GPRS 等模块玩法一样,对嵌入式程序员来说,不需要关心通讯模块内部数据 及协议栈工作原理,只要通过串口编程获得数据即可

                                代码实现:

                                整合前面串口控制小车代码,接入蓝牙模块

                                if (!strcmp(UART1_RX_Buffer, "M1"))
                                {
                                    goForward();
                                    HAL_Delay(10);
                                }
                                else if (!strcmp(UART1_RX_Buffer, "M2"))
                                {
                                    goBack();
                                    HAL_Delay(10);
                                }
                                else if (!strcmp(UART1_RX_Buffer, "M3"))
                                {
                                    goLeft();
                                    HAL_Delay(10);
                                }
                                else if (!strcmp(UART1_RX_Buffer, "M4"))
                                {
                                    goRight();
                                    HAL_Delay(10);
                                }
                                else
                                    stop();
                                
                                5.2 蓝牙控制并测速小车

                                原理:运用上面讲到的蓝牙模块和测速模块,将两者代码整合

                                5.3 wifi控制测速小车
                                • Wifi模块-ESP-01s
                                • 蓝牙,ESP-01s,Zigbee, NB-Iot等通信模块都是基于AT指令的设计

                                  AT指令介绍:

                                  • AT指令集是从终端设备(Terminal Equipment,TE)或数据终端设备(Data Terminal Equipment,DTE)向终端适配器(Terminal Adapter,TA)或数据电路终端设备(Data Circuit Terminal Equipment,DCE)发送的。
                                  • 其对所传输的数据包大小有定义:即对于AT指令的发送,除AT两个字符外,最多可以接收1056个 字符的长度(包括最后的空字符)。
                                  • 每个AT命令行中只能包含一条AT指令;对于由终端设备主动向PC端报告的URC指示或者response 响应,也要求一行最多有一个,不允许上报的一行中有多条指示或者响应。AT指令以回车作为结 尾,响应或上报以回车换行为结尾。

                                    硬件接线

                                    • 把esp8266插进串口1

                                      使用方法:

                                      Wifi模块-ESP-01s_esp01s波特率-CSDN博客

                                      5.4 4g控制小车

                                      原理:运用EC03-DNC4G通信模块

                                      模块介绍:

                                      • 基于串口AT指令的开发方式
                                      • 有两种工作模式,默认是透传模式,通过其他方式进入AT指令模式
                                      • 注意插卡不要出错,下图红色位置为SIM卡状态灯,亮才是正常

                                        代码不做修改,直接基于蓝牙小车整合, 4g模块只要做好外网透传就可以了

                                        6.语音控制小车

                                        6.1语音模块配置:

                                        使用SU-03T / LD3320

                                        具体介绍看我之前写过的博客:SU-03T语音模块的使用_罗小白的干爹的博客-CSDN博客

                                          

                                        6.2 语音控制小车开发和调试代码

                                        硬件接线:

                                         循迹小车: 

                                        • 循迹模块(左) -- PB3
                                        • 循迹模块(右) -- PB4

                                           跟随小车: 

                                          • 跟随模块(左) -- PA8
                                          • 跟随模块(右) -- PA9

                                             避障小车: 

                                            • sg90:PB9
                                            • Trig:PA10
                                            • Echo:PA11

                                               OLED模块: 

                                              • SCL -- PB6
                                              • SDA -- PB7

                                                 语音模块: 

                                                • A25 -- PA15 (跟随)
                                                • A26 -- PA13 (避障)
                                                • A27 -- PA14 (循迹)

                                                  cubeMX配置  

                                                  代码示例:

                                                  #include "main.h"
                                                  #include "i2c.h"
                                                  #include "tim.h"
                                                  #include "gpio.h"
                                                  #include "sg90.h"
                                                  #include "sr04.h"
                                                  #include "motor.h"
                                                  #include "oled.h"
                                                  #define MIDDLE 0
                                                  #define LEFT 1
                                                  #define RIGHT 2
                                                  #define BZ 1
                                                  #define XJ 2
                                                  #define GS 3
                                                  #define LeftWheel_Value_XJ HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_3)
                                                  #define RightWheel_Value_XJ HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_4)
                                                  #define LeftWheel_Value_GS HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_8)
                                                  #define RightWheel_Value_GS HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_9)
                                                  #define A25 HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_15)
                                                  #define A26 HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_13)
                                                  #define A27 HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_14)
                                                  void SystemClock_Config(void);
                                                  char dir;
                                                  void xunjiMode()
                                                  {
                                                  	if(LeftWheel_Value_XJ == GPIO_PIN_RESET && RightWheel_Value_XJ == GPIO_PIN_RESET)
                                                  		goForward();
                                                  	if(LeftWheel_Value_XJ == GPIO_PIN_SET && RightWheel_Value_XJ == GPIO_PIN_RESET)
                                                  		goLeft();
                                                  	if(LeftWheel_Value_XJ == GPIO_PIN_RESET && RightWheel_Value_XJ == GPIO_PIN_SET)
                                                  		goRight();
                                                  	if(LeftWheel_Value_XJ == GPIO_PIN_SET && RightWheel_Value_XJ == GPIO_PIN_SET)
                                                  		stop();
                                                  }
                                                  void gensuiMode()
                                                  {
                                                  	if(LeftWheel_Value_GS == GPIO_PIN_RESET && RightWheel_Value_GS == GPIO_PIN_RESET)
                                                  		goForward();
                                                  	if(LeftWheel_Value_GS == GPIO_PIN_SET && RightWheel_Value_GS == GPIO_PIN_RESET)
                                                  		goRight();
                                                  	if(LeftWheel_Value_GS == GPIO_PIN_RESET && RightWheel_Value_GS == GPIO_PIN_SET)
                                                  		goLeft();
                                                  	if(LeftWheel_Value_GS == GPIO_PIN_SET && RightWheel_Value_GS == GPIO_PIN_SET)
                                                  		stop();
                                                  }
                                                  void bizhangMode()
                                                  {
                                                  	double disMiddle;
                                                  	double disLeft;
                                                  	double disRight;
                                                  	if(dir != MIDDLE){
                                                  		sgMiddle();
                                                  		dir = MIDDLE;
                                                  		HAL_Delay(300);
                                                  	}
                                                  	disMiddle = get_distance();
                                                  	
                                                  	if(disMiddle > 35){
                                                  		//前进
                                                  		goForward();
                                                  	}else if(disMiddle < 10){
                                                  		goBack();
                                                  		
                                                  	}else
                                                  	{
                                                  		//停止
                                                  		stop();
                                                  		//测左边距离
                                                  		sgLeft();
                                                  		HAL_Delay(300);
                                                  		disLeft = get_distance();
                                                  		
                                                  		sgMiddle();
                                                  		HAL_Delay(300);
                                                  		
                                                  		sgRight();
                                                  		dir = RIGHT;
                                                  		HAL_Delay(300);
                                                  		disRight = get_distance();
                                                  		
                                                  		if(disLeft < disRight){
                                                  			goRight();
                                                  			HAL_Delay(150);
                                                  			stop();
                                                  		}
                                                  		if(disRight < disLeft){
                                                  			goLeft();
                                                  			HAL_Delay(150);
                                                  			stop();
                                                  		}
                                                  	}
                                                  	HAL_Delay(50);
                                                  }
                                                  int main(void)
                                                  {
                                                      int mark = 0;
                                                      HAL_Init();
                                                      SystemClock_Config();
                                                      MX_GPIO_Init();
                                                      MX_TIM4_Init();
                                                      MX_TIM2_Init();
                                                      MX_I2C1_Init();
                                                  	initSG90();
                                                  	HAL_Delay(1000);
                                                  	dir = MIDDLE;
                                                  	Oled_Init();
                                                  	Oled_Screen_Clear();
                                                  	Oled_Show_Str(2,2,"-----Ready----");
                                                    while (1)
                                                    {
                                                  		//满足寻迹模式的条件
                                                  		if(A25 == 1 && A26 == 1 && A27 == 0){
                                                  			if(mark != XJ){
                                                  				Oled_Screen_Clear();
                                                  				Oled_Show_Str(2,2,"-----XunJi----");
                                                  			}
                                                  			mark = XJ;
                                                  			xunjiMode();
                                                  		}
                                                  		//满足跟随模式的条件
                                                  		if(A25 == 0 && A26 == 1 && A27 == 1){
                                                  			if(mark != GS){
                                                  				Oled_Screen_Clear();
                                                  				Oled_Show_Str(2,2,"-----GenSui----");
                                                  			}
                                                  			mark = GS;
                                                  			gensuiMode();
                                                  		}
                                                  		//满足避障模式的条件
                                                  		if(A25 == 1 && A26 == 0 && A27 == 1){
                                                  			if(mark != BZ){
                                                  				Oled_Screen_Clear();
                                                  				Oled_Show_Str(2,2,"-----BiZhang----");
                                                  			}
                                                  			mark = BZ;
                                                  			bizhangMode();
                                                  		}
                                                    }
                                                  }
                                                  

转载请注明来自码农世界,本文标题:《STM32智能小车(循迹、跟随、避障、测速、蓝牙、wife、4g、语音识别)总结》

百度分享代码,如果开启HTTPS请参考李洋个人博客
每一天,每一秒,你所做的决定都会改变你的人生!

发表评论

快捷回复:

评论列表 (暂无评论,74人围观)参与讨论

还没有评论,来说两句吧...

Top