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

前言

这周刚好做了一个小项目,需要用到单片机控制一个小车移动,在实验室搜刮了一些材料,进行了一些调试工作,感觉也是蛮有意思的。小车的底盘用的是之前电赛剩下的,单片机用的是最小系统板,蓝牙模块是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电池,应该能跑起来吧。

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 以下是基于STM32F103C8T6TB6612FNG的电机转动代码示例,你可以根据实际情况进行修改: ```c #include "stm32f10x.h" #define AIN1_PIN GPIO_Pin_0 #define AIN2_PIN GPIO_Pin_1 #define BIN1_PIN GPIO_Pin_2 #define BIN2_PIN GPIO_Pin_3 #define STBY_PIN GPIO_Pin_4 void GPIO_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitStructure.GPIO_Pin = AIN1_PIN | AIN2_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = BIN1_PIN | BIN2_PIN | STBY_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_ResetBits(GPIOA, AIN1_PIN | AIN2_PIN); GPIO_ResetBits(GPIOB, BIN1_PIN | BIN2_PIN | STBY_PIN); } void Motor_A_Forward(void) { GPIO_SetBits(GPIOA, AIN1_PIN); GPIO_ResetBits(GPIOA, AIN2_PIN); } void Motor_A_Backward(void) { GPIO_ResetBits(GPIOA, AIN1_PIN); GPIO_SetBits(GPIOA, AIN2_PIN); } void Motor_A_Stop(void) { GPIO_ResetBits(GPIOA, AIN1_PIN | AIN2_PIN); } void Motor_B_Forward(void) { GPIO_SetBits(GPIOB, BIN1_PIN); GPIO_ResetBits(GPIOB, BIN2_PIN); } void Motor_B_Backward(void) { GPIO_ResetBits(GPIOB, BIN1_PIN); GPIO_SetBits(GPIOB, BIN2_PIN); } void Motor_B_Stop(void) { GPIO_ResetBits(GPIOB, BIN1_PIN | BIN2_PIN); } int main(void) { GPIO_Configuration(); while (1) { Motor_A_Forward(); Motor_B_Forward(); GPIO_SetBits(GPIOB, STBY_PIN); delay_ms(1000); Motor_A_Backward(); Motor_B_Backward(); GPIO_SetBits(GPIOB, STBY_PIN); delay_ms(1000); Motor_A_Stop(); Motor_B_Stop(); GPIO_ResetBits(GPIOB, STBY_PIN); delay_ms(1000); } } void delay_ms(uint32_t ms) { uint32_t i, j; for (i = 0; i < ms; i++) for (j = 0; j < 9000; j++); } ``` 注:以上代码仅供参考,具体实现要根据硬件电路和实际需求进行修改。 ### 回答2: 要控制TB6612FNG电机转动,可以使用STM32F103C8T6PWMB开发板和相应的代码。 首先,需要配置STM32F103C8T6PWMB的GPIO引脚作为输出模式,用于控制TB6612FNG电机驱动模块。接下来,可以使用PWM信号控制电机的转速。 首先,初始化PB8、PB9、PB10、PB11引脚作为输出模式,用于控制电机的IN1、IN2、IN3、IN4输入。代码如下: ``` RCC->APB2ENR |= RCC_APB2ENR_IOPBEN; // 使能GPIOB时钟 GPIOB->CRH &= ~GPIO_CRH_CNF8_0; // 选择推挽输出模式 GPIOB->CRH |= GPIO_CRH_MODE8; // 设置输出模式为10MHz GPIOB->CRH &= ~GPIO_CRH_CNF9_0; GPIOB->CRH |= GPIO_CRH_MODE9; GPIOB->CRL &= ~GPIO_CRL_CNF10_0; GPIOB->CRL |= GPIO_CRL_MODE10; GPIOB->CRL &= ~GPIO_CRL_CNF11_0; GPIOB->CRL |= GPIO_CRL_MODE11; ``` 然后,可以使用PWM信号控制电机转速,使用TIM3定时器和通道3和通道4来产生PWM信号。代码如下: ``` RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; // 使能TIM3时钟 GPIOB->CRL &= ~GPIO_CRL_CNF0_0; // PB0配置为复用输出 GPIOB->CRL |= GPIO_CRL_CNF0_1; GPIOB->CRL |= GPIO_CRL_MODE0_0; GPIOB->CRL &= ~GPIO_CRL_MODE0_1; TIM3->PSC = 0; // 设置预分频系数为1 TIM3->ARR = 255; // 设置自动重载寄存器的值为255 TIM3->CCR3 = 128; // 设置通道3占空比初始值为50% TIM3->CCR4 = 0; // 设置通道4占空比初始值为0% TIM3->CCMR2 |= TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1; // PWM模式1 TIM3->CCMR2 &= ~TIM_CCMR2_OC3M_0; TIM3->CCMR2 |= TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4M_1; TIM3->CCMR2 &= ~TIM_CCMR2_OC4M_0; TIM3->CCER |= TIM_CCER_CC3E; // 使能通道3输出 TIM3->CCER |= TIM_CCER_CC4E; // 使能通道4输出 TIM3->CR1 |= TIM_CR1_CEN; // 使能定时器 ``` 以上代码是一个简单的示例,可以根据需要进行修改和扩展。在这个示例中,GPIOB引脚8至11配置为输出模式,用于控制TB6612FNG电机驱动模块的输入信号。TIM3定时器配置为PWM模式,使用通道3控制IN1输入信号,通道4控制IN2输入信号,以控制电机转速。 这是一个基本的代码框架,你可以根据具体的电机和需求进行相应的调整和优化。这里只是提供了一个简单的示例作为参考。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值