软件设计部分主要包括uboot移植、内核编译、系统移植、设备驱动编程、应用程序编程(QT编程、mysql数据库编程、控制系统编程)、各个模块的功能函数(部分是在windows下面的IAR中实现)。
软件部分的结构框图如图4-1所示:
图4-1 软件结构框图
由于每个操作系统在运行前必须要运行一段小程序,这个就是通常说的Bootloader,类似windows的BIOS的固件程序[15]。通过这段程序可以初始化硬件设备、建立内存空间映射图,从而将系统引入一个合适的状态,以便能够最终调用操作系统内核准备好的正确环境[16]。Bootloader的启动程序流程图如图4-2所示:
图4-2 Bootloader的启动程序流程图
①下载U-boot源码,到网站http://www.denx.de/wiki/U-Boot下载需要的源码uboot1.1.6-2012-09-25.tar.gz
②建立和ARM板相适应的类型
修改顶层的Makefile工程文件,修改编译器的路径如图4-3所示:
图4-3 编译器的路径③修改相关的参数参数包括目标板的相关的文件;独立于处理器体系结构的通用代码,例如内存大小的探测与故障检测;与处理器相关的文件,设备驱动程序等。④编译并测试运行#make smdk6410_config和#make命令如图4-4所示图4-4 运行#make smdk6410_config和#make命令编译的时间比较长,最后生成的一个u-boot文件,使用file u-boot命令查看该文件,判断是否生成了ARM平台可以运行的u-boot。图4-5 查看u-boot文件的属性将生成的u-boot文件拷贝到SD卡上进行,然后开发板通过SD卡启动,进入putty控制台设置U-boot环境变量:图4-6 设置U-boot环境变量
这里要检测能够ping通虚拟机才能进行下一步:使用ping 192.168.12.18
图4-7 检测能够ping通虚拟机 4.2 内核裁剪 Linux内核虽然支持多种硬件平台,但是对于一个嵌入式的设备,有些功能是没有必要的,所以要进行一些裁剪[17]。对于这个平台需要用到的就是触摸屏,GPIO口,以太网,串口,摄像头,NandFlash这几个主要驱动程序。只需要将用到的东西裁剪下来就够了。下面是内核裁剪的基本思路: ①下载并解压源码包,本次课题使用的Linux-3.0.1版本的源码; ②配置编译平台修改Makefile,主要的是体系结构和交叉编译变量的定义。如图4-8所示:
图4-8 修改makefile ③裁剪内核 执行命令: #make menuconfig
图4-10 内核裁剪界面 进入到Device Drivers上进行配置自己想要的信息其中空格代表的是不需要编译,“*”代表的是必须编译的,“M”是可加载的选项。执行make zImage制作内核镜像,生成的内核镜像在arch/arm/boot/目录下: 通过tftp服务烧写内核镜像到NandFlash上。 4.3 根文件系统的制作 利用OK6410开发提供的工具使用命令: #./mkyaffs2image-nand256m FileSystem-Yaffs2 rootfs.yaffs2 通过tftp服务文件系统烧写到NandFlash中。 4.4 QT程序设计 本课题的QT程序设计有两个部分,一个是在PC机上运行的远程的客户端,一个是在开发板的触屏上运行的Qt程序。Qt开发的框架如图4-12所示:
嵌入式Qt应用程序从开发到能够应用的流程如图4-13所示:
OK6410板上运行的程序设计,首先做一个UI界面,这里使用的是QWidget类设计的。QWidget类是所有用户界面对象的基类。窗口部件是用户界面的一个原子:他从窗口系统接受鼠标、键盘、和其他事件(点触),并且在屏幕上绘制自己的表现[18]。每一个窗口部件都是矩形,窗口部件可以被其他的父窗口盖住不见或者被前面的部件盖住一部分。使用qt-disgner进行布局设计,会得到自己想要的界面效果,本系统在开发板上运行的GUI界面如图4-14所示。
图4-14 GUI界面
信号与槽机制是Qt的核心机制,信号与槽式一种高级接口,用于对象之间的通信[19]。当对象改变时,信号就由该对象发射(emit)出去,指导对象所要做的全部事情,通过调用 QObject对象的
connect函数来将某个对象的信号与另外一个对象的槽函数相关联,这样当发射者发射信号时,接收者的槽函数将被调用[20]。该函数的定义如下:
bool QObject::connect ( constQObject * sender, const char * signal, const QObject * receiver, const char *member )
本课题中的电灯泡和温湿度的触发就是使用的这个机制,当触屏上的一个事件电灯泡被按下的瞬间就会发送一个信号,调用电灯泡点亮的函数。信号与槽的机制流程图如图4-15所示:
使用的一些信号如下:
signals:
void isLight(bool states); //灯泡控制信号
void setTempSignal(int temp); //温度信号
void setHumSignal(int hum); //湿度信号
使用的槽函数如下:
private slots:
void on_btnLed4_clicked();
void on_btnLed3_clicked();
voidon_btnLed2_clicked();
void on_btnLed1_clicked();
void readMyCom();
void all_Led_on_writeCom();
void all_Led_off_writeCom();
void temphum_writeCom();
void setLightStates(bool states);
void setTempSlot(int temp);
void setHumSlot(int hum);
信号与槽的连接函数如下:
MainWin::MainWin(QWidget*parent) :
QWidget(parent),
ui(new Ui::MainWin)
{
ui->setupUi(this);
startInit();
……
connect(tempTimer, SIGNAL(timeout()), this,SLOT(temphum_writeCom()));
connect(this,SIGNAL(isLight(bool)),this,SLOT(setLightStates(bool)));
connect(this,SIGNAL(setTempSignal(int)),this,SLOT(setTempSlot(int)));
connect(this,SIGNAL(setHumSignal(int)),this,SLOT(setHumSlot(int)));
……
}
本课题的以室内的空调作为一个智能电器,设置一个温度阈值,当温度到达这个阈值的时候启动空调,比如一般夏天室内的温度为25摄氏度,如果温度超过了25摄氏度,那么空调就会自动开启。通过周期性的获取室内当前的温度,并且对接受的信息与设定的值进行比对当温度到达适宜的温度的时候自动停止,这样可以节约用电。这里的数据处理是DHT11获取环境中的温度,发送到OK6410开发板,与设置的温度进行对比,如果温度超出了阈值,那么就发送相应的指令。同时在触摸屏的界面上可以看到空调相应的状态。图4-16是温度控制的一个流程图。
由于ARM网关和Zigbee协调器之间是基于RS232串口通信,所以为了解决串口通讯的问题,本课题采用的是波特率为115200,数据位为8,停止位为1,无校验数据位。涉及到数据通信就得设计一个数据的帧格式,这里的帧格式设置为:帧头+节点ID码+模块的ID码+数据(CommandH + CommandL)+帧尾。
这个数据帧的详细说明可以用DHT11温湿度传感器来描述,例如一个温度数据帧为:
0xCC,0xEE,0x01,0x03,0x01,0x00,0x00,0xFF
模块ID用于区别是那个模块由灯的模块、温湿度模块、电机模块等。这些数据只是发送的命令,那么接受数据帧格式有些区别的在于帧头,数据为0xEE 0xCC例如接收到一个数据帧为:
0xEE,0xCC,0x01,0x03,0x01,0x01,0x01,0xFF
Qt串口编程使用的是库函数编程,使用QextSerialPort类,QextSerialPort类读取串口数据的方式有两种:Polling即轮询方式、EventDriven是指事件驱动方式[21]。但是在Linux系统中只能使用Polling方式,这种方式下串口读/写是同步的,信号在这种状态下是无法使用。这种方式的优点是系统开销小,但是需要自己定义一个定时器去读取串口的数据。
使用QextSerialPort实现串口通信,实现串口数据通信的配置工作在下面代码中可以集中的体现。
void MainWin::openCom() { myCom = new Posix_QextSerialPort(COM, QextSerialBase::Polling); //这里QextSerialBase::QueryMode应该使用QextSerialBase::Polling isOpen = myCom->open(QIODevice::ReadWrite); if(isOpen){ // QMessageBox::information(this, tr("打开成功"), tr("已成功打开串口 ") + COM, QMessageBox::Ok); }else{ QMessageBox::critical(this, tr("打开失败"), tr("未能打开串口 ") + COM + tr("\n该串口设备不存在或已被占用"), QMessageBox::Ok); return; } //设置波特率:115200 myCom->setBaudRate(BAUD115200); //设置数据位:8位 myCom->setDataBits(DATA_8); //设置校验:无 myCom->setParity(PAR_NONE); //设置停止位 myCom->setStopBits(STOP_1); //开启读取定时器 timer->start(timerdly); //设置数据流控制 myCom->setFlowControl(FLOW_OFF); //设置延时 myCom->setTimeout(TIME_OUT); }
Zigbee的程序设计使用了IARworkbench开发工具,实现了串口的通信、灯泡的控制、风扇空调的控制、窗帘的控制、传感器的数据传输功能。
串口的使用基本步骤:
①初始化串口,其中包含波特率、校验位、中断、时钟频率等。具体的代码如下:
/**************************************************************** 串口初始化函数 ***********************************************************/ void InitUart() { CLKCONCMD &= ~0x40; // 设置系统时钟源为 32MHZ晶振 while(CLKCONSTA & 0x40); // 等待晶振稳定 CLKCONCMD &= ~0x47; // 设置系统主时钟频率为 32MHZ PERCFG = 0x00; //位置1 P0口 P0SEL = 0x3c; //P0_2,P0_3,P0_4,P0_5用作串口,第二功能 P2DIR &= ~0XC0; //P0 优先作为UART0 ,优先级 U0CSR |= 0x80; //UART 方式 U0GCR |= 11; //U0GCR与U0BAUD配合 U0BAUD |= 216; // 波特率设为115200 UTX0IF = 0; //UART0 TX 中断标志初始置位0 U0CSR|=0x40; //允许接收 IEN0|=0x84; //开总中断,接收中断 }
②向发送缓冲区发送数据或者从接收缓冲区读取数据。具体的代码如下:
/**************************************************************** 串口发送数据函数 ****************************************************************/ void Uart_Send_String(uchar *Data,int len) { int j; for(j=0;j<len;j++) { U0DBUF = *Data++; while(UTX0IF == 0); UTX0IF = 0; } } /*******************串口接收函数***********************/ #pragma vector = URX0_VECTOR __interrupt void UART0_ISR(void) { URX0IF = 0; // 清中断标志 command = U0DBUF; Rxdata[p]=command; p++; if ( p == 8) { p = 0; } }
传感器模块、电机模块、电灯的控制主要用到的是IO口控制编程,使用的配置方式类似STM32的配置方式,具体的步骤如下:
①功能设置寄存器P1SEL,默认是普通IO口。0表示普通IO口,1表示第二功能
②输入输出配置P1DIR,0表示输入,1表示输出
③IO口的输入时的电路模式配置,0表示上拉/下拉,1表示高阻态。
具体的代码以电灯泡为例说明:
void LEDABCD_INIT(void) { P1SEL &= ~0x30; //选择端口 P1DIR |= 0x30; //设置为输出模式 P1INP &= ~0x30; //打开上拉 }
由于OK6410开发板上的屏幕与PC机上的屏幕不一样,大小和字体都要设定需要设定。因为用到的触摸屏是480*272的分辨率,这里支持的中文字体为“wenquanyi”所以在main.cpp中加入以下的代码:
int main(int argc, char *argv[]) { QApplication a(argc, argv); …… MainWin w; #if defined(_WS_QWS) || defined(Q_WS_QWS) w.showFullScreen(); QFont font("wenquanyi",12,QFont::Bold); a.setFont(font); #else w.resize(480, 272); #endif …… w.show(); …… }
在终端中使用交叉编译工具编译并结合脚本语言制作一个shell脚本,使用命令:
#vim myshell.sh
在文件中添加以下的内容:
#/bin/bash
qmake -project
/opt/qt-4.7.1/bin/qmake
make
cp SmartLife /nfsroot/FileSystem-Yaffs2/
然后更改myshell.sh的属性使用命令:
#chmod +777 myshell.sh
执行myshell.sh就可以实现交叉编译并将生成的可在ARM上运行的文件。
这样生成的SmartLife是一个ARM平台上可以执行的文件在putty中使用./SmartLife –qws 即可执行生成SmartLife。
用户需要使用用户名和密码才能登录,可爱的企鹅作为背景图片,实时查询家居中的所有情况,这样可以通过网络远程控制家居中的情况。
登录的界面使用对话框的模式来写的调用了Dialog类,用户名和密码的校验使用的是密文方式进行的,具体的代码如下:
void Dialog::on_loginbtn_clicked() { QTextCodec::setCodecForTr(QTextCodec::codecForLocale()); if(ui->usredit->text()==tr("zhanghao")&&ui->pwdedit->text()==tr("123456")) { accept(); } else { QMessageBox::warning(this,tr("warning"),tr("user name or passwd wrong"),tr("yes"),tr("no")); //如果不正确,弹出警告对话框 ui->usredit->clear();//清空用户名输入框 ui->pwdedit->clear();//清空密码输入框 ui->usredit->setFocus();//将光标转到用户名输入框 } }
背景图片的添加使用绘图工具将背景图片放到工程目录的image下,通过调用paintEvent()函数来时实现,具体的代码:
void Dialog::paintEvent(QPaintEvent *) //界面图片的载人,要在头文件里声明此成员 { QPainter painter(this); QPixmap pix; pix.load(":/image/8.JPG"); painter.drawPixmap(0,0,400,350,pix); }
客户端相关的UI设计图如图4-17、4-18、4-19、4-20所示:
调试的思路是:使用USB转TTL串口,将USB转TTL串口的RX与OK6410开发上的上的TXD,将USB转串口的TX与OK6410开发板上的RXD,特别要注意的是要将OK6410开发板上的GND和USB转TTL串口的GND要接在一起(共地很重要),之前因为这个问题浪费了很多的时间,后来实验室的同学跟我说这个一定要共地,否则大多数时候数据传输会出现误码。然后使用串口调试助手在PC机上看发送的命令是否与自己在触摸屏上的操作一致,将接收到的数据保存在txt文档中,用记事本打开保存的txt文档,然后再与之前的操作所对应的命令进行比对发现这个txt文档进行比对发现完全一样。可以得出结论1:智能家居的ARM网关的串口可以向外界发送指令。
同样的在串口调试助手中发送相应的数据,如果在TFT屏上能够实现相应的显示结果那么ARM网关串口可以接收外界发送的数据。可以得出结论2:智能家居ARM网关的串口可以正确的接受数据并做出相应的应答。
综合结论1和结论2可以得出ARM网关的串口程序是正确的。
在Zigbee上的串口要确定数据是正确的,这里发送的数据是每都是通过串口的,首先得确定发送的数据是正确的,这里使用的串口调试助手,将发送的数据与串口调试助手上显示的数据进行比对,如果没有错误,那么这个串口的发送数据是没有问题的。经过几番的调试和修改代码发现串口的数据发送终于可以按所需要的格式的发送数据了。接下来的便是接收数据,使用的方法是将接收到的数据发送出来在串口调试助手上显示。但是在使用串口函数的时候就得给发送处理的数据屏蔽掉,不然发送处理的数据是无法预测的。
Zigbee与ARM之间的调试出现的问题了一些问题,用无线传输数据会发送出错误的信息,这里的原因可能是代码的问题,因为使用无线传输要使用协议栈,而这个Zigbee并没有跑协议栈,如此用无线传输肯定会出现问题。使用的无线串口数传输模块CC1101进行调试发现还是有问题,其中的原因可能是时序的问题,如果要完全解决这个问题,还是有很大的困难,这个课题做到的仅仅是有线的数据传输。于是这个无线的传输失败告终了,只能使用有线的数据传输来进行调试,这样达到了预期的效果。
智能家居模型是使用PVC板制作而成的,在模型中有:LED做成的灯具、直流电机和PVC材料做成的空调模型、减速电机和布条做成的窗帘、光照度采集模块(光敏电阻)、温湿度采集模块(DHT11)、门禁模块(单片机+RFID+舵机+蜂鸣器)。
智能家居运行(灯具全部打开,门禁刷卡成功,温湿度在设定的范围内)效果图1。如图5-1所示:
开发板上运行的效果(4个LED灯全部亮起,可以看到温湿度正常当前的显示:26摄氏度,湿度为35%,设定的温度阈值为27摄氏度,湿度为70%,当前的温湿度均没有超过阈值,所以空调没有开启)如图5-2所示:
本设计中对目前国内外物联网智能家居系统的发展现状和未来的发展趋势,以及相关技术进行了详细的分析。在现有的智能家居系统解决方案的基础上,提出了处理器和无线技术相结合的设计方案,并且融合了互联网通信技术,实现了真正意义上的物联网,以及远程对智能家居系统进行实时的监控和管理。本文中主要完成了以下工作:
(1)在现有的智能家居系统解决方案的基础上,提出了智能家居系统的设计方案,主要包括家庭网关主控制器、终端受控设备、家庭内部控制网络、外部通讯网络四个部分。
(2)对家庭网关及其核心功能模块进行了详细的分析,并且完成了电源模块、系统复位电路、串行接口电路、接口、接口、以太网接口电路、存储电路、接口电路的硬件设汁。
(3)在软件设计方面主要是进行了引导程序的移植,接着在此基础上进行了内核的移植与相应的配置工作,最后,完成了文件系统的制作与移植以及嵌入式服务器的移植。
(4)对家庭内部控制网络模块进行了分析,完成了客厅灯具的开关控制、家居中温湿度监测及控制、智能窗帘的控制。
(5)在无线网络技术的协议栈和应用开发接口函数等基础上,实现了家庭内部控制网络之间的通信,并在此基础上进行了远程控制和查询功能的初步实现。
(6)在无线射频(RFID)的基础上,实现了智能门禁的控制功能。
本文中很多可以扩展的地方,可以通过数据库实现家庭中环境的存储查询统计等功能。同时可以通过手机短信接受的方式对家中的异常情况进行报警,另外还可以进行Android开发和IOS的开发。
由于本人的水平有限,在文中出现一些不足和疏忽,希望不吝赐教,欢迎批评和交流。
版权声明:本文为博主原创文章,未经博主允许不得转载。