![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHL6lFVNFzZq5keJpHW4Z0MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZwpmL5MDOwUzNyAjM2ADNwkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
簡介
- 小車的動力部分由4個帶減速箱的電機,和兩個L298N電機驅動子產品組成。
- 通過STM32核心闆控制電機驅動子產品,JDY-31藍牙子產品與手機通訊。
- 總共三塊電池,一塊專門給單片機供電,另外兩塊串聯在一起同時給電機驅動子產品供電(為什麼選擇分别供電後面的電源硬體部分有說)。
- 源碼下載下傳,提取碼:nxo3
硬體
- 硬體的基本介紹
STM32智能遙控小車,超詳細-附下載下傳直接可以用,雙電源跑賊快!
- 電機的位置很重要,因為電機驅動子產品一個可以分别控制兩個電機,而且程式裡面也是分别控制單個電機的動作。
- 如果電機的和自己想要的運動方向不一樣的話,交換一下接線端的兩個線就好了。程式裡面小車前進預設是電機1、2CW(順時針),電機3、4CCW(逆時針)。
-
L298N電機驅動子產品
電機驅動子產品這裡不細講,網上有很多資料,很簡單的。
下面這個是右邊的驅動闆,控制電機1、2。
下面是左邊的驅動闆,控制電機3、4。STM32智能遙控小車,超詳細-附下載下傳直接可以用,雙電源跑賊快! STM32智能遙控小車,超詳細-附下載下傳直接可以用,雙電源跑賊快!
- 兩個驅動闆是并聯的,供電時應選擇粗一點的導線給驅動闆供電,最好就不要使用杜邦線,如果使用杜邦線有可能會電壓不夠。
- 有的時候電機可能要用手轉一下才能動,那是因為電壓太小了,可以串聯幾個電池。我是用一個小米充電寶和4節幹電池,電 壓應該在10V左右。
- 注意不能串聯太多充電寶,因為充電寶上面有過電流保護,電流太大充電寶可能會斷電,甚至有可能會直接弄壞充電寶。
- 如果是雙電源的話,單片機要和兩個驅動闆共地。
- 電機驅動子產品上面的電機使能端的跳帽要拔掉,因為程式是用PWM來控制使能引腳的通點時長來讓小車轉彎的。
-
藍牙子產品JDY-31
我使用的是透傳藍牙子產品,簡單來說就是手機連接配接後給藍牙發送什麼資料,然後藍牙子產品再原封不動的通過序列槽發給單片機,是以隻要懂序列槽的基本通信就好了。
STM32智能遙控小車,超詳細-附下載下傳直接可以用,雙電源跑賊快!
- 0V和3.3V是從單片機上取電的,驅動闆取電的話有可能會有幹擾。
- PA2、PA3是單片機的序列槽2引腳。本來我想用序列槽1通訊的,但是好像序列槽1隻能接受到單片機發出來的資料,不能用手機發送進去給單片機(有可能是因為序列槽1的時鐘頻率太高),而且序列槽1正好留出來下載下傳程式,是以就選擇了序列槽2。
- 藍牙子產品的預設波特率是9600,為了友善我也将序列槽的波特率也初始化成了9600。
- 藍牙子產品的STATE引腳是藍牙的連接配接狀态引腳,連接配接成功會變成高電平。該引腳我用來給小車做緊急刹車,當藍牙沒有連接配接或是斷開小車會自動刹車,以免藍牙突然斷開時小車失控的運動。
-
電源
小車采用雙電源供電
STM32智能遙控小車,超詳細-附下載下傳直接可以用,雙電源跑賊快!
-
這裡我選擇用兩個電源分别給單片機和驅動闆供電的原因
電機啟動會有很大的啟動電流會在一瞬間将電流全部拉走,使得單片機斷電,如果藍牙子產品也是從單片機上取點也會斷電。
直接電機的電刷會産生火花對通訊有幹擾,有些品質好的電機可能沒有(不太确定是不是這個原因,但同電源時通訊确實有幹擾,藍牙會接收一些奇怪的資料)。
- 驅動闆的兩塊電源串聯連接配接,然後連接配接到驅動闆的電源接線端。
程式
程式隻要按照上面的連接配接都是可以直接使用,如果硬體有改動我也有定義宏可以直接做更改。
代碼裡面的注釋比較詳細,這裡隻是将重要的代碼放上來,稍作講解,最好是直接下載下傳來看。
下載下傳位址,提取碼:qsmu
- 目錄結構
STM32智能遙控小車,超詳細-附下載下傳直接可以用,雙電源跑賊快!
- 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();
}
}
}
- 主要是各功能的初始化。
- 藍牙狀态的判斷。
- 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);
}
- 序列槽通過接收十六進制的指令來控制驅動闆。
- 接收到特定的指令後,單片機會将小車執行的指令再發送回給藍牙。
- 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();
}
注意:因為單片機是控制驅動闆進而間接的控制電機,為了簡單我直接把控制驅動闆的引腳看成是控制電機的。
- 電機的引腳初始化。
- 小車前進、後退、原地旋轉和刹車的函數封裝。
- 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
- 電機引腳的頭檔案
- 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沒有
-
檢視指令
單片機複位的時候會通過藍牙發送這些資料,用手機連上藍牙然後複位單片機就可以看到了。
printf("-----指令-----\n
0x01:小車後退\n
0x02:小車向左\n
0x03:小車向右\n
0x04:小車前進\n
0x05:小車刹車\n
0x06:電機停止\n
0x07:小車逆時針\n
0x08:小車轉向停止\n
0x09:小車順時針\n");
- 單片機就是通過這些十六進制的資料來控制小車的運動,隻要将遙控器上的按鍵對應這些資料就行了。
- 按鍵說明
STM32智能遙控小車,超詳細-附下載下傳直接可以用,雙電源跑賊快! -
按鍵配置
按鍵配置簡單,隻要根據指令來配置就好了,我就舉幾個例子。
- 前進
STM32智能遙控小車,超詳細-附下載下傳直接可以用,雙電源跑賊快! - 左轉
STM32智能遙控小車,超詳細-附下載下傳直接可以用,雙電源跑賊快! - 刹車
STM32智能遙控小車,超詳細-附下載下傳直接可以用,雙電源跑賊快! - 原地順時針
STM32智能遙控小車,超詳細-附下載下傳直接可以用,雙電源跑賊快! 接下來可以開車上路了
祝你好運!!!