Category Archives: Arduino小组

Arduino实践-弹性小按键到矩阵键盘

上图所示是Arduino常用来输入的弹性小按键。如图所示,弹性小按键有四个引脚,内部工作原理是,1和2端口已连接在一个弹片上,3和4端口连接在另外一个弹片上,当小按键被按下,弹性按键内部的两个弹片相互接触,当松开按键时弹片由于弹性恢复分离状态。因此使用按键时,从1或2端口中选一个连入电路一段,3或4连入电路另一端。
使用Arduino检测弹性按键是否被按下,我们先把按键如下图所示接好

如图所示,当弹性小按键被按下时,数字端口2将直接和地线GND相连,即对配置为输入的数字端口2输入低电平,该实例程序可以在Arduino IDE环境下的File->Examples->Digital->Button中找到

const int buttonPin = 2; //连接按键的数字端口编号
const int ledPin = 13; //连接LED的数字端口编号

//定义随按键状态而改变的变量
int buttonState = 0;

void setup() {
  //初始化LED驱动端口为输出
  pinMode(ledPin, OUTPUT);
  //初始化按键端口为输入
  pinMode(buttonPin, INPUT);
}

void loop(){
  //读取按键端口状态
  buttonState = digitalRead(buttonPin);

  //检测按键是否被按下
  //如果是,变量buttonState为常量值 HIGH:
  if (buttonState == HIGH) {
    //打开LED:
    digitalWrite(ledPin, HIGH);
}
else {
  //熄灭LED
  digitalWrite(ledPin, LOW);
  }
}

上述方法的按键与Arduino连接时,每一个按键都需要占用Arduino一个端口,如果我们需要较多的按键则会遇到麻烦,于是我们引入矩阵键盘,将所有按键按行和列排布。这种键盘有现成零件出售,并且也有现成的库可用,省去使用时可能带来的技术细节问题。

我们以4×4矩阵键盘为例来说明使用方法。将16个按键排成4行4列,每一行将每个按键的一段连接在一起构成行线,每一列也类似,这样便一共有4行4列共8根线,我们将这8根线依图连接到Arduino的8个数字I/O口上。
检测的原理是,先送一列低电平,其余列均为高电平,然后立即轮流检测一次各行是否有低电平,若没有则说明送低电平这一列没有键被按下,然后继续轮次送低电平到其余列扫描。若一次送低电平到列,并逐检测每行的过程中有低电平,则该行有按键被按下,而送低电平的列则为被按下键的列数。行数、列数均确定后,该按键即被确定。因为Arduino逐行逐列扫描和检测的速度足够快,所以你无须担心它会遗漏被你按下的键。
该示例程序中,我们定义和使用了一个字符二维数组,多维数组属于程序设计中相对高阶的内容,第二篇中没有讲述,这里补充说明一下。
在本站C语言介绍的文章中,我们简要介绍了数组这种数据类型。数组就是给一类型的简单数据依次编号,a[0]、a[1]、a[2]、a[3]…。但这种数据类型在某些场合是不够方便的,譬如,我要定义一个数组,记录9×9乘法表的结果。记录99乘法表结果的最佳办法不是定义一个长度为81的数组,而是定义一个9行9列的数组,这样,引用数组元素的下标就有两个,行和列,这样有两个下标量的数组称为二维数组,依次类推还有三维数组等等。

 

显然,二维数组给记录键盘按键字符这样的数据带来了方便,譬如我需要知道键盘第2行第3列的数据,只要引用已经定义好的二维数组hexaKeys中的hexaKeys[2][3]就行。
错啦!变态的程序员都是从零开始数数,而不是一。所以引用第二行第三列的数据应该是hexaKey[1][2]。在二维数组定义之初,我们就可以对其进行赋值,以上述4×4键盘为例,其格式为

char hexaKeys[ROWS][COLS] = {
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};

以下是连线硬件图

#include <Keypad.h>

const byte ROWS = 4; //四行
const byte COLS = 4; //四列
//定义键盘上的按键标识
char hexaKeys[ROWS][COLS] = {
  {'1','2','3','A'},
  {'4','5','6','B'},
  {'7','8','9','C'},
  {'*','0','#','D'}
};
byte rowPins[ROWS] = {4, 5, 6, 7}; //连接到行扫描的输入输出端口
byte colPins[COLS] = {8, 9, 10, 11}; //连接到列扫描的输入输出端口

//定义Keypad类的实例
Keypad customKeypad = Keypad( makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);

void setup(){
  Serial.begin(9600);
}

void loop(){
  char customKey = customKeypad.getKey();

  if (customKey){
    Serial.println(customKey);
  }
}

该程序的作用是,不断检测键盘是否有按键被按下,如果有将被按下的键检测出来,然后打印到出口输出。

打开Arduino IDE自带的串口监控窗口,当我按下小键盘上的一些按键时,监控窗口将会显示一系列对应的字符,如图所示。

Arduino从面向过程到面向对象

如果你以前没有过面向对象程序设计的经历。上一节介绍的1602的一些示例代码,可能会让你感到困惑。回顾之前出现过的器件,譬如LED、变阻器等。它们都是直接输入输出一些比较简单的数字量或者模拟量。但是1602液晶屏却不一样,它的屏幕上最多能同时显示32个字符,传统的方法显然不适合操纵它。那怎么办呢?
为了从处理繁琐的细节问题中解脱出来,程序设计言语发展史上提出大规模的程序设计应该从面向过程转而面向对象的。而要说明面向对象,就绕不开对象和类的概念。

对象(object)
对象可以看成是计算机世界对现实世界的一种模拟。譬如仙剑奇侠传的游戏中,男主角李逍遥就是一个对象。为了便于和真实世界的武侠类比,他可以有类似属性和方法。譬如,体力和真气,可以用一个int类型的整数来描述。李逍遥这个对象还可以有些动作,譬如说,行走和快速行走,但这会消耗体力;它还可以喝酒,就会补充真气;他还可以施展已经习得的法术,譬如万剑诀,这会较少相应敌方对象的体力。如果体力低于零或者等于零,就会触发死亡,游戏结束的事件。
对象是作用是封装和抽象。封装后的对象李逍遥只剩下几个简单的接口,从外部看来,我们只需要对这些属性或者方法进行操作即可。

类(class)
类是一个模板或者说一个蓝图,譬如说林月如和李逍遥一样,都有体力值和真气值,都能行走、快速行走和饮酒,但是不会万剑诀,她会的是一阳指。还有赵灵儿也基本类似,这几个具体的对象都有一些类似的属性和方法,于是,我们把这些共同的属性抽象成一个模板我们命名为武侠人物,武侠人物就是一个类。此外,还需要介绍共有(public)的私有(private)这两个概念。其实共有就是对象的这个属性或者方法在对象之外都可以直接操作,但私有的属性或者方法只能由这个对象内部的其它方法来操作,在武侠人物这个类中,体力值和真气值就是这样的类型,它不应该允许外部直接操作,必须经过一些内部机制,譬如饮酒增加真气值来做到。
我们可以用伪代码描述如下:
class 武侠人物
{
private:
int 体力值;
int 真气值;
public:
void 行走()
{
体力值--;
}
void 快速行走()
{
体力值 = 体力值 – 2;
}
void 饮酒()
{
真气值++;
}
}
有没有发现公共的方法很像是函数,没错,在面向过程的程序设计中,我们称之为函数或者过程,在面向对象的程序设计中,我们称之为方法。此后,除特别声明外,方法、函数和过程这三者可在一定上下文环境内互换,不做特别区分。
构造函数(constructor)

用武侠人物的模板,创造出一个具体的武侠人物李逍遥。这个过程叫做类的实例化(instantiation)。在这个实例化的过程中,我们往往需要做一些初始化的工作,譬如设定李逍遥的体力值和真气值。这就需要构造函数,就像人这个类,不管张三还是李四,都需要有出生这个过程,而构造函数就是实例出生的函数。

构造函数的函数名和类名一样,并且如果你不在类的定义中声明,它也是默认存在的(系统会在装载程序到Arduino前在后台默认加上),只是没有参数。譬如前面的class 武侠人物,它默认的构造函数就是
武侠人物()
{
}
请注意,构造函数前面没有返回类型,即使是写成void空类型也不行,即
void 武侠人物()
是完全错误的写法。
还有一个问题没有解决,那就是,创建类的实例时,往往需要传入一些参数,譬如初始的体力值和真气值。那么这个时候,就不能使用默认没有参数传入的构造函数了。我们将上诉武侠人物这个类的定义修改如下,加入重新定义的构造函数
class 武侠人物
{
private:
//此处略去类的属性成员
public:
武侠人物(int 初始体力值, int 初始真气值)
{
体力值 = 初始体力值;
真气值 = 初始真气值;
}
//此处略去类的其他方法成员
}
下面我们用 武侠人物 这个类 来创建一个实例 李逍遥 ,并且让它的初始体力值为80,初始真气值为60
武侠人物 李逍遥(80,60);
然后我们让李逍遥这个对象快速前进,则其体力值会由80减少到78
李逍遥.快速行走();
通过上述的比喻说明,读者对面向对象的程序设计是否有了一点眉目?接下来回归正题,运用面向对象的思想,把1602液晶屏视为一个对象,来逐步分析上一小节的程序背后的一些东西。
#include <LiquidCrystal.h>
这句话表示在装载Arduino程序前,先将LiquidCrystal.h这个头文件(head file)引用进来,那么在这个头文件中所包含的类定义就会生效。这个文件在哪里呢?我们先找到Arduino目录下的libraries文件夹,我们发现有个LiquidCrystal命名的文件夹(目录)。打开它,看看里面都有些什么?

这时候我们终于看到了这个文件的庐山真面目!此外我们还看到两个文件keywords.txt和LiquidCrystal.cpp。我们用UltraEdit这样的专业文本编辑工具分别打开这三个文件,因为UltraEdit这一类专业工具能够根据语法自动识别并用颜色标记不同功能的字符。
看着这些直接操作硬件底层的晦涩代码,很容易让人似懂非懂,甚至是云里雾里。不要这不要紧,我们的目的不是要读懂它,至少现在不需要。但是,针对这三个文件的功能,还是需要知道一些简单的介绍。

图 头文件LiquidCrystal.h

这个文件一般称为头文件(head file),后缀名.h,除后缀门外,其文件名与类名相同,这是约定的。其中的一些内容主要是一些常量的定义和类的定义(见图中箭头所示),一般采用#define预处理命令。此外,它里面往往完整的定义了整个类的成员,包括属性和方法。但是具体方法的相关执行代码,则没有给出。

图 C++源文件LiquidCrystal.cpp

这个文件一般称为源文件(source file),后缀名.cpp(C plus plus),除后缀门外,其文件名与类名相同,这也是约定的。与头文件相对,它往往作为补充同名头文件中相关方法成员的具体执行代码。如图中箭头所指为begin()方法的具体执行代码。

图 说明文件keyword.txt和Arduino官方参考

keyword.txt这个文件和简明扼要的记录了LiquidCrystal类中可供外部调用的方法,它的名字是固定的。其实对于LiquidCrystal这样Arduino内置的类,它的方法在菜单中选择help->Reference,然后在打开的网页中选择Libraries->LiquidCrystal,则弹出如图所示的详细列表,详细参考中对该类方法的叙述会更加详细和权威,请读者自己参阅。
此外,图的目录内容中还有一个examples的文件夹,打开一看,其中的内容正好和菜单里的示例对应,所以每个子文件夹下的.ino文件其实就是菜单中看到的范例。

这一节讲述的思想非常重要,Arduino之所以简单高效,就是因为稍微复杂一点的器件都已经有了简单易用的类。结合本节内容,请重新仔细阅读上一节的代码以及其中的注释,相信你一定会受益匪浅。

Arduino让1602液晶屏好容易

1602液晶显示屏(LCD)
字符型型液晶是一种用5×7点阵图形来显示字符的液晶显示器,根据显示的容量可以分为1行16个字、2行16个字、2行20个字等,最常用的为2行16个字,即右图所示的1602液晶模块。它只能显示ASCII码,不能显示中文字符。
1602字符型LCD模块的应用广泛,各种液晶厂家均有提供几乎同规格的1602模块,1602字符型LCD模块最初采用的LCD控制器采用的是HD44780,在各厂家生产的1602模块当中,基本上也都采用了与之兼容的控制IC,所以特性上基本一致;当然,很多厂商提供了不同的字符颜色、背光色之类的显示模块。其重要安装尺寸及引脚说明如下所示

编号 符号 引脚说明 编号 符号 引脚说明
1 VSS 电源地 9 D2 数据I/O
2 VDD 电源正极 10 D3 数据I/O
3 VL 液晶显示偏压信号 11 D4 数据I/O
4 RS 数据/命令选择端(H/L) 12 D5 数据I/O
5 R/W 读/写选择端(H/L) 13 D6 数据I/O
6 E 使能信号 14 D7 数据I/O
7 D0 数据I/O 15 BLA 背光源正极
8 D1 数据I/O 16 BLK 背光源负极

参考其数据手册及Arduino的LiquidCrystal库只需将其引脚参照上述方式一半字节(4路数据口)相连即可,如图所示
我们依照图纸连接好1602液晶屏和变阻器,接线稍有些复杂,如果后续步骤不能显示出相关结果,请重新检查连接是否缺漏。接着在Arduino IDE下,如图找出现成的示例程序File->Examples->LiquidCrystal->HelloWorld,然后将程序下载到Arduino的控制板中。

除了HelloWorld这个示例程序外,1602液晶屏的示例程序还有Autoscroll、Blink、Cursor、CustomCharacter、Dispaly、Scroll、SerialDispaly、setCursor、TextDirection都可以分别装载到Arduino控制板中去,试试运行他们,看看会有啥效果。本节所使用的代码可能会令读者感到困惑,不必理会和完全理解代码中的含义,只要动手体验一下即可,其具体内容会下下一节中详细讲述。

/*
  电路:
 * LCD RS引脚连接到Arduino的数字端口12
 * LCD Enable引脚连接到Arduino的数字端口11
 * LCD D4引脚连接到Arduino的数字端口5
 * LCD D5引脚连接到Arduino的数字端口4
 * LCD D6引脚连接到Arduino的数字端口3
 * LCD D7引脚连接到Arduino的数字端口2
 * LCD R/W引脚连接到Arduino的地线(GND)
 * 10K(10千欧变阻器):
 * 两端分别连接到Arduino的+5V和地线(GND)
 * 滑片端连接到 LCD VO 端口 (3号引脚)
 此处注释省略了部分贡献者信息,该代码属于公共域

//引用相关库文件
#include 

//根据引脚初始化库
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

void setup() {
  //设置液晶屏的行数和列数
  lcd.begin(16, 2);
  //打印字符串到液晶屏
  lcd.print("hello, world!");
}

void loop() {
  //将光标设置到第1行,第0列
  // (注释:第1行其实是第2行,因为是从0开始计数的):
  lcd.setCursor(0, 1);
  //打印运行后的时间
  lcd.print(millis()/1000);
}

用Arduino奏出你要的音乐

蜂鸣器是一种一体化结构的电子讯响器,采用直流电压供电,广泛应用于报警器、电子玩具、汽车电子设备、电话机等电子产品中作发声器件。轮式移动机器人上采用的是右图所示的有源自激蜂鸣器。由于自激蜂鸣器是直流电压驱动的,不需要利用交流信号进行驱动单片机驱动他激蜂鸣器的方式有两种:一种是PWM 输出口直接驱动,另一种是利用I/O 定时翻转电平产生驱动波形对蜂鸣器进行驱动。
PWM 输出口直接驱动是利用PWM 输出口本身可以输出一定的方波来直接驱动蜂鸣器。在单片机的软件设置中有几个系统寄存器是用来设置PWM 口的输出的,可以设置占空比、周期等等,通过设置这些寄存器产生符合蜂鸣器要求的频率的 波形之后,只要打开PWM 输出,PWM 输出口就能输出该频率的方波,这个时候利用这个波形就可以驱动蜂鸣器了。比如频率为2000Hz 的蜂鸣器的驱动,可以知道周期为500μs,这样只需要把PWM 的周期设置为500μs,占空比电平设置为250μs,就能产生一个频率为2000Hz 的方波,通过这个方波就可以去驱动这个蜂鸣器了。调节占空比不同的PWM方波,即可产生不同音调的鸣叫声了。

蜂鸣器是一种根据电压频率发出响声的电子元件,还记得家里的台式电脑开机时的“滴”一声吗?那就是接在主板上的蜂鸣器发出的声音。

按照驱动方式的不同,可分为:有源蜂鸣器(内含驱动线路)和无源蜂鸣器(外部驱动)
外观如图所示。左边的是无源蜂鸣器,右边的是有源蜂鸣器。

从外观上看,两种蜂鸣器好像一样,但仔细看,两者的高度略有区别,有源蜂鸣器,高度为9mm,而无源蜂鸣器高度为8mm。如将两种蜂鸣器的引脚都朝上放置时,可以看出有绿色电路板的一种是无源蜂鸣器,没有电路板而用黑胶封闭的是有源蜂鸣器。进一步判断有源蜂鸣器和无源蜂鸣器,可以用万用表最低量程的电阻档测试:用黑表笔接蜂鸣器 "+"引脚,红表笔在另一引脚上来回触碰,贴近耳朵听,如果发出咔、咔声的是无源蜂鸣器;如果能发出持续声音的,电阻在几百欧以上的,是有源蜂鸣器。有源蜂鸣器直接接上额定电源,就可连续发声;而无源蜂鸣器则和电磁扬声器一样,需要接在音频输出电路中才能发声。
以电磁式蜂鸣器为例说明其工作原理,它由振荡器、电磁线圈、磁铁、振动膜片及外壳等组成。接通电源后,振荡器产生的音频信号电流通过电磁线圈,使电磁线圈产生磁场。振动膜片在电磁线圈和磁铁的相互作用下,周期性地振动发声。
下面是用Arduino去控制蜂鸣器发声的连线图及程序源代码,有源和无源蜂鸣器都可以用同样的方式去控制。

int buzzer=8;//设置控制蜂鸣器的数字IO脚
 
void loop() 
{ 
	unsigned char i,j;//定义变量
	while(1) 
	{ 
		for(i=0;i&lt;80;i++)//输出一个频率的声音 
		{ 
			digitalWrite(buzzer,HIGH);//发声音 
			delay(1);//延时1ms 
			digitalWrite(buzzer,LOW);//不发声音 
			delay(1);//延时ms 
		} 
		for(i=0;i&lt;100;i++)//输出另一个频率的声音 
		{ 
			digitalWrite(buzzer,HIGH);//发声音 
			delay(2);//延时2ms 
			digitalWrite(buzzer,LOW);//不发声音 
			delay(2);//延时2ms 
		} 
	} 
}

想用Arduino奏出美妙的音乐吗?其实用不同频率的PWM波去驱动蜂鸣器即可得到不同的旋律,下面的范例就是以不同频率的PWM方波产生不同的声调。如果你家里还有废旧收音机,还可以拆下来使用,那样的效果可是更好的哦!

/*旋律
*本例使用蜂鸣器或扬声器来演奏简单的旋律,
*它通过驱动不同频率的方波,产生相应的声调
* 下面是计算声调和频率的参考公式
*		高电平时间=周期/2=1/(2*声调频率)
* 不同声调的频率如下表所示:
 *
 * 音符 	         频率 	        周期     高电平时间
 * c 	        261 Hz 	        3830 	    1915 	
 * d 	        294 Hz 	        3400    	1700 	
 * e 	        329 Hz 	        3038    	1519 	
 * f 	        349 Hz 	        2864 	    1432 	
 * g 	        392 Hz 	        2550 	    1275 	
 * a 	        440 Hz 	        2272 	    1136 	
 * b 	        493 Hz 	        2028	    1014	
 * C	            523 Hz	        1912    	956
 *代码说明参考网址
 * http://www.arduino.cc/en/Tutorial/Melody
 */
 
int speakerPin = 9;
 
int length = 15; // the number of notes
char notes[] = "ccggaagffeeddc "; // a space represents a rest
int beats[] = { 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 4 };
int tempo = 300;
 
void playTone(int tone, int duration) {
  for (long i = 0; i &lt; duration * 1000L; i += tone * 2) {
    digitalWrite(speakerPin, HIGH);
    delayMicroseconds(tone);
    digitalWrite(speakerPin, LOW);
    delayMicroseconds(tone);
  }
}
 
void playNote(char note, int duration) {
  char names[] = { 'c', 'd', 'e', 'f', 'g', 'a', 'b', 'C' };
  int tones[] = { 1915, 1700, 1519, 1432, 1275, 1136, 1014, 956 };
 
  // play the tone corresponding to the note name
  for (int i = 0; i &lt; 8; i++) {
    if (names[i] == note) {
      playTone(tones[i], duration);
    }
  }
}
 
void setup() {
  pinMode(speakerPin, OUTPUT);
}
 
void loop() {
  for (int i = 0; i &lt; length; i++) {
    if (notes[i] == ' ') {
      delay(beats[i] * tempo); // rest
    } else {
      playNote(notes[i], beats[i] * tempo);
    }
 
    // pause between notes
    delay(tempo / 2); 
  }
}

Arduino操纵发光二极管(LED)

发光二极管英语Light-Emitting Diode缩写LED[1] 是一种能发光的半导体电子元件,透过三价与五价元素所组成的复合光源。此种电子元件早在1962年出现,早期只能够发出低光度的红光,被hp买下专利后当作指示灯利用。及后发展出其他单色光的版本,时至今日,能够发出的光已经遍及可见光、红外线紫外线,光度亦提高到相当高的程度。用途由初时的指示灯及显示板等;随着白光发光二极管的出现,近年逐渐发展至被普遍用作照明用途。

发光二极管只能够往一个方向导通(通电),叫作正向偏置(正向偏置),当电流流过时,电子与空穴在其内重合而发出单色光,这叫电致发光效 应,而光线的波长、颜色跟其所采用的半导体物料种类与故意掺入的元素杂质有关。具有效率高、寿命长、不易破损、反应速度快、可靠性高等传统光源不及的优 点。白光LED的发光效率近年有所进步;每千流明成本,也因为大量的资金投入使价格下降,但成本仍远高于其他的传统照明。虽然如此,近年仍然越来越多被用 在照明用途上。

——————本条解释源自维基百科

发光二极管应该是最常见最简单的输出器件了,因为它通常只有两种状态。亮和灭,市面上常见的发光二极管就有贴片式和直插式两类。下面简要介绍一下。
贴片式的在你的Arduino板子上就有几个,方形的透明小玩意,它们能告诉你一些简单的信息,譬如电路板上丝印层写着TX的LED如果亮起,则表明板子的主控器正在使用串口向外发送数据,同样RX的LED亮起表示串口正在接收数据。

上图所示的常见的直插引脚式发光二极管(LED),上图发光二极管有Ф3mm和Ф5mm两种,指的是发光二极管的直径。由上述照片可以看出,它们的引脚都有长短之分,长的引脚是正极,短的引脚是负极。只有当电流从长引脚流入,从短引脚流出时,LED才会发光。这种接线引脚有正负极之分的原件通常称为极性原件。
除了要注意LED的连接方向之外,一定不要忘记需要串联电阻,因为如果直接将发光二极管的正负极和电源或者端口的正负极相连,会导致发光二极管通过的电流过大而烧毁。发光二极管方向接反只是无法正常发亮工作,不会造成发光二极管的损坏。让我们根据欧姆定律来点小计算。
根据我们查阅相关手册(查阅的方法11.2节会叙述),我们知道一般的LED只需通过5mA的电流即可发光,一般不应超过20mA。当发光二极管发光时,其两端的电压约为1.7V,Arduino端口提供的是5V的电压,则串联电阻两端的压降为5V-1.3V=3.3V,串联一个330Ω的电阻后,3.3V/330Ω=0.01A=10mA。看来330Ω的电阻能够符合要求。

根据上面的电路原理图和面包板接线图,我们连好流水灯所需的电路,开始往Arduino中装载程序,其源代码如下所示。

int Led1 = 4; 
int Led2 = 5; 
int Led3 = 6; 
int Led4 = 7; 
int Led5 = 8; 
int Led6 = 9;
int Led7 = 10;
int Led8 = 11;
 
//led灯花样显示样式1子程序 
void style_1(void) 
{ 
  unsigned char j; 
  for(j=4;j&lt;=11;j++)//每隔200ms依次点亮4~11引脚相连的led灯    {      digitalWrite(j,HIGH);//点亮j引脚相连的led灯      delay(200);//延时200ms    }    for(j=11;j&gt;=4;j--)//每隔200ms依次熄灭11~4引脚相连的led灯 
  { 
    digitalWrite(j,LOW);//熄灭j引脚相连的led灯 
    delay(200);//延时200ms 
  } 
} 
//灯闪烁子程序 
void flash(void) 
{ 
  unsigned char j,k; 
  for(k=0;k&lt;=1;k++)//闪烁两次 
    {
    for(j=4;j&lt;=11;j++)//点亮4~11引脚相连的led灯 
    digitalWrite(j,HIGH);//点亮不j引脚相连的led灯 
    delay(200);//延时200ms 
    for(j=4;j&lt;=11;j++)//熄灭4~11引脚相连的led灯      digitalWrite(j,LOW);//熄灭不j引脚相连的led灯      delay(200);//延时200ms    }  }  //led灯花样显示样式2子程序  void style_2(void)  {    unsigned char j,k;    k=1;//设置k的数值为1    for(j=7;j&gt;=4;j--) 
  { 
    digitalWrite(j,HIGH);//点亮灯 
    digitalWrite(j+k,HIGH);//点亮灯 
    delay(400);//延时400ms 
    k +=2;//k值加2 
  } 
  k=7;//设置k值为5 
  for(j=4;j&lt;=7;j++) 
  { 
    digitalWrite(j,LOW);//熄灭灯 
    digitalWrite(j+k,LOW);//熄灭灯 
    delay(400);//延时400ms 
    k -=2;//k值减2 
  } 
} 
//led灯花样显示样式3子程序 
void style_3(void) 
{ 
  unsigned char j,k;//led灯花样显示样式3子程序 
  k=7;//设置k值为5 
  for(j=4;j&lt;=7;j++)    {      digitalWrite(j,HIGH);//点亮灯      digitalWrite(j+k,HIGH);//点亮灯      delay(400);//延时400ms      digitalWrite(j,LOW);//熄灭灯      digitalWrite(j+k,LOW);//熄灭灯      k -=2;//k值减2   }    k=3;//设置k值为3    for(j=6;j&gt;=4;j--) 
  {
    digitalWrite(j,HIGH);//点亮灯 
    digitalWrite(j+k,HIGH);//点亮灯 
    delay(400);//延时400ms 
    digitalWrite(j,LOW);//熄灭灯 
    digitalWrite(j+k,LOW);//熄灭灯 
    k +=2;//k值加2 
  } 
} 
void setup() 
{ 
  unsigned char i; 
  for(i=4;i&lt;=11;i++)//依次设置4~11个数字引脚模式 
  pinMode(i,OUTPUT);//设置第i个引脚为输出模式 
} 
void loop() 
{ 
  style_1();//样式1 
  flash();//闪烁 
  style_2();//样式2 
  flash();//闪烁 
  style_3();//样式3 
  flash();//闪烁 
}

 

Arduino模拟量的输入和PWM输出

模拟量的输入

在家里的开关,除了控制电灯亮灭的二值状态(0或1)的开关,我们往往还会引入一种可以连续的旋钮以便连续控制某一物理量,譬如睡灯的亮度、风扇的转速。无意中,我们就引入了一种称之为模拟量的东西。自然界中的物理量往往都是模拟量,譬如温度、湿度、气压等,我们用一类叫做传感器的电子装置把这些物理量转化为电学量(如电压、电流)。然后和Arduino的模拟量端口连接起来,让Arduino可以读取。

 

                                  
上图所示,就是电子市场上常见的一种元件,俗称电位器,你家里应该就有不少这个,譬如音响上调节声音大小的旋钮,用来调节吊扇无级变速的开关等等。其实它和你在初中物理中所学的电阻器没什么两样,只是实际应用中没什么可能弄个那么大的变阻器,它往往只是一种教学演示器材或者在电流很大等特殊场合才会使用。如果你拆开电位器,你就会发现,它里面其实就是一个金属接触片在270°的扇形碳膜上接触。如果你还稍加一点点想象,它是不是就是一个简单的角度传感器,将角度的大小通过阻值的大小转换,最终变成电压,或者说模拟信号以便用于输入到Arduino控制板中。只是由于碳膜和金属接触片接触过程中必然会因为接触面粗糙或存在杂质等磕磕碰碰,从而造成噪声并最终转化为干扰信号,显然,其动态性能并不太好。
我们从一个简单的实验开始,打开Arduino的开发环境,调出例子模拟输入(File->Example->Analog->AnalogInput)。程序一开始就有大段用/*标记开始和*/标记结束的灰色段落,是注释。它分别说明了这段程序功能,电路的接线方法以及作者和开源声明。这种注释一般称为块注释(block comments),它所注释的内容往往跨行,但是/* */注释对不能嵌套,否则会出错,你可以试试看。而//称为行注释(line comments),从它开始到本行结束,或者说遇到换行符为止。此外,我建议往程序的setup()和loop()过程中分别添加如下所示的波浪线语句,以方便我们从串口监控程序状况。

int sensorPin = A0; //选择电位器的引脚
int ledPin = 13; // 选择LED(发光二极管)的引脚
int sensorValue = 0; // 存储由传感器传来的值
 
void setup() {
  //定义LED(发光二极管)引脚为输出模式
  pinMode(ledPin, OUTPUT);
  //配置串口波特率,为9600
  Serial.begin(9600);
}
 
void loop() {
  //读取传感器的值,并把值赋给变量sensorValue
  sensorValue = analogRead(sensorPin);
  //打开LED(发光二极管)
  digitalWrite(ledPin, HIGH);
  //持续时间为sensorValue个毫秒
  delay(sensorValue);
  //关闭LED(发光二极管)
  digitalWrite(ledPin, LOW);
  //持续时间为sensorValue个毫秒
  delay(sensorValue);
  //向串口打印sensorValue的值
  Serial.print(sensorValue);
  //向串口打印一个制表符
  Serial.print('\t');
  //转换sensorValue为对应的电压值并打印到串口
  Serial.print(sensorValue*5.0/1023);
  //给电压值补上单位V(伏特)
  Serial.println("V");
}

按图示接好线后,打开串口监视程序(Tools->Serial Monitor),扭动电位器,我们就可以看到串口输出的值从0到1023,对应的电压从0V到5V。由此可知,Arduino对于模拟量的输入,其采样精度是10位的,因为210=1024,分辨率就是5V/1024=0.0049V,也就是说两个相差小于0.0049V的电压差Arduino无法分辨。此外,Arduino每隔100µs(微秒)=0.0001s(秒)对信号进行采样,将模拟量转换为0至1023中的一个值。这个采样的时间间隔,称之为采样周期,它的倒数(f=1/T),称为采样频率。


模拟量的输出
接下来我们要谈到的是模拟量的输出。Arduino并不能直接输出较为精确的模拟量,而是以一种称为PWM(脉宽调制)方波的形式来间接输出模拟量的。脉宽调制的原理请见下图,


同样周期的脉冲方波,其高电平所占据整个周期的比例将会影响到诸如发光二极管、电机等装置的效果。还记得上一节的程序吗?扭动电位器,当闪烁的时间间隔很小,如10ms以下时,你是否发现肉眼已经无法分辨,而觉得发光二极管L是持续点亮的。下面,我们对程序做一些小小的改动,来验证我们的想法。
map()函数是一种映射,它将从一个区间的相对值映射成另一个区间的相对值并返回。譬如我们规定冰点时温度为0摄氏度,沸点为100摄氏度,而华氏温度把冰点温度定为32华氏度,沸点为212华氏度。根据这一关系,我们就可以直接写出从华氏温度到摄氏温度转换的语句,Celsius = map(Fahrenheit, 32, 212, 0, 100),不过仍需注意的是map函数只能适用于不带小数的整数。

int sensorPin = A0; //选择电位器的引脚
int ledPin = 13; // 选择LED(发光二极管)的引脚
int sensorValue = 0; // 存储由传感器传来的值
int pulseWidth;
 
void setup() {
  //定义LED(发光二极管)引脚为输出模式
  pinMode(ledPin, OUTPUT);
  pinMode(6, INPUT);
  //digitalWrite(6, HIGH);
  //配置串口波特率,为9600
  Serial.begin(9600);
}
 
void loop() {
  //读取传感器的值,并把值赋给变量sensorValue
  sensorValue = analogRead(sensorPin);
  pulseWidth = map(sensorValue, 0, 1023, 0, 10000);
  //打开LED(发光二极管)
  digitalWrite(ledPin, HIGH);
  //根据输入模拟量的大小,控制延迟时间以达到调节占空比的目的
  delay(pulseWidth/1000);
  //关闭LED(发光二极管)
  digitalWrite(ledPin, LOW);
  //根据输入模拟量的大小,控制延迟时间以达到调节占空比的目的
  delay((10000-pulseWidth)/1000);
  //向串口打印sensorValue的值
  Serial.print(map(sensorValue, 0, 1023, 0, 100));
  //向串口打印一个制表符
  Serial.print("%");
  Serial.print('\t');
  //转换sensorValue为对应的电压值并打印到串口
  Serial.print(sensorValue*5.0/1023);
  //给电压值补上单位V(伏特)
  Serial.println("V");
}

打开串口监控窗口,缓慢的条件电位器,你将会发现,发光二极管L逐渐的由亮变暗。不过还有一个奇特的现象,当电压模拟量由低到高的变化过程中,约在0.5V左右的,发光二极管L因为导通而陡然变亮,似乎亮度不是连续变化的。如果你学过电子技术的课程,或者见过二极管的伏安特性曲线,你就会明白这是导通前的死区照成的,你也可以凭此现象大概的估算开启电压的大小。
上面所述的确实是PWM工作的真正原理,不过这种调用delay()延时函数的方法并没有真正利用Arduino的潜能,如果我们以此方法来产生模拟量输出,不仅占用控制器的工作时间,而且难以同时输出多个模拟量。真正使用Arduino内置定时器以驱动模拟输出口输出PWM的方法如下例所示。需要注意的是,并不是所有的数字端口都能输出PWM方波的模拟量,拿起你的Arduino,仔细观察就会发现,上面标有~的6个端口才能输出模拟量,它们分别是3、5、6、9、10和11。我们选择9号端口,这次就不能利用13号端口自带的发光二极管L了,我们需要借助面包板连接电路,电路和程序如下。

int sensorPin = A0; //选择电位器的引脚
int ledPin = 9; // 选择LED(发光二极管)的引脚
int sensorValue = 0; // 存储由传感器传来的值
int pulseWidth;
 
void setup() {
  //配置串口波特率,为9600
  Serial.begin(9600);
}
 
void loop() {
  //读取传感器的值,并把值赋给变量sensorValue
  sensorValue = analogRead(sensorPin);
  pulseWidth = map(sensorValue, 0, 1023, 0, 255);
  analogWrite(ledPin, pulseWidth);
  Serial.print(map(sensorValue, 0, 1023, 0, 100));
  //向串口打印一个制表符
  Serial.print("%");
  Serial.print('\t');
  //转换sensorValue为对应的电压值并打印到串口
  Serial.print(sensorValue*5.0/1023);
  //给电压值补上单位V(伏特)
  Serial.println("V");
}

上面的示例其实就是将上例带波浪线的4行语句等价替换成简洁的analogWrite()函数来输出结果。原理其实完全相同,只是这次无须再占用控制器资源,此外,对于模拟量的PWM方式输出无须在setup()过程中调用pinMode()函数。

配套实验:AnalongInOutSerial

Arduino数字量的输入与输出

说起数字量,这是一种最简单的信号,只有两种状态,非零即一。你就当它是你家里一盏灯的开关就好,要么开(1),要么关(0)。在Arduino中,这些数字量表示为电压,高电平5V表示数字量1,低电平0V表示数字量0。这样粗略的比喻后面还有一个问题,就是到底是输入还是输出。假如是输出,那么由Arduino决定,譬如电灯,如果没有开关,我们只能看到它是亮还是暗;假如是输入,那么由外部决定,譬如开关,由你去拨动它的状态是开还是关。
拿起Arduino,你就会发现上面有标号0至13的端口,这就是Arduino Uno所有的数字量输入输出端口。打个比喻,你家有十四盏电灯,编号好从零到十三(变态的程序员都是从零开始数数,而不是一),它们可以配置成电灯输出亮或者暗,用来表达两种状态,也可以配置成开关,等待你去拨动它们。我们在setup()过程中时常要配置好每个数字量端口是用来输入还是输出的,以便在loop()过程中能够正常的使用。譬如,我要将13号数字端口配置为输出,需要在setup()过程中添加如下语句,
pinMode(13, OUTPUT);
如果我们要将0号端口配置为输入,需要在setup()过程中添加如下语句,
pinMode(0, INPUT);
请注意,INPUT和OUTPUT所以字母要全部大写,才会变成绿色,否则不能通过编译,就会出错。
还记得Blink示例程序吗?http://arduino.cc/网站中,我们可以找到Products->Arduino Uno控制板的介绍页面,获得一些该控制板的重要信息。譬如

你可以点击上图所示区域,打开查看Arduino Uno的电路原理图,用户需安装Adobe Reader。
也可以下载Eagle后打开其完整的电路图设计文档,包括印刷电路板(PCB)的布局(
Layout)。Eagle是一款完全免费且易学易用的印刷电路板设计软件,可到http://www.cadsoftusa.com/下载。
通过打开的PDF电路图文档,我们知道,Arduino板子上黄色的发光二极管(LED)是和Arduino的13号数字端口连接起来的,并串联了1kΩ的电阻来限流,以防电流过大而烧毁发光二极管(标号L)。所以当数字端口13为高电平时,无论输入或输出,标号为L的黄色发光二极管都会亮起。

现在,你应该已经可以完全读懂Blink这个示例程序了。下面我们根据这个程序,添加一些语句,使得我们可以控制板上那个黄色的发光二极管的亮灭。

int led = 13; //设定变量led并赋值为13
void setup()
{
  pinMode(led, OUTPUT); //设置13号端口为输出
  pinMode(0, INPUT); //设置0号端口为输入
}
void loop()
{
  if(digitalRead(0) == HIGH) //读取0号端口的状态,如果为高电平
    digitalWrite(led, HIGH); //13号端口为高电平,L发光二极管亮
  else //否则
    digitalWrite(led, LOW); //13号端口为低电平,L发光二极管灭
}

我们如何才能改变0号端口的状态呢?让我们先看看Arduino Uno下面的一排接线端POWER(电源)。我们一般所用到的就是5V(高电平典型值),GND(Ground电平参考点),3.3V(某些芯片或者模块需要3.3V电源驱动,如果使用5V,可能会损坏)。我们可以用面包板所用的配线,将5V接到端口0,这就相当于向0号端口输入高电平,接入GND,输入低电平。
什么,出现下图所示的红色错误信息,无法装载程序到Arduino。你是不是在装载程序的过程中没有把0和1号端口断开连接?忘了上一节所讲的,它们既是数字输入输出端口,又是用作与计算机通信的串口。


你可以使用面包板连接线连接到面包板。如下图所示,有读者可能并不知道面包板是怎样连接和使用的,稍后将做简要说明,详情请参考附录B。


配套实验:1、Blink 2、digitalReadSerial

让你的Arduino和计算机通讯

串口(RS232)是微控制器最常用的通信接口,也往往是从计算机转载程序到这些微控制器中的唯一方法。不过由于现在很多人使用的笔记本电脑上已经没有了传统的串口,见图,而台式机上基本还保留着。Arduino的设计者们早就考虑到了这一点,所以在Arduino里面其实是有一个USB到串口的芯片,这也是之前要安装驱动程序的原因。这样你就完全可以使用USB连接线让Arduino和计算机连接起来,并完全模拟成串口通信的方式进行程序的装载和它们之间的通信。


粗略的说来,串口其实只有Tx和Rx两条数据线,如图所示

查看Arduino上的串口,查看电路板上的丝印层,可以知道占用的是引脚0和引脚1,在装置程序时用它和计算机通信用它,也可如图所所示方式与别的器件进行连接。所以在装载程序时,请保证这两个端口不被其他元器件占用,否则装载可能会失败。
由于Arduino几乎没有自带输出设备,通过连接后打印信息到串口并显示在计算机屏幕上将会是你日后调试程序的必备技能。下面,将以串口的输出和输入函数来说明如何使用Arduino串口。将下面9行程序输入到开发环境中,并装载到Arduino中。保持Arduino和计算机之间USB线的连接,点击开发环境菜单Tools->Serial Monitor(也可按快捷键Ctrl+Shift+M),就可看到图示的串口监控窗口。
void setup()
{
Serial.begin(9600);//设置串口的波特率为9600
}
void loop()
{
Serial.println("Hello Arduino");//输出一行Hello Arduino
delay(5000);//延时5秒
}

我们来讲解这段程序的含义。任何Arduino程序,都必须包含以下两个过程,也可以称为函数,本书后续将通用这两种名称而不再说明,其语法是这样的
返回值类型 函数名(参数)
{
函数体
}
返回值是这个函数执行完以后可能需要带回的结果,其类型可以是整数(int),实数(float)或者字符(char)等等,也可以不带回任何结果,此时应写为空(void),参数也是类似的道理,不过我们一般可以在圆括号里省略什么也不写表示空,当然,你也可以写入void以强调参数为空,但返回值类型里的void不能省略。大括号{}内就是该函数需要执行的语句。让我们来看看Arduino程序里必不可少的两个函数,setup(设置)和loop(循环)
void setup()
{
}
void loop()
{
}
当Arduino一上电启动,或电路板上那个唯一的小按键reset(复位)被按下时,Arduino将会执行一次setup函数大括号中的语句,然后重复循环执行loop函数,直至断电。通常,我们将一些在Arduino执行其功能前需要配置的一系列初始化步骤放在setup过程中,把Arduino需要不断执行的动作放在loop函数中。

Serial.begin(9600);是我们在使用串口前必须配置好的步骤,一般,我们以分号(;)来划分,每个分号隔开每个语句,所以上面程序示例中的分号都不能省略。括号中的9600是波特率,这是一个通信技术中的术语,用于表示串口传输数据的速率。初学者建议采用默认的9600,但必须记住,如果要使用串口,该配置语句必须在串口使用之前已被执行过。
Serial.println("Hello Arduino");是串口的打印行函数,是print line的简化而来,括号中的句子必须用双引号括起来,而且你会发现无法输入中文,即使从别处拷贝而来的中文,在串口监控窗口中显示的也是乱码。
delay(5000);是延时5000毫秒,即5秒。在这五秒时间里,Arduino在停顿在此语句上,什么也不做,所以我们在串口监视窗口中看到的是,每隔5秒都会输出Hello Arduino语句。你可以尝试将5000改成其他数字,甚至去掉该语句,重新装载程序到Arduino中运行,再按快捷键Ctrl+Shift+M,打开串口监视窗口,看看会发生什么现象。
每行双斜杠//后面的字符会变成灰色,这是注释,写给人看的,Arduino运行时会忽略其存在。目前Arduino的开发环境虽然已支持中文界面,但仍无法输入中文,如需书写中文注释可在其它文本编辑软件内输入中文后拷贝至Arduino的开发环境中。
注意,Arduino Mega系列控制板拥有不止一个串口,故使用时与下例有所差别,请参考附录或Arduino Reference。

检测点:

问题1,在例2-1中,串口输出的字符串“Hello Arduino”是位于loop(循环)过程中的,如果我把这个语句移至setup(设置)过程中将会发生什么现象?思考后尝试一下以验证你的判断。

实验,

指导:在开发环境下,选择 “File”->“Examples”->"Communiction"->"ASCIITable"(文件->例子->通信->ASCII码表),将这个开发环境自带的示例程序装载到你的Arduino中运行(暂不需要读懂程序内容),并按快捷键Ctrl+Shift+M打开串口监视窗口查看。

运用互联网知识库,诸如维基百科http://zh.wikipedia.org/,查询有关ASCII码的信息。

工欲善其事必先利其器-搭建Arduino开发环境

工欲善其事必先利其器-搭建开发环境

本节仅介绍在windows环境下搭建开发环境,想必使用MacLinux系统的用户都是使用计算机的老手,对于这部分读者,请参考Arduino官网或我们的博客。操作过程的众多步骤中我们省略了部分截图,如果您在搭建开发环境的过程中遇到问题,请我们提问。你学习Arduino的第一步应该走好

下面以Anduino UNO 为例,分步讲解一下安装步骤。

1.下载开发环境并解压

首先,得到一块Arduino UNO开发板,然后下载Arduino开发环境,进入http://arduino.cc/en/Main/Softwarewindows为例,下载Arduino 1.0.3 (release notes): Windows。(1.0.3是软件版本号,它随时会更新,影响本节随后所述的文件名及路径名,请读者理解后,自行下载较新版本的开发环境)。这个Arduino自带的开发环境是完全绿色的,下载arduino-1.0.3-windows.zip文件到硬盘后,解压到C:\路径下,双击C:\arduino-1.0.3目录内的arduino.exe文件即可运行开发环境。为方便使用,你可以选择图标后点击鼠标右键,发送到->桌面快捷方式,这样可以不必每次打开C:\arduino-1.0.3目录。

2.安装USB驱动

A)如果您尚未安装完整Arduino的驱动,只要你把UNO通过USB连接到电脑后,在WINDOWS下,会提示“发现新硬件”。

B)进入控制面板中的设备管理器,在有感叹号的USB Device上点右键,选择更新驱动。或者等电脑弹出“找到新的硬件向导”对话,再选择“从列表或指定位置安装”,找到你下载IDE后,解压存放位置,路径选择到drivers止步。

提示:安装Arduino 诸如Duemilanove等较早期的Arduino控制板时,需将路径指向IDE后,解压存放位置,路径选择到C:\arduino-1.0.3\drivers\FTDI USB Drivers安装驱动,并且提示窗口可能会出现两次,即驱动程序需要安装两次

C)点击下一步,选择UNO驱动,最后点击“完成”选项。

D)再次进入控制面板中的设备管理器,在端口(COM LPT)有UNO安装成功提示,如下图所示串口号是COM3(具体串口号会因计算机环境而有所差异)。

3.测试

(A) 正确的选择串口和Arduino控制板型号,Tools->Board-->UNO,Serial Port-->COM3(工具->电路板型号->UNO,串口号->COM3

(B) 驱动安装成功后,按一下UNO主板复位键。然后找到C:\arduino-1.0.3目录下的“arduino.exe”,双击打开Arduino的开发环境,也有很多资料称之为IDE(Integrated Development Environment,集成开发环境)。直接进入菜单 “File”->“Examples”->"Basics"->"Blink"(文件->例子->基础->闪烁),再点击编译Upload(上载)按键下载程序。

(C)在下载过程中,板子上的RXTX灯会闪亮,最后在IDE界面下提示“Done uploading”(完成下载),说明下载成功。黄色的灯L每隔一秒闪一次。

至此,你的第一个Arduino程序已经装载到Arduino上并成功运行了。

理念-行胜于言

笔者始终认可以下几条理念:

  1. 技术类书籍的学习不大可能类似于小说的阅读,后续内容的理解往往强烈依赖于对之前知识的掌握。因而必须要有一定的方法来证实自己已达到了体验后续更激动人心内容的能力,故而笔者会尽最大努力在每一节后都设置检测点或实验。每位读者要尽可能的尝试,让自己通过这些检测点或实验后再阅读后面的内容。

  2. 入门书籍,尤其是作为便于自学的书籍材料而非课堂讲义,一定要遵循学习者的认知规律,认清所面对的读者群体,明白他们会在一本书籍上所花费的阅读时间,放低门槛,拓宽读者群,现在诸多教材的弊病就是把教材写成包罗万象的手册。为此,我们会尽可能的遵循循序渐进的原则,尽量避免提及让读者困惑的术语。非常重要而且需要罗列的参考内容,我们会编辑类似于小手册的方式放置在附录或者博客上,以供读者可以方便查阅。

  3. 为动手实践尽可能的提供一切必要条件,我们将会为使用Arduino制作一个两轮自主移动式机器人的实例而贯穿全书。笔者也是从学校毕业的学生,体会到本书的读者可能在获取材料上缺乏经验或不能买到实惠的材料,强烈建议大家一定要动手实践,因为本书讨论的就是一个实践的主题。

  4. 这是一个网络的时代,正所谓师傅带进门,修行靠自己,要培养自己通过网络获取知识已达到自我提升的能力。

毕竟,本文属于零基础入门级,仅起到抛砖引玉的作用,加上篇幅所限,对本书主题更加深入和广泛的探讨将会放到我们的博客上,同时也欢迎广大读者和Arduino以及机器人技术的爱好者和我们交流互动。但我们仅仅欢迎那些愿意主动动脑筋去尝试解决问题的读者,对于那些仅仅需要Google一下就可知道答案的问题,以及诸如做一个毕业设计遇到问题帮我解决的人,请非诚勿扰!

检测点:

问题1,通过网络获取你需要知道有关Arduino的事情

听说你在学Arduino,据说有一种叫做LilyPad型号Arduino控制板,似乎体积特别小,不知道它是属于Arduino家族中专门面对何种应用场合而设计的呢?

提示:在Google输入LilyPad+Arduino,看不懂英文的读者,可将网址复制到谷歌翻译(网址:http://translate.google.com.hk/)后,点击翻译。

实验2Arduino开发环境中的代码是以12像素显示的,看起来太小,请将其设置为18像素,并且我不习惯英文界面,将其设置为中文。

指导:File->Preferences->Editor language:->简体中文(Chinese Simplified/ File->Preferences->Editor font size:->18

提示:设置完以后需重启Arduino开发环境方可生效。

机器人结构搭建与Makeblock入门

Makeblock是一款不错的机构搭建工具,它使得我们对于机构的理解和创新不再只局限于草纸上的理论推算或者是三维软件的仿真之中。但无论是使用机构仿真软件亦或Makeblock这样优秀的百搭平台,建立基本观念避免了盲目的探索,对快速搭建出实用的机构大有帮助。

百搭平台中最常见的两种运动副:

在使用Makeblock搭建的过程中,我们很容易发现有些零件连接在一起后就不会再有相对的运动,成为一个整体,而有些则不是,正因为如此,才有了丰富多彩的运动和动力传递形式。否则就只能是一个好看的静态模型。

为简单起见,我们就以最简单的两种相对运动,绕某一轴转动 和 沿某一直线平动 为起点,开始学习搭建中的技巧。

转动副(Turning pairs

平动副(Sliding pairs

这两种运动副最显浅易懂,而且还可以相互转化,后面我们将会看到。

不过,在搭建一个相对复杂的机构,然后给某个转动副环节加上电机企图使其运动的过程中,是否会遇到这样的问题,要么整个机构卡住了,要么机构加了电机似乎整个机构还能自由运动,于是补上一个连接想来约束,结果却又矫枉过正让机构完全无法运动。

要彻底解决这个问题,首先,我们需要先谈谈自由度的问题。

如上图所述,三个两两垂直的坐标轴构成笛卡尔坐标系XYZ。那么一个完全自由的物体,在空间中的运动可以用六个分量来描述,分别是沿XYZ三个轴的平动和转动。

嗯,这还是太复杂了,为了避免引入复杂的数学,我们再一次缩小研究范围到平面XOY,那么就没有六个自由度,而是下图所示的三个

平面的3个自由度:X方向的平动,Y方向的平动,绕Z轴的转动

转动副约束了XY两个方向的平动,保留了Z轴的转动,因此转动副减少了机构的两个自由度

同样平动副约束了XY一个方向的平动,还有Z轴的转动,因此平动副业减少了机构的两个自由度

Makeblock介绍

Makeblock是一款铝积木式的结构模块和电子模块的组合,包括基本结构部件,传动部件,电机,传感器,控制器等等。主要零部件是铝合金材质,以Arduino作为控制器。利用此平台,你可以在很短的时间里实现自己的各种创意想法。动手制作机器人或者自动化装置的原型需要制作者同时拥有机械、电子、软件编程三个方面的专业技能,这就使得DIY这类产品的门槛很高。Makeblock主要宗旨是让制作变得很简单,让初学者易于登堂入室,人人都可以动手做一些东西,并体会其中的乐趣,让大人和孩子们共同学习。

1998年,乐高(Lego)推出了它的第一款Mindstorms机器人套件,乐高的形象也由单纯的玩具制造商变成向大众提供创意原型平台而深得人心,至今该产品仍然被广泛的使用,从教育到科研、从学习到娱乐。而现在,一家在深圳的创业公司也在做着和乐高机器人看似相同当却有着更加远大理想的工作————构建开源易用的机械电子百搭平台,产品叫做“Makeblock”

和许多初学者一样,我在大学的时候经常看到别人网上发布的DIY的机器人倍感鼓舞,准备大展身手,可等到自己真正动起手来的时候却发现问题很多,让本来很好的创意荒废。不过电子方面的问题还算比较好解决的,至少可以找到一些电子爱好者咨询帮助,慢慢也还能上手。而如果你要搭建机器人这类产品,机械零件不可或缺却又困难重重。单个零件的制造不仅成本高昂,来回迭代的设计变更会严重拖延进度,你在实现想法的同时还要消耗大量不必要的精力去寻找愿意接受你制造需求的零件供应者或者代加工者。验证创意的头脑风暴阶段,除了运用软件仿真,我们更加需要简单易用的百搭平台来验证、初步实现我们的想法,然后展现给大家分享或者进行下一步的商业化。更重要的是构建这样一个电子机械兼有的百搭平台,在教育领域激发孩子们的创造力和想象力的助益更是巨大的。

要动手制作智能机器人一般需要做三个领域的工作:搭建机械结构、拼接电子硬件和编写软件程序。在以Arduino为代表的趣味电子制作和开源软硬件运动浪潮席卷国外后,动手制作一些自己感兴趣的项目已经不再是少数技术极客们的专利。随后又出现了各类易学易用的图形化编程工具,大大降低了大众进入这个领域的门槛。如今,经过多批零件的生产,Makeblock的各主要零部件的质量终于有大幅的提高,外观也采用了和苹果手机一样的铝合金氧化工艺,闪闪发亮的金色和蓝色零件对消费者也越发有吸引力。

Makeblock的特色

螺纹槽——极高灵活度的搭建

Makeblock的最主要零件就是两种主梁———双孔梁和U型梁,他们都有我们独创的专利———螺纹槽。螺纹槽的好处不言而喻,你可以在槽任何地方安装螺丝,这样,就可以搭建出任意角度或者间距的梁或者其它零件,这使得整个零件系统搭建的灵活度大大提高。

铝合金——强度和重量的完美平衡

Makeblock绝大部分零件采用铝合金制作,铝合金是典型重量轻而强度相对高的材料。采用Makeblock搭建的小车不仅不会像塑料零件那样容易松动,而且甚至整个结构承受一个人的重量都不会变形或者损坏。

多种规格多种方式——灵活配置你需要的

你可能在某些场合只需要提供很小的扭转动力,也可能需要较大的扭转动力以便于承受更重的负载。我们提供至少3种不同规格的直流电机,供你不同的需求来选择。又或者你并不需要很大的动力,但是需要精确控制运动的位置,这时候你可以选用我们的步进电机,它还配有非常容易控制的电控模块。还有可能你需要电机在某个位置输出一定的力,不会受干扰的偏移原来预定的位置,譬如航模中的摆舵,这时候就可以选用我们提供的大扭力伺服电机(舵机)。

Makeblock平台的灵活性不仅体现在零件选择上有大中小的规格,更体现在自由搭配的数量上。履带就是一个很好的例子,履带是由类似手表带一样的方式,通过中间轴和橡胶的配合一起完成的,使用者可以通过使用不同数量的履带单元,自由调节这种履带的长度以适应不同场合的需求。

多种传动结构——让运动变换更有趣

你可以通过同步皮带轮将电机运动的速度降低,扭矩提高,以达到传动的目的。也可以使用我们提供的直线运动部件让电机的旋转运动变成来回的直线运动。

采用我们的多功能滑轮装上履带或者橡皮车轮,你也可以把电机的运动转换成履带和地面的摩擦运动,这一个零件同时实现了三种不同可能性的功能。

如果你系统的学习过机械原理,知道什么叫连杆机构,说不定可以试试拼个六足蜘蛛来玩玩。这种机器人实现了机械专业学生渴望机构变换搭建的需求,可以作为实验课的配套教具。

不懂接口不想连线——积木式电子接线

机械零件的安装和搭建是容易了,那电子模块呢?也许你曾经看过一些电子模块的接线混乱如麻,不仅连接不可靠,用户更要花费大量时间在对接口找编号上面。Makeblock向用户提供统一的四芯水晶头接口,不仅插拔的用户体验大幅提升,而且连接更可靠,从此再也无需为接线而烦恼。

此外,Makeblock提供的电子模块也是有着和机械结构件一样的通孔大小和间距。用户只需要轻轻摁动可拆卸的塑胶铆钉,便可轻松固定各种电子模块和主控制板Arduino

我是乐高控——选择Makeblock也继续用乐高

Makeblock是完全开放的平台,我们兼容乐高。乐高零件的标准是直径4mm间距8mm的通孔。而Makeblock也一样,部分零件也采用16mm的间距规格,这丝毫没有妨碍到乐高的用户过渡到Makeblock这个平台上来。不仅如此,你曾经用过的乐高零件还可以作为Makeblock丰富扩展的一部分。

 

此外,我们还提供从Arduino到乐高零部件的转接板,这样,你完全可以继续使用Arduino来控制乐高的电子零配件,丝毫没有问题,这也许又是一条能让用户感到惊喜的好消息吧?

Makeblock的受众

Makeblock对于喜欢自己动手制作机器人、数控机床、互动玩具产品的创客们显然是极佳的选择。

Makeblock官方网站:http://www.makeblock.cc/

开始搭建一个简单的Makeblock坦克

现在,让我们开始搭建你的第一个简单的Makeblock机器坦克,你必须要确保手上要有一套完整的Makeblock初学者套件。让我们拿出一块Arduino Uno R3或者Makeblock自带的Meduino,它们是完全一样的东西,除了名字。因为Arduino是被注册的商标,除了Arduino官方团队以外,其他完全一样的兼容板也不能使用。

接着在Makeblock套件中找出Makeblock基础盾牌。如下图所示,把它插接到Arduino上。

第一步,添加电机

如图所示,用沉头十字小螺丝将两个电机分别装到电机托架上去,然后把整个电机连托架安装到8个单位长度的双孔梁上,注意,呈斜对角线安装效果较佳。

第二步,搭建机器人底座

将六个单位的双孔梁拿出,如下图所示,两侧插入并用螺丝刀拧紧,坦克底座即完成。

第三步,安装驱动轮

此范例的坦克机器人需要四个轮子,其中两个是主动轮(连接电机),另外两个是被动轮。它们的搭建稍有差别,首先,将两个主动轮用联轴器连接

第四步,连接被动轮

如下图所示,取出一边带螺纹,一边光滑的小轴,此外,我们还需要两个杯轴承,一个白色塑料挡圈以及一个侧面需要旋入止付螺丝的轴套。此步骤稍有点复杂,涉及的新零件较多,请按图逐步操作

第五步,安装履带

Makeblock的履带式可扩展式的,类似于钢带手表的表带,中间通过光滑的小轴连接。本范例需要每边需要20片履带片,在履带片串接小轴过程中,请尽量使用工具(如螺丝刀手柄)来按压,以免受伤。 履带串接好后,即可如下图所示套到底座的轮子上。

Wiring a robot tank

第一步,将基础控制板安装在电池托盘上

将六节AA5号电池)的电池架安装到塑料托盘上,用3mm的尼龙铆钉固定,再用4根铜螺柱和杯头螺丝安装好主控板。请注意,不要拧太紧,塑料板较脆,容易产生裂缝。

第二步,把主控器及电池托盘安装到坦克上

同样的方式,但使用直径4mm的尼龙铆钉将塑料板安装在坦克的主双孔梁上。

 

第三步,添加超声波传感器

用两颗M4X8的螺母和两个尼龙铆钉,安装好Makeblock的第二代超声波传感器,你就可以用它来测量距离了。如果你需要了解更多关于这款传感器的信息,请访问Makeblock的官网。

第四步,接线

下图简要表示了主控器如何与电池盒、两个直流电机、超声波传感器和红外接收器连接。如果需要更多关于这两种传感器的知识,请回顾本书第三篇的相关内容。

 

搭建一个三轮机器人小车

第一步,搭建小车的基本结构

在搭建过程中尤其注意要垂直螺纹槽旋入螺丝,以保护螺纹槽不受损。万向轮的连接螺母不是匹配普通杯头螺丝的螺母,请注意选用正确的螺母。

第二步,将两直流电机安装到小车主结构上

第三步,安装所有小车需要的轮子

此步骤依然需要使用联轴器,可见在电机D轴和多功能车轮的连接过程中,铝联轴器是必不可缺的。先安装联轴器到车轮上,再使用止付螺丝安装车轮到电机的D轴上。此外,在车轮外加装橡胶轮胎能够有效的提供静摩檫力,使小车在干燥的地面行驶时更有效率。

三轮机器车的接线

这些步骤和四轮坦克类似,唯一不同的地方就是超声波传感器、电池夹以及主控板的安装。电池夹和主控板通过4mm的尼龙铆钉安装到双孔梁上,超声波传感器用4mm的尼龙铆钉固定在L型金属片上。

编程

Makeblock的扩展板1.0在用户首次使用时已经内置了我们初学者套件的代码。你可以使用在红外遥控器面板上的测试(TEST)按键来切换超声波壁障模式和红外控制模式。当你开始使用你的机器人,你可以使用红外遥控来控制你的机器人。当你按下测试键,机器人就切换到智能壁障模式,它可以自动的避开障碍物向前行进。在壁障模式中,你依旧可以使用红外遥控来控制你的机器人

安装Arduino开发环境及Makeblock

安装Arduino部分在本书第一章已经完整阐述相关步骤,这里不再赘叙。下面说明如何在Arduino IDE已经安装好的基础上引用Makeblock的库文件。

下载Makeblock库:

https://github.com/Makeblock-official/Makeblock-Library.

Github是全球知名的开源代码分享社区,Makeblock希望我们的开发者也参与其中,你也可以提交你自己对于代码的理解和修改。点击上述网址,进入githubMakeblock代码库的页面,你可以直接浏览当中的代码,相关内容请读者自己尝试或在网上查阅相关资料。为了在本地运行,点击页面右下角的Download Zip,如图所示,将整个库文件以ZIP压缩包文件的形式下载下来

新版的Arduino IDE支持直接导入Zip格式文件的库,在导航菜单中点击->Sketch->Import Library然后选择Add Library

将路径指向我们刚才下载文件的目录即可,如图所示

当然,你也可以手动安装,这种方式我们在本书第三章有提到。解压文件夹“Makeblock_Library”,并将其放置到Arduino软件根目录下的libraries文件夹下。

运行示例代码库

USB连接到你的Arduino或者Meduino控制板,新版的Arduino IDE会自动帮你安装控制板所需驱动,这可能需要约一分钟的时间。重启Arduino IDE后,

在导航菜单栏中即可看到Makeblock官方提供的示例代码,如下图所示:

file>examples>Makeblock_Library>examples>makeblock_Robot_Starter_Kit>

 

选择控制板类型:

在导航菜单栏Tools>Board>中对你所使用的控制板类型进行选择,如下图所示。如果你使用的是Meduino请选择Arduino Uno,如果你使用的事Me-Baseboard,请选择Arduino Leonardo

选择串口

Arduino IDE的菜单栏中,导航到Tools>Serial Port>Com x如下图所示,选择你正在使用Arduino连接的串口。

  • 装载程序

单击upload图标,编译并装载代码到控制板中,当装载过程完成,Arduino IDE的状态栏中会显示“Done uploading”.

装载代码中可能会遇到的问题?

由于Me BaseBoard是基于LeonardoArduino控制器,装载代码的过程中可能会遇到如下提示错误: "Couldn't find a Leonardo on the selected port". 先检查你是否选择了正确的串口号,如果确认无误,在转载前请按紧控制板上的reset按键不放,同时点击Arduino IDE中的upload按键,如果还不能解决问题,请访问Makeblock官方论坛(英文):

(http://forum.makeblock.cc/t/me-baseboard-leonardo-error/)

来反馈你的问题,并取得技术支持。

运行你的机器人

Makeblock官方提供了两个示例代码IR_Control是用来使用红外遥控远程控制机器人的程序;Ultrasonic_car是自动壁障机器人的示例程序,确保你往控制器中装载的是你想要的程序。

以下两个是你初次使用Makeblock可能会遇到的问题

如何使用红外遥控器

首先,确保你选择了IR_Control 的示例程序并装载好到你的主控器中,同时,还要确保你的红外传感器、超声波传感器接到了主控器中正确的端口,在最新版的Makeblock中,默认红外传感器是接在6号端口的 (PORT_6)。这时你可以打开主控器上的电源开关,注意如果是使用Makeblock扩展板插接在Arduino上,需要打开扩展板上的开关,并且电池连接线需要和扩展板供电,而不是Arduino主控板供电,否则无法向电机供电。

下面的图片显示了红外遥控器上面指令按键的分布,其它并未标记的按键暂时未被使用,将来请你写程序来用上它们。

有些时候,当你发现使用遥控操纵的过程当中,你想让机器人前行,它却转弯。这是因为电机的正负极接线接反了,这时你只需将电机的连接线对调一下,如下图所示。如果你遇到想让机器人前行它却后退的问题呢?嗯,就让我们把这个问题留给读者自己思考吧,多试几次,你一定会体会其中的缘由的。

运行自动壁障机器人

首先,你要确保你是将示例程序Ultrasonic_car装载到你的主控器中的,同时你还需要保证超声波传感器连接的事主控器中的7号端口 (PORT_7);同样,你需要保证红外传感器连接到6号端口(PORT_6).打开开关,开始你人生自制的第一个壁障机器人小车的乐趣吧。