嵌入式项目:STM32平衡车详解 (基础知识篇) (基于STM32F103C8T6)

前言:

本文是基于B站草履虫编写的平衡车相关内容,包括模块和基础知识,结合代码进行讲解,将知识进行汇总

(由于本篇内容较长,请结合目录使用)

注:基于开源精神,本文仅供学习参考


目录

前言:

本文是基于B站草履虫编写的平衡车相关内容,包括模块和基础知识,结合代码进行讲解,将知识进行汇总

注:基于开源精神,本文仅供学习参考

1.基础知识

1.1输出模式

1.1.1推挽输出

1.1.2开漏输出

1.1.3复用推挽和开漏输出

1.2电阻

[1.2.1 上拉电阻](#1.2.1 上拉电阻)

1.2.2下拉电阻

1.3IIC通信协议(I2C)

1.3.1总线的概念

[1.3.2 IIC通信规则](#1.3.2 IIC通信规则)

1.3.3IIC通信数据的发送和接收

1.4欧拉角

1.5H桥驱动电路

1.5.1基本电路图

1.5.2正反转

1.5.3调速

1.5.4死区

1.5.5刹车

1.6USART串口通信

1.6.1基本原理

1.6.2波形原理

2.点亮LED

2.1模块原理图

2.2工程

2.2.1配置外部时钟

2.2.2配置引脚

2.2.3下载程序

2.2.4创建工程

3.OLED模块

3.1模块配置

3.2模块原理图

3.3工程

3.3.1配置通信引脚

3.3.2配置时钟

3.3.3配置OLED驱动代码

3.3.4简单改写驱动配置

3.3.5显示数字

3.3.6显示字符串

3.3.7显示汉字

3.3.8使用PCtoLCD2002生成汉字编码

3.3.9测试代码和现象

4.MPU6050

4.1MPU6050工作原理

4.2陀螺仪传感器

4.3加速度传感器

4.4精确欧拉角

4.5模块原理介绍

4.6模块原理图

4.7工程

4.7.1创建工程

4.7.2设置软件IIC通信

4.7.3添加.c文件

4.7.4添加.h文件

4.7.5详解代码与现象

5.超声波模块

5.1基本工作原理

5.2模块原理图

5.3工程

5.3.1思路

5.3.2创建工程

5.3.3配置工程

5.3.4使能内部时钟

5.3.5生成代码

5.3.5创建模块文件

5.3.6代码

5.3.7现象

6.电机驱动

6.1TB6612驱动模块

6.1.2TB6612的引脚说明

6.1.3驱动原理

6.2工程

6.2.1创建工程

6.2.2打开定时器TIM

6.2.3配置预分频系数

6.2.4配置占空比

6.2.5配置控制方向GPIO

6.2.6创建模块文件

6.2.7代码

6.2.8现象

7.编码器测速

8.蓝牙模块

8.1JDY-31蓝牙模块

8.2模块原理图

8.3工程

8.3.1创建Uart文件

8.3.2配置USART

8.3.3生成代码

8.3.4发送数据

8.3.5缓存数据

8.3.6接收中断

​编辑

总结(必看):


1.基础知识

1.1输出模式

1.1.1推挽输出

如图推挽输出内部结构,PMOS管和NMOS管同一时间只有一个管子会导通

当PMOS管导通,此时输出一个高电平(VCC),称之为推

同理,当NMOS管导通,此时输出一个低电平(接地),称之为挽

1.1.2开漏输出

如图为开漏输出,与推挽输出相比,开漏少了一个PMOS管

当NMOS导通时,此时输出一个低电平(接地)

当NMOS断开时,此时电路为高阻态的状态(此时两个MOS管都为截断状态,可被视为电阻无穷大)

开漏输出不具有高电平的输出能力,所以我们外接一个上拉电阻(下面会讲)

使电路具备输出高电平的能力

1.1.3复用推挽和开漏输出

当我们使用高频芯片内部外设时(每秒钟电平变化上万次)

像 PWM、I2C、USART 等

使用复用功能会极大方便我们的编程

1.2电阻

1.2.1 上拉电阻

作用:将电路电平提升至一个高电平的状态(可将不确定电平拉为高电平)

1.2.2下拉电阻

作用:默认状态让电路为低电平,输入高电平时也可以让电路呈高电平状态

1.3IIC通信协议(I2C)

1.3.1总线的概念

非总线通信一般有:串口通信等

总线通信一般有:I2C通信等

总线优点:可以很轻松的增加与芯片通信的通信模块数量

1.3.2 IIC通信规则

这里我们着重介绍I2C (IIC - Inter - Integrated Circuit 内部集成电路)

SCL:传输时钟信号

SDA:传输数据信号

通信规则:

1.所有设备SCL/SDA引脚必须连接到主线SCL/SDA线上

2.同一时间只允许一个设备向外发送数据

3.仅主机主动产生SCL信号控制波特率(通信速度)

4.仅主机主动发起数据传输,从机无控制权(主机主动发送数据或接收数据)

5.传输方向:主机->从机,从机->主机,不存在从机->从机

1.3.3IIC通信数据的发送和接收

从机地址最后一个比特位(x)表示数据通信方向:

x=0:主机发数据给从机

x=1:主机从 从机读取数据

1.4欧拉角

欧拉角:以运动物体建立坐标系,通过绕轴旋转来表示运动物体当前的姿态

Yaw -偏航角:表示飞机绕Z轴旋转的角度,决定了飞机在水平方向上的朝向(决定飞机航行向)

Roll-翻滚角:表示飞机绕X轴旋转的角度

Pitch -俯仰角:表示飞机绕Y轴旋转的角度

一般我们可以通过加速度计和陀螺仪测量欧拉角

1.5H桥驱动电路

1.5.1基本电路图

Q1和Q3称为上桥

Q2和Q4称为下桥

单片机的4个IO口分别控制H桥的四个MOS(Qx)管,就可以实现电机的正反转、调速、刹车

(M为电机)

1.5.2正反转

打开Q1和Q4(假设此情况为正转)

电流方向Q1->M->Q4(如图所示)

打开Q3和Q2(那么此情况为反转)

1.5.3调速

单片机的PWM的输入可以给到上桥也可以给到下桥

调节PWM的占空比,占空比越大,电机转速越大,占空比越小电机转速越小

占空比即PWM在一个周期中高电平的时间与单位周期的比值

1.5.4死区

这里不过多解释,本质就是,单边上下桥不能同时导通(例如Q1和Q2不能同时导通)

否则会烧坏模块(即电源和GND短接)

1.5.5刹车

刹车基本原理:电机正负极相接

在H桥中我们可以同时打开两个上桥或两个下桥,实现刹车

1.6USART串口通信

1.6.1基本原理

在单片机中我们常用Tx表示发送端,Rx表示接收端,USART串口通信将Tx和Rx用导线相接

1.6.2波形原理

发送和接收的每条数据都有一条方形波

一条数据包括一个起始位和一个停止位

起始位永远是低电平,停止位永远是高电平

高电平发送二进制1

低电平发送二进制0

1-8为位代表数据位,为二进制,按十进制数值来算,最大可发送数字128,这个数字对应ASCII表

并且是倒过来读数

此图数据为01000001,对应ASCII码值为'A'

注意两个设备实现USART串口通信必须波特率相同


2.点亮LED

2.1模块原理图

2.2工程

2.2.1配置外部时钟

使用外部时钟的原因:比内部时钟更精准,所以通常情况我们都会使用外部时钟

打开STM32CubeMX并配置外部晶振(Crystal/Ceramic Resonator)

并将HCLK设置成72MHz(最大)

2.2.2配置引脚

打开PC13引脚

接着配置PC13引脚的模式

GPIO Output level -> Low

GPIO Mode -> Push Pull

GPIO Pull-up/Pull-down -> No

Maximum output speed -> High

User Label ->LED

  1. 根据上面的原理图,PC13连接LED负极,也就是说PC13高电平LED会灭,低电平LED就会亮

2.输出模式默认是推挽输出,开漏输出用的不多,特殊情况再用

3.推挽输出不需要上下拉电阻,推挽相当于直接接负极或正极

4.点亮LED对速度(芯片功耗)无要求,特殊情况再讨论

5.给PC13重新起名字 LED

2.2.3下载程序

在这里我们选择Serial Wire 的下载方式

因为我们的芯片就是通过电脑连接芯片SWCLK和SWDIO引脚下载程序的

2.2.4创建工程

注意IDE这一栏选择MDK-ARM

接着生成代码

驱动已经用软件配置好了,下面写一下主函数

cpp 复制代码
int main(void)
{

  
  HAL_Init();

  SystemClock_Config();

  MX_GPIO_Init();
 
  while (1)
  {
		HAL_GPIO_WritePin(LED_GPIO_Port,LED_Pin,GPIO_PIN_RESET); //亮
		HAL_Delay(500); //延时500毫秒
		HAL_GPIO_WritePin(LED_GPIO_Port,LED_Pin,GPIO_PIN_RESET); //灭
		HAL_Delay(500); 	
  }

}

接着编译,调试(烧录)就行了


3.OLED模块

3.1模块配置

本项目使用的OLED模块配置

屏幕大小:0.96寸

像素点:128*64

PIN:

GND:OLED电源接地

VCC:OLED电源正极(可接3.3V/5V)

SCL:通信,时钟信号线(I2C)

SDA:通信,数据信号线(I2C)

3.2模块原理图

注意:通信引脚通常需要后期配置,这里是通用的原理图

3.3工程

3.3.1配置通信引脚

根据原理图,OLED通信引脚SCL- PB8 SDA-PB9

在软件左边配置框里找到I2C1并进行配置

其它选项默认即可

3.3.2配置时钟

跟LED一样,时钟设置为72兆

3.3.3配置OLED驱动代码

将驱动代码(驱动代码在文章结尾)放在指定路径里

OLED\Core\Inc 放入 oled.h oledfont.h

OLED\Core\Src 放入 oled.c

3.3.4简单改写驱动配置

在include 沙盒里添加 olde.h

在工程里把**.c** 文件添加进去(前面文件操作时**.h** 文件已经自动添加了,.c文件需手动添加)

在oled.h检查引脚

SCK ->PB8

SDA ->PB9

跟在Cube里设置的一样,不用修改

3.3.5显示数字

OLED_ShowNum(0,0,123456,6,16);

OLED_ShowNum函数五个参数:

x坐标-1代表1个像素点

y坐标-1代表8个像素点

显示数字

数字长度

数字大小(16像素:8*16,12像素:6*12)

3.3.6显示字符串

OLED_ShowString(0,2,"hello world!",16);

OLED_ShowString函数四个参数:

x坐标-1代表1个像素点

y坐标-1代表16个像素点

显示字符串

字符串大小 (16像素:8*16,12像素:6*12)

3.3.7显示汉字

着重说一下输出汉字

一个汉字像素------16*16

OLED_ShowCHinese(0,4,0);

OLED_ShowCHinese函数三个参数:

x坐标

y坐标

Hzk库

Hzk:汉字库

Hzk汉字库是用软件生成的,生成软件(PCtoLCD2002)在文章结尾

想要输入什么汉字,就用软件生成数组,写到Hzk库里,用0-x(字符数组)数字表示

3.3.8使用PCtoLCD2002生成汉字编码

首先设置基本格式

复制生成的编码到Hzk库里

3.3.9测试代码和现象

首先在主函数里添加这两个函数

OLED_Init(); //初始化
OLED_Clear();//清屏

然后添加输出函数,并配置函数的参数,下面是主函数

cpp 复制代码
int main(void)
{

  HAL_Init();

  SystemClock_Config();

  MX_GPIO_Init();
  MX_I2C1_Init();

	OLED_Init(); //初始化
	OLED_Clear();//清屏
	OLED_ShowNum(0,0,123456,6,16);
	OLED_ShowString(0,2,"hello world!",16);
	OLED_ShowCHinese(0,4,0 );
	OLED_ShowCHinese(16,4,1);
	OLED_ShowCHinese(32,4,2);

  while (1)
  {

  }

}

结果


4.MPU6050

4.1MPU6050工作原理

MPU6050 内部整合了 3 轴陀螺仪和 3 轴加速度传感器,并且含有一个第二 IIC 接口,可用于连接外部磁力传感器即AUX_CL 和 AUX_DA,并利用自带的数字运动处理器(DMP: DigitalMotion Processor)硬件加速引擎,通过主 IIC 接口,向应用端输出完整的 9 轴融合演算数据

在mpu6050中我们基于欧拉角()用陀螺仪传感器测角度,用加速度传感器测加速度

4.2陀螺仪传感器

在MPU6050中有三轴陀螺仪

陀螺仪工作原理:测量角速度进而求出欧拉角

g(x/y/z):绕x/y/z轴旋转的角速度

计算公式:

4.3加速度传感器

在MPU6050中有三轴加速度计

加速度计工作原理:分解g(重力加速度),得到X/Y/Z三轴上的加速度

a(x/y/z):飞机在x/y/z轴上的加速度

利用三角关系求出各个方向上的加速度

4.4精确欧拉角

这两个传感器各有优缺点

我们根据实际情况分配给他们权重,以此来精确欧拉角

计算公式

α一般取值:

Δt:读取传感器的时间间隔(例如200Hz)

t:加速度计对陀螺仪纠偏时间(一般取0.1)

4.5模块原理介绍

4.6模块原理图

4.7工程

4.7.1创建工程

复制OLED工程

接着创建工程

将MPU6050驱动函数放入工程文件夹里

4.7.2设置软件IIC通信

打开工程,设置软件IIC通信(用软件编程的方式模拟IIC,因为PB3/4无IIC硬件接口)(硬件软件IIC通信效果无差别)

4.7.3添加.c文件

4.7.4添加.h文件

4.7.5详解代码与现象

main.c

初始化

定义三个角度的参数

在主函数里调用这三个参数

定义一个用于缓存的变量

添加库**#include "stdio.h",用于sprintf**

(sprintf函数打印到字符串中(要注意字符串的长度要足够容纳打印的内容,否则会出现内存溢出),printf函数打印输出到屏幕上)

姿态在OLED中的显示

语句sprintf((char*)display_buf, "pich:%.2f",pitch);解读:

因为OLED打印字符串,将display_buf强制类型转换成char类型,并将pitch打印到display_buf中

测试结果(数字为动态变化)

完整代码(仅显示头文件和进程部分)

cpp 复制代码
#include "main.h"
#include "i2c.h"
#include "gpio.h"


#include "oled.h"
#include "IIC.h"
#include "inv_mpu.h"
#include "inv_mpu_dmp_motion_driver.h"
#include "mpu6050.h"
#include "stdio.h"


float pitch,roll,yaw; //定义三个角度的参数
uint8_t display_buf[20];  //显示缓存

void SystemClock_Config(void);


int main(void)
{

  HAL_Init();


  SystemClock_Config();


  MX_GPIO_Init();
  MX_I2C1_Init();
 
	OLED_Init(); //初始化
	OLED_Clear();//清除
	
	MPU_Init(); //初始化MPU
	mpu_dmp_init();
	OLED_ShowString(0,00,"Init succeed",16);
	

  while (1)
  {
		HAL_Delay(10);
		mpu_dmp_get_data(&pitch,&roll,&yaw);
		
		sprintf((char*)display_buf, "pich:%.2f   ",pitch);
		OLED_ShowString(0,2,display_buf,16);
		sprintf((char*)display_buf, "pich:%.2f   ",roll);
		OLED_ShowString(0,4,display_buf,16);
		sprintf((char*)display_buf, "pich:%.2f   ",yaw);
		OLED_ShowString(0,6,display_buf,16);
  }

}

5.超声波模块

5.1基本工作原理

本项目使用HC-SR04 超声波测距模块,可提供 2cm-400cm(实际上到200cm ) 的非接触式距离感测功能,测距精度可达高到的非接触式距离感测功能,测距精度可达高到 3mm

(1)采用 IO 口 TRIG 触发测距,给最少 10us 的高电平信呈。
(2)模块自动发送 8 个 40khz 的方波,自动检测是否有信号返回;
(3)有信号返回,通过 IO 口 ECHO 输出一个高电平,高电平持续的时间就是超声
波从发射到返回的时间。

公式

测试距离=(高电平时间*声速(340M/S))/2;

超声波时序图

原理:提供一个 10uS 以上脉冲触发信号,该模块内部将发出 8 个 40kHz 周期电平并检测回波。一旦检测到有回波信号则输出回响信号 。回响信号的脉冲宽度与所测的距离成正比。由此通过发射信号到收到的回响信号时间间隔可以计算得到距离

建议测量周期为 60ms 以上,以防止发射信号对回响信号的影响

该内容摘自HC-SR04超声波测距模块的原理介绍与代码实现-CSDN博客,详细内容参考以上链接

5.2模块原理图

Vcc:+5V电源供电
Trig:输入触发信号(可以触发测距)
Echo:传出信号回响(可以传回时间差)
Gnd:接地

注:
1 、此模块不宜带电连接,若要带电连接,则先让模块的 GND 端先连接,否则会影响模块的正常工作。
2 、测距时,被测物体的面积不少于 0.5 平方米且平面尽量要求平整,否则影响测量的结果

5.3工程

5.3.1思路

1.触发信号只需要输出10ms的高电平,所以触发引脚我们直接设置为GPIO(利用GPIO输出15ms的高电平)

2.回响信号使用外部中断(回响引脚配置成外部中断)

3.在回响信号上升沿时产生中断,利用单片机内部定时器定时,下降沿时再产生一次中断,这时读取定时器的值

4.利用定时器里的值(产生高电平的时间)根据公式计算结果

测试距离=高电平时间*声速(340M/S))/2;

5.3.2创建工程

创建MPU6050文件夹副本并改名为HC-SR04

打开副本里的CubeMx

5.3.3配置工程

设置

PA2-GPIO_EXTI2

PA3-GPIO_Output

将PA3引脚速度设置为High

选择PA2中断模式(上升沿下降沿都中断)

打开EXTI Line(不打开中断无任何作用)

5.3.4使能内部时钟

使能内部时钟并设置预分频系数为71

分频系数计算公式

5.3.5生成代码

5.3.5创建模块文件

创建并添加模块文件

sr04.c、sr04.h

5.3.6代码

main.c

cpp 复制代码
#include "main.h"
#include "i2c.h"
#include "tim.h"
#include "gpio.h"



#include "oled.h"
#include "IIC.h"
#include "inv_mpu.h"
#include "inv_mpu_dmp_motion_driver.h"
#include "mpu6050.h"
#include "stdio.h"
#include "sr04.h"


float pitch,roll,yaw; //定义三个角度的参数
uint8_t display_buf[20];  //显示缓存
extern float distance;


void SystemClock_Config(void);

int main(void)
{

  HAL_Init();


  SystemClock_Config();


  MX_GPIO_Init();
  MX_I2C1_Init();
  MX_TIM3_Init();
  
	OLED_Init(); //初始化
	OLED_Clear();//清除
	
	MPU_Init(); 
	mpu_dmp_init();
	OLED_ShowString(0,00,"Init succeed",16);
	


  while (1)
  {
		HAL_Delay(100);
		mpu_dmp_get_data(&pitch,&roll,&yaw);
		

		sprintf((char*)display_buf, "pich:%.2f   ",roll);
		OLED_ShowString(0,2,display_buf,16);
		GET_Distance();
		sprintf((char*)display_buf, "distance:%.2f   ",distance);
		OLED_ShowString(0,4,display_buf,12);

  }

}

sr04.c

cpp 复制代码
#include "sr04.h"

uint16_t count; //1count=1us
float distance; //保存距离(cm)


extern TIM_HandleTypeDef htim3; //声明外部定义变量

void RCCdelay_us(uint32_t udelay)
{
  __IO uint32_t Delay = udelay * 72 / 8;//(SystemCoreClock / 8U / 1000000U)
    //见stm32f1xx_hal_rcc.c -- static void RCC_Delay(uint32_t mdelay)
  do
  {
    __NOP();
  }
  while (Delay --);
}//us级延时函数

void GET_Distance(void) //发送触发信号
{
	HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3,GPIO_PIN_SET); //输出高电平
	RCCdelay_us(12); //信号持续时间为10us,保险起见设置为12us
	HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3,GPIO_PIN_RESET); //输出低电平
}

//中断函数
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	//判断上升沿还是下降沿
	if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_2)==GPIO_PIN_SET)
	{
		__HAL_TIM_SetCounter(&htim3,0);
		HAL_TIM_Base_Start(&htim3); //打开计时器时钟让它开始计时
	}
	else
	{
		HAL_TIM_Base_Stop(&htim3); //停止计时
		count = __HAL_TIM_GetCounter(&htim3);
		distance = count*0.017;//count/1000000*340*100/2;
	}
	
}

sr04.h

cpp 复制代码
#ifndef _SR04_H
#define	_SR04_H

#include "stm32f1xx_hal.h"

void GET_Distance(void);


#endif

5.3.7现象


6.电机驱动

6.1TB6612驱动模块

本项目使用TB6612模块驱动电机

6.1.2TB6612的引脚说明

6.1.3驱动原理

1. AIN1和AIN2、BIN1和BIN2分别与AO1、AO2和BO1、BO2配合使用来控制电机的方向和速度

2.通过PWM信号控制PWMA和PWMB可以调节电机的转速

3.YSTB用于控制模块的工作状态

4.VM和VCC分别提供电机驱动和逻辑电路的电源

5.该驱动模块为高电平时有效,否则,直流电机处于停止状态

具体参考本篇第1.5节

(本篇参考stm32学习探究:利用TB6612驱动直流电机-CSDN博客)

6.2工程

6.2.1创建工程

创建HC-SR04文件夹副本并改名为MOTOR

6.2.2打开定时器TIM

我们使用定时器TIM输出PWM波

打开PA11和PA8的时钟(对应原理图)

6.2.3配置预分频系数

配置预分频系数即配置PWM输出频率(常用10KHz)(两个TIM输出频率相同,占位比可不相同)

公式以及参数:

F:频率

HCLK:主时钟(72M)

PSC+1:预分频系数

ARR:用于设置定时器的计数周期

10^4:10KHz

7200:PSC=0,ARR=7199(数字可适当不同分配,71和99有利于调占空比)

6.2.4配置占空比

公式以及参数

1:期望的比值(全速)

Pulse:信号

6.2.5配置控制方向GPIO

AIN1:PB13

AIN2:PB12

BIN1:PB14

BIN2:PB15

6.2.6创建模块文件

motor.c和motor.h

6.2.7代码

main.c

cpp 复制代码
#include "main.h"
#include "i2c.h"
#include "tim.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "oled.h"
#include "IIC.h"
#include "inv_mpu.h"
#include "inv_mpu_dmp_motion_driver.h"
#include "mpu6050.h"
#include "stdio.h"
#include "sr04.h"
#include "motor.h"



float pitch,roll,yaw; //定义三个角度的参数
uint8_t display_buf[20];  //显示缓存
extern float distance;

/* USER CODE END PV */

void SystemClock_Config(void);

int main(void)
{
 
  HAL_Init();

  SystemClock_Config();

  MX_GPIO_Init();
  MX_I2C1_Init();
  MX_TIM3_Init();
  MX_TIM1_Init();
  /* USER CODE BEGIN 2 */
	OLED_Init(); //初始化
	OLED_Clear();//清除
	
	MPU_Init(); 
	mpu_dmp_init();
	OLED_ShowString(0,00,"Init succeed",16);
	
	//使能PWM
	HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);
	HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_4);
	
	Load(1000,1000); //转速 ,注意:1000可能数值有点小,如果出现轮子不动的情况,请改大数值


 
  while (1)
  {
		HAL_Delay(100);
		mpu_dmp_get_data(&pitch,&roll,&yaw);
		

		sprintf((char*)display_buf, "pich:%.2f   ",roll);
		OLED_ShowString(0,2,display_buf,16);
		GET_Distance();
		sprintf((char*)display_buf, "distance:%.2f   ",distance);
		OLED_ShowString(0,4,display_buf,12);

  }

}

注意:1000可能数值有点小,如果出现轮子不动的情况,请改大数值

motor.c

cpp 复制代码
#include "motor.h"

extern TIM_HandleTypeDef htim1;

int abs(int p)
{
	if(p>0)
		return p;
	else
		return -p;
}

//参数:左右电机转速   范围-7200~7200
void Load(int moto1,int moto2)			
{
	//两个
	if(moto1<0) //左电机
	{
		//AIN1和AIN0一个高电平一个低电平才能驱动电机,BIN同理
		HAL_GPIO_WritePin(GPIOB,GPIO_PIN_13,GPIO_PIN_SET);
		HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_RESET);
	}
	else
	{
		HAL_GPIO_WritePin(GPIOB,GPIO_PIN_13,GPIO_PIN_RESET);
		HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_SET);
	}
	//配置占空比
	__HAL_TIM_SetCompare(&htim1,TIM_CHANNEL_4,abs(moto1));
	
	if(moto2<0)//右电机
	{
		HAL_GPIO_WritePin(GPIOB,GPIO_PIN_14,GPIO_PIN_SET);
		HAL_GPIO_WritePin(GPIOB,GPIO_PIN_15,GPIO_PIN_RESET);
	}
	else
	{
		HAL_GPIO_WritePin(GPIOB,GPIO_PIN_14,GPIO_PIN_RESET);
		HAL_GPIO_WritePin(GPIOB,GPIO_PIN_15,GPIO_PIN_SET);
	}
	__HAL_TIM_SetCompare(&htim1,TIM_CHANNEL_1,abs(moto2));
}

motor.h

cpp 复制代码
#ifndef _MOTOR_H
#define	_MOTOR_H

#include "stm32f1xx_hal.h"


void Load(int moto1, int moto2);

#endif

6.2.8现象

两个轮以一定速度转动


7.编码器测速

为了方便大家理解,本篇放入<调试篇>讲解,谢谢大家

请大家下载 6.Encoder代码以便蓝牙模块的测试


8.蓝牙模块

8.1JDY-31蓝牙模块

本项目使用JDY-31蓝牙模块

8.2模块原理图

8.3工程

8.3.1创建Uart文件

将Encoder的副本重命名为Uart

8.3.2配置USART

1.打开PB10、PB11引脚,并设置为USART

2.设置基本的串口通信模式Asynchronous(异步)

3.打开全局中断(设备需要接收信息,需要打开全局中断)

记录和确保两台设备波特率相同

(参考8.1的数据)

Tx:接收数据

Rx:发送数据

(具体可参考1.6USART串口通信)

8.3.3生成代码

8.3.4发送数据

两种发送数据方式

8.3.5缓存数据

在 stm32f1xx_it.c中定义缓存数据数组

8.3.6接收中断

打开接收中断

rx_buf:缓存数组

1:每接收到一个字节就进入一次中断

每次中断需要执行HAL_UART_Receive_IT

总结(必看):

本篇资源来自:

【草履虫都能学会的STM32平衡小车教程(基础篇)】https://www.bilibili.com/video/BV1Gc411v73h?p=8\&vd_source=066060e5c72e1d7ec627401517cd9584(源代码部分修改)

【[铁头山羊stm32入门教程] 课程介绍】https://www.bilibili.com/video/BV1dQ4y1J7pD?vd_source=066060e5c72e1d7ec627401517cd9584

本项目资料汇总:
链接:https://pan.baidu.com/s/1uZ7UJ_uKbrhnDnoxRR4gAw?pwd=Ucar
提取码:Ucar

作者留言:本人学生党,制作不易,字数共1w+,如有侵权,有错误或不恰当的地方,及时沟通

如有任何问题可在评论区提问,3小时内解答

2024.9.20-2024.9.26

相关推荐
小林熬夜学编程1 小时前
【Linux网络编程】第十四弹---构建功能丰富的HTTP服务器:从状态码处理到服务函数扩展
linux·运维·服务器·c语言·网络·c++·http
Jackey_Song_Odd1 小时前
C语言 单向链表反转问题
c语言·数据结构·算法·链表
A懿轩A3 小时前
C/C++ 数据结构与算法【数组】 数组详细解析【日常学习,考研必备】带图+详细代码
c语言·数据结构·c++·学习·考研·算法·数组
半盏茶香3 小时前
在21世纪的我用C语言探寻世界本质 ——编译和链接(编译环境和运行环境)
c语言·开发语言·c++·算法
字节高级特工4 小时前
【C++】深入剖析默认成员函数3:拷贝构造函数
c语言·c++
计算机学长大白5 小时前
C中设计不允许继承的类的实现方法是什么?
c语言·开发语言
yutian06068 小时前
Keil MDK下载程序后MCU自动重启设置
单片机·嵌入式硬件·keil
XH华10 小时前
初识C语言之二维数组(下)
c语言·算法
析木不会编程11 小时前
【小白51单片机专用教程】protues仿真独立按键控制LED
单片机·嵌入式硬件·51单片机
Uu_05kkq13 小时前
【C语言1】C语言常见概念(总结复习篇)——库函数、ASCII码、转义字符
c语言·数据结构·算法