由于项目上需要用到无刷电机,一般的无刷电机驱动器又大又不方便,所以最后选择了航模上使用的无刷电调,当时只是刚好看到BLHeli_S电调,就直接下单了。

使用电调控制无刷电机,最大的问题就是电机保护和油门行程校准。第一,单片机在控制电调驱动无刷电机的时候就需要跳过电机保护;第二,油门行程校准由于不同的协议没有具体的介绍,手上又没有遥控器和飞控,光靠单片机算法模拟一个值一个值来试是很难的。

一开始我是用单片机定时器试,后面买了个pwm测试电调的模块,完全适配BLHeli_S电调,用示波器发现调速很简单,就是50hz的1ms高电平3s左右就解锁电机保护,1-2ms高电平代表最小油门和最大油门。

esp12F PWM esp12f pwm调速_嵌入式硬件

esp12F PWM esp12f pwm调速_stm32_02

 

一、让BLHeli_S电调控制无刷电机动起来

使用单片机连接BLHeli_S电调,白色PWM信号线,黑色为信号端接地校准。

电调连接电池和无刷电机,电调买的时候会介绍支持的电池,我的支持2S-6S(1S代表串联一块3.7V电池),我用的3S电池(11.1V),红接电池正极,黑接电池负极,无刷电机三相没有顺序随便接到电调。

esp12F PWM esp12f pwm调速_Data_03

 因为需要用到无线功能,单片机我使用的是esp-12F和Arduino开发,代码如下,记得改WIFI名字和密码。其他的单片机也是一样的,都是模拟遥控器关闭电机保护控制电机。这里用的是定时器,换成20ms循环delayMicroseconds( us );//微秒级延时函数电机控制会更平滑,这主要是analogWrite(pin,i)//i在0-255之间,高电平时间对应数字大小从低到高,对于1-2ms需要千分度的油门来说调节精度不够,但是如果想单片机控制无刷电机又要做其他事情,就只能用这个定时器了。

#include <ESP8266WiFi.h>
#include <WiFiUdp.h>

#define motor0 2  //GPIO2

const char ssid[] = "Redmi K40";  //  your network SSID (name)
const char pass[] = "12345679";       // your network password
WiFiUDP Udp;
unsigned int localPort = 2380;
char incomingPacket[255];
float rd = 0;
int Data_length;

void setup()
{
  Wire.begin();
  Serial.begin(115200);
  WiFi.begin(ssid, pass);
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }
  Serial.println(WiFi.localIP());
  Udp.begin(localPort);//启动UDP监听本地端口
  pinMode(motor0, OUTPUT);
  digitalWrite(motor0, LOW);
   analogWrite(motor0, 13);
}

void loop()
{
  Data_length = Udp.parsePacket(); //获取接收的数据的长度
  if (Data_length) //如果有数据那么Data_length不为0,无数据Data_length为0
  {
    int len = Udp.read(incomingPacket, 255);  //读取数据,将数据保存在数组incomingPacket中
    if (len > 0)  //为了避免获取的数据后面乱码做的判断
    {
      incomingPacket[len] = 0;
    }
    Udp.beginPacket(Udp.remoteIP(), Udp.remotePort()); //准备发送数据到目标IP和目标端口
    Udp.print("Receive data:");  //将数据receive data:放入发送的缓冲区
    if (len == 1) { rd = int(incomingPacket[0]) - 48; }//一位数控制速度
    Udp.println(rd);  //将接收到的数据放入发送的缓冲区
    Udp.endPacket();  //向目标IP目标端口发送数据
  }
  analogWrite(motor0, 13+rd);//13-25,13解锁,25最大速度
}

白线接GPIO2,黑线接GND。接好线后,需要先在Arduino标签栏->工具->串口监视器,看串口管理器的回复IP。

esp12F PWM esp12f pwm调速_单片机_04

然后打开NetAssist(串口调试助手),连接同一WiFi,选择UDP协议,随便使用一个本地主机端口,打开端口,在远程主机里输入你刚刚看到的回复IP:2380,并且发送一个数字看能否收到回复。

esp12F PWM esp12f pwm调速_单片机_05

收到回复后,在窗口发送的数字就是可以调节的50Hz的PWM波高电平时间。

默认高电平时间是1ms,因为解锁BLHeli_S电调的电机保护就是高电平1ms。

上电单片机后,连接电调,当听到嘀、嘀、嘀(3S电池响3声短),然后滴(短),滴~(稍长的一声),电机解锁成功。在窗口输入个位数1-9,电机就可以进入转速工作模式,输入0停止。

如果需要精确控制,下面这里代码控制速度更加精确。

#include <ESP8266WiFi.h>
#include <WiFiUdp.h>

#define motor0 2  //GPIO2

const char ssid[] = "Redmi K40";  //  your network SSID (name)
const char pass[] = "12345679";       // your network password
WiFiUDP Udp;
unsigned int localPort = 2380;
unsigned int sendport = 2390;
char incomingPacket[255];
float rd = 0;
int Data_length;

void setup()
{
  Wire.begin();
  Serial.begin(115200);
  WiFi.begin(ssid, pass);
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }
  Serial.println(WiFi.localIP());
  Udp.begin(localPort);//启动UDP监听本地端口
  pinMode(motor0, OUTPUT);
  digitalWrite(motor0, LOW);
   analogWrite(motor0, 13);
}

void loop()
{
  Data_length = Udp.parsePacket(); //获取接收的数据的长度
  if (Data_length) //如果有数据那么Data_length不为0,无数据Data_length为0
  {
    int len = Udp.read(incomingPacket, 255);  //读取数据,将数据保存在数组incomingPacket中
    if (len > 0)  //为了避免获取的数据后面乱码做的判断
    {
      incomingPacket[len] = 0;
    }
    Udp.beginPacket(Udp.remoteIP(), Udp.remotePort()); //准备发送数据到目标IP和目标端口
    Udp.print("Receive data:");  //将数据receive data:放入发送的缓冲区
    if (len == 1) { rd = int(incomingPacket[0]) - 48; }//一位数控制速度
    Udp.println(rd);  //将接收到的数据放入发送的缓冲区
    Udp.endPacket();  //向目标IP目标端口发送数据
  }
delayMicroseconds(19000-rd*100);
digitalWrite(motor0,LOW);//管脚写入函数
delayMicroseconds(1000+rd*100);
digitalWrite(motor0,HIGH);//管脚写入函数
}

二、Keil使用BLHeli_S电调工程

多次尝试之后,发现就使用航模的电调和自己的工程完全不匹配,如果不想自己做无刷驱动,又想直接用上无刷驱动电调,那就只能编程了。

在网上也搜了关于BLHeli_S电调的Keil工程,但是发现资料很少,这里主要参考此博主的文章,然后说一下自己遇到的坑。

原文链接:

按照博主的内容,去keil官网填一下几个空,就可以下载软件C51V961.EXE,在github下载BLHeli_S工程源代码,BLHeli_S /SiLabs文件夹里所有文件就是源代码。

Github下载代码时,压缩包解压总提示不可预料的压缩末端,下多次也是一样,没办法,新建文件BLHeli_S.asm用记事本把网页上的4000行代码全部粘贴进去,效果是一样的。

我看了自己电调芯片型号为EFM8BB21F16G,然后数了电调上引脚数20,那就是和博主一样的EFM8BB21F16G-QFN20。

至于引脚方案怎么确定是哪个?我是看了电调引脚接线和芯片引脚图的,我的电调接线方案就是J_,估计大部分BLHeli_S电调都是。

esp12F PWM esp12f pwm调速_单片机_06

esp12F PWM esp12f pwm调速_Data_07

 keil新建工程并选择此芯片,只导入了BLHeli_S.asm,按照博主更改代码取消3处注释,这里代码已经有区别了。

ESCNO EQU J_

MCU_48MHZ EQU  2

FETON_DELAY EQU 50

一点编译,很多错误。

看了一下代码,Github的代码已经更新了,和原来博主下载到的代码不一致,而找Github历史版本又不确定是哪一版,没办法自己改最新版代码的报错,最后如下导入5个文件,下载不了就新建文件复制代码保存效果一致。

esp12F PWM esp12f pwm调速_esp12F PWM_08

 由于只需要用到J.inc的引脚定义,删除所有其他硬件类型判断,例如

IF ESCNO == A_
 $include (A.inc)    ; Select pinout A
 ENDIF

esp12F PWM esp12f pwm调速_esp12F PWM_09

然后就可以编译成功。