当前位置: 首页 > news >正文

STM32 串口计算器实现

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 使用说明

  1. 连接硬件:将STM32通过USB-TTL连接到电脑

  2. 打开串口终端:使用PuTTY、SecureCRT等串口工具,设置115200波特率

  3. 基本计算

    calc> 2+3*4
    结果: 14
    
  4. 科学计算

    calc> sin(pi/2)
    结果: 1.000000
    
  5. 变量存储

    calc> A=10*5
    A = 50.000000
    calc> B=A+25
    B = 75.000000
    calc> (A+B)/2
    结果: 62.500000
    
  6. 查看帮助

    calc> help
    

五、功能扩展建议

5.1 硬件扩展

  1. OLED显示:实时显示计算结果
  2. 矩阵键盘:物理按键输入
  3. SD卡存储:保存计算历史和配置
  4. RTC时钟:添加时间戳功能

5.2 软件扩展

  1. 复数运算:支持复数计算
  2. 矩阵运算:矩阵加减乘除
  3. 单位换算:长度、重量、温度等单位换算
  4. 编程模式:支持简单程序编写
  5. 图形显示:绘制函数图像
http://www.zskr.cn/news/1369558.html

相关文章:

  • MATLAB XFOIL翼型分析终极指南:如何在MATLAB中实现专业级空气动力学计算
  • DeepSeek审计日志功能深度拆解(Gartner认证级日志治理框架首次公开)
  • Nodejs服务端应用集成TaoToken多模型API的完整配置指南
  • 佛山地下管道漏水检测——东诚管线自研技术定位误差≤5cm - 品牌优选官
  • 2026木门十大品牌加盟指南:值得关注的木门十大品牌深度解析 - 匠言榜单
  • 三步改造小爱音箱:让传统智能音箱秒变AI语音助手的完整指南
  • 跨平台资源包管理工具VPKEdit:游戏开发者的终极解决方案
  • 抖音下载器:3步搞定无水印视频批量下载,效率提升90%
  • 【系统学AI】02 token机制全解:LLM如何‘读懂‘人类语言
  • 上门回收行业获客越来越难?放弃盲目扫楼,GEO优化靠AI搜索大模型流量营销推广精准接单 - 一点学习库
  • 数据丢失时的数字救援队:TestDisk与PhotoRec的救赎之路
  • 3分钟快速上手:Unlock Music音乐解锁工具终极指南
  • qmc-decoder终极指南:3分钟解锁QQ音乐加密音频的完整解决方案
  • 京东自动化脚本终极指南:3步搭建免费京豆自动获取系统
  • 通过TaotokenCLI工具一键配置多开发环境下的API访问密钥
  • Taotoken平台API Key申请与用量看板查看教程
  • 成都成华区装修公司哪家靠谱?按模式选对才省心 - 成都人评鉴
  • Windows生态融合新路径:APK-Installer让安卓应用无缝接入桌面环境
  • 【仅限首批内测团队公开】DeepSeek v3.2.1对话引擎隐藏参数调优指南:3个未文档化flag让多轮F1值飙升23.6%
  • 5分钟免费解锁英雄联盟全皮肤:R3nzSkin国服特供版终极指南
  • 限时可用!Gemini免费额度“灰度扩容通道”实测成功(仅开放给GCP新认证开发者):3个注册即享+2个邀请加赠技巧,手慢无
  • 免费开源数据恢复终极指南:TestDisk与PhotoRec拯救你的宝贵数据
  • Claude Code用户如何通过Taotoken稳定使用并获得更多Token
  • 海南省儋州CPPMSCMP官网报考入口,官方授权双证报考中心 - 众智商学院课程中心
  • 免费AI视频放大神器:Video2X让你的老旧视频重获新生
  • 别再用关键词过滤了!用Python和朴素贝叶斯,手把手教你打造一个98%准确率的垃圾邮件拦截器
  • PbootCMS模板引擎RCE漏洞深度验证与边界穿透实战
  • 仅限前500名开发者获取:ChatGPT+Tableau自动化连接器私有部署包(含OAuth2.0审计日志模块)
  • 2026 济南高端手表回收专业测评:添价收鉴定水准尽显专业功底 - 薛定谔的梨花猫
  • 火爆分享如何用Taotoken一分钟接入OpenAI兼容API并开始调用