当前位置: 代码网 > it编程>硬件开发>stm32 > STM32智能小车(循迹、跟随、避障、测速、蓝牙、wifi、4g、语音识别)总结

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

2024年07月28日 stm32 我要评论
STM32智能小车(循迹、跟随、避障、测速、蓝牙、wife、4g、语音识别)总结循迹小车循迹小车3.跟随/避障小车4.测速小车5.远程控制小车6.语音控制小车

前言

有需要帮忙代做51和32小车或者其他单片机项目,课程设计,报告,pcb原理图的小伙伴,可以在文章最下方加我v交流咨询,本篇文章的小车所有功能实现的代码还有硬件清单放在资源包里,有需要的自行下载即可!

目录

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语音模块的使用_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();
		}
  }
}


(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com