51單片機程序設計——簡易出租車計步器

寫在開頭:因爲程序文件是先上傳的,裏面有些註釋掉的內容是與程序完全無關,是本人原本寫程序時無用的部分,可以不用理會,另外有些變量沒有寫註明具體的作用,只看程序比較難理解,我在寫這篇文章時有所解釋,可以再這裏看一下。

簡易出租車計步器的要求是:

(1)LED數碼管或LCD顯示路程和價格,價格單位爲元,小數點後保留1位;路程單位爲km,小數點後保留1位;
(2)起步價6.0元,3km;3km以後按1.2元/km計費;
(3)其他功能(創新部分),如顯示時間、溫溼度等;
(4)系統調試、分析、總結與功能實現。

這個程序我用的是郭天祥的51單片機,只用了那個開發板上的資源,沒有連接任何外設,做出來的東西也相當簡單,用到了單片機上的六位數碼管。

一、設計思路

郭天祥的51單片機上P3.4到P3.7連接的是獨立鍵盤s1到s4,這個程序只用到s1,數碼管六位都用到了。數碼管的前三位顯示路程,後三位顯示價格。路程的初始值爲0.0,每按一次s1路程加0.5公里。價格和路程之間是一個分段函數的關係,路程<3.0時,價格始終等於6.0;路程>大於3.0時,價格始終等於(路程-3)*1.2+6.0。
做完程序用wps做了個流程圖,如下:
在這裏插入圖片描述
下面是幾個效果圖,是從當時拍的視頻裏截的圖,我現在身邊沒有郭天祥的51單片機只能用以前拍的了。
在這裏插入圖片描述
1、路程初始值
在這裏插入圖片描述
2、按了三次按鍵後,路程爲1.5,價格仍爲6
在這裏插入圖片描述
3、按了七次按鍵後,路程爲3.5,價格爲12.0(3+0.5 * 1.2=12.0)

二、程序作用簡單描述,頭文件,函數聲明,段選、位選、按鍵使用I/O口定義,全局變量定義

首先使用宏定義:

	#define uchar unsigned char
	#define uint unsigned int

之後可減少編程負擔,一般寫單片機程序是都要在開頭寫上。
然後定義P3.4到P3.7分別爲獨立鍵盤s1到s4,其中只用到s1,其餘三個按鍵想添加其他功能時可以酌情使用。

	sbit s1=P3^4;	 //定義P34口是s1
	sbit s2=P3^5;	 //定義P35口是s2
	sbit s3=P3^6;	 //定義P36口是s3
	sbit s4=P3^7;	 //定義P37口是s4

定義了三個數碼管顯示使用的數組:

uchar code table1[]={0x3f,0x06,0x5b,0x4f,
					 0x66,0x6d,0x7d,0x07,
				     0x7f,0x6f,0x77,0x7c,
			 	 	 0x39,0x5e,0x79,0x71};

第一個是顯示從0到F的數組,當然只用到其中的0到9

	uchar code table2[]={0xbf,0x86,0xdb,0xcf,
						 0xe6,0xed,0xfd,0x87,
						 0xff,0xef,0xf7,0xfc,
						 0xb9,0xde,0xf9,0xf1};		

第二個也是顯示0到F的數組,但是顯示每個數時都會同時顯示這個數碼管右下角的小數點

	uchar code table3[]={0x7c,0x79,0x6e,0x3f,0x37,0x5e};

第三個算是個字母數組,總共6個元素,分別代表b,e,y,o,n,d,是用來在價格超過99.9,價格爆表時顯示在整個六位數碼管上的。
其餘全局變量與函數聲明的內容會在後面提到,這裏先不說了

下面是主函數前包括程序作用簡單描述,頭文件,函數聲明,段選、位選、按鍵使用I/O口定義和全局變量定義的內容的程序:

//按鍵一次加0.5公里,左三數碼管顯示路程,右三數碼管顯示對應的價格,
//每按一次獨立按鍵k1加0.5公里,可更改
#include<reg52.h>
#include<intrins.h>
#define uchar unsigned char
#define uint unsigned int
	
sbit s1=P3^4;	 //定義P34口是s1
sbit s2=P3^5;	 //定義P35口是s2
sbit s3=P3^6;	 //定義P36口是s3
sbit s4=P3^7;	 //定義P37口是s4

uchar code table1[]={0x3f,0x06,0x5b,0x4f,
					 0x66,0x6d,0x7d,0x07,
					 0x7f,0x6f,0x77,0x7c,
				 	 0x39,0x5e,0x79,0x71};
uchar code table2[]={0xbf,0x86,0xdb,0xcf,
					 0xe6,0xed,0xfd,0x87,
					 0xff,0xef,0xf7,0xfc,
					 0xb9,0xde,0xf9,0xf1};
uchar code table3[]={0x7c,0x79,0x6e,0x3f,0x37,0x5e};

sbit dula=P2^6;
sbit wela=P2^7;
										
uchar key;										
uint journey=0,cost=60;										
uint shi,ge,td;
uchar i,a=0xfe;
										
void delayms(uint);
void displayj(uint,uint,uint);
void displayc(uint,uint,uint);
void beyond();										
unsigned char keypros1();

三、程序主體內容

所有的數值都乘以了10,在數碼管顯示時將數字的百位當做十位,十位當做個位並加上小數點,個位當做十分位。如果不習慣這種用法,可以自己把程序裏的數字除以10來使用。

void main()
{
	while(1)
	{
		if(keypros1())//檢測是否按下s1
			journey+=5;					//每按一次按鍵,加5÷10=0.5公里,可更改,例,改爲3,即爲一次加0.3公里
		if(journey>30)
			cost=60+(journey-30)*12;
			
		if(journey<100)
			shi=0;
		else shi=journey/100;
		ge=journey/10%10;
		td=journey%10;
		displayj(shi,ge,td);
		
		if(cost<100)
			shi=0;
		else if(cost>999)
			beyond();
		else shi=cost/100;
		ge=cost/10%10;
		td=cost%10;		
		if(cost<100)
			shi=0;
		else shi=cost/100;		
		displayc(shi,ge,td);
		
		
	}
}

主函數主體爲一個while函數,裏面的內容無限循環,journey代表路程,爲全局變量,初始值爲0,keypros1()檢測s1是否按下,若按下返回值1,反之,返回值0。

unsigned char keypros1()
{
  key=0;
	if(s1==0)		  //檢測按鍵s1是否按下
	{	
		delayms(10);   //消除抖動 一般大約10ms
		if(s1==0)	 //再次判斷按鍵是否按下
		{	
			key=1;		
		}
		while(!s1);	 //檢測按鍵是否鬆開
	}
	if(key==1)
			return 1;
	else return 0;
}

由於本人在編程序時把所有的數字都乘以了10,也就是說,按鍵一次加0.5變成了加5,主函數循環檢測是否按下按鍵,返回值爲1,則journey加5。
全局變量cost,即價格的初始值爲60,journey>30之前,不改變cost的值,journey>30之後,始終有cost=(journey-30)*12+60。
計算完journey和cost的值後,會將journey的百位賦給shi,十位賦給ge,個位賦給td。
shi代表十位,ge代表個位,td代表十分位,是英文tenths digit 的縮寫。
然後使用函數displayj(shi,ge,td);這個函數是顯示路程的函數:

void displayj(uint shi,uint ge,uint td)
{
	if(shi!=0)//shi爲0時不顯示
	{
		dula=1;
		P0=table1[shi];
		dula=0;
		P0=0xff;	
		wela=1;
		P0=0xfe;
		wela=0;
		delayms(1);
	}
	dula=1;
	P0=table2[ge];
	dula=0;
	P0=0xff;	
	wela=1;
	P0=0xfd;
	wela=0;
	delayms(1);
	
	dula=1;
	P0=table1[td];
	dula=0;
	P0=0xff;	
	wela=1;
	P0=0xfb;
	wela=0;
	delayms(1);	
}

當shi爲0時不顯示,如下圖:
在這裏插入圖片描述
shi和td顯示時使用數組table1,即沒有小數點的數組;
ge顯示時使用數組table2,即有小數點的數組;

之後再將cost的百位賦給shi,十位賦給ge,個位賦給td。然後使用displayc(uint shi,uint ge,uint td);與顯示journey的函數大同小異:

void displayc(uint shi,uint ge,uint td)
{
	if(shi!=0)
	{
		dula=1;
		P0=table1[shi];
		dula=0;
		P0=0xff;	
		wela=1;
		P0=0xf7;
		wela=0;
		delayms(1);
	}

	dula=1;
	P0=table2[ge];
	dula=0;
	P0=0xff;
	wela=1;
	P0=0xef;
	wela=0;
	delayms(1);
	
	dula=1;
	P0=table1[td];
	dula=0;
	P0=0xff;	
	wela=1;
	P0=0xdf;
	wela=0;
	delayms(1);	
}

如果一直按s1直到cost>999,即顯示值>99.9,則爆表。這時如果不加以限制,十位將會顯示十六進制,個位仍是十進制,完全亂套。
所以,主函數中也會循環檢測cost是否大於999,當cost>999時,會進入beyond()函數,函數如下:

void beyond()
{
	while(1)
	{
		for(i=0;i<=5;i++)
		{
			dula=1;
			P0=table3[i];
			dula=0;
			P0=0xff;

			wela=1;
			P0=a;						   //a初值爲0xfe,第一個數碼管亮
			wela=0;
			delayms(1);				   //延時1s
			a=_crol_(a,1);			       //循環左移
			if(a==0xbf)					   
				a=0xfe;
		}
	}
}	

進入這個函數後就會六位數碼管無限循環顯示beyond,不再返回主函數,效果如下:
在這裏插入圖片描述

四、仿真圖

畫仿真圖時,因爲連接數碼管的是P0,所以必須外接上拉電阻,如果是其他I/O口則不必。
在這裏插入圖片描述
另外,程序中如果先段選後位選在實物上顯示很正常,在proteus中顯示的就有問題,這個程序也一樣;反之,先位選後段選就都沒有問題了。當然,如果有充足的I/O口,也可以使用兩個8位I/O口分別段選和位選,這樣proteus上顯示絕對沒有問題,只是有些畫蛇添足了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章