先上代码:
void Draw_Keyboard_Layout(void)
{
LCD_ShowString(5,25,200,16,16,"Power by Vincent-NJW");
LCD_DrawRectangle(5,110,234,314);//矩形框
LCD_DrawLine(62,110,62,314);//三条竖线
LCD_DrawLine(119,110,119,314);
LCD_DrawLine(177,110,177,314);
LCD_DrawLine(5,161,234,161);//三条横线
LCD_DrawLine(5,212,234,212);
LCD_DrawLine(5,263,234,263);
LCD_ShowString(31,131,200,16,16,"1");//写数字和符号
LCD_ShowString(88,131,200,16,16,"2");
LCD_ShowString(145,131,200,16,16,"3");
LCD_ShowString(202,131,200,16,16,"+");
LCD_ShowString(31,182,200,16,16,"4");
LCD_ShowString(88,182,200,16,16,"5");
LCD_ShowString(145,182,200,16,16,"6");
LCD_ShowString(202,182,200,16,16,"-");
LCD_ShowString(31,233,200,16,16,"7");
LCD_ShowString(88,233,200,16,16,"8");
LCD_ShowString(145,233,200,16,16,"9");
LCD_ShowString(202,233,200,16,16,"*");
LCD_ShowString(31,280,200,16,16,".");
LCD_ShowString(88,284,200,16,16,"0");
LCD_ShowString(145,284,200,16,16,"=");
LCD_ShowString(202,284,200,16,16,"/");
LCD_DrawLine(5,68,234,68);
LCD_ShowString(5,50,200,16,16,"process:");
LCD_ShowString(5,75,200,16,16,"answer:");
LCD_DrawLine(5,93,234,93);
}
//触摸扫描函数 .:10 =:11 +:12 -:13 *:14 /:15 ,没有触摸或者触摸到键盘以外的位置返回16
int Touch_Return(void)
{
tp_dev.scan(0);
if(tp_dev.sta&TP_PRES_DOWN) //触摸屏被按下
{
if(tp_dev.x[0]>=5&&tp_dev.x[0]<=62&&tp_dev.y[0]>=110&&tp_dev.y[0]<=161)//按键按下会执行的操作
{
vTaskResume(Calculate_Task_Handler);
return 1;
}
if(tp_dev.x[0]>=5&&tp_dev.x[0]<=62&&tp_dev.y[0]>=161&&tp_dev.y[0]<=212)
{
vTaskResume(Calculate_Task_Handler);
return 4;
}
if(tp_dev.x[0]>=5&&tp_dev.x[0]<=62&&tp_dev.y[0]>=212&&tp_dev.y[0]<=263)
{
vTaskResume(Calculate_Task_Handler);
return 7;
}
if(tp_dev.x[0]>=5&&tp_dev.x[0]<=62&&tp_dev.y[0]>=263&&tp_dev.y[0]<=314)
{
vTaskResume(Calculate_Task_Handler);
return 10;
}
if(tp_dev.x[0]>=62&&tp_dev.x[0]<=119&&tp_dev.y[0]>=110&&tp_dev.y[0]<=161)//第二列
{
vTaskResume(Calculate_Task_Handler);
return 2;
}
if(tp_dev.x[0]>=62&&tp_dev.x[0]<=119&&tp_dev.y[0]>=161&&tp_dev.y[0]<=212)
{
vTaskResume(Calculate_Task_Handler);
return 5;
}
if(tp_dev.x[0]>=62&&tp_dev.x[0]<=119&&tp_dev.y[0]>=212&&tp_dev.y[0]<=263)
{
vTaskResume(Calculate_Task_Handler);
return 8;
}
if(tp_dev.x[0]>=62&&tp_dev.x[0]<=119&&tp_dev.y[0]>=263&&tp_dev.y[0]<=314)
{
vTaskResume(Calculate_Task_Handler);
return 0;
}
if(tp_dev.x[0]>=119&&tp_dev.x[0]<=177&&tp_dev.y[0]>=110&&tp_dev.y[0]<=161)//第三列
{
vTaskResume(Calculate_Task_Handler);
return 3;
}
if(tp_dev.x[0]>=119&&tp_dev.x[0]<=177&&tp_dev.y[0]>=161&&tp_dev.y[0]<=212)
{
vTaskResume(Calculate_Task_Handler);
return 6;
}
if(tp_dev.x[0]>=119&&tp_dev.x[0]<=177&&tp_dev.y[0]>=212&&tp_dev.y[0]<=263)
{
vTaskResume(Calculate_Task_Handler);
return 9;
}
if(tp_dev.x[0]>=119&&tp_dev.x[0]<=177&&tp_dev.y[0]>=263&&tp_dev.y[0]<=314)
{
vTaskResume(Calculate_Task_Handler);
return 11;
}
if(tp_dev.x[0]>=177&&tp_dev.x[0]<=234&&tp_dev.y[0]>=110&&tp_dev.y[0]<=161)//第四列
{
vTaskResume(Calculate_Task_Handler);
return 12;
}
if(tp_dev.x[0]>=177&&tp_dev.x[0]<=234&&tp_dev.y[0]>=161&&tp_dev.y[0]<=212)
{
vTaskResume(Calculate_Task_Handler);
return 13;
}
if(tp_dev.x[0]>=177&&tp_dev.x[0]<=234&&tp_dev.y[0]>=212&&tp_dev.y[0]<=263)
{
vTaskResume(Calculate_Task_Handler);
return 14;
}
if(tp_dev.x[0]>=177&&tp_dev.x[0]<=234&&tp_dev.y[0]>=263&&tp_dev.y[0]<=314)
{
vTaskResume(Calculate_Task_Handler);
return 15;
}
else
return 16;
}
else //没有按键按下的时候
{
delay_ms(10);
return 16;
}
}
void calculate_task(void *pvParameters)
{
int stage=0;//stage:用于判断第一个输入数的小数点前部分和后部分,输入第一个数时按下小数点置1。
int stage2=0;//stage:用于判断第二个输入数的小数点前部分和后部分,输入第一个数时按下小数点置1。
int stage1=0;//用于判断输入的是第几个数,按下+-*/时置1。
int stage3=0;//按下=置1
int Number_Distance=68;
int Number2_Distance=60;
int Number_Before_Point=0;//记录第一个数小数点后多少位,用于算number大小
int Number1_Before_Point=0;//记录第一个数小数点后多少位,用于算number大小
int Calculate_Stage;//存储按的是+-*/,+:1 -:2 *:3 /:4
double number=0;//第一个数存储
double number1=0;//第二个数存储
double number2=0;//结果存储
int Number2_Int=1;//记录number2整数部分有几位
int Number2_Int1;//记录number2整数部分有几位时的中间变量
int Number2_Float;//显示number2小数部分
while(1)
{
if(stage1==0&&(Touch_Return_Number==0||Touch_Return_Number==1||Touch_Return_Number==2||Touch_Return_Number==3||Touch_Return_Number==4||Touch_Return_Number==5||
Touch_Return_Number==6||Touch_Return_Number==7||Touch_Return_Number==8||Touch_Return_Number==9||Touch_Return_Number==10))//第一个数处理
{
if(stage==0)//小数点前面部分
{
if(Touch_Return_Number==10)
{
stage=1;//按小数点stage置1
LCD_ShowString(Number_Distance,50,200,16,16,".");
Number_Distance+=8;
vTaskSuspend(Calculate_Task_Handler);
}
else
{
LCD_ShowNum(Number_Distance,50,Touch_Return_Number,1,16);
Number_Distance+=8;
number=number*10+Touch_Return_Number;
//printf("%d\r\n",number);
vTaskSuspend(Calculate_Task_Handler);
}
}
else//小数点后面部分
{
Number_Before_Point++;
LCD_ShowNum(Number_Distance,50,Touch_Return_Number,1,16);
Number_Distance+=8;
number=number+Touch_Return_Number/(10.0*Number_Before_Point);
vTaskSuspend(Calculate_Task_Handler);
}
}
if(stage3==0&&stage1==0&&(Touch_Return_Number==12||Touch_Return_Number==13||Touch_Return_Number==14||Touch_Return_Number==15))//按下+-*/之后把stage1置1
{
stage1=1;
if(Touch_Return_Number==12)
{
LCD_ShowString(Number_Distance,50,200,16,16,"+");
Calculate_Stage=1;
vTaskSuspend(Calculate_Task_Handler);
}
if(Touch_Return_Number==13)
{
LCD_ShowString(Number_Distance,50,200,16,16,"-");
Calculate_Stage=2;
vTaskSuspend(Calculate_Task_Handler);
}
if(Touch_Return_Number==14)
{
LCD_ShowString(Number_Distance,50,200,16,16,"*");
Calculate_Stage=3;
vTaskSuspend(Calculate_Task_Handler);
}
if(Touch_Return_Number==15)
{
LCD_ShowString(Number_Distance,50,200,16,16,"/");
Calculate_Stage=4;
vTaskSuspend(Calculate_Task_Handler);
}
Number_Distance+=8;
}
if(stage3==0&&stage1==1&&(Touch_Return_Number==0||Touch_Return_Number==1||Touch_Return_Number==2||Touch_Return_Number==3||Touch_Return_Number==4||Touch_Return_Number==5||
Touch_Return_Number==6||Touch_Return_Number==7||Touch_Return_Number==8||Touch_Return_Number==9||Touch_Return_Number==10))//第二个数处理
{
if(stage2==0)//小数点前面部分
{
if(Touch_Return_Number==10)
{
stage2=1;//按小数点stage置1
LCD_ShowString(Number_Distance,50,200,16,16,".");
Number_Distance+=8;
vTaskSuspend(Calculate_Task_Handler);
}
else
{
LCD_ShowNum(Number_Distance,50,Touch_Return_Number,1,16);
Number_Distance+=8;
number1=number1*10+Touch_Return_Number;
//printf("%d\r\n",number);
vTaskSuspend(Calculate_Task_Handler);
}
}
else//小数点后面部分
{
Number1_Before_Point++;
LCD_ShowNum(Number_Distance,50,Touch_Return_Number,1,16);
Number_Distance+=8;
number1=number1+Touch_Return_Number/(10.0*Number1_Before_Point);
vTaskSuspend(Calculate_Task_Handler);
}
}
if(Touch_Return_Number==11&&stage1==1&&stage3==0)//按下=
{
stage3=1;
if(Calculate_Stage==1)
number2=number+number1;
if(Calculate_Stage==2)
number2=number-number1;
if(Calculate_Stage==3)
number2=number*number1;
if(Calculate_Stage==4)
{
if(number1==0)
{
LCD_ShowString(Number2_Distance,75,200,16,16,"error");
vTaskSuspend(Calculate_Task_Handler);
}
else
number2=number/number1;
}
Number2_Int1=(int)number2;
while(1)
{
if((Number2_Int1/10)==0)
break;
else
{
Number2_Int++;
Number2_Int1/=10;
}
}
printf("%d\r\n",Number2_Int);
printf("%d\r\n",(int)number2);
printf("%f\r\n",number);
printf("%f\r\n",number1);
printf("%f\r\n",number2);
if(number2<0)
{
LCD_ShowString(Number2_Distance,75,200,16,16,"-");
Number2_Distance+=8;
number2=-number2;
}
LCD_ShowNum(Number2_Distance,75,(int)number2,Number2_Int,16);
Number2_Distance=Number2_Distance+8*Number2_Int;
LCD_ShowString(Number2_Distance,75,200,16,16,".");
Number2_Distance+=8;
if((((int)(number2*10))%10)==0)
{
LCD_ShowNum(Number2_Distance,75,0,1,16);
Number2_Distance+=8;
}
Number2_Float=(int)((number2-(int)number2)*1000);
LCD_ShowNum(Number2_Distance,75,Number2_Float,3,16);
vTaskSuspend(Calculate_Task_Handler);
}
}
}
以上展示的是最主要的三个函数,第一个是画页面布局,第二个是按下触摸屏的返回值,第三个是简易计算器的逻辑。
首先第一点我要说明的是我的代码是在正点原子的mini板上跑的,对于硬件的连接和驱动协议上面我这里就不在过多的解释。第一点原因是mini板的原理图大家在正点原子的官网上都可以下的到,第二点是在正点原子给出的官方历程库里面包含了板载全部硬件的驱动程序。虽然对于我们来说驱动程序的编写和理解也是非常重要的,但是作者在写程序的时候只有一下午时间,由于时间比较紧迫,我们就保持着拿来主义的态度,先基本实现自己的功能,对于驱动程序的理解后期可以在慢慢深究。
第二点我要说的是我的代码都是放到freertos里面跑的,我创建了两个任务,第一个是触摸屏的扫描返回,第二个是计算器的逻辑。通过触摸一个数字或者符号来实现对计算器逻辑任务的解挂让计算器逻辑函数一次执行,这样做的好处是会让整个逻辑看起来更加的清晰和简单。
接下来我来讲一下三个函数功能,第一个void Draw_Keyboard_Layout(void)是在触摸屏上画一个4*4的方格以及内部填充的符号合数字以及计算的过程和结果的显示,操作比较简单就是调用LCD的API函数。效果如下:
第二个函数是触摸扫描函数int Touch_Return(void)原理是在触摸屏的扫描任务中不断检测用户按下位置的坐标,在检测到用户按下后把坐标存放在tp_dev.x[0]和tp_dev.y[0]里面,然后与我画的框格的范围做比较,根据不同的范围先解挂计算器任务然后返回对应的值。具体的操作流程我在代码栏都有注释。第三个函数也是最核心的部分void calculate_task(void *pvParameters)计算器的逻辑函数,大概思路是这样的,我用了一个全局变量Touch_Return_Number来实现两个任务之间的通信。当Touch_Return_Number的值符合我在计算器的逻辑函数设定的标准和一系列的标志位都符合条件之后会进入到不同的if语句里面去实现不同的功能。当执行完一个操作之后,我会把计算器逻辑任务挂起,等待用户按下按键之后,触摸屏返回任务会解挂计算器逻辑任务。从而实现,按一个按键执行一次计算器逻辑操作(对应一个if语句)。对应的逻辑大家可以看我代码的注释部分,我写的比较详细,应该可以理解,如果有疑问的话可以加作者的QQ,QQ在文章末尾有给出。
由于作者只有一下午的时间写这些代码,加上触摸的界面比较的简单,所以就没有用STemwin等GUI库。由于开发时间短也导致一些bug我没有来的急及时的更改,第一个就是浮点数的精度问题,由于计算机对浮点数的存储并不是准确的,所以导致在做小数位数比较多的浮点数乘法的时候会有一定的误差,其实可以用一些简单的算法和小技巧提高精度,但是目前程序的效果对于作者来说已经足够了。第二个就是在结果是整数的时候,小数点后面的零会有两个单位的间隔,但是考虑到不影响结果的观察所以就没有更改。还有一些小bug希望读者在实践过程中发现,写的逻辑不是那么的严谨,希望见谅!
name:Vincent-NJW
QQ:1504012979