# ARM开发（9）基于STM32的简单四则运算计算器

xiaoxiao2021-02-28  13

1.1 本实验实现基于STM32的简单四则运算计算器 1.2 实验思路：理解计算器原理（按键扫描，字符实时显示，运算表达式计算，浮点数转字符串，字符串结果显示） 1.3 开发环境 : MDK5 库函数版本开发 JLINK仿真 STM32F103VBT6芯片

#ifndef __KEY_H #define __KEY_H #include"sys.h" #define KEY_NULL 0 // no key #define KEY_0 40 // value 0 #define KEY_1 41 #define KEY_2 42 #define KEY_3 43 #define KEY_4 44 #define KEY_5 45 #define KEY_6 46 #define KEY_7 47 #define KEY_8 48 #define KEY_9 49 // #define KEY_POINT 50 // decimal point #define KEY_OP_ADD 64 // '+' addition #define KEY_OP_SUB 65 // '-' substract #define KEY_OP_MUL 66 // '*' multiplication #define KEY_OP_DIV 67 // '/' divide #define KEY_EQU 81 // '=' void KEY_Init(void);//IO³õÊ¼»¯ void funcScanKey(void); u8 Key_Read(void); #endif

2.2 key.c代码(按键扫描函数)：

#include "stm32f10x.h" #include "key.h" #include "delay.h" typedef struct { u8 flagKeyDown:1; u8 flagKeyRelease:1; u8 flagKeyLong:1; } TEMP_FLAGS_Type; TEMP_FLAGS_Type tempFlags; u8 specialKey; void KEY_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE); GPIO_InitStructure.GPIO_Pin = KEY_OUT_PINS ;//0x7f ;//GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6;//ROWPINS; ÐÐ GPIO_InitStructure.GPIO_Mode =GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOE, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = KEY_IN_PINS ; //GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10| GPIO_Pin_11|GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14;//COLPINS; ÁÐ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOE, &GPIO_InitStructure); GPIO_SetBits(GPIOE,GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6); GPIO_ResetBits(GPIOE, GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10| GPIO_Pin_11|GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14); } static u8 Timer2of16ms = 0; static u8 sourceKey = 0; static u8 keyCol, keyRow; u8 const keyTableMax = 7; u8 const keyTableMaxCol = 8; u16 const scanTable[keyTableMax] = { 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1}; u16 const scanTable2[keyTableMaxCol] = { 0x01, 0x02, 0x04, 0x08, 0x010, 0x20, 0x40, 0x80}; u8 const keyTable[keyTableMax][keyTableMaxCol] = { { KEY_FUNC_UP, KEY_FUNC_DOWN, KEY_ABS_INC, KEY_ENTER, 0, KEY_Z_SET, KEY_Y_SET, KEY_X_SET}, { KEY_SDM, 0, KEY_HA, 0, KEY_0, KEY_1, KEY_4, KEY_7}, { KEY_FUNC_RARC, KEY_CLS, KEY_HALF, 0, KEY_POINT, KEY_2, KEY_5, KEY_8}, { KEY_FUNC_LINE, KEY_FUNC_CIRCLE, 0, KEY_MM_INCH, KEY_SIGN, KEY_3, KEY_6, KEY_9}, { KEY_CALL, KEY_TOOL, KEY_OP_ADD, KEY_OP_SUB, KEY_OP_MUL, KEY_OP_DIV, KEY_EQU, KEY_CLEAR}, { KEY_OP_INV, 0, KEY_OP_SIN, KEY_OP_COS, KEY_OP_TAN, KEY_ARC, KEY_OP_SQRT, KEY_CALCU}, { KEY_ZHUIDU, 0, 0, 0, 0, KEY_Z_ZERO, KEY_Y_ZERO, KEY_X_ZERO } }; void funcScanKey(void) { u16 x ; uint16_t tempdata; static u16 keyDelay = 0; static u8 keyRelDelay = 0; static u8 row = 0; u8 currKey,keyLongCnt; Timer2of16ms ++; if(Timer2of16ms >= keyTableMax) Timer2of16ms = 0; tempdata = GPIO_ReadOutputData(KEY_PORT) ; tempdata &= ~KEY_OUT_PINS; tempdata |= scanTable[Timer2of16ms ]; GPIO_Write(KEY_PORT, tempdata); tempdata = 0; x = tempdata; tempdata = GPIO_ReadInputData(KEY_PORT) ; tempdata &= 0x7f80; tempdata >>= 7; x = tempdata; if(x>0) // 有按键按下了 { currKey = x; if((currKey != sourceKey) || (row != Timer2of16ms) ) // 按键判断 { keyDelay = 0; sourceKey = currKey; tempFlags.flagKeyDown = 0; tempFlags.flagKeyLong = 0; row = Timer2of16ms; keyLongCnt = 0; return; } else { if((row != Timer2of16ms)) return; keyDelay++; keyLongCnt++; if(keyDelay > 10) { keyRelDelay = 0; if(tempFlags.flagKeyDown) { if(keyLongCnt >= 100) { specialKey = x; tempFlags.flagKeyLong = 1; // 长按键处理 for(currKey = 0; currKey < keyTableMaxCol; currKey++) { if(x == scanTable2[currKey]) break; } keyCol = currKey; keyRow = row; } } else { tempFlags.flagKeyDown = 1; for(currKey = 0; currKey < keyTableMaxCol; currKey++) { if(x == scanTable2[currKey]) break; } keyCol = currKey; keyRow = row; } } } } else // 没按键按下 { if(row != Timer2of16ms) return; keyDelay = 0; if(tempFlags.flagKeyLong) { keyRelDelay++; if(keyRelDelay > 3) { tempFlags.flagKeyLong = 0; tempFlags.flagKeyDown = 0; tempFlags.flagKeyRelease = 0; keyLongCnt = 0; } } if(tempFlags.flagKeyDown) { keyRelDelay++; if(keyRelDelay > 3) { tempFlags.flagKeyDown = 0; tempFlags.flagKeyRelease = 1; keyLongCnt = 0; } } } } u8 Key_Read(void) //扫描得到按键值 { if(tempFlags.flagKeyRelease) { tempFlags.flagKeyRelease = 0; return keyTable[keyRow][keyCol]; } else if(tempFlags.flagKeyLong) return keyTable[keyRow][keyCol]; return KEY_NULL; }

2.3 calcalator.h代码

#ifndef __CALCALATOR_H #define __CALCALATOR_H #include "sys.h" extern double Jud(char str[],int begin, int end); //动态规划四则运算过程 extern double displaynum(char str[],int len); //四则运算结果函数 extern void float_to_str(char *str,double num); //双精度化为字符串函数 extern u8 str_to_ma(char st); //字符转为数码管段选码值 extern char key(u8 c); //判断按键扫描值是否是四则运算符或者0~9字符 #endif

2.4 calcalator.c代码（计算器运算）

#include "calcalator.h" #include"stdio.h" #include"stdlib.h" #include"string.h" #include"math.h" #include "sys.h" int fst[1005]; u8 const seg88Code[17] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F, 0x77, 0X7C, 0x39, 0x5E, 0x79, 0x71,0x01}; double Jud(char str[],int begin, int end)// 动态规划四则运算过程 { int i; double k; for(i = begin; i <= end; i++) { if(str[i]== '+' && fst[i] == fst[begin]) { k = Jud(str,begin, i - 1) + Jud(str,i + 1, end); return k; } } for(i = end; i >= begin; i--) { if(str[i]=='-' && fst[i] == fst[begin]) { k = Jud(str,begin, i - 1) - Jud(str,i + 1, end); return k; } } for(i = begin; i <= end; i++) { if(str[i] == '*' && fst[i] == fst[begin]) { k = Jud(str,begin, i - 1) * Jud(str,i + 1, end); return k; } } for(i = end; i >= begin; i--) { if(str[i] == '/' && fst[i] == fst[begin]) { k = Jud(str,begin, i - 1) / Jud(str,i + 1, end); return k; } } if(str[begin]=='(') { for(i = begin + 1; fst[i] >= fst[begin + 1]; i++); k = Jud(str,begin + 1, i - 1); } else { char *p = str; sscanf(p+begin, "%lf", &k); } return k; } double displaynum(char str[],int len)// 四则运算结果函数 { int i; double ans; memset(fst, 0, sizeof(fst)); fst[0] = 1; for(i = 1; i <= len - 1; i++) { if(str[i - 1]== '(') fst[i] = fst[i - 1] + 1; else if(str[i] == ')') fst[i] = fst[i - 1] - 1; else fst[i] = fst[i - 1]; } ans = Jud(str,0, len - 1); return ans; } void float_to_str(char *str,double num)// 双精度化为字符串函数 { int high; double low,tp; char *start=str; int n=0; char ch[20]; int i; high=(int)num; low=num-high; while(high>0){ ch[n++]='0'+high; high=high/10; } for(i=n-1;i>=0;i--){ *str++=ch[i]; } num -= (int)num; tp = 0.1; *str++='.'; while(num > 1e-8){ num -= tp * (int)(low * 10); tp /= 10; *str++='0'+(int)(low*10); low=low*10.0-(int)(low*10); } *str='\0'; str=start; } extern u8 str_to_ma(char s)// 字符转为数码管段选码值 { if(s=='.') return 0x10; else return seg88Code[s-'0']; }

2.5 main.c代码(具体实现计算器四则运算)：

#include "sys.h" #include "delay.h" #include "led.h" #include "beep.h" #include "key.h" #include "keyled.h" #include "calcalator.h" #include"stdlib.h" #include"stdio.h" #include"string.h" char key(u8 c)// 判断按键扫描值是否是四则运算符或者0~9字符 { char ckey; if(c>=40&&c<=49) { ckey=(char)(c-40+'0'); return ckey; } else if(c==50) { ckey='.'; return ckey; } else if(c==64) { ckey='+'; return ckey; } else if(c==65) { ckey='-'; return ckey; } else if(c==66) { ckey='*'; return ckey; } else if(c==67) { ckey='/'; return ckey; } else return 'o'; } int main(void) { u8 c,i,n,t,q; double m; char str[20]; char dispay_num[20]; u8 led_num[8]={0}; u8 dnum[20]; u8 const seg88Code[17] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F, 0x77, 0X7C, 0x39, 0x5E, 0x79, 0x71,0x01}; char ckey; delay_init(); KEY_Init(); BEEP_Init(); Initial_LED(); while(1) { c=0; i=0; t=0; m=0; q=0; while(1) { funcScanKey(); c=Key_Read(); if(c != KEY_NULL) { ckey=key(c); if(ckey!='o') dispay_num[i++]=ckey; if(c>=40&&c<=49) { led_num[t++]=seg88Code[c-40]; } else if ((c>=64&&c<=67)||c==81) { DisplayOn(1); Displayy(led_num,t); t=0; delay_ms (1000); DisplayOn(0); if(c==81) break; } delay_ms(10); } } if(c==81) { int h; h=(int)(i); m=displaynum(dispay_num,h); float_to_str(str,m); n=strlen(str); for(q=0;q<n;q++) { dnum[q]=str_to_ma(str[q]); } DisplayOn(1); Displayy(dnum,n); delay_ms(1000); DisplayOn(0); } } }

3.1 与板子接线测试效果良好，基本四则运算结果较好，唯一缺点对tm1629a的显示不是很好，因为数码管排列顺序不是顺序，可以在上面修改，基本思路是这样。