当前位置:   article > 正文

基于STM32的蓝牙遥控小车:手机APP控制小车运动_stm32蓝牙控制小车

stm32蓝牙控制小车

在这里插入图片描述

一、智能双轮小车+蓝牙APP控制前后左右调速运动

功能描述

1、小车可以前后左右运动
2、可以调节小车的速度
3、使用两个电机驱动两个轮子,一个万向轮跟随运动
4、需要自己设计电机驱动电路,达到最小成本 (不超过1.5元)
5、设计一个手机APP可以蓝牙连接小车,进行控制

原理图设计

1、单片机最小系统电路,采用stm32f103c6t6单片机节省成本,包含复位电路、供电电路,下载接口
在这里插入图片描述

2、电源设计,电源可以用USB供电,也可以外接电池供电,4-7V都可以
加粗样式3、蓝牙模块电路,用来和手机进行通信,下发指令控制小车进行运动,当然小车也可以传输消息到手机进行显示,比如电量、速度等信息
在这里插入图片描述

4、电机驱动电路,这里是用了一个非常便宜的驱动芯片,大约1元一片的DRV8833,搭建一下外围电路就可以工作了,接线端子接入电机就可以正常驱动电机运转

在这里插入图片描述

PCB设计

下面就是我设计的PCB了,体积是非常小巧的,焊接好接上电机就可以正常使用啦
在这里插入图片描述
这是搭载到小车上的效果
在这里插入图片描述
小车的视频可以留意哔哩哔哩或者到我的咸鱼查看,账号名都是:化作尘my

源码设计

1、主函数代码设计
首先要初始化延时、串口、GPIO、电机驱动等外设
先让电机停止运转,while循环里面等待蓝牙指令处理

int main(void)
{
    delay_init();	    	 //延时函数初始化
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
    uart_init(9600);	 	//串口初始化为115200
	usart2_init(usart2_baund);//串口2初始化为usart2_baund
    printf("uart_init OK\r\n");
    GPIO_Config();  //GPIO初始化
	Drv8833_Init(); //电机驱动初始化  
	set_drv8833_para(AIN_NUM,DIR_STOP,0);//停止电机转动 
	set_drv8833_para(BIN_NUM,DIR_STOP,0);//停止电机转动 
    while(1)
	{
		Query_IN_Response(); //蓝牙串口指令处理
        delay_ms(1);
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

2、电机驱动代码
这里由于电机只有两个,比较少,为了灵活运用普通IO口驱动电机,我使用了GPIO模拟输出PWM的方法,用一个定时器定时10us,并设定参数让IO口在定时中断里面定时翻转,比如100个周期内高电平为50个低电平为50个,那么就得到一个周期为1ms,脉宽为50%的PWM

//驱动接口初始化
void Drv8833_Init(void)
{
	 GPIO_InitTypeDef  GPIO_InitStructure;
 	
	 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	 
	 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	 
	 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);	 
	 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);	 
	 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
	 GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable , ENABLE);  	

	
	 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
	 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO口速度为50MHz
	
	#if 0
	TIM_PWM_Init();
	#else
	 GPIO_InitStructure.GPIO_Pin = nSLEEP_PIN;			
	 GPIO_Init(nSLEEP_PORT, &GPIO_InitStructure);					 
	 nSLEEP_DISABLE();
	#endif
#if (DRV8833_NUM >= 1)
	 GPIO_InitStructure.GPIO_Pin = AIN1_PIN;			
	 GPIO_Init(AIN1_PORT, &GPIO_InitStructure);					 
	 AIN1_L();				
	
	 GPIO_InitStructure.GPIO_Pin = AIN2_PIN;			
	 GPIO_Init(AIN2_PORT, &GPIO_InitStructure);					 
	 AIN2_L();				
	
	 GPIO_InitStructure.GPIO_Pin = BIN1_PIN;			
	 GPIO_Init(BIN1_PORT, &GPIO_InitStructure);					 
	 BIN1_L();				
	
	 GPIO_InitStructure.GPIO_Pin = BIN2_PIN;			
	 GPIO_Init(BIN2_PORT, &GPIO_InitStructure);					 
	 BIN2_L();		

#endif

#if (DRV8833_NUM >= 2)
	 GPIO_InitStructure.GPIO_Pin = CIN1_PIN;			
	 GPIO_Init(CIN1_PORT, &GPIO_InitStructure);					 
	 CIN1_L();				
	
	 GPIO_InitStructure.GPIO_Pin = CIN2_PIN;			
	 GPIO_Init(CIN2_PORT, &GPIO_InitStructure);					 
	 CIN2_L();				
	
	 GPIO_InitStructure.GPIO_Pin = DIN1_PIN;			
	 GPIO_Init(DIN1_PORT, &GPIO_InitStructure);					 
	 DIN1_L();				
	
	 GPIO_InitStructure.GPIO_Pin = DIN2_PIN;			
	 GPIO_Init(DIN2_PORT, &GPIO_InitStructure);					 
	 DIN2_L();	
#endif
    TIM2_Int_Init(100 - 1, 1);		//10Us中断
}

//定时器2中断服务程序
void TIM2_IRQHandler(void)   //TIM2中断
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源 
	{
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update  );  //清除TIMx的中断待处理位:TIM 中断源 
		set_drv8833_ctrl();
	}
}

//设置DRV8833的参数 num  电机编号  dir 前后   speed 速度
void set_drv8833_para(WEL_NUM num,DIR_TypeDef dir,u8 speed)
{
	drv[num].drv_speed = speed;
	drv[num].drv_dir = dir;
}
	
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79

3、蓝牙控制部分代码,这里把接收到手机发过来的摇杆数据,处理成每个电机的速度、前后。

u8 Query_IN_Response(void)
{
    u8 buff[USART2_MAX_RECV_LEN] = {0}; //接收数据缓冲区
    u8 recv_flag = 0;                    // 1:接收完成
    u16 Num = 0;
    u8 retn = 0;
    u8 i;
    u8 *ptemp = buff;
    if (USART2_RX_STA & 0x8000)          //接收完成
    {
        recv_flag = 1;
        Num = USART2_RX_STA & 0x3fff;
        memcpy(buff, (const char *)USART2_RX_BUF, Num); //拷贝数据
        USART2_RX_BUF[Num] = '\0';
        buff[Num] = '\0';        //加上字符串结束符;
        USART2_RX_STA = 0;        //清楚接收完成标志
        {
//            printf("\r\n接收完成: \"");
//            for(i = 0; i < Num; i++)
//                printf("%c", buff[i]);
//            printf("\"\r\n");
        }
    }
    if (recv_flag) //接收完成
    {
        if (strncmp((const char *)ptemp, "SPEED:", sizeof("SPEED:") - 1) == 0) //接收到正确的指令
        {
            retn = 1;
        } else if (strncmp((const char *)ptemp, "DIR:", sizeof("DIR:") - 1) == 0) //接收到正确的指令
        {
			
            retn = 2;
        } else if (strncmp((const char *)ptemp, "ROCKER:", sizeof("ROCKER:") - 1) == 0) //接收到正确的指令
        {
            ptemp += sizeof("ROCKER:[") - 1;
            sysFlag.rocker.x = atoi((const char *)ptemp);    //读取
			while(ptemp != NULL && *ptemp != '\0')
			{
				ptemp++;
				if(*ptemp == ',')
				{
					sysFlag.rocker.y = atoi((const char *)(ptemp+1));    //读取
					break;
				}
			}
			if(sysFlag.rocker.y < 0)
			{
				if(sysFlag.rocker.x>0)
				{
					int speed1,speed2;
					if(sysFlag.rocker.x<PIANYI)sysFlag.rocker.x = 0;
					else sysFlag.rocker.x -= PIANYI;
					 
					speed1 = (abs(sysFlag.rocker.y)+sysFlag.rocker.x/2);
					if(speed1 > 100 )speed1 = 100;
					speed2 = (abs(sysFlag.rocker.y)-sysFlag.rocker.x/2);
					if(speed2 < 0 )speed2 = 0;
					set_drv8833_para(AIN_NUM,DIR_FRONT,speed1);
					set_drv8833_para(BIN_NUM,DIR_FRONT,speed2);
					printf("右前 speed %d \t%d\r\n",speed1,speed2);
				}else
				{
					int speed1,speed2;
					if(sysFlag.rocker.x>-PIANYI)sysFlag.rocker.x = 0;
					else sysFlag.rocker.x += PIANYI;
					speed1 = (abs(sysFlag.rocker.y)+sysFlag.rocker.x/2);
					if(speed1 < 0 )speed1 = 0;
					speed2 = (abs(sysFlag.rocker.y)-sysFlag.rocker.x/2);
					if(speed2 > 99 )speed2 = 100;
					set_drv8833_para(AIN_NUM,DIR_FRONT,speed1);
					set_drv8833_para(BIN_NUM,DIR_FRONT,speed2);
					printf("左前 speed %d \t%d\r\n",speed1,speed2);
				}
			}else if(sysFlag.rocker.y > 0)
			{
				if(sysFlag.rocker.x>0)
				{
					int speed1,speed2;
					if(sysFlag.rocker.x<PIANYI)sysFlag.rocker.x = 0;
					else sysFlag.rocker.x -= PIANYI;
					speed1 = (abs(sysFlag.rocker.y)+sysFlag.rocker.x/2);
					if(speed1 > 100 )speed1 = 100;
					speed2 = (abs(sysFlag.rocker.y)-sysFlag.rocker.x/2);
					if(speed2 < 0 )speed2 = 0;
					set_drv8833_para(AIN_NUM,DIR_BACK,speed1);
					set_drv8833_para(BIN_NUM,DIR_BACK,speed2);
					printf("右后 speed %d \t%d\r\n",speed1,speed2);
				}else
				{
					int speed1,speed2;
					if(sysFlag.rocker.x>-PIANYI)sysFlag.rocker.x = 0;
					else sysFlag.rocker.x += PIANYI;
					speed1 = (abs(sysFlag.rocker.y)+sysFlag.rocker.x/2);
					if(speed1 < 0 )speed1 = 0;
					speed2 = (abs(sysFlag.rocker.y)-sysFlag.rocker.x/2);
					if(speed2 > 99 )speed2 = 100;
					set_drv8833_para(AIN_NUM,DIR_BACK,speed1);
					set_drv8833_para(BIN_NUM,DIR_BACK,speed2);
					printf("左后 speed %d \t%d\r\n",speed1,speed2);
				}
			}else if(sysFlag.rocker.y == 0 && sysFlag.rocker.x == 0)
			{
				set_drv8833_para(AIN_NUM,DIR_STOP,0);
				set_drv8833_para(BIN_NUM,DIR_STOP,0);
			}
//			printf("rocker[%d, %d ]",sysFlag.rocker.x,sysFlag.rocker.y);
            retn = 3;
        }
        if(retn)
        {
//            printf("处理OK\r\n");
            sysFlag.drv_chg_flag = 1;
        }
    }
    return retn;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116

5、手机APP设计
手机APP是使用的QT5设计,大家要是想要做这个项目的话一定要先下载一个APP看看是否可以搜索到蓝牙,因为有少部分网友说他的手机用这个APP是搜索不到蓝牙的,导致用不了这个APP进行连接,解决方法就是要换一个手机

在这里插入图片描述
在这里插入图片描述

二、更多功能小车网友推荐后开发…

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/621641
推荐阅读
相关标签
  

闽ICP备14008679号