基于51单片机的出租车计价系统(附protues仿真)

概要

链接:https://pan.baidu.com/s/1kyaGEdhLMkj_CHjE-wPmDw 
提取码:yyw9

总体结构如下图所示:

达到的任务需求:

不同的计价收费标准

     白天 1元/公里    晚上  2元/ 公里   途中等待(30s)1元/30s

等待显示P-

功能切换按键(3个)

 启动计价开关      数据复位      白天/晚上/等待模式转换

数码管显示

显示单价,等待模式,公里数,总价格

单价可自定义

 

整体架构流程

整体结构简洁,使用51单片机的定时器0中断完成速度的模拟,及各价的统计。

硬件整体由三个部分组成:单片机最小系统部分,数码管显示及驱动部分,按键扫描部分。

1、单片机最小系统部分。由于单片机的P0口是开漏输出,数码管驱动部分使用到了,所以应加上上拉电阻。晶振提供系统运行的12mhz的时钟信号。复位电路采用高电平复位,该系列51单片机RST引脚高电平超过4个机器周期则自动复位。

2、数码管显示及驱动部分。采用动态扫描原理,使用74hc138译码器进行位选功能,使用74hc245锁存器提高io口的驱动能力。动态扫描是人眼视觉感知的一种现象,它利用人眼的暂留视觉效应来实现多位数码管等显示设备的连续显示。动态扫描原理:在数码管动态扫描中,每个数码管仅在一个瞬间被激活,并持续一段非常短的时间,随后迅速切换到下一个数码管。这个切换的速度远远快于人眼的感知,所以我们感觉到所有数码管都在同时显示。对于数码管动态扫描,一般的频率范围在几十赫兹到几千赫兹之间。

3、为了简化电路,按键按下直接连接到低电平,使用查询方式检测按键是否按下和使用软件进行按键的消抖。按键消抖是指在按下或释放按钮时,电路会因为机械部件的震动或弹跳而导致短暂的多次触发。这种现象可能会对电路造成误触发或干扰。本设计采用软件滤波方式消抖,在软件层面对按键信号进行滤波处理,通过设置延时时间或使用状态机等方式来确保只接收有效的稳定信号,忽略短暂的抖动信号。

软件整体包含主程序和中断程序两个部分:

主程序原理:单片机开始运行程序之后开启定时器,开启定时器中断。并进行相关的初始化和寄存器的设置工作。然后进入一个循环执行函数。该循环过程主要进行四个工作:

1、检测按键是否按下,如果按键1按下,则让开始/暂停标志位在0-1之间顺序切换。如果按键2按下,则让模式标志位在1-3之间顺序切换。

2、单价的显示。判断当前模式为1-3的任意一种。若模式标志位为1则显示单价1.0,若模式标志位为2则显示单价2.0,若模式标志位为3则显示p-表示为等待模式。

3、路程价格的计算。计算白天行驶的路程和晚上的路程总和,计算三种模式下记录的价格总价。

4、路程和价格显示。通过除和取余计算处每一位该显示的数字,并设置数码管显示数组的值,之后借助中断进行数码管的扫描显示。

中断函数原理:

1、进行按键扫描函数及消抖函数的执行。

2、进行数码管的扫描函数。

3、判断开始标志位。如果为1,则进行下一步的判断。为0,则退出中断。

4、判断模式标志。如果模式标志位为1,则白天相应的相对值加1.则夜晚相应的相对值加1.则等待相应的相对值加1.这些相对值用于主函数循环里的距离和价格计算。

系统基本流程图:

注:详细代码可查看技术细节或链接文件!

技术名词解释

通过使用定时器中断,可以实现定时操作,例如定时延时、周期性任务执行、计时等。定时器中断能够提高系统的实时性和精确性,方便进行时间敏感的操作和监控任务。

  • 动态扫描:
  • 动态扫描是人眼视觉感知的一种现象,它利用人眼的暂留视觉效应来实现多位数码管等显示设备的连续显示。动态扫描原理:在数码管动态扫描中,每个数码管仅在一个瞬间被激活,并持续一段非常短的时间,随后迅速切换到下一个数码管。这个切换的速度远远快于人眼的感知,所以我们感觉到所有数码管都在同时显示。对于数码管动态扫描,一般的频率范围在几十赫兹到几千赫兹之间。
  • 按键消抖:

  • 按键消抖是指在按下或释放按钮时,电路会因为机械部件的震动或弹跳而导致短暂的多次触发。这种现象可能会对电路造成误触发或干扰。下面是按键消抖的原因:机械弹跳:当按键被按下或释放时,机械接触部件(如弹簧或金属片)可能会因为弹性或惯性导致短时间内反复接触和分离。这种机械弹跳会引起瞬时断开和闭合,导致短暂的多次触发信号。机械震动:按键在使用过程中可能会受到外界震动或物理振动的影响,导致按键触发信号的误差。这种机械震动也可能会引起按键的短暂弹跳,由此产生消抖问题。电气干扰:电路中存在的电磁干扰或噪音也可能导致按键触发信号的抖动。这些干扰可以干扰信号线路或开关元件的接触,引起不稳定的触发信号。以上原因造成的按键消抖问题可能会对电子系统的精确控制、数据输入和用户体验等方面带来不便和困扰。为了解决按键消抖问题,通常会采取以下方法:软件滤波:在软件层面对按键信号进行滤波处理,通过设置延时时间或使用状态机等方式来确保只接收有效的稳定信号,忽略短暂的抖动信号。硬件滤波:在电路设计中使用外部元件(如电容器或电阻器)来滤除按键信号中的抖动。这些元件可以通过RC滤波电路来减小信号的上升和下降时间,从而防止抖动信号的误触发。使用独立的按键消抖芯片:有一些专门的按键消抖芯片可用于处理按键信号,通过内部的消抖算法来确保稳定触发信号的传递。

  • 该设计采用软件滤波方式

  • 定时器中断:

    在基于51系列单片机的系统中,定时器中断是一种常见的中断方式,可用于实现定时功能。以下是51单片机定时器中断的一般原理:51单片机的定时器:51单片机通常具有一个或多个定时器,例如8051芯片具有定时器/计数器T0和T1。这些定时器是硬件模块,有自己的计数器和控制寄存器。定时器控制寄存器:每个定时器都有一个相应的控制寄存器,用于设置定时器的工作模式、计数值、中断使能等。通过编程控制这些寄存器的值,可以配置定时器的工作方式和定时周期。中断使能和优先级:定时器中断通常需要在中断控制寄存器中使能。此外,还需要设置中断优先级,以确定定时器中断与其他中断的优先级关系。在51单片机中,中断优先级是通过中断优先级寄存器IP进行设置。定时器中断触发:定时器根据设定的定时周期开始计数。当定时器的计数值达到预设的值时,定时器的中断标志位会被置位。此时,如果相应的定时器中断使能位被设置,定时器中断就会触发,进入中断服务程序。中断服务程序:中断服务程序是在定时器中断触发时执行的一段特殊代码。在中断服务程序中,可以执行一系列操作,如更新显示、数据处理、状态改变等。在处理完中断服务程序后,程序会返回到中断被触发时的执行位置,继续执行主程序。

技术细节

详细的工程代码:

主函数代码

#include <STC89C5xRC.H>
#include "Timer0.h"
#include "Key.h"
#include "Nexie.h"

/*********可自己更改单价******/
#define P_DAY 0.01	  //白天距离0.01元/10米
#define P_Night 0.02  //晚上0.02元/10米
#define P_wait 0.03  //wait时0.03元/10米

unsigned int D_distance=0,N_distance=0,Wait=0;	
unsigned int mode_flag=1;		//白天-夜晚-等待--模式标志											
unsigned char KeyNum=0,temp;//按键检测相关变量
unsigned char start=0;			//开始、暂停标志位

unsigned int distance;			//存放总距离数
float price;								//存放总价格

/**
  * @brief  按键扫描及相关标志位改变
  * @param  无
  * @retval 无
  */
void keyscan(void)
{
	KeyNum=Key();
	if(KeyNum)
		temp=KeyNum;
	switch (temp)
	{
		case 1:start++;if(start>1){start=0;}temp=0;break;
		case 2:mode_flag++;if(mode_flag>3){mode_flag=1;};temp=0;break;
	}
}
/**
  * @brief  单价及等待模式显示
  * @param  无
  * @retval 无
  */
void danjia_show()
{
	switch (mode_flag)
	{
		case 1:Nixie_SetBuf(1,11);Nixie_SetBuf(2,0);temp=0;break;
		case 2:Nixie_SetBuf(1,12);Nixie_SetBuf(2,0);temp=0;break;
		case 3:Nixie_SetBuf(1,20);Nixie_SetBuf(2,21);temp=0;break;//20为显示P,21显示-
	}
}
/**
  * @brief  公里数显示
  * @param  无
  * @retval 无
  */
void Distance_show()
{
	distance=D_distance+N_distance;
	Nixie_SetBuf(3,distance/10000%10);
	Nixie_SetBuf(4,distance/1000%10+10);
	Nixie_SetBuf(5,distance/100%10);
}

/**
  * @brief  价格显示
  * @param  无
  * @retval 无
  */
void Price_show()
{
	unsigned int ge=0,shi=0,xiao=0;//第一次缩放的变量,个位十位小数位
	
	price=D_distance*P_DAY+N_distance*P_Night+Wait*P_wait;
															//把float类型的price强制转换为int类型
	ge=(unsigned int)price%10;	//防止unsigned char类型导致数据丢失
	shi=(unsigned int)price/100%10;
	xiao=(unsigned int)price/10%10;
	
	Nixie_SetBuf(8,(unsigned char) ge);					//显示个位
	Nixie_SetBuf(7,(unsigned char) xiao+10);		//显示小数位
	Nixie_SetBuf(6,(unsigned char) shi);			//显示十位
}

void main()
{
	Timer0_Init();
	while(1)
	{
		keyscan();
		danjia_show();
		Distance_show();
		Price_show();
	}
}



void Timer0_Routine() interrupt 1
{
		static unsigned int T0Count1,T0Count2,T0Count3,T0Count4;
		TL0 = 0x18 ;		//设置定时初始值
		TH0 = 0xFC ;		//设置定时初始值
		T0Count1++;T0Count2++;T0Count3++;T0Count4++;//每1ms进行一次++;
			
		if(T0Count1>=20)//20ms调用一次Key_Loop()函数
		{
			T0Count1=0;
			Key_Loop();
		}
			if(T0Count2>=2)//2ms调用一次Nixie_Loop()函数
		{
			T0Count2=0;
			Nixie_Loop();
		}
		if(T0Count3>=100)//100ms加10m
			
		{
			T0Count3=0;
			if(start==1)
			{
				if(mode_flag==1)
				{
					D_distance++;
				}
				else if(mode_flag==2)
				{
					N_distance++;
				}
			}
		}
		if(T0Count4==500)//0.0333块钱每秒
		{
			T0Count4=0;
			if(start==1)
			{
				if(mode_flag==3)
				Wait++;
			}	
		}	
}

数码管扫描代码:

#include <STC89C5xRC.H>
#include "Delay.h"
unsigned char Nixie_Buf[9]={0,10,10,10,10,10,10,10,10};//0是为了照应loop中i=1
unsigned char NixieNumb[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,
													0xBF,0x86,0xDB,0xCF,0xE6,0xED,0xFD,0x87,0xFF,0x6F,
														0x73,0x40};//P和-
													
/**
  * @brief  外部调用对数组Nixie_Buf[9]赋值
  * @brief  使得loop函数循环对scan函数赋值时读到相应数据
  * @brief  未赋值的数组位即为10;对应NixieNumb[]中的0;
  * @param Table,Number在Nixie_Buf数组的下标 赋的值(该值即对应NixieNumb[]中的值)
  * @retval 无
  */
void Nixie_SetBuf(unsigned char Table,Number)
{
	Nixie_Buf[Table]=Number;
}

/**
  * @brief  数码管显示
  * @param  Table,Number显示的位置 显示的数字
	* @param 两参数由定时器按周期扫描时给定
  * @retval 无
  */
void Nixie_Scan(unsigned char Table,Number)
	{
		P0=0x00;//消隐
		switch(Table)
		{//     P2_4=C-4,P2_3=B-2;P2_2=A-1
			case 1:P24=1;P23=1;P22=1;break;
			case 2:P24=1;P23=1;P22=0;break;
			case 3:P24=1;P23=0;P22=1;break;
			case 4:P24=1;P23=0;P22=0;break;
			case 5:P24=0;P23=1;P22=1;break;
			case 6:P24=0;P23=1;P22=0;break;
			case 7:P24=0;P23=0;P22=1;break;
			case 8:P24=0;P23=0;P22=0;break;
		}
		P0=NixieNumb[Number];//不用delay再消隐,因为定时器进来有时间间隔
		
	}
/**
  * @brief  定时器调用的函数
  * @param  无
  * @retval 无
  */
	void Nixie_Loop(void)//定时器每进一次就调用一次scan函数
	{										//使得八个数码管依次快速显示
		static unsigned char i=1;
		Nixie_Scan(i,Nixie_Buf[i]);
		i++;
		if(i>=9)i=1;
	}

按键扫描代码:

#include <STC89C5xRC.H>
unsigned char Key_KeyNumber;

/**
  * @brief  外部调用主函数
  * @param 无
  * @retval Temp 传出在loop函数中得到的Key_KeyNumber值;
  */
unsigned char Key(void)
{
	unsigned char Temp=0;
	Temp=Key_KeyNumber;
	Key_KeyNumber=0;//对全局变量清0,防止下次中断进来
									//没按键按下仍传出上次按键值
	return Temp;
}

/**
  * @brief  定时器按周期进来一次则检测四个按键是否有按下的
  * @param 无
  * @retval keyNumber得到键值在loop中调用并返回值给NowState
  */
unsigned char Key_GetState()
{
unsigned char keyNumber=0;
	if(P30==0)keyNumber=1;
	if(P31==0)keyNumber=2;
return keyNumber;
}
/**
  * @brief  定时器周期扫描函数
  * @param 无
  * @retval 无
  */
void Key_Loop(void)
{
	static unsigned char NowState,LastState;//静态变量防止下次进来销毁
	LastState=NowState; 
	NowState=Key_GetState(); //调用按键按下函数,并把按下的值赋给NowState
	if(LastState==1 && NowState==0)//如果上次为1按下状态,这次为松开状态0则可以确定1键按下了
	{
	 Key_KeyNumber=1; //把得到的键值从中断中传出,放到全局变量Key_KeyNumber中
	}
	if(LastState==2 && NowState==0)
	{
	 Key_KeyNumber=2;
	}
}

定时器初始化代码:

#include <STC89C5xRC.H>

/**
  * @brief  定时器初始化
  * @param 无
  * @retval 无
  */
	void Timer0_Init(void)		//1毫秒@12.000MHz
	{
		AUXR &= 0x7F;			//定时器时钟12T模式
		TMOD &= 0xF0;			//设置定时器模式
		TL0 = 0x18;				//设置定时初始值
		TH0 = 0xFC;				//设置定时初始值
		TF0 = 0;				//清除TF0标志
		TR0 = 1;				//定时器0开始计时
		ET0=1;
		EA=1;
		PT0=0;
	}
	
	

小结

注:本设计仿真使用的是protues的8.15版本,如不能打开请检查版本序号!

该设计基本完成了设计任务的要求,显示单价,等待模式,公里数,总价格都能稳定显示,按键不会误触发,有消抖处理。欢迎大家的讨论交流和指正!也希望能给你带来一些帮助!

  • 5
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 基于51单片机的电子时钟项目需要通过Keil编写程序,并使用Proteus进行仿真电路搭建。该项目主要包括以下几个步骤: 1. 硬件搭建:按照电子时钟的设计需求,连接51单片机和相关的电子元件,如晶体振荡器、数码管、按键等。通过Proteus软件,可以将这些元件连接起来,搭建出完整的电路。 2. Keil程序编写:使用Keil软件,编写51单片机的C语言程序。该程序需要实现时钟的功能,包括时、分、秒的显示和计时、调整时间、闹钟功能等。通过编程,可以控制数码管的显示,以及对按键进行响应。 3. Proteus仿真:将编写好的程序通过Proteus软件连接至搭建好的电路。进行仿真测试时,可以通过模拟时钟的不同状态,调试和验证编写的程序的正确性和稳定性。仿真过程中,可以检查数码管的显示情况,以及程序对按键输入的响应。 4. 优化和调试:根据仿真过程中的结果,对程序进行优化和调试。可能需要根据具体的需求,修改程序中的一些逻辑或代码,确保电子时钟的功能正常运行,并符合设计要求。 总的来说,基于51单片机的电子时钟项目需要通过Keil编写程序,并结合Proteus进行仿真电路搭建和测试。通过这样的开发流程,可以实现一个功能完善、稳定可靠的电子时钟。 ### 回答2: 基于51单片机的电子时钟keil程序和protues仿真电路组成了一个完整的设计方案。 首先,keil程序是用于开发51单片机的集成开发环境,它提供了编译、调试和仿真等功能,能够帮助程序员快速开发出51单片机的应用程序。在电子时钟的设计中,我们可以使用keil来编写单片机的程序代码,实现时钟的各种功能。 其次,protues是一款电子设计自动化软件,它提供了电子电路仿真和PCB布局设计等功能,能够帮助我们快速验证电路的正确性。在电子时钟的设计中,我们可以使用protues来建立电子时钟的仿真电路,验证单片机代码的正确性和稳定性。 基于51单片机的电子时钟设计,我们可以使用keil来编写单片机的程序代码,实现时钟的各种功能,包括时间显示、报时功能、闹钟功能等。通过keil的编译、调试和仿真功能,我们可以验证代码的正确性和稳定性。 在电路设计方面,我们可以使用protues来建立电子时钟的仿真电路,通过仿真可以验证电路的正确性和稳定性,包括时钟电路、数码管驱动电路、按钮输入电路等。通过protues的电路仿真功能,我们可以检查电路设计的错误和漏洞,提前解决电路问题。 基于51单片机电子时钟keil程序和protues仿真电路的组合,我们可以全面验证电子时钟的功能和性能,确保设计的准确性和可靠性,为最终实现一个完整的电子时钟设计提供了有力的支持。 ### 回答3: 基于51单片机的电子时钟keil程序和protues仿真电路可以实现以下功能: 1. 显示当前时间:我们可以使用数码管或LCD显示模块来显示当前的时、分和秒。通过编写相应的程序,我们可以从单片机的时钟源获取当前时间,并将其转换为可以在数码管或LCD上显示的格式。 2. 时间调整功能:可以通过按钮或旋钮等输入设备来调整电子时钟的时间。当用户按下或旋转输入设备时,我们可以响应用户的操作,并对时钟的时间进行相应的调整。 3. 闹钟功能:我们可以设置闹钟功能,让电子时钟在特定的时间点发出警报声。通过在程序中设置闹钟时间和警报声的播放方式,我们可以实现这一功能。 4. 温湿度监测:如果我们希望电子时钟能够同时监测室内的温度和湿度,我们可以连接温湿度传感器,并在程序中读取传感器的数据。然后,我们可以将这些数据显示在数码管或LCD上。 程序开发过程中,我们可以使用keil来编写51单片机的程序,通过keil提供的调试工具来测试和调试程序的正确性。同时,我们可以在protues设计仿真电子时钟的电路,包括单片机、显示模块、输入设备和传感器等所有的硬件组件。这样,我们可以在protues中验证电路的功能和效果,并进行性能优化和调试。当电路和程序都满足我们的要求后,我们就可以将程序烧录到实际的硬件上,并使用它作为一台完整的电子时钟了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SFR-小曾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值