当前位置:文档之家› c语言简易计算器程序(带解释)

c语言简易计算器程序(带解释)

/* Easy calculator
K4:+ K8:- K12:* K16:/ K14:Clear K15:=
*/
/*程序要点已做说明,初学者肯定有用*/
#include

typedef unsigned char uint8; /*0-255,signed char -128到127*/
typedef unsigned int uint16; /*0-65535*/
typedef unsigned long uint32; /*0到2^32-1*/
typedef char int8;
typedef int int16;
typedef long int32;

sbit KeyIn1 = P2^4;
sbit KeyIn2 = P2^5;
sbit KeyIn3 = P2^6;
sbit KeyIn4 = P2^7;
sbit KeyOut1 = P2^3;
sbit KeyOut2 = P2^2;
sbit KeyOut3 = P2^1;
sbit KeyOut4 = P2^0;
sbit ADDR0 = P1^0;
sbit ADDR1 = P1^1;
sbit ADDR2 = P1^2;
sbit ADDR3 = P1^3;
sbit ENLED = P1^4;
sbit BUZZ = P1^6;

#define FADD 10
#define FSUB 11
#define FMUL 12
#define FDIV 13
#define FRES 14
#define FEQU 15

#define KEY_DELAY 300
#define BUZ_DELAY 80

code uint8 Ledcode[13]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff,0xbf,0x86};
/*0-9; 关闭;负号; E 共13个*/

uint8 Led_n=0; /*数码管编号*/
uint8 Led_buf[6]; /*数码管显示时编码*/
float Tmp1=0, Tmp2=0; /*暂存用实数变量,范围?float 32位 6或7位有效数字(这里为7位),范围 -3.4*10(-38)~3.4*10(38)*/
int8 C_flag=0; /*键按下的标志,如果不为运算符号就为0,是运算符就不为0*/

/*延时*/
void delay(uint16 n)
{
while (n--);
}


/*蜂鸣器发声,在BUZ_DELAY 80一段时间内让蜂鸣器端延时电平高低变化,发出声响*/
void buzzer_sound(void)
{
uint16 i;

for (i=0; i{
BUZZ = ~BUZZ;
delay(100);
}

BUZZ = 1;
}


/* 按键扫描,返回所按下的键值。若两键同时反应,即使前键返回一个值,后键也返回一个值,val也等于后者*/
int8 scan_key(void)
{
int8 val=-1; /*未按下任何键,val=-1*/

KeyOut1 = 0; /*选中1列*/
KeyOut2 = 1;
KeyOut3 = 1;
KeyOut4 = 1;
if (KeyIn1 == 0)
{
delay(KEY_DELAY);
if (KeyIn1 == 0)
val = 1;
}
if (KeyIn2 == 0)
{
delay(KEY_DELAY);
if (KeyIn2 == 0)
val = 2;
}
if (KeyIn3 == 0)
{
delay(KEY_DELAY);
if (KeyIn3 == 0)
val = 3;
}
if (KeyIn4 == 0)
{
delay(KEY_DELAY);
if (KeyIn4 == 0)
val = FADD; /*10*/
}
while ((KeyIn1 == 0)||(KeyIn2 == 0)||(KeyIn3 == 0)||(KeyIn4 == 0));
/*当键按下未放开时就等待,以免扫描后面时影响后面造成错误*/

KeyOut1 = 1;
KeyOut2 = 0; /*选中2列*/
KeyOut3 = 1;
KeyOut4 = 1;
if (KeyIn1 == 0)
{
delay(KEY_DELAY);
if (KeyIn1 == 0)
val = 4;
}
if (KeyIn2 == 0)
{
delay(KEY_DELAY);
if (KeyIn2 == 0)
val = 5;
}
if (KeyIn3 == 0)
{
delay(KEY_DELAY);
if (KeyIn3 == 0)
val = 6;
}
if (KeyIn4 == 0)
{
delay(KEY_DELAY);
if (KeyIn4 == 0)
val = FSUB; /*11*/
}
while ((KeyIn1 == 0)||(KeyIn2 == 0)||(KeyIn3 == 0)||(KeyIn4 == 0));

KeyOut1 = 1;
KeyOut2 = 1;
KeyOut3 = 0; /*选

中3列*/
KeyOut4 = 1;
if (KeyIn1 == 0)
{
delay(KEY_DELAY);
if (KeyIn1 == 0)
val = 7;
}
if (KeyIn2 == 0)
{
delay(KEY_DELAY);
if (KeyIn2 == 0)
val = 8;
}
if (KeyIn3 == 0)
{
delay(KEY_DELAY);
if (KeyIn3 == 0)
val = 9;
}
if (KeyIn4 == 0)
{
delay(KEY_DELAY);
if (KeyIn4 == 0)
val = FMUL; /*12*/
}
while ((KeyIn1 == 0)||(KeyIn2 == 0)||(KeyIn3 == 0)||(KeyIn4 == 0));

KeyOut1 = 1;
KeyOut2 = 1;
KeyOut3 = 1;
KeyOut4 = 0; /*选中4列*/
if (KeyIn1 == 0)
{
delay(KEY_DELAY);
if (KeyIn1 == 0)
val = 0;
}
if (KeyIn2 == 0)
{
delay(KEY_DELAY);
if (KeyIn2 == 0)
val = FRES; /*14清除*/
}
if (KeyIn3 == 0)
{
delay(KEY_DELAY);
if (KeyIn3 == 0)
val = FEQU; /*15等于*/
}
if (KeyIn4 == 0)
{
delay(KEY_DELAY);
if (KeyIn4 == 0)
val = FDIV; /*13*/
}
while ((KeyIn1 == 0)||(KeyIn2 == 0)||(KeyIn3 == 0)||(KeyIn4 == 0));

if (val > 0)
buzzer_sound(); /*蜂鸣器响一段时间停止鸣叫*/

return val;
}


/*验证数据有效性,太大则返回1,小则返回0,这里太大其实受6管中最高位显示正负,其余5个管子显示
最大11111的限制,数值大于11111就是100000以上已经不能显示了*/
bit check_num(float f_num)
{
if (f_num >= 100000)
return 1;

return 0;
}



/*制作数码管错误标志*/
void make_led_error(void)
{
int8 i;

for (i=0; i<5; i++)
Led_buf[i] = Ledcode[10]; /*11号元素0xFF,即6管都不显示,或者说显示清零*/
Led_buf[5] = Ledcode[12]; /*第六管显示“E"*/
}



/*制作数码管整数数据,i_num是输入的数值*/
/*与显示浮点数不同的是不用扩大必要的倍数以去除小数点,且不用记住有效小数的小数位数*/
void make_led_inumber(int32 i_num) /*形参是整数型的*/
{
bit s_flag=0; /*sflag是i_num为负值时的标志,输入负值时等于1*/
int16 sit; /*权位*/
int8 i;

if (i_num < 0)
{
s_flag = 1;
i_num = -i_num;
}

ET0 = 0;
for (i=4, sit=10000 ; i>=1; i--, sit/=10)
/*特殊for语句,如果i>=1,那么执行for语句内部,执行完再i--,且sit=10000/10=1000*/
{
if (i_num >= sit) /*i_num输入数值的绝对值若大于10000,那么跳出for循环*/
break;
Led_buf[i] = Ledcode[10]; /*若不大于10000,Led_buf[4]=Ledcode[10]=0xFF不显示*/
i_num -= i_num/sit*sit; /*i_num=i_num-i_num/sit*sit*/
}
for (;i>=1; i--, sit/=10)
{
Led_buf[i] = Ledcode[i_num/sit];
i_num -= i_num/sit*sit;
}
Led_buf[0] = Ledcode[i_num] & 0x7F;
if (s_flag) /*如果标志为1,负值*/
Led_buf[5] = Ledcode[11]; /*第六个数码管显示负号*/
else
Led_buf[5] = Ledcode[10]; /*不显示*/
ET0 = 1;

}




/*制作数码管浮点数据,f_num是要显示的答案,最多7位有效数字,正负范围很大,可不用考虑不在范围*/
void make_led_fnumber(float f_num) /*形

参是float型,范围虽很大,但不计小数点最大共7位(十进制形式)*/
{
bit s_flag=0; /*负数标志,f_num负为1*/
int32 num; /*num用于暂存浮点数据f_num经过扩大后的值*/
int16 sit; /*-32768=2^-16到32767,*/
int8 i, decimal, dot_sit=0;
/*运算扩大后的num值,得到f_num的小数位数(人为选定最大4位),存在decimal; dot_sit是f_num的可以显示
的位数0-4,因为并不是所有的小数位可供显示,有总共只可显示5位有效值的限制,*/


if (f_num < 0)
{
s_flag = 1;
f_num = -f_num;
}

num = (int32)(f_num*10000+0.5); /*扩大10000倍,小数点后五位可能起作用,最后一位四舍五入*/
/*16位65535,32位4294967296,扩大10000倍应该就是32位范围*/
for (decimal=4; decimal>0; decimal--)
{
if (num % 10 != 0) /*注意num已被强行转化为整数*/
break; /*对应一个decimal值=4,除以10若有余数,说明小数点后第四位非零,跳出整个for循环,执行 dot_sit = decimal,即小数点位数为4位*/
num /= 10; /*如果num对10取余无余数,那么num缩小10倍,例1.234X10000--12340,对10取余无余数,所以就
是1234,这时候小数点位数为3位,下一次就有余数了,直接跳出for循环,小数点位数为3位 decimal=3,以此类推,得到真正位数decimal,真正小数点最大位数为4,最小为0*/
}

//dot_sit = decimal; /*逗点位置暂时等于decimal小数点位数,这一句不要也可以,是多余的,相当于给初值*/

if (f_num >= 10000) /*若fnum大于10000,满五位,可以显示小数点位数为0,例10001.5678取五位为10001,说明人为把一长串 数字规正为 5位有效数字。其实设为大于1111也可以,有五位就舍去小数位,总共有6管,1管显示正负5管显示有效值。
其实已经暗含了同时最大不大于100000,即输入输出值不能为6为有效位*/
dot_sit = 0;


else if (f_num >= 1000)
/*若fnum大于等于1000小于10000,f_num的小数位数大于等于1,则可以显示小数点位数为1,例如1001.5*/
if (decimal >= 1)
dot_sit = 1;

else if (f_num >= 100) /*若fnum大于等于100小于1000,且小数点位数大于等于2,则可以显示小数点位数为2,例如101.51*/
if (decimal >= 2)
dot_sit = 2;

else if (f_num >= 10) /*若fnum大于等于10小于100,且小数点位数大于等于3,则可以显示小数点位数为3,例如11.511*/
if (decimal >= 3)
dot_sit = 3;

else if (f_num >= 0) /*若fnum大于等于0小于10,且小数点位数大于等于4,则可以显示小数点位数为4,例如1.5112*/
if (decimal >=

4)
dot_sit = 4;
/*最后一句else if 是新增的,源程序漏掉了此句*/

for (i=0; if_num *= 10;

num = (int32)(f_num+0.5); /*扩大后的浮点数取整数,即末位四舍五入*/

/*上段程序编的不好,理念难理解!明白f_num是浮点数,先判断小数点后取四位后真正有效位是小数点后多少位;结合
f_num数值大小判断小数点后可显示几位有效位(因为小数点前和后总共能显示5位),有几位可以显示小数点位,就扩大相应倍数转化为整数,显示时适当放置小数点位置就可以了*/

/*目的是把fnum转化为num并记住原可以显示几位小数点,准备送去显示*/


ET0 = 0; /*关中断小门*/



for (i=4, sit=10000; i>=1; i--, sit/=10) /*最高位为0就不显示的问题处理*/
/* i可以认为是显示数组元素的编号,第五个数码管就是Led_buf[4],sit是权,位的权*/
{
if (num >= sit) /*扩大后若值大于10000,说明五位的最高位不为0,那么最高位不用考虑不显示的问题*/
break;
if (i == dot_sit)
/*同样如果i等于可以显示的小数点后位数,说明i对应小数点前一个数码管(其实就是显示小数点的管子),这个管子对应位
不管是不是0都要显示,所以不用消零处理,所以结束for循环*/
break;

/*上述四句仅仅是部分不执行后句的条件。如果num小于10000,说明最高位第5个数码管的数值将是0,所以让最高位不显示*/
/* i=4,sit=10000时如果num小于10000,说明高位第5个数码管的数值将是0,所以让高位第5个数码管不显示*/
/*同理i=3,sit=1000时如果num小于1000,说明高位第5,4个数码管的数值将是0,所以让次高位第4个数码管也不显示*/
/*同理i=2,sit=100时如果num小于100,说明高位第5,4,3个数码管的数值都将是0,所以让高位第3个数码管也不显示*/
/*同理i=1,sit=10时如果num小于10,说明高位第5,4,3,2个数码管的数值都将是0,所以让高位第2个数码管也不显示*/
/*当i=0时,前面已不存在显示零的问题,所以到i=0结束*/
/*需要消零处理的情况:扩大后的数值占不满全部5位时需要消除前面的零;不需要消零处理的情况:如果i等于可以显示的小数点后位数,Led_buf[i]是小数点前一个数码管(其实就是显示小数点的管子),不管该管是不是0都要显示,所以不用消零处理*/

Led_buf[i] = Ledcode[10]; /*ledbuf[i]=不显示;num小于1000并且i不等于小数点后位数,ledbuf[i]=不显示*/


num -= num/si

t*sit; //?????可去掉
/*?该句作用是让num成为去掉最高位的尾数值:num/sit的商是最高位,最高位乘以sit等于略去了高位以后部分
比如:12345/10000*10000并不是12345,而是依照顺序运算先得到商1,再乘以10000后为10000,num=12345-10000=2345 ,
即num=2345。这一句看起来可以去掉,没有现实意义,在下一个for中才有意义*/

}






for ( ; i>=1 ; i--, sit/=10) /*i未给初值而已,如果i=0,不执行for语句*/
{
Led_buf[i] = Ledcode[num/sit];
/*这里与上一个for形成对应,即默认num >= sit即num>=10000 或1000或100或10,且对应默认i=4,3,2,1*/
/*这里还要默认:num < sit即num<10000 或1000或100或10时,且对应默认上一个for中途跳出时的i的值=3,2,1*/
/*所以还要生成零高位以后的显示值,他的sum与i值接续自上一个for循环的跳出值*/
/*扩大后若值大于10000,第一个for不起作用*/

num -= num/sit*sit;

/*以num=12345为例:
第一次i=4,开始sit=10000,Led_buf[4] = Ledcode[12345/10000]=1,num=12345-10000=2345 ,即num=2345*/
/*下一次i=3,sit=1000, Led_buf[3] = Ledcode[2345/1000]=2; num=2345-2345/1000x1000=345*/
/*下一次i=2,sit=100, Led_buf[2] = Ledcode[345/100]=3; num=345-345/100x100=45*/
/*下一次i=1,sit=10, Led_buf[1] = Ledcode[45/10]=4; num=45-45/10x10=5*/


/*下一次i=0,sit=1, Led_buf[0] = Ledcode[5/1]=5; num=5-5/1X1==0,这时前面按理应该是
for ( ; i>=0 ; i--, sit/=10),但这样会出现i=-1,而i是uint8型无符号数,这样会出错*/

}
Led_buf[0] = Ledcode[num];

/*这一句出现是因为i是uint8型数据,它是无符号数,不能为-1,所以在前面for语句中只能 i>=1,只能到Led_buf[1] ,
这一句单独给出Led_buf[0]=Ledcode[5]是对上面for语句的补充*/




Led_buf[dot_sit] &= 0x7F; /*dot_sit是f_num的可以显示的位数,0-4,即给第一管到第五管适当位置添加小数点*/
if (s_flag) /*s_flag为负数标志,f_num负为1*/
Led_buf[5] = Ledcode[11]; /*为负数,最高位显示“-”*/
else
Led_buf[5] = Ledcode[10]; /*正数最高位不显示,由此可见,六个管子,只能显示一个符号和5位有效值*/
ET0 = 1;

}


/*6个数码管显示buf0-5*/
void show_num(uint8 *buf)
{
ENLED = 1;
switch (Led_n)
{
case 0:
ADDR0 = 0;
ADDR1 = 0;
ADDR2 = 0;
P0 = buf[0];
break;
case 1:
ADDR0 = 1;
ADDR1 = 0;
ADDR2 = 0;
P0 = buf[1];
break;
case 2:
ADDR0 = 0;
ADDR1 = 1;
ADDR2 = 0;
P0 = buf[2];
break;
case 3:
ADDR0 = 1;
ADDR1 = 1;
ADDR2 = 0;
P0 = buf[3];
break;
case 4:
ADDR0 = 0;
ADDR1 = 0;
ADDR2 = 1;
P0 = buf[4];
break;
case 5:
ADDR0 = 1;
ADDR1 = 0;
ADDR2 = 1;
P0 = buf[5];
break;
}
ENLED = 0;

if (Led_n >= 5)
Led_n = 0

;
else
Led_n++;
}



/*计算程序*/
void calculate(int8 key_val)
{
float ans;
bit err=0; /*位,用于表示正确或错误,错误为1,正确为0*/

if ((key_val >= FADD) && (key_val <= FDIV)) /*键值大于等于10或小于等于13*/
{
C_flag = key_val; /*说明是运算符按下,计算标记=键值*/
}
else if (key_val == FEQU) /*键值等于15制作显示数据ledbuf,运算数用完也清零*/
{
switch (C_flag) /*如果cflag=”1“,也就是说是运算符按下就不等于零,就满足=”1“*/
{
case FADD: ans = Tmp1+Tmp2; break; /*结果anser=Tmp1+Tmp2*/
case FSUB: ans = Tmp1-Tmp2; break;
case FMUL: ans = Tmp1*Tmp2; break;
case FDIV: if (Tmp2 == 0)
{
err = 1; /*错误标志为1*/
break;
}
else
{
ans = Tmp1/Tmp2;
break;
}
}
if (check_num(ans)) /*如果检查答案后答案太大,大于100000,那么check_num=1*/
err = 1; /*如果check_num=1,答案超范围,所以错误标志也为1*/
if (err)
make_led_error(); /*制作数码管错误标志,第六管显示"E",前句两处出现err,只要一处使err=1就显示错误“E"*/
else
make_led_fnumber(ans);/*把switch答案制作成浮点显示数据ledbuf,这里加减不用浮点显示,乘除要浮点显示*/
C_flag = 0; /*运算符标志用完后清零*/
Tmp1 = 0; /*运算数用完也清零*/
Tmp2 = 0;
}
else if (key_val == FRES) /*键值等于14显示清零,运算数用完也清零*/
{
make_led_fnumber(0); /*重点:把0制作成显示浮点数据ledbuf,即显示清零*/
C_flag = 0;
Tmp1 = 0;
Tmp2 = 0;
}
else /*如果键值不是10-15,转入该分支,*/
{
if (C_flag == 0) /*如果cflag=0,那么说明不是运算符按下*/
{
ans = Tmp1;
ans *= 10;
ans += key_val;
if (check_num(ans)) /*如果检查答案后答案太大,大于100000,那么check_num=1*/
return; /*返回调用函数处,返回空,等价于调用函数到此为止*/
else
Tmp1 = ans;
make_led_inumber(Tmp1); /*重点句:把tmp1制作成数码管整数显示数据*/
}
else /*如果cflag=1,那么说明是运算符按下*/
{
ans = Tmp2;
ans *= 10;
ans += key_val;
if (check_num(ans)) /*如果检查答案后答案太大,大于100000,那么check_num=1*/
return; /*没有值返回空,编写者其实也想中断函数执行返回调用函数处而break只退出所循环体*/
else
Tmp2 = ans;
make_led_inumber(Tmp2);/*重点句:把tmp2制作成数码管整数显示数据*/
}

}

}

/*例如先按9,不是运算符,执行else if下的大else,temp1=0,ans=9=temp1,显示9;接着若按下加号,执行第一个if,
tmp2=0,cflag=1,再按下8,执行小else,ans=8=temp2,显示8;接着按下等号,cflag已记住运算符加,执行
else if (key_

val == FEQU),得到答案ans,make_led_fnumber(ans)显示17;如果按下清除键,显示0 */
/*疑问:运算数几十几是怎样输入的?以temp1为例,先输入键值5,由于初值temp1=0,所以运算后tem1=5,再输入成了50+5=55,就是如此*/



main()
{
int8 key_val;

ADDR3 = 1; /*上面数码管打开*/
ENLED = 0;

make_led_inumber(0); /*把0制作成数码管整数显示数据*/

TMOD = 0x01; /*定时器0 16位定时器,2ms*/
TH0 = 0xF8;
TL0 = 0xCC;
TR0 = 1;
ET0 = 1;
EA = 1;

while (1)
{
key_val = scan_key(); /*扫描键值并返回*/
if (key_val == -1) /*未按下任何键,scan_key()=val=-1*/
continue; /*如果未按下任何键就结束本次(跳过后面计算函数)又回到while(1)第一句*/
calculate(key_val); /*如果有键按下就执行计算程序calculate(key_val)*/

}
}


void time0() interrupt 1
{
TR0 = 0;
TH0 = 0xF8;
TL0 = 0xCC;

show_num(Led_buf); /*6个数码管显示buf0-5,每2ms显示更新一次*/

TR0 = 1;
}



/*浮点型连同小数点显示数码管上:3.2546先乘10000=32546记b
char x0000,x000,x00,x0,x;
int xxxx,xxx,xx;
x0000=b/10000;
xxxx=b%10000;
x000=xxxx/1000;
xxx=xxxx&1000;
.....................

一位位弄出来(程序里x0000x000x00x0x)用数码管显示程序挨显示上去*/

/*ABC三键ABC分别代表123
当先按下A键三数码管显示001再按下B键数码管显示012再按下C键数码管显示123
当先按下B键三数码管显示002再按下A显示021再按下B键显示212
假设三数码管显示缓冲区由高位低位分别32H,31H,30H.
当检测有按键按下时候,把31H单元值赋给32H,把30H单元值赋给31H,再把当前键值赋给30H.了.
管有多少按键,管按键键值多少都实现,有点像计算器输入*/
/*单精度浮点数 float 怎样输出送三位数码管显示 小数点位 由PLC控制发送数据float a;
int b
b=a*10; //乘10化整
s1=b/100; //十位
s2=b/10%10 +10; //位加小数点
s3=b%10; //小数*/

/*输入最多5位,显示所有值小于100000,否则最高位显示E,不能连续运算,每算一次需清零一次,
减法负号在最高位不超过四位小数,输入的只能是整数,其它乱用也可能乱码*/


相关主题
文本预览
相关文档 最新文档