12回答

0收藏

[原创] 给12864增加触屏

51单片机 51单片机 9749 人阅读 | 12 人回复 | 2015-01-29

放假了,这几天闲的没事做就想给12864增加个触屏来玩。因为以前玩触屏都是玩彩屏的,12864是不带触屏的大家都知道,所以就打算自己加。
首先量好屏幕的尺寸,然后去淘宝买块触屏板,我直接买了3.0寸(77*45)的,大小刚刚好,然后别忘了触屏的驱动芯片,这里我用了很常用的2046芯片。
这里特别提醒一下,如果你的焊接能力不是很强的话建议多买几块芯片,最好能买个转接板……不然制作过程中可能你会废掉很多芯片的……相信我。对了,记得买排针!

接下来就是焊接了,当然我们得先知道芯片的每个引脚拿来干嘛的,看数据手册。


可以参考手册上的那个电路来制作,当然实际上我们并不需要7、8两个引脚,13脚是busy,也不需要。其他的都是必须的,9、10两个脚记得连接到VCC,不然似乎会导致数据读不出?
嗯,然后开始制作吧。首先我们需要引出5根数据脚,外加2个电源脚,总共7个引脚,当然你可以把VCC和GND寄生在12864上,这样就不需要额外引出,但是我建议还是引出用独立的电源吧,会好一些。也就是说我们至少要用5根排针引出引脚,嗯,首先你得找好你要在屏幕的什么位置引出你的触屏脚,我是直接在屏幕原来的引脚右边引出。

这个是我已经做好的成品,用热熔胶把线和芯片都固定好了。引脚就在原来排针的旁边引出。排针的GND我是把屏幕那块挂掉了一点焊在上面,所以GND是和屏幕共用的。其他的都是单独引出,用0.1mm的漆包线连接。然后你就得开始焊芯片了……用漆包线把下面这些圈了圈的线都接上

红色全接VCC,黑色接GND,绿色的全部引出到排针,同时IRQ还需要一个1-10K的电阻上拉,然后紫色的都接触屏,剩下的悬空不接。

这里我友情提醒您:芯片别离排针太远,不然无法读出数据的!上面的图就是个例子……最左边是原来焊的芯片,读不出数据,已经废掉了【毕竟引脚太细小,我原本是打算直接在板子上刻出焊盘焊上的,结果太远了,而且焊上还不好取下,最后是把一个引脚弄断了,废了】……
所以能靠输出排针近一点就尽量靠近一点,不要离的太远!不然板子上也有很多线路会产生电磁波造成干扰的。特别漆包线又那么细……
这也是原来焊的……就屏幕最顶端那个位置,线是接对了,但是读不出。


用漆包线慢慢连接吧。
焊好后,把接触屏的X+X-Y+Y-从屏幕附近的孔穿过来,不然漆包线很细直接从屏幕旁边引出去很容易在使用过程中弄断的。注意触屏的排线方向是向哪边的,如果是向背光源就从背光源这边引出,如果是在另一头就从另一边引出,这个问题在最初放置芯片的时候就要考虑好,你得兼顾引出的排针位置和触屏的位置,尽量最短距离。
全部引出焊好后在排针那里的VCC和GND之间可以焊上一个0.1μF的瓷片电容,如上上上图,当时仅仅是测试我就用了个直插的104,然后制作差不多完成后我换成了贴片的,所以在成品里基本看不到那个电容~尽量做的好一点嘛……
我买的触屏引线方向是靠背光源的,所以就从背光源那边引出,先留长一点没关系,因为你还得测试能不能用。
焊好到这里就给单片机写好测试程序,只要能读出数据就可以继续了。【程序部分在二楼】


测试好能读出数据后,开始最后调整,把触屏贴好固定在屏幕上,然后把连接触屏的漆包线剪短到恰好接上然后焊好
,再测试一次没有问题后用热熔胶把线全部封起来固定在板子上,再测试一次,确认没问题你的触屏就添加成功了。



分享到:
回复

使用道具 举报

回答|共 12 个

倒序浏览

沙发

PokeBox

发表于 2015-1-29 15:19:39 | 只看该作者

此楼是触屏测试程序

本帖最后由 PokeBox 于 2015-1-29 16:15 编辑

嗯,程序部分的话其实要写还是很简单的嘛……因为我偷懒了,直接把2个程序合成1个,首先你得准备2个原料~这一节我就教大家怎么把2个程序合成1个吧~以后要写程序就可以照搬别人的然后稍加修改一下自己就可以剩下很多时间了。

首先需要2个东西,一个是12864的基本程序,一个是彩屏的触屏测试程序,我直接用了最简单的,显示字符,因为我的12864是带字库的,所以就可以做基本的测试,触屏后显示芯片输出的AD值,只要有AD值就说明你的硬件制作成功了。

那么开始吧。
首先贴出12864的基本程序,主函数因为我们要自己写,原来的不需要,所以我就直接删掉了~
  1. /*-----------------------------------------------
  2. 功能:单个菜单,多页显示,不带子菜单
  3. ------------------------------------------------*/
  4. #include <reg52.h>
  5. #include <intrins.h>
  6. #include "delay.h"

  7. #define uchar unsigned char
  8. #define uint unsigned int
  9. #define        ulong unsigned long
  10. sbit RS = P0^5;
  11. sbit RW = P0^6;
  12. sbit E  = P0^7;

  13. sbit PSB   = P0^2;
  14. sbit PAUSE = P0^3;
  15. sbit RES   = P0^4;

  16. #define DataPort P2        //单片机 P2<------> 液晶DB0-DB7

  17. /*------------------------------------------------
  18.                     检测忙位
  19. ------------------------------------------------*/
  20. void Check_Busy()
  21. {
  22.     RS=0;
  23.     RW=1;
  24.     E=1;
  25.     DataPort=0xff;
  26.     while((DataPort&0x80)==0x80);//忙则等待
  27.     E=0;
  28. }
  29. /*------------------------------------------------
  30.                    写命令
  31. ------------------------------------------------*/
  32. void Write_Cmd(unsigned char Cmd)
  33. {
  34.         Check_Busy();
  35.         RS=0;
  36.         RW=0;
  37.         E=1;
  38.         DataPort=Cmd;
  39.         DelayUs2x(5);
  40.         E=0;
  41.         DelayUs2x(5);
  42. }
  43. /*------------------------------------------------
  44.                     写数据
  45. ------------------------------------------------*/
  46. void Write_Data(unsigned char Data)
  47. {
  48.         Check_Busy();
  49.         RS=1;
  50.         RW=0;
  51.         E=1;
  52.         DataPort=Data;
  53.         DelayUs2x(5);
  54.         E=0;
  55.         DelayUs2x(5);
  56. }
  57. /*------------------------------------------------
  58.                    液晶屏初始化
  59. ------------------------------------------------*/
  60. void Init_ST7920()
  61. {
  62.    DelayMs(40);           //大于40MS的延时程序
  63.    PSB=1;                 //设置为8BIT并口工作模式
  64.    DelayMs(1);            //延时
  65.    RES=0;                 //复位
  66.    DelayMs(1);            //延时
  67.    RES=1;                 //复位置高
  68.    DelayMs(10);
  69.    Write_Cmd(0x30);       //选择基本指令集
  70.    DelayUs2x(50);         //延时大于100us
  71.    Write_Cmd(0x30);       //选择8bit数据流
  72.    DelayUs2x(20);         //延时大于37us
  73.    Write_Cmd(0x0c);       //开显示(无游标、不反白)
  74.    DelayUs2x(50);         //延时大于100us
  75.    Write_Cmd(0x01);       //清除显示,并且设定地址指针为00H
  76.    DelayMs(15);           //延时大于10ms
  77.    Write_Cmd(0x06);       //指定在资料的读取及写入时,设定游标的移动方向及指定显示的移位,光标从右向左加1位移动
  78.    DelayUs2x(50);         //延时大于100us
  79. }

  80. /*------------------------------------------------
  81.                    显示用户自定义字符
  82. ------------------------------------------------*/
  83. void DisplayCGRAM(unsigned char x,unsigned char y)
  84. {
  85. switch(y)
  86.      {
  87.          case 1: Write_Cmd(0x80+x);break;
  88.          case 2: Write_Cmd(0x90+x);break;
  89.          case 3: Write_Cmd(0x88+x);break;
  90.          case 4: Write_Cmd(0x98+x);break;
  91.       default:break;
  92.         }
  93.     Write_Data(00);
  94.     Write_Data(00);

  95. }
  96. /*------------------------------------------------
  97.                    显示字符串
  98. x:横坐标值,范围0~8
  99. y:纵坐标值,范围1~4
  100. ------------------------------------------------*/
  101. void LCD_PutString(unsigned char x,unsigned char y,unsigned char code *s)
  102. {
  103. switch(y)
  104.      {
  105.          case 1: Write_Cmd(0x80+x);break;
  106.          case 2: Write_Cmd(0x90+x);break;
  107.          case 3: Write_Cmd(0x88+x);break;
  108.          case 4: Write_Cmd(0x98+x);break;
  109.       default:break;
  110.         }
  111. while(*s>0)
  112.    {
  113.       Write_Data(*s);
  114.       s++;
  115.       DelayUs2x(50);
  116.    }
  117. }
  118. /*------------------------------------------------
  119.                       清屏
  120. ------------------------------------------------*/
  121. void ClrScreen()
  122. {
  123.    Write_Cmd(0x01);
  124.    DelayMs(15);
  125. }
复制代码
ok,然后准备一个触屏的程序,别人都已经写为一个文件了很方便,直接拿过来修改一下即可。下面贴出原来的代码
  1. #include <reg51.h>
  2. #include <intrins.h>
  3. #include <sys\sys.h>
  4. #include <touch\touch.h>
  5. #include <lcd\lcd.h>

  6. //***因触摸屏批次不同等原因,默认的校准参数值可能会引起触摸识别不准,建议校准后再使用,不建议使用固定的默认校准参数
  7. //uint vx=15242,vy=11131;        //比例因子,此值除以1000之后表示多少个AD值代表一个像素点
  8. //uint chx=3898,chy=145;                //默认像素点坐标为0时的AD起始值
  9. uint vx=15332,vy=11249;
  10. uint chx=137,chy=3957;

  11. //***因触摸屏批次不同等原因,默认的校准参数值可能会引起触摸识别不准,建议校准后再使用,不建议使用固定的默认校准参数

  12. struct tp_pix_  tp_pixad,tp_pixlcd;         //当前触控坐标的AD值,前触控坐标的像素值

  13. uchar tpstate(void)
  14. {
  15.         return IRQ;
  16. }

  17. //**********************************************************
  18. void spistar(void)                //SPI开始
  19. {
  20.         CS=1;
  21.         DCLK=1;
  22.         DIN=1;
  23.         DCLK=1;
  24. }
  25. //**********************************************************
  26. void WriteCharTo7843(unsigned char num)                  //SPI写数据
  27. {
  28.         unsigned char count=0;
  29.         DCLK=0;
  30.         for(count=0;count<8;count++)
  31.         {
  32.                 num<<=1;
  33.                 DIN=CY;
  34.                 DCLK=0; _nop_();_nop_();_nop_();        //上升沿有效
  35.                 DCLK=1; _nop_();_nop_();_nop_();
  36.         }
  37. }
  38. //**********************************************************
  39. uint ReadFromCharFrom7843()                         //SPI 读数据
  40. {
  41.         uchar count=0;
  42.         uint Num=0;
  43.         for(count=0;count<12;count++)
  44.         {
  45.                 Num<<=1;
  46.                 DCLK=1; _nop_();_nop_();_nop_();        //下降沿有效
  47.                 DCLK=0; _nop_();_nop_();_nop_();
  48.                 if(DOUT)
  49.                 {
  50.                         Num|=1;
  51.                 }
  52.         }
  53.         return(Num);
  54. }
  55. //从7846/7843/XPT2046/UH7843/UH7846读取adc值          0x90=y   0xd0-x
  56. uint ADS_Read_AD(unsigned char CMD)
  57. {
  58.         uint l;
  59.         CS=0;
  60.         WriteCharTo7843(CMD);                //送控制字即用差分方式读X坐标 详细请见有关资料
  61.         DCLK=1; _nop_();_nop_();_nop_();_nop_();
  62.         DCLK=0; _nop_();_nop_();_nop_();_nop_();
  63.         l=ReadFromCharFrom7843();
  64.         CS=1;
  65.         return l;
  66. }
  67. //读取一个坐标值
  68. //连续读取READ_TIMES次数据,对这些数据升序排列,
  69. //然后去掉最低和最高LOST_VAL个数,取平均值
  70. #define READ_TIMES 15 //读取次数
  71. #define LOST_VAL 5          //丢弃值
  72. uint ADS_Read_XY(uchar xy)
  73. {
  74.         uint i, j;
  75.         uint buf[READ_TIMES];
  76.         uint sum=0;
  77.         uint temp;
  78.         for(i=0;i<READ_TIMES;i++)
  79.         {
  80.                 buf[i]=ADS_Read_AD(xy);
  81.         }
  82.         for(i=0;i<READ_TIMES-1; i++)//排序
  83.         {
  84.                 for(j=i+1;j<READ_TIMES;j++)
  85.                 {
  86.                         if(buf[i]>buf[j])//升序排列
  87.                         {
  88.                                 temp=buf[i];
  89.                                 buf[i]=buf[j];
  90.                                 buf[j]=temp;
  91.                         }
  92.                 }
  93.         }
  94.         sum=0;
  95.         for(i=LOST_VAL;i<READ_TIMES-LOST_VAL;i++) sum+=buf[i];
  96.         temp=sum/(READ_TIMES-2*LOST_VAL);
  97.         return temp;
  98. }
  99. //带滤波的坐标读取
  100. //最小值不能少于100.
  101. uchar Read_ADS(uint *x,uint *y)
  102. {
  103.         uint xtemp,ytemp;
  104.         xtemp=ADS_Read_XY(CMD_RDX);
  105.         ytemp=ADS_Read_XY(CMD_RDY);
  106.         if(xtemp<100||ytemp<100) return 0;//读数失败
  107.         *x=xtemp;
  108.         *y=ytemp;
  109.         return 1;//读数成功
  110. }
  111. //2次读取ADS7846,连续读取2次有效的AD值,且这两次的偏差不能超过
  112. //50,满足条件,则认为读数正确,否则读数错误.
  113. //该函数能大大提高准确度
  114. #define ERR_RANGE 20 //误差范围
  115. uchar Read_ADS2(uint *x,uint *y)
  116. {
  117.         uint x1,y1;
  118.          uint x2,y2;
  119.          uchar flag;
  120.         flag=Read_ADS(&x1,&y1);
  121.         if(flag==0) return(0);
  122.         flag=Read_ADS(&x2,&y2);
  123.         if(flag==0) return(0);

  124.         if(((x2<=x1&&x1<x2+ERR_RANGE)||(x1<=x2&&x2<x1+ERR_RANGE))        //前后两次采样在+-ERR_RANGE内
  125.         &&((y2<=y1&&y1<y2+ERR_RANGE)||(y1<=y2&&y2<y1+ERR_RANGE)))
  126.         {
  127.                 *x=(x1+x2)>>1;
  128.                 *y=(y1+y2)>>1;
  129.                 return 1;
  130.         }else return 0;
  131. }
  132. //精确读取一次坐标,校准的时候用的
  133. uchar Read_TP_Once(void)
  134. {
  135.         uchar re=0;
  136.         uint x1,y1;
  137.         while(re==0)
  138.         {
  139.                 while(!Read_ADS2(&tp_pixad.x,&tp_pixad.y));
  140.                 delayms(10);
  141.                 while(!Read_ADS2(&x1,&y1));
  142.                 if(tp_pixad.x==x1&&tp_pixad.y==y1)
  143.                 {
  144.                         re=1;
  145.                 }
  146.         }
  147.         return re;
  148. }
  149. //////////////////////////////////////////////////
  150. //与LCD部分有关的函数
  151. //画一个触摸点
  152. //用来校准用的
  153. void Drow_Touch_Point(uint x,uint y)
  154. {
  155.         LCD_DrawLine(x-12,y,x+13,y);//横线
  156.         LCD_DrawLine(x,y-12,x,y+13);//竖线
  157.         LCD_DrawPoint(x+1,y+1);
  158.         LCD_DrawPoint(x-1,y+1);
  159.         LCD_DrawPoint(x+1,y-1);
  160.         LCD_DrawPoint(x-1,y-1);
  161. //        Draw_Circle(x,y,6);//画中心圈
  162. }
  163. //转换结果
  164. //根据触摸屏的校准参数来决定转换后的结果,保存在X0,Y0中
  165. uchar Convert_Pos(void)
  166. {
  167.         uchar l=0;
  168.         if(Read_ADS2(&tp_pixad.x,&tp_pixad.y))
  169.         {
  170.                 l=1;
  171.                 tp_pixlcd.x=tp_pixad.x>chx?((ulong)tp_pixad.x-(ulong)chx)*1000/vx:((ulong)chx-(ulong)tp_pixad.x)*1000/vx;
  172.                 tp_pixlcd.y=tp_pixad.y>chy?((ulong)tp_pixad.y-(ulong)chy)*1000/vy:((ulong)chy-(ulong)tp_pixad.y)*1000/vy;
  173.         }
  174.         return l;
  175. }
  176. //触摸屏校准代码
  177. //得到四个校准参数
  178. void Touch_Adjust(void)
  179. {
  180.         float vx1,vx2,vy1,vy2;  //比例因子,此值除以1000之后表示多少个AD值代表一个像素点
  181.         uint chx1,chx2,chy1,chy2;//默认像素点坐标为0时的AD起始值
  182. ……用不上,省略,不然篇幅太长……
  183. }
复制代码
嗯……这是触屏的程序,然后,我们把两串代码合成一个文件,因为触屏是基于显示功能之上所以把触屏的程序复制粘贴到12864的程序文件里,然后整理一下,把程序定义的部分全部放在最全面,函数放在12864的函数下面,然后稍加整理,这时你就完成了1/3的工程了,然后把触屏的调用了屏幕显示的部分全部用12864的显示函数替换掉,没法使用的函数就直接删掉它不要,比如下面的触屏校准函数这里我们用不上就直接删,最后剩下的就是我们要的函数体程序。然后重新编写主函数,已经知道Convert_Pos()函数是触屏的值转换函数,所以我们只要调用这个就可以,其他函数都可以不用理会。但是别乱删函数体,因为你得弄清楚你需要用的函数的依赖关系,依赖用到的函数你不能删,不然会报错的。
最后,主函数写成这样:
  1. #define Xa tp_pixad.x
  2. #define Ya tp_pixad.y
  3. #define Xc tp_pixlcd.x
  4. #define Yc tp_pixlcd.y
  5. main()
  6. {
  7.         spistar();
  8.         Init_ST7920();   //初始化
  9.         while(1)
  10.         {
  11.                         if(Penirq==0)//按键按下了
  12.                         {
  13.                                 if(Convert_Pos())//得到单次按键值
  14.                                 {
  15.                                         LCD_PutString(0,1,"Xa:");LCD_ShowNum(Xa);
  16.                                         LCD_PutString(0,2,"Ya:");LCD_ShowNum(Ya);
  17.                                         LCD_PutString(0,3,"X:");LCD_ShowNum(Xc);
  18.                                         LCD_PutString(0,4,"Y:");LCD_ShowNum(Yc);
  19.                                 }
  20.                         }
  21.         }
  22. }
复制代码
OK,保存拿去编译,

有9个警告,都是一些函数没有用到的警告,说明没用到的函数我们没删掉也没被调用,无关紧要,无视,只要没错误就OK了,毕竟只是测试触屏能不能用。但是真正想要用来做东西的话就最好做到0错误0警告!
写入单片机运行,效果如图,点击触屏任意位置都有不同的数值输出就说明你的触屏制作OK了。

下面我直接放源码吧,方便大家测试,至于引脚的定义大家自己修改好了。使用的单片机是STC12C5A60S2的。

25-12864液晶菜单操作.7z

6.75 KB, 阅读权限: 10, 下载次数: 5

售价: 1 与非币  [记录]

触屏测试程序

板凳

PokeBox

发表于 2015-1-29 15:49:35 | 只看该作者

此楼是高级程序,屏幕画板

本帖最后由 PokeBox 于 2015-1-29 16:15 编辑

嗯……测试成功并且屏幕也做好了的话可以来玩点好玩的,用12864画画~这是必须的嘛……
首先我们要准备一个12864的画点程序,一样可以去百度到……【我很懒的……当然不要觉得用别人的东西很XX,但是你能把别人的东西用到自己的东西上那就是你的本事,开源精神不也是酱紫的么~这次51我也来玩一下开源~】
一样的,原料,12864的程序,带有画点的功能的,触屏程序还是使用↑_↑楼上的,两者结合。
简单说一下这个画点的函数:
  1. void drawPoint(unsigned char x,unsigned char y,unsigned char color)//XY坐标,点亮还是熄灭
  2. {
  3.         unsigned char row,collum,cbite;
  4.         unsigned char tempH,tempL;
  5.         writeCommand(0x34);//写命令
  6.         writeCommand(0x36);
  7. //坐标转换
  8.         collum=x>>4;
  9.         cbite=x&0x0f;
  10.         if(y<32)
  11.                 row=y;
  12.         else{
  13.                 row=y-32;
  14.                 collum+=8;
  15.         }
  16.         writeCommand(0x80+row);//写命令到坐标,就是我们要画点在屏幕上的位置
  17.         writeCommand(0x80+collum);
  18.         readData();        //这个不能少,不然会不正常,之前写那个触屏测试的时候有这个会报警告,不知道为什么然后我就删掉就没问题,毕竟那个测试程序也没用到这个函数,但在这里就正常了……但如果在这删掉这个显示会不正常,这是读屏幕内部RAM的函数,不写会把旁边的像素一起带过去……
  19.         tempH=readData();
  20.         tempL=readData();
  21.         writeCommand(0x80+row);
  22.         writeCommand(0x80+collum);
  23.         if (color)//判断是要显示还是擦
  24.         {
  25.                 if(cbite<8)
  26.                 {
  27.                         tempH|=(1<<(7-cbite));//显示点
  28.                 }
  29.                 else
  30.                 {
  31.                         tempL|=(1<<(15-cbite));
  32.                 }
  33.         }
  34.         else
  35.         {
  36.                 if(cbite<8)
  37.                 {
  38.                         tempH&=~(1<<(7-cbite));//取反擦除点
  39.                 }
  40.                 else
  41.                 {
  42.                         tempL&=~(1<<(15-cbite));
  43.                 }
  44.         }
  45.         writeData(tempH);//写入新点数据
  46.         writeData(tempL);
  47.         writeCommand(0x30);
  48. }
复制代码
嘛,注释可能不是很准确,毕竟不是我写的←_←,但是知道大概是怎么回事就可以了。
其实画点是很容易的,和↑_↑楼上那个程序基本是一样的,只是把坐标显示改成画点就OK……
如代码所示……

嗯,这里我用了个判断语句把那些没调用的函数写在了这里,然后编译就没问题了~


开始画画吧哈哈……
这个程序代码里包括了一些画直线和画圆的函数,画圆的我是从彩屏那弄过来的……其他是原来的。那么,上代码大家拿去研究吧~

对了,需要注意的是,那个触屏校准程序在这里估计是算法不对,校准值始终是不正确,所以我是自己手动把屏幕的值给算出来了,如果你自己弄的触屏用↑_↑楼上的程序测试感觉坐标不对的话就自己手动的校准一下,算法如下:

12864触屏程序.7z

5.71 KB, 阅读权限: 20, 下载次数: 3

售价: 2 与非币  [记录]

触屏测试程序

地板

奋斗哥

发表于 2015-1-29 16:14:29 | 只看该作者

so 强大,膜拜!
5#

liujincai

发表于 2015-1-29 16:16:11 | 只看该作者

精彩,学习!!!
6#

jameswang

发表于 2015-1-29 16:16:27 | 只看该作者

多谢楼主分享!!!牛~~~
7#

shaoziyang

发表于 2015-1-29 20:23:45 | 只看该作者

是电阻屏,不是电容屏啊。
8#

PokeBox

发表于 2015-1-29 21:12:10 | 只看该作者

shaoziyang 发表于 2015-1-29 20:23
是电阻屏,不是电容屏啊。

小屏幕电容屏没意义……
9#

PokeBox

发表于 2015-1-29 21:13:55 | 只看该作者

shaoziyang 发表于 2015-1-29 20:23
是电阻屏,不是电容屏啊。

或者说,这种屏幕这种设备用电容屏毫无意义……就128x64分辨率的屏幕用电容屏也太那啥了……
10#

shaoziyang

发表于 2015-1-29 22:14:30 | 只看该作者

对啊,电阻屏精度还高一些。
您需要登录后才可以回帖 注册/登录

本版积分规则

关闭

站长推荐上一条 /3 下一条