- 4.74 MB
- 2022-09-27 发布
- 1、本文档由用户上传,淘文库整理发布,可阅读全部内容。
- 2、本文档内容版权归属内容提供方,所产生的收益全部归内容提供方所有。如果您对本文有版权争议,请立即联系网站客服。
- 3、本文档由用户上传,本站不保证质量和数量令人满意,可能有诸多瑕疵,付费之前,请仔细阅读内容确认后进行付费下载。
- 网站客服QQ:403074932
无线电子菜谱系统的设计及实现附程毕业论文目录第一章绪论11.1研究背景和意义11.2论文的研究目标21.3内容安排3第二章系统总体方案设计与论证42.1系统组成42.2总体方案设计52.2.1主控制器模块52.1.2无线模块62.1.3彩屏模块82.1.4SD卡模块82.1.5触屏模块92.1.6语音模块112.1.7电源模块112.1.8传感器模块122.1.9串口模块12第三章硬件电路设计143.1主控制器模块143.2无线模块153.3TFT彩屏模块163.4SD卡模块173.5触屏模块193.6语音模块193.7电源模块213.8温度传感器模块213.9串口模块22第四章软件的设计234.1开发调试工具234.2软件调试244.2.1移动端硬件驱动程序244.2.2接收端硬件驱动程序304.2.2上位机程序设计33第五章系统调试分析425.1移动端调试425.2移动端和接收端联调43II\n5.3系统联调44第六章总结45参考文献46致谢47附录48II\n第一章绪论1.1研究背景和意义人类利用无线通信技术的历史已经有几千年了,古时候用的烽火台就是最原始的无线通信。但这时候的无线通信技术还只是处于萌芽阶段,只有到19世纪末意大利人马可尼发明无线电报开始,人类才真正开始大规模地利用无线通信技术。近数十年来随着计算机技术和电子技术的发展,无线通信技术更是以日新月异的速度向前发展,它也成为了通信领域的一个重点研究方向。现代的无线通信技术是建立在硬件电路的基础上的,因此微电子技术的发展直接制约着无线通信技术的发展[1]。回顾集成电路的发展历程,我们可以看到,自发明集成电路至今40多年以来,“从电路集成到系统集成”这句话是对IC产品从SSI(SmallScaleIntegrated小规模集成电路)到VLSI(VeryLargeScaleIntegrated超大规模集成电路)今天特大规模集成电路发展过程的最好总结,即整个集成电路产品的发展经历了从传统的板上系统(System-on-board)到片上系统(System-on-a-chip)的过程。随着集程度的提高,芯片的体积能耗和成本在逐步降低,这也使电子产品向便携式和低端市场发展。虽然微电子的发展历史已经有半个多世纪,但是射频芯片的发展却是近几年的事。从分类上来看,射频芯片属于专用集成电路。目前国际上有很多专门生产射频芯片的公司,例如Nordic公司和Chipcon公司。这些芯片一般工作在免费频段,采用专门的调制解调技术,内部集成了很多电路。像Nordic公司的NRF24L01芯片,它是单片射频收发芯片,工作于2.4~2.5GHzISM(IndustrySciencemedicine,工业、科学、医学)频段,芯片内置频率合成器、功率放大器、晶体振荡器和调制器等功能模块,输出功率和通信频道可通过程序进行配置。芯片能耗非常低,以-5dBm的功率发射时,工作电流只有10.5mA,接收时工作电流只有18mA,多种低功率工作模式,节能设计更方便。其DuoCeiverTM技术使NRF24L01可以使用同一天线,同时接收两个不同频道的数据。98\n无线电子点菜系统是无线通信技术的一个典型应用。近些年来,随着人民生活水平的提高和生活方式的转变,餐饮业具有巨大的投资市场,被称为中国的黄金产业。人们在消费过程中对服务质量也有了更高的要求,同时餐馆之间的激烈竞争也促进了无线通信技术在餐饮业中的应用。无线电子点菜系统的目的就是利用最先进的2.4GRF无线通信技术、嵌入式移动数据库技术以及触摸屏的掌上电脑技术,实现餐饮业的全面数字化,降低人工费用。无线电子点菜系统能够进一步提高餐厅档次、提高点菜效率,而且具有结账、更新菜单、实时查看账单等功能,而且能够有效避免人为错误、避免跑单、实时查看餐馆收益情况、提供查看诸如今日流水账这样的统计信息等,用数字化的手段来革新餐饮行业,优化业务流程,为每位客户提供更优质的服务,实现餐厅利润的最大化而又不至于成本过高,是餐饮行业向信息化发展的一个重要标志。由于使用无线技术通信,可以不用进行复杂的布线,这也大大降低了餐馆的建设成本,减少了对线路维护的开支。同时,无线通信设备的可移动性也使服务员随时可以和吧台联系。无线电子点菜系统基于目前很热门的技术——嵌入式技术,依托一定的硬件平台。因此微电子技术的发展对系统的性能有很大的限制,目前微电子技术已经发展到了SOC(SystemOnChip,片上系统),集成度获得了极大的提高。同时,芯片的价格,体积和能耗进一步降低,这些都使无线电子点菜系统向移动化和大众化方向发展。可以说,随着微电子技术的进一步发展,无线电子点菜系统仍然有很大的发展空间。1.2论文的研究目标本设计主要是用一个主控模块控制外围器件使其协调工作,初步决定通信方式使用单工方式,先实现其功能,即多个从机只向主机发送信息,主机不返回任何信息,可制作一个移动端(发送端)和一个连接电脑的接收端进行演示。其基本功能应包括:1.移动端能进行菜单显示,利用触屏等方式输入信息;2.点菜过程中需要有一定的交互,例如进行语音提示;3.移动端在点菜完毕后利用预定的协议打包发送数据,连接电脑的接收端实时接收数据并解码,再利用串口传送至电脑,电脑上位机程序最后进行相应操作,完成一次点单过程。对于后期产品功能初步实现后再考虑半双工的通信方式,进行实时数据更新、点单反馈等更人性化的操作。98\n1.3内容安排本文是对无线电子菜谱系统系统设计及实现进行了深入的研究,全文共分为六章,各章节的内容安排如下:第一章介绍了本文的研究背景和各章节的内容安排情况。第二章先介绍了系统总体的结构,然后对每个模块进行选型,并对所使用的芯片及其原理进行了简单的介绍。第三章在上一章的基础上设计自己的硬件平台,详细介绍了平台的组成部分和各自的功能。第四章主要介绍了移动端、接收端和上位机软件方面的设计。第五章主要讲了系统调试方面所遇到的问题,以及对该问题的分析,并提出解决方法。第六章对本次的设计工作进行了总结,包括所思、所感、所得等。98\n第二章系统总体方案设计与论证2.1系统组成根据对本系统基本功能的理解,可为本系统描述一个基本的使用场景:当顾客从服务员手中拿到点菜系统的移动端,餐桌号已由服务员预先输入,此时播放语音提示,提示顾客直接进入点菜流程,通过触屏顾客一页页的浏览菜单,包括价格、菜品图片、菜系等信息,最终顾客完成点菜,单击“完成”按钮,进行语音温馨提醒“美味稍等片刻”,此后屏幕再次恢复到点菜前的界面,为丰富显示内容,此时显示实时温度,直到触发进入下次点菜流程。而电脑端在接收到经过解码的数据之后,信息入库,提示有一新菜单。这样移动端与接收端就协调工作最终完成一次点单流程。由此可确定本系统的硬件系统应包括语音模块、TFT彩屏模块、主控模块、存储模块、无线模块、触屏模块、温度检测模块。其移动端系统结构框图如图2-1,连接电脑的接收端系统结构框图如图2-2。图2-1移动端系统结构图图2-2接收端系统结构图在确定了系统的总体方案后再逐步细化到各模块,下面将对系统各模块的选型及其简介进行阐述。98\n2.2总体方案设计2.2.1主控制器模块在开题报告中曾经将主控制器选为MC9S12XS128,这是基于对彩屏刷屏速率的考虑,在之后的硬件调试中发现将MC9S12XS128的时钟频率设置在32MHZ时,彩屏的刷屏速度并不是很快,平均显示一张320*240的图片会用38S左右的时间,而将频率设为40MHZ以后,刷屏速率也并无明显增快。而且因为没有用文件系统,存储的图片是用Image2Lcd软件转换成bin的二进制格式,存储在1G内存卡里面,当频率设置过高后读SD卡会变得不稳定,错误率过高。故处于上述考虑,将主控制模块换为STC90C516RD+,该款单片机最高可使用40M晶振[2],经测试使用24M晶振后,单片机平均显示一张320*240的图片会用46S左右的时间,而读内存卡时由于速度不是很快,传输误码率也几乎可以忽略。故将主控模块选为该款单片机。下面将对STC90C516RD+单片机进行简要介绍。STC90C516RD+系列单片机是宏晶科技推出的新一代增强型单片机,具有抗干扰/高速/低功耗的的特点,完全兼容传统的8051单片机指令码,可任意选择12时钟/机器周期或6时钟/机器周期,内部整合了MAX810专用复位电路,时钟频率在12MHz以下时,复位脚可直接接地[3]。1.工作电压:5.5V-3.8V(5V单片机)/3.8V-2.4V(3V单片机);2.工作频率范围:0-40MHz;3.用户所使用的应用程序空间最大可达61KB;4.内部集成了1280字节/512/256字节RAM;5.通用I/O口(35/39个),复位后为:P1/P2/P3是准双向口/弱上拉,P0口是开漏输出,作为总线扩展用时,不用加上拉电阻,作为I/O口用时,需加上拉电阻;6.ISP(在系统可编程)/IAP(在应用可编程),无需专用编程器/仿真器,可通过串口(P3.0/P3.1)直接下载用户程序,8K程序3-5秒即可完成一片; 7.电可擦除只读存储功能;98\n1.内部集成看门狗功能;2.片上集成MAX810专用复位电路,外部晶体12M以下时,可不接外部复位电路,此时复位脚可直接接地;3.共2个16位定时器/计数器,其中定时器0还可以当成2个8位定时器使用;4.2路外部中断,采用下降沿中断或低电平方式触发中断,低功耗模式可由外部中断以低电平方式触发中断唤醒;5.1路通用异步串行口(UART),方便传输数据;6.工作温度范围:0-75℃/-40-+85℃;7.封装:LQFP-44,PDIP-40,PLCC-44;2.1.2无线模块根据要求无线模块选用NRF24L01。现在国际上有很多公司生产基于ZigBee协议的芯片,芯片的集成度也越来越高。例如挪威的Nordic公司的NRF系列芯片,还有Chipcon公司的无线数据传输芯片等。各个公司的芯片原理基本相同,编程规则大致相同,因此选用Nordic公司的NRF24L01也能符合系统要求,下面详细叙述一下NRF24L01芯片的特点[4]。同蓝牙一样NRF24L01无线收发一体芯片也工作在2.4GHZ自由频段,能够在世界各地的无线领域畅通无阻。NRF24L01支持多点间的通信,比蓝牙具有更高的传输速度,最高传输速率可超过1Mbit/s。它采用SOC方法设计,仅需焊接少量的外围元件便可组成一块射频收发电路。与蓝牙不同的是,NRF24L01没有复杂的通信协议,方便用户使用,相同产品之间可以自由通信而无限制。更重要的是,NRF24L01的价格比蓝牙产品更低。因此NRF24L01是业界体积最小、功耗最少、外围元件最少的低成本射频系统级芯片。NRF24L01的内部结构原理及外部组成框图如图2-3所示,它主要有四种工作方式。 98\n图2-3NRF24L01的内部结构原理及外部组成框图1.待机模式待机模式1在保证快速启动的同时降低了系统平均消耗电流。晶振在待机模式1下正常工作。而在待机模式2下仅有部分时钟缓冲器处在工作状态。当发送端发送输入输出寄存器为空并且引脚CE电平为高时进入待机模式2。在处于待机模式时,芯片保持寄存器配置字内容不变。2.掉电模式工作在掉电模式时,关闭NRF24L01各功能,保持着最小的电流消耗。而在进入掉电模式后,NRF24L01停止工作,但保持寄存器内容不变。掉电模式由寄存器中PWR_UP位来控制。3.ShockBurstTM模式ShockBurstTM模式下该芯片可以与成本较低的低速MCU连接。高速信号处理是由芯片本部的射频协议处理的,NRF24L01提供SPI接口,数据率取决于单片机本身接口速度。在ShockBurstTM模式下,当接收到有效的地址和数据时IRQ通知MCU,随后MCU可将接收到的数据从RXFIFO寄存器中读出。4.增强型ShockBurstTM模式增强型ShockBurst98\nTM模式使得执行双向链接协议更为容易、有效。一般典型的双向链接为:发送方要求接收端设备在接收到数据后应有应答信号,以便于发送方检测是否有数据丢失的情况,一旦发生数据丢失,则重新发送丢失的数据将该数据恢复。在增强型的ShockBurstTM模式下可以同时控制应答及重发功能而无需增加MCU的工作量。2.1.3彩屏模块彩屏显示较传统12864等显示屏显示更为美观,可显示内容更多。但彩屏相对价格也比较昂贵,本次设计采用分辨率为320*240的2.8寸屏幕,控制器为ILI9325,并行端口操作,一般市场价格为50左右,相对低廉。且彩屏上附触屏,减少外围电路设计。ILI9325有一个16位的变址寄存器(IR)、一个18位的写数据寄存器(WDR)和一个18位的读数据寄存器(RDR)。变址寄存器(IR)存储来自控制寄存器和内部的GRAM的指令信息。写数据寄存器(WDR)用来暂时存储要被写到控制寄存器和内部的GRAM中的数据。读数据寄存器(RDR)用来暂时存储从GRAM中读取的数据。MCU中要写入内部GRAM的数据,首先写到写数据寄存器(WDR),然后再由内部操作自动的写到内部的GRAM中。要读取的数据要通过读数据寄存器(RDR)从内部GRAM中读取,因此无效数据将被读到数据总线,当ILI9325从内部的GRAM中读取第一个数据的时候,有效数据将在ILI9325进行了第二次读操作之后被读出。寄存器在寄存器执行时间中会被连续的写入数据,除非在振荡器起震的时候执行了0个循环。ILI9325根据外部的接口和显示移动图片的情况提供了并行的RGB接口和VSYNC(垂直同步)接口。当RGB接口被选择,显示操作将和外部提供的信号VSYNC,HSYNC,DOTCLK同步。在RGB模式,根据使能信号的极性(高电平或是低电平)数据(DB17-0)将会根据这些同步信号被写入来阻止在数据更新的时候屏幕闪烁。2.1.4SD卡模块单片机内部仅有最大61KB的ROM,而一副50*50像素的图片就有5000字节的数据,所以依靠单片机内部的存储容量是远远不够的,所以98\n本次设计为了存储显示的图片,采用内存卡存储。并且由于后期产品升级的时候会有菜单信息等需要更新,所以将关于菜单方面的图片等存储在内存卡里面也,这样便于更换里面的文件。SD卡的技术是基于MultiMedia卡(MMC)格式上发展而来,大小和MMC差不多,尺寸为32mm*24mm*2.1mm。SD卡与MMC卡保持着向上兼容。SD卡内部主要分两部分:存储介质(nandflash)和主控制器。以51为核心而设计的主控制器,内部锁相环电路最高可以倍频到近100M。而作为存储介质的nandflash容量从1Gbit至128Gbit,甚至还高。其主要分类有:1.MMC卡:结构类似SD卡,采用MMC协议。2.SD1.0:SD1.0卡最大可支持容量达2Gbyte。3.SD2.0:SD2.0卡最大可支持容量达32Gbyte。这三种卡的协议是向上兼容的。也就是说SD2.0可以兼容SD1.0协议,SD1.0可以兼容MMC卡协议,即MMC协议的指令可以操SD1.0、SD2.0,同理SD1.0协议的命令可以操作SD2.0卡。所以平时看到初始化SD卡的时候只用了CMD0和CMD1两条命令就可以让上面三类卡进入传输状态的原因:使用MMC卡协议初始化时只定义了CMD0和CMD1。SD卡接口支持SD和SPI两种模式。在SD模式下采用4线并行模式,占用更多的端口。一般为提高传输速度,多媒体存储设备都会采用此种接口,而SPI模式更适合于低端MCU的使用,传输速度有限。图2-4SD卡引脚封装2.1.5触屏模块基于目前主流的输入方式是触屏输入,本次设计采用触屏输入方式一方面是为了迎合趋势,另一方面采用此种输入方式可大大简化硬件电路设计,且输入的信息可根据软件而定制,比传统的键盘输入方式更为灵活。由于价格的原因本次设计采用电阻屏,驱动芯片利用集成的XPT2046。98\n电阻触摸屏的工作原理主要是通过压力感应原理来实现对屏幕内容的操作和控制的,这种触摸屏屏体部分是一块与显示器表面非常配合的多层复合薄膜。面板(PET)下电极(ITO膜或ITO玻璃)OCA光学胶背板(PC等)键片上电极(ITO膜)OCA光学胶图2-5电阻式触屏结构不同线制的电阻屏坐标计算方法也不一样,我所采用的是四线电阻屏。当有物体施加一定的压力在触摸屏表面时,导致上层的ITO导电层发生形变与下层的ITO层发生接触,该结构可等效为图2-6Y-X-Y+X+所示电路。图2-6触摸时等效电路可分为如下两步计算触点的X,Y坐标:1.计算X坐标,在电极X+端施加驱动电压Vdrive,X-电极接地,测量引出端Y+的电压,由于ITO层均匀导电,Y+端的电压与Vdrive电压之比等于触点X坐标与屏幕的宽度之比,即98\n2.计算Y坐标,在电极Y+端施加驱动电压Vdrive,Y-电极接地,测量引出端X+的电压,由于ITO层均匀导电,X+端的电压与Vdrive电压之比等于触点Y坐标与屏幕的高度之比,即驱动芯片XPT2046是一种典型的逐次逼近模数转换器(SARADC),包含了采样/保持、模数转换、串口数据输出等功能。同时芯片集成有一个2.5V的内部参考电压源、温度检测电路,工作时使用外部时钟。XPT2046可以单电源供电,电源电压范围为2.7V~5.5V。参考电压值直接决定ADC的输入范围,参考电压可以使用内部参考电压,也可以从外部直接输入1V~VCC范围内的参考电压(要求外部参考电压源输出阻抗低)。X、Y、Z、VBAT、Temp和AUX模拟信号经过片内的控制寄存器选择后进入ADC,ADC可以配置为单端或差分模式。选择VBAT、Temp和AUX时可以配置为单端模式,作为触摸屏应用时,可以配置为差分模式,这可有效消除由于驱动开关的寄生电阻及外部的干扰带来的测量误差,提高转换准确度。在有触摸动作时的坐标转换由XPT2046完成,在得到坐标值后,会向MCU发出IRQ通知信号,单片机读取数据后经转换程序,转换为屏幕上对应的像素点,这样就完成了一次触屏取样过程。2.1.6语音模块本系统在设计时提出语音提示,这是一款人性化的产品所必须的,并且在最后的设计中由于大量端口的复用,最后不得不放弃震动提示的功能,这也使得该模块的存在意义重大。语音模块保持原来的设计思路,采用集成的WTV020-SD模块,可外置SD卡,将语音用特定软件转换成ad4格式后,再将语音数据按照顺序存储于SD卡内,单片机只需按照时序进行相应触发即可播放特定语音,使用方便,外围电路简单。2.1.7电源模块设计中有单片机、TFT彩屏、触屏模块需5V供电,而SD卡、NRF24L01、WTV020-SD模块需3.3V供电,所以本系统电源模块中必须能同时提供5V、3.3V的电源。其中5V采用LM7805稳压,而3.3V则采用AMS111798\n3.3经5V降压产生,该电路技术也很成熟,只需在焊接引脚时需要注意一下。2.1.8传感器模块由于单片机的刷屏速率不高,为了在不操作本系统时,丰富彩屏的显示内容,并且又能够显示一些实用信息而不占用过多的端口资源,所以采用单总线式温度传感器DS18B20。所谓的单总线接口是指仅需一个端口引脚进行通讯,而无需外部器件,由数据线供电,待机功耗为零。测温范围为-55~+125℃,以0.5℃递增,温度以9位数字量读出,转换温度数字量时间为200ms(典型值)。用户可自定义非易失性报警温度,传感器报警搜索命令识别并标识超过用户所限定温度(温度报警条件)的器件。DS18B20是这样测温的:用一个高温度系数的振荡器确定一个门周期,在这个门周期内,内部计数器对一个低温度系数的振荡器的脉冲进行计数,从而来得到温度值。计数器一开始被预置到对应于-55℃的一个值,如果在门周期结束前计数器到达0,则增加温度寄存器的值,表示所测得的温度大于-55℃。同时,将计数器复位到某个值,这个值由斜坡式累加器电路确定,用以补偿感温振荡器的抛物线特性。之后计数器又重新计数直到0,如果仍未结束门周期,将一直重复上述过程。为了获得更高的分辨率可用下述方法。首先,读取传感器所测得的温度值,并将表示0.5℃位(LSB)的值从读取的值中截去,这个值叫做READ。然后再读取计数器中剩余的值,这个值是在门周期结束后所保留下来的值,这个值叫做REMAIN。最后,我们用到在这个温度下每度的计数值PER_C。用户可以用公式计算实际温度值。2.1.9串口模块由于接收端需要和上位机进行通讯,所以需要应用到串口模块,串口通信的两种最基本的方式有同步串行通信方式和异步串行通信方式。同步串行是指SPI(SerialPeripheralInterface)的缩写,顾名思义就是串行外围设备接口。SPI总线系统是一种同步串行外设接口,它可以使MCU与各种外围设备以串行方式进行通信以交换信息,TRM450是SPI接口。异步串行是指UART(UniversalAsynchronous98\nReceiver/Transmitter),通用异步接收/发送。UART是一个并行输入成为串行输出的芯片。UART包含TTL电平的串口和RS232电平的串口。TTL电平是3.3V的,而RS232是负逻辑电平,它定义+5~+12V为低电平,而-12~-5V为高电平。本次设计采用异步串行通信方式。转换芯片使用美信(MAXIM)公司专为RS-232标准串口设计的MAX232芯片,使用+5V单电源供电。图2-7MAX232内部电路及外围接口电路98\n第三章硬件电路设计3.1主控制器模块单片机最小系统做为整个开发板的控制中心,控制各个外围电路协调工作,完成系统设计的功能。单片机工作时需要为其焊接一个最小应用系统板,其上电路应包括复位电路、时钟电路。图3-151最小系统板原理图外部RST引脚复位就是从外部向RST引脚加一定宽度的复位脉冲,从而实现单片机的复位。将RST复位管脚拉高并维持至少24个时钟加10us后,单片机会进入复位状态,将RST复位管脚拉回低电平后,单片机结束复位状态并从用户程序区的0000H处开始正常工作。98\n时钟电路负责为单片机提供能源。XTAL1内部时钟电路反相放大器的输入端,接外部晶振的一个引脚。当直接使用外部时钟源时,此引脚接外部时钟源的输入端。XTAL2内部时钟电路反相放大器的输出端,接外部晶振的另一引脚。当直接使用外部时钟源时,此引脚可浮空,此时XTAL2实际将XTAL1输入的时钟进行输出。我用的是典型的时钟电路,时钟从XTAL1进再从XTAL2出来。上图的第40、20引脚分别接电源的Vcc、Gnd,负责为单片机供电。第31引脚为内外存储器选择引脚,本系统中将其拉高,选择内部存储器,此时PSEN、ALE引脚可浮空。由于PO口默认为高阻状态所以为PO口加上上拉电阻。3.2无线模块无线模块根据要求用NRF24L01,该模块的电路如图3-2。图3-2NRF24L01应用电路将上述电路焊接好并通过预留的插针连接到单片机,这样将无线模块与最小系统板通过拔插的方式连接,这样无论在硬件还是在软件上改变单片机与其连接的引脚时都非常方便。图3-3预留的引脚98\nVCC脚所接电压范围为1.9V~3.6V之间,而不能在这个区间之外,大于3.6V将会毁坏该模块。推荐使用电压3.3V左右。除了电源VCC和接地端之外,其余引脚均可直接和普通的5V单片机IO口直接相连,无需电平转换,当然这个模块也更加适用于3V单片机了。在硬件上面没有SPI的单片机同样也可以控制本模块,单片机用软件模拟SPI通讯协议,用普通IO口即可。3.3TFT彩屏模块TFT彩屏模块利用购买的现成模块,利用FPC接口将其与单片机进行连接。图3-4FPC接口该模块采用并行数据传输方式,DB0~DB7、DB10~DB17为其数据端口,占用单片机大量端口,使得单片机仅有的32个通用I/O引脚一下子用去16个,剩余模块的端口资源所剩无几,这也为之后的单片机编程提出要求,需要统筹规划各模块,否则对于复用的端口易引起冲突。98\n引脚名称功能CS片选RS数据/命令RW读RD写DB0-DB7数据低8位DB10-DB17数据高8位LED背光-极LED_K背光+极VCCTFT电源+GNDTFT电源-表3-1FPC接口引脚说明3.4SD卡模块SD卡支持两种总线方式:SD方式与SPI方式。其中SD方式采用6线制,使用CLK、CMD、DAT0~DAT3进行数据通信。而SPI方式采用4线制,使用CS、CLK、MOSI、MISO进行数据通信。SD方式的数据传输速度比SPI方式要快,采用单片机对SD卡进行读写时一般都采用SPI模式。采用不同的初始化方式可以使SD卡工作于SD方式或SPI方式。图3-5SD卡应用电路SD卡的工作电压是3.3V,并且若用5V单片机直接驱动SD卡时需要为各端口加一限流电阻。98\n表3-2SD卡引脚说明引脚编号SD模式SPI模式名称类型描述名称类型描述1CD/DAT3IO或PP卡检测/数据线3CSI片选2CMDPP命令/回应DII数据输入3VSS1S电源地VSSS电源地4VDDS电源VDDS电源5CLKI时钟SCLKI时钟6VSS2S电源地VSS2S电源地7DAT0IO或PP数据线0DOO或PP数据输出8DAT1IO或PP数据线1RSV9DAT2IO或PP数据线2RSV注:S:电源供给I:输入、O:采用推拉驱动的输出PP:采用推拉驱动的输入输出本系统设计时只是希望将SD卡作为一个大的存储器,实现简单的扇区读写,没有引用复杂的文件系统,那样无疑会增加软件的编写难度,也并没有必要。用51单片机操作SD实现对它的读写,最核心的是它的时序,采用SD方式或SPI方式,也具有不同的操作方法。SD方式提供更大的总线数据带宽,但占用的端口也更多,所以本次设计采用SPI方式。3.5触屏模块本次设计中采用的是四线电阻屏,XPT2046的典型应用电路如图3-6。PENIRQ变低并作为处理器的中断信号。在测量X、Y和Z坐标的过程中,XP输入将与PENIRQ内部上拉电阻分离,这样可以避免来自这个内部上拉电阻的漏电流流过触摸屏,同时也避免了误差。此外,在X、Y和Z坐标测量中,PENIRQ被禁止,输出为低电平;在测量VBAT、AUX和temp过程中,PENIRQ98\n被禁止,输出为高电平。如果最后写进XPT2046的控制字包含了PD0=1,笔中断输出功能禁止,不能监测触摸屏上触摸动作。在这些情况下,要重新使能笔中断输出功能,需把带有PD0=0的控制字写进XPT2046。如果在最后写入的控制字中包含了PD0=0,笔中断输出将在这次转换结束后使能。转换结束发生在输出转换数据位1时的DCLK时钟的下降沿。建议无论在任何时候,处理器在发控制字给XPT2046之前都要屏蔽PENIRQ的中断,以避免错误的中断的触发。图3-6XPT2046应用电路3.6语音模块语音模块采用购买现成的WTV020-SD模块,这是一款大容量存储类型的可重复擦写语音内容的语音模块,最大可外挂容量为1GB的MicroSD卡。能加载AD4格式语音和WAV格式语音。WTV020-SD模块SD卡存储器控制端音频输出BUSY输出图3-7WTV020-SD应用方框图由于本设计中需要用单片机控制其语音播放,可利用该模块的二线串口控制模式来达到控制目的,封装好的模块其应用电路图如图3-8。98\n图3-8WTV020-SD应用电路音频信号选择从SPK+、SPK-端输出到扬声器。该模块可直接驱动0.5W扬声器。但由于没有找到小功率的扬声器,故在SPK+后加了LM386功放电路,增益200,以此来驱动2W的扬声器。选择二线串口控制模式,脉冲触发,由MCU或按键对I/O口进行控制。I/O口P02、P03、P07分别为下一曲、上一曲、播放/停止功能,P04为CLK输入,P05为DI输入,P04、P05组成二线串口控制模式。SD卡支持根目录文件读取方式,就是说,只把文件放置到SD卡的根目录就可以了。文件名为十六进制数据,包括WAV、AD4格式音频文件均如此,如0000.AD4、0001.AD4等。最多可读取到前512个音频文件。按键类型控制模式进行控制时,如点击“下一曲”可触发下一段音频,按文件名的数据顺序进行触发。并口模式和二线串口模式则直接发送文件名的二进制数据,进行触发播放语音。焊接按键主要是为了测试用,也可不用焊接。3.7电源模块本次设计中需要有5V和3.3V供电电路,其中5V供电采用LM7805,而3.3V采用AMS11173.3稳压芯片。并且焊接了一个开关和电源指示灯,方便操作和观察,其应用电路如图3-9。98\n图3-9电源模块应用电路3.8温度传感器模块DS18B20通过单总线发送或接收信息,在单片机和DS18B20之间只需要一条连接线即可。无需外部电源,用于读写和温度转换的电源可以从数据线本身获得。图3-10DS18B20应用电路因为出厂时每个DS18B20都有一个独特的片序列号,所以多只DS1820可以同时连在一根单总线上,这样就大大简化了电路的设计,读取某个DS18B20的温度只需先进行片序列号匹配即可。3.9串口模块同上位机通讯需要将TTL和RS232电平进行转换,采用MAX232芯片完成这样的功能,其原理图如图3-11。图3-10MAX232原理图98\n图3-11MAX232原理图通讯时利用单片机的P30、P31来进行串口通讯。设计上位机程序时若采用事件驱动方式,由计算机直接管理时,字节之间不可控,而且单片机串行口和PC机串行口速率差别较大,接收程序一定要精心合理的设计,才能使传输稳定可靠,否则很容易出现意想不到的问题。在调试过程中,如果不小心将串行口调试工具的波特率和开发板串行口的波特率设置为不同,就会出现错误。98\n第四章软件的设计4.1开发调试工具驱动程序是硬件电路的灵魂,没有驱动的硬件电路是没有用的。STC89C516RD+是51类单片机,可以像开发其他51单片机驱动一样开发它的驱动程序。单片机软件开发平台选择比较流行的KeiluVision4,因为现在关于KeiluVision4软件的资料很多,这样上手就会很快[5]。图4-1是该软件的运行界面。图4-1KeiluVision4运行界面而开发上位机软件则采用VisualC++6.0,是微软公司推出的一款优秀开发工具。VisualC++6.0在不牺牲灵活性、性能和控制力度的同时,给C++带来了更高水平的生产效率。它具有可视化的界面,封装了大量的类,使界面制作变的很简单,使用它可以方便快捷地开发Windows环境下的应用程序。VisualC++98\n6.0专门为串行口通信提供了MSComm控件,使用该控件程序员不必花时间去了解比较复杂的API函数,通过简单修改控件的属性和使用控件提供的方法就可以实现对串口的配置,完成串口发送和接收数据[6]。图4-2是该软件的运行界面。图4-2VisualC++6.0运行界面4.2软件调试整个数据传输系统有两部分组成:与PC机相连的开发板为接收端,它不能移动,接收移动端发送过来的数据,经编码成预定的格式向上位机发送数据;可以移动的为移动端,它由开发板和无线数据传输等模块组成。由于两端的地位和功能不同,因此对应的驱动程序也不同。4.2.1移动端硬件驱动程序移动端的硬件驱动程序主要功能有:驱动TFT彩屏、读触屏坐标、操作SD卡、播放语音、读取实时温度、无线发送点菜数据。对于程序的编写如果按照以前那样全部写在一个文件里,大量的代码,不易于维护及修改,程序出错了难以定位,故采用构件化的方式将各模块的驱动程序分开来写,然后再添加到工程,程序结构清晰[7]。98\n表4-1移动端端口分配表模块引脚名称端口备注ILI9325TFT彩屏模块RSP2^4复用WRP2^5复用CSP2^6不可复用RSTP2^7不可复用DATA_HP1不可复用DATA_LP0不可复用DS18B20温度传感器DATAP2^5复用XPT2046触屏模块DCLKP2^4复用CSP3^3不可复用DINP3^1未复用DOUTP2^0复用IRQP3^2不可复用WTV020-SD语音模块RST未使用CLKP2^2不可复用DIP2^1不可复用SD卡DCLKP2^5复用DIP2^4复用DOP3^6不可复用CSP3^7不可复用NRF24L01无线模块MISOP3^0未复用MOSIP2^3未复用SCKP2^0复用CEP3^5不可复用CSNP3^4不可复用在写程序之前需要将每个模块的所用的端口分配好,由于本设计MCU的端口严重不足,不得不复用端口,而由端口复用所产生的问题在编程时也很难查出来,所以编写程序时需要格外注意。本次设计采用的方法是,先分别调试每个模块,然后再综合,表4-1是经过调试后定下的端口分配表。98\n图4-3是移动端主程序的流程图,图4-2为中断流程图。图4-4中断流程图图4-3移动端主程序流程图其中“*输入桌号”、“*点菜”里面均是死循环,通过跳转程序转到下一流程,这样设计也是仿照VC++的窗口编程,每一个窗口都是一个while(1){...}循环,只有收到特定的消息或满足特定的条件才转到另一个流程,“*输入桌号”、“*点菜”这两个流程内部的流程分别如图4-5、图4-6。在图4-4中断流程图中采用的是外部中断0下降沿中断方式,在触屏有物体按下时,XPT2046会发出中断信号IRQ,单片机进入中断进行相应处理,在退出中断时返回主程序继续显示实时温度。98\n图4-5“输入桌号”流程图在图4-5中标识“”的对应于TFT屏幕上显示的相应按钮,而流程图中的“跳转到diancai”、“跳转到end”,其中的字母标号是在程序中设置的某段程序的入口标号,当满足条件时,如利用“gotodiancai;”这样的程序进行跳转,跳出死循环,转到下一个流程,类似于汇编里的JUMP指令,跳转到某段程序的入口地址[8]。98\n图4-6“点菜”流程图在图4-6中也用了图4-5中的表示方法。并且在图4-3主程序流程图中用了“清全屏”,而在图4-5“输入桌号”流程图和图4-6“点菜”流程图中用了“清屏”这是有区别的,因为单片机的刷屏速率不高,在程序开始时才用了“清全屏”,这是必须的,因为TFT彩屏初始化成功后显示的是一系列杂色,所以必须将整个屏幕进行刷新,而之后在程序正常运行时用“清屏”只是进行局部刷新,只对需要改变的地方,进行写入新数据,退出时再恢复,这样提高了运行效率。不求单项设计的最先进性,但求总体设计的合理性。98\n在程序最后所发送的数据的格式为:桌号,菜1,菜2,...,菜n,0x63,0x73。其中菜n表示该菜品所对应的序号,存储于数组menu_data中,最大可存储100字节的数据,其中0x63是‘c’的ASCII字码符,在接收端通过判断是否接收到该字符作为结束标志,而0x73是‘s’的ASCII字码符,移动端发送数据时通过判断该字符来结束发送。NRF24L01一次性最多只能发送32字节的数据,当需要发送的数据非32的倍数时,就会使得发送的最后一组数据并不是每个都是有效的,在定义NRF24L01最大发送个数时,将其设置为1,这样当遇到‘s’时停止发送,发送方式更灵活,软件编程更方便。在本设计中单片机通过软件模拟SPI方式与NRF24L01传输数据,数据发送函数如下:/*************************************************************//*函数:NRF24L01_SingleSend(unsignedchar*tx_buf)/*功能:单个循环发送tx_buf中数据/*************************************************************/voidNRF24L01_SingleSend(unsignedcharTxBuf[]){unsignedchari=0;while(TxBuf[i]!='s')NRF24L01_TxPacket(&TxBuf[i++]);//TransmitTxbufferdata}4.2.2接收端硬件驱动程序接收端的硬件驱动程序主要有两种功能:实现开发板通过串行口和PC机通信;实现开发板通过某些I/O口和无线数据传输模块进行通信。98\n表4-2接收端端口分配表模块引脚名称端口备注NRF2401无线模块MISOP2^5未复用MOSIP2^3未复用SCKP2^2未复用CEP2^1未复用CSNP2^4未复用串口通信模块TXDP3^0未复用RXDP3^1未复用接收端仅有无线模块和串口模块,功能比较少,所以单片机的端口完全够用,表4-7则为接收端的端口分配表。图4-7接收端主程序流程图STC89C52RC单片机串行口的工作方式和波特率由控制寄存器SCON和特殊功能寄存器PCON组成。通过设置合适的波特率和帧格式,来实现开发板和PC机之间准确的数据传递。串行口可以通过软件设置四种工作方式,各种工作方式的数据格式和波特率均有所不同,在本设计中将串行口工作于方式2、8位数据位、1位停止位、无校验、波特率9600,用的函数为:voidStartUART(void)//晶振12M98\n{TMOD=0x20;SCON=0x50;TH1=0xFD;TL1=TH1;PCON=0x00;TR1=1;}接收端在接收到数据后将所有的数据转换成ASCII码,并且在每相邻两个数据之间插入一个‘a’以便于区分,转换成形如“1a2a5a8a9a”这样的字符串,其中第一个为桌号,其余数字为所点菜品对应的序号,该序号在上位机的数据库里与菜单的序号也是一一对应的。数据编码的函数如下:while(menu_data[j]!='\0'){if(menu_data[j]<=9){send_data[i++]=menu_data[j++]|0x30;send_data[i++]='a';}else{send_data[i++]=(menu_data[j]/10)|0x30;send_data[i++]=(menu_data[j++]%10)|0x30;send_data[i++]='a';}}98\nsend_data[i]='\0';因为现在设计的菜单数量还不是很多,菜单序号没有上3位数字的,因此采用if(menu_data[j]<=9){...}else{....}这样的判断足矣,最后在字符串结尾写‘\0’结束符,向上位机发送数据,采用的发送函数如下:while(send_data[i]!='\0')R_S_Byte(send_data[i++]);4.2.2上位机程序设计上位机的功能主要有:对接收端发送来的数据进行解码存储到数据库;设置串口相关信息;对历史账单进行管理;对菜单的添加、删除、修改操作。上位机程序采用VisualC++6.0开发,该工具集成大量的API函数,利用其提供的方法可方便的编写上位机软件[9]。本次设计初步设计了四个基于对话框的窗口,为其创建的类有CWirelessMenuDlg、History、Setting、Updata四个类。而所有操作的基础都是基于数据库的,所以下面首先介绍数据库的设计。(一)数据库的设计在程序里由于涉及到了数据库,所以首先要设计数据库结构,存储某桌账单时需要的信息有桌号、点菜时间、点菜顺序、点菜数、应付金额、付账情况、点菜菜单,而存储菜单时最基本的信息应包括菜品名称、单价。这样可分别设计BILL表、MENU表分别存储账单信息和菜单信息。数据库采用MicrosoftAccess创建。表4-3和表4-4分别为BILL表和MENU表的结构。表4-3BILL表结构字段名称数据类型备注序号自动编号主键点菜顺序数字点菜时间文本桌号数字点菜数数字98\n应付金额数字付账情况文本字段名称数据类型备注点菜1文本点菜2文本续表4-3在BILL表设置了两个字段“点菜1”和“点菜2”,在存储的菜单信息量不大的情况下将从下位机接受来的菜单信息存储到“点菜1”下,“点菜2”下写‘a’表示未存储信息。而当所存储的菜单的字符长度超过255时则将其余信息存储到“点菜2”下,这样两个字段共同存储的最大字符长度将达到510,完全能够满足要求。表4-4MENU表结构字段名称数据类型备注序号数字主键菜品名称文本单价数字在MENU表中将字段“序号”的数据类型设置成数字而非自动编号,主要是为了实现在程序中对该表的修改。在删除该表中某条记录时会造成“序号”的不连续,若用自动编号则数据库会标识该条记录所对应的“序号”的唯一性,而无法用新的记录进行填充,所以将其数据类型设置为数字,以便于修改。(二)上位机软件的设计1.下面首先介绍主窗口类CWirelessMenuDlg,表4-5列出了其中所拥有的控件资源及为其映射的消息和对应的消息处理函数。该窗口中主要完成的功能有:付账;接收数据并对其处理刷新显示;显示今日账单;点击显示每个账单的详情;进入各个子窗口的入口。由于本窗口需要操作数据库,所以先介绍关于数据库的操作。(1)在StdAfx.h文件中先用#import"C:\\ProgramFiles\\CommonFiles\\System\\ado\\msado15.dll"no_namespacerename("EOF","adoEOF")语句引入数据库环境(2)在应用程序CWirelessMenuApp:InitInstance()初始化里加入:98\n::CoInitialize(NULL);...::CoUninitialize();(1)然后在对话框程序CWirelessMenuDlg中增加两个数据成员:_ConnectionPtrm_pc;//智能对象/指针_RecordsetPtrm_pr;(2)在构造函数里增加对上述数据成员的初始化:m_pc.CreateInstance("adodb.connection");m_pr.CreateInstance("adodb.recordset");这样就可以用m_pr、m_pc两个指针操作数据库了。(3)在对话框初始化里打开自己建的库CStringconsql="Provider=Microsoft.Jet.OLEDB.4.0;DataSource=C:\\Users\\Administrator\\Desktop\\WirelessMenu.mdb;PersistSecurityInfo=False";m_pc->Open((_bstr_t)consql,"","",adConnectUnspecified);在整个程序里只可打开一次库,否则会发生错误,但可以打开多次表。(4)对表的操作主要有查询和三种动作插入、删除、更新。下面对这几种操作各举一例,其余的都大同小异。查询:sql.Format("select*fromBILLwhere序号=%s",bill_num);m_pr->Open((_bstr_t)sql,m_pc.GetInterfacePtr(),adOpenDynamic,adLockOptimistic,adCmdText);插入:sql.Format("insertintoMENUvalues(%d,'%s','%s')",maxno,m_vgename,m_price);m_pc->Execute((_bstr_t)sql,NULL,adCmdText);删除:sql.Format("deletefromMENUwhere序号=%s",bill_num);m_pc->Execute((_bstr_t)sql,NULL,adCmdText);更新:sql.Format("updateMENUset序号=序号-1where序号>%s",bill_num);m_pc->Execute((_bstr_t)sql,NULL,adCmdText);尤其要注意每次打开表之后,在操作完之后一定要用m_pr->Close();关闭打开的表,否则会发生错误[10][11]。98\n表4-5CWirelessMenuDlg类控件资源控件名称ID标题变量名类型消息消息处理函数备注主框架IDD_WIRELESSMENU_DIALOGWirelssMenuWM_INITDIALOGOnInitDialog静态文本默认今日账单静态文本默认账单详情静态文本默认今日客户数:静态文本默认今日流水账:静态文本IDC_COUNTm_countCString静态文本IDC_TOTALMONEYm_totalmoneyCString按钮IDC_PAYBILL结账BN_CLICKEDOnPaybill按钮IDC_HISBILL历史账单BN_CLICKEDOnHisbill按钮IDC_SET设置BN_CLICKEDOnSet按钮IDC_UPDATA更新BN_CLICKEDOnUpdata按钮IDCANCEL退出系统BN_CLICKED编辑框IDC_STATUSm_statusCString列表控件IDC_LIST1m_list1CListCtrlNM_CLICKOnClickList1勾选“Report”列表控件IDC_LIST2m_list2CListCtrl勾选“Report”串口控件IDC_MSCOMM1m_ctrlCommCMSCommOnCommOnOnCommMscomm1在主窗口中添加了MSComm控件,对该控件的初始化我放在了“设置”98\n窗口中,使得主窗口的界面更加清晰。对接收来的数据处理放置在函数OnOnCommMscomm1()里面,采用事件驱动方式,即当通信事件发生时,MSComm控件会触发OnComm事件,调用者可以捕获该事件,通过检查其CommEvent属性便可确认发生的是哪种事件或错误,从而进行相应的处理。这种方法的优点是响应及时、可靠性高[12]。数据处理的流程图如图4-8。图4-8上位机数据处理流程图当m_ctrlComm.GetCommEvent()==2时候,数据到来,触发OnComm事件,调用函数OnOnCommMscomm1()。在图4-8中,因为接收端发送来的数据格式是形如“2a3a5a8a”这样的字符串,其中第一个数字是桌号,其余的才是所点的菜单,所以首先应将这两部分分离并分别存储。然后查表获取当前时间日期,再遍历整个数据库找到最大序号,利用获取的当前日期查表找出最大点菜顺序。将所点的菜单字符串分离得到每个序号,查MENU表计算该单总金额。具体的函数实现见附录第89页voidCWirelessMenuDlg::OnOnCommMscomm1()函数。98\n2.子窗口“设置”,为其映射了类Setting,表4-6列出了其中所拥有的控件资源及为其映射的消息和对应的消息处理函数。该窗口完成的主要功能有:设置串口各参数,包括串口号、波特率、数据位、停止位、奇偶校验;打开设置的串口。之所以将串口控件放在主窗口,而没有放在“设置”窗口,是因为当“设置”窗口关闭后,该窗口中的资源将会被销毁,而无法继续使用串口资源,所以放在了主窗口。控件名称ID标题变量名类型消息消息处理函数主框架IDD_SETTING设置WM_INITDIALOGOnInitDialog列表框IDC_COMBO1m_comCComboBox列表框IDC_COMBO2m_baudCComboBox列表框IDC_COMBO3m_corectCComboBox列表框IDC_COMBO4m_dataCComboBox列表框IDC_COMBO5m_stopCComboBox静态文本默认串口静态文本默认波特率静态文本默认校验位静态文本默认数据位静态文本默认停止位按钮IDC_CONN连接BN_CLICKEDOnConn表4-6Setting类控件资源MSComm控件有许多很重要的属性,限于篇幅只给出几个较为重要和常用的属性[13][14]。98\n表4-7MSComm控件的重要属性属 性说 明CommPort通信端口号Settings以字符串形式表示的波特率、奇偶校验、数据位PortOpen通信端口的状态,打开或是关闭Input接收数据Output发送数据InputMode接收数据的类型:0为文本;1为二进制打开串口时先获取用户设置的关于串口的各参数,然后利用表4-7中的方法对串口进行设置,具体的函数实现见附录voidSetting::OnConn()函数。3.子窗口“历史账单”,为其映射了类History,表4-8列出了其中所拥有的控件资源及为其映射的消息和对应的消息处理函数。在该窗口中主要完成的功能有:显示最近记录数150条;对非今日的账单的进行付账;查询某日期账单,并更新所选取日期的统计信息。98\n控件名称ID标题变量名类型消息消息处理函数备注主框架IDD_HISBILL历史账单WM_INITDIALOGOnInitDialog静态文本默认未付(人):静态文本默认欠账(元):静态文本默认总收入(元):静态文本IDC_NOPAYPERSONm_nopaypersonCString静态文本IDC_NOPAYMONEYm_nopaymoneyCString静态文本IDC_TOTALMONEYm_totalmoneyCString按钮IDC_FIXBILL补账BN_CLICKEDOnFixbill按钮IDC_REFRESHOLD刷新BN_CLICKEDOnRefreshold按钮IDCANCEL退出BN_CLICKED日期时间拾取器IDC_DATETIMEPICKER2DTN_CLOSEUPOnCloseupDatetimepicker2列表控件IDC_LIST1m_list1CListCtrl勾选“Report”表4-8History类控件资源由于这是在主窗口下打开的一个子窗口,所以为了要用主窗口打开的数据库里面的资源,要获得在在对话框程序CWirelessMenuDlg类中增加的两个数据成员:_ConnectionPtrm_pc;_RecordsetPtrm_pr;的指针地址[15],实现如下:_ConnectionPtr&m_pc=((CWirelessMenuDlg*)AfxGetMainWnd())->m_pc;98\n_RecordsetPtr&m_pr=((CWirelessMenuDlg*)AfxGetMainWnd())->m_pr;这样就可操作数据库里的BILL表和MENU表了。4.子窗口“更新”,为其映射了类Updata,表4-9列出了其中所拥有的控件资源及为其映射的消息和对应的消息处理函数。在该窗口中主要完成的功能有:对数据库MENU表里的记录进行添加、删除、修改。表4-9Updata类控件资源控件名称ID标题变量名类型消息消息处理函数备注主框架IDD_UPDATA更新WM_INITDIALOGOnInitDialog静态文本默认菜单静态文本默认菜品名称静态文本默认价格(元)编辑框IDC_VGENAMEm_vgenameCString编辑框IDC_PRICEm_priceCString列表控件IDC_LIST1m_list1CListCtrlNM_CLICKOnClickList1勾选“Report”按钮IDC_ADD添加BN_CLICKEDOnAdd按钮IDC_MODIFY修改BN_CLICKEDOnModify按钮IDC_DELETE删除BN_CLICKEDOnDelete按钮IDCANCEL返回BN_CLICKED在本窗口里同样也用到了数据库,使用的方法同在“历史账单”窗口中的使用方法一样。98\n第五章系统调试分析在完成所有的软硬件设计并实现后,最后进行系统联调,看各个部分能否按预定义的想法进行协调工作,这是系统设计的最后一步,也是最重要的一步。5.1移动端调试在打开电源后系统能正常初始化,TFT屏先清全屏,然后读内存卡显示图片,之后也能正常显示实时温度。在按下触摸屏之后,显示提示界面,但按下“取消”之后,本来应该将屏幕下半部还原,然后显示温度,但出现了如图5-1的情况。图5-1问题界面本来不应该在顶部写数据的,但在写入温度数据之前,在顶部显示了一系列乱码。经过查找代码分析应该是本来在底部写温度数据时,突然触发进入中断,在底部重新写入了数据,改变了TFT彩屏地址指针的地址,等到退出中断,重新显示温度时,已经在显示温度函数的内部了,地址指针变了,不能接着进入中断之前的位置进行写入新的数据,此时就会发生上述不可预料的错误,等到再次在主程序里面执行到显示温度的函数时,才重新指定显示的地址,这时候才正常显示温度了。找出问题解决的方法是尽量减少在主程序里显示字符的个数,这样出错的几率就会少一点,在改变了程序的结构之后,再测试上述问题没有再出现。其它功能均能按正常计划进行。98\n5.2移动端和接收端联调这样的测试主要是为了看无线收发数据是否正常,是否能正常编、解码,此时未引入上位机软件,利用STC-ISP4.83软件的串口调试功能进行调试,其运行界面如图5-2,串口波特率9600,8位数据位,无校验、1位停止位。图5-2串口调试助手在接收端数据未进行处理,直接将数据发送给串口调试助手,数据收发正常。但如果接收端每接收一个数据就将接收的数据进行处理,再接收下一个数据,就会产生如果发送的数据为“1,2,3,4,5,6,7,‘c’,‘s’”,而传给串口调试助手的数据却为“1aa3a4a5a6a7a”,这样的情况。经分析这样的情况有可能是,发送端不断的发送数据,而接收端正在处理某个数据时,就不能及时的读取接收到的数据,产生丢失数据的情况。所以接收端将数据处理的部分放在接收到字符c之后,之前将每次收到的字符存储在预定的数组中,之后再统一处理,这样问题就能很好的解决。程序配置NRF24L01的工作模式为增强型ShockBurst98\nTM,这样每次的数据收发问答就由NRF24L01模块自动进行,而无需人为干预。5.3系统联调系统联调的最终目的就是要实现论文开篇虚拟的那样场景的功能。移动端点菜完毕,发送数据,接收端对数据接收后进行处理通过串口发送给上位机,上位机再将接收到的数据进行处理存入数据库再提示新账单的到来,刷新显示,这样完成一次点单过程。系统正常运行时的界面如图5-3。图5-3上位机运行界面接收端发送数据给上位机会引起串口的中断,从而会进入函数voidCWirelessMenuDlg::OnOnCommMscomm1()内进行数据处理,由于一些未知名的原因,有时将数据发送给上位机,而从缓冲区不能完整的读出发送的数据,有时又会将发送的数据分成好几个片段读出,不能一次性读出,例如发送“1a2a3a5a8a10a”,有时会连续两次进入OnOnCommMscomm1()函数内部,利用MessageBox弹窗功能观察到会连续弹出“1a2a3a”、“5a8a10a”,在将程序改变之后,只能减少这种情况的发生,却不能完全抑制,一旦发生这样的错误,程序不得不中止或产生错误的账单信息,这也是我后来尤其要注意修改的地方。98\n参考文献[1]邹涛.网络与无线通信技术[M].北京:人民邮电出版社,2004.[2]涂时亮.单片机软件设计技术[M].重庆:科学文献出版社,1990.[3]万福君,潘松峰.单片微机原理系统设计与应用[M].合肥:中国科学技术大学出版社,2001.[4]张崇.单片2.4GHz无线收发一体芯片nRF2401及其应用[J].国外电子元器件,2004,Vol.27,No.5:34-36.[5]郭天祥.51单片机C语言教程入门提高、开发、拓展全攻略[M].北京:电子工业出版社,2009.[6]屈磊,李媛媛.VisualC++开发入门与编程实践[M].北京:电子工业出版社,2007.[7]彭伟.单片机C语言程序设计实训100例:基于8051+Proteus仿真[M].北京:电子工业出版社,2009.[8]周荷琴,吴秀清.微型计算机原理与接口技术(第四版)[M].合肥:中国科学技术大学出版社,2008.[9]李俊,于良耀.VisualC++6.0实用教程[M].北京:人民邮电出版社,2002.[10]杨佃福.VC中几种数据库访问技术的比较与选择[J].计算机应用与研究,2002,Vol.28,No.4:24-26.[11]潘爱民.一致的数据访问技术ADO/OLEDB[J].微电脑世界,1999,Vol.24,No.4:21-22.[12]孙鑫.VC++深入详解[M].北京:电子工业出版社,2006.[13]任哲.MFCWindows应用程序设计[M].北京:清华大学出版社,2004.6.[14]求是科技.WindowsAPI程序设计参考大全[M].北京:人民邮电出版社,2006.[15]KangWoochul,Son,SangHyuk,Stankovic,JohnA.Design,Implementation,andEvaluationofaQoS-AwareReal-TimeEmbedded98\nDatabase[J].IEEETransactionsonComputers,2012,Vol.25,No.3:20-22.98\n附录附录一系统电路图98\n附录二系统实物图一.移动端点菜界面二.接收端98\n一.上位机接收到数据98\n附录三源程序清单//移动端主程序#include#include"gui.h"#include"ILI9325.h"#include"SD.h"#include"DS18B20.h"#include"Touch.h"#include"Audio.h"#include"NRF24L01.h"#include"menu.h"#defineMAX_NUM30dataunsignedintcount=0;datacharPAGE=1;xdataunsignedcharTxBuf[]={33,35,26,38,18,54,23,10,2,11,4,5,6,7,8,9,10,25,12,13,47,'c','s'};dataunsignedcharsend_flag=1;datainttemp=0;voidInitInt0(void);voidmain(){VoicePlay(0);Init_NRF24L01();Init_DS18B20();ILI9325_Initial();//TFT初始化GUI_clearscreen(RGB(255,255,0));//清屏if(SdInit())//SD卡初始化DisInitMenu();GUI_sprintf_string(0,304,"Temperature:",BLUE,MAIN_BACKGROUND,1);98\nInitInt0();while(1){temp=ReadTemp_DS18B20();GUI_wrlval(136,304,temp,4,2,BLUE,MAIN_BACKGROUND,1);}}voidInt0Interrupt(void)interrupt0{unsignedcharmenu_data[MAX_NUM+3];unsignedcharn=0;unsignedcharflag=1;charj=0;unsignedchari=0;unsignedchardesk_num=1;EX0=0;VoicePlay(5);DisEnsureDiancai(1);while(1){n=EnsurePos(1,&flag);if(n==DIANCAI_OK){VoicePlay(1);DisEnsureDiancai(0);DisInputDeskNum(1);flag=0;gotoinput_desknum;98\n}if(n==DIANCAI_NO){VoicePlay(5);DisEnsureDiancai(0);//GUI_wrlval(136,304,temp,4,2,BLUE,MAIN_BACKGROUND,1);flag=0;gotoend;}}input_desknum:while(1){n=EnsurePos(2,&flag);if((n==INPUT_OK)&&flag){VoicePlay(5);DisInputDeskNum(0);DisVice();DisMenu(PAGE);menu_data[0]=desk_num;j++;flag=0;gotodiancai;}if((n==INPUT_NO)&&flag){VoicePlay(5);DisInputDeskNum(0);flag=0;gotoend;98\n}if((n==INPUT_ADD)&&flag){VoicePlay(5);desk_num++;if(desk_num==51)desk_num=50;GUI_wrul(65,292,desk_num,3,RED,WHITE,1);flag=0;}if((n==INPUT_PLUS)&&flag){VoicePlay(5);desk_num--;if(desk_num==0)desk_num=1;GUI_wrul(65,292,desk_num,3,RED,WHITE,1);flag=0;}}diancai:while(1){n=EnsurePos(3,&flag);if((n==LAST_PAGE)&&flag){VoicePlay(5);PAGE--;if(PAGE==0)PAGE=1;DisMenu(PAGE);flag=0;}98\nif((n==NEXT_PAGE)&&flag){VoicePlay(5);PAGE++;if(PAGE==5)PAGE=4;DisMenu(PAGE);flag=0;}if((n==CANCEL)&&flag){VoicePlay(5);GUI_clearscreen(RGB(255,255,0));//清屏DisInitMenu();gotoend;}if((n>=0x01)&&(n<=0x07)&&flag){VoicePlay(5);menu_data[j]=(PAGE-1)*7+n;GUI_sprintf_chartodec(0,0,menu_data[j],RED,WHITE,1);j++;if(j==MAX_NUM)j=MAX_NUM-1;flag=0;}if((n==OK)&&flag){if(j==1)//未选择则提示“Youhaven'tchosenanything!”{VoicePlay(2);GUI_box(0,276,239,319,PURPLE);98\nGUI_sprintf_string(10,289,"Youhaven'tchosenanything!",WHITE,PURPLE,1);ILI9325_delayms(2000);DisVice();flag=0;}if(j>=2){//发送数据VoicePlay(3);GUI_sprintf_string(0,260,"SENDING.....",WHITE,PURPLE,1);menu_data[j++]='c';menu_data[j]='s';NRF24L01_SingleSend(menu_data);//TransmitTxbufferdataVoicePlay(4);GUI_sprintf_string(0,260,"SENDING.....OK!",WHITE,PURPLE,1);GUI_clearscreen(RGB(255,255,0));//清屏DisInitMenu();flag=0;gotoend;}}}end:GUI_sprintf_string(0,304,"Temperature:",BLUE,MAIN_BACKGROUND,1);PAGE=1;EX0=1;}voidInitInt0(void)98\n{EX0=1;IT0=0;EA=1;}//接收端主程序#include#include#include#include"NRF24L01.h"//串口初始化voidStartUART(void){//波特率9600TMOD=0x20;SCON=0x50;TH1=0xFD;TL1=TH1;PCON=0x00;TR1=1;}//通过串口将接收到数据发送给PC端voidR_S_Byte(ucharR_Byte){SBUF=R_Byte;while(TI==0);//查询法TI=0;}voidmain(void){unsignedchari=0;charj=0;unsignedcharRxBuf[RX_PLOAD_WIDTH];unsignedcharmenu_data[100];unsignedcharsend_data[100];unsignedchartest[]="RECEIVETEST!";init_NRF24L01();StartUART();98\nwhile(test[i]!='\0')R_S_Byte(test[i++]);i=0;j=0;while(1){SetRX_Mode();if(nRF24L01_RxPacket(RxBuf)){menu_data[j]=RxBuf[0];//R_S_Byte(RxBuf[0]);if(menu_data[j]=='c'){menu_data[j]='\0';i=0;j=0;while(menu_data[j]!='\0'){//R_S_Byte(menu_data[j]);if(menu_data[j]<=9){send_data[i++]=menu_data[j++]|0x30;send_data[i++]='a';}else{send_data[i++]=(menu_data[j]/10)|0x30;send_data[i++]=(menu_data[j++]%10)|0x30;send_data[i++]='a';}}send_data[i]='\0';i=0;while(send_data[i]!='\0')R_S_Byte(send_data[i++]);i=0;j=-1;}j++;}}}98\n//GUI.h#ifndefGUI_H#defineGUI_H#include"LCM/ILI9325.h"/*--------------------定义常用颜色的颜色码---------------------------*/#defineRED0xF800//红#defineYELLOW0xFFE0//黄#defineGREEN0x07E0//绿#defineCYAN0x07FF//青#defineBLUE0x001F//蓝#definePURPLE0xF81F//紫#defineBLACK0x0000//黑#defineWHITE0xFFFF//白#defineGRAY0x7BEF//灰#definePOINT1#defineNO_POINT0//外部声明变量//externunsignedcharcodepic[];//图片pic数组,外部声明//externunsignedcharcodecorrect[];//校正X图片externunsignedcharcodeinput_char[];externunsignedcharcodediancai_char[];//预定义汉字,外部声明externunsignedcharcodelast_char[];externunsignedcharcodenext_char[];externunsignedcharcodecancel_char[];externunsignedcharcodeok_char[];externunsignedcharcodechoose_char[];/*--------------------------------GUI函数----------------------------*///---------------------------------------------------------------------//函数名:voidGUI_clearscreen()//功能:清屏(水平扫描)//参数:b_color----是背景颜色//返回:无//说明:使用背景颜色清除TFT模块屏幕的全部显示内容//---------------------------------------------------------------------voidGUI_clearscreen(unsignedintb_color);//---------------------------------------------------------------------//函数名:voidGUI_Point()//功能:画点//参数:(x,y)----是点的坐标//color----是点的颜色98\n//返回:无//说明:用指定的颜色在指定的坐标位置上画出一个点//---------------------------------------------------------------------voidGUI_Point(unsignedintx,unsignedinty,unsignedintcolor);//---------------------------------------------------------------------//函数名:voidGUI_sprintf_chartobit()//功能:无符号字符型数以二进制形式显示//参数:(x,y)----是显示内容的左上角坐标//dat(0~255)----为要写的数字,//color----是显示字符的颜色//b_color----是背景颜色//point----=0不指定背景=1指定背景//返回:无//说明:无符号字符型数用指定的颜色和背景颜色在指定的坐标位置上//以二进制形式显示出来//---------------------------------------------------------------------voidGUI_sprintf_chartobit(unsignedintx,unsignedinty,unsignedchardat,unsignedintcolor,unsignedintb_color,unsignedcharpoint);//---------------------------------------------------------------------//函数名:GUI_sprintf_chartohex()//功能:无符号字符型数以十六进制形式显示//参数:(x,y)----是显示内容的左上角坐标//dat(0~255)----是欲显示的无符号字符型数,//color----是显示字符的颜色,//b_color----是背景颜色//point----=0不指定背景=1指定背景//返回:无//说明:无符号字符型数用指定的颜色和背景颜色在指定的坐标位置上//以十六进制形式显示出来//---------------------------------------------------------------------voidGUI_sprintf_chartohex(unsignedintx,unsignedinty,unsignedchardat,unsignedintcolor,unsignedintb_color,unsignedcharpoint);//---------------------------------------------------------------------//函数名:GUI_sprintf_chartodec()//功能:无符号字符型数以十进制形式显示//参数:(x,y)----是显示内容的左上角坐标//dat(0~999)----是欲显示的无符号字符型数,//color----是显示字符的颜色//b_color----是背景颜色//point----=0不指定背景=1指定背景//返回:无98\n//说明:无符号字符型数用指定的颜色和背景颜色在指定的坐标位置上//以十进制形式显示出来//---------------------------------------------------------------------voidGUI_sprintf_chartodec(unsignedintx,unsignedinty,unsignedintdat,unsignedintcolor,unsignedintb_color,unsignedcharpoint);//---------------------------------------------------------------------//函数名:GUI_sprintf_nu()//功能:显示1位16进制数//参数:(x,y)----是显示内容的左上角坐标//nu(0~15)----欲显示的1位16进制数,//color----颜色//b_color----背景颜色//point----=0不指定背景=1指定背景//返回:无//说明:用指定位置上显示1位16进制数//---------------------------------------------------------------------voidGUI_sprintf_nu(unsignedintx,unsignedinty,unsignedcharnu,unsignedintcolor,unsignedintb_color,unsignedcharpoint);//---------------------------------------------------------------------//函数名:GUI_wrul()//功能:以十进制形式显示无符号长整型数//参数:(x,y)----是显示内容的右上角坐标;//num(0~4,294,967,295(十进制))----是欲显示的无符号长整型数//n(0~10)----指定显示的位数//color----颜色//b_color----背景颜色//返回:无//说明:将一个无符号长整型数以十进制形式显示在TFT屏上//注意:说利用该函数,我们最多将一个10位十进制数显示在LCD屏上//在使用该函数时,不论最终显示的数有几位,它都将在LCD屏幕上//占n个字符的空间(显示的多位十进制数不足n位时,//本函数会在有效显示数字前面补空格)。//---------------------------------------------------------------------voidGUI_wrul(unsignedintx,unsignedinty,unsignedlongnum,unsignedcharn,unsignedintcolor,unsignedintb_color,unsignedcharpoint);//---------------------------------------------------------------------//函数名:GUI_wrlval()//功能:以十进制形式显示长变量//参数:x,y----是显示内容的右上角坐标//num----欲显示的长变量//bits----允许显示的位数98\n//dp----小数点位置//color----颜色//b_color----背景颜色//point----0:不指定背景1:指定背景//返回:无//说明:将一个长变量的数值以十进制形式显示在TFT屏上,可以根据需要确定//是否显示小数点以及小数点位置,也可以确定在TFT屏幕上显示的位数//注意:1、要显示的数据比规定的显示位数多,规定的位全部写"-",表示数据溢、//出,如“---.--”//2、本函数实际上相当于GUI_wrul函数的增强版,如果指定bits=10dp=0,//如下所示://GUI_wrlval(20,239,tp,10,0,WHITE,BLACK);//那么,其作用和GUI_wrul函数完全一样。//---------------------------------------------------------------------voidGUI_wrlval(unsignedintx,unsignedinty,unsignedlongnum,unsignedcharbits,unsignedchardp,unsignedintcolor,unsignedintb_color,unsignedcharpoint);//---------------------------------------------------------------------//函数名:GUI_sprintf_char()//功能:显示英文或数字字符//参数:(x,y)----是显示内容的左上角坐标//c----显示的英文字符,//color-----字符颜色//b_color----背景颜色//point----0:不指定背景1:指定背景//返回:无//说明:用指定位置上显示单个英文字符或数字字符//如“GUI_sprintf_char(3,300,'a',RED,YELLOW,0);”//---------------------------------------------------------------------voidGUI_sprintf_char(unsignedintx,unsignedinty,unsignedcharc,unsignedintcolor,unsignedintb_color,unsignedcharpoint);//---------------------------------------------------------------------//函数名:GUI_sprintf_string()//功能:显示英文字符串//参数:(x,y)----是显示内容的左上角坐标//*s----英文字符串指针,//color----颜色//b_color----背景颜色//point----0:不指定背景1:指定背景//返回:无//说明:在指定位置上显示英文字符串//如“GUI_sprintf_string(6,179,"IAMAGOODPERSON!!",BLUE,BLACK,0);"98\n//---------------------------------------------------------------------voidGUI_sprintf_string(unsignedintx,unsignedinty,charcode*s,unsignedintcolor,unsignedintb_color,unsignedcharpoint);//---------------------------------------------------------------------//函数名:GUI_sprintf_HZ()//功能:显示预定义汉字字符串//参数:(x,y)----是显示内容的左上角坐标//color----颜色//b_color----背景颜色//point----0:不指定背景1:指定背景//*china_char----存放预定义汉字指针数组//count----要显示的汉字个数//返回:无//说明:在指定位置上显示预定义的中文字符串。//要显示的中文字符串事先定义在chinese.h中的china_char[]数组中,//如果想修改中文字符串长度、内容,修改chinese.h中的china_char[]数组,//建议用字模提取软件zimoV2.2将你要显示的中文字符串自动转换为数组//数据//---------------------------------------------------------------------voidGUI_sprintf_HZ(unsignedintx,unsignedinty,unsignedintcolor,unsignedintb_color,unsignedcharpoint,unsignedcharcode*china_char,unsignedcharcount);//---------------------------------------------------------------------//函数名:GUI_HLine()//功能:画横线//参数:(x,y)----是横线起点坐标//length----是横线长度//color----是横线颜色//返回:无//说明:用指定的颜色在指定位置上画出指定长度的一条横线//---------------------------------------------------------------------voidGUI_HLine(unsignedintx,unsignedinty,unsignedintlength,unsignedintcolor);//---------------------------------------------------------------------//函数名:GUI_RLine()//功能:画竖线//参数:(x,y)----是竖线起点坐标//high----竖线高度//color----是竖线颜色//返回:无//说明:用指定的颜色在指定位置上画出指定长度的一条竖线//---------------------------------------------------------------------98\nvoidGUI_RLine(unsignedintx,unsignedinty,unsignedinthigh,unsignedintcolor);//---------------------------------------------------------------------//函数名:GUI_line()//功能:画直线(可以画任意方向直线,包括横线、竖线、斜线)//参数:(x1,y1)----起点//(x2,y2)----终点//color----颜色//返回:无//说明:用指定的颜色在指定的两点间画出一条直线//---------------------------------------------------------------------voidGUI_line(unsignedintx1,unsignedinty1,unsignedintx2,unsignedinty2,unsignedintcolor);//---------------------------------------------------------------------//函数名:GUI_box()//功能:画实心矩形//参数:(sx,sy)----左上角顶点坐标//(ex,ey)----右下角顶点坐标//color----颜色//返回:无//说明:在指定位置上画出实心矩形//---------------------------------------------------------------------voidGUI_box(unsignedintsx,unsignedintsy,unsignedintex,unsignedintey,unsignedintcolor);//---------------------------------------------------------------------//函数名:GUI_rectangle()//功能:画空心矩形//参数:(sx,sy)----左上角顶点坐标//(ex,ey)----右下角顶点坐标//color----颜色//返回:无//说明:在指定位置上画出空心矩形//---------------------------------------------------------------------voidGUI_rectangle(unsignedintsx,unsignedintsy,unsignedintex,unsignedintey,unsignedintcolor);//---------------------------------------------------------------------//函数名:GUI_dythreeangle()//功能:画空心等腰三角形//参数:(ux,uy)----上顶点坐标//(lx,ly)----左顶点坐标//(rx,ry)----右顶点坐标98\n//color----颜色//返回:无//说明:在指定位置上画出实心等腰三角形//---------------------------------------------------------------------voidGUI_dythreeangle(intux,intuy,intlx,intly,intrx,intry,unsignedintcolor);//---------------------------------------------------------------------//函数名:GUI_DisPicture()//功能:显示图片(图标)//参数:(x,y)----是开始点的坐标//length----是图片长度//high----是图片高度。//pic----图片数组的指针//返回:无//说明:用指定位置上显示事先定义的图片。//要显示的图片事先定义在bmp.h中的pic[]数组中,//如果想修改图片大小、内容,请修改bmp.h中的pic[]数组,//用Image2Lcd软件将你要显示的图象自动转换为数组数据//---------------------------------------------------------------------//voidGUI_DisPicture(unsignedcharx,unsignedinty,unsignedcharlength,unsignedinthigh)voidGUI_DisPicture(unsignedintx,unsignedinty,unsignedintlength,unsignedinthigh,constunsignedchar*pic);//---------------------------------------------------------------------//函数名:RGB()//功能:RGB颜色混合//参数:R(0-255)----红色分量//G(0-255)----绿色分量//B(0-255)----蓝色分量//返回:按R5-G6-B5格式混合后的16位颜色码//说明:将电脑上常见的R8-G8-B8格式转换成8位单片机常用的R5-G6-B5格式//---------------------------------------------------------------------unsignedintRGB(unsignedcharR,unsignedcharG,unsignedcharB);//---------------------------------------------------------------------//函数名:Calc_SD_Add()//功能:计算图片地址//参数:n(1~255)----第n张图片//返回:第n张图片的起始物理地址//说明:第一张地址:1540608;第二张地址:1704448;第三张地址:1868288;//每张图片地址差163840//---------------------------------------------------------------------unsignedlongCalc_SD_Add(unsignedcharn);98\n//---------------------------------------------------------------------//函数名:DisPic_SD_CompLED()//功能:全屏显示第n张图片//参数:n(1~255)----第n张图片//返回:无//说明:直接显示无效果//---------------------------------------------------------------------voidDisPic_SD_CompLED(unsignedcharn);//---------------------------------------------------------------------//函数名:DisPic_SD_AnyLED()//功能:指定长宽显示第n张图片//参数:(x,y)----图片左上角坐标//length----图片长度//high-----图片高度//n(1~255)----第n张图片//point----0:显示图片中tmp指定的颜色1:不显示图片中tmp指定的颜色//b_color----主背景色//tmp----图片中的指定颜色//返回:无//说明:直接显示无效果//---------------------------------------------------------------------voidDisPic_SD_AnyLED(unsignedintx,unsignedinty,unsignedintlength,unsignedinthigh,unsignedcharn,unsignedcharpoint,unsignedintb_color,unsignedinttmpicture);#endif//ILI9325.h#ifndefILI9325_H#defineILI9325_H#include"reg52.h"//============================================================//根据芯片资料定义#defineWINDOW_XADDR_START0x0050//HorizontalStartAddressSet#defineWINDOW_XADDR_END0x0051//HorizontalEndAddressSet#defineWINDOW_YADDR_START0x0052//VerticalStartAddressSet#defineWINDOW_YADDR_END0x0053//VerticalEndAddressSet#defineGRAM_XADDR0x0020//GRAMHorizontalAddressSet#defineGRAM_YADDR0x0021//GRAMVerticalAddressSet#defineGRAMWR0x0022//memorywrite#defineGUI_LCM_YMAX319#defineGUI_LCM_XMAX21998\n//定义TFT彩屏模块接口sbitTFT_RS=P2^4;//数据/命令选择sbitTFT_WR=P2^5;//写sbitTFT_CS=P2^6;//片选sbitTFT_RST=P2^7;//复位#defineTFT_DATA_HP1//数据口高位#defineTFT_DATA_LP0//数据口低位//---------------------------------------------------------------------//函数名:ILI9325_delayms()//功能:实现与参数直接对应的时间(单位为毫秒)的延时//参数:count(0~255)----定时范围//返回:无//说明:一个毫秒级可变延时函数://---------------------------------------------------------------------voidILI9325_delayms(unsignedintcount);//---------------------------------------------------------------------//函数名:ILI9325_delayus()//功能:实现与参数直接对应的时间(单位为微秒)的延时//参数:count----定时范围*1us//返回:无//说明:一个微秒级可变延时函数://---------------------------------------------------------------------voidILI9325_delayus(unsignedintcount);//---------------------------------------------------------------------//函数名:ILI9325_Initial(void)//功能:该函数用于初始化TFT彩屏模块//参数:无//返回:无//说明:TFT初始化//---------------------------------------------------------------------voidILI9325_Initial(void);//---------------------------------------------------------------------//函数名:write_cmd()//功能:写命令,写一个命令到TFT控制器//参数:CH----命令字的高8位//CL----命令字的低8位,一起指定要执行的命令//返回:无//说明:无98\n//---------------------------------------------------------------------voidwrite_cmd(unsignedcharCH,unsignedcharCL);//---------------------------------------------------------------------//函数名:write_data()//功能:写数据,写一个数据到TFT控制器//参数:DH----数据字的高8位//DL----数据字的低8位,一起指定要写入的数据//返回:无//说明:无//---------------------------------------------------------------------voidwrite_data(unsignedcharDH,unsignedcharDL);//---------------------------------------------------------------------//函数名:write_data_u16()//功能:写16位数据,写一个数据到TFT控制器//参数:dat----为要写入的16位数据//返回:无//说明:无//---------------------------------------------------------------------voidwrite_data_u16(unsignedintdat);//---------------------------------------------------------------------//函数名:write_cmd_data()//功能:写命令数据,写命令数据到TFT控制器//参数:cmd----为要写入的命令字节//dat----为要写入的16位数据//返回:无//说明:无//---------------------------------------------------------------------voidwrite_cmd_data(unsignedcharcmd,unsignedintdat);//---------------------------------------------------------------------//函数名:LCD_setwindow()//功能:设置窗口//参数:(x0,y0)----为显示窗口左上角坐标//(x1,y1)----为显示窗口右下角坐标//返回:无//说明:该函数设置TFT模块的显示窗口//---------------------------------------------------------------------voidLCD_setwindow(unsignedcharx0,unsignedinty0,unsignedcharx1,unsignedinty1);//---------------------------------------------------------------------98\n//函数名:LCD_setxy(unsignedcharx,unsignedinty)//功能:设定当前显示坐标//参数:(x,y)----为当前显示坐标//返回:无//说明:本函数实际上是LCD_setwindow函数的一个特例,显示窗口为最小,仅是//一个像素点//---------------------------------------------------------------------voidLCD_setxy(unsignedcharx,unsignedinty);#endif//SD.h#ifndefSD_H#defineSD_H#include//定义SD卡需要的4根信号线/*sbitSD_CLK=P3^5;sbitSD_DI=P3^6;sbitSD_DO=P3^4;sbitSD_CS=P3^7;*/sbitSD_CLK=P2^5;sbitSD_DI=P2^4;sbitSD_DO=P3^6;sbitSD_CS=P3^7;//---------------------------------------------------------------------//函数名:SdWrite()//功能:写一字节到SD卡,模拟SPI总线方式//参数:n----要写入的数据//返回:无//---------------------------------------------------------------------voidSdWrite(unsignedcharn);//---------------------------------------------------------------------//函数名:SdRead()//功能:从SD卡读一字节,模拟SPI总线方式//参数:无//返回:读回的数据//---------------------------------------------------------------------unsignedcharSdRead();//---------------------------------------------------------------------//函数名:SdResponse()//功能:检测SD卡的响应98\n//参数:无//返回:返回读取的状态//---------------------------------------------------------------------unsignedcharSdResponse();//---------------------------------------------------------------------//函数名:SdCommand()//功能:发命令到SD卡//参数:command----命令//argument----指令//CRC----校验码//返回:无//---------------------------------------------------------------------voidSdCommand(unsignedcharcommand,unsignedlongargument,unsignedcharCRC);//---------------------------------------------------------------------//函数名:SdInit()//功能:初始化SD卡//参数:无//返回:1:初始化成功0:初始化失败//---------------------------------------------------------------------unsignedcharSdInit(void);//---------------------------------------------------------------------//函数名:SdWriteBlock()//功能:往SD卡指定地址写数据,一次最多512字节//参数:Block----数据存放的数组//address----存数据的地址//len----所存数据的长度//返回:1:存储成功0:存储失败//---------------------------------------------------------------------unsignedcharSdWriteBlock(unsignedchar*Block,unsignedlongaddress,intlen);//---------------------------------------------------------------------//函数名:SdReadBlock()//功能:从SD卡指定地址读取数据,一次最多512字节//参数:Block----数据存放的数组//address----读数据的地址//len----所读数据的长度//返回:1:读取成功0:读取失败//---------------------------------------------------------------------unsignedcharSdReadBlock(unsignedchar*Block,unsignedlongaddress,intlen);98\n#endif//DS18B20.h#ifndef_DS18B20_H_#define_DS18B20_H_#includesbitDS18B20_DATA=P2^5;//ROM操作#defineJUMP_ROM0xCC#defineSTART0x44#defineREAD_EEROM0xBE//---------------------------------------------------------------------//函数名:Delay_DS18B20//功能:DS18B20延时函数//参数:num--------延时数值//返回:无//---------------------------------------------------------------------voidDelay_DS18B20(unsignedintnum);//---------------------------------------------------------------------//函数名:Init_DS18B20//功能:DS18B20复位,并返回状态//参数:无//返回:x=0则初始化成功x=1则初始化失败//---------------------------------------------------------------------unsignedcharInit_DS18B20(void);//---------------------------------------------------------------------//函数名:ReadByte_DS18B20//功能:读一个字节//参数:无//返回:从DS18B20读出的一个位值//---------------------------------------------------------------------unsignedcharReadByte_DS18B20(void);//---------------------------------------------------------------------//函数名:WriteByte_DS18B20//功能:写一个字节//参数:dat--------要写入的数据//返回:无//---------------------------------------------------------------------voidWriteByte_DS18B20(unsignedchardat);98\n//---------------------------------------------------------------------//函数名:ReadTemp_DS18B20//功能:读取温度//参数:无//返回:读取的温度值//---------------------------------------------------------------------intReadTemp_DS18B20(void);#endif//Touch.h#ifndefTOUCH_H#defineTOUCH_H#include//touchpanelinterfacedefinesbitTP_DCLK=P2^4;//时钟sbitTP_CS=P3^3;//片选sbitTP_DIN=P3^1;//数据输入sbitTP_DOUT=P2^0;//数据输出sbitTP_IRQ=P3^2;//检测触摸屏响应信号//存储坐标structtp_point{unsignedintx;unsignedinty;};#defineCONTROL_DFR_Y0x90//控制字10010000即用差分方式读X坐标#defineCONTROL_DFR_X0xD0//控制字11010000即用差分方式读Y坐标#defineTIME10//循环读数次数,最大16次#defineLEFT_UP_X10//TFT左上角X坐标#defineLEFT_UP_Y16//TFT左上角Y坐标#defineRIGHT_DOWN_X229//TFT右下角X坐标#defineRIGHT_DOWN_Y303//TFT右下角Y坐标//---------------------------------------------------------------------//函数名:TP_Init()//功能:XPT2046各端口初始化//参数:无//返回:无//---------------------------------------------------------------------voidTP_init();98\n//---------------------------------------------------------------------//函数名:tp_isirq()//功能:检测触摸屏是否处于中断中,在中断中调用检测//参数:无//返回:0-没有被按下,1-触摸屏被按下//---------------------------------------------------------------------unsignedcharTP_isirq(void);//---------------------------------------------------------------------//函数名:WriteCharTo7843()//功能:SPI写数据//参数:cmd----要写的指令//返回:无//---------------------------------------------------------------------voidWriteCharTo7843(unsignedcharcmd);//---------------------------------------------------------------------//函数名:ReadFromCharFrom7843()//功能:SPI读数据//参数:无//返回:dat----为读出的12位值//---------------------------------------------------------------------unsignedintReadFromCharFrom7843();//---------------------------------------------------------------------//函数名:Get_Touch_XY()//功能:得到当前触摸坐标//参数:*pt----坐标结构体指针//返回:无//---------------------------------------------------------------------voidGet_touch_XY(structtp_point*pt);//---------------------------------------------------------------------//函数名:TP_get_substract()//功能:取得触摸屏坐标函数//参数:*px----取得的x坐标//*py----取得的y坐标//返回:structtp_point型坐标数据//说明:连续读两次值,若在误差允许范围内,取均值;不是很好用//---------------------------------------------------------------------structtp_pointTP_get_substract();//---------------------------------------------------------------------//函数名:TP_get_uptodown()98\n//功能:取得触摸屏坐标函数//参数:无//返回:structtp_point型坐标数据//说明:连续读N次值,升序排列,取中间三组均值//---------------------------------------------------------------------structtp_pointTP_get_uptodown();//---------------------------------------------------------------------//函数名:TP_coverage()//功能:取得TFT坐标函数//参数:pt----取得的原始x,y坐标,结构体数据//*px----转换的x坐标//*py----转换的y坐标//返回:无//说明:转换的准确度不够,原理待查//---------------------------------------------------------------------voidTP_coverage(structtp_point*pt,structtp_point*px);//---------------------------------------------------------------------//函数名:TP_correct()//功能:校正TFT坐标函数//参数:无//返回:无//---------------------------------------------------------------------voidTP_correct();//---------------------------------------------------------------------//函数名:TP_MiaoDian()//功能:描点//参数:px----描点坐标//返回:无//---------------------------------------------------------------------voidTP_MiaoDian(structtp_pointpx);#endif//AUDIO.h#ifndef_AUDIO_H__#define_AUDIO_H__#includesbitRST=P2^3;sbitCLK=P2^2;sbitDI=P2^1;98\n//语音模块命令码#defineVolum(v)(0XFFF0|v)//0~7级声音,FFF0最小,FFF7最大#defineAllCircle0XFFFB#defineStopCircle0XFFFC#defineOneCircle0XFFFD#definePlayPause0XFFFE#defineStop0XFFFF//---------------------------------------------------------------------//函数名:Voice_Delay500us//功能:延时500us//参数:无//返回:无//说明:晶振24M//---------------------------------------------------------------------voidVoiceDelay500us(void);//---------------------------------------------------------------------//函数名:Voice_DelayMs//功能:延时500us//参数:ms----延时参数*1ms//返回:无//说明:晶振24M//---------------------------------------------------------------------voidVoiceDelayMs(unsignedintms);//---------------------------------------------------------------------//函数名:Voice_DelayMs//功能:延时500us//参数:addr----播放指令//返回:无//说明:晶振24M//---------------------------------------------------------------------voidVoicePlay(unsignedintaddr);#endif//NRF24L01.h#ifndefNRF24L01_H#defineNRF24L01_H#include#include#include98\ntypedefunsignedcharuchar;typedefunsignedcharuint;//IO端口定义sbitMISO=P3^0;sbitMOSI=P2^3;sbitSCK=P2^0;sbitCE=P3^5;sbitCSN=P3^4;//sbitIRQ=P3^3;#defineTX_ADR_WIDTH5//5uintsTXaddresswidth#defineRX_ADR_WIDTH5//5uintsRXaddresswidth#defineTX_PLOAD_WIDTH1//32//5//20uintsTXpayload#defineRX_PLOAD_WIDTH1//32//5//20uintsTXpayload//NRF24L01寄存器指令#defineREAD_REG0x00//读寄存器指令#defineWRITE_REG0x20//写寄存器指令#defineRD_RX_PLOAD0x61//读取接收数据指令#defineWR_TX_PLOAD0xA0//写待发数据指令#defineFLUSH_TX0xE1//冲洗发送FIFO指令#defineFLUSH_RX0xE2//冲洗接收FIFO指令#defineREUSE_TX_PL0xE3//定义重复装载数据指令#defineNOP0xFF//保留//SPI(nRF24L01)寄存器地址#defineCONFIG0x00//配置收发状态,CRC校验模式以及收发状态响应方式#defineEN_AA0x01//自动应答功能设置#defineEN_RXADDR0x02//可用信道设置#defineSETUP_AW0x03//收发地址宽度设置#defineSETUP_RETR0x04//自动重发功能设置#defineRF_CH0x05//工作频率设置#defineRF_SETUP0x06//发射速率、功耗功能设置#defineSTATUS0x07//状态寄存器#defineOBSERVE_TX0x08//发送监测功能#defineCD0x09//地址检测#defineRX_ADDR_P00x0A//频道0接收数据地址#defineRX_ADDR_P10x0B//频道1接收数据地址#defineRX_ADDR_P20x0C//频道2接收数据地址#defineRX_ADDR_P30x0D//频道3接收数据地址#defineRX_ADDR_P40x0E//频道4接收数据地址98\n#defineRX_ADDR_P50x0F//频道5接收数据地址#defineTX_ADDR0x10//发送地址寄存器#defineRX_PW_P00x11//接收频道0接收数据长度#defineRX_PW_P10x12//接收频道0接收数据长度#defineRX_PW_P20x13//接收频道0接收数据长度#defineRX_PW_P30x14//接收频道0接收数据长度#defineRX_PW_P40x15//接收频道0接收数据长度#defineRX_PW_P50x16//接收频道0接收数据长度#defineFIFO_STATUS0x17//FIFO栈入栈出状态寄存器设置//---------------------------------------------------------------------//函数名:Delay()//功能:长延时//参数:s----延时的时间//返回:无//---------------------------------------------------------------------voidDelay(unsignedints);//---------------------------------------------------------------------//函数名:inerDelay_us()//功能:延时函数//参数:n----延时的时间//返回:无//说明:NRF24L01内部us级延时//---------------------------------------------------------------------voidinerDelay_us(unsignedcharn);//---------------------------------------------------------------------//函数名:Init_NRF24L01()//功能:NRF24L01初始化//参数:无//返回:无//---------------------------------------------------------------------voidInit_NRF24L01(void);//---------------------------------------------------------------------//函数名:SPI_RW()//功能:NRF24L01的SPI写时序//参数:uchar----要写的数据//返回:无//---------------------------------------------------------------------uintSPI_RW(uintuchar);//---------------------------------------------------------------------98\n//函数名:SPI_Read()//功能:NRF24L01的SPI时序//参数:reg----寄存器地址//返回:返回寄存器值//---------------------------------------------------------------------ucharSPI_Read(ucharreg);//---------------------------------------------------------------------//函数名:SPI_RW_Reg()//功能:NRF24L01读写寄存器函数//参数:reg----寄存器地址//value----写指定寄存器值//返回:返回芯片状态//---------------------------------------------------------------------uintSPI_RW_Reg(ucharreg,ucharvalue);//---------------------------------------------------------------------//函数名:SPI_Read_Buf()//功能:用于读数据,,//参数:reg----为寄存器地址//pBuf----为待读出数据地址//uchars----读出数据的个数//返回:返回芯片状态//---------------------------------------------------------------------uintSPI_Read_Buf(ucharreg,uchar*pBuf,ucharuchars);//---------------------------------------------------------------------//函数名:SPI_Write_Buf()//功能:用于写数据:为寄存器地址,pBuf:为待写入数据地址,uchars:写入数据的个数//参数:reg----为寄存器地址//pBuf----为待写入数据地址//uchars----写入数据的个数//返回:返回寄存器状态//---------------------------------------------------------------------uintSPI_Write_Buf(ucharreg,uchar*pBuf,ucharuchars);//---------------------------------------------------------------------//函数名:SetRX_Mode()//功能:数据接收配置//参数:无//返回:无//---------------------------------------------------------------------voidSetRX_Mode(void);98\n//---------------------------------------------------------------------//函数名:NRF24L01_RxPacket()//功能:数据读取后放如rx_buf接收缓冲区中//参数:rx_buf----接收数据指针//返回:revale为1则有数据接收//---------------------------------------------------------------------unsignedcharNRF24L01_RxPacket(unsignedchar*rx_buf);//---------------------------------------------------------------------//函数名:NRF24L01_TxPacket()//功能:发送tx_buf中数据//参数:TxBuf----发送数据指针//返回:无//说明:一次性发送TX_ADR_WIDTH指定宽度的数据//---------------------------------------------------------------------voidNRF24L01_TxPacket(unsignedchar*tx_buf);//---------------------------------------------------------------------//函数名:NRF24L01_SingleSend()//功能:单个循环发送tx_buf中数据//参数:TxBuf----发送数据数组//返回:无//说明:采用单个发送方式,以‘s’判断结尾//---------------------------------------------------------------------voidNRF24L01_SingleSend(unsignedcharTxBuf[]);#endif//MENU.h#ifndef_MENU_H_#define_MENU_H_#include"gui.h"#include"ILI9325.h"#include"SD.h"//#include"DS18B20.h"#include"Touch.h"#include"Audio.h"#defineMAIN_BACKGROUNDRGB(255,255,0)#defineLAST_PAGE_BCLRGB(153,153,255)#defineNEXT_PAGE_BCLRGB(255,153,255)#defineCANCEL_BCLRGB(51,0,255)#defineOK_BCLRGB(255,153,0)98\n#defineCANCEL_HZCLRGB(228,0,255)#defineOK_HZCLRGB(255,41,37)#defineCHOOSE_BCLRGB(155,228,52)#defineMENU_BACKRGB(201,238,158)#defineDCXT_SEQ1#defineVGE_SEQ2#defineCHOOSE_SEQ3#defineTEST_SEQ4#defineCAI_SEQ5#defineCHOOSE_POS_X170#defineCHOOSE_POS_Y42#defineCHOOSE_DISTANCE33#defineCHOOSE_WIDTH89#defineCHOOSE_HIGH24//菜单134*20V#defineMENU_POS_X0//16#defineMENU_POS_Y46//45#defineMENU_DISTANCE33#defineMENU_HIGH19#defineLAST_PAGE0xFC#defineNEXT_PAGE0xFD#defineCANCEL0xFE#defineOK0xFF#defineINPUT_OK0xF9#defineINPUT_NO0xF8#defineINPUT_ADD0xF7#defineINPUT_PLUS0xF6#defineDIANCAI_OK0xFA#defineDIANCAI_NO0xFBvoidDisInitMenu();voidDisInputDeskNum(unsignedcharj);voidDisEnsureDiancai(unsignedcharj);voidDisVice();voidDisMenuBack();voidDisMenu(unsignedcharn);unsignedcharChoosePos(structtp_pointpt,unsignedcharj);unsignedcharEnsurePos(unsignedcharj,unsignedchar*m);98\n#endif//上位机程序//*************************CWirelessMenuDlg*****************************voidCWirelessMenuDlg::Refresh(){//获取今日时间SYSTEMTIMEst;CStringstrDate,strTime;GetLocalTime(&st);strDate.Format("%4d-%02d-%02d",st.wYear,st.wMonth,st.wDay);//清空列表框数据m_list1.DeleteAllItems();CStringsql="select*fromBILL";m_pr->Open((_bstr_t)sql,m_pc.GetInterfacePtr(),adOpenDynamic,adLockOptimistic,adCmdText);unsignedinttotal_money=0,count=0;while(!m_pr->adoEOF){_variant_tt,t1;CStringbill_num,bill_seq,bill_time,bill_desk,bill_count,bill_money,bill_matter;t=m_pr->GetCollect("序号");bill_num.Format("%d",t.iVal);t=m_pr->GetCollect("点菜顺序");bill_seq.Format("%d",t.iVal);t=m_pr->GetCollect("点菜时间");bill_time=(char*)(_bstr_t)t;charcurdata[50];strncpy(curdata,(LPCTSTR)bill_time,sizeof(curdata));curdata[10]='\0';CStringjudge_data(curdata);t=m_pr->GetCollect("桌号");bill_desk.Format("%d",t.iVal);t=m_pr->GetCollect("点菜数");bill_count.Format("%d",t.iVal);98\nt1=m_pr->GetCollect("应付金额");bill_money.Format("%d",t1.iVal);t=m_pr->GetCollect("付账情况");bill_matter=(char*)(_bstr_t)t;if(strDate==judge_data){intindex=m_list1.GetItemCount();m_list1.InsertItem(index,bill_num);m_list1.SetItemText(index,1,bill_seq);m_list1.SetItemText(index,2,bill_time);m_list1.SetItemText(index,3,bill_desk);m_list1.SetItemText(index,4,bill_count);m_list1.SetItemText(index,5,bill_money);m_list1.SetItemText(index,6,bill_matter);count++;total_money+=t1.iVal;}m_pr->MoveNext();}m_pr->Close();m_count.Format("%d",count);m_totalmoney.Format("%d",total_money);UpdateData(0);}voidCWirelessMenuDlg::OnClickList1(NMHDR*pNMHDR,LRESULT*pResult){//TODO:Addyourcontrolnotificationhandlercodehereunsignedinti=0,Count=0;unsignedintnum=1,num_temp=0;_variant_tt;CStringbill_num,bill_money,bill_dish,menu_name,menu_price;intindex;m_list2.DeleteAllItems();//清除原数据index=m_list1.GetNextItem(-1,LVNI_SELECTED);//取鼠标点击list1序号if(index!=-1)//选取到序号,未选取到则不处理{bill_num=m_list1.GetItemText(index,0);//----------------------------------------------连表读取开始--------------------------------------//连表98\nCStringsql;sql.Format("select*fromBILLwhere序号=%s",bill_num);m_pr->Open((_bstr_t)sql,m_pc.GetInterfacePtr(),adOpenDynamic,adLockOptimistic,adCmdText);t=m_pr->GetCollect("点菜1");bill_dish=(char*)(_bstr_t)t;m_pr->Close();//Cstring转为char型chardish[100];strncpy(dish,(LPCTSTR)bill_dish,sizeof(dish));while(dish[i]!='\0'){while(dish[i]!='a'){i++;Count++;}if(Count==3){num=(dish[i-Count]-'0')*100+(dish[i-Count+1]-'0')*10+(dish[i-Count+2]-'0');}if(Count==2){num=(dish[i-Count]-'0')*10+(dish[i-Count+1]-'0');}if(Count==1){num=dish[i-Count]-'0';}Count=0;i++;sql.Format("select*fromMENUwhere序号=%d",num);m_pr->Open((_bstr_t)sql,m_pc.GetInterfacePtr(),adOpenDynamic,adLockOptimistic,adCmdText);t=m_pr->GetCollect("菜品名称");menu_name=(char*)(_bstr_t)t;t=m_pr->GetCollect("单价");menu_price.Format("%d",t.iVal);m_pr->Close();98\nintindex1=m_list2.GetItemCount();m_list2.InsertItem(index1,menu_name);m_list2.SetItemText(index1,1,menu_price);}}//-------------------------------------------------------读取结束-----------------------------------*pResult=0;}#include"Setting.h"voidCWirelessMenuDlg::OnSet(){Settingdlg;dlg.DoModal();}voidCWirelessMenuDlg::OnPaybill(){intindex=0;CStringseq,sql;//获取今日时间SYSTEMTIMEst;CStringstrDate,strTime;GetLocalTime(&st);if(st.wMonth<=9)strDate.Format("%d-0%d-%d",st.wYear,st.wMonth,st.wDay);elsestrDate.Format("%d-%d-%d",st.wYear,st.wMonth,st.wDay);index=m_list1.GetNextItem(-1,LVNI_SELECTED);//取鼠标点击list1序号if(index!=-1)//选取到序号,未选取到则不处理{seq=m_list1.GetItemText(index,0);sql.Format("select*fromBILLwhere序号=%s",seq);m_pr->Open((_bstr_t)sql,m_pc.GetInterfacePtr(),adOpenDynamic,adLockOptimistic,adCmdText);_variant_tt;t=m_pr->GetCollect("付账情况");m_pr->Close();CStringbill_matter;98\nbill_matter=(char*)(_bstr_t)t;if(bill_matter=="未付"){sql.Format("updateBILLset付账情况='已付'where序号=%s",seq);m_pc->Execute((_bstr_t)sql,NULL,adCmdText);sql.Format("updateBILLset点菜顺序=0where序号=%s",seq);m_pc->Execute((_bstr_t)sql,NULL,adCmdText);sql.Format("select*fromBILL");m_pr->Open((_bstr_t)sql,m_pc.GetInterfacePtr(),adOpenDynamic,adLockOptimistic,adCmdText);while(!m_pr->adoEOF){_variant_tt,t1;CStringbill_num,bill_seq,bill_time;t=m_pr->GetCollect("序号");bill_num.Format("%d",t.iVal);t1=m_pr->GetCollect("点菜顺序");t=m_pr->GetCollect("点菜时间");bill_time=(char*)(_bstr_t)t;charcurdata[50];strncpy(curdata,(LPCTSTR)bill_time,sizeof(curdata));curdata[10]='\0';CStringjudge_data(curdata);if(strDate==judge_data){intx=t1.iVal-1;sql.Format("updateBILLset点菜顺序=%dwhere序号=%s",x,bill_num);m_pc->Execute((_bstr_t)sql,NULL,adCmdText);}m_pr->MoveNext();}m_pr->Close();}Refresh();MessageBox("该账单已付款成功!!!");}}98\n#include"History.h"voidCWirelessMenuDlg::OnHisbill(){Historydlg;dlg.DoModal();}voidCWirelessMenuDlg::OnOnCommMscomm1(){intb=0;for(b=0;b<10;b++);//--------------------------------------------------接收数据----------------------------------------VARIANTvariant_inp;COleSafeArraysafearray_inp;LONGlen,k;BYTErxdata[200];//设置BYTE数组An8-bitintegerthatisnotsigned.CStringstrtemp;CStringm_strRXData;if(m_ctrlComm.GetCommEvent()==2)//事件值为2表示接收缓冲区内有字符{////////以下为根据自己的通信协议加入处理代码variant_inp=m_ctrlComm.GetInput();//读缓冲区safearray_inp=variant_inp;//VARIANT型变量转换为ColeSafeArray型变量len=safearray_inp.GetOneDimSize();//得到有效数据长度for(k=0;kOpen((_bstr_t)sql,m_pc.GetInterfacePtr(),adOpenDynamic,adLockOptimistic,adCmdText);t=m_pr->GetCollect(short(0));//找到最大序号intmaxno=t.iVal+1;m_pr->Close();//关闭BILL表//------------------------------------------------------------------------------------------------------//----------------------------------查表找到最大点菜顺序---------------------------------------98\nintmaxpoint=0;sql="select*fromBILL";m_pr->Open((_bstr_t)sql,m_pc.GetInterfacePtr(),adOpenDynamic,adLockOptimistic,adCmdText);while(!m_pr->adoEOF){CStringbill_seq,bill_time;t=m_pr->GetCollect("点菜时间");bill_time=(char*)(_bstr_t)t;charcurdata[50];strncpy(curdata,(LPCTSTR)bill_time,sizeof(curdata));curdata[10]='\0';CStringjudge_data(curdata);t=m_pr->GetCollect("点菜顺序");if(strDate==judge_data){if(maxpoint<=t.iVal)maxpoint=t.iVal;}m_pr->MoveNext();}m_pr->Close();maxpoint++;//------------------------------------------------------------------------------------------------------//---------------------------------------------查表计算总金额-------------------------------------i=0;//变量清零Count=0;inttotal=0;//应付金额intper=0;//点菜序号chardish1[100];strncpy(dish1,(LPCTSTR)dish,sizeof(dish1));while(dish1[i]!='\0'){while(dish1[i]!='a'){i++;Count++;}if(Count==3){per=(dish1[i-Count]-'0')*100+(dish1[i-Count+1]-'0')*10+(dish1[i-Count+2]-'0');98\n}if(Count==2){per=(dish1[i-Count]-'0')*10+(dish1[i-Count+1]-'0');}if(Count==1){per=dish1[i-Count]-'0';}Count=0;i++;sql.Format("select*fromMENUwhere序号=%d",per);m_pr->Open((_bstr_t)sql,m_pc.GetInterfacePtr(),adOpenDynamic,adLockOptimistic,adCmdText);t=m_pr->GetCollect("单价");total+=t.iVal;m_pr->Close();}//------------------------------------------------------------------------------------------------------sql.Format("insertintoBILLvalues(%d,%d,'%s',%d,%d,%d,'未付','%s','a')",maxno,maxpoint,Time,desknum,point,total,dish);m_pc->Execute((_bstr_t)sql,NULL,adCmdText);Refresh();MessageBox("有新菜单来了!!!");}}#include"Updata.h"voidCWirelessMenuDlg::OnUpdata(){Updatadlg;dlg.DoModal();}//********************************History********************************BOOLHistory::OnInitDialog(){CDialog::OnInitDialog();m_list1.InsertColumn(0,"序号",LVCFMT_LEFT,50,-1);m_list1.InsertColumn(1,"点菜顺序",LVCFMT_LEFT,80,-1);m_list1.InsertColumn(2,"点菜时间",LVCFMT_CENTER,140,-1);m_list1.InsertColumn(3,"桌号",LVCFMT_LEFT,50,-1);m_list1.InsertColumn(4,"点菜数",LVCFMT_LEFT,60,-1);98\nm_list1.InsertColumn(5,"应付金额",LVCFMT_LEFT,80,-1);m_list1.InsertColumn(6,"付账情况",LVCFMT_LEFT,80,-1);HistoryRefresh();returnTRUE;//returnTRUEunlessyousetthefocustoacontrol//EXCEPTION:OCXPropertyPagesshouldreturnFALSE}voidHistory::OnFixbill(){_ConnectionPtr&m_pc=((CWirelessMenuDlg*)AfxGetMainWnd())->m_pc;_RecordsetPtr&m_pr=((CWirelessMenuDlg*)AfxGetMainWnd())->m_pr;intindex=0;CStringseq,sql;index=m_list1.GetNextItem(-1,LVNI_SELECTED);//取鼠标点击list1序号if(index!=-1)//选取到序号,未选取到则不处理{seq=m_list1.GetItemText(index,0);sql.Format("select*fromBILLwhere序号=%s",seq);m_pr->Open((_bstr_t)sql,m_pc.GetInterfacePtr(),adOpenDynamic,adLockOptimistic,adCmdText);_variant_tt;t=m_pr->GetCollect("付账情况");m_pr->Close();CStringbill_matter;bill_matter=(char*)(_bstr_t)t;if(bill_matter=="未付"){sql.Format("updateBILLset付账情况='已付'where序号=%s",seq);m_pc->Execute((_bstr_t)sql,NULL,adCmdText);}HistoryRefresh();ParentRefresh();MessageBox("该账单已付款成功!!!");}}voidHistory::HistoryRefresh(){m_list1.DeleteAllItems();98\nunsignedintnopayperson=0,nopaymoney=0,totalmoney=0;_ConnectionPtr&m_pc=((CWirelessMenuDlg*)AfxGetMainWnd())->m_pc;_RecordsetPtr&m_pr=((CWirelessMenuDlg*)AfxGetMainWnd())->m_pr;intcount=0;CStringsql="select*fromBILL";m_pr->Open((_bstr_t)sql,m_pc.GetInterfacePtr(),adOpenDynamic,adLockOptimistic,adCmdText);while(!m_pr->adoEOF){_variant_tt,t1;CStringbill_num,bill_seq,bill_time,bill_desk,bill_count,bill_money,bill_matter;t=m_pr->GetCollect("序号");bill_num.Format("%d",t.iVal);t=m_pr->GetCollect("点菜顺序");bill_seq.Format("%d",t.iVal);t=m_pr->GetCollect("点菜时间");bill_time=(char*)(_bstr_t)t;t=m_pr->GetCollect("桌号");bill_desk.Format("%d",t.iVal);t=m_pr->GetCollect("点菜数");bill_count.Format("%d",t.iVal);t1=m_pr->GetCollect("应付金额");bill_money.Format("%d",t1.iVal);t=m_pr->GetCollect("付账情况");bill_matter=(char*)(_bstr_t)t;totalmoney+=t1.iVal;if(bill_matter=="未付"){nopayperson++;nopaymoney+=t1.iVal;}98\nintindex=m_list1.GetItemCount();m_list1.InsertItem(index,bill_num);m_list1.SetItemText(index,1,bill_seq);m_list1.SetItemText(index,2,bill_time);m_list1.SetItemText(index,3,bill_desk);m_list1.SetItemText(index,4,bill_count);m_list1.SetItemText(index,5,bill_money);m_list1.SetItemText(index,6,bill_matter);m_pr->MoveNext();count++;if(count>=150)//显示的记录数break;}m_pr->Close();m_nopayperson.Format("%d",nopayperson);m_nopaymoney.Format("%d",nopaymoney);m_totalmoney.Format("%d",totalmoney);UpdateData(0);}voidHistory::ParentRefresh(){//获取今日时间SYSTEMTIMEst;CStringstrDate,strTime;GetLocalTime(&st);strDate.Format("%4d-%02d-%02d",st.wYear,st.wMonth,st.wDay);CListCtrl*p=(CListCtrl*)AfxGetApp()->m_pMainWnd->GetDlgItem(IDC_LIST1);p->DeleteAllItems();//清空列表框数据_ConnectionPtr&m_pc=((CWirelessMenuDlg*)AfxGetMainWnd())->m_pc;_RecordsetPtr&m_pr=((CWirelessMenuDlg*)AfxGetMainWnd())->m_pr;CStringsql="select*fromBILL";m_pr->Open((_bstr_t)sql,m_pc.GetInterfacePtr(),adOpenDynamic,adLockOptimistic,adCmdText);while(!m_pr->adoEOF){98\n_variant_tt;CStringbill_num,bill_seq,bill_time,bill_desk,bill_count,bill_money,bill_matter;t=m_pr->GetCollect("序号");bill_num.Format("%d",t.iVal);t=m_pr->GetCollect("点菜顺序");bill_seq.Format("%d",t.iVal);t=m_pr->GetCollect("点菜时间");bill_time=(char*)(_bstr_t)t;charcurdata[50];strncpy(curdata,(LPCTSTR)bill_time,sizeof(curdata));curdata[10]='\0';CStringjudge_data(curdata);t=m_pr->GetCollect("桌号");bill_desk.Format("%d",t.iVal);t=m_pr->GetCollect("点菜数");bill_count.Format("%d",t.iVal);t=m_pr->GetCollect("应付金额");bill_money.Format("%d",t.iVal);t=m_pr->GetCollect("付账情况");bill_matter=(char*)(_bstr_t)t;if(strDate==judge_data){intindex=p->GetItemCount();p->InsertItem(index,bill_num);p->SetItemText(index,1,bill_seq);p->SetItemText(index,2,bill_time);p->SetItemText(index,3,bill_desk);p->SetItemText(index,4,bill_count);p->SetItemText(index,5,bill_money);p->SetItemText(index,6,bill_matter);}m_pr->MoveNext();}m_pr->Close();}98\nvoidHistory::OnCloseupDatetimepicker2(NMHDR*pNMHDR,LRESULT*pResult){CTimetime;m_DateCtrl.GetTime(time);CStringstrTime=time.Format("%Y-%m-%d");m_list1.DeleteAllItems();unsignedintnopayperson=0,nopaymoney=0,totalmoney=0;_ConnectionPtr&m_pc=((CWirelessMenuDlg*)AfxGetMainWnd())->m_pc;_RecordsetPtr&m_pr=((CWirelessMenuDlg*)AfxGetMainWnd())->m_pr;intcount=0;CStringsql="select*fromBILL";m_pr->Open((_bstr_t)sql,m_pc.GetInterfacePtr(),adOpenDynamic,adLockOptimistic,adCmdText);while(!m_pr->adoEOF){_variant_tt,t1;CStringbill_num,bill_seq,bill_time,bill_desk,bill_count,bill_money,bill_matter;t=m_pr->GetCollect("序号");bill_num.Format("%d",t.iVal);t=m_pr->GetCollect("点菜顺序");bill_seq.Format("%d",t.iVal);t=m_pr->GetCollect("点菜时间");bill_time=(char*)(_bstr_t)t;charcurdata[50];strncpy(curdata,(LPCTSTR)bill_time,sizeof(curdata));curdata[10]='\0';CStringjudge_data(curdata);t=m_pr->GetCollect("桌号");bill_desk.Format("%d",t.iVal);t=m_pr->GetCollect("点菜数");bill_count.Format("%d",t.iVal);98\nt1=m_pr->GetCollect("应付金额");bill_money.Format("%d",t1.iVal);t=m_pr->GetCollect("付账情况");bill_matter=(char*)(_bstr_t)t;m_pr->MoveNext();if(judge_data==strTime){totalmoney+=t1.iVal;if(bill_matter=="未付"){nopayperson++;nopaymoney+=t1.iVal;}intindex=m_list1.GetItemCount();m_list1.InsertItem(index,bill_num);m_list1.SetItemText(index,1,bill_seq);m_list1.SetItemText(index,2,bill_time);m_list1.SetItemText(index,3,bill_desk);m_list1.SetItemText(index,4,bill_count);m_list1.SetItemText(index,5,bill_money);m_list1.SetItemText(index,6,bill_matter);}}m_pr->Close();m_nopayperson.Format("%d",nopayperson);m_nopaymoney.Format("%d",nopaymoney);m_totalmoney.Format("%d",totalmoney);UpdateData(0);*pResult=0;}voidHistory::OnRefreshold(){HistoryRefresh();}//**********************************Setting******************************BOOLSetting::OnInitDialog(){98\nCDialog::OnInitDialog();//默认值com396008None1m_com.SetCurSel(3);m_baud.SetCurSel(5);m_correct.SetCurSel(1);m_data.SetCurSel(4);m_stop.SetCurSel(0);returnTRUE;//returnTRUEunlessyousetthefocustoacontrol//EXCEPTION:OCXPropertyPagesshouldreturnFALSE}voidSetting::OnConn(){intnSel;CStringChooseContent,ChooseContent1,set;intcom=0,baud=0,correct=0,data=0,stop=0;CStringcs;nSel=m_com.GetCurSel();//选中com的索引com=nSel+1;nSel=m_baud.GetCurSel();//选中baud的索引if(nSel!=LB_ERR)m_baud.GetLBText(nSel,ChooseContent);//选中的内容保存baud=atoi(ChooseContent);nSel=m_correct.GetCurSel();//选中correct的索引if(nSel!=LB_ERR)m_correct.GetLBText(nSel,ChooseContent1);//选中的内容保存nSel=m_data.GetCurSel();//选中data的索引if(nSel!=LB_ERR)m_data.GetLBText(nSel,ChooseContent);//选中的内容保存data=atoi(ChooseContent);nSel=m_stop.GetCurSel();//选中stop的索引if(nSel!=LB_ERR)m_stop.GetLBText(nSel,ChooseContent);//选中的内容保存stop=atoi(ChooseContent);//------------------------------------------------------------------------------------------------------//---------------------------------------------------串口---------------------------------------------CMSComm*p=(CMSComm*)AfxGetApp()->m_pMainWnd->98\nGetDlgItem(IDC_MSCOMM1);if(p->GetPortOpen())//如果串口打开则关闭p->SetPortOpen(FALSE);p->SetCommPort(com);//选择comif(!p->GetPortOpen())p->SetPortOpen(TRUE);//打开串口elseAfxMessageBox("cannotopenserialport");set.Format("%d,n,%d,%d",baud,data,stop);//波特率,校验,数据位,停止位p->SetSettings(set);p->SetRThreshold(1);//参数1表示每当串口接收缓冲区中有多于或等于1个字符时将引发一个接收数据的OnComm事件p->SetInputLen(0);//设置当前接收区数据长度为0p->GetInput();//先预读缓冲区以清除残留数据//------------------------------------------------------------------------------------------------------CStringstr="已连接";AfxGetApp()->m_pMainWnd->GetDlgItem(IDC_STATUS)->SetWindowText(str);MessageBox("连接成功!");}//******************************Updata**********************************BOOLUpdata::OnInitDialog(){CDialog::OnInitDialog();m_list1.InsertColumn(0,"序号",LVCFMT_LEFT,50,-1);m_list1.InsertColumn(1,"菜品名称",LVCFMT_CENTER,120,-1);m_list1.InsertColumn(2,"单价",LVCFMT_CENTER,80,-1);RefreshUpdata();returnTRUE;//returnTRUEunlessyousetthefocustoacontrol//EXCEPTION:OCXPropertyPagesshouldreturnFALSE}voidUpdata::RefreshUpdata(){m_list1.DeleteAllItems();_ConnectionPtr&m_pc=((CWirelessMenuDlg*)AfxGetMainWnd())->m_pc;_RecordsetPtr&m_pr=((CWirelessMenuDlg*)AfxGetMainWnd())->m_pr;98\nCStringsql="select*fromMENU";m_pr->Open((_bstr_t)sql,m_pc.GetInterfacePtr(),adOpenDynamic,adLockOptimistic,adCmdText);while(!m_pr->adoEOF){_variant_tt,t1;CStringbill_num,bill_name,bill_money;t=m_pr->GetCollect("序号");bill_num.Format("%d",t.iVal);t=m_pr->GetCollect("菜品名称");bill_name=(char*)(_bstr_t)t;t=m_pr->GetCollect("单价");bill_money.Format("%d",t.iVal);intindex=m_list1.GetItemCount();m_list1.InsertItem(index,bill_num);m_list1.SetItemText(index,1,bill_name);m_list1.SetItemText(index,2,bill_money);m_pr->MoveNext();}m_pr->Close();}voidUpdata::OnAdd(){//TODO:AddyourcontrolnotificationhandlercodehereUpdateData(1);_ConnectionPtr&m_pc=((CWirelessMenuDlg*)AfxGetMainWnd())->m_pc;_RecordsetPtr&m_pr=((CWirelessMenuDlg*)AfxGetMainWnd())->m_pr;CStringsql="selectmax(序号)fromMENU";m_pr->Open((_bstr_t)sql,m_pc.GetInterfacePtr(),adOpenDynamic,adLockOptimistic,adCmdText);_variant_tt;t=m_pr->GetCollect(short(0));//找到最大序号intmaxno=t.iVal+1;m_pr->Close();//关闭MENU表98\nsql.Format("insertintoMENUvalues(%d,'%s','%s')",maxno,m_vgename,m_price);m_pc->Execute((_bstr_t)sql,NULL,adCmdText);RefreshUpdata();MessageBox("添加成功!");}voidUpdata::OnModify(){//TODO:AddyourcontrolnotificationhandlercodehereUpdateData(1);_ConnectionPtr&m_pc=((CWirelessMenuDlg*)AfxGetMainWnd())->m_pc;_RecordsetPtr&m_pr=((CWirelessMenuDlg*)AfxGetMainWnd())->m_pr;CStringsql;sql.Format("updateMENUset菜品名称='%s'where序号=%s",m_vgename,ChooseNum);m_pc->Execute((_bstr_t)sql,NULL,adCmdText);sql.Format("updateMENUset单价=%swhere序号=%s",m_price,ChooseNum);m_pc->Execute((_bstr_t)sql,NULL,adCmdText);RefreshUpdata();MessageBox("修改成功!");}voidUpdata::OnDelete(){//TODO:Addyourcontrolnotificationhandlercodehereintindex=m_list1.GetNextItem(-1,LVNI_SELECTED);//取鼠标点击list1序号if(index!=-1)//选取到序号,未选取到则不处理{CStringbill_num;bill_num=m_list1.GetItemText(index,0);_ConnectionPtr&m_pc=((CWirelessMenuDlg*)AfxGetMainWnd())->m_pc;_RecordsetPtr&m_pr=((CWirelessMenuDlg*)AfxGetMainWnd())->m_pr;CStringsql;sql.Format("deletefromMENUwhere序号=%s",bill_num);m_pc->Execute((_bstr_t)sql,NULL,adCmdText);charnum[10];strncpy(num,(LPCTSTR)bill_num,sizeof(num));98\nintxuhao=atoi(num);sql.Format("updateMENUset序号=序号-1where序号>%s",bill_num);m_pc->Execute((_bstr_t)sql,NULL,adCmdText);RefreshUpdata();MessageBox("删除成功!");}}voidUpdata::OnClickList1(NMHDR*pNMHDR,LRESULT*pResult){//TODO:Addyourcontrolnotificationhandlercodehereintindex=m_list1.GetNextItem(-1,LVNI_SELECTED);//取鼠标点击list1序号if(index!=-1)//选取到序号,未选取到则不处理{CStringbill_num;bill_num=m_list1.GetItemText(index,0);ChooseNum=bill_num;_ConnectionPtr&m_pc=((CWirelessMenuDlg*)AfxGetMainWnd())->m_pc;_RecordsetPtr&m_pr=((CWirelessMenuDlg*)AfxGetMainWnd())->m_pr;CStringsql;sql.Format("select*fromMENUwhere序号=%s",bill_num);m_pr->Open((_bstr_t)sql,m_pc.GetInterfacePtr(),adOpenDynamic,adLockOptimistic,adCmdText);_variant_tt;t=m_pr->GetCollect("菜品名称");m_vgename=(char*)(_bstr_t)t;t=m_pr->GetCollect("单价");m_price.Format("%d",t.iVal);m_pr->Close();UpdateData(0);}*pResult=0;}98