天天看點

STM32智能遙控小車,超詳細-附下載下傳直接可以用,雙電源跑賊快!

STM32智能遙控小車,超詳細-附下載下傳直接可以用,雙電源跑賊快!

簡介

  • 小車的動力部分由4個帶減速箱的電機,和兩個L298N電機驅動子產品組成。
  • 通過STM32核心闆控制電機驅動子產品,JDY-31藍牙子產品與手機通訊。
  • 總共三塊電池,一塊專門給單片機供電,另外兩塊串聯在一起同時給電機驅動子產品供電(為什麼選擇分别供電後面的電源硬體部分有說)。
  • 源碼下載下傳,提取碼:nxo3

硬體

  1. 硬體的基本介紹
    STM32智能遙控小車,超詳細-附下載下傳直接可以用,雙電源跑賊快!
  • 電機的位置很重要,因為電機驅動子產品一個可以分别控制兩個電機,而且程式裡面也是分别控制單個電機的動作。
  • 如果電機的和自己想要的運動方向不一樣的話,交換一下接線端的兩個線就好了。程式裡面小車前進預設是電機1、2CW(順時針),電機3、4CCW(逆時針)。
  1. L298N電機驅動子產品

    電機驅動子產品這裡不細講,網上有很多資料,很簡單的。

    下面這個是右邊的驅動闆,控制電機1、2。

    STM32智能遙控小車,超詳細-附下載下傳直接可以用,雙電源跑賊快!
    下面是左邊的驅動闆,控制電機3、4。
    STM32智能遙控小車,超詳細-附下載下傳直接可以用,雙電源跑賊快!
  • 兩個驅動闆是并聯的,供電時應選擇粗一點的導線給驅動闆供電,最好就不要使用杜邦線,如果使用杜邦線有可能會電壓不夠。
  • 有的時候電機可能要用手轉一下才能動,那是因為電壓太小了,可以串聯幾個電池。我是用一個小米充電寶和4節幹電池,電 壓應該在10V左右。
  • 注意不能串聯太多充電寶,因為充電寶上面有過電流保護,電流太大充電寶可能會斷電,甚至有可能會直接弄壞充電寶。
  • 如果是雙電源的話,單片機要和兩個驅動闆共地。
  • 電機驅動子產品上面的電機使能端的跳帽要拔掉,因為程式是用PWM來控制使能引腳的通點時長來讓小車轉彎的。
  1. 藍牙子產品JDY-31

    我使用的是透傳藍牙子產品,簡單來說就是手機連接配接後給藍牙發送什麼資料,然後藍牙子產品再原封不動的通過序列槽發給單片機,是以隻要懂序列槽的基本通信就好了。

    STM32智能遙控小車,超詳細-附下載下傳直接可以用,雙電源跑賊快!
  • 0V和3.3V是從單片機上取電的,驅動闆取電的話有可能會有幹擾。
  • PA2、PA3是單片機的序列槽2引腳。本來我想用序列槽1通訊的,但是好像序列槽1隻能接受到單片機發出來的資料,不能用手機發送進去給單片機(有可能是因為序列槽1的時鐘頻率太高),而且序列槽1正好留出來下載下傳程式,是以就選擇了序列槽2。
  • 藍牙子產品的預設波特率是9600,為了友善我也将序列槽的波特率也初始化成了9600。
  • 藍牙子產品的STATE引腳是藍牙的連接配接狀态引腳,連接配接成功會變成高電平。該引腳我用來給小車做緊急刹車,當藍牙沒有連接配接或是斷開小車會自動刹車,以免藍牙突然斷開時小車失控的運動。
  1. 電源

    小車采用雙電源供電

    STM32智能遙控小車,超詳細-附下載下傳直接可以用,雙電源跑賊快!
  • 這裡我選擇用兩個電源分别給單片機和驅動闆供電的原因

    電機啟動會有很大的啟動電流會在一瞬間将電流全部拉走,使得單片機斷電,如果藍牙子產品也是從單片機上取點也會斷電。

    直接電機的電刷會産生火花對通訊有幹擾,有些品質好的電機可能沒有(不太确定是不是這個原因,但同電源時通訊确實有幹擾,藍牙會接收一些奇怪的資料)。

  • 驅動闆的兩塊電源串聯連接配接,然後連接配接到驅動闆的電源接線端。

程式

程式隻要按照上面的連接配接都是可以直接使用,如果硬體有改動我也有定義宏可以直接做更改。

代碼裡面的注釋比較詳細,這裡隻是将重要的代碼放上來,稍作講解,最好是直接下載下傳來看。

下載下傳位址,提取碼:qsmu

  • 目錄結構
    STM32智能遙控小車,超詳細-附下載下傳直接可以用,雙電源跑賊快!
  1. main.c
#include "stm32f10x.h"                  // Device header
#include "MTR_GPIO.h"
#include "USART.h"
#include "LED.h"
#include "blueTooth.h"
#include "PWM_GeneralTim.h"

int main(void){
	USART_Config();//序列槽
	MTR_GPIOInit();//電機引腳
	GENERAL_TIM_Init();//定時器PWM初始化,用于小車轉向
	blueToothInit();//藍牙初始化
	LEDInit();
	printf("-----指令-----\n\
	0x01:小車後退\n\
	0x02:小車向左\n\
	0x03:小車向右\n\
	0x04:小車前進\n\
	0x05:小車刹車\n\
	0x06:電機停止\n\
	0x07:小車逆時針\n\
	0x08:小車轉向停止\n\
	0x09:小車順時針\n");
	while(1){
		//如果藍牙斷開,小車會一直在刹車狀态
		if(BLUE_TOOTH_STATE != Bit_SET){
			MTR_CarBrakeAll();
		}
	}
}
           
  • 主要是各功能的初始化。
  • 藍牙狀态的判斷。
  1. stm32f10x_it.c
void DEBUG_USART_IRQHandler(void){
	uint8_t CMD = 0;//接收的指令
	if(USART_GetITStatus(DEBUG_USARTx,USART_IT_RXNE)!=RESET){
		LED1_ON;
		CMD = USART_ReceiveData(DEBUG_USARTx);//讀取一個
		switch(CMD){
			case 0x06:
				//電機停止
				MTR_CarBrakeAll();
				printf("0x06");
				break;
			case 0x08:
				//小車轉向停止
				PWM_CarMaxGo();
				printf("0x08");
				break;
			case 0x05:
				//小車刹車
				MTR_CarBrakeAll();
				printf("0x05");
				break;
			case 0x02:
				//小車向左
				PWM_CarLeft();
				printf("0x02");
				break;
			case 0x03:
				//小車向右
				PWM_CarRight();
				printf("0x03");
				break;
			case 0x04:
				//小車前進
				MTR_CarGo();
				printf("0x04");
				break;
			case 0x01:
				//小車後退
				MTR_CarBackGo();
				printf("0x01");
				break;
			case 0x07:
				//小車逆時針
				MTR_CarCCW();
				printf("0x07");
				break;
			case 0x09:
				//小車順時針
				MTR_CarCW();
				printf("0x09");
				break;
		}
		LED1_OFF;//讓LED閃爍來表示資料的接收
	}
	USART_ClearFlag(DEBUG_USARTx,USART_FLAG_RXNE);
} 
           
  • 序列槽通過接收十六進制的指令來控制驅動闆。
  • 接收到特定的指令後,單片機會将小車執行的指令再發送回給藍牙。
  1. MTR_GPIO.c
#include "MTR_GPIO.h"

//刹車
void MTR_CarBrakeAll(void){
	MTR1_BRAKE;
	MTR2_BRAKE;
	MTR3_BRAKE;
	MTR4_BRAKE;
}

//前進
void MTR_CarGo(void){
	MTR1_CW;
	MTR2_CW;
	MTR3_CCW;
	MTR4_CCW;
}

//後退
void MTR_CarBackGo(void){
	MTR1_CCW;
	MTR2_CCW;
	MTR3_CW;
	MTR4_CW;
}

//順時針
void MTR_CarCW(void){
	MTR1_CCW;
	MTR2_CCW;
	MTR3_CCW;
	MTR4_CCW;
}

//逆時針
void MTR_CarCCW(void){
	MTR1_CW;
	MTR2_CW;
	MTR3_CW;
	MTR4_CW;
}

void MTR_GPIOInit(void){
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(MTR1_GPIO_CLK|MTR2_GPIO_CLK|MTR3_GPIO_CLK|MTR4_GPIO_CLK,ENABLE);//時鐘
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽輸出
	//電機1
	GPIO_InitStructure.GPIO_Pin = MTR1_GPIO_PIN;
	GPIO_Init(MTR1_GPIO_PORT, &GPIO_InitStructure);
	//電機2
	GPIO_InitStructure.GPIO_Pin = MTR2_GPIO_PIN;
	GPIO_Init(MTR2_GPIO_PORT, &GPIO_InitStructure);
	//電機3
	GPIO_InitStructure.GPIO_Pin = MTR3_GPIO_PIN;
	GPIO_Init(MTR3_GPIO_PORT, &GPIO_InitStructure);
	//電機4
	GPIO_InitStructure.GPIO_Pin = MTR4_GPIO_PIN;
	GPIO_Init(MTR4_GPIO_PORT, &GPIO_InitStructure);
	//小車刹車
	MTR_CarBrakeAll();
}
           

注意:因為單片機是控制驅動闆進而間接的控制電機,為了簡單我直接把控制驅動闆的引腳看成是控制電機的。

  • 電機的引腳初始化。
  • 小車前進、後退、原地旋轉和刹車的函數封裝。
  1. MTR_GPIO.h
#ifndef __MTR_GPIO_H
#define __MTR_GPIO_H

#include "stm32f10x.h"

/*
	小車四輪驅動
	電機1:右上
	電機2:右下
	電機3:左上
	電機4:左下
*/
#define MTR1_GPIO_PORT		GPIOB
#define MTR1_GPIO_CLK 	    RCC_APB2Periph_GPIOB
#define MTR1_GPIO_PIN		GPIO_Pin_6|GPIO_Pin_7
#define MTR1_CW				{GPIO_ResetBits(MTR1_GPIO_PORT,GPIO_Pin_6);GPIO_SetBits(MTR1_GPIO_PORT,GPIO_Pin_7);}//順時針
#define MTR1_CCW			{GPIO_SetBits(MTR1_GPIO_PORT,GPIO_Pin_6);GPIO_ResetBits(MTR1_GPIO_PORT,GPIO_Pin_7);}//逆時針
#define MTR1_BRAKE			GPIO_ResetBits(MTR1_GPIO_PORT,MTR1_GPIO_PIN);

#define MTR2_GPIO_PORT    	GPIOB
#define MTR2_GPIO_CLK 	    RCC_APB2Periph_GPIOB
#define MTR2_GPIO_PIN		GPIO_Pin_8|GPIO_Pin_9
#define MTR2_CW				{GPIO_ResetBits(MTR2_GPIO_PORT,GPIO_Pin_8);GPIO_SetBits(MTR2_GPIO_PORT,GPIO_Pin_9);}//順時針
#define MTR2_CCW			{GPIO_SetBits(MTR2_GPIO_PORT,GPIO_Pin_8);GPIO_ResetBits(MTR2_GPIO_PORT,GPIO_Pin_9);}//逆時針
#define MTR2_BRAKE			GPIO_ResetBits(MTR2_GPIO_PORT,MTR2_GPIO_PIN);

#define MTR3_GPIO_PORT    	GPIOB
#define MTR3_GPIO_CLK 	    RCC_APB2Periph_GPIOB
#define MTR3_GPIO_PIN		GPIO_Pin_12|GPIO_Pin_13
#define MTR3_CW				{GPIO_ResetBits(MTR3_GPIO_PORT,GPIO_Pin_12);GPIO_SetBits(MTR3_GPIO_PORT,GPIO_Pin_13);}//順時針
#define MTR3_CCW			{GPIO_SetBits(MTR3_GPIO_PORT,GPIO_Pin_12);GPIO_ResetBits(MTR3_GPIO_PORT,GPIO_Pin_13);}//逆時針
#define MTR3_BRAKE			GPIO_ResetBits(MTR3_GPIO_PORT,MTR3_GPIO_PIN);

#define MTR4_GPIO_PORT    	GPIOB
#define MTR4_GPIO_CLK 	    RCC_APB2Periph_GPIOB
#define MTR4_GPIO_PIN		GPIO_Pin_14|GPIO_Pin_15
#define MTR4_CW				{GPIO_ResetBits(MTR4_GPIO_PORT,GPIO_Pin_14);GPIO_SetBits(MTR4_GPIO_PORT,GPIO_Pin_15);}//順時針
#define MTR4_CCW			{GPIO_SetBits(MTR4_GPIO_PORT,GPIO_Pin_14);GPIO_ResetBits(MTR4_GPIO_PORT,GPIO_Pin_15);}//逆時針
#define MTR4_BRAKE			GPIO_ResetBits(MTR4_GPIO_PORT,MTR4_GPIO_PIN);

void MTR_CarBrakeAll(void);		//小車刹車
void MTR_CarGo(void);			//小車前進
void MTR_CarBackGo(void);		//小車後退
void MTR_CarCW(void);			//小車順時針轉
void MTR_CarCCW(void);			//小車逆時針轉
void MTR_GPIOInit(void);

#endif
           
  • 電機引腳的頭檔案
  1. PWM_GeneralTim.c
#include "PWM_GeneralTim.h" 

static void GENERAL_TIM_GPIO_Config(void) 
{
  GPIO_InitTypeDef GPIO_InitStructure;

  // 輸出比較通道1 GPIO 初始化
	RCC_APB2PeriphClockCmd(GENERAL_TIM_CH1_GPIO_CLK, ENABLE);
  GPIO_InitStructure.GPIO_Pin =  GENERAL_TIM_CH1_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GENERAL_TIM_CH1_PORT, &GPIO_InitStructure);
	
	// 輸出比較通道2 GPIO 初始化
	RCC_APB2PeriphClockCmd(GENERAL_TIM_CH2_GPIO_CLK, ENABLE);
  GPIO_InitStructure.GPIO_Pin =  GENERAL_TIM_CH2_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GENERAL_TIM_CH2_PORT, &GPIO_InitStructure);
	
	// 輸出比較通道3 GPIO 初始化
	RCC_APB2PeriphClockCmd(GENERAL_TIM_CH3_GPIO_CLK, ENABLE);
  GPIO_InitStructure.GPIO_Pin =  GENERAL_TIM_CH3_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GENERAL_TIM_CH3_PORT, &GPIO_InitStructure);
	
	// 輸出比較通道4 GPIO 初始化
	RCC_APB2PeriphClockCmd(GENERAL_TIM_CH4_GPIO_CLK, ENABLE);
  GPIO_InitStructure.GPIO_Pin =  GENERAL_TIM_CH4_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GENERAL_TIM_CH4_PORT, &GPIO_InitStructure);
}


///*
// * 注意:TIM_TimeBaseInitTypeDef結構體裡面有5個成員,TIM6和TIM7的寄存器裡面隻有
// * TIM_Prescaler和TIM_Period,是以使用TIM6和TIM7的時候隻需初始化這兩個成員即可,
// * 另外三個成員是通用定時器和進階定時器才有.
// *-----------------------------------------------------------------------------
// *typedef struct
// *{ TIM_Prescaler            都有
// *	TIM_CounterMode			     TIMx,x[6,7]沒有,其他都有
// *  TIM_Period               都有
// *  TIM_ClockDivision        TIMx,x[6,7]沒有,其他都有
// *  TIM_RepetitionCounter    TIMx,x[1,8,15,16,17]才有
// *}TIM_TimeBaseInitTypeDef; 
// *-----------------------------------------------------------------------------
// */

/* ----------------   PWM信号 周期和占空比的計算--------------- */
// ARR :自動重裝載寄存器的值
// CLK_cnt:計數器的時鐘,等于 Fck_int / (psc+1) = 72M/(psc+1)
// PWM 信号的周期 T = ARR * (1/CLK_cnt) = ARR*(PSC+1) / 72M
// 占空比P=CCR/(ARR+1)

static void GENERAL_TIM_Mode_Config(void)
{
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;
  // 開啟定時器時鐘,即内部時鐘CK_INT=72M
	GENERAL_TIM_APBxClock_FUN(GENERAL_TIM_CLK,ENABLE);

/*--------------------時基結構體初始化-------------------------*/
	// 配置周期
	// 自動重裝載寄存器的值,累計TIM_Period+1個頻率後産生一個更新或者中斷
	TIM_TimeBaseStructure.TIM_Period=GENERAL_TIM_Period;	
	// 驅動CNT計數器的時鐘 = Fck_int/(psc+1)
	TIM_TimeBaseStructure.TIM_Prescaler= GENERAL_TIM_Prescaler;	
	// 時鐘分頻因子 ,配置死區時間時需要用到
	TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;		
	// 計數器計數模式,設定為向上計數
	TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;		
	// 重複計數器的值,沒用到不用管
	TIM_TimeBaseStructure.TIM_RepetitionCounter=0;	
	// 初始化定時器
	TIM_TimeBaseInit(GENERAL_TIM, &TIM_TimeBaseStructure);

	/*--------------------輸出比較結構體初始化-------------------*/	
	// 配置為PWM模式
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
	// 輸出使能
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
	// 輸出通道電平極性配置	
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
	
	// 輸出比較通道 1
	TIM_OCInitStructure.TIM_Pulse = 0;
	TIM_OC1Init(GENERAL_TIM, &TIM_OCInitStructure);
	TIM_OC1PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);
	
	// 輸出比較通道 2
	TIM_OCInitStructure.TIM_Pulse = 0;
	TIM_OC2Init(GENERAL_TIM, &TIM_OCInitStructure);
	TIM_OC2PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);
	
	// 輸出比較通道 3
	TIM_OCInitStructure.TIM_Pulse = 0;
	TIM_OC3Init(GENERAL_TIM, &TIM_OCInitStructure);
	TIM_OC3PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);
	
	// 輸出比較通道 4
	TIM_OCInitStructure.TIM_Pulse = 0;
	TIM_OC4Init(GENERAL_TIM, &TIM_OCInitStructure);
	TIM_OC4PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);
	
	// 使能計數器
	TIM_Cmd(GENERAL_TIM, ENABLE);
}

void GENERAL_TIM_Init(void)
{
	GENERAL_TIM_GPIO_Config();
	GENERAL_TIM_Mode_Config();		
}

//小車左轉
void PWM_CarLeft(void){
	TIM_SetCompare1(GENERAL_TIM,0);
	TIM_SetCompare2(GENERAL_TIM,0);
	TIM_SetCompare3(GENERAL_TIM,4000);
	TIM_SetCompare4(GENERAL_TIM,4000);
}

//小車右轉
void PWM_CarRight(void){
	TIM_SetCompare1(GENERAL_TIM,4000);
	TIM_SetCompare2(GENERAL_TIM,4000);
	TIM_SetCompare3(GENERAL_TIM,0);
	TIM_SetCompare4(GENERAL_TIM,0);
}

//小車最大速度
void PWM_CarMaxGo(void){
	TIM_SetCompare1(GENERAL_TIM,0);
	TIM_SetCompare2(GENERAL_TIM,0);
	TIM_SetCompare3(GENERAL_TIM,0);
	TIM_SetCompare4(GENERAL_TIM,0);
}

/*********************************************END OF FILE**********************/
           
  • 小車轉向的原理是跟坦克的一樣,通過降低轉向邊的輪子速度來實作。而控制轉速就要用到PWM波來減少或增多電機的通斷時間(其實是驅動闆的電機使能引腳)。
  • 轉向函數的封裝

PS:完整的程式太多了,最好下載下傳下來慢慢看。

藍牙遙控器

做到這裡就剩隻藍牙遙控器的配置了,軟體呢随便哪個都行,隻要能收發資料就行。

不過我推薦用我這個,因為長得好看。

下載下傳位址,提取碼:kvar。此軟體為安卓,ios沒有

  1. 檢視指令

    單片機複位的時候會通過藍牙發送這些資料,用手機連上藍牙然後複位單片機就可以看到了。

printf("-----指令-----\n

0x01:小車後退\n

0x02:小車向左\n

0x03:小車向右\n

0x04:小車前進\n

0x05:小車刹車\n

0x06:電機停止\n

0x07:小車逆時針\n

0x08:小車轉向停止\n

0x09:小車順時針\n");

  • 單片機就是通過這些十六進制的資料來控制小車的運動,隻要将遙控器上的按鍵對應這些資料就行了。
  1. 按鍵說明
    STM32智能遙控小車,超詳細-附下載下傳直接可以用,雙電源跑賊快!
  2. 按鍵配置

    按鍵配置簡單,隻要根據指令來配置就好了,我就舉幾個例子。

  • 前進
    STM32智能遙控小車,超詳細-附下載下傳直接可以用,雙電源跑賊快!
  • 左轉
    STM32智能遙控小車,超詳細-附下載下傳直接可以用,雙電源跑賊快!
  • 刹車
    STM32智能遙控小車,超詳細-附下載下傳直接可以用,雙電源跑賊快!
  • 原地順時針
    STM32智能遙控小車,超詳細-附下載下傳直接可以用,雙電源跑賊快!

    接下來可以開車上路了

    祝你好運!!!

繼續閱讀