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

在這裏插入圖片描述

簡介

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

硬件

  1. 硬件的基本介紹
    在這裏插入圖片描述
  • 電機的位置很重要,因爲電機驅動模塊一個可以分別控制兩個電機,而且程序裏面也是分別控制單個電機的動作。
  • 如果電機的和自己想要的運動方向不一樣的話,交換一下接線端的兩個線就好了。程序裏面小車前進默認是電機1、2CW(順時針),電機3、4CCW(逆時針)。
  1. L298N電機驅動模塊
    電機驅動模塊這裏不細講,網上有很多資料,很簡單的。
    下面這個是右邊的驅動板,控制電機1、2。
    在這裏插入圖片描述
    下面是左邊的驅動板,控制電機3、4。
    在這裏插入圖片描述
  • 兩個驅動板是並聯的,供電時應選擇粗一點的導線給驅動板供電,最好就不要使用杜邦線,如果使用杜邦線有可能會電壓不夠。
  • 有的時候電機可能要用手轉一下才能動,那是因爲電壓太小了,可以串聯幾個電池。我是用一個小米充電寶和4節乾電池,電 壓應該在10V左右。
  • 注意不能串聯太多充電寶,因爲充電寶上面有過電流保護,電流太大充電寶可能會斷電,甚至有可能會直接弄壞充電寶。
  • 如果是雙電源的話,單片機要和兩個驅動板共地。
  • 電機驅動模塊上面的電機使能端的跳帽要拔掉,因爲程序是用PWM來控制使能引腳的通點時長來讓小車轉彎的。
  1. 藍牙模塊JDY-31
    我使用的是透傳藍牙模塊,簡單來說就是手機連接後給藍牙發送什麼數據,然後藍牙模塊再原封不動的通過串口發給單片機,所以只要懂串口的基本通信就好了。
    在這裏插入圖片描述
  • 0V和3.3V是從單片機上取電的,驅動板取電的話有可能會有干擾。
  • PA2、PA3是單片機的串口2引腳。本來我想用串口1通訊的,但是好像串口1只能接受到單片機發出來的數據,不能用手機發送進去給單片機(有可能是因爲串口1的時鐘頻率太高),而且串口1正好留出來下載程序,所以就選擇了串口2。
  • 藍牙模塊的默認波特率是9600,爲了方便我也將串口的波特率也初始化成了9600。
  • 藍牙模塊的STATE引腳是藍牙的連接狀態引腳,連接成功會變成高電平。該引腳我用來給小車做緊急剎車,當藍牙沒有連接或是斷開小車會自動剎車,以免藍牙突然斷開時小車失控的運動。
  1. 電源
    小車採用雙電源供電
    在這裏插入圖片描述
  • 這裏我選擇用兩個電源分別給單片機和驅動板供電的原因
    電機啓動會有很大的啓動電流會在一瞬間將電流全部拉走,使得單片機斷電,如果藍牙模塊也是從單片機上取點也會斷電。
    直接電機的電刷會產生火花對通訊有干擾,有些質量好的電機可能沒有(不太確定是不是這個原因,但同電源時通訊確實有干擾,藍牙會接收一些奇怪的數據)。
  • 驅動板的兩塊電源串聯連接,然後連接到驅動板的電源接線端。

程序

程序只要按照上面的連接都是可以直接使用,如果硬件有改動我也有定義宏可以直接做更改。
代碼裏面的註釋比較詳細,這裏只是將重要的代碼放上來,稍作講解,最好是直接下載來看。
下載地址,提取碼:z5us

  • 目錄結構
    在這裏插入圖片描述
  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. 按鍵說明
    在這裏插入圖片描述
  2. 按鍵配置
    按鍵配置簡單,只要根據指令來配置就好了,我就舉幾個例子。
  • 前進
    在這裏插入圖片描述
  • 左轉在這裏插入圖片描述
  • 剎車在這裏插入圖片描述
  • 原地順時針在這裏插入圖片描述 接下來可以開車上路了
    祝你好運!!!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章