写点什么

基于 STM32 设计的计算器 (实现基本运算)

作者:DS小龙哥
  • 2022 年 1 月 07 日
  • 本文字数:5072 字

    阅读完需:约 17 分钟

1. 项目介绍

计算器是最常见的工具了,现在不管是手机、电脑都带有计算器功能,支持强大的科学运算等。


当前文章介绍的是 STM32+LCD 触摸屏设计的一个触摸计算器功能,实现基本的加减乘除,二进制转换显示等功能。LCD 屏使用的是 3.5 寸带触摸屏的显示屏,方便操作屏幕,MCU 采用 STM32F103ZET6。


设计的这个计算器用到的硬件不多,主要是 LCD 屏和触摸屏,用到了一个 W25Q64 存储芯片,保存触摸屏校准后的一些配置数据,这个可有可无,只是方便不需要每次断电后重新校准。


运行效果图如下:





完整项目源码下载地址: https://download.csdn.net/download/xiaolong1126626497/63976226


视频演示地址: https://live.csdn.net/v/182604

2. 项目实现

2.1 运算实现思路

功能介绍:



在除法计算过程中,如果商是小数,计算器得到的结果也是精准的,是 double 类型。在计算过程中,可以实现连续运算。过程中是逐步计算出数据来的。


触摸校准流程:



计算器算法:


2.2 LCD 显示屏驱动代码

LCD 的驱动芯片是 NT35310,支持 8080 时序读写寄存器,当前项目采用模拟时序控制 LCD 屏,移植性较高。


核心代码如下:


#include "lcd.h"#include "stdlib.h"#include "usart.h"   #include "delay.h"#include "math.h"#include "timer.h"#include "spi.h"#include "usart.h"#include <stdio.h>#include "key.h"#include "rtc.h"#include "wannianli.h"#include "touch.h"#include "led.h"#include <stdlib.h>#include "shuzimo.h"#include <string.h>#include "calculator.h"
/*函数功能:写LCD数据函数参数:data:要写入的值 */void LcdWriteData(u16 data){ LCD_RS=1; //写数据 LCD_CS=0; //选中LCD屏 //输出数据 LCD_DATA0=(data>>0&0x01); LCD_DATA1=(data>>1&0x01); LCD_DATA2=(data>>2&0x01); LCD_DATA3=(data>>3&0x01); LCD_DATA4=(data>>4&0x01); LCD_DATA5=(data>>5&0x01); LCD_DATA6=(data>>6&0x01); LCD_DATA7=(data>>7&0x01); LCD_DATA8=(data>>8&0x01); LCD_DATA9=(data>>9&0x01); LCD_DATA10=(data>>10&0x01); LCD_DATA11=(data>>11&0x01); LCD_DATA12=(data>>12&0x01); LCD_DATA13=(data>>13&0x01); LCD_DATA14=(data>>14&0x01); LCD_DATA15=(data>>15&0x01); LCD_WR=0; //表示准备写数据 LCD_WR=1; //表示数据写完成 LCD_CS=1; //取消LCD屏片选}

/*函数功能:写寄存器参 数:regval:寄存器值*/ void LcdWriteReg(u16 data){ LCD_RS=0; //写命令 LCD_CS=0; //选中LCD屏 //输出数据 LCD_DATA0=(data>>0&0x01); LCD_DATA1=(data>>1&0x01); LCD_DATA2=(data>>2&0x01); LCD_DATA3=(data>>3&0x01); LCD_DATA4=(data>>4&0x01); LCD_DATA5=(data>>5&0x01); LCD_DATA6=(data>>6&0x01); LCD_DATA7=(data>>7&0x01); LCD_DATA8=(data>>8&0x01); LCD_DATA9=(data>>9&0x01); LCD_DATA10=(data>>10&0x01); LCD_DATA11=(data>>11&0x01); LCD_DATA12=(data>>12&0x01); LCD_DATA13=(data>>13&0x01); LCD_DATA14=(data>>14&0x01); LCD_DATA15=(data>>15&0x01); LCD_WR=0; //表示准备写数据 LCD_WR=1; //表示数据写完成 LCD_CS=1; //取消LCD屏片选}

/*函数功能:设置光标位置函数参数: Xpos:横坐标 Ypos:纵坐标*/void LcdSetCursor(u16 Xpos, u16 Ypos){ LcdWriteReg(0X2A); LcdWriteData(Xpos>>8); LcdWriteData(Xpos&0XFF); LcdWriteReg(0X2B); LcdWriteData(Ypos>>8); LcdWriteData(Ypos&0XFF); }


/*功 能: 初始化LCD屏幕说 明: 用于3.5寸屏幕的初始化。 LCD ID:5310硬件连接:硬件连接:FSMC_D0 ------PD14FSMC_D1 ------PD15FSMC_D2 ------PD0FSMC_D3 ------PD1FSMC_D4 ------PE7FSMC_D5 ------PE8FSMC_D6 ------PE9FSMC_D7 ------PE10FSMC_D8 ------PE11FSMC_D9 ------PE12FSMC_D10 -----PE13FSMC_D11 ------PE14FSMC_D12 ------PE15FSMC_D13 ------PD8FSMC_D14 ------PD9FSMC_D15 ------PD10
LCD_BL(背光) ----PB0FSMC_NE4(CS) --->PG12FSMC_NWE(WR/CLK)--->PD5 FSMC_NOE(RD) --->PD4FSMC_A10(RS) --->PG0*/void LcdInit(void){ RCC->APB2ENR|=1<<3; //使能PORTB时钟 RCC->APB2ENR|=1<<5; //使能PORTD时钟 RCC->APB2ENR|=1<<6; //使能PORTE时钟 RCC->APB2ENR|=1<<8; //使能PORTG时钟
/*1. 初始化控制IO口*/ GPIOB->CRL&=0xFFFFFFF0; //LCD_BL(背光) GPIOB->CRL|=0x0000000B; GPIOG->CRH&=0xFFF0FFFF; //FSMC_NE4(CS) GPIOG->CRH|=0x00030000; GPIOD->CRL&=0xFF00FFFF; //FSMC_NWE(WR/CLK)\FSMC_NOE(RD) GPIOD->CRL|=0x00330000; GPIOG->CRL&=0xFFFFFFF0; //FSMC_A10(RS) GPIOG->CRL|=0x00000003; /*2. 初始化数据线*/ GPIOD->CRL&=0xFFFFFF00; GPIOD->CRL|=0x00000033; GPIOD->CRH&=0x00FFF000; GPIOD->CRH|=0x33000333; GPIOE->CRL&=0x0FFFFFFF; GPIOE->CRL|=0x30000000; GPIOE->CRH&=0x00000000; GPIOE->CRH|=0x33333333;}
/*函数功能:画点函数形参:x,y:坐标*/void LcdDrawPoint(u16 x,u16 y,u16 color){ LcdSetCursor(x,y); //设置光标位置 LcdWriteReg(0X2C); //开始写入GRAM LcdWriteData(color);}
/*函数功能:显示一个汉字*/ void LcdShowFont(u8 *font,u16 x,u16 y,u16 size,u16 high,u16 color1,u16 color2){ u8 data; u16 i,j,k; for(i=0;i<high;i++) { LcdSetCursor(x,y); //设置光标位置 LcdWriteReg(0X2C); //开始写入GRAM for(j=0;j<size/8;j++) { data=*font; //取出一个值 for(k=0;k<8;k++) { if(data&0x80)LcdWriteData(color1); else LcdWriteData(color2); data<<=1; } font++; } y++; }}
void LcdShowFont_zong(u8 *font,u16 x,u16 y,u16 size,u16 high){ u16 i,j; u8 data; u16 y0=y; for(i=0;i<size*high/8;i++) { data=*font; //取出一个值 for(j=0;j<8;j++) { if(data&0x80)LcdDrawPoint(x,y,YELLOW); else LcdDrawPoint(x,y,LIGHTGREEN); y++; data<<=1; if((y-y0)==high) //一列显示完毕,可以换行 { x++; y=y0; //纵坐标归位 } } font++; }}
void lcd_clear(u16 x,u16 y,u16 color) //清屏{ int i; LcdSetCursor(x,y); //设置光标位置 LcdWriteReg(0X2C); //开始写入GRAM for(i=0;i<320*480;i++) { LcdWriteData(color); }}
void paint(u8 *font,u16 x,u16 y,u16 size,u16 high){ u16 i,j; for(i=0;i<high;i++,y++) { LcdSetCursor(x,y); //设置光标位置 LcdWriteReg(0X2C); //开始写入GRAM for(j=0;j<size;j++) { LcdWriteData(*font<<8|*(font+1)); font+=2; } }}
复制代码

2.3 触摸屏代码

触摸屏采用 XPT2046 芯片,一个 24 位的 ADC 芯片,支持 SPI 接口。


代码里主要完成两个操作: 1. 读取 XPT2046 检测到的数据 2. 实现触摸屏校准算法


代码如下:


#include "touch.h"#include "delay.h"#include "lcd.h"#include "spi.h"#include <stdio.h>
#define T_MOSI1 GPIOF->ODR|=1<<9;#define T_MOSI0 GPIOF->ODR&=~(1<<9);#define T_SCK1 GPIOB->ODR|=1<<1;#define T_SCK0 GPIOB->ODR&=~(1<<1);#define T_CS1 GPIOF->ODR|=1<<11;#define T_CS0 GPIOF->ODR&=~(1<<11);
extern struct kxy{ float kx; float ky; u16 x1; u16 y1; u16 x2; u16 y2; u16 x3; u16 y3; u16 x4; u16 y4; u16 xx; u16 yy;}xielv;
void touch_lint(void){ RCC->APB2ENR|=1<<3; //打开PB口时钟 RCC->APB2ENR|=1<<7; //打开PF口时钟 GPIOB->CRL&=0XFFFFF00F; //配置PB口 GPIOB->CRL|=0X00000830; GPIOF->CRH&=0XFFFF000F; //配置PF口 GPIOF->CRH|=0X00003830; T_SCK1 GPIOF->IDR|=1<<10; T_CS1; }


void touch_write(u8 data) //往XPT2046中写入命令{ u8 i; T_CS0 T_SCK0 T_MOSI0 for(i=0;i<8;i++) { if(data&0x80) T_MOSI1 else T_MOSI0 T_SCK0 T_SCK1 data=data<<1; }}
u16 touch_read(u8 data) //从XPT2046中读取数据{ u16 i,dat=0; touch_write(data); delay_us(6); for(i=0;i<16;i++) { dat=dat<<1; T_SCK0 T_SCK1 if(GPIOB->IDR&1<<2) { dat|=1<<0; } } T_CS1 dat=dat>>4; return dat;}
void si_shizi(u16 color){ Draw_line(0,10,20,10,color); Draw_line(10,0,10,20,color); Draw_line(300,10,320,10,color); Draw_line(310,0,310,20,color); Draw_line(0,470,20,470,color); Draw_line(10,460,10,480,color); Draw_line(300,470,320,470,color); Draw_line(310,460,310,480,color); }
void jiaozhun(u16 x1,u16 y1,u16 x2,u16 y2,u16 x3,u16 y3,u16 x4,u16 y4){ xielv.kx=(300.0/(x1-x2)+300.0/(x3-x4))/2; xielv.ky=(460.0/(y1-y3)+460.0/(y2-y4))/2;}
void lcd_jiaozhun(void){ read_data((u8*)&xielv,791920,sizeof(struct kxy)); if(xielv.kx<0) { u8 *buff=malloc(100); u8 *bufi=malloc(50); u8 i=0; u16 x0,y0; lcd_clear(0,0,YELLOW); si_shizi(BLUE); lcd_string((u8*)"校准开始",buff,16,130,220,767600,32,64); delay_ms(3000); juxing_tianchong(80,220,160,16,YELLOW); lcd_string((u8*)"请点击第一个十字中心",bufi,16,80,220,767600,32,64); while(1) { if(!(GPIOF->IDR&1<<10)) { delay_ms(20); if(!(GPIOF->IDR&1<<10)) { x0=touch_read(0xD0); y0=touch_read(0X90); i++; if(i==1) { Draw_line(0,10,20,10,YELLOW); Draw_line(10,0,10,20,YELLOW); juxing_tianchong(80,220,160,16,YELLOW); lcd_string((u8*)"请点击第二个十字中心",bufi,16,80,220,767600,32,64); xielv.x1=x0; xielv.y1=y0; } if(i==2) { Draw_line(300,10,320,10,YELLOW); Draw_line(310,0,310,20,YELLOW); juxing_tianchong(80,220,160,16,YELLOW); lcd_string((u8*)"请点击第三个十字中心",bufi,16,80,220,767600,32,64); xielv.x2=x0; xielv.y2=y0; } if(i==3) { Draw_line(0,470,20,470,YELLOW); Draw_line(10,460,10,480,YELLOW); juxing_tianchong(80,220,160,16,YELLOW); lcd_string((u8*)"请点击第四个十字中心",bufi,16,80,220,767600,32,64); xielv.x3=x0; xielv.y3=y0; } if(i==4) { Draw_line(300,470,320,470,YELLOW); Draw_line(310,460,310,480,YELLOW); juxing_tianchong(80,220,160,16,YELLOW); lcd_string((u8*)"校准完毕",buff,16,130,220,767600,32,64); delay_ms(3000); juxing_tianchong(80,220,160,16,YELLOW); xielv.x4=x0; xielv.y4=y0; jiaozhun(xielv.x1,xielv.y1,xielv.x2,xielv.y2,xielv.x3,xielv.y3,xielv.x4,xielv.y4); break; } delay_ms(40); } } } clear_shanqu(761920); write_every((u8*)&xielv,sizeof(struct kxy),791920); }}
复制代码


用户头像

DS小龙哥

关注

之所以觉得累,是因为说的比做的多。 2022.01.06 加入

熟悉C/C++、51单片机、STM32、Linux应用开发、Linux驱动开发、音视频开发、QT开发. 目前已经完成的项目涉及音视频、物联网、智能家居、工业控制领域

评论

发布
暂无评论
基于STM32设计的计算器(实现基本运算)