用电调控制无刷电机制作蓝牙遥控飞艇
【注:学习STM32总结做的笔记,大神勿喷。有不足之处还望不吝赐教,谢谢。工程代码在最后。】
蓝牙控制 调控电调 无刷电机 STM32F103单片机 用电调控制无刷电机制作蓝牙遥控飞艇
这是队友在某宝上买的无刷电机和电调,因为比赛需要,做了条飞艇出来。由于无刷电机质量问题(划重点),导致比赛失了败。
其实做出来是挺简单的,就是驱动电调去控制无刷电机,对于一些刚接触电调、无刷电机的人来说,有一定的难度(网上的教程很多,但很多都说得不明不白的)。下面我就完整的把我的讲一下吧,有不对的地方可以指出来,还望勿喷。
说之前还是要强调一下
注意事项:
1.调试要看紧点电机(安全问题,防止浆翼打伤手,能拿掉浆翼就拿掉)
2.防水措施一定要做好,不然进水会烧掉电路
3.电调要跟无刷电机接对线(电机有正反浆,接错会打转,或者不动。。电调只会往一个方向给信号)
4.船模设计要合理,要考虑船的承载能力和水位,不然飞艇会成潜艇(船头要轻,浮起来。不然前面太重,后面推动,飞艇还会成潜艇)
5.我这款无刷电机能下水,做船的建议电机浆翼到水就好(倾斜对水),不要整只下到水,阻力太大船就没动力了。
6.这些都是意见与经验之谈,觉得有用的就用。
我用的单片机是STM32F103,电调是好盈的(HOBBYWNG)(材料型号规格要按实际要求选择,还要看它的说明书),无刷电机是三线的(不懂可百度)。
接线问题:黑线接GND(一定要和单片机共地接),红线接+5v(不接也没问题,还是建议接上),白线(信号线)接输出PWM的管脚,另外两条粗粗的红黑线–自然是接电池电源
下图是电调的说明
电调说明也可以参考这位博主的文章:https://blog.csdn.net/CalShell/article/details/44424347
电调驱动
驱动电调关键就是PWM频率,有的人用50Hz的PWM能驱动电调,也有的人用500Hz能驱动电调。这个要具体看电调的要求,以及PWM的输出(PWM设置分不分频会影响PWM的计数快慢,从而影响电调的控制),我下面的pwm设了分频,不理解PWM分不分频的可自行百度。如果按照我的这组数(1999,71)调不通,可以或者不分频多改改测测,接着就是解锁电调,电调第一次使用要设置量程,然后接上线就能动了,就是这么简单。
我这电调驱动的值在(5—500),5—速度最大;500—速度最小,无刷电机停止
TIM3_PWM_Init(1999,71); //不要问我怎么来的,这个数值的PWM频率是我测出来的((1999,71)这组数不唯一)。具体可以去理解一下PWM频率怎么计算
/*/解锁电调*/
/*电调第一次使用要设置量程,解锁两个电调步骤,先拉到最高油门,再拉到最低油门,这一步电机会动,一定要注意安全*/
TIM_SetCompare2(TIM3,10);
TIM_SetCompare3(TIM3,10);
delay_ms(1000); //先延时
TIM_SetCompare2(TIM3,500);
TIM_SetCompare3(TIM3,500);
delay_ms(10); //再延时
/*/解锁电调*/
电调PWM信号输出–示波器
电调驱动电机–第一次解锁和高速运行时的信号输出
电调驱动电机–电机停止时的信号输出
我做的飞艇用了三个电机–正反浆电机、加速喷水电机。正反浆无刷电机–控制前进左右方向,加速喷水电机–备用动力(终点快人一步)。
L298N驱动加速喷水电机就不多做解释了,接线如下图,代码也在最下面了。
接线图
调测–成品图
代码区
mian.c
#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "usart3.h"
#include "timer.h"
int main(void)
{
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
uart_init(115200); //串口初始化为115200
usart3_init(9600);//蓝牙的串口初始化 注意手机与单片机的蓝牙连接波特率是要9600
TIM3_PWM_Init(1999,71); //不要问我怎么来的,这个数值的PWM频率是我测出来的。具体可以去理解一下PWM频率怎么计算
/********************解锁电调**********************/
/*电调第一次使用要设置量程,解锁两个电调步骤,先拉到最高油门,再拉到最低油门,这一步电机会动,一定要注意安全*/
TIM_SetCompare2(TIM3,10);
TIM_SetCompare3(TIM3,10);
delay_ms(1000); //先延时
TIM_SetCompare2(TIM3,500);
TIM_SetCompare3(TIM3,500);
delay_ms(10); //再延时
/********************解锁电调**********************/
while(1)
{
if(USART3_RX_STA&0x8000)
{
if(USART3_RX_BUF[0]=='l')//前进
{
TIM_SetCompare2(TIM3,10);//电机1全速前进
TIM_SetCompare3(TIM3,10);//电机2全速前进
}
if(USART3_RX_BUF[0]=='h')//停止
{
TIM_SetCompare2(TIM3,500);//电机1停止
TIM_SetCompare3(TIM3,500);//电机2停止
}
if(USART3_RX_BUF[0]=='z')//左转
{
TIM_SetCompare2(TIM3,100);//电机1减速
TIM_SetCompare3(TIM3,400);//电机2全速
}
if(USART3_RX_BUF[0]=='y')//右转
{
TIM_SetCompare2(TIM3,400);//电机1全速
TIM_SetCompare3(TIM3,100);//电机2减速
}
if(USART3_RX_BUF[0]=='q')//备用电机启动
{
TIM_SetCompare4(TIM3,10);//备用电机全速
}
if(USART3_RX_BUF[0]=='t')//备用电机停止
{
TIM_SetCompare4(TIM3,1999);//备用电机停止
}
USART3_RX_STA=0;//清空接收标记
}
}
}
timer.c
#include "timer.h"
//通用定时器3中断初始化
//这里时钟选择为APB1的2倍,而APB1为36M
//arr:自动重装值。
//psc:时钟预分频数
//这里使用的是定时器3!
void TIM3_Int_Init(u16 arr,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 计数到5000为500ms
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 10Khz的计数频率
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级0级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级3级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
TIM_Cmd(TIM3, ENABLE); //使能TIMx外设
}
//定时器3中断服务程序
void TIM3_IRQHandler(void) //TIM3中断
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update ); //清除TIMx的中断待处理位:TIM 中断源
LED1=!LED1;
}
}
//TIM3 PWM部分初始化
//PWM输出初始化
//arr:自动重装值
//psc:时钟预分频数
//PB5------TIM_SetCompare2------左边电机
//PB0------TIM_SetCompare3------右边电机
//PB1------TIM_SetCompare4------加速喷水电机
void TIM3_PWM_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能定时器3时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); //使能GPIO外设和AFIO复用功能模块时钟
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射 TIM3_CH2->PB5
//设置该引脚为复用输出功能,输出TIM3 CH2的PWM脉冲波形 GPIOB.5
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_5; //TIM_CH2
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO
GPIO_ResetBits(GPIOB,GPIO_Pin_1); //PB1要拉低,因为芯片默认高电平,而L298N一个脚接地,会形成回路,电机就默认驱动了的
//初始化TIM3
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
//初始化TIM3 通道2 PWM模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
TIM_OC2Init(TIM3, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM3 OC2
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR2上的预装载寄存器
//初始化TIM3 通道3 PWM模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
TIM_OC3Init(TIM3, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM3 OC2
TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR2上的预装载寄存器
//初始化TIM3 通道4 PWM模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
TIM_OC4Init(TIM3, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM3 OC2
TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR2上的预装载寄存器
TIM_Cmd(TIM3, ENABLE); //使能TIM3
}
timer.h
#ifndef __TIMER_H
#define __TIMER_H
#include "sys.h"
void TIM3_Int_Init(u16 arr,u16 psc);
void TIM3_PWM_Init(u16 arr,u16 psc);
#endif
蓝牙部分
usart3.c
#include "sys.h"
#include "usart3.h"
u8 USART3_RX_BUF[USART3_REC_LEN]; //数组用来存储接收到的数据,而USART3_REC_LEN为最多能接收的字节限度
u16 USART3_RX_STA=0; //接收状态标记 0-14位为接收字节数,15位接收完成标志位
void usart3_init(u32 bound)
{
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructurea;
USART_InitTypeDef USART_InitStruct;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE);//使能串口3
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); //使能GPIOB
USART_DeInit(USART3);//串口3复位 不是必要的一步
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure); //PB10设置为复用推挽输出
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_11;
GPIO_Init(GPIOB,&GPIO_InitStructure); //PB11设置为浮空输入
NVIC_InitStructurea.NVIC_IRQChannel=USART3_IRQn;
NVIC_InitStructurea.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructurea.NVIC_IRQChannelPreemptionPriority=2;
NVIC_InitStructurea.NVIC_IRQChannelSubPriority=2;
NVIC_Init(&NVIC_InitStructurea); //串口3中断的设置
USART_ITConfig(USART3,USART_IT_RXNE,ENABLE);//串口3中断使能和中断类型设置
USART_InitStruct.USART_BaudRate=bound;//串口波特率
USART_InitStruct.USART_HardwareFlowControl=USART_HardwareFlowControl_None;//无硬件数据流控制
USART_InitStruct.USART_Mode=USART_Mode_Rx|USART_Mode_Rx;//收发模式
USART_InitStruct.USART_Parity=USART_Parity_No;//无奇偶校验位
USART_InitStruct.USART_StopBits=USART_StopBits_1;//一个停止位
USART_InitStruct.USART_WordLength=USART_WordLength_8b;//字长为8位数据格式
USART_Init(USART3,&USART_InitStruct); //串口3的一些参数设置
USART_Cmd(USART3,ENABLE);//串口3使能
}
void USART3_IRQHandler()//串口3的中断
{
u8 res;//暂时缓存接收的数据
if(USART_GetITStatus(USART3,USART_IT_RXNE)!=RESET)//判断是否为串口3接收中断
{
res=USART_ReceiveData(USART3);//接收到数据放进res
USART3_RX_BUF[USART3_RX_STA&0x7FFF]=res;//数据放进数组中,则可以用到main函数中了
USART3_RX_STA++; //字节长度++
if(USART3_RX_STA>(USART3_REC_LEN-1))USART3_RX_STA=0;//接收数据错误,重新开始接收 +;
USART3_RX_STA|=0x8000;//串口3接收完成
USART_ClearITPendingBit(USART3,USART_IT_RXNE);//清除接收中断标志
}
}
usart3.h
#ifndef USART3_H_
#define USART3_H_
#include "sys.h"
#include "stdio.h"
#define USART3_REC_LEN 200 //定义最大接收字节数 200
extern u8 USART3_RX_BUF[USART3_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节
extern u16 USART3_RX_STA; //接收状态标记 0-14位为接收字节数,15位接收完成标志位
void usart3_init(u32 bound);
#endif
【注:文件源码是Keil uVision5–STM32工程文件】
参考文件源码下载链接:
https://download.csdn.net/download/qq_28056277/11232414
原创文章,转载请注明出处,谢谢。