第2章 pcDuino快速开发入门
2.1 pcDuino硬件平台介绍
2.1.1 pcDuino系列板卡介绍
许多初见本书的读者可能首先会问:什么是pcDuino?用一句话概括,pcDuino就是Mini PC+Arduino。pcDuino实物如图2-1所示。
图2-1 pcDuino V2板卡
首先,pcDuino是LinkSprite推出的一台Mini PC,它拥有高性能的ARM处理器、USB接口、HDMI显示输出、网络接口等,并可以像PC一样运行Ubuntu Linux操作系统(见图2-2)或像平板电脑一样运行Android操作系统(见图2-3)。
图2-2 pcDuino运行Ubuntu操作系统
图2-3 pcDuino运行Android操作系统
表2-1为pcDuinoV1的硬件配置信息。
表2-1 pcDuinoV1配置
pcDuino搭载的是一颗由全志科技设计生产的A10处理器,内部集成了Cortex-A8(ARMv7)内核,采用55nm工艺生产,主频可达1GHz。这款国产CPU具有高性价比,广泛用于低端平板电脑市场,并且在近年来成为国内外开源硬件平台的宠儿,有不少平台都采用了这款CPU(见图2-4)。
图2-4 pcDuino上的全志A10CPU
首先,这款全志A10处理器具有ARM Mali-400GPU,支持硬件高清解码、HDMI或LCD输出,能够以1080p(30fps)或双通道720p(30fps)的速率播放H.264编码视频,最大支持2GB DDR2/DDR3内存,支持NAND Flash、SD卡,并可以选择从上述设备或者USB启动。A10还集成了SATA硬盘接口和摄像头接口(pcDuino上没有引出),并拥有GPIO、UART、I2C、SPI、AD等常见的嵌入式接口,非常适合DIY应用。
其次,pcDuino具有兼容Arduino的特性。现在很多市面上的板卡都声称与Arduino兼容,但大多数都是接口兼容,即具有与Arduino类似的引脚定义,但软件上还是使用各自的开发环境,不能共用代码。pcDuino不但在硬件接口上完全兼容Arduino,可以使用Arduino的扩展模块--这一点的好处我们将在第3章中证实--同时它还可以使用Arduino的软件环境进行开发。pcDuino通过提供良好的API,支持在Ubuntu下运行Arduino IDE,使用Arduino现有的软件库并在pcDuino板卡上直接编译执行程序。
除了Arduino的软件开发方式之外,用户还可以使用Python、Java、C/C++等常见的编程语言设计软件。无论你是想要实现复杂应用的嵌入式工程师、对硬件一窍不通的程序员,或是几乎没有编程基础的学生和创客(Maker),都可以轻松地使用pcDuino实现自己的创意。
目前pcDuino已有多个衍生型号(见图2-5)。最初的版本为pcDuino V1,配置1GB内存、2GB NAND Flash、两个USB Host接口。作为pcDuino的原型版,该版本的插针在PCB板的一侧,需要转接板才能连接Arduino Shield。
图2-5 pcDuino大家族(左上为V1,右上为V2,左下为Lite,右下为Lite WiFi)
此后LinkSprite又在V1版本的基础上推出了pcDuino Lite。Lite与V1板卡共用了PCB,只是通过选焊一些芯片来降低成本,比如内存降为512MB,取消了内置NAND Flash,而且需要外接TF卡才能使用。pcDuino Lite的价格也降至与B版Raspberry Pi相近,但性能仍然比Raspberry Pi高出许多,适合学生等关心价格的初学者使用。
pcDuino V2作为V1的改进版本,重新修改了PCB,除了拥有V1中的配置以外,在板上继承了无线网卡(当然相对地也少了一个USB Host接口),并将扩展接口重新排布,使之能够兼容Arduino的接口尺寸,可以直接使用部分Arduino的扩展模块,同时这也改善了主板与扩展模块之间的机械连接结构。
如果你需要一块尺寸更小、更加便宜的板子,就可以选择pcDuino Lite WiFi。该型号缩小了PCB尺寸,砍掉了以太网的RJ45接口并只保留了256MB的内存,完全依靠WiFi连接网络。这一型号适合成本敏感的应用,并且更加小巧。
2.1.2 pcDuino接口及外设
pcDuino上包含1个用于连接显示器并输出图形和声音的HDMI接口,1~2个USB Host接口,1个USB OTG接口,1个RJ45网络接口,以及一组用于扩展功能的排针。后面介绍pcDuino时大致需要涉及以下配件。
·迷你USB接口5V电源适配器,至少能够提供2A电流,同时USB线也要求能够承载2A以上电流,请勿使用过细的数据线。
·支持HDMI接口的高清显示器。
·HDMI连接线:如果没有带HDMI接口的显示器。可以使用HDMI到VGA转换线或者HDMI到DVI转换线。
·USB接口键盘、鼠标。
·USB HUB。由于USB接口较少,推荐使用一个USB HUB进行扩展。这里需要注意,当HUB上连接的设备较多时,pcDuino的USB接口可能无法满足各个设备的供电需要,此时最好更换电流更大的电源适配器,或使用独立供电的USB HUB。
·microSD卡和读卡器:推荐使用两块2GB以上的microSD卡。一块用来升级内核,另外一块用来升级Ubuntu文件系统。
不同型号的pcDuino扩展接口也略有差别,如扩展pcDuino V1和pcDuino Lite的扩展接口全在板子的一侧,而pcDuino V2以后的接口为了更好兼容Arduino模块而分布在PCB的两侧。即使如此,二者的I/O引脚排序依然是相同的。图2-6为pcDuino V1与V2之间的接口排布示意图。
pcDuino的扩展接口包含了用户可以编程控制的GPIO和多种总线接口,其中上面一排为数字接口,下面一排为模拟接口和电源引脚,其中大部分数字引脚都可以通过配置选择作为专用接口或普通GPIO。具体配置如下:
·14×GPIO(最大输出电4mA),部分可输出PWM(可调占空比)波形
·1×UART接口
·2×I 2C总线接口
·4×SPI总线接口
·6×ADC接口,ADC 0、ADC 1为6位AD,其余为10位AD
图2-6 pcDuino V1/Lite扩展接口示意
除了兼容Arduino的接口以外,pcDuino还额外提供了几个接口,如图2-6右下角的Debug串口,在没有网络和显示器的情况下,可以使用此串口通过TTL-to-RS232或者USB-to-Serial模块连接PC,再使用SecureCRT等软件来访问pcDuino。另外,此串口会在设备启动后打印信息,内核信息也会在该串口上显示,这为想要调试Bootloader和内核的用户提供了一个方便的途径,如图2-7所示。
由于pcDuino的接口定义兼容Arduino,部分Arduino模块可以直接在pcDuino上使用。为什么说是部分呢?由于Arduino一部分主板使用的AVR单片机是5V I/O电平的,而pcDuino使用3.3V I/O电平(部分Arduino的主板也使用3.3V电平),因此采用5V电平的模块需要进行电平转换才能在pcDuino上使用。可以在pcDuino和扩展模块之间增加一个LinkSprite的T Board来实现电平转换,注意pcDuino V1和V2使用的T Board略有不同。
除了Arduino丰富的模块以外,pcDuino官方还推出了一款Linker Base Shield,它将pcDuino的I/O引出的同时,为每组I/O都搭配了电源引脚,这样可以十分方便地为pcDuino扩展I/O模块。
图2-7 利用Debug串口进行调试
同时,由于pcDuino运行多任务的Linux操作系统,可以通过运行多个应用程序进程分别控制不同的I/O,各个程序可以独立运行互不影响,只要程序之间使用的I/O不发生冲突。这样,一台pcDuino就可以作为多个Arduino来使用。
pcDuino作为一台Mini PC,可以使用多种语言和工具进行软件开发,在接下来的几章中,笔者会依次讲解各种语言和环境的开发方法。
在本章的后两节中,读者可以了解到如何使用C/C++通过命令行和Arduino IDE编写并执行程序,这也将是本书的主要编程方法。第5章和第6章将介绍除C语言以外的编程语言:Python和JavaScript,以及使用Cloud9IDE等开发环境编写软件。第7章会向读者讲解如何使用QT for Android为pcDuino开发Android系统下运行的应用软件。
在介绍每一种开发语言或开发方法时都会配合一个实例,以帮助读者更好地理解这些高级语言是如何在pcDuino上执行并控制硬件的。
2.2 使用Arduino IDE编写pcDuino程序
本节将演示如何使用Arduino IDE为pcDuino编写Sketch,并通过一个按键来控制LED。LED连接至pcDuino的GPIO1,按键连接至pcDuino的GPIO2。
pcDuino的Ubuntu系统中已经集成了Arduino IDE,可以直接使用。单击屏幕左下角的系统菜单,选择Programming→Arduino IDE,就会弹出Arduino IDE窗口,如图2-8所示。
图2-8 运行Arduino IDE
pcDuino的Arduino IDE在外观和使用上与Arduino官方版本完全相同,并且已经包含了在pcDuino上运行所需的环境和针对pcDuino的函数库(见图2-9)。
图2-9 Arduino IDE for pcDuino
单击菜单栏的Tools→Board→pcDuino(ARM Cortex-A8),选择pcDuino为程序运行的目标板(见图2-10)。
图2-10 选择目标板
在Sketch→Import Library菜单中可以添加库文件,如图2-11所示,这些库与我们在命令行的c_environment中看到的相同,只需选择相应的项目就可以将函数库加入工程,并且自动在代码中添加头文件引用。如果这些库无法满足需求,还可以通过Add Library添加第三方函数库。这里我们只需要使用基本的GPIO,因此不需要添加额外的库。
图2-11 添加函数库
第一次打开Arduino IDE窗口会显示一个新的空白Sketch模板,我们可以在这个模板的基础上编写代码,同时也可以参考为pcDuino编写的例子。在菜单栏选择File→Examples→00.pcDuino→0.2.Linker_kits→linker_led_test打开一个LED测试例程,如图2-12所示。
图2-12 打开测试例程
这里我们要在原作的基础上添加一个按键,按键的控制方法可以参考linker_button_test例程,Sketch代码见代码清单2-1。
代码清单2-1:GPIO控制LED、按钮范例代码
/******************************************** 程序名称:led_button_test 功能:GPIO控制LED、按键范例代码 版本:v1.0 作者:懒兔子 <haixiaoli2@163.com> *********************************************/ //包含头文件 #include <core.h> //宏定义及全局变量 int led_pin = 1; //GPIO1 连接LED int key_pin = 2; //GPIO2 连接Button int keystats; //用于保存按键状态 //初始化设置部分 void setup() { pinMode(led_pin, OUTPUT); pinMode(key_pin, INPUT); } //循环执行部分 void loop() { keystats = digitalRead(key_pin); // 读取按键状态 digitalWrite(led_pin, keystats); // 若按键按下则点亮LED delay(50); }
这段代码与单独控制LED相比多了一个按键,因此定义了key_pin变量,其值为2,即表示按键接在GPIO2上。由于要检测按键的输入状态(是否按下),因此key_pin配置为INPUT模式。同时,我们还需要一个keystats变量来存放按键的状态。
在loop()循环程序中,通过digitalRead函数读取按键的状态,将读到的数值赋给keystats变量。当按键按下时,key_pin为高电平,keystats=1;按键松开时,key_pin为低电平,keystats=0。由于led_pin也是高电平点亮的,因此将keystats的值直接通过led_pin输出。这样当按键按下时将点亮LED。loop()函数每50ms循环执行一次,也就是每50ms检测一次按键状态。
图2-13所示为编写完成的Sketch。
图2-13 编写Sketch代码
图2-14 保存Sketch
由于我们打开的是一个Arduino IDE自带的例程,而这个例程是只读而不能修改的,因此需要通过菜单中的File→Save as选项将编写完成的代码另存一份。这里命名为my_led_button_test,保存后,会在选定的目录里生成一个my_led_button_test文件夹,其中的my_led_button_test.ino文件就是Arduino IDE保存的Sketch文件,如图2-14所示。
pcDuino作为Mini PC与Arduino的结合体,不需要外接计算机和下载器就可以编写和运行Sketch。通过点击工具栏中的按钮可以编译编写完成的Sketch,并且检查是否有语法错误;而使用按钮除了编译代码以外,还可以直接让代码在pcDuino上运行(相当于Arduino的程序下载过程),如图2-15所示。
图2-15 编译、下载代码
程序完成编译和下载过程后,会弹出一个命令行窗口,这时Sketch已经开始执行。按下按键后LED会点亮。关闭命令行窗口可以使Sketch停止运行,如图2-16所示。
图2-16 运行Sketch
2.3 命令行下的C/C++开发方式
2.3.1 建立C语言开发环境
在进行pcDuino开发之前,首先要获取pcDuino的C语言开发环境,也就是Arduino程序的底层支持包。pcDuino官方将C环境放在Github网站上,使用者可以通过Web页面或者Git工具自由下载。
Ubuntu默认不集成Git工具,因此需要手动下载安装Git,命令如下:
$ sudo apt-get install git
安装完成后,跳转到用户目录/home/Ubuntu,并使用Git工具获取pcDuino的C语言开发环境,命令如下:
$ cd ~ $ git clone https://github.com/pcduino/c_enviroment
图2-17展示了使用Git工具下载pcDuino的C语言开发环境。
如果在使用Git工具的过程中出现如下错误信息,可能是因为pcDuino的时间与服务器时间不同步,需要更新系统时间(pcDuino虽然有内部RTC,但由于没有电池,断电后时间就会丢失)。
Cloning into 'c_enviroment'... error: server certificate verification failed. CAfile: /etc/ssl/certs/ca-certificates.crt CRLfile: none while accessing https://github.com/pcduino/c_enviroment/info/refs fatal: HTTP request failed
图2-17 使用Git工具获取pcDuino的C语言开发环境
这时使用date命令修改系统时钟,然后再运行上面的git命令:
$ sudo date -s 2013/12/3 $ sudo date -s 21:11:00
下载完成后,可以通过命令查看c_enviroment目录的结构:
$ cd c_enviroment $ ls Makefile hardware libraries sample
简单解释下目录结构。Makefile文件是用于编译程序的脚本,用户通过运行make命令执行该脚本中的内容,从而将代码编译成可执行文件。hardware目录下存放pcDuino的程序支持代码文件,包括pcDuino的接口定义,以及支撑Arduino代码运行的API和一些基本函数库等。libraries目录下是几个常用接口的函数库,如,SPI目录是SPI接口函数库,Wire目录是I 2C接口的函数库,LiquidCrystal目录是LCD显示库,而PN532_SPI目录则是NFC Shield的函数库。sample目录中存放了一些C语言例程,初学者可以通过学习这些例程来熟悉各个接口的使用方法,我们在一开始也会通过修改现有的例程来实现自己的第一个pcDuino程序。
小贴士
有Linux下C语言开发经验的读者可以查看各个目录下的代码,来了解pcDuino上如何实现Arduino风格的编程。这些代码并不复杂,只是调用简单的设备驱动并向上留出接口,但却很好地为Arduino程序开发提供了支撑。希望更进一步开发pcDuino的读者也可以在此基础上编写自己的API和底层函数。
要使用C语言开发环境,首先要通过GCC(GNU Compiler Collection,GNU编译器套装)对其进行编译,而pcDuino已经内置了GCC。如前面内容提到的,使用一条make命令就会自动进行这些工作:
$ make
经过一段调试后编译完成。下面我们进入存放例程的sample目录,通过文本编辑工具查看已有的例子。关于文本编辑器,笔者推荐初学者使用nano,这个工具提供了类似于DOS界面文本编辑器的功能和快捷键菜单,可以进行文本查找等基本操作,操作方法提示显示在终端的下方,比如^G Get Help表示按下Ctrl+G键调出帮助,^X Exit表示Ctrl+X退出程序,如图2-18所示。
图2-18 命令行下的nano工具
nano同时支持彩色的C语言关键字,这对于命令行下的开发有很大的帮助。当然,对于从事专业开发的读者来说,vi会是一个更好的选择。
彩色关键字需要终端程序支持,如果使用SecureCRT软件远程连接pcDuino,需要简单设置一下。选择菜单栏Options→Session Options,在Session Options窗口中选择左侧的Emulation项目,将Terminal类型设置为Linux,并选中ANSI Color选项。这样终端软件就会以Linux风格显示命令行,在nano等软件中也可以显示彩色关键字了,如图2-19所示。
图2-19 设置SecureCRT支持Linux风格命令行
2.3.2 命令行方式的Sketch设计
下面我们使用nano为pcDuino编写第一个Sketch,用于点亮一盏LED。这个Sketch的代码和第1章的Arduino点亮LED完全相同,只是多了一个头文件引用:#include<core.h>。这一点需要注意,在pcDuino上运行的Arduino代码都需要加入此头文件。事实上,pcDuino和Arduino的代码具有很好的兼容性,只要有相应的C语言库支持,很多Arduino的程序经过简单修改就可以在pcDuino上运行。
将LED连接至pcDuino的GPIO1,将按键连接至GPIO2。通过nano工具在sample目录下通过如下命令创建一个led_button_test.c文件(当使用nano打开一个文件时,如果该文件名对应的文件不存在,nano就会新建一个文件),然后输入代码清单2-1中的代码。
$ nano led_button_test.c
代码编写完成后,按Ctrl+X键退出nano,程序会提示是否保存刚才修改的文件,按Y键,程序还会询问保存的文件名,再按回车键将文件保存成led_button_test.c。
每次创建新的代码文件时,都需要修改Makefile以实现对新文件的编译。使用nano修改sample目录下的Makefile文件:
$ nano Makefile
在几行“OBJS+=…”的后面另起一行,添加刚才创建的代码文件(不用添加“.c”后缀名,编译器会自动识别对应的C文件):
OBJS += led_button_test
保存后退出,在sample目录下运行make命令,编译器开始对该目录中的代码进行编译。完成编译后在c_enviroment/output/test/目录下会生成一个led_button_test可执行文件,运行这个文件就可以查看效果了。
在运行Sketch之前,先将LED与pcDuino连接好,或者也可以使用与pcDuino配套的Linker Base Shield和Linker_LED模块。连接pcDuino和Linker Base Shield,将Linker_LED模块通过排线插在D1连接器上。
在test目录下运行Sketch:
$ ./led_button_test
LED开始闪烁,按Ctrl+C键可以退出Sketch。以后编写新的Sketch,都可以在sample目录中进行,通过修改Makefile添加自己的代码文件,然后用make命令编译,编译好的可执行文件保存在output目录下,与C文件的名称相同。
2.3.3 pcDuino下的代码调试
对于比较复杂的Sketch来说,很难保证代码一次编写完成就能正确运行,因此调试对于程序设计来说显得尤为重要。由于Arduino设备没有仿真工具,缺少断点、单步等调试手段,常用的调试方法是LED+串口输出调试信息,在pcDuino上进行开发时也是如此。不过pcDuino运行的是Linux操作系统,可以方便地使用一些Arduino没有的调试方法,如通过标准的printf和scanf函数在终端中显示信息并输入参数等,或使用Linux自带的GDB工具进行调试。
让我们来看一下pcDuino自带的linker_led_test.c例程,如代码清单2-2所示。
代码清单2-2:pcDuino自带LED例程
/* * LED test program */ #include <core.h> int led_pin = 1; void setup() { #ifndef PCDUINO_IDE if(argc != 2){ goto _help; } led_pin = atoi(argv[1]); #endif if((led_pin < 0) || (led_pin > 13)){ goto _help; } pinMode(led_pin, OUTPUT); return; _help: printf("Usage %s LED_PIN_NUM(0-13)\n", argv[0]); exit(-1); } void loop() { digitalWrite(led_pin, HIGH); // set the LED on delay(1000); // wait for a second digitalWrite(led_pin, LOW); // set the LED off delay(1000); // wait for a second }
执行这段代码时,通过在程序名称后面追加参数可以灵活选择使用哪个GPIO作为LED引脚,如加一个数字2即表示使用GPIO2作为LED引脚:
$ ./linker_led_test 2
代码中的argc表示程序拥有的参数个数,程序名本身也算作一个参数,如果只运行./linker_led_test,那么argc=1。argv[x]表示程序的第x个参数,其中程序名本身是argv[0],因此程序名后面跟随的GPIO编号是argv[1],这个参数是字符串,因此需要通过atoi()函数将其转换成数字。两条goto语句则表示如果参数的数量不等于2,并且LED使用的引脚编号不在pcDuino的I/O范围内,则显示一条本程序用法的提示信息,并退出程序,将不会执行loop()函数中的代码。
我们也可以在代码中增加printf调试信息,修改后的loop()函数如下:
void loop() { digitalWrite(led_pin, HIGH); // set the LED on printf("LED status: ON!\n"); delay(1000); // wait for a second digitalWrite(led_pin, LOW); // set the LED off printf("LED status: OFF!\n"); delay(1000); // wait for a second }
重新编译执行,就可以看到显示LED状态的调试信息,如图2-20所示。
图2-20 显示LED状态的调试信息
在代码中增加打印信息,或者相反,在特定的代码阶段控制LED的亮灭都可以帮助开发者掌握代码的运行情况。这种方法简单而有效,适用于结构不是特别复杂的软件。如果涉及的代码量很大,或者结构特别复杂,还可以使用Linux自带的GDB工具进行调试。GDB工具的使用方法不在本书的讨论范围内,感兴趣的读者可以通过网络资料或相关数据进一步了解其使用方法。