基于stm32f103c8t6的蓝牙小车(可以控制车速,以及有数码管显示速度)

蓝牙模块的理解:

蓝牙可以理解为一个无线的串口,手机和单片机之间可以通过蓝牙来发送数据,控制单片机IO口,进而来控制小车,总体的逻辑是,手机发送数据给蓝牙,蓝牙将这个数据再发送给单片机。另外蓝牙的代码跟我们学的串口通信差不多。

usart2.c

#include "usart2.h"	  
 	 
void uart2_init( u32 bound )
{
	/* GPIO端口设置 */
	GPIO_InitTypeDef	GPIO_InitStructure;
	USART_InitTypeDef	USART_InitStructure;
 
 
	RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE ); 
	RCC_APB1PeriphClockCmd( RCC_APB1Periph_USART3, ENABLE );         /* 使能USART3,GPIOA时钟 */
 
	/* PB10 TXD3 */
	GPIO_InitStructure.GPIO_Pin	= GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Speed	= GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode	= GPIO_Mode_AF_PP;
	GPIO_Init( GPIOB, &GPIO_InitStructure );
 
	/* PB11 RXD3 */
	GPIO_InitStructure.GPIO_Pin	= GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Mode	= GPIO_Mode_IN_FLOATING;
	GPIO_Init( GPIOB, &GPIO_InitStructure );
 
 
	/* USART 初始化设置 */
	USART_InitStructure.USART_BaudRate		= bound;                                /* 串口波特率 */
	USART_InitStructure.USART_WordLength		= USART_WordLength_8b;                  /* 字长为8位数据格式 */
	USART_InitStructure.USART_StopBits		= USART_StopBits_1;                     /* 一个停止位 */
	USART_InitStructure.USART_Parity		= USART_Parity_No;                      /* 无奇偶校验位 */
	USART_InitStructure.USART_HardwareFlowControl	= USART_HardwareFlowControl_None;       /* 无硬件数据流控制 */
	USART_InitStructure.USART_Mode			= USART_Mode_Rx | USART_Mode_Tx;        /* 收发模式 */
 
	USART_Init( USART3, &USART_InitStructure );                                             /* 初始化串口1 */
	USART_Cmd( USART3, ENABLE );                                                            /* 使能串口 2 */
}

这里就是我的蓝牙模块的初始化代码,我用的是串口3,对应的RXD引脚是PB11,TXD引脚是PB10,详情看上面的代码。

蓝牙App的制作开发

相信很多小伙伴做这个项目更多是喜欢自己做一个遥控器,实现“自己生产化”,所以我就没有使用配套资料给我的app,自己做了一个。可以给大家参考下我自己制作的一个app

怎么制作这个app呢,我这个有链接,小伙伴可以自己去观看

EDA的蓝牙app制作视频

电机驱动模块:

stm32因为输出的电流电压比较小,所以我们 需要使用L298N模块来驱动电机,我这里使用的是直流电机。我大概说说怎么使用,L298N有4个IN端口,4个OUT端口,OUT是接电机的,OUT1 2是接一个电机的正负极,另外 OUT3 4接另外一个电机。IN端口接单片机IO端口。,输入不同的值可以让电机正反转,同时可以使用PWM模式来控制电机的转速。

具体的视频,我推荐一下链接,博主用的是51来讲解的,原理差不多,很容易懂:

L298N讲解

PWM调速

PWM设置占空比

这个板块针对的是一些小伙伴不怎么会使用stm32设置占空比的问题,我不过多讲述,我提供一个博主的视频,对大家有很大的帮助:

PWM设置

PWM.c

#include "stm32f10x.h"                  // Device header

void PWM_Init(void)
{
	    GPIO_InitTypeDef GPIO_InitStructure;
      TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
      TIM_OCInitTypeDef TIM_OCInitStructure;

      //使能定时器TIM4时钟,注意TIM4时钟为APB1
      RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
      //使能PWM输出GPIO时钟
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE); 
                                                                          
      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;//定时器TIM4的PWM输出通道1,TIM4_CH1
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
      GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO

      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;//定时器TIM4的PWM输出通道2,TIM4_CH2
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
      GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO

      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;//定时器TIM4的PWM输出通道3,TIM4_CH3
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
      GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO

      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;//定时器TIM4的PWM输出通道4,TIM4_CH4
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
      GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO

      TIM_TimeBaseStructure.TIM_Period = 100 - 1;//arr;//自动重装值
      TIM_TimeBaseStructure.TIM_Prescaler =36 - 1;//psc; //时钟预分频数
      TIM_TimeBaseStructure.TIM_ClockDivision = 0;
      TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//TIM向上计数模式
      TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //初始化TIM4
     
      //初始化TIM4_CH1的PWM模式
      TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//??PWM??1
      TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//??????
      TIM_OCInitStructure.TIM_Pulse = 0; //
      TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//??????
      TIM_OC1Init(TIM4, &TIM_OCInitStructure);//???TIM4_CH1

      //初始化TIM4_CH2的PWM模式
      TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
      TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
      TIM_OCInitStructure.TIM_Pulse = 0;
      TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
       //TIM4_CH2初始化,OC2
      TIM_OC2Init(TIM4, &TIM_OCInitStructure);

       //初始化TIM4_CH3的PWM模式
      TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
      TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
      TIM_OCInitStructure.TIM_Pulse = 0;
      TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
      TIM_OC3Init(TIM4, &TIM_OCInitStructure);

      //初始化TIM4_CH4的PWM模式
      TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
      TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
      TIM_OCInitStructure.TIM_Pulse = 0;
      TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
      TIM_OC4Init(TIM4, &TIM_OCInitStructure);

      //使能4个通道的预装载寄存器
      TIM_OC1PreloadConfig(TIM4, TIM_OCPreload_Enable);//OC1
      TIM_OC2PreloadConfig(TIM4, TIM_OCPreload_Enable);//OC2
      TIM_OC3PreloadConfig(TIM4, TIM_OCPreload_Enable);//OC3
      TIM_OC4PreloadConfig(TIM4, TIM_OCPreload_Enable);//OC4
      TIM_ARRPreloadConfig(TIM4, ENABLE); //使能重装寄存器

      TIM_Cmd(TIM4, ENABLE);//使能定时器TIM4,准备工作 
}

数码管显示车速:

!!!!注意我使用的是共阳极数码管

LED.c

#include "stm32f10x.h"                  // Device header
#include "LED.h"
void LED_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_1 | GPIO_Pin_2|GPIO_Pin_0 |GPIO_Pin_3 |GPIO_Pin_4 |GPIO_Pin_5 |GPIO_Pin_6|GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	//灯暗
	GPIO_SetBits(GPIOA, GPIO_Pin_1 | GPIO_Pin_2|GPIO_Pin_0 |GPIO_Pin_3 |GPIO_Pin_4 |GPIO_Pin_5 |GPIO_Pin_6|GPIO_Pin_7);
}

void s0(void){
  // 设置所有引脚为低电平
  GPIO_ResetBits(GPIOA, GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_3);
  // 设置第7引脚为高电平
  GPIO_SetBits(GPIOA, GPIO_Pin_7| GPIO_Pin_6 );
}

void s1(void){
  // 设置所有引脚为高电平,除了第0引脚和第2引脚
  GPIO_SetBits(GPIOA, GPIO_Pin_0 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7);
  // 设置第0引脚和第2引脚为低电平
  GPIO_ResetBits(GPIOA, GPIO_Pin_1 | GPIO_Pin_2);
}

void s2(void){
  // 设置所有引脚为高电平,除了第0引脚和第2引脚
  GPIO_SetBits(GPIOA, GPIO_Pin_2 | GPIO_Pin_5 | GPIO_Pin_7);
  // 设置第0引脚和第2引脚为低电平
  GPIO_ResetBits(GPIOA, GPIO_Pin_1 | GPIO_Pin_3| GPIO_Pin_4| GPIO_Pin_6| GPIO_Pin_0);
}

void s3(void){
  // 设置所有引脚为高电平,除了第0引脚和第2引脚
  GPIO_SetBits(GPIOA, GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7);
  // 设置第0引脚和第2引脚为低电平
  GPIO_ResetBits(GPIOA, GPIO_Pin_1 | GPIO_Pin_3| GPIO_Pin_2| GPIO_Pin_6| GPIO_Pin_0);
}

void s4(void){
  // 设置所有引脚为高电平,除了第0引脚和第2引脚
  GPIO_SetBits(GPIOA, GPIO_Pin_0 | GPIO_Pin_3 | GPIO_Pin_7| GPIO_Pin_4);
  // 设置第0引脚和第2引脚为低电平
  GPIO_ResetBits(GPIOA, GPIO_Pin_1 | GPIO_Pin_2| GPIO_Pin_5| GPIO_Pin_6);
}

void s5(void){
  // 设置所有引脚为高电平,除了第0引脚和第2引脚
  GPIO_SetBits(GPIOA,  GPIO_Pin_1 | GPIO_Pin_7| GPIO_Pin_4);
  // 设置第0引脚和第2引脚为低电平
  GPIO_ResetBits(GPIOA, GPIO_Pin_2| GPIO_Pin_5| GPIO_Pin_6|GPIO_Pin_0 |GPIO_Pin_3);
}


void s6(void){
  // 设置所有引脚为高电平,除了第0引脚和第2引脚
  GPIO_SetBits(GPIOA,  GPIO_Pin_1 | GPIO_Pin_7 );
  // 设置第0引脚和第2引脚为低电平
  GPIO_ResetBits(GPIOA, GPIO_Pin_2| GPIO_Pin_5| GPIO_Pin_6|GPIO_Pin_0 |GPIO_Pin_3| GPIO_Pin_4);
}

void s7(void){
  // 设置所有引脚为高电平,除了第0引脚和第2引脚
  GPIO_SetBits(GPIOA,  GPIO_Pin_3| GPIO_Pin_5| GPIO_Pin_6|GPIO_Pin_4 |GPIO_Pin_7 );
  // 设置第0引脚和第2引脚为低电平
  GPIO_ResetBits(GPIOA, GPIO_Pin_2| GPIO_Pin_1|GPIO_Pin_0);
}

void sz8(void){
  // 设置所有引脚为高电平,除了第0引脚和第2引脚
  GPIO_SetBits(GPIOA,  GPIO_Pin_7 );
  // 设置第0引脚和第2引脚为低电平
  GPIO_ResetBits(GPIOA, GPIO_Pin_3| GPIO_Pin_5| GPIO_Pin_6|GPIO_Pin_4 |GPIO_Pin_2| GPIO_Pin_1|GPIO_Pin_0);
}


void s9(void){
  // 设置所有引脚为高电平,除了第0引脚和第2引脚
  GPIO_SetBits(GPIOA,  GPIO_Pin_7|GPIO_Pin_4  );
  // 设置第0引脚和第2引脚为低电平
  GPIO_ResetBits(GPIOA, GPIO_Pin_3| GPIO_Pin_5| GPIO_Pin_6|GPIO_Pin_2| GPIO_Pin_1|GPIO_Pin_0);
}


 
 

按键模块:

我需要对这里进行解释说明,我的板子上面有一个按钮,按下去,才可以正常使用车的功能,所以这里有一个按键模块

KEY.c

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

void Key_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
}

uint8_t Key_GetNum(void)
{
	if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_15) == 0)//检测按键有没有按下
	{
		Delay_ms(20);
		while (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_15) == 0);
		Delay_ms(20);
		return 1;
	}
	return 0;
}



小车运动模块

这里我需要解释一下,有些朋友可能会疑惑,只有电机,车怎么左右转起来的,其实这里有个秘诀,如果要左转的话,我只需要让左边的轮子不动或者向后转动,让右边的轮子转动起来就可以,简单概括就是右边的轮子要比左边的轮子运动快就可以实现了,然后我的一个轮子是自由轮子,具体看图:

上面那个是自由轮子

主函数模块:

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "robot.h"
#include "Key.h"
#include "LED.h"
uint8_t i;
uint16_t flag_key=0;
uint8_t state;
void speed_con(int t){
  if(state=='s'){makerobo_Spin_Left(t);}
	else if(state=='f'){makerobo_run(t);}
	else if(state=='t'){makerobo_Spin_Right(t);}
	else if(state=='l'){makerobo_Left(t);}
	else if(state=='p'){makerobo_brake();}
	else if(state=='r'){makerobo_Right(t);}
}
int main(void)
{
	int data;
	LED_Init();
	robot_Init();    // 机器人初始化
	Key_Init();      // 按键初始化
	uart2_init(9600);
	while (1)
	{
		if(Key_GetNum() == 1)
		{
			flag_key=!flag_key;
		}
		if(flag_key==1){
			if(USART_GetFlagStatus(USART3,USART_FLAG_RXNE)==1)     
		  {
			data=USART_ReceiveData(USART3);
			switch(data){//检测运动状态按键是否按下
				case 10:  //case多少是根据我设计的那个app决定的所以前面的视频需要先看一下
					makerobo_Spin_Left(100);
				  s9();
				  state='s';//state是记录车子当前运动的状态,方便后面我给他设置挡位时候,数码管可以显示出来
				break;
				case 11:
					makerobo_run(100);
				  s9();
					state='f';
				break;
				case 12:
					makerobo_Spin_Right(100);
					s9();
				  state='t';
				break;
				case 13:
					makerobo_Left(100);
					s9();
				  state='l';
				break;
				case 14:
					makerobo_brake();
				  s9();
				  state='p';
				break;
				case 15:
					makerobo_Right(100);
					s9();
				  state='r';
				break;
				case 16:
					makerobo_back(100);
				  s9();
				  state='b';
				break;		
			}
			if(data==1|data==2|data==3|data==4|data==5|data==6|data==7|data==8|data==9){ //检测挡位按键是否按下
				speed_con(data*10);
				if(data==1) s1(); //占空比10%,记为1档
				if(data==2) s2();
				if(data==3) s3();
				if(data==4) s4();
				if(data==5) s5();
				if(data==6) s6();
				if(data==7) s7();
				if(data==8) sz8();
				if(data==9) s9();
			}
		}
	}
}
	}

开发板原理图:

各位朋友,请给我点点免费的赞 谢谢支持啦

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值