码迷,mamicode.com
首页 > 其他好文 > 详细

手把手教你做蓝牙小车(三)

时间:2016-06-24 16:03:29      阅读:1007      评论:0      收藏:0      [点我收藏+]

标签:

第6节 马达

要说蓝牙小车哪个模块最重要,多数人一定会以为是马达。
之前说过,为了防止开发板被电流击穿,控制马达时要增加一块扩展板。
所以,控制马达,只对扩展板编程,而不需要对马达编程。
此外,扩展板厂家会提供通过扩展板控制马达的代码。

综上,对开发人员来说,马达,只要确认存在就可以了。

6.1 扩展板

Arduino开发板只提供了一些基础、通用的接口,针对一些常见的特殊功能,Arduino专门为其推出了扩展板。

6.1.1 官方扩展板

Arduino官方目前总共推出了5款扩展板。
分别是
Arduino Motor Shield

技术分享

Arduino Proto Shield

技术分享

Arduino Ethernet Shield

技术分享

Arduino GSM Shield

技术分享

Arduino WiFi Shield 101

技术分享

其中的Arduino Motor Shield就是专为马达设计的扩展板。

6.1.2 第三方扩展板

Arduino是开放平台,所以有不少第三方设计者为Arduino设计扩展板。
我们今天要使用的是下面这款双L293D芯片的马达扩展板。

技术分享

作为一个软件程序员,我们只需要知道L293D是一种“H桥电机驱动器”就足够了。
如果你还有更多好奇心,可以参看L293D的datasheet文件

之所以选择这块马达扩展板,而没有选择官方推出的扩展板。
一个原因是这块扩展板有两个L293D,它支持同时控制四个车轮。四轮驱动,好牛X的感觉:)
另一个原因是这个开发板便宜。

6.2 马达和车轮

马达采用比较常见的这种

技术分享

车轮只要能和马达匹配就行

技术分享

因为不会针对这两个设备编程,所以没有太多要求。

6.3 代码分析

厂家提供了一些扩展板相关的代码,我把相关的三个文件打包放在安豆网方便大家下载

6.3.1 扩展板库文件

下载解压后,打开MotorTest目录,可以看到三个文件。

AFMotor.cpp,AFMotor.h是扩展板的库文件。
Arduino有两种方法使用库文件。

第一种方法是把它们加入Arduino库文件目录中。
把库文件打成zip包,直接打包文件或放在目录下打包都可以。
选择“菜单 项目->加载库->添加一个.ZIP库”,文件就被加到了”c:\Users\UserName\Documents\Arduino\libraries\”目录。
所以你也可以直接拷贝这两个文件到以上目录下。

第二种方法是把这两个文件和使用他们的ino文件放在一起。
使用时根据相对路径调用库文件。

所有arduino模块,厂家都会提供库文件,这个库文件相当于sdk。
大多数时候,我们不需要了解它的详细实现过程,只要知道它提供哪些接口,怎么使用就可以了。

6.3.2 扩展板测试程序

MotorTest.ino是这款马达扩展板的演示程序。
我们需要详细了解这个文件。

6.3.2.1 引用头文件

第5行
#include <AFMotor.h>
如果你没把AFMotor.h放在arduino库文件目录下,这里要改成""引用头文件。

6.3.2.2 生成马达对象

技术分享

从这张图中可以看到,这款开发板可以控制的四个马达,接口分别被标注为M1、M2、M3、M4。
第7行
AF_DCMotor motor(4);
生成一个控制M4号马达的对象。

6.3.2.3 设置马达速度

第14行
motor.setSpeed(200);
从函数名判断是设置马达的速度,代码理解到这个程度就足够了。
当然,如果你非常有好奇心,我还是有必要满足一下。

看看setSpeed函数的实现:

void AF_DCMotor::setSpeed(uint8_t speed) {
  switch (motornum) {
    case 1:
      setPWM1(speed); break;
    case 2:
      setPWM2(speed); break;
    case 3:
      setPWM3(speed); break;
    case 4:
      setPWM4(speed); break;
  }
}

我们传入的参数是200,即speed=200
初始化AF_DCMotor对象时,指定了M4号马达,此处的motornum等于4
setSpeed最终执行的是case 4: setPWM4(200);

再看看setPWM4函数的实现:

inline void setPWM4(uint8_t s) {
#if defined(__AVR_ATmega8__) || \
    defined(__AVR_ATmega48__) ||     defined(__AVR_ATmega88__) ||     defined(__AVR_ATmega168__) ||     defined(__AVR_ATmega328P__)
  // use PWM from timer0A on PB3 (Arduino pin #6)
  OCR0B = s;
#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
  // on arduino mega, pin 6 is now PH3 (OC4A)
  OCR3A = s;
#elif defined(__PIC32MX__)
  // Set the OC2 (pin 5) PMW duty cycle from 0 to 255
  OC2RS = s;
#else
#error "This chip is not supported!"
#endif
}

我手头这块Mega板对应的宏定义是__AVR_ATmega2560__,即执行了代码OCR3A = s;
你如果不确定哪个宏定义对应你的开发板,可以用这个方法。
每一个#if下都胡乱写一些代码,不相同即可。

inline void setPWM4(uint8_t s) {
#if defined(__AVR_ATmega8__) || \
    defined(__AVR_ATmega48__) ||     defined(__AVR_ATmega88__) ||     defined(__AVR_ATmega168__) ||     defined(__AVR_ATmega328P__)
  // use PWM from timer0A on PB3 (Arduino pin #6)
  OCR0B = s;
 abc
#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
  // on arduino mega, pin 6 is now PH3 (OC4A)
  OCR3A = s;
 def
#elif defined(__PIC32MX__)
  // Set the OC2 (pin 5) PMW duty cycle from 0 to 255
  OC2RS = s;
 ghi
#else
#error "This chip is not supported!"
#endif
}

编译一下,编译器会告诉你某行代码出错了。我这里提示def出错了。
我就确定我的开发板对应的是__AVR_ATmega1280__defined(__AVR_ATmega2560__

OCR3A = s这行代码做了什么?简单说,就是告诉Arduino,输出电压按时间分成255份,s份输出1,其他的输出0。
OCR3A = 200就是200份输出1,55份输出0。整体看,Arduino就输出了一个5(v)*200/255~=4(v)的电压。

还不满意我这个解释?
那就自己学习Mega PinMappingPWM两篇文档。


/*******************************************************************/
* 版权声明
* 本教程只在CSDN和安豆网发布,其他网站出现本教程均属侵权。
/*******************************************************************/


6.3.2.4 马达初始状态设置为停止

第16行
motor.run(RELEASE);
先看看RELEASE的定义,在AFMotor.h中一共定义了四条命令。

// Constants that the user passes in to the motor calls
#define FORWARD  1
#define BACKWARD 2
#define BRAKE    3
#define RELEASE  4

接着分析run函数的实现,先看函数的后半段。

void AF_DCMotor::run(uint8_t cmd) {
...
  switch (cmd) {
    case FORWARD:
      latch_state |= _BV(a);
      latch_state &= ~_BV(b);
      MC.latch_tx();
      break;
    case BACKWARD:
      latch_state &= ~_BV(a);
      latch_state |= _BV(b);
      MC.latch_tx();
      break;
    case RELEASE:
      latch_state &= ~_BV(a);     // A and B both low
      latch_state &= ~_BV(b);
      MC.latch_tx();
      break;
  }
}

先看看_BV是个啥东东。
#define _BV(bit) (1 << (bit)),作用就是把1左移bit位。

我们继续分析刚才发出的RELEASE命令,
latch_state &= ~_BV(a);,把latch_state的第a位清零。
latch_state &= ~_BV(b);,把latch_state的第b位清零。
MC.latch_tx();调用AFMotorController类latch_tx函数。

回过头再分析run函数的前半段

void AF_DCMotor::run(uint8_t cmd) {
  uint8_t a, b;
  switch (motornum) {
    case 1:
      a = MOTOR1_A; b = MOTOR1_B; break;
    case 2:
      a = MOTOR2_A; b = MOTOR2_B; break;
    case 3:
      a = MOTOR3_A; b = MOTOR3_B; break;
    case 4:
      a = MOTOR4_A; b = MOTOR4_B; break;
    default:
      return;
  }
  ...
}

继续查看MOTOR1_A的定义

#define MOTOR1_A 2
#define MOTOR1_B 3
#define MOTOR2_A 1
#define MOTOR2_B 4
#define MOTOR4_A 0
#define MOTOR4_B 6
#define MOTOR3_A 5
#define MOTOR3_B 7

结合这个定义,我们可以得出结论:
latch_state变量的2,3位对应MOTOR1,也就是扩展板上看到的M1。
1,4位对应M2,
5,7位对应M3,
0,6位对应M4。

继续分析latch_tx函数

void AFMotorController::latch_tx(void) {
  uint8_t i;

  //LATCH_PORT &= ~_BV(LATCH);
  digitalWrite(MOTORLATCH, LOW);

  //SER_PORT &= ~_BV(SER);
  digitalWrite(MOTORDATA, LOW);

  for (i = 0; i < 8; i++) {
    //CLK_PORT &= ~_BV(CLK);
    digitalWrite(MOTORCLK, LOW);

    if (latch_state & _BV(7 - i)) {
      //SER_PORT |= _BV(SER);
      digitalWrite(MOTORDATA, HIGH);
    } else {
      //SER_PORT &= ~_BV(SER);
      digitalWrite(MOTORDATA, LOW);
    }
    //CLK_PORT |= _BV(CLK);
    digitalWrite(MOTORCLK, HIGH);
  }
  //LATCH_PORT |= _BV(LATCH);
  digitalWrite(MOTORLATCH, HIGH);
}

这段代码根据latch_state各个位的状态开或关MOTORDATA引脚。
如果不详细学习这个硬件知识,完全搞不懂这是在做什么。
目前只要知道通过这些操作,马达是可以被有效控制的就足够了。

6.3.2.5 马达前进

  ...
  motor.run(FORWARD);
  for (i = 0; i < 255; i++) {
    motor.setSpeed(i);
    delay(10);
  }

  for (i = 255; i != 0; i--) {
    motor.setSpeed(i);
    delay(10);
  }
  ...

经过刚才的分析,这一块的代码就很简单了。
motor.run(FORWARD)让车轮向前转
第一个for循环i逐渐变大,motor.setSpeed(i)使车轮速度越来越快。
第一个for循环i逐渐减小,motor.setSpeed(i)使车轮速度越来越慢到最后停止。

6.3.2.6 马达后退

  motor.run(BACKWARD);
  for (i = 0; i < 255; i++) {
    motor.setSpeed(i);
    delay(10);
  }

  for (i = 255; i != 0; i--) {
    motor.setSpeed(i);
    delay(10);
  }

其他代码比较简单,就不再分析了。

6.4 连接模块

技术分享

蓝牙模块还是5.2节的连接方法。
这里要注意扩展板和Mega的连接方式。扩展板没有Pin脚的这头和Mega没有Pin脚的这头放一边,扩展板0 Pin脚和Mege 0 Pin脚重合,图中黄线所示。

6.5 测试

上传程序到开发板,可以观察到马达先向前转,速度从慢到快,又从快到慢。
接着马达向后转,速度从慢到快,又从快到慢。

手把手教你做蓝牙小车(三)

标签:

原文地址:http://blog.csdn.net/anddlecn/article/details/51724969

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!