基于51的计算器C程序
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Calculator_c51
/*******************************************************************************
************ LABCENTER ELECTRONICS ************ ************ Proteus VSM Sample Design Code ************
************ Integer Calculator ( 2K Code Limit) ************
*******************************************************************************/
#include <intrins.h>
#include <reg51.h>
#include "calc.h"
//Variables
static data LONG lvalue;
static data LONG rvalue;
static data CHAR currtoken;
static data CHAR lasttoken;
static data CHAR lastpress;
static xdata CHAR outputbuffer[MAX_DISPLAY_CHAR];
VOID main (VOID)
//Initialise our variables and call the
//Assembly routine to initialise the LCD display.
{ lvalue = 0;
rvalue = 0;
currtoken = '=';
lasttoken = '0';
initialise(); // Initialize the LCD
calc_output(OK);
calc_evaluate();
}
VOID calc_evaluate()
{ CHAR data key;
INT data i;
CHAR xdata number[MAX_DISPLAY_CHAR];
CHAR xdata *bufferptr;
// Clear the buffer before we start.
for (i = 0; i <= MAX_DISPLAY_CHAR; i++)
{ number[i] = ' ';
}
bufferptr = number;
for (;;)
{ key = calc_getkey();
if (calc_testkey(key))
// Key test positive for digit so we read it into the
// buffer and then write the buffer to the screen/LCD.
// Size limit the number of digits - allow for termination
// and possible negative results.
{ if (bufferptr != &number[MAX_DISPLAY_CHAR - 2]) { *bufferptr = key;
calc_display(number);
bufferptr++;
}
}
else
// Key is an operator so pass it to the function handlers.
// If we are just after startup or cancel then assign to lvalue
// otherwise assign to rvalue.
{
//Assign the value.
if (lasttoken == '0')
{ lvalue = calc_asciidec (number);}
else
{ rvalue = calc_asciidec (number);}
//Clear the number buffer.
bufferptr = number;
for (i = 0;i <= MAX_DISPLAY_CHAR; i++)
{ number[i] = ' '; }
//Process the Operator.
currtoken = key;
if (currtoken == 'C')
{ calc_opfunctions(currtoken); }
else
{ calc_opfunctions(lasttoken); }
// Clear the outputbuffer for reuse on next operation.
for (i = 0;i <= MAX_DISPLAY_CHAR;i++)
{ outputbuffer[i] = ' ';}
bufferptr = number;
// Handle the equals operation here for brevity.
// All we need do is preserve the previous operator in
// lasttoken.
if (currtoken != 0x3D) lasttoken = currtoken;
}
lastpress = key;
}
}
VOID calc_opfunctions (CHAR token)
// Handle the operations. Lvalue holds the result and we test for
// consecutive operator presses.
{ CHAR data result;
switch(token)
// Add.
{ case '+' : if ((currtoken == '=' ) || ((lastpress >= 0x30) && (lastpress <=0x39)))
{ lvalue += rvalue;
result = calc_chkerror(lvalue);
}
else
{ result = SLEEP; } break;
// Subtract.
case '-' : if ((currtoken == '=' ) || ((lastpress >= 0x30) && (lastpress <=0x39)))
{ lvalue -= rvalue;
result = calc_chkerror(lvalue);
}
else
{ result = SLEEP;} break;
// Multiply.
case '*' : if ((currtoken == '=' ) || ((lastpress >= 0x30) && (lastpress <=0x39)))
{ lvalue *= rvalue;
result = calc_chkerror(lvalue);
}
else
{ result = SLEEP;} break;
// Divide.
case '/' : if ((currtoken == '=' ) || ((lastpress >= 0x30) && (lastpress <=0x39)))
{ if (rvalue)
{ lvalue /= rvalue;
result = calc_chkerror(lvalue);
}
else
{ result = ERROR;}
}
else
{ result = SLEEP;} break;
// Cancel.
case 'C' : lvalue = 0;
rvalue = 0;
currtoken = '0';
lasttoken = '0';
result = OK; break;
default : result = SLEEP;
}
calc_output(result);
}
/************************************************************************ ***** Utility Routines *****
***************************/
INT calc_chkerror (LONG num)
// Check upper and lower bounds for the display.
// i.e. 99999999 and -99999999.
{ if ((num >= -9999999) && (num <= 9999999))
return OK;
else
return ERROR;
}
VOID calc_output (INT status)
// Output according to the status of the operation.
// *Sleep* is used for the first op press after a full cancel
// or on startup.
{ switch (status)
{ case OK : calc_display(calc_decascii(lvalue)); break;
case SLEEP : break;
case ERROR : calc_display("Exception "); break;
default : calc_display("Exception "); break;
}
}
LONG calc_asciidec (CHAR *buffer)
// Convert the ASCII string into the floating point number.
{ LONG data value;
LONG data digit;
value = 0;
while (*buffer != ' ')
{ digit = *buffer - 48;
value = value*10 + digit;
buffer++;
}
return value;
}
CHAR *calc_decascii (LONG num)
// A rather messy function to convert a floating
// point number into an ASCII string.
{ LONG data temp = num;
CHAR xdata *arrayptr = &outputbuffer[MAX_DISPLAY_CHAR];
LONG data divisor = 10;
LONG data result;
CHAR data remainder,asciival;
INT data i;
// If the result of the calculation is zero
// insert a zero in the buffer and finish.
if (!temp)
{ *arrayptr = 48;
goto done;
}
// Handle Negative Numbers.
if (temp < 0)
{ outputbuffer[0] = '-';
temp -= 2*temp;
}
for (i=0 ; i < sizeof(outputbuffer) ; i++)
{ remainder = temp % divisor;
result = temp / divisor;
// If we run off the end of the number insert a space into
// the buffer.
if ((!remainder) && (!result))
{ *arrayptr = ' ';}
// We're in business - store the digit offsetting
// by 48 decimal to account for the ascii value.
else
{ asciival = remainder + 48;
*arrayptr = asciival;
}
temp /= 10;
// Save a place for a negative sign.
if (arrayptr != &outputbuffer[1]) arrayptr--;
}
done: return outputbuffer;
}
CHAR calc_testkey (CHAR key)
// Test whether the key is a digit or an operator. Return 1 for digit, 0 for op. { if ((key >= 0x30) && (key <= 0x39))
{ return 1;}
else
{ return 0;}
}
/************************************************************************
***** I/O Routines *****
***********************/
CHAR calc_getkey (VOID)
// Use the input routine from the *Keypad_Read* assembly file to
// Scan for a key and return ASCII value of the Key pressed.
{ CHAR data mykey;
do mykey = input();
while (mykey == 0);
return mykey;
}
VOID calc_display (CHAR buf[MAX_DISPLAY_CHAR])
// Use the Output and Clearscreen routines from the
// *LCD_Write* assembly file to output ASCII values to the LCD.
{ INT data i = 0;
clearscreen();
for (i ; i <= MAX_DISPLAY_CHAR ; i++)
{ if (buf[i] != ' ')
{ output(buf[i]); }
}
}
/******************************************************************************
************ LABCENTER ELECTRONICS ************ ************ Proteus VSM Sample Design Code ************
************ Integer Calculator ( 2K Code Limit) ************
********************************************************************************/ typedef void VOID;
typedef int INT;
typedef unsigned short WORD;
typedef char CHAR;
typedef unsigned char BYTE;
typedef float FLOAT;
typedef double DOUBLE;
typedef long LONG;
// Define the maximum number of ASCII characters that can fit
// on the display.
#define MAX_DISPLAY_CHAR 9
//Error handling status.
enum ERROR { OK = 0, SLEEP = 1, ERROR = 2};
/************************************************************************
***** FUNCTION PROTOTYPES *****
******************************/
VOID calc_evaluate();
//Operator indirect function.
VOID calc_opfunctions (CHAR token);
//Utility functions.
CHAR calc_testkey (CHAR ch);
LONG calc_asciidec (CHAR *buffer);
CHAR *calc_decascii (LONG num);
INT calc_chkerror (LONG num);
VOID calc_output (INT status);
// I/O functions.
CHAR calc_getkey (VOID);
VOID calc_display (CHAR buf[MAX_DISPLAY_CHAR]);
//Assembly Function prototypes.
void initialise();
char input ();
char output(char ch);
void clearscreen();
/*******************************************************************************
************ LABCENTER ELECTRONICS ************ ************ Proteus VSM Sample Design Code ************
************ Integer Calculator ( 2K Code Limit) ************
*******************************************************************************/
NAME KEYPAD
; This routine will read a character press from the keypad and return it in R7.
;Set up Segments for the Input Routine - No parameters.
?PR?input?KEYPAD SEGMENT CODE
PUBLIC input
?DT?input?KEYPAD SEGMENT DATA
RSEG ?DT?input?KEYPAD ; Local Variable Segment for Output Routine. KEY_ROW1 equ 0EFh
KEY_ROW2 equ 0DFh
KEY_ROW3 equ 0BFh
KEY_ROW4 equ 07Fh
keyflags: ds 16
RSEG ?PR?input?KEYPAD ; Code Segment for Output Routine.
input:
keyscan:push DPH
push DPL
mov R0,#keyflags ; R0 addresses the key toggle bytes
mov R1,#KEY_ROW1 ; R1 address the keyboard row address
mov R2,#4 ; R2 counts rows
ksrow: mov P2,R1 ; Set row address to port P2
nop
mov A,P1 ; Read column data from port P1
mov R3,#4 ; R3 counts keys per row
anl A,#3Fh
ks0: rrc A ; Move next bit into carry
mov R4,A ; R4 preserves the row data
jc ks1 ; Jump if key not pressed
mov A,@R0 ; Test if key already pressed
mov @R0,#1 ; Flag pressed anyway
jz ksnew ; Jump if key newly pressed
jmp ks2
ks1: mov @R0,#0 ; Flag key as not pressed
ks2: inc R0 ; Loop for next key in this row
mov A,R4
djnz R3,ks0
mov A,R1 ; Jiggle R1 to address next row
rl A
mov R1,A
djnz R2,ksrow
clr A
mov R7,A ; Return zero - no (new) key press.
jmp ksend
ksnew: mov DPTR,#keycodes ; We've found a new key since last time:
mov A,R0 ; The key flag address (ordinal) is in R0
clr C
subb A,#keyflags
movc A,@A+DPTR
mov R7,A ; Move the Key into R7 to be returned.
ksend: mov P2,#0FFh
pop DPL
pop DPH
ret
;Data tables for returned row bits
keycodes: db '7','8','9', '/'
db '4','5','6', '*'
db '1','2','3', '-'
db 'C','0','=', '+'
END
/******************************************************************************
************ LABCENTER ELECTRONICS ************ ************ Proteus VSM Sample Design Code ************
************ Integer Calculator ( 2K Code Limit) ************
********************************************************************************/ NAME LCD
;Set up Code Segment and exports:
LCD SEGMENT CODE
RSEG LCD
PUBLIC _output
PUBLIC initialise
PUBLIC clearscreen
;LCD Register Addresses.
LCD_CMD_WR equ 00h
LCD_DATA_WR equ 01h
LCD_BUSY_RD equ 02h
LCD_DATA_RD e qu 03h
LCD_PAGE equ 80h
;LCD Commands
LCD_CLS equ 1
LCD_HOME equ 2
LCD_SETMODE equ 4
LCD_SETVISIBLE equ 8
LCD_SHIFT equ 16
LCD_SETFUNCTION equ 32
LCD_SETCGADDR equ 64
LCD_SETDDADDR equ 128
; Initialisation Routine for the LCD display.
initialise: mov A,#030h ;1 line, 8 bits
call wrcmd
mov A,#LCD_SETVISIBLE + 4
call wrcmd
mov A,#LCD_SETDDADDR+15 ; Start at right hand side of the display
call wrcmd
mov A,#LCD_SETMODE + 3 ; Automatic Increment - Display shift left.
call wrcmd
ret
; We move the parameter (held in R7) into the Accumulator prior to writing it.
_output: mov A,R7
call wrdata
ret
;Clears the LCD display and sets the initialisation conditions.
clearscreen: mov A,#LCD_CLS
call wrcmd
mov A,#LCD_SETDDADDR + 15
call wrcmd
ret
;*****************************
;******** SUBROUTINES ********
;*****************************
;Sub routine to write command:
wrcmd: mov P2,#LCD_PAGE
mov R0,#LCD_CMD_WR
movx @R0,A
jmp wtbusy
; Subroutine to Write a Character to the LCD Display.
wrdata: MOV P2,#LCD_PAGE
MOV R0,#LCD_DATA_WR
MOV A,R7
MOVX @R0,A
; Subroutine to wait for a busy clear.
wtbusy: MOV R1,#LCD_BUSY_RD
MOVX A,@R1
JB ACC.7,wtbusy
ret
END
11。