首页 > 其他分享 >【记录】stm32f103c8t6+hc05+TB6612FNG实现蓝牙app控制直流电机

【记录】stm32f103c8t6+hc05+TB6612FNG实现蓝牙app控制直流电机

时间:2024-07-20 19:55:29浏览次数:8  
标签:Pin USART app TB6612FNG TIM hc05 Motor GPIO void

前言

这周刚好做了一个小项目,需要用到单片机控制一个小车移动,在实验室搜刮了一些材料,进行了一些调试工作,感觉也是蛮有意思的。小车的底盘用的是之前电赛剩下的,单片机用的是最小系统板,蓝牙模块是hc05,直流电机也是最普通的小马达。

软硬件

调试软件:keil5

主控板:stm32f103c8t6

蓝牙模块:HC-05

电机驱动模块:TB6612

app:蓝牙调试宝

驱动电机

下图是TB6612驱动模块原理图

AIN1/AIN2,BIN1/BIN2,PWMA/PWMB为控制信号输入端,AO1/AO2,BO1/BO2为两路电机控制输出端。STBY正常工作接5v电压,VCC接3.3v,VM接5v,GND接地。

引脚接线情况:

TB6612单片机
VM5V
VCC3.3V
GNDGND
PWMAA2
PWMBA3
AIN1A4
AIN2A5
BIN1A6
BIN2A7
AO1电机1(M+)
AO2电机1(M-)
BO1电机2(M+)
BO2电机2(M-)

定时器的PWM模式驱动电机

Motor.h

#ifndef __MOTOR_H
#define __MOTOR_H

void Motor_Init(void);
void Motor_SetSpeed(int8_t Speed);
void Motor_SetSpeed2(int8_t Speed);


void Bluetooth_Receive_Handler(uint8_t data);


#endif

Motor.c 

#include "stm32f10x.h"                  // Device header
#include "PWM.h"

void Motor_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	PWM_Init();
}

void Motor_SetSpeed(int8_t Speed)
{
	if (Speed >= 0)
	{
		GPIO_SetBits(GPIOA, GPIO_Pin_4);
		GPIO_ResetBits(GPIOA, GPIO_Pin_5);
		PWM_SetCompare3(Speed);
	}
	else
	{
		GPIO_ResetBits(GPIOA, GPIO_Pin_4);
		GPIO_SetBits(GPIOA, GPIO_Pin_5);
		PWM_SetCompare3(-Speed);
	}
}


void Motor_SetSpeed2(int8_t Speed)
{
	if (Speed >= 0)
	{
		GPIO_SetBits(GPIOA, GPIO_Pin_6);
		GPIO_ResetBits(GPIOA, GPIO_Pin_7);
		PWM_SetCompare4(Speed);
	}
	else
	{
		GPIO_ResetBits(GPIOA, GPIO_Pin_6);
		GPIO_SetBits(GPIOA, GPIO_Pin_7);
		PWM_SetCompare4(-Speed);
	}
}

pwm.h

#ifndef __PWM_H
#define __PWM_H

void PWM_Init(void);
void PWM_SetCompare3(uint16_t Compare);
void PWM_SetCompare4(uint16_t Compare);

#endif

pwm.c

#include "stm32f10x.h"                  // Device header

void PWM_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3; 
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	TIM_InternalClockConfig(TIM2);
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;		//ARR
	TIM_TimeBaseInitStructure.TIM_Prescaler = 36 - 1;		//PSC
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
	
	TIM_OCInitTypeDef TIM_OCInitStructure;
	TIM_OCStructInit(&TIM_OCInitStructure);
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
	TIM_OCInitStructure.TIM_Pulse = 0;		//CCR
	TIM_OC3Init(TIM2, &TIM_OCInitStructure);
	TIM_OC4Init(TIM2, &TIM_OCInitStructure);                  // 初始化TIM2的通道4
	
	TIM_Cmd(TIM2, ENABLE);
}

void PWM_SetCompare3(uint16_t Compare)
{
	TIM_SetCompare3(TIM2, Compare);
}

void PWM_SetCompare4(uint16_t Compare)
{
    TIM_SetCompare4(TIM2, Compare);   // 设置TIM2的通道4的捕获比较寄存器值
}

蓝牙模块HC05

引脚接线情况:

HC05单片机
RXDPA9
TXDPA10
GNDGND
VCCVCC(5v)

蓝牙控制我选择的是手机app控制,应用商店直接下载蓝牙调试宝。

连接成功后,在手机app上输入指令1,电机转动,输入0,电机停止。                                       

附上代码:

main.c

#include "main.h"
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "Motor.h"
#include "Key.h"
#include "USART1.h"

//时间结构体  在RTC_day.h中初始化了
//时间结构体定义在 RTC_day.h

//刷新时间标志
uint16_t TimeDisplay_cnt,TimeDisplay,T;
uint8_t KeyNum;
int8_t Speed;
int receivedChar =0;  // 默认接收到的0
	
int main(void)
{	
	 init_ALL();     //初始化所有函数
	 Motor_Init();           // 初始化电机
	 Motor_SetSpeed(0);     // 设置电机1速度
	 Motor_SetSpeed2(0);     //设置电机2速度

    while (1)
    {

		// 检查串口是否有数据可接收
        receivedChar =(int)USART_ReceiveData(USART1);  // 读取接收到的字符
        Delay_ms(200);

        // 根据接收到的字符控制电机
        if (receivedChar == 1)
        {
			Delay_ms(200);
            Motor_SetSpeed(100);    // 如果接收到数字1,电机1转动
			Motor_SetSpeed2(100);    // 如果接收到数字1,电机2转动
			Delay_ms(200);
        }
        else if (receivedChar == 0)
        {
			Delay_ms(200);
            Motor_SetSpeed(0);     // 如果接收到数字0,电机1停止
			Motor_SetSpeed2(0);     // 如果接收到数字0,电机2停止
			Delay_ms(200);
        }


    }
		
}


//初始化所有函数:
void init_ALL(void)
{
	SysTick_Init(72);         //初始化滴答计时器
	Timer2_Init();						//初始化定时器2
	Usart1_Init(115200);      //串口1初始化
	
}


//定时器2中断服务函数
void TIM2_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
	{		
		if(++TimeDisplay_cnt==100)  //定时器刷新时间
		{
		
		}
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);//清出中断寄存器标志位,用于退出中断
	}
}

 USART1.c:

#include "USART1.h"

void USART1_IRQHandler(void)
{
 
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断
	{USART_ClearFlag(USART1, USART_FLAG_RXNE);}
	//返回收到的任意数据
	USART_SendData(USART1,USART_ReceiveData(USART1)); 
	
}


///重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
		/* 发送一个字节数据到串口 */
		USART_SendData(USART1, (uint8_t) ch);
		
		/* 等待发送完毕 */
		while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);		
	
		return (ch);
}

///重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数
int fgetc(FILE *f)
{
		/* 等待串口输入数据 */
		while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
		return (int)USART_ReceiveData(USART1);
}


//选择串口发送数据--自定义Printf
void UsartPrintf (USART_TypeDef *USARTx, char *fmt,...)
{
 
	unsigned char UsartPrintfBuf[296];                                  //最大长度296
	va_list ap;
	unsigned char *pStr = UsartPrintfBuf;
	
	va_start(ap, fmt);
	vsnprintf((char *)UsartPrintfBuf, sizeof(UsartPrintfBuf), fmt, ap);	//格式化
	va_end(ap);
	
	while(*pStr != 0)
	{
		USART_SendData(USARTx, *pStr++);
		while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET);
	}
}



//发送一个字节 
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)
{
	//发送一个字节数据到USART 
	USART_SendData(pUSARTx,ch);
		
	// 等待发送数据寄存器为空 
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);	
}

//发送8位的数组
void Usart_SendArray( USART_TypeDef * pUSARTx, uint8_t *array, uint16_t num)
{
  uint8_t i;
	
	for(i=0; i<num; i++)
  {
	    //发送一个字节数据到USART
	    Usart_SendByte(pUSARTx,array[i]);	
  
  }
	//等待发送完成
	while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET);
}

//发送字符串
void Usart_SendString( USART_TypeDef * pUSARTx, char *str)
{
	unsigned int k=0;
  do 
  {
      Usart_SendByte( pUSARTx, *(str + k) );
      k++;
  } while(*(str + k)!='\0');
  
  //等待发送完成 
  while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET)
  {}
}

//发送一个16位数
void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch)
{
	uint8_t temp_h, temp_l;
	
	// 取出高八位 
	temp_h = (ch&0XFF00)>>8;
	//取出低八位 
	temp_l = ch&0XFF;
	
	//发送高八位 
	USART_SendData(pUSARTx,temp_h);	
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);

	//发送低八位 
	USART_SendData(pUSARTx,temp_l);	
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);	
}




不过上面的代码是很基础的,想让小车真正流畅地跑起来还需要做很多改进,电源模块用的是DC-DC模块。

下面测试一下电源模块:

下面是用蓝牙调试宝来控制电机:

不过,目前都是用杜邦线连接,看着不太美观,在嘉立创上画了免费的板子做一下集成,还没有到,到时候用上实验室的飞无人机的4s电池,应该能跑起来吧。

标签:Pin,USART,app,TB6612FNG,TIM,hc05,Motor,GPIO,void
From: https://blog.csdn.net/qq_45509667/article/details/140324458

相关文章

  • 关于service层自动生成mapper接口时为静态方法的解决办法
    在Service层自动生成Mapper方法的时候出现带方法体的静态方法而不是抽象方法,例如mapper中生成这样的方法:staticIntegerordersStatistics(Mapmaps){  }原因可能有两种1、mapper层未加mapper注解2、Service层调用的是Mapper类而不是使用Mapper生成的mapper对象错......
  • 居家养老小程序APP有哪些核心功能
    居家养老小程序或APP的核心功能通常围绕着提高老年人生活质量、保障其健康安全、以及加强家庭成员之间的联系而设计。以下是一些关键的核心功能:1.**紧急呼叫与救援**:  -提供一键紧急呼叫功能,可快速联系到预先设定的紧急联系人或医疗救援服务。  -GPS定位,允许家人或......
  • 基于SpringBoot+Vue+uniapp的公考客观题复习系统的详细设计和实现(源码+lw+部署文档+
    文章目录前言详细视频演示具体实现截图技术栈后端框架SpringBoot前端框架Vue持久层框架MyBaitsPlus系统测试系统测试目的系统功能测试系统测试结论为什么选择我代码参考数据库参考源码获取前言......
  • [实践篇]13.29 QNX下的系统性能监控工具 - sysMonAppQNX(一)
    一,sysMonAppQNX工具简介sysMonAppQNX,即SystemMonitorApplicationforQNX;它是有QNX开发的系统监控应用程序。它用于监控QNX系统的性能和健康状况。sysMonAppQNX可以监视各种指标,包括CPU使用率、内存使用率、磁盘I/O、网络流量和进程活动。它还可以生成警报以指示潜在......
  • 从零开始部署yolov8到安卓手机详细教程 ——使用YOLOV8大模型开发的物体检测Android手
    1.使用了yolov8大模型来进行物体检测android手机APP⒉.使用了coco数据集进行训练,app可以检测出“人类"∵"自行车"."汽车"∵"摩托车"."飞机","公共汽车"∵"火车","卡车"∵."船","红绿灯","消防栓","停车标志"∵,"停车收费表&......
  • java基础学习:序列化之 - ObjectMapper
    文章目录一、介绍二、主要功能三、使用方法官网:一、介绍ObjectMapper是Jackson库中的一个核心类,用于在Java对象和JSON数据之间进行转换。Jackson是一个流行的Java库,用于处理JSON数据。它提供了灵活的方式来序列化和反序列化Java对象,即将Java对象转换......
  • CMP-7000A - Applications Programming
    Module: CMP-7000A- Applications ProgrammingAssignment: R002-Game Coding and Testing PresentationLearningoutcomes•    Youwilldemonstratecompetence in using Pythonprogrammingskills by creatingandcodingyourown personalgameappl......
  • [MAUI 项目实战] 笔记App(二):数据库设计
    @目录Sqlite配置创建实体笔记实体类笔记分组实体笔记片段实体笔记片段负载实体笔记片段仓库实体笔记模板(场景)实体笔记片段模板实体笔记片段模板负载实体配置EF创建映射迁移和种子数据项目地址Sqlite配置应用程序里使用Sqlite作为数据库,使用EntityFramworkCore作为ORM,使用CodeFir......
  • app测试
    好几个月没有写博客记录学习心得了,这次回老家深夜闲来无事写一篇记录下这段时间的面试心得,这次面试过程很多面试官都问APP的有关测试,下面我就自己的认识和工作中的经验来谈谈自己对APP测试的认识:1.push消息推送测试检查push消息是否按照指定的业务规则发送。检查不接收推送消息......
  • 很呆的一个问题:我的新项目又找不到mapper这个bean了
    1.选springboot版本<properties><java.version>8</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outpu......