STM32 串口计算器程序,支持基本运算、科学计算、括号运算、变量存储等功能,具有友好的交互界面和错误处理。
一、系统架构
STM32 串口计算器系统架构:
├── 硬件层
│ ├── STM32F103C8T6 微控制器
│ ├── USB-TTL 串口模块
│ ├── 按键输入(可选)
│ └── OLED 显示屏(可选)
├── 驱动层
│ ├── USART 串口驱动
│ ├── 定时器延时
│ ├── GPIO 控制
│ └── 中断管理
├── 计算引擎层
│ ├── 表达式解析器
│ ├── 词法分析器
│ ├── 语法分析器
│ ├── 计算执行器
│ └── 错误处理
├── 功能模块层
│ ├── 基本运算(+ - * / %)
│ ├── 科学计算(sin/cos/tan/log/exp/sqrt)
│ ├── 括号运算
│ ├── 变量存储(A-Z)
│ ├── 常量定义(π, e)
│ └── 历史记录
└── 用户界面层├── 命令行交互├── 帮助系统├── 结果显示└── 错误提示
二、硬件连接
2.1 硬件配置
STM32F103C8T6 USB-TTL模块 功能
PA9 (TX1) -----> RX 串口发送
PA10 (RX1) <----- TX 串口接收
GND -----> GND 地线
3.3V -----> VCC 电源(可选)
2.2 可选外设
OLED显示屏:I2C接口(PB6-SCL, PB7-SDA)
按键输入:PB0-PB3(数字0-9,运算符)
LED指示:PC13(计算状态指示)
三、核心代码实现
3.1 头文件定义 (calculator.h)
#ifndef CALCULATOR_H
#define CALCULATOR_H#include "stm32f10x.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <ctype.h>// 计算器配置
#define MAX_EXPR_LEN 256 // 最大表达式长度
#define MAX_TOKEN_LEN 32 // 最大标记长度
#define MAX_STACK_SIZE 64 // 最大栈大小
#define MAX_HISTORY 10 // 历史记录数量
#define MAX_VARIABLES 26 // 变量数量(A-Z)// 错误代码
#define CALC_OK 0
#define CALC_ERR_SYNTAX 1 // 语法错误
#define CALC_ERR_DIV_ZERO 2 // 除零错误
#define CALC_ERR_OVERFLOW 3 // 溢出错误
#define CALC_ERR_UNDEF_VAR 4 // 未定义变量
#define CALC_ERR_INVALID_OP 5 // 无效操作符
#define CALC_ERR_PAREN 6 // 括号不匹配// 标记类型
typedef enum {TOKEN_NUMBER = 0, // 数字TOKEN_OPERATOR, // 运算符TOKEN_FUNCTION, // 函数TOKEN_VARIABLE, // 变量TOKEN_CONSTANT, // 常量TOKEN_LPAREN, // 左括号TOKEN_RPAREN, // 右括号TOKEN_END // 结束标记
} TokenType;// 运算符优先级
typedef enum {OPERATOR_PLUS = 1, // +OPERATOR_MINUS, // -OPERATOR_MULTIPLY, // *OPERATOR_DIVIDE, // /OPERATOR_MODULO, // %OPERATOR_POWER // ^
} OperatorType;// 函数类型
typedef enum {FUNC_SIN = 1, // sinFUNC_COS, // cosFUNC_TAN, // tanFUNC_LOG, // logFUNC_LN, // lnFUNC_EXP, // expFUNC_SQRT, // sqrtFUNC_ABS // abs
} FunctionType;// 标记结构
typedef struct {TokenType type; // 标记类型union {double number; // 数字值OperatorType op; // 运算符FunctionType func; // 函数char variable; // 变量名double constant; // 常数值} value;char str[MAX_TOKEN_LEN]; // 字符串表示
} Token;// 计算器状态
typedef struct {double variables[MAX_VARIABLES]; // 变量存储(A-Z)double constants[2]; // 常量存储(π, e)char history[MAX_HISTORY][MAX_EXPR_LEN]; // 历史记录int history_count; // 历史记录数量int error_code; // 错误代码char error_msg[128]; // 错误信息int precision; // 显示精度int angle_mode; // 角度模式(0=弧度,1=角度)
} CalculatorState;// 栈结构
typedef struct {Token items[MAX_STACK_SIZE];int top;
} TokenStack;// 函数声明
void Calculator_Init(void);
void Calculator_ProcessCommand(char *command);
double Calculator_EvaluateExpression(char *expression);
void Calculator_PrintResult(double result);
void Calculator_ShowHelp(void);
void Calculator_ShowVariables(void);
void Calculator_ShowHistory(void);
void Calculator_ClearAll(void);// 表达式解析函数
int TokenizeExpression(char *expr, Token tokens[], int max_tokens);
int ParseTokens(Token tokens[], int token_count, Token postfix[]);
double EvaluatePostfix(Token postfix[], int count);// 栈操作函数
void Stack_Init(TokenStack *stack);
void Stack_Push(TokenStack *stack, Token item);
Token Stack_Pop(TokenStack *stack);
Token Stack_Peek(TokenStack *stack);
int Stack_IsEmpty(TokenStack *stack);
int Stack_Size(TokenStack *stack);// 工具函数
double CalculateFunction(FunctionType func, double arg);
double CalculateOperator(OperatorType op, double a, double b);
int GetOperatorPrecedence(OperatorType op);
int IsOperator(char c);
int IsFunction(char *str);
double GetConstant(char *str);#endif // CALCULATOR_H
3.2 主程序实现 (main.c)
#include "stm32f10x.h"
#include "calculator.h"
#include "usart.h"
#include "delay.h"
#include "led.h"// 全局变量
CalculatorState calc_state;
char input_buffer[MAX_EXPR_LEN];
int input_index = 0;// 系统初始化
void System_Init(void)
{// 初始化系统时钟SystemClock_Init();// 初始化延时Delay_Init();// 初始化LEDLED_Init();// 初始化串口USART1_Init(115200);// 初始化计算器Calculator_Init();printf("========================================\r\n");printf(" STM32 串口计算器 v1.0\r\n");printf("========================================\r\n");printf("输入 'help' 查看帮助信息\r\n");printf("输入 'vars' 查看变量\r\n");printf("输入 'history' 查看历史记录\r\n");printf("输入 'clear' 清除所有数据\r\n");printf("========================================\r\n");
}int main(void)
{System_Init();while(1){printf("calc> ");// 接收用户输入input_index = 0;memset(input_buffer, 0, sizeof(input_buffer));while(1){if(USART1_GetChar(&input_buffer[input_index])){// 回显字符USART1_SendChar(input_buffer[input_index]);if(input_buffer[input_index] == '\r' || input_buffer[input_index] == '\n'){input_buffer[input_index] = '\0';break;}input_index++;if(input_index >= MAX_EXPR_LEN - 1){printf("\r\n输入过长!\r\n");break;}}}// 处理命令if(strlen(input_buffer) > 0){Calculator_ProcessCommand(input_buffer);}printf("\r\n");}
}
3.3 计算器核心实现 (calculator.c)
#include "calculator.h"CalculatorState calc_state;// 初始化计算器
void Calculator_Init(void)
{int i;// 初始化变量for(i = 0; i < MAX_VARIABLES; i++){calc_state.variables[i] = 0.0;}// 初始化常量calc_state.constants[0] = 3.14159265358979323846; // πcalc_state.constants[1] = 2.71828182845904523536; // e// 初始化历史记录for(i = 0; i < MAX_HISTORY; i++){strcpy(calc_state.history[i], "");}calc_state.history_count = 0;// 初始化其他参数calc_state.error_code = CALC_OK;calc_state.precision = 6;calc_state.angle_mode = 0; // 默认弧度模式
}// 处理命令
void Calculator_ProcessCommand(char *command)
{char *args[4];int arg_count = 0;char *token;char *rest = command;// 解析命令参数while((token = strtok_r(rest, " ", &rest))){args[arg_count++] = token;if(arg_count >= 4) break;}if(arg_count == 0) return;// 处理命令if(strcmp(args[0], "help") == 0){Calculator_ShowHelp();}else if(strcmp(args[0], "vars") == 0){Calculator_ShowVariables();}else if(strcmp(args[0], "history") == 0){Calculator_ShowHistory();}else if(strcmp(args[0], "clear") == 0){Calculator_ClearAll();printf("已清除所有数据\r\n");}else if(strcmp(args[0], "deg") == 0){calc_state.angle_mode = 1;printf("已切换到角度模式\r\n");}else if(strcmp(args[0], "rad") == 0){calc_state.angle_mode = 0;printf("已切换到弧度模式\r\n");}else if(strchr(args[0], '=') != NULL){// 变量赋值char *eq_pos = strchr(args[0], '=');char var_name = toupper(*args[0]);char *expr = eq_pos + 1;if(var_name >= 'A' && var_name <= 'Z'){double result = Calculator_EvaluateExpression(expr);if(calc_state.error_code == CALC_OK){calc_state.variables[var_name - 'A'] = result;printf("%c = %.6f\r\n", var_name, result);}else{printf("错误: %s\r\n", calc_state.error_msg);}}else{printf("错误: 变量名必须是A-Z\r\n");}}else{// 计算表达式double result = Calculator_EvaluateExpression(command);if(calc_state.error_code == CALC_OK){Calculator_PrintResult(result);// 添加到历史记录if(calc_state.history_count < MAX_HISTORY){strcpy(calc_state.history[calc_state.history_count], command);calc_state.history_count++;}else{// 移动历史记录for(int i = 1; i < MAX_HISTORY; i++){strcpy(calc_state.history[i-1], calc_state.history[i]);}strcpy(calc_state.history[MAX_HISTORY-1], command);}}else{printf("错误: %s\r\n", calc_state.error_msg);}}
}// 计算表达式
double Calculator_EvaluateExpression(char *expression)
{Token tokens[MAX_STACK_SIZE];Token postfix[MAX_STACK_SIZE];int token_count;double result;// 重置错误状态calc_state.error_code = CALC_OK;// 词法分析token_count = TokenizeExpression(expression, tokens, MAX_STACK_SIZE);if(token_count <= 0){calc_state.error_code = CALC_ERR_SYNTAX;strcpy(calc_state.error_msg, "语法错误:无法解析表达式");return 0.0;}// 语法分析(中缀转后缀)int postfix_count = ParseTokens(tokens, token_count, postfix);if(postfix_count <= 0){calc_state.error_code = CALC_ERR_SYNTAX;strcpy(calc_state.error_msg, "语法错误:表达式格式不正确");return 0.0;}// 计算后缀表达式result = EvaluatePostfix(postfix, postfix_count);return result;
}// 打印结果
void Calculator_PrintResult(double result)
{if(fabs(result) < 1e-10) result = 0.0; // 处理负零if(fabs(result - (int)result) < 1e-10){printf("结果: %d\r\n", (int)result);}else{printf("结果: %.*f\r\n", calc_state.precision, result);}
}// 显示帮助
void Calculator_ShowHelp(void)
{printf("========================================\r\n");printf(" STM32 串口计算器帮助\r\n");printf("========================================\r\n");printf("基本运算:\r\n");printf(" +, -, *, /, %% 加减乘除取模\r\n");printf(" ^ 幂运算\r\n");printf(" () 括号运算\r\n");printf("\r\n科学函数:\r\n");printf(" sin(x) 正弦函数\r\n");printf(" cos(x) 余弦函数\r\n");printf(" tan(x) 正切函数\r\n");printf(" log(x) 常用对数\r\n");printf(" ln(x) 自然对数\r\n");printf(" exp(x) 指数函数\r\n");printf(" sqrt(x) 平方根\r\n");printf(" abs(x) 绝对值\r\n");printf("\r\n常量:\r\n");printf(" pi 圆周率 (%.10f)\r\n", calc_state.constants[0]);printf(" e 自然常数 (%.10f)\r\n", calc_state.constants[1]);printf("\r\n变量:\r\n");printf(" A-Z 26个变量存储\r\n");printf(" 赋值: A=10+5 将表达式结果赋给变量A\r\n");printf("\r\n命令:\r\n");printf(" help 显示此帮助\r\n");printf(" vars 显示所有变量\r\n");printf(" history 显示历史记录\r\n");printf(" clear 清除所有数据\r\n");printf(" deg 切换到角度模式\r\n");printf(" rad 切换到弧度模式\r\n");printf("\r\n示例:\r\n");printf(" calc> 2+3*4\r\n");printf(" calc> sin(pi/2)\r\n");printf(" calc> A=10*5\r\n");printf(" calc> B=A+sqrt(25)\r\n");printf(" calc> (A+B)/2\r\n");printf("========================================\r\n");
}// 显示变量
void Calculator_ShowVariables(void)
{int i;printf("========================================\r\n");printf(" 变量存储\r\n");printf("========================================\r\n");for(i = 0; i < MAX_VARIABLES; i++){if(fabs(calc_state.variables[i]) > 1e-10){printf(" %c = %.*f\r\n", 'A'+i, calc_state.precision, calc_state.variables[i]);}}printf(" 角度模式: %s\r\n", calc_state.angle_mode ? "角度" : "弧度");printf("========================================\r\n");
}// 显示历史记录
void Calculator_ShowHistory(void)
{int i;printf("========================================\r\n");printf(" 历史记录\r\n");printf("========================================\r\n");if(calc_state.history_count == 0){printf(" 无历史记录\r\n");}else{for(i = 0; i < calc_state.history_count; i++){printf(" %d: %s\r\n", i+1, calc_state.history[i]);}}printf("========================================\r\n");
}// 清除所有数据
void Calculator_ClearAll(void)
{int i;for(i = 0; i < MAX_VARIABLES; i++){calc_state.variables[i] = 0.0;}for(i = 0; i < MAX_HISTORY; i++){strcpy(calc_state.history[i], "");}calc_state.history_count = 0;calc_state.error_code = CALC_OK;
}// 词法分析
int TokenizeExpression(char *expr, Token tokens[], int max_tokens)
{int token_idx = 0;char *p = expr;char token_str[MAX_TOKEN_LEN];int token_len;while(*p && token_idx < max_tokens){// 跳过空格if(isspace(*p)){p++;continue;}// 数字(包括小数点)if(isdigit(*p) || *p == '.'){token_len = 0;while((isdigit(*p) || *p == '.') && token_len < MAX_TOKEN_LEN-1){token_str[token_len++] = *p++;}token_str[token_len] = '\0';tokens[token_idx].type = TOKEN_NUMBER;tokens[token_idx].value.number = atof(token_str);strcpy(tokens[token_idx].str, token_str);token_idx++;}// 变量或函数else if(isalpha(*p)){token_len = 0;while(isalnum(*p) && token_len < MAX_TOKEN_LEN-1){token_str[token_len++] = *p++;}token_str[token_len] = '\0';// 检查是否是函数if(IsFunction(token_str)){tokens[token_idx].type = TOKEN_FUNCTION;if(strcmp(token_str, "sin") == 0) tokens[token_idx].value.func = FUNC_SIN;else if(strcmp(token_str, "cos") == 0) tokens[token_idx].value.func = FUNC_COS;else if(strcmp(token_str, "tan") == 0) tokens[token_idx].value.func = FUNC_TAN;else if(strcmp(token_str, "log") == 0) tokens[token_idx].value.func = FUNC_LOG;else if(strcmp(token_str, "ln") == 0) tokens[token_idx].value.func = FUNC_LN;else if(strcmp(token_str, "exp") == 0) tokens[token_idx].value.func = FUNC_EXP;else if(strcmp(token_str, "sqrt") == 0) tokens[token_idx].value.func = FUNC_SQRT;else if(strcmp(token_str, "abs") == 0) tokens[token_idx].value.func = FUNC_ABS;}// 检查是否是常量else if(strcmp(token_str, "pi") == 0 || strcmp(token_str, "PI") == 0){tokens[token_idx].type = TOKEN_CONSTANT;tokens[token_idx].value.constant = calc_state.constants[0];}else if(strcmp(token_str, "e") == 0 || strcmp(token_str, "E") == 0){tokens[token_idx].type = TOKEN_CONSTANT;tokens[token_idx].value.constant = calc_state.constants[1];}// 变量else if(strlen(token_str) == 1 && token_str[0] >= 'A' && token_str[0] <= 'Z'){tokens[token_idx].type = TOKEN_VARIABLE;tokens[token_idx].value.variable = token_str[0];}else{return -1; // 无效的标识符}strcpy(tokens[token_idx].str, token_str);token_idx++;}// 运算符else if(IsOperator(*p)){token_str[0] = *p++;token_str[1] = '\0';tokens[token_idx].type = TOKEN_OPERATOR;if(token_str[0] == '+') tokens[token_idx].value.op = OPERATOR_PLUS;else if(token_str[0] == '-') tokens[token_idx].value.op = OPERATOR_MINUS;else if(token_str[0] == '*') tokens[token_idx].value.op = OPERATOR_MULTIPLY;else if(token_str[0] == '/') tokens[token_idx].value.op = OPERATOR_DIVIDE;else if(token_str[0] == '%') tokens[token_idx].value.op = OPERATOR_MODULO;else if(token_str[0] == '^') tokens[token_idx].value.op = OPERATOR_POWER;strcpy(tokens[token_idx].str, token_str);token_idx++;}// 括号else if(*p == '('){tokens[token_idx].type = TOKEN_LPAREN;tokens[token_idx].value.op = OPERATOR_PLUS; // 占位strcpy(tokens[token_idx].str, "(");token_idx++;p++;}else if(*p == ')'){tokens[token_idx].type = TOKEN_RPAREN;tokens[token_idx].value.op = OPERATOR_PLUS; // 占位strcpy(tokens[token_idx].str, ")");token_idx++;p++;}else{return -1; // 无效字符}}return token_idx;
}// 解析标记(中缀转后缀)
int ParseTokens(Token tokens[], int token_count, Token postfix[])
{TokenStack stack;int postfix_idx = 0;int i;Stack_Init(&stack);for(i = 0; i < token_count; i++){Token token = tokens[i];switch(token.type){case TOKEN_NUMBER:case TOKEN_CONSTANT:case TOKEN_VARIABLE:postfix[postfix_idx++] = token;break;case TOKEN_FUNCTION:Stack_Push(&stack, token);break;case TOKEN_OPERATOR:while(!Stack_IsEmpty(&stack) && Stack_Peek(&stack).type != TOKEN_LPAREN &&GetOperatorPrecedence(Stack_Peek(&stack).value.op) >= GetOperatorPrecedence(token.value.op)){postfix[postfix_idx++] = Stack_Pop(&stack);}Stack_Push(&stack, token);break;case TOKEN_LPAREN:Stack_Push(&stack, token);break;case TOKEN_RPAREN:while(!Stack_IsEmpty(&stack) && Stack_Peek(&stack).type != TOKEN_LPAREN){postfix[postfix_idx++] = Stack_Pop(&stack);}if(!Stack_IsEmpty(&stack) && Stack_Peek(&stack).type == TOKEN_LPAREN){Stack_Pop(&stack); // 弹出左括号}else{calc_state.error_code = CALC_ERR_PAREN;strcpy(calc_state.error_msg, "括号不匹配");return -1;}// 如果函数栈顶是函数,弹出函数if(!Stack_IsEmpty(&stack) && Stack_Peek(&stack).type == TOKEN_FUNCTION){postfix[postfix_idx++] = Stack_Pop(&stack);}break;}}// 弹出剩余运算符while(!Stack_IsEmpty(&stack)){Token token = Stack_Pop(&stack);if(token.type == TOKEN_LPAREN || token.type == TOKEN_RPAREN){calc_state.error_code = CALC_ERR_PAREN;strcpy(calc_state.error_msg, "括号不匹配");return -1;}postfix[postfix_idx++] = token;}return postfix_idx;
}// 计算后缀表达式
double EvaluatePostfix(Token postfix[], int count)
{TokenStack stack;Token token;double a, b, result;int i;Stack_Init(&stack);for(i = 0; i < count; i++){token = postfix[i];switch(token.type){case TOKEN_NUMBER:Stack_Push(&stack, token);break;case TOKEN_CONSTANT:token.value.number = token.value.constant;Stack_Push(&stack, token);break;case TOKEN_VARIABLE:token.value.number = calc_state.variables[token.value.variable - 'A'];Stack_Push(&stack, token);break;case TOKEN_OPERATOR:if(Stack_Size(&stack) < 2){calc_state.error_code = CALC_ERR_SYNTAX;strcpy(calc_state.error_msg, "语法错误:缺少操作数");return 0.0;}b = Stack_Pop(&stack).value.number;a = Stack_Pop(&stack).value.number;result = CalculateOperator(token.value.op, a, b);if(calc_state.error_code != CALC_OK){return 0.0;}token.type = TOKEN_NUMBER;token.value.number = result;Stack_Push(&stack, token);break;case TOKEN_FUNCTION:if(Stack_IsEmpty(&stack)){calc_state.error_code = CALC_ERR_SYNTAX;strcpy(calc_state.error_msg, "语法错误:函数缺少参数");return 0.0;}a = Stack_Pop(&stack).value.number;result = CalculateFunction(token.value.func, a);if(calc_state.error_code != CALC_OK){return 0.0;}token.type = TOKEN_NUMBER;token.value.number = result;Stack_Push(&stack, token);break;}}if(Stack_Size(&stack) != 1){calc_state.error_code = CALC_ERR_SYNTAX;strcpy(calc_state.error_msg, "语法错误:表达式格式不正确");return 0.0;}return Stack_Pop(&stack).value.number;
}// 计算函数
double CalculateFunction(FunctionType func, double arg)
{double result;switch(func){case FUNC_SIN:if(calc_state.angle_mode == 1) // 角度模式result = sin(arg * 3.14159265358979323846 / 180.0);elseresult = sin(arg);break;case FUNC_COS:if(calc_state.angle_mode == 1)result = cos(arg * 3.14159265358979323846 / 180.0);elseresult = cos(arg);break;case FUNC_TAN:if(calc_state.angle_mode == 1)result = tan(arg * 3.14159265358979323846 / 180.0);elseresult = tan(arg);break;case FUNC_LOG:if(arg <= 0){calc_state.error_code = CALC_ERR_INVALID_OP;strcpy(calc_state.error_msg, "对数函数的参数必须大于0");return 0.0;}result = log10(arg);break;case FUNC_LN:if(arg <= 0){calc_state.error_code = CALC_ERR_INVALID_OP;strcpy(calc_state.error_msg, "自然对数的参数必须大于0");return 0.0;}result = log(arg);break;case FUNC_EXP:result = exp(arg);break;case FUNC_SQRT:if(arg < 0){calc_state.error_code = CALC_ERR_INVALID_OP;strcpy(calc_state.error_msg, "平方根的参数不能为负数");return 0.0;}result = sqrt(arg);break;case FUNC_ABS:result = fabs(arg);break;default:calc_state.error_code = CALC_ERR_INVALID_OP;strcpy(calc_state.error_msg, "不支持的函数");return 0.0;}return result;
}// 计算运算符
double CalculateOperator(OperatorType op, double a, double b)
{double result;switch(op){case OPERATOR_PLUS:result = a + b;break;case OPERATOR_MINUS:result = a - b;break;case OPERATOR_MULTIPLY:result = a * b;break;case OPERATOR_DIVIDE:if(fabs(b) < 1e-10){calc_state.error_code = CALC_ERR_DIV_ZERO;strcpy(calc_state.error_msg, "除数不能为零");return 0.0;}result = a / b;break;case OPERATOR_MODULO:if(fabs(b) < 1e-10){calc_state.error_code = CALC_ERR_DIV_ZERO;strcpy(calc_state.error_msg, "取模运算的除数不能为零");return 0.0;}result = fmod(a, b);break;case OPERATOR_POWER:result = pow(a, b);break;default:calc_state.error_code = CALC_ERR_INVALID_OP;strcpy(calc_state.error_msg, "不支持的运算符");return 0.0;}// 检查溢出if(isinf(result) || isnan(result)){calc_state.error_code = CALC_ERR_OVERFLOW;strcpy(calc_state.error_msg, "计算结果溢出");return 0.0;}return result;
}// 栈操作函数
void Stack_Init(TokenStack *stack)
{stack->top = -1;
}void Stack_Push(TokenStack *stack, Token item)
{if(stack->top < MAX_STACK_SIZE - 1){stack->items[++stack->top] = item;}
}Token Stack_Pop(TokenStack *stack)
{Token empty_token;empty_token.type = TOKEN_NUMBER;empty_token.value.number = 0.0;strcpy(empty_token.str, "");if(stack->top >= 0){return stack->items[stack->top--];}return empty_token;
}Token Stack_Peek(TokenStack *stack)
{Token empty_token;empty_token.type = TOKEN_NUMBER;empty_token.value.number = 0.0;strcpy(empty_token.str, "");if(stack->top >= 0){return stack->items[stack->top];}return empty_token;
}int Stack_IsEmpty(TokenStack *stack)
{return stack->top == -1;
}int Stack_Size(TokenStack *stack)
{return stack->top + 1;
}// 工具函数
int GetOperatorPrecedence(OperatorType op)
{switch(op){case OPERATOR_POWER: return 4;case OPERATOR_MULTIPLY:case OPERATOR_DIVIDE:case OPERATOR_MODULO: return 3;case OPERATOR_PLUS:case OPERATOR_MINUS: return 2;default: return 0;}
}int IsOperator(char c)
{return (c == '+' || c == '-' || c == '*' || c == '/' || c == '%' || c == '^');
}int IsFunction(char *str)
{return (strcmp(str, "sin") == 0 || strcmp(str, "cos") == 0 || strcmp(str, "tan") == 0 || strcmp(str, "log") == 0 || strcmp(str, "ln") == 0 || strcmp(str, "exp") == 0 || strcmp(str, "sqrt") == 0 || strcmp(str, "abs") == 0);
}
3.4 串口驱动 (usart.c)
#include "usart.h"// USART1初始化
void USART1_Init(uint32_t baudrate)
{GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;// 使能时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);// 配置USART1 TX (PA9) 和 RX (PA10)GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_Init(GPIOA, &GPIO_InitStructure);// 配置USART参数USART_InitStructure.USART_BaudRate = baudrate;USART_InitStructure.USART_WordLength = USART_WordLength_8b;USART_InitStructure.USART_StopBits = USART_StopBits_1;USART_InitStructure.USART_Parity = USART_Parity_No;USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;USART_Init(USART1, &USART_InitStructure);// 配置中断NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);// 使能接收中断USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);// 使能USARTUSART_Cmd(USART1, ENABLE);
}// 发送单个字符
void USART1_SendChar(char ch)
{USART_SendData(USART1, (uint8_t)ch);while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}// 发送字符串
void USART1_SendString(char *str)
{while(*str){USART1_SendChar(*str++);}
}// 接收单个字符(非阻塞)
int USART1_GetChar(char *ch)
{if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET){*ch = (char)USART_ReceiveData(USART1);return 1;}return 0;
}// 重定向printf
int _write(int fd, char *ptr, int len)
{int i;for(i = 0; i < len; i++){USART1_SendChar(ptr[i]);}return len;
}
3.5 测试程序 (test.c)
#include "calculator.h"
#include <assert.h>// 测试函数
void RunCalculatorTests(void)
{printf("========================================\r\n");printf(" STM32 计算器测试\r\n");printf("========================================\r\n");// 测试基本运算printf("测试基本运算:\r\n");assert(fabs(Calculator_EvaluateExpression("2+3") - 5.0) < 1e-6);assert(fabs(Calculator_EvaluateExpression("10-4") - 6.0) < 1e-6);assert(fabs(Calculator_EvaluateExpression("3*7") - 21.0) < 1e-6);assert(fabs(Calculator_EvaluateExpression("15/3") - 5.0) < 1e-6);assert(fabs(Calculator_EvaluateExpression("17%5") - 2.0) < 1e-6);printf(" 基本运算测试通过 ✓\r\n");// 测试括号运算printf("测试括号运算:\r\n");assert(fabs(Calculator_EvaluateExpression("(2+3)*4") - 20.0) < 1e-6);assert(fabs(Calculator_EvaluateExpression("10/(2+3)") - 2.0) < 1e-6);assert(fabs(Calculator_EvaluateExpression("2*(3+4*(5-2))") - 30.0) < 1e-6);printf(" 括号运算测试通过 ✓\r\n");// 测试科学函数printf("测试科学函数:\r\n");assert(fabs(Calculator_EvaluateExpression("sin(pi/2)") - 1.0) < 1e-6);assert(fabs(Calculator_EvaluateExpression("cos(0)") - 1.0) < 1e-6);assert(fabs(Calculator_EvaluateExpression("sqrt(16)") - 4.0) < 1e-6);assert(fabs(Calculator_EvaluateExpression("log(100)") - 2.0) < 1e-6);assert(fabs(Calculator_EvaluateExpression("ln(e)") - 1.0) < 1e-6);printf(" 科学函数测试通过 ✓\r\n");// 测试变量printf("测试变量存储:\r\n");Calculator_EvaluateExpression("A=10");Calculator_EvaluateExpression("B=20");assert(fabs(Calculator_EvaluateExpression("A+B") - 30.0) < 1e-6);assert(fabs(Calculator_EvaluateExpression("A*B") - 200.0) < 1e-6);printf(" 变量存储测试通过 ✓\r\n");// 测试错误处理printf("测试错误处理:\r\n");Calculator_EvaluateExpression("10/0"); // 除零错误assert(calc_state.error_code == CALC_ERR_DIV_ZERO);Calculator_EvaluateExpression("sqrt(-1)"); // 负数平方根assert(calc_state.error_code == CALC_ERR_INVALID_OP);printf(" 错误处理测试通过 ✓\r\n");printf("========================================\r\n");printf(" 所有测试通过!\r\n");printf("========================================\r\n");
}
参考代码 STM32串口计算器 www.youwenfan.com/contentcnv/72257.html
四、编译与部署
4.1 Keil工程配置
Target: STM32F103C8
Device: STM32F103C8T6
C/C++:Include Paths: .\inc; .\User; .\HardwareDefine: STM32F10X_MD, USE_STDPERIPH_DRIVER
Linker:Scatter File: STM32_FLASH.sct
Debug:Debugger: ST-Link Debugger
4.2 使用说明
-
连接硬件:将STM32通过USB-TTL连接到电脑
-
打开串口终端:使用PuTTY、SecureCRT等串口工具,设置115200波特率
-
基本计算:
calc> 2+3*4 结果: 14 -
科学计算:
calc> sin(pi/2) 结果: 1.000000 -
变量存储:
calc> A=10*5 A = 50.000000 calc> B=A+25 B = 75.000000 calc> (A+B)/2 结果: 62.500000 -
查看帮助:
calc> help
五、功能扩展建议
5.1 硬件扩展
- OLED显示:实时显示计算结果
- 矩阵键盘:物理按键输入
- SD卡存储:保存计算历史和配置
- RTC时钟:添加时间戳功能
5.2 软件扩展
- 复数运算:支持复数计算
- 矩阵运算:矩阵加减乘除
- 单位换算:长度、重量、温度等单位换算
- 编程模式:支持简单程序编写
- 图形显示:绘制函数图像
