pcDuino是一款迷你的PC机,只有信用卡那么大小,却能够运行Ubuntu和Android的ICS系统。最新版本的pcDuino3采用了全志A20双核1.5GHz ARM Cortex A7处理器,性能优异。下面以该系统为实例,介绍一些与嵌入式系统软硬件相关的内容。
I2C(Inter-Integrated Circuit)总线是由PHILIPS公司开发的两线式串行总线,用于连接微控制器及其外围设备,产生于20世纪80年代,最初的目的是进行音频和视频设备开发,现在主要应用于服务器管理,包括单个组件状态的通信。例如管理员可对各个组件进行查询,以管理系统的配置和掌握组件的功能状态,如电源和系统风扇,可随时监控内存、硬盘、网络、系统温度等多个参数,增加了系统安全性,方便管理。由于接口直接在组件之上,因此I2C总线占用的空间非常小,减少了电路板的空间和芯片管脚的数量,降低了互联成本,集中体现了I2C总线最主要的优点——简单和有效。
具体地说,I2C总线是由数据线SDA和时钟SCL构成的串行总线,各种被控制器件均并联在这条总线上,每个器件都有唯一的地址识别,可以作为总线上的一个发送器件或接收器件(具体由器件的功能决定)。I2C总线的接口电路结构如图1-8所示。
I2C总线的几种信号状态具体分为以下几种。
图1-8 I2C接口电路结构图
·空闲状态:SDA和SCL都为高电平。
·开始条件(S):SCL为高电平时,SDA由高电平向低电平跳变,开始传送数据。
·结束条件(P):SCL为高电平时,SDA由低电平向高电平跳变,结束传送数据。
·数据有效:在SCL的高电平期间,SDA保持稳定,数据有效。SDA的改变只发生在SCL的低电平期间。
·ACK信号:数据传输的过程中,接收器件每接收一个字节数据要产生一个ACK信号,向发送器件发出特定的低电平脉冲,表示已经收到数据。
针对I2C总线的基本操作主要包括读、写及控制。主器件(通常为微控制器)控制I2C总线,产生串行时钟(SCL)、控制总线的传输方向、产生开始和停止条件。数据传输过程中,主器件产生开始条件,随后是器件的控制字节(前7位是从器件的地址,最后一位为读写位),接下来是读写操作的数据,以及ACK响应信号。数据传输结束时,主器件产生停止条件。具体的过程如图1-9所示。
图1-9 I2C总线数据传输图
通过分析I2C源码可知,在drivers/I2C/目录下,包含以下几个重要文件和目录。
1)文件I2C-core.c:I2C子系统核心功能的实现。
2)文件I2C-dev.c:通用的从设备驱动实现。
3)目录busses:里面包括基于不同平台实现的I2C总线控制器驱动,A20使用的源文件为I2C-sun7i.c。
在sys_config.fex中有5组I2C总线可供使用,分别是twi0、twi1、twi2、twi3和twi4。配置如下:
[twi0_para] twi0_used=1 twi0_scl=port :PB0<2> <default> <default> <default> twi0 _sda=port :PB1<2> <default> <default> <default> [twi1_para] twi1_used=1 twi1_scl=port :PB18<2> <default> <default> <default> twi1_sda=port :PB19<2> <default> <default> <default> [twi2_para] twi2_used=1 twi2_scl=port :PB20<2> <default> <default> <default> twi2_sda=port :PB21<2> <default> <default> <default> ......
其中常用的为twi0、twi1、twi2,twi3与twi4使用时按照twi0等格式进行添加即可。
若使用哪一组I2C总线,将对应的twiX_used置为1即可。通常情况下,twi0、twi1、twi2均设置为1。
对于I2C总线控制器的配置,可通过命令make ARCH=arm menuconfig进入配置主界面,并按以下步骤操作:
1)选择Device Drivers选项,进入下一级配置。
2)选择I2C support选项,进入下一级配置。
3)选择I2C HardWare Bus support选项,进入下一级配置。
4)选择Allwinner Technology SUN7I I2C interface选项,可选择直接编译进内核中,也可以选择编译成模块,如图1-10所示。
此外,若需要获取指定I2C总线控制器相关的调试打印信息,可选择sun7i i2c print transfer information选项,并在bus num id选项中指定对应的I2C总线控制器编号,可输入0、1、2和3、4,如图1-11所示。
位于drivers/I2C/busses目录下的文件I2C-sun7i.c,是基于SUN7I平台实现的I2C总线控制器驱动,功能是为系统中5条I2C总线实现相应的读写方法。控制器驱动自身不会进行任何的通信,而是等待其他设备驱动调用其函数。
图1-10 Allwinner Technology SUN7I I2C interface选项配置
图1-11 I2C总线控制器调试信息配置
如图1-12所示是基于SUN7I平台的I2C驱动层次架构图,其中有5块I2C adapter,分别对应SUN7I平台上的5块I2C控制器。
系统开机时,I2C控制器驱动首先被装载,I2C控制器驱动用于支持I2C总线的读写。在I2C_sun7i_algorithm结构体中定义了I2C总线通信方法函数I2C_sun7i_xfer(),该函数实现了对I2C总线访问的具体方法。设备驱动通过调用这个函数,实现对I2C总线的访问;而在函数I2C_sun7i_probe()中完成了对I2C adapter的初始化。
I2C_adapter对应一个控制器。一个I2C控制器需要I2C_algorithm中提供的通信函数来控制控制器上产生特定的访问周期。I2C_algorithm中的关键函数master_xfer()用于产生I2C访问周期需要的信号,以I2C_msg(即I2C消息)为单位。I2C_msg是I2C传输的基本单位,包含从设备的具体地址、消息的类型以及要传输的具体数据信息。每个I2C消息传输前,都会产生一个开始位,紧接着传送从设备。I2C_client对应于真实的物理设备,每个I2C设备都需要一个I2C_client来描述。I2C_driver对应一套驱动方法,其主要成员函数是probe()、remove()、suspend()、resume()等,另外id_table是该驱动所支持的I2C设备的ID表。I2C_driver与I2C_client的关系是一对多,一个I2C_driver上可以支持多个同等类型的I2C_client。
图1-12 I2C驱动层次架构图
I2C常用接口如下所示:
·I2C_add_driver
·I2C_del_driver
·I2C_set_clientdata
·I2C_get_clientdata
·I2C_master_send
·I2C_master_send
·I2C_master_resv
·I2C_transfer
A20有两路显示系统,支持双屏输出,支持LCD屏的接口形式及最大分辨率如表1-1所示。
表1-1 LCD输出I/O口
HVRGB接口和CPU/I80接口用于并行数据输出,是TTL电平的屏接口,其中LCD0从PD口输出,LCD1从PA口输出。
LVDS接口用于串行输出,是差分信号的屏接口,LVDS0从PD0~PD9输出,LVDS1从PD10~PD19口输出。
DSI接口使用A20+SSD2828的方式,通过A20的HV RGB接口输出,由转换IC转成DSI接口。
如果一路输出使用Dual Link LVDS,占用了LVDS的所有引脚,另外一路只能使用其他接口形式输出。其他接口形式任意组合的双屏输出都支持。
以LVDS屏为例,LVDS屏接口分为Single Link和Dual Link两种。LVDS屏使用LVDS差分信号,LVDS Single Link具有1组时钟对和3组或4组数据对。
如图1-13所示是一个典型LVDS Single Link屏的模组规格书的引脚定义。A20与该LCD屏的引脚连接如表1-2所示。
表1-2 LVDS Single Link显示屏引脚定义
该LCD屏有3组数据对,显示要求为18bit色深,且不区分模式。故
lcd_lvds_colordepth=1,lcd_lvds_mode=0
图1-13 A20与LVDS Single Link显示屏连接图
该LCD屏参数如表1-3所示。时序参数配置与HVparallelRGB类似。区别于HVparallelRGB,该LCD屏参数没有指定BackPorch和SyncWidth。根据A20时序要求,LCD控制器配置如下:
·lcd_ht>lcd_x×cycle+lcd_hbp,得lcd_hbp<64。
·取lcd_hbp=20;lcd_hbp>lcd_hspw,取lcd_hspw=10。
·lcd_vbp=20,lcd_vspw=10。
LCD I/O必须配置为LVDS。
表1-3 LVDS Single Link显示屏参数
(1)LCD硬件参数
1)lcd_if为LCD接口选择,参数值所对应的含义如下:
0——HVRGB接口;1——CPU/I80接口;2——Reserved;3——LVDS接口;4——DSI接口。
2)lcd_hv_if,这个参数只有在lcd_if=0时才有效,定义RGB同步屏下的几种接口类型。设置相应值的对应含义如下:
0——ParallelRGB;8——SerialRGB;10——DummyRGB;11——RGBDummy。
3)lcd_hv_s888_if,这个参数只有在lcd_if=0且lcd_hv_if=1(SerialRGB)时才有效。定义奇数行RGB输出的顺序如下。
0: OddlinesR→G→B:EvenlineR→G→B 1: OddlinesB→R→G 2: EvenlineR→G→B OddlinesG→B→R 4: EvenlineR→G→B OddlinesR→G→B 5: EvenlineB→R→G OddlinesB→R→G 6: EvenlineB→R→G OddlinesG→B→R 8: EvenlineB→R→G OddlinesR→G→B 9: EvenlineG→B→R OddlinesB→R→G 10:OddlinesG→B→R:EvenlineG→B→R
4)lcd_hv_syuv_if,这个参数用来设置YUV输出顺序的,只有在lcd_if=0且lcd_hv_if=2(SerialYUV)时才有效。定义YUV输出格式如下:
0——YUYV;1——YVYU;2——UYVY;3——VYUY。
5)lcd_cpu_if,只有在lcd_if=1时,即设置为CPU/180类型的接口,这个参数才有效。设置相应值的对应含义如下:
0——18bit/1cycle parallel(RGB666);4——16bit/1cycle parallel(RGB565);6——18bit/3cycle parallel(RGB666);7——16bit/2cycle parallel(RGB565)。
6)lcd_lvds_ch,设置相应值的对应含义如下:
0——Single Link;1——Dual Link。
LVDS接口的LCD屏,定义1组时钟对+3/4组数据对,为1个link。若有2组时钟对,则为两个link。
7)lcd_lvds_bitwidth,设置相应值对应含义如下:
0——8bit per color;1——6bit per color。
8)lcd_lvds_mode,这个参数只有在lcd_lvds_bitwidth=0时才有效。设置相应值对应含义如下:
0——NS mode;1——JEDIA mode。
NS mode和JEDIA mode的定义如图1-14所示。
图1-14 LVDS JEDIA mode和NS mode
9)lcd_pin。
示例:lcdd0=port:PD00<3><0><default><default>
含义:lcdd0这个引脚,即PD0,配置为LVDS输出。
第一个尖括号表示功能分配:0为输入,1为输出,2为LCD输出,3为LVDS接口输出,7为disable。
第二个尖括号表示内置电阻:0表示内部电阻高阻态,如果是1则内部电阻上拉,2代表内部电阻下拉。使用default代表默认状态,即电阻上拉。其他数据无效。
第三个尖括号表示驱动能力:default表示驱动能力是等级1。
第四个尖括号表示默认值:即是当设置为输出时,该引脚输出的电平,0为低电平,1为高电平。
LCD PIN的配置如下:
LCD为HVRGB或DSI屏,CPU/I80屏时,必须定义相应的I/O口为LCD输出(如果是0路输出,第一个尖括号为2;如果是1路输出,第一个尖括号为3)。
LCD为LVDS屏时,必须定义PD口对应的I/O口为LVDS输出(即第一个尖括号为3)。
LCD PIN的所有I/O均可通过注释方式去掉其定义,显示驱动对注释I/O不进行初始化操作。
10)lcd_gpI/O_x。
示例:lcd_gpI/O_0=port:PA06<0><0><default><default>
含义:lcd_gpI/O_0引脚为PA06。
第一个尖括号表示功能分配:0为输入,1为输出。
第二个尖括号表示内置电阻:0内部电阻高阻态,如果是1则内部电阻上拉,2就代表内部电阻下拉。使用default代表默认状态,即电阻上拉。其他数据无效。
第三个尖括号表示驱动能力:default表示驱动能力是等级1。
第四个尖括号表示默认值:即是当设置为输出时,该引脚输出的电平,0为低电平,1为高电平。
A20配置中,共有6个可选的lcd_gpI/O引脚,lcd_gpI/O_0、lcd_gpI/O_1,lcd_gpI/O_2、lcd_gpI/O_3、lcd_gpI/O_4、lcd_gpI/O_5。
11)lcd_bl_en。
示例:lcd_bl_en=port:PH07<1><0><default><1>
含义:lcd_power引脚为PH07,PH07输出高电平时打开LCD背光:上下拉不使能。
第一个尖括号表示功能分配:1为输出。
第二个尖括号表示内置电阻:0内部电阻高阻态,如果是1则内部电阻上拉,2就代表内部电阻下拉。使用default代表默认状态,即电阻上拉。其他数据无效。
第三个尖括号表示驱动能力:default表示驱动能力是等级1。
第四个尖括号表示输出有效所需电平:LCD背光工作时的电平,0为低电平,1为高电平。
12)lcd_power。
示例:lcd_power=port:power2<1><0><default><1>
含义:LCD的供电定义gpI/O控制。
13)lcd_pwm。
示例:lcd_pwm=port:PB02<2><0><default><default>
含义:PB02输出PWM信号。
A20方案固定PB02为PWM信号输出引脚。建议使用此默认配置。
PWM(Pulse Width ModulatI/On,脉冲宽度调制)即脉宽调制,是一种脉冲编码技术。一般PWM信号的周期不变,用占空比(有效电平在整个信号周期中的时间比率,为0%~100%)来表示编码数值。PWM可以用于对模拟信号电平进行数字编码方法,也可以通过控制高电平(或低电平)在整个周期中的时间来控制输出的能量,从而控制电机转速或LED亮度。
PWM控制技术以其控制简单灵活和动态响应好的优点成为电力电子技术中最广泛应用的控制方式,也成为研究的热点。当今科学技术的发展已经模糊了学科界限,结合现代控制理论思想或无谐振波开关技术将会成为PWM控制技术发展的主要方向之一。如图1-15所示为典型的PWM波形。
图1-15 典型的PWM波形
PWM信号一般由计数器和比较器产生。计数器以一定的频率自加,比较器中设定了一个阈值,当计数器中的数字小于这个阈值时,输出一种电平状态(如高电平);当数字大于这个阈值时,输出另一种电平状态(如低电平);当计数器计满后清零,又回到最初的电平状态。这样通过I/O引脚的周期翻转,就形成了PWM波形,如图1-16所示。
图1-16 产生PWM原理
pcDuino上的PWM信号如图1-17所示。
图1-17 pcDuino上的PWM
可用于输出PWM信号的GPIO共有6个,其中GPIO3、9、10、11的PWM频率为125Hz~2kHz,GPIO5、6的PWM频率只能设置为195Hz、260Hz、390Hz、520Hz、781Hz中的某个特定值。Arduino库中提供了analogWrite()函数来设置PWM占空比,增加了pwmfreq_set()函数,用于设置PWM信号周期。5、6引脚为复用引脚。
在人机交互过程中,G-Sensor起着非常重要的作用,G-Sensor作为输入设备,能感知当前其所处的空间状态,将它附着在终端上配合使用,能测量出终端在空间上的坐标状态,从而获知终端用户的操作意图,如横竖屏切换、转弯变向等。
通常G-Sensor通过4个引脚与主机连接,分别为VCC、GND、SDA、SCL。引脚正常工作时候的高电平均为3.3V。
在G-Sensor的硬件调试过程中,需要确认下列项:
1)各个引脚与HOST正确连接。
2)电源电压是否正常,即VCC接入电压为3.3V,GND电压为0V。
3)I2C引脚电平是否匹配。
4)设备使用的I2C地址,特别是对一台设备进行多个地址设置时。
(1)G-Sensor的体系结构(如图1-18所示)
图1-18 G-Sensor体系结构
G-Sensor设备为使用I2C总线进行通信的输入设备,G-Sensor driver通过调用I2C驱动的相应接口来实现对G-Sensor设备的控制与通信,如G-Sensor driver对G-Sensor设备硬件和各寄存器的读写访问等。
G-Sensor driver将底层硬件对用户输入访问的响应转换为标准的输入事件,再通过核心层(Input Core)提交给事件处理层;核心层向下提供了G-Sensor driver的编程接口,向上又提供了事件处理层的编程接口。事件处理层(Input Event Drivers)为用户空间的应用程序提供了统一访问设备的接口,并对驱动层提交的事件进行处理;用户空间(User space)将根据设备的节点进行数据的读取以及相应的处理。
(2)模块数据结构描述
1)struct I2C_driver bma250_driver:该变量会注册到I2C_driver中,driver.name为匹配设备名,probe为设备的侦测函数,address_list为I2C的侦测地址,suspend为休眠唤醒函数。
代码清单1-1:bma250_driver注册
static struct I2C_driver bma250_driver = { .class = I2C_CLASS_HWMON, .driver = { .owner = THIS_MODULE, .name= SENSOR_NAME, }, .id_table= bma250_id, .probe= bma250_probe, .remove= bma250_remove, #ifdef CONFIG_HAS_EARLYSUSPEND #else #ifdef CONFIG_PM .suspend = bma250_suspend, .resume = bma250_resume, #endif #endif .address_list = normal_I2C, };
2)struct bma250_data:代表了G-Sensor驱动所需要的信息的集合,用于帮助实现对采样信息的处理。
代码清单1-2:struct bma250_data注册
struct bma250_data { struct I2C_client *bma250_client; atomic_t delay; atomic_t enable; unsigned char mode; struct input_dev *input; struct bma250acc value; struct mutex value_mutex; struct mutex enable_mutex; struct mutex mode_mutex; struct delayed_work work; struct work_struct irq_work; #ifdef CONFIG_HAS_EARLYSUSPEND struct early_suspend early_suspend; unsigned char range_state; unsigned char bandwidth_state; #endif };
3)struct bma250acc:用于记录采样时获得的x轴、y轴、z轴的坐标信息。
代码清单1-3:struct bma250acc注册
struct bma250acc{ s16 x, y, z; } ;
嵌入式系统结构分布如图1-19所示。应用程序通过调用嵌入式操作系统实现对以嵌入式处理器为核心的硬件平台的操作。简单嵌入式系统通常不运行操作系统,而是将用户代码直接运行在CPU上。在遇到相对复杂的任务时,选择适当的操作系统可以减少开发的时间。一般来说,运行在单片机上的操作系统有OpenRTOS、FreeRTOS、uc/OS-ii等,这些操作系统的典型特征是简单、不具备动态程序加载的功能,它们与用户程序连接在一起,没有或很少有完整的文件系统,适合于固定的应用环境。
图1-19 嵌入式系统结构分布
相比较而言,高级一些的复杂嵌入式系统则不然。以移动电话为例,软件部分在处理存储等内部请求的同时响应用户的操作。更主要的,用户还要能够安装自定义的应用程序,这使得操作系统必须具有复杂的任务调度、请求处理能力。常见的高级操作系统有GNU/Linux、Windows CE、Windows Phone、vxWorks等,这些系统与前面所述的FreeRTOS等不同,拥有常见的计算机操作系统架构,能处理更加复杂的请求和动态加载代码。
很多处理器上电时仅有部分基本模块工作,为了成功加载操作系统内核,还必须借助引导器。以Linux内核来说,需要使用Grub/Lilo等引导器才能够在x86计算机上运行。同样,在手机等移动设备常用的ARM处理器上,Linux需要U-Boot、vivo、blob等引导器才能启动。引导器通常不需要编写,通过修改原有的代码,使之运行在自定义的平台上即可。
中间件是为系统软件和应用软件提供连接的软件,如数据库、Web服务器等均属于中间件。为便于软件各部件之间的沟通,在现代信息技术应用框架中,如Web服务、面向服务的体系结构等,中间件的应用比较广泛。严格来讲,中间件技术已经不局限于应用服务器、数据库服务器。围绕中间件,Apache基金会、IBM、Oracle、微软各自发展出了较为完整的软件产品体系。中间件技术创建在对应用软件部分常用功能的抽象上,将常用且重要的过程调用、分布式组件、消息队列、事务、安全、连接器、商业流程、网络并发、HTTP服务器、Web Service等功能集于一身,或者分别在不同品牌的不同产品中完成。
在嵌入式系统中,中间件一般不会处理复杂的大型应用。简单来说,嵌入式中间件就是运行在应用程序之下、操作系统之上,联系应用程序与操作系统,或应用程序与其他应用程序的软件。一般情况下,一个典型的系统中,图形用户界面、嵌入式数据库、运行库等都属于中间件。在传统开发过程中,软件通常由开发商自行开发完成。然而,随着嵌入式系统越来越大,功能越来越强,系统中从底层架构到上层需求之间或多或少存在重复,中间件就成为了一种低成本、高质量的选择。好的中间件选型会大幅度减少开发者的工作,同时减少测量、调试的工作。
一个完备的Android嵌入式系统中,成品的中间件几乎包含了支持APK程序的一切,从底层库函数到Java虚拟机,再到第三方的JNI库,最后是各种各样的JAR包。这些大量的中间件大幅度减少了Android系统开发的时间与成本。
作为运行在系统最顶层的程序——应用程序(简称APP),需要直接与用户沟通,因此不仅需要具备完善的功能,还要具备美观大方的用户界面,以及舒适的用户体验。APP开发往往依据常规软件工程的开发流程,在此不赘述。
pcDuino是联斯普瑞电子科技有限公司发布的一款小巧而强大的PC平台,它结合了ARM架构的迷你PC和Arduino的优势,实现了开源软件Linux和开源硬件Arduino生态系统的完美结合。
pcDuino支持Ubuntu Linux和Android 4.0ICS,采用标准的Android SDK,支持Python、C语言、Java等编程语言。pcDuino的出厂默认设置是将Ubuntu安装在NAND flash里,Ubuntu可以从NAND flash启动,或者通过可启动的Micro SD卡启动。用户可以根据自己的需要将pcDuino的系统更新为最新的系统。
pcDuino&Ubuntu支持Ubuntu Linux 12.04版,该版本是为在具有DRAM限制并且支持NAND flash的ARM Cortex平台上运行而优化的定制版。设备可以通过支持USB接口的鼠标和键盘来操作,具体的支持程序清单如表1-4所示。
表1-4 Ubuntu Linux 12.04版的支持程序
注:系统管理员和密码都是ubuntu。
pcDuino为支持HD-TV的输出显示设备对Android 4.0进行优化,形成定制版Android 4.0。此显示设备需要通过支持USB接口的鼠标和键盘来操作,具体支持的部分程序清单如表1-5所示。
表1-5 Android 4.0定制版支持的部分程序