此例程可以用于學(xué)習(xí)單片機技術(shù),基于51單片機制作的自動售貨機仿真設(shè)計與調(diào)試,keil工程+proteus工程
仿真原理圖如下(proteus仿真工程文件可到本帖附件中下載)
設(shè)計了一款以INTEL公司出品的80C51單片機為核心的自動售貨機,并且著重詳細(xì)地介紹了自動售貨機的整體系統(tǒng)設(shè)計方案、硬件選擇基礎(chǔ)、軟件使用方法及技巧。以80C51作為CPU處理單元連接各個功能模塊;以4*4矩陣鍵盤作為輸入控制模塊對貨物進(jìn)行種類和數(shù)量的選擇以及模擬幣的投入功能;以LCD1602液晶作為顯示模塊來顯示當(dāng)前的購物狀態(tài)以及幣狀態(tài);以LED的顯示來表示當(dāng)前選擇貨物以及出貨的狀態(tài)。通過Protues7.5單片機電路原理圖進(jìn)行連接布線連接各個模塊;再用Keil uVision3專業(yè)編譯軟件完成源程序編譯和調(diào)試,最終進(jìn)行自動售貨機的實驗電路仿真來模擬自動售貨。結(jié)合工作原理、系統(tǒng)設(shè)計、軟件編譯和實驗仿真來實現(xiàn)自動售貨這一相關(guān)功能。
本課題研究的是通過核心單片機80C51控制的自動售貨機。涉及到用keil Uvision3來進(jìn)行C語言程序的編譯調(diào)試,通過protues7.5電路連接來設(shè)計系統(tǒng)電路。通過80C51與輸入模塊、輸出顯示模塊、找零模塊、顯示模塊的連接來系統(tǒng)模擬自動售貨這一功能。設(shè)計從能根據(jù)投入的自動售貨的機器。用4*4矩陣鍵盤來模擬貨物種類以及數(shù)量的選擇,再通過鍵盤其他按鍵來模擬投功能。用LCD1602作為輸出顯示模塊,通過液晶顯示來明確按鍵的功能以及自動售貨機的購貨狀態(tài)等。用LED燈來表示貨物的種類。因為識別功能是在自動售貨機的外部硬件電路方面來加以具體實施的,而本設(shè)計是模擬自動售貨機的功能,所以不考慮識別的模塊設(shè)計,在這里只作原理介紹。
系統(tǒng)結(jié)構(gòu)自動售貨機是集光、機、電一體化的自動售貨裝置,能夠獨立完成自動售貨這一功能。 自動售貨機的工作原理是:在初始化的界面等待顧客按鍵選擇貨物的種類和數(shù)量顧客投入,按下確定購買鍵后等待售貨機自動計算所購商品的總價,然后提示顧客投入。所投的經(jīng)過外部硬件檢測傳感器檢測真假后累加計數(shù)。若顧客確認(rèn)購買系統(tǒng)則跳轉(zhuǎn)到下一步驟,反之取消購買即退出幣返回到主界面,等待顧客下一步操作。自動售貨機通過幣的總價與和所購商品的總價的差值來進(jìn)行出貨找零。總體工作原理如圖2.1: 圖2.1 自動售貨機的總體工作原理圖 2.1.2 自動售貨機的控制子系統(tǒng)概述自動售貨機的控制子系統(tǒng)由以下幾個部分組成,分別是預(yù)設(shè)自動售貨系統(tǒng)、金額累加和找零系統(tǒng)、售完檢測系統(tǒng)、售出累計及反饋功能系統(tǒng)。 - 預(yù)設(shè)自動售貨機系統(tǒng):自動售貨機出售的商品可分為若干個品種,在其售前設(shè)定價格時必須預(yù)先寄存在控制系統(tǒng)內(nèi)。設(shè)定的方式原理:商品的價格設(shè)置由按鍵和存儲器組成,通過按鍵選擇被設(shè)價商品的種類及價格,并將設(shè)定的價格寫入存儲器中,存儲器本身需具有斷電保護(hù)記憶功能,此為采用軟件。此外,如果在自動售貨機中,在出售商品的種類少,價格變化小,使用幣單一的情況下,也可以用比較簡單的價格設(shè)定方法----硬件設(shè)置,即采用撥碼開關(guān)對應(yīng)的方式設(shè)置。
- 金額累加和找零系統(tǒng):控制系統(tǒng)的核心CPU對識幣 退、無貨檢驗及購貨信號進(jìn)行循環(huán)檢查。如果顧客投入幣,檢測到信號后,通過通信系統(tǒng)把識別的幣數(shù)值信號送入累計存儲器器中進(jìn)行累加計數(shù)。要是幣數(shù)值滿足購貨需求則通過串行接口提示購貨,售貨機則通過并行擴(kuò)展接口驅(qū)動電磁閥或者微電機驅(qū)動送出貨物,同時存儲器計數(shù)清零跳入到找零系統(tǒng),自動售貨完成。如果幣數(shù)值不滿足購貨需求則提示顧客繼續(xù)投幣或者取消購貨,取消購貨后由售貨機退出投入的幣返回初始化界面。
- 貨物檢測系統(tǒng):為了貨物供應(yīng)充足,把接觸開關(guān)和行程開關(guān)安裝在自動售貨機貨物存儲存道的下方,當(dāng)存儲存道尚有貨物時,行程開關(guān)保持閉合,CPU控制自動售貨機正常售貨。貨物供應(yīng)不足的時候,行程開關(guān)自動斷開,向CPU發(fā)出缺貨信號,經(jīng)過檢測后發(fā)送到外部電機或者電磁閥來推動外部貨物進(jìn)入存儲道達(dá)到自動添加貨物的目的,以保持貨源充足。
- 售出累計及反饋功能系統(tǒng):自動售貨機的CPU在每次售出一種貨物后,由通信系統(tǒng)向出貨存儲器中累計已經(jīng)售出的數(shù)據(jù),通過自動售貨機內(nèi)部控制的按鍵可以顯示出累計數(shù)據(jù)以掌握銷售情況。此外,由外部按鍵操作在按下自測功能鍵后,出現(xiàn)自測功能信號,可以通過檢測結(jié)果查看各個模塊的功能情況,也可以檢測自動售貨機的貨物存儲道驅(qū)動功能。
2.2 自動售貨機的功能簡介
2.2.1 自動售貨機的功能概述基本原理:通過矩陣鍵盤來選擇貨物的種類與數(shù)量過后自動售貨機提示。自動售貨機的幣識別器對所投幣進(jìn)行識別,根據(jù)金額大小然后將商品選擇權(quán)通過LCD液晶顯示給客戶,客戶按鍵選擇后,CPU控制芯片發(fā)出指令將所選擇商品從儲備料道中送達(dá)取物口。 功能描述:貨物種類一共設(shè)有8種,這8種商品通過選擇按鍵進(jìn)行選擇確認(rèn),通過數(shù)量選擇按鍵確定購買數(shù)量,價格規(guī)定為1-8元不等;幣識別器能夠識別各種。在規(guī)定的時間內(nèi),投了幾次幣后,幣能夠?qū)崿F(xiàn)自動累加功能,這樣設(shè)計會把投入的所有幣總額數(shù)目數(shù)據(jù)傳遞到中央控制元器件進(jìn)行處理;在超過規(guī)定時間后自動關(guān)閉,數(shù)據(jù)處理單元按照總數(shù)和購買的數(shù)量和價格乘積來進(jìn)行加減運算累,實現(xiàn)購買和找回的功能。 本設(shè)計中有一共有16個按鍵選擇,其中有兩個是貨物選擇鍵,有兩個是數(shù)量選擇鍵,此外還有6個幣投入鍵1個確認(rèn)鍵和1個取消鍵。 2.2.2 自動售貨機的設(shè)計思路- 本設(shè)計以這樣的工作流程開始自動售貨機的自動售貨過程:
- 啟動系統(tǒng),開始待機;
- 顧客通過按鍵選擇商品的種類以及數(shù)量并確認(rèn);
- 售貨機檢查是否有足夠的貨物并通過LCD提示等待顧客投;
- 顧客投入,售貨機自動檢測是否足夠;
- 金額足夠多,售貨機將推出顧客選擇的相應(yīng)數(shù)量的商品,若金額不足則直接退還;
- 推出商品,售貨機轉(zhuǎn)入找零系統(tǒng)退出余;
- 系統(tǒng)自動復(fù)位,完成售貨;
- 系統(tǒng)采用硬件設(shè)計思路如圖2.3:
- 采用單片機80C51作CPU;
- 采用P3.5口作為輸入端;
- 采用LCD1602液晶為顯示模塊:
- 采用4*4矩陣鍵盤連接P1口作為貨物選擇選擇端;
- 采用P0口實現(xiàn)出貨、找零功能;
圖2.3 自動售貨機系統(tǒng)原理圖 - 輸入:由于本設(shè)計模擬輸入功能是由4*4矩陣鍵盤來完成的,而鍵盤連接在80C51的P1口上,則幣投入設(shè)定為:
P1口的按鍵值為0X04的時候代表1的投入; P1口的按鍵值為0X05的時候代表5的投入; P1口的按鍵值為0X06的時候代表10的投入; P1口的按鍵值為0X07的時候代表20的投入; P1口的按鍵值為0X08的時候代表50的投入; P1口的按鍵值為0X09的時候代表100的投入; P1口的按鍵值為0X0f的時候代表購物確定“OK”按鈕; P1口的按鍵值為0X0e的時候代表購物取消“NO”按鈕; 本設(shè)計假定自動售貨機的商品種類為8種,價格為1、2、3、4、5、6、7、8元。每一個價格代表一種商品且一經(jīng)售出就由外部電機自動完成補貨。規(guī)定每次最多只能購買10個。貨物選擇: P1口的按鍵值為OX00的時候代表選擇貨物價格“price+”; P1口的按鍵值為OX01的時候代表選擇貨物價格“price-”; P1口的按鍵值為OX02的時候代表選擇貨物數(shù)量“num+”; P1口的按鍵值為OX03的時候代表選擇貨物價格“num-”; - 輸出:選擇商品由P0口控制的相應(yīng)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燈亮; - 顯示:本設(shè)計的顯示端口由P2端口和P3.2(RS)、P3.3(RW)、P3.4(E)控制LCD1602液晶來顯示購貨狀態(tài),用P0口控制LED燈表示貨物種類。通過它可以顯示購貨的種類、數(shù)量、總價以及找零等。
3 自動售貨機的硬件設(shè)計
3.1 80C51的簡介
3.1.1 80C51的基本概述由INTEL公司出品的MCS-51系列的80C51其實用性非常高。由于它是采用CHMOS的工藝技術(shù)制造,所以它是一款穩(wěn)定性很高的高性能8位單片機,是HCMOS中的最基本的產(chǎn)品之一。在制作工藝的程中不僅繼承和擴(kuò)展了先前單片機的指令系統(tǒng)和體系結(jié)構(gòu)更是把HMOS的高速高密度的技術(shù)特點和CHMOS的低功耗特點相結(jié)合。為了滿足需求,在80C51內(nèi)部置入CPU、RAM(128字節(jié))、I/O(32個雙向輸入輸出)、定時器/計數(shù)器(16位)、串行通信口、兩級中斷結(jié)構(gòu)以及片內(nèi)時鐘震蕩電路。此外,它還可以通過選擇空閑和掉電的方式用于低功耗模式來進(jìn)行工作,空閑模式下保持串行口、中斷系統(tǒng)、RAM和定時器正常工作而同時凍結(jié)CPU來保證其正常運行。掉電模式下,自動保存RAM數(shù)據(jù),時鐘震蕩停止、芯片的其他功能停止工作。  圖3.1 80C51單片機內(nèi)部基本結(jié)構(gòu) 3.1.2 80C51的引腳功能介紹
圖3.2 80C51的引腳圖 80C51單片機的40個引腳大致可分為4類:電源、時鐘、控制和I/O引腳。 - VCC - 芯片電源,接+5V;
- VSS - 接地端。
- 時鐘:XTAL1、XTAL2晶體振蕩電路反相輸入端和輸出端。
- 控制線:控制線共有4根:
- ALE/PROG:地址鎖存允許/片內(nèi)EPROM編程脈沖
- ALE功能用來鎖存P0口送出的低8位地址;
- PROG功能:片內(nèi)有EPROM的芯片,在EPROM編程期間,此引腳輸入編程脈沖。
- RST(Reset)功能是復(fù)位信號輸入端;
- VPD功能是在Vcc掉電情況下,接備用電源。
- EA/Vpp(內(nèi)外ROM選擇/片內(nèi)EPROM編程電源):
- EA功能:內(nèi)外ROM選擇端;
- Vpp功能:片內(nèi)有EPROM的芯片,在EPROM編程期間,施加編程電源Vpp。
- I/O線:80C51共有4個8位并行I/O端口即P0、P1、P2、P3口,共32個引腳;P3口還具有第二功能——用于特殊信號輸入輸出和控制信號(屬控制總線)P0口輸入時需要接上拉電阻才能置1。
在每次使用單片機之前,我們都要使單片機復(fù)位,讓CPU以及其他功能部件都處于一個確定的初始狀態(tài),以消除上一次用戶的操作對本次用戶操作的影響。51的RST引腳是復(fù)位信號的輸入端。復(fù)位信號是高電平有效,持續(xù)時間要有24個時鐘周期以上。例如:若MCS-51單片機的時鐘頻率為12MHz,則復(fù)位脈沖寬度至少應(yīng)為2us。通常,80C51的復(fù)位有自動上電復(fù)位和人工按紐復(fù)位兩種 自動上電復(fù)位電路的工作原理是:電容在通電的時候相當(dāng)于短路情況,導(dǎo)致RST引腳上的電位為高電平,這樣電容會因為電阻被充電,然后RST端逐漸降低電壓直到變?yōu)榈碗娖剑瑥亩箚纹瑱C開始正常工作。由于自動售貨機的功能需求,本次設(shè)計采用自動上電復(fù)位電路。
3.2 LCD1602字符型液晶簡介
3.2.1 LCD1602的概述LCD1602液晶也叫1602字符型液晶,它是一種專門用來顯示字母、數(shù)字、符號等的點陣型液晶模塊。它由若干個5X7或者5X11等點陣字符位組成,每個點陣字符位都可以顯示一個字符,每位之間有一個點距的間隔,每行之間也有間隔,起到了字符間距和行間距的作用,正因為如此所以它不能很好地顯示圖形(用自定義CGRAM,顯示效果也不好)。1602LCD是指顯示的內(nèi)容為16X2,即可以顯示兩行,每行16個字符液晶模塊(顯示字符和數(shù)字)。市面上字符液晶大多數(shù)是基于HD44780液晶芯片的,控制原理是完全相同的,因此基于HD44780寫的控制程序可以很方便地應(yīng)用于市面上大部分的字符型液晶。液晶顯示器以其微功耗、體積小、顯示內(nèi)容豐富、超薄輕巧的諸多優(yōu)點,在各類儀表和低功耗系統(tǒng)中得到廣泛的應(yīng)用。根據(jù)顯示內(nèi)容可以分為字符型液晶,圖形液晶。根據(jù)顯示容量又可以分為單行16字,2行16字,兩行20字等等。 3.2.2 LCD1602的硬件結(jié)構(gòu)
圖3.3 LCD1602的基本結(jié)構(gòu)圖 引腳說明:LCD1602一般是16個引腳 第1腳:VSS為接地電源。 第2腳:VDD接5V正電源。 第3腳:VEE為液晶顯示器對比度調(diào)整端,通過連接電源正負(fù)來調(diào)節(jié)對比度的強弱。當(dāng)連接電源正端時對比度最弱,反之連接負(fù)極則最高。對比度太高時會產(chǎn)生所謂的“鬼影”,在連接時可以通過接一個電位器來調(diào)整對比度。 第4腳:RS為數(shù)據(jù)命令選擇端,電平為H時選擇數(shù)據(jù)寄存器、電平為L時選擇指令寄存器。 第5腳:RW為讀寫選擇端,電平為H時進(jìn)行讀操作,電平為L時進(jìn)行寫操作。 第6腳:E端為使能端,當(dāng)E端由高電平跳變成低電平時,液晶模塊執(zhí)行命令。 第7~14腳:D0~D7為8位雙向數(shù)據(jù)線。 第15~16腳:空腳或背燈電源,15腳背光正極,16腳背光負(fù)極。 3.2.3 LCD1602的指令說明表3.1 顯示模式設(shè)置 - 顯示開/關(guān)及光標(biāo)位置如表3.2所示:
表3.2 顯示開/關(guān)及光標(biāo)位置 - 數(shù)據(jù)控制:控制器內(nèi)部設(shè)有一個數(shù)據(jù)地址指針,可以根據(jù)它來訪問內(nèi)部的全部字節(jié)。
- 數(shù)據(jù)指針設(shè)置如表3.3所示:
表3.3 數(shù)據(jù)指針設(shè)置 - 讀數(shù)據(jù):輸入時 RW 、RS、 E均等于H,輸出D0-D7=數(shù)據(jù)
- 寫數(shù)據(jù):輸入時RW=L、RS=H、D0-D7=數(shù)據(jù)、E=高脈沖,輸出無;
- 其他設(shè)置如表3.4所示:
表3.4 其他設(shè)置 - 指令1:清除顯示,指令碼為01H,清楚顯示數(shù)據(jù);
- 指令2:光標(biāo)返回到地址00H即光標(biāo)復(fù)位;
- 指令3:光標(biāo)和顯示位置設(shè)置,光標(biāo)按方向移動,高/低電平右/左移,S:屏幕上所有文字是否左移或右移,高電平移動,低電平不移動;
- 指令4:顯示開關(guān)控制D:控制整體的顯示開與關(guān),高電平表示開顯示,低電平表示關(guān)顯示。C:控制光標(biāo)的開與關(guān),高電平表示有光標(biāo),低電平表示無光標(biāo) B:控制光標(biāo)是否閃爍,高電平閃爍,低電平不閃爍;
- 指令5:光標(biāo)或顯示移位 S/C :高電平時顯示移動的文字,低電平時移動光標(biāo);
- 指令6:功能設(shè)置命令 DL:高電平時為4位總線,低電平時為8位總線 N:低電平時為單行顯示,高電平時為雙行顯示,F(xiàn):低電平時顯示5X7的點陣字符,高電平時顯示5X10的顯示字符;
- 指令7:字符發(fā)生器RAM地址設(shè)置;
- 指令8:DDRAM地址設(shè)置;
- 指令9:讀忙信號和光標(biāo)地址BF:忙標(biāo)志位,高電平表示忙,此時模塊不能接收命令或數(shù)據(jù),如果為低電平表示不忙;
3.2.4 LCD1602的基本時序操作LCD1602的基本時序操作以及對應(yīng)的狀態(tài)和輸入輸出如表3.5所示:
表3.5 基本時序操作及對應(yīng)狀態(tài)的輸入輸出 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線作為行線和列線組成的鍵盤。每個鍵的位置設(shè)置在每條行線和列線在交叉處上,可以知道4*4矩陣鍵盤共有4*4=16個按鍵。這樣的設(shè)置可以有效地提高單片機I/O口的利用率,與獨立式按鍵相比大大降低了接口占用率。當(dāng)設(shè)計過程中需要按鍵個數(shù)要求比較大時,為了釋放出I/O端口來供其他連接需要時往往采用這種矩陣式按鍵來解決問題。在矩陣鍵盤中,行列線均通過一個按鍵加以連接取代了直接交叉相連。這樣,在很大的程度上增加了按鍵數(shù),而直接將端口線連接在鍵盤上則會減少一倍的按鍵數(shù)量。在需要很多按鍵的設(shè)計中采用矩陣鍵盤明顯地比獨立式按鍵更合理,而且行列線越多效果越明顯。 3.3.2 4×4矩陣鍵盤的硬件結(jié)構(gòu)
圖3.6 4*4矩陣鍵盤的基本結(jié)構(gòu)圖 在應(yīng)用矩陣鍵盤的設(shè)計中要編譯程序的時候必定會計算出每個按鍵的接口地址是多少以方便編譯,在矩陣鍵盤的按鍵確定上我們可以采用兩種方法: 行掃描法是我們常用的一種識別按鍵的方法,這種方法需要我們逐行逐列進(jìn)行掃描查詢,分為兩個步驟: - 將連接的全部行線置為低電平,然后檢測所有列線的電位狀態(tài)。只要某一列列線電位為低電平,就表示矩陣鍵盤中這列有鍵被按下,并且在被按下的閉合的按鍵在低電平列線與全部根行線相交叉的4個按鍵之中。反之,若所有列線電位均為高電平,則表示矩陣鍵盤中沒有按鍵被按下;
- 通過前一可知按鍵處在判斷的4個按鍵之中,在確認(rèn)了有按鍵被按下后,就可進(jìn)入到確定具體被閉合鍵的操作。然后再依次將所有行線電位置為低電平,即只有一根行線為低電平,其余行線則為高電平。在確定了某根行線電位為低電平后,再逐行檢測所有列線的電平狀態(tài)。若某根列線的電位為低電平,則被按下的按鍵就處在這根列線和低電平行線的交叉處;
這種確定矩陣鍵盤按鍵的方法也是非常有效的方法,也分為兩個步驟: - 先讓所連接的端口高四位為高(1),低四位為低(0),若矩陣鍵盤上有按鍵被按下,則高四位中會有一個電平從1被翻轉(zhuǎn)到0,低四位則不會改變電平狀態(tài),然后即可確定矩陣鍵盤上被按下的按鍵的所在行的具體位置;
- 讓讓所連接的端口高四位為低(0),低四位為高(1)。若矩陣鍵盤上有按鍵被按下,則低四位中會會有一個電平1翻被轉(zhuǎn)為0,高四位則不會改變電平狀態(tài),然后即可確定矩陣鍵盤上被按下的按鍵的所在列的具體位置。綜合這兩個步驟就可以判斷出被按下的按鍵具體位置;
3.4 識別系統(tǒng)
3.4.1對硬幣的識別目前在國內(nèi),對于硬幣的識別可采用多種方法,比如激光掃描、應(yīng)變片測重量、光電管檢測大小等,然而在自動售貨機中采用結(jié)構(gòu)簡單、成本低、測量準(zhǔn)確及其非接觸測量等優(yōu)點集一身的渦流傳感器檢測。 電渦流檢測原理:以高頻的電信號通過一個線圈,在這個過程中產(chǎn)生變化的磁場通過硬幣的表面即變化磁通通過硬幣的表面,相應(yīng)地硬幣表面上產(chǎn)生電渦流, 并產(chǎn)生反向的變化磁場,以削弱原來線圈產(chǎn)生出來的磁場。然后根據(jù)激勵磁場線圈幅值的變化,通過變化的給定值即可測出真假硬幣。
圖3.7 電渦流檢測電路原理圖 圖3.7中Q2所需要的頻率由Q1、L、C4等元件組成的振蕩電路所提供,從而在C點產(chǎn)生一正弦波振蕩信號;然后再A、B 兩點外接一個傳感線圈,當(dāng)有硬幣投入通過線圈時會改變C點原有信號的幅值大小,而真假硬幣通過線圈時改變的幅值大小是不一樣的,通過對比設(shè)定給定真幣的參照值來判斷真假硬幣。 但是在實際應(yīng)用中,由于某些條件的原因會影響到電子線路元器件的判定值,比如溫漂的影響,由于材質(zhì)的差異比較小,從而導(dǎo)致信號值得差距變小,若受到的溫漂影響稍微增強一點,那么則很難精確地確檢測出不行的,為此可以采用電橋的方法來進(jìn)行改善彌補,從而減小由溫漂造成的干擾。圖中L3、L4都是激勵線圈,L3上方放置一個標(biāo)準(zhǔn)的1元硬幣或其它用低碳鋼做成的圓片,L4為檢測有無硬幣通過和是否為真假的激勵線圈,A、B兩信號通過通信模塊同時送到后續(xù)的運算放大器之中進(jìn)行相減,從而抵消因溫漂所造成的影響。
3.5 貨物選擇系統(tǒng)貨物選擇系統(tǒng)主要通過矩陣鍵盤按鍵操作來完成。4*4矩陣鍵盤是自動售貨機中的輸入裝置,價格低廉,結(jié)構(gòu)簡單,使用方便,在單片機應(yīng)用系統(tǒng)中得到廣泛地應(yīng)用。鍵盤按照接口原理可分為編碼鍵盤與非編碼鍵盤兩類,它們的主要區(qū)別是識別鍵符及給出相應(yīng)鍵碼的方法。編碼鍵盤主要是用硬件來實現(xiàn)對鍵的識別,非編碼鍵盤主要是由軟件來實現(xiàn)鍵盤的定義與識別。 CPU可以采用查詢或中斷方式了解有無將鍵輸入,并檢查是哪一個鍵按下,然后通過跳轉(zhuǎn)指令轉(zhuǎn)入執(zhí)行該鍵的功能程序,執(zhí)行完后再返回主程序。 一個完整的矩陣鍵盤控制程序應(yīng)該具備以下功能: - 能夠準(zhǔn)確檢測鍵盤上有無按鍵按下,為了消除鍵盤的按鍵機械觸點抖動可以采取硬件或軟件措施;
- 有良好的邏輯處理方法,按鍵處理可以獨立開展,在處理期間對任何一個按鍵的操作不對系統(tǒng)產(chǎn)生影響,只要按鍵按下系統(tǒng)都只執(zhí)行一次按鍵功能程序;
- 輸出的按鍵值或者鍵號可以達(dá)到很精確的狀態(tài)來正確執(zhí)行跳轉(zhuǎn)指令;
圖3.9 貨物選擇系統(tǒng)圖
3.6 出貨及找零系統(tǒng)在本系統(tǒng)中,總共設(shè)計了8種飲料,價格分別為1、2、3、4、5、6、7、8元不等,由顧客通過按鍵選擇確定后,投入售貨機可識別的幣(幣可以累加計數(shù))。當(dāng)投入的幣總值不夠購買選擇的商品總值時,售貨機顯示金取消交易并退出所投幣,如果所投幣大于等于購買選擇的商品總值時,由售貨機出貨并計算剩余幣。但是在單片機的輸出中只有高電平和低電平之分。在自動售貨機中只需要安裝一個驅(qū)動電機,待顧客投入幣后將信號送入系統(tǒng)通過相應(yīng)電路來驅(qū)動電機推出相應(yīng)商品即可。本設(shè)計只模擬實現(xiàn)此功能,負(fù)責(zé)軟件仿真,暫不考慮硬件電機方面,只作介紹。在這里,我們選用光敏三極管來實現(xiàn)這一功能。當(dāng)單片機輸出端為高電平時,則驅(qū)動二極管發(fā)光,使驅(qū)動電機電路導(dǎo)通,這時驅(qū)動電機開始工作等待信號。當(dāng)單片機輸出端為低電平時,則二極管熄滅。驅(qū)動電機電路斷開,即完成推貨動作,LED燈閃爍。售貨機通過投幣時累計的計數(shù)和購買商品的總金額進(jìn)行相減的運算,在LCD液晶上顯示出余幣的數(shù)量,顧客取走貨物后自動退還余幣。 圖3.10 出貨及找零
4 系統(tǒng)的軟件流程圖設(shè)計
4.1 自動售貨機貨物選擇流程圖本設(shè)計提供1-8元不等的貨物,分別用1-8的序號對應(yīng)貨物。當(dāng)啟動系統(tǒng)后進(jìn)入初始化界面,等待顧客選擇貨物種類和數(shù)量,按鍵“OK”則跳入投幣系統(tǒng),按鍵“NO”則返回初始化等待界面。在這里只以1號商品作為例子來講解具體流程如圖4.1: 圖4.1 自動售貨機貨物選擇流程圖
4.2 自動售貨機投幣系統(tǒng)流程圖在4*4矩陣鍵盤上模擬投入幣,若投入一種面值的幣就累加一種面值的幣,如果沒有投入幣或者投入幣值不足購買商品的話則提示繼續(xù)投幣。當(dāng)所投幣足夠時,進(jìn)入出貨找零系統(tǒng)如圖4.2所示: 圖4.2 自動售貨機投幣系統(tǒng)流程圖
4.3 自動售貨機出貨找零系統(tǒng)流程圖進(jìn)入出貨找零系統(tǒng)時,如果購買商品后沒有剩余幣則直接推出商品,若還有剩余幣則找出余幣,如圖4.3所示: 4.3 自動售貨機出貨找零系統(tǒng)流程圖
5.2 仿真結(jié)果設(shè)計好程序之后,還需要通過protues7.5和keil3.0來實現(xiàn)程序的調(diào)試和仿真,仿真結(jié)果圖如下: - 當(dāng)啟動自動售貨機時機器進(jìn)入初始化等待狀態(tài),等待按鍵輸入,如圖5.1所示:
圖5.1 初始化等待界面 - 通過按鍵price+和price-來選擇貨物的種類,然后通過num+和num-來選擇所購買貨物的數(shù)量,同時代表被選擇貨物的LED指示燈亮,如圖5.2所示:
圖5.2 選擇貨物的種類及數(shù)量 - 當(dāng)選擇好貨物后,按下“OK”鍵進(jìn)入到投幣系統(tǒng),此時機器會自動計算出所購貨物總金額,如圖5.3所示:
圖5.3 貨物選擇并計價 - 當(dāng)機器計算出總價等待顧客投入幣并按下“OK”鍵后,當(dāng)投入幣幣值大于或等于總價時就出貨并找零,若投入幣幣值小于總價則不能出貨只能按下“NO”退出所投幣或繼續(xù)投幣,如圖5.4所示:
圖5.4 出貨找零 - 當(dāng)完成以上步驟后則完成一次自動購物過程,機器自動復(fù)位回到初始化界面如圖5.5所示:
圖5.5 自動復(fù)位返回初始化界面
附錄B:系統(tǒng)總圖
單片機源程序如下: - #include<reg51.h>
- /*******************define*************************/
- #define WAIT_SALE 0 //定義1602顯示狀態(tài)
- #define NO_WATER 1
- #define INPUT_MONEY 2
- #define SALING 3
- #define CHANGE 4
- #define CANCLE 5
- #define NO_DATA 0
- #define water_change(x, y) ((x) & (~(0x01 << y))) //宏 控制水種類的切換
- #define uint unsigned int
- #define uchar unsigned char
- unsigned char water_total[] = {10, 10, 10, 10, 10, 10, 10, 10};
- unsigned char water_price[] = {1, 2, 3, 4, 5, 6, 7, 8};
- uchar code wait_sale[]="for sale"; //定義所顯示的內(nèi)容
- uchar code no_water[] = "no water!";
- uchar code price[] = "price:";
- uchar code number[] = "num:";
- uchar code sum[] = "SUM:";
- uchar code input[] = "INPUT:";
- uchar code change[] = "change:";
- uchar code put_water[] = "put water...";
- uchar code cancle[] = "cancle...";
- unsigned char e=0x00;
- sbit P2_0=P2^0; //定義接端口
- sbit P2_1=P2^1;
- sbit P2_2=P2^2;
- sbit P2_3=P2^3;
- sbit lcd_en=P3^4;
- sbit rs=P3^2;
- sbit rw = P3^3;
- /****************************************************/
- /***********************function**********************************/
- /************lcd 1602*************************/
- void lcd_1602_delay(uint z) //延時
- {
- uint x,y;
- for(x=z;x>0;x--)
- for(y=110;y>0;y--);
- }
- void lcd_1602_write_com(uchar com) //寫命令
- {
- rs=0;
- rw=0;
- P2=com;
- lcd_1602_delay(5);
- lcd_en=1;
- lcd_1602_delay(5);
- lcd_en=0;
- }
- void lcd_1602_write_data(uchar date) //寫數(shù)據(jù)
- {
- rs=1;
- rw=0;
- P2=date;
- lcd_1602_delay(5);
- lcd_en=1;
- lcd_1602_delay(5);
- lcd_en=0;
- }
- void lcd_1602_init() //LCD1602初始化函數(shù)
- {
- lcd_en=0;
- lcd_1602_write_com(0x38);
- lcd_1602_write_com(0x0e);
- lcd_1602_write_com(0x06);
- lcd_1602_write_com(0x01);
- }
- void lcd_1602_wait_sure() //顯示'OK'&'NO'界面
- {
- lcd_1602_write_com(0x80+0x40);
- lcd_1602_write_data('O');
- lcd_1602_write_data('K');
-
- lcd_1602_write_com(0x80+0x4e);
- lcd_1602_write_data('N');
- lcd_1602_write_data('O');
- }
- void lcd_1602_show_num(uint num) // 1602顯示數(shù)字的函數(shù)
- {
- if(num <= 9)
- {
- lcd_1602_write_data('0'+num);
- lcd_1602_write_data(' ');
- }else if(num >= 10 )
- {
- lcd_1602_write_data('0'+num/10);
- lcd_1602_write_data('0'+num%10);
- }
- }
- void lcd1602_info_display(uint choose, uint num_type, uint num_count) //1602顯示函數(shù)
- {
- int num;
- lcd_1602_write_com(0x01);
- switch(choose)
- {
- case WAIT_SALE:
- lcd_1602_write_com(0x80+0x03); //
- for(num = 0; num < 8; num++)
- {
- lcd_1602_write_data(wait_sale[num]);
- }
- break;
- case NO_WATER:
- lcd_1602_write_com(0x80+0x03);
- for(num = 0; num < 9; num++)
- {
- lcd_1602_write_data(no_water[num]);
- }
- break;
- case SALING:
- //price
- lcd_1602_write_com(0x80+0x00);
- for(num = 0; num < 6; num++)
- {
- lcd_1602_write_data(price[num]);
- }
- lcd_1602_write_data('0'+(num_type+1));
- lcd_1602_write_data(' ');
- //number
- lcd_1602_write_data(' ');
- for(num = 0; num < 4; num++)
- {
- lcd_1602_write_data(number[num]);
- }
- lcd_1602_show_num(num_count);
-
-
- break;
- case INPUT_MONEY:
- //need money
- lcd_1602_write_com(0x80+0x00);
- for(num = 0; num < 4; num++)
- {
- lcd_1602_write_data(sum[num]);
- }
- lcd_1602_show_num(num_type);
- //input
- lcd_1602_write_data(' ');
- lcd_1602_write_data(' ');
- for(num = 0; num < 6; num++)
- {
- lcd_1602_write_data(input[num]);
- }
- lcd_1602_show_num(num_count);
- break;
- case CHANGE:
- if(!num_count)
- {
- //put water
- lcd_1602_write_com(0x80+0x02);
- for(num = 0; num < 13; num++)
- {
- lcd_1602_write_data(put_water[num]);
- }
- }
- else
- {
- //cancle water
- lcd_1602_write_com(0x80+0x02); //取消選擇時寫命令退幣
- for(num = 0; num < 9; num++)
- {
- lcd_1602_write_data(cancle[num]);
- }
- }
- //change
- lcd_1602_write_com(0x80+0x43);
- for(num = 0; num < 7; num++)
- {
- lcd_1602_write_data(change[num]);
- }
- lcd_1602_show_num(num_type);
- break;
- default:
- break;
- }
- lcd_1602_write_com(0x0c);
- }
- void lcd_1602_saling(uint type, uint num)
- {
- lcd1602_info_display(SALING, type, num);
- lcd_1602_wait_sure();
- }
- void lcd_1602_inputing(uint need, uint input)
- {
- lcd1602_info_display(INPUT_MONEY, need, input);
- lcd_1602_wait_sure();
- }
- /*************************************************/
- void delay1(unsigned int t) // 延時函數(shù)
- {
- unsigned int i,j;
- for(i=0;i<t;i++)
- for(j=0;j<10;j++);
- }
- void out_water() //推出水函數(shù)
- {
- int i, j = 100;
- while(j--)
- {
- for(i = 0; i < 8; i++)
- {
- P0 = water_change(0xff, i);
- delay1(50);
- }
- }
- P0 = 0xff;
- }
- unsigned char key(void) //按鍵響應(yīng)函數(shù)
- {
- unsigned char key,key1,key2;
- P1=0XF0;
- if((P1&0XF0)!=0XF0) //ked down
- {
- delay1(10); //延時去抖動
- if((P1&0XF0)!=0XF0) //仍然有鍵按下
- {
- key1=P1&0XF0; //讀入列線值
- P1=0X0F;
- key2=P1&0X0F;
- key=key1|key2;
- switch(key) //按鍵映射
- {
- case 0xee: e=0x00;break;
- case 0xde: e=0x01;break;
- case 0xbe: e=0x02;break;
- case 0x7e: e=0x03; break;
- case 0xed: e=0x04;break;
- case 0xdd: e=0x05;break;
- case 0xbd: e=0x06;break;
- case 0x7d: e=0x07; break;
- case 0xeb: e=0x08;break;
- case 0xdb: e=0x09;break;
- case 0xbb: e=0x0a;break;
- case 0x7b: e=0x0b; break;
- case 0xe7: e=0x0c;break;
- case 0xd7: e=0x0d;break;
- case 0xb7: e=0x0e;break;
- case 0x77: e=0x0f; break;
- }
- while((P1&0X0F)!=0X0F);
- delay1(10);
- return 1;
- }
- }
- return 0;
- }
- /*********************************************************************/
- /*****************main********************************/
- void main()
- {
- unsigned char ok = 0, water_type = -1, water_is_choose = 0;
- char water_count = 0;
- unsigned int flag = 0, money = 0, need_money;
- lcd_1602_init();
- again:
- ok = 0; water_type = -1; water_count = 0; water_is_choose = 0;
- flag = 0; money = 0; need_money = 0;
- lcd1602_info_display(WAIT_SALE, 0, 0);
- while(!ok) //choose the water and count
- {
- flag=key();
- if(flag)
- {
- switch(e) //不同的按鍵進(jìn)行不同的操作
- {
- case(0x00):
- water_type++;
- water_is_choose = 1;
- if(water_type == 8)
- {
- water_type = 0;
- }
- P0 = water_change(0xff, water_type);
- break;
- case(0x01):
- water_type--;
- water_is_choose = 1;
- if(water_type == -1 || water_type == -2)
- {
- water_type = 7;
- }
- P0 = water_change(0xff, water_type);
- break;
- case(0x02):
- if(water_is_choose == 1)
- {
- water_count++;
- if(water_count > water_total[water_type])
- {
- water_count = water_total[water_type];
- }
- }
- break;
- case(0x03):
- if(water_is_choose == 1)
- {
- water_count--;
- if(water_count < 0)
- {
- water_count = 0;
- }
- }
- break;
- case(0x0e): // 返回
- goto again;
- break;
- case(0x0f): // 確認(rèn)
- if(water_is_choose == 1)
- {
- ok=1;
- }
- break;
- default:
- break;
- }
- lcd_1602_saling(water_type, water_count);
- }
- }
-
- //choose ok: calculate the money //計算總數(shù)
- need_money = water_count * water_price[water_type];
- lcd_1602_inputing(need_money, money);
- ok = 0;
- while(!ok) //get money
- {
- flag=key();
- if(flag)
- {
- switch(e)
- ……………………
- …………限于本文篇幅 余下代碼請從51黑下載附件…………
復(fù)制代碼
Keil5代碼與Proteus8.8仿真下載:
仿真程序文檔.7z
(756.5 KB, 下載次數(shù): 114)
2022-4-25 20:57 上傳
點擊文件名下載附件
|