找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 4560|回复: 4
收起左侧

51单片机自动售货机源程序与Proteus仿真原理图及调试过程

  [复制链接]
ID:679189 发表于 2022-4-25 20:10 | 显示全部楼层 |阅读模式
此例程可以用于学习单片机技术,基于51单片机制作的自动售货机仿真设计与调试,keil工程+proteus工程
仿真原理图如下(proteus仿真工程文件可到本帖附件中下载)
51hei.gif

设计了一款以INTEL公司出品的80C51单片机为核心的自动售货机,并且着重详细地介绍了自动售货机的整体系统设计方案、硬件选择基础、软件使用方法及技巧。以80C51作为CPU处理单元连接各个功能模块;以4*4矩阵键盘作为输入控制模块对货物进行种类和数量的选择以及模拟币的投入功能;以LCD1602液晶作为显示模块来显示当前的购物状态以及币状态;以LED的显示来表示当前选择货物以及出货的状态。通过Protues7.5单片机电路原理图进行连接布线连接各个模块;再用Keil uVision3专业编译软件完成源程序编译和调试,最终进行自动售货机的实验电路仿真来模拟自动售货。结合工作原理、系统设计、软件编译和实验仿真来实现自动售货这一相关功能。

本课题研究的是通过核心单片机80C51控制的自动售货机。涉及到用keil Uvision3来进行C语言程序的编译调试,通过protues7.5电路连接来设计系统电路。通过80C51与输入模块、输出显示模块、找零模块、显示模块的连接来系统模拟自动售货这一功能。设计从能根据投入的自动售货的机器。用4*4矩阵键盘来模拟货物种类以及数量的选择,再通过键盘其他按键来模拟投功能。用LCD1602作为输出显示模块,通过液晶显示来明确按键的功能以及自动售货机的购货状态等。用LED灯来表示货物的种类。因为识别功能是在自动售货机的外部硬件电路方面来加以具体实施的,而本设计是模拟自动售货机的功能,所以不考虑识别的模块设计,在这里只作原理介绍。

系统结构
自动售货机是集光、机、电一体化的自动售货装置,能够独立完成自动售货这一功能。
自动售货机的工作原理是:在初始化的界面等待顾客按键选择货物的种类和数量顾客投入,按下确定购买键后等待售货机自动计算所购商品的总价,然后提示顾客投入。所投的经过外部硬件检测传感器检测真假后累加计数。若顾客确认购买系统则跳转到下一步骤,反之取消购买即退出币返回到主界面,等待顾客下一步操作。自动售货机通过币的总价与和所购商品的总价的差值来进行出货找零。总体工作原理如图2.1:
图2.1 自动售货机的总体工作原理图
2.1.2 自动售货机的控制子系统概述
自动售货机的控制子系统由以下几个部分组成,分别是预设自动售货系统、金额累加和找零系统、售完检测系统、售出累计及反馈功能系统。
  • 预设自动售货机系统:自动售货机出售的商品可分为若干个品种,在其售前设定价格时必须预先寄存在控制系统内。设定的方式原理:商品的价格设置由按键和存储器组成,通过按键选择被设价商品的种类及价格,并将设定的价格写入存储器中,存储器本身需具有断电保护记忆功能,此为采用软件。此外,如果在自动售货机中,在出售商品的种类少,价格变化小,使用币单一的情况下,也可以用比较简单的价格设定方法----硬件设置,即采用拨码开关对应的方式设置。
  • 金额累加和找零系统:控制系统的核心CPU对识币 退、无货检验及购货信号进行循环检查。如果顾客投入币,检测到信号后,通过通信系统把识别的币数值信号送入累计存储器器中进行累加计数。要是币数值满足购货需求则通过串行接口提示购货,售货机则通过并行扩展接口驱动电磁阀或者微电机驱动送出货物,同时存储器计数清零跳入到找零系统,自动售货完成。如果币数值不满足购货需求则提示顾客继续投币或者取消购货,取消购货后由售货机退出投入的币返回初始化界面。
  • 货物检测系统:为了货物供应充足,把接触开关和行程开关安装在自动售货机货物存储存道的下方,当存储存道尚有货物时,行程开关保持闭合,CPU控制自动售货机正常售货。货物供应不足的时候,行程开关自动断开,向CPU发出缺货信号,经过检测后发送到外部电机或者电磁阀来推动外部货物进入存储道达到自动添加货物的目的,以保持货源充足。
  • 售出累计及反馈功能系统:自动售货机的CPU在每次售出一种货物后,由通信系统向出货存储器中累计已经售出的数据,通过自动售货机内部控制的按键可以显示出累计数据以掌握销售情况。此外,由外部按键操作在按下自测功能键后,出现自测功能信号,可以通过检测结果查看各个模块的功能情况,也可以检测自动售货机的货物存储道驱动功能。

2.2 自动售货机的功能简介
2.2.1 自动售货机的功能
概述
基本原理:通过矩阵键盘来选择货物的种类与数量过后自动售货机提示。自动售货机的币识别器对所投币进行识别,根据金额大小然后将商品选择权通过LCD液晶显示给客户,客户按键选择后,CPU控制芯片发出指令将所选择商品从储备料道中送达取物口。
功能描述:货物种类一共设有8种,这8种商品通过选择按键进行选择确认,通过数量选择按键确定购买数量,价格规定为1-8元不等;币识别器能够识别各种。在规定的时间内,投了几次币后,币能够实现自动累加功能,这样设计会把投入的所有币总额数目数据传递到中央控制元器件进行处理;在超过规定时间后自动关闭,数据处理单元按照总数和购买的数量和价格乘积来进行加减运算累,实现购买和找回的功能。
本设计中有一共有16个按键选择,其中有两个是货物选择键,有两个是数量选择键,此外还有6个币投入键1个确认键和1个取消键。
2.2.2 自动售货机的设计思路
  • 本设计以这样的工作流程开始自动售货机的自动售货过程:
  •    启动系统,开始待机;
  •    顾客通过按键选择商品的种类以及数量并确认;
  •    售货机检查是否有足够的货物并通过LCD提示等待顾客投;
  •    顾客投入,售货机自动检测是否足够;
  •    金额足够多,售货机将推出顾客选择的相应数量的商品,若金额不足则直接退还;
  •    推出商品,售货机转入找零系统退出余;
  •    系统自动复位,完成售货;
  • 系统采用硬件设计思路如图2.3:
  •    采用单片机80C51作CPU;
  •    采用P3.5口作为输入端;
  •    采用LCD1602液晶为显示模块:
  •    采用4*4矩阵键盘连接P1口作为货物选择选择端;
  •    采用P0口实现出货、找零功能;

图2.3 自动售货机系统原理图
  • 硬件端口的选择思路:
  •    输入:由于本设计模拟输入功能是由4*4矩阵键盘来完成的,而键盘连接在80C51的P1口上,则币投入设定为:
P1口的按键值为0X04的时候代表1的投入;
P1口的按键值为0X05的时候代表5的投入;
P1口的按键值为0X06的时候代表10的投入;
P1口的按键值为0X07的时候代表20的投入;
P1口的按键值为0X08的时候代表50的投入;
P1口的按键值为0X09的时候代表100的投入;
P1口的按键值为0X0f的时候代表购物确定“OK”按钮;
P1口的按键值为0X0e的时候代表购物取消“NO”按钮;
本设计假定自动售货机的商品种类为8种,价格为1、2、3、4、5、6、7、8元。每一个价格代表一种商品且一经售出就由外部电机自动完成补货。规定每次最多只能购买10个。货物选择:
P1口的按键值为OX00的时候代表选择货物价格“price+”;
P1口的按键值为OX01的时候代表选择货物价格“price-”;
P1口的按键值为OX02的时候代表选择货物数量“num+”;
P1口的按键值为OX03的时候代表选择货物价格“num-”;
  •    输出:选择商品由P0口控制的相应LED指示灯:
选择商品1用P0.0控制D1LED灯亮;
选择商品2用P0.1控制D2LED灯亮;
选择商品3用P0.2控制D3LED灯亮;
选择商品4用P0.3控制D4LED灯亮;
选择商品5用P0.4控制D5LED灯亮;
选择商品6用P0.5控制D6LED灯亮;
选择商品7用P0.6控制D7LED灯亮;
选择商品8用P0.7控制D8LED灯亮;
  •    显示:本设计的显示端口由P2端口和P3.2(RS)、P3.3(RW)、P3.4(E)控制LCD1602液晶来显示购货状态,用P0口控制LED灯表示货物种类。通过它可以显示购货的种类、数量、总价以及找零等。

3  自动售货机的硬件设计
3.1 80C51的简介
3.1.1 80C51的基本概述
由INTEL公司出品的MCS-51系列的80C51其实用性非常高。由于它是采用CHMOS的工艺技术制造,所以它是一款稳定性很高的高性能8位单片机,是HCMOS中的最基本的产品之一。在制作工艺的程中不仅继承和扩展了先前单片机的指令系统和体系结构更是把HMOS的高速高密度的技术特点和CHMOS的低功耗特点相结合。为了满足需求,在80C51内部置入CPU、RAM(128字节)、I/O(32个双向输入输出)、定时器/计数器(16位)、串行通信口、两级中断结构以及片内时钟震荡电路。此外,它还可以通过选择空闲和掉电的方式用于低功耗模式来进行工作,空闲模式下保持串行口、中断系统、RAM和定时器正常工作而同时冻结CPU来保证其正常运行。掉电模式下,自动保存RAM数据,时钟震荡停止、芯片的其他功能停止工作。
图3.1 80C51单片机内部基本结构
3.1.2 80C51的引脚功能介绍
图3.2 80C51的引脚图
80C51单片机的40个引脚大致可分为4类:电源、时钟、控制和I/O引脚。
  • 电源:
  •    VCC - 芯片电源,接+5V;
  •    VSS - 接地端。
  • 时钟:XTAL1、XTAL2晶体振荡电路反相输入端和输出端。
  • 控制线:控制线共有4根:
  •    ALE/PROG:地址锁存允许/片内EPROM编程脉冲
  •    ALE功能用来锁存P0口送出的低8位地址;
  •    PROG功能:片内有EPROM的芯片,在EPROM编程期间,此引脚输入编程脉冲。
  •    PSEN:外ROM读选通信号。
  •    RST/VPD(复位/备用电源):
  •    RST(Reset)功能是复位信号输入端;
  •    VPD功能是在Vcc掉电情况下,接备用电源。
  •    EA/Vpp(内外ROM选择/片内EPROM编程电源):
  •    EA功能:内外ROM选择端;
  •    Vpp功能:片内有EPROM的芯片,在EPROM编程期间,施加编程电源Vpp。
  • I/O线:80C51共有4个8位并行I/O端口即P0、P1、P2、P3口,共32个引脚;P3口还具有第二功能——用于特殊信号输入输出和控制信号(属控制总线)P0口输入时需要接上拉电阻才能置1。
在每次使用单片机之前,我们都要使单片机复位,让CPU以及其他功能部件都处于一个确定的初始状态,以消除上一次用户的操作对本次用户操作的影响。51的RST引脚是复位信号的输入端。复位信号是高电平有效,持续时间要有24个时钟周期以上。例如:若MCS-51单片机的时钟频率为12MHz,则复位脉冲宽度至少应为2us。通常,80C51的复位有自动上电复位和人工按纽复位两种
自动上电复位电路的工作原理是:电容在通电的时候相当于短路情况,导致RST引脚上的电位为高电平,这样电容会因为电阻被充电,然后RST端逐渐降低电压直到变为低电平,从而使单片机开始正常工作。由于自动售货机的功能需求,本次设计采用自动上电复位电路。

3.2 LCD1602字符型液晶简介
3.2.1 LCD1602的概述
LCD1602液晶也叫1602字符型液晶,它是一种专门用来显示字母、数字、符号等的点阵型液晶模块。它由若干个5X7或者5X11等点阵字符位组成,每个点阵字符位都可以显示一个字符,每位之间有一个点距的间隔,每行之间也有间隔,起到了字符间距和行间距的作用,正因为如此所以它不能很好地显示图形(用自定义CGRAM,显示效果也不好)。1602LCD是指显示的内容为16X2,即可以显示两行,每行16个字符液晶模块(显示字符和数字)。市面上字符液晶大多数是基于HD44780液晶芯片的,控制原理是完全相同的,因此基于HD44780写的控制程序可以很方便地应用于市面上大部分的字符型液晶。液晶显示器以其微功耗、体积小、显示内容丰富、超薄轻巧的诸多优点,在各类仪表和低功耗系统中得到广泛的应用。根据显示内容可以分为字符型液晶,图形液晶。根据显示容量又可以分为单行16字,2行16字,两行20字等等。
3.2.2 LCD1602的硬件结构
图3.3 LCD1602的基本结构图
引脚说明:LCD1602一般是16个引脚
第1脚:VSS为接地电源。
第2脚:VDD接5V正电源。
第3脚:VEE为液晶显示器对比度调整端,通过连接电源正负来调节对比度的强弱。当连接电源正端时对比度最弱,反之连接负极则最高。对比度太高时会产生所谓的“鬼影”,在连接时可以通过接一个电位器来调整对比度。
第4脚:RS为数据命令选择端,电平为H时选择数据寄存器、电平为L时选择指令寄存器。
第5脚:RW为读写选择端,电平为H时进行读操作,电平为L时进行写操作。
第6脚:E端为使能端,当E端由高电平跳变成低电平时,液晶模块执行命令。
第7~14脚:D0~D7为8位双向数据线。
第15~16脚:空脚或背灯电源,15脚背光正极,16脚背光负极。
3.2.3 LCD1602的指令说明
  • LCD1602的初始化设置:
  •    显示模式设置如表3.1所示:
表3.1 显示模式设置
  
  •    显示开/关及光标位置如表3.2所示:
表3.2 显示开/关及光标位置
  • 数据控制:控制器内部设有一个数据地址指针,可以根据它来访问内部的全部字节。
  •    数据指针设置如表3.3所示:
表3.3 数据指针设置
  •    读数据:输入时 RW 、RS、 E均等于H,输出D0-D7=数据
  •    写数据:输入时RW=L、RS=H、D0-D7=数据、E=高脉冲,输出无;
  •    其他设置如表3.4所示:
表3.4 其他设置
  • LCD1602液晶模块的读写指令:
  •    指令1:清除显示,指令码为01H,清楚显示数据;
  •    指令2:光标返回到地址00H即光标复位;
  •    指令3:光标和显示位置设置,光标按方向移动,高/低电平右/左移,S:屏幕上所有文字是否左移或右移,高电平移动,低电平不移动;
  •    指令4:显示开关控制D:控制整体的显示开与关,高电平表示开显示,低电平表示关显示。C:控制光标的开与关,高电平表示有光标,低电平表示无光标 B:控制光标是否闪烁,高电平闪烁,低电平不闪烁;
  •    指令5:光标或显示移位 S/C :高电平时显示移动的文字,低电平时移动光标;
  •    指令6:功能设置命令 DL:高电平时为4位总线,低电平时为8位总线 N:低电平时为单行显示,高电平时为双行显示,F:低电平时显示5X7的点阵字符,高电平时显示5X10的显示字符;
  •    指令7:字符发生器RAM地址设置;
  •    指令8:DDRAM地址设置;
  •    指令9:读忙信号和光标地址BF:忙标志位,高电平表示忙,此时模块不能接收命令或数据,如果为低电平表示不忙;
3.2.4 LCD1602的基本时序操作
LCD1602的基本时序操作以及对应的状态和输入输出如表3.5所示:

表3.5 基本时序操作及对应状态的输入输出
LCD1602的读写时序操作如图3.4、3.5所示:
图 3.4读时序操作图
图 3.5写时序操作图

3.3 4*4矩阵键盘简介
3.3.1 4*4矩阵键盘的概述
4*4矩阵键盘又是4*4行列键盘,它是分别用4条I/O线作为行线和列线组成的键盘。每个键的位置设置在每条行线和列线在交叉处上,可以知道4*4矩阵键盘共有4*4=16个按键。这样的设置可以有效地提高单片机I/O口的利用率,与独立式按键相比大大降低了接口占用率。当设计过程中需要按键个数要求比较大时,为了释放出I/O端口来供其他连接需要时往往采用这种矩阵式按键来解决问题。在矩阵键盘中,行列线均通过一个按键加以连接取代了直接交叉相连。这样,在很大的程度上增加了按键数,而直接将端口线连接在键盘上则会减少一倍的按键数量。在需要很多按键的设计中采用矩阵键盘明显地比独立式按键更合理,而且行列线越多效果越明显。
3.3.2 4×4矩阵键盘的硬件结构

图3.6 4*4矩阵键盘的基本结构图
在应用矩阵键盘的设计中要编译程序的时候必定会计算出每个按键的接口地址是多少以方便编译,在矩阵键盘的按键确定上我们可以采用两种方法:
  • 行扫描法:
行扫描法是我们常用的一种识别按键的方法,这种方法需要我们逐行逐列进行扫描查询,分为两个步骤:
  •    将连接的全部行线置为低电平,然后检测所有列线的电位状态。只要某一列列线电位为低电平,就表示矩阵键盘中这列有键被按下,并且在被按下的闭合的按键在低电平列线与全部根行线相交叉的4个按键之中。反之,若所有列线电位均为高电平,则表示矩阵键盘中没有按键被按下;
  •    通过前一可知按键处在判断的4个按键之中,在确认了有按键被按下后,就可进入到确定具体被闭合键的操作。然后再依次将所有行线电位置为低电平,即只有一根行线为低电平,其余行线则为高电平。在确定了某根行线电位为低电平后,再逐行检测所有列线的电平状态。若某根列线的电位为低电平,则被按下的按键就处在这根列线和低电平行线的交叉处;
  • 高低电平翻转法:
这种确定矩阵键盘按键的方法也是非常有效的方法,也分为两个步骤:
  •    先让所连接的端口高四位为高(1),低四位为低(0),若矩阵键盘上有按键被按下,则高四位中会有一个电平从1被翻转到0,低四位则不会改变电平状态,然后即可确定矩阵键盘上被按下的按键的所在行的具体位置;
  •     让让所连接的端口高四位为低(0),低四位为高(1)。若矩阵键盘上有按键被按下,则低四位中会会有一个电平1翻被转为0,高四位则不会改变电平状态,然后即可确定矩阵键盘上被按下的按键的所在列的具体位置。综合这两个步骤就可以判断出被按下的按键具体位置;

3.4 识别系统
3.4.1对硬币的识别
目前在国内,对于硬币的识别可采用多种方法,比如激光扫描、应变片测重量、光电管检测大小等,然而在自动售货机中采用结构简单、成本低、测量准确及其非接触测量等优点集一身的涡流传感器检测。
电涡流检测原理:以高频的电信号通过一个线圈,在这个过程中产生变化的磁场通过硬币的表面即变化磁通通过硬币的表面,相应地硬币表面上产生电涡流, 并产生反向的变化磁场,以削弱原来线圈产生出来的磁场。然后根据激励磁场线圈幅值的变化,通过变化的给定值即可测出真假硬币。

图3.7 电涡流检测电路原理图
图3.7中Q2所需要的频率由Q1、L、C4等元件组成的振荡电路所提供,从而在C点产生一正弦波振荡信号;然后再A、B 两点外接一个传感线圈,当有硬币投入通过线圈时会改变C点原有信号的幅值大小,而真假硬币通过线圈时改变的幅值大小是不一样的,通过对比设定给定真币的参照值来判断真假硬币。
但是在实际应用中,由于某些条件的原因会影响到电子线路元器件的判定值,比如温漂的影响,由于材质的差异比较小,从而导致信号值得差距变小,若受到的温漂影响稍微增强一点,那么则很难精确地确检测出不行的,为此可以采用电桥的方法来进行改善弥补,从而减小由温漂造成的干扰。图中L3、L4都是激励线圈,L3上方放置一个标准的1元硬币或其它用低碳钢做成的圆片,L4为检测有无硬币通过和是否为真假的激励线圈,A、B两信号通过通信模块同时送到后续的运算放大器之中进行相减,从而抵消因温漂所造成的影响。


3.5 货物选择系统
货物选择系统主要通过矩阵键盘按键操作来完成。4*4矩阵键盘是自动售货机中的输入装置,价格低廉,结构简单,使用方便,在单片机应用系统中得到广泛地应用。键盘按照接口原理可分为编码键盘与非编码键盘两类,它们的主要区别是识别键符及给出相应键码的方法。编码键盘主要是用硬件来实现对键的识别,非编码键盘主要是由软件来实现键盘的定义与识别。
CPU可以采用查询或中断方式了解有无将键输入,并检查是哪一个键按下,然后通过跳转指令转入执行该键的功能程序,执行完后再返回主程序。
一个完整的矩阵键盘控制程序应该具备以下功能:
  • 能够准确检测键盘上有无按键按下,为了消除键盘的按键机械触点抖动可以采取硬件或软件措施;
  • 有良好的逻辑处理方法,按键处理可以独立开展,在处理期间对任何一个按键的操作不对系统产生影响,只要按键按下系统都只执行一次按键功能程序;
  • 输出的按键值或者键号可以达到很精确的状态来正确执行跳转指令;
图3.9 货物选择系统图

3.6 出货及找零系统
在本系统中,总共设计了8种饮料,价格分别为1、2、3、4、5、6、7、8元不等,由顾客通过按键选择确定后,投入售货机可识别的币(币可以累加计数)。当投入的币总值不够购买选择的商品总值时,售货机显示金取消交易并退出所投币,如果所投币大于等于购买选择的商品总值时,由售货机出货并计算剩余币。但是在单片机的输出中只有高电平和低电平之分。在自动售货机中只需要安装一个驱动电机,待顾客投入币后将信号送入系统通过相应电路来驱动电机推出相应商品即可。本设计只模拟实现此功能,负责软件仿真,暂不考虑硬件电机方面,只作介绍。在这里,我们选用光敏三极管来实现这一功能。当单片机输出端为高电平时,则驱动二极管发光,使驱动电机电路导通,这时驱动电机开始工作等待信号。当单片机输出端为低电平时,则二极管熄灭。驱动电机电路断开,即完成推货动作,LED灯闪烁。售货机通过投币时累计的计数和购买商品的总金额进行相减的运算,在LCD液晶上显示出余币的数量,顾客取走货物后自动退还余币。
图3.10 出货及找零

4  系统的软件流程图设计
4.1 自动售货机货物选择流程图
本设计提供1-8元不等的货物,分别用1-8的序号对应货物。当启动系统后进入初始化界面,等待顾客选择货物种类和数量,按键“OK”则跳入投币系统,按键“NO”则返回初始化等待界面。在这里只以1号商品作为例子来讲解具体流程如图4.1:
图4.1 自动售货机货物选择流程图

4.2 自动售货机投币系统流程图
在4*4矩阵键盘上模拟投入币,若投入一种面值的币就累加一种面值的币,如果没有投入币或者投入币值不足购买商品的话则提示继续投币。当所投币足够时,进入出货找零系统如图4.2所示:
图4.2 自动售货机投币系统流程图

4.3 自动售货机出货找零系统流程图
进入出货找零系统时,如果购买商品后没有剩余币则直接推出商品,若还有剩余币则找出余币,如图4.3所示:
4.3 自动售货机出货找零系统流程图

5.2 仿真结果
设计好程序之后,还需要通过protues7.5和keil3.0来实现程序的调试和仿真,仿真结果图如下:
  • 当启动自动售货机时机器进入初始化等待状态,等待按键输入,如图5.1所示:
图5.1 初始化等待界面
  • 通过按键price+和price-来选择货物的种类,然后通过num+和num-来选择所购买货物的数量,同时代表被选择货物的LED指示灯亮,如图5.2所示:
图5.2 选择货物的种类及数量
  • 当选择好货物后,按下“OK”键进入到投币系统,此时机器会自动计算出所购货物总金额,如图5.3所示:
图5.3 货物选择并计价
  • 当机器计算出总价等待顾客投入币并按下“OK”键后,当投入币币值大于或等于总价时就出货并找零,若投入币币值小于总价则不能出货只能按下“NO”退出所投币或继续投币,如图5.4所示:
图5.4 出货找零
  • 当完成以上步骤后则完成一次自动购物过程,机器自动复位回到初始化界面如图5.5所示:
图5.5 自动复位返回初始化界面

附录B:系统总图

单片机源程序如下:
  1. #include<reg51.h>

  2. /*******************define*************************/
  3. #define WAIT_SALE        0                 //定义1602显示状态
  4. #define NO_WATER         1                 
  5. #define INPUT_MONEY        2
  6. #define SALING                3
  7. #define CHANGE                4
  8. #define CANCLE                5
  9. #define NO_DATA                0
  10. #define water_change(x, y) ((x) & (~(0x01 << y))) //宏 控制水种类的切换
  11. #define uint unsigned int
  12. #define uchar unsigned char

  13. unsigned char water_total[] = {10, 10, 10, 10, 10, 10, 10, 10};   
  14. unsigned char water_price[] = {1, 2, 3, 4, 5, 6, 7, 8};
  15. uchar code wait_sale[]="for sale";                  //定义所显示的内容
  16. uchar code no_water[] = "no water!";
  17. uchar code price[] = "price:";
  18. uchar code number[] = "num:";
  19. uchar code sum[] = "SUM:";
  20. uchar code input[] = "INPUT:";
  21. uchar code change[] = "change:";
  22. uchar code put_water[] = "put water...";
  23. uchar code cancle[] = "cancle...";

  24. unsigned char e=0x00;                    
  25. sbit P2_0=P2^0;                       //定义接端口
  26. sbit P2_1=P2^1;
  27. sbit P2_2=P2^2;
  28. sbit P2_3=P2^3;
  29. sbit lcd_en=P3^4;            
  30. sbit rs=P3^2;
  31. sbit rw = P3^3;
  32. /****************************************************/

  33. /***********************function**********************************/
  34. /************lcd 1602*************************/
  35. void lcd_1602_delay(uint z)          //延时
  36. {
  37. uint x,y;
  38. for(x=z;x>0;x--)
  39. for(y=110;y>0;y--);
  40. }

  41. void lcd_1602_write_com(uchar com)   //写命令
  42. {
  43. rs=0;
  44. rw=0;
  45. P2=com;
  46. lcd_1602_delay(5);
  47. lcd_en=1;
  48. lcd_1602_delay(5);
  49. lcd_en=0;
  50. }

  51. void lcd_1602_write_data(uchar date) //写数据
  52. {
  53. rs=1;
  54. rw=0;
  55. P2=date;
  56. lcd_1602_delay(5);
  57. lcd_en=1;
  58. lcd_1602_delay(5);
  59. lcd_en=0;
  60. }

  61. void lcd_1602_init()               //LCD1602初始化函数
  62. {
  63. lcd_en=0;
  64. lcd_1602_write_com(0x38);
  65. lcd_1602_write_com(0x0e);
  66. lcd_1602_write_com(0x06);
  67. lcd_1602_write_com(0x01);
  68. }

  69. void lcd_1602_wait_sure()         //显示'OK'&'NO'界面
  70. {
  71. lcd_1602_write_com(0x80+0x40);
  72. lcd_1602_write_data('O');
  73. lcd_1602_write_data('K');
  74.                
  75. lcd_1602_write_com(0x80+0x4e);
  76. lcd_1602_write_data('N');
  77. lcd_1602_write_data('O');
  78. }

  79. void lcd_1602_show_num(uint num)       // 1602显示数字的函数
  80. {
  81. if(num <= 9)
  82. {
  83.    lcd_1602_write_data('0'+num);
  84.    lcd_1602_write_data(' ');
  85.   }else if(num >= 10 )
  86. {
  87.    lcd_1602_write_data('0'+num/10);
  88.    lcd_1602_write_data('0'+num%10);
  89.   }
  90. }

  91. void lcd1602_info_display(uint choose, uint num_type, uint num_count)  //1602显示函数
  92. {
  93. int num;
  94. lcd_1602_write_com(0x01);
  95. switch(choose)
  96.   {
  97. case WAIT_SALE:
  98. lcd_1602_write_com(0x80+0x03);       //
  99. for(num = 0; num < 8; num++)
  100. {
  101. lcd_1602_write_data(wait_sale[num]);        
  102. }
  103. break;
  104. case NO_WATER:
  105. lcd_1602_write_com(0x80+0x03);
  106. for(num = 0; num < 9; num++)
  107.   {
  108.    lcd_1602_write_data(no_water[num]);        
  109.    }
  110. break;
  111. case SALING:
  112. //price
  113. lcd_1602_write_com(0x80+0x00);
  114. for(num = 0; num < 6; num++)
  115.   {
  116.     lcd_1602_write_data(price[num]);
  117.    }
  118. lcd_1602_write_data('0'+(num_type+1));
  119. lcd_1602_write_data(' ');
  120. //number
  121. lcd_1602_write_data(' ');
  122. for(num = 0; num < 4; num++)
  123.           {
  124.            lcd_1602_write_data(number[num]);
  125.            }
  126. lcd_1602_show_num(num_count);
  127.                
  128.                
  129. break;
  130. case INPUT_MONEY:
  131. //need money        
  132. lcd_1602_write_com(0x80+0x00);
  133. for(num = 0; num < 4; num++)
  134.     {
  135.       lcd_1602_write_data(sum[num]);
  136.     }
  137. lcd_1602_show_num(num_type);
  138. //input
  139. lcd_1602_write_data(' ');
  140. lcd_1602_write_data(' ');
  141. for(num = 0; num < 6; num++)
  142.         {
  143.        lcd_1602_write_data(input[num]);
  144.         }
  145. lcd_1602_show_num(num_count);
  146. break;
  147. case CHANGE:
  148. if(!num_count)
  149.                 {
  150.                 //put water
  151.        lcd_1602_write_com(0x80+0x02);
  152.                 for(num = 0; num < 13; num++)
  153.             {
  154. lcd_1602_write_data(put_water[num]);
  155.             }
  156.        }
  157. else
  158. {
  159. //cancle water
  160. lcd_1602_write_com(0x80+0x02);              //取消选择时写命令退币
  161. for(num = 0; num < 9; num++)
  162.         {
  163.              lcd_1602_write_data(cancle[num]);
  164.         }
  165. }
  166. //change
  167. lcd_1602_write_com(0x80+0x43);
  168. for(num = 0; num < 7; num++)
  169.         {
  170.             lcd_1602_write_data(change[num]);
  171.         }
  172. lcd_1602_show_num(num_type);
  173. break;
  174. default:
  175. break;
  176. }
  177. lcd_1602_write_com(0x0c);
  178. }
  179. void lcd_1602_saling(uint type, uint num)
  180. {
  181.         lcd1602_info_display(SALING, type, num);
  182.         lcd_1602_wait_sure();
  183. }

  184. void lcd_1602_inputing(uint need, uint input)
  185. {
  186.   lcd1602_info_display(INPUT_MONEY, need, input);
  187.   lcd_1602_wait_sure();
  188. }
  189. /*************************************************/
  190. void delay1(unsigned int t)           // 延时函数
  191. {
  192. unsigned int i,j;
  193. for(i=0;i<t;i++)
  194. for(j=0;j<10;j++);
  195. }

  196. void out_water()             //推出水函数
  197. {
  198. int i, j = 100;
  199. while(j--)
  200.   {
  201.         for(i = 0; i < 8; i++)
  202.         {
  203.           P0 = water_change(0xff, i);
  204.      delay1(50);
  205.     }
  206.   }
  207. P0 = 0xff;
  208. }

  209. unsigned char key(void)       //按键响应函数
  210. {
  211. unsigned char key,key1,key2;
  212. P1=0XF0;
  213. if((P1&0XF0)!=0XF0)           //ked down
  214.     {
  215.        delay1(10);                         //延时去抖动
  216.        if((P1&0XF0)!=0XF0)                  //仍然有键按下
  217.       {
  218.         key1=P1&0XF0;                          //读入列线值
  219.         P1=0X0F;
  220.         key2=P1&0X0F;
  221.         key=key1|key2;
  222. switch(key)                  //按键映射
  223. {
  224. case 0xee: e=0x00;break;
  225. case 0xde: e=0x01;break;
  226. case 0xbe: e=0x02;break;
  227. case 0x7e: e=0x03; break;
  228. case 0xed: e=0x04;break;
  229. case 0xdd: e=0x05;break;
  230. case 0xbd: e=0x06;break;
  231. case 0x7d: e=0x07; break;
  232. case 0xeb: e=0x08;break;
  233. case 0xdb: e=0x09;break;
  234. case 0xbb: e=0x0a;break;
  235. case 0x7b: e=0x0b; break;
  236. case 0xe7: e=0x0c;break;
  237. case 0xd7: e=0x0d;break;
  238. case 0xb7: e=0x0e;break;
  239. case 0x77: e=0x0f; break;
  240. }
  241. while((P1&0X0F)!=0X0F);
  242. delay1(10);
  243. return 1;
  244. }
  245.   }
  246. return 0;
  247. }

  248. /*********************************************************************/
  249. /*****************main********************************/
  250. void main()
  251. {
  252. unsigned char ok = 0, water_type = -1,  water_is_choose = 0;
  253. char water_count = 0;
  254. unsigned int flag = 0, money = 0, need_money;

  255. lcd_1602_init();
  256. again:
  257. ok = 0; water_type = -1; water_count = 0; water_is_choose = 0;
  258. flag = 0; money = 0; need_money = 0;        
  259. lcd1602_info_display(WAIT_SALE, 0, 0);
  260. while(!ok)        //choose the water and count
  261. {
  262.   flag=key();
  263.   if(flag)
  264.   {
  265.     switch(e)         //不同的按键进行不同的操作
  266.      {
  267.         case(0x00):
  268.         water_type++;
  269.         water_is_choose = 1;
  270.         if(water_type == 8)
  271.                {
  272.              water_type = 0;
  273.                 }
  274. P0 = water_change(0xff, water_type);
  275. break;               
  276. case(0x01):
  277. water_type--;
  278. water_is_choose = 1;
  279. if(water_type == -1 || water_type == -2)
  280. {
  281.   water_type = 7;
  282. }
  283. P0 = water_change(0xff, water_type);
  284. break;
  285. case(0x02):
  286. if(water_is_choose == 1)
  287.   {
  288.      water_count++;
  289.      if(water_count > water_total[water_type])
  290.      {
  291.        water_count = water_total[water_type];
  292.             }
  293.    }
  294. break;
  295. case(0x03):
  296. if(water_is_choose == 1)
  297. {
  298.   water_count--;
  299.   if(water_count < 0)
  300.         {
  301.       water_count = 0;
  302.          }
  303. }                                
  304. break;
  305. case(0x0e):        //  返回
  306. goto again;
  307. break;   
  308. case(0x0f):         //   确认
  309. if(water_is_choose == 1)
  310.          {        
  311.       ok=1;
  312.           }                                
  313.         break;
  314.    default:
  315.    break;                                
  316. }
  317. lcd_1602_saling(water_type, water_count);                        
  318.         }
  319. }
  320.         
  321. //choose ok: calculate the money        //计算总数
  322. need_money = water_count * water_price[water_type];
  323. lcd_1602_inputing(need_money, money);
  324. ok = 0;        
  325. while(!ok)                //get money
  326.         {
  327.                 flag=key();
  328.                 if(flag)
  329.                 {
  330.                         switch(e)
  331. ……………………

  332. …………限于本文篇幅 余下代码请从51黑下载附件…………
复制代码

Keil5代码与Proteus8.8仿真下载:
仿真程序文档.7z (756.5 KB, 下载次数: 114)

评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的黑币奖励!

查看全部评分

回复

使用道具 举报

ID:262 发表于 2022-4-26 05:16 | 显示全部楼层
好资料,51黑有你更精彩!!!
回复

使用道具 举报

ID:983355 发表于 2022-5-14 18:14 | 显示全部楼层
您好啊,“自动售货机的控制子系统由以下几个部分组成,分别是预设自动售货系统、金额累加和找零系统、售完检测系统、售出累计及反馈功能系统。”
我刚试了一遍,请问这里的售出累计及反馈功能系统  能显示出来吗?
回复

使用道具 举报

ID:1060270 发表于 2023-1-4 11:30 | 显示全部楼层
51黑真的很好哦
回复

使用道具 举报

ID:92810 发表于 2023-1-9 16:29 | 显示全部楼层
谢谢楼主分享,下载学习啦
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表