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

1. 项目介绍

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

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

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

运行效果图如下:

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

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

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

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

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

2. 项目实现

2.1 运算实现思路

功能介绍:

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

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

触摸校准流程:

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

计算器算法:

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

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 ------PD14
FSMC_D1 ------PD15
FSMC_D2 ------PD0
FSMC_D3 ------PD1
FSMC_D4 ------PE7
FSMC_D5 ------PE8
FSMC_D6 ------PE9
FSMC_D7 ------PE10
FSMC_D8 ------PE11
FSMC_D9 ------PE12
FSMC_D10 -----PE13
FSMC_D11 ------PE14
FSMC_D12 ------PE15
FSMC_D13 ------PD8
FSMC_D14 ------PD9
FSMC_D15 ------PD10

LCD_BL(背光) ----PB0
FSMC_NE4(CS) --->PG12
FSMC_NWE(WR/CLK)--->PD5 
FSMC_NOE(RD) --->PD4
FSMC_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);
    }
}
上一篇:容器服务ACK支持AliyunLinux2和场景优化


下一篇:2013年7月14日-地图展示