原创 不带字库的图形点阵LCD模块编程技巧

2012-6-7 11:56 3276 22 24 分类: 软件与OS

带字库的图形点阵LCD模块价格要比不带字库的贵很多,但是使用不带字库的LCD模块,往往在编程上要麻烦许多。最近我做一个PAC控制器的时候,使用不带字库的12864LCD模块,发现在程序上使用一些技巧,能使不带字库的模块用起来和带字库的一样方便。

首先要感谢下面这篇博文:

 http://bbs.ednchina.com/BLOG_ARTICLE_20371.HTM

但是我用其中的代码移植到飞思卡尔的编译环境下出现很多错误,不能直接使用。

下面是演示LCD显示的代码,只需要在函数disp_str()输入显示字符串的位置和字符串就可以了。这样不是和带字库的LCD模块一样方便使用吗?


void LCD_Demo() {

  uchar i,j,len;
  uchar Char_Index; //字符所在的行
  uchar x,y;
  Lcd_Init();
 
  ClearScreen();
 
  disp_str(16,4,"液晶显示MCR MOTOR");  //显示字符串
 
 
}

这其中的关键奥秘在于,输入到函数中的字符串会被编译成ASCII代码,其中汉字占两位uchar,其他字符占一位uchar.请看disp_str()函数内容:

void disp_str(uchar x,uchar y,uchar str[]){
 
   uchar i=0;
   uchar List_X;
  
   
   List_X=x;
 
  
   while(str>0)  //遍历字符串数组。
   {
      
      if (str < 128)  //非汉字的字符被编译为小于128的数值
      {   /* ASCII */
         disp_char(List_X,y,str);
         List_X+=ASC_CHR_WIDTH;
         MDelay(500);
      }
      else   //汉字的字符被编译为大于128的数值,并且占两位uchar
      {   /* 中文 */
     
         disp_hz(List_X,y,str,str[i+1]);
         i++;   //跳一位,因为汉字站两位数组位。
         List_X+=ASC_HZ_WIDTH;
         MDelay(500);
        
      }
      i++;
   }
}

从字符串中解析出来ASCII码后,就可以根据这个来找对应的字模。字模还是要做的,但是字模的格式与一般的不太一样。如下面的代码。

typedef struct typFNT_ASC16   /* 字符字模显示数据结构 */
{
   char Index[1];
   char Msk[ASC_CHR_WIDTH*2];
};
 
struct typFNT_ASC16 const ASC_16[] = {  
/*--  宋体12;  此字体下对应的点阵为:宽x高=8x16   --*/

"0",0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x0F,0x10,0x20,0x20,0x10,0x0F,0x00,
"1",0x00,0x10,0x10,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,
"2",0x00,0x70,0x08,0x08,0x08,0x88,0x70,0x00,0x00,0x30,0x28,0x24,0x22,0x21,0x30,0x00,
"3",0x00,0x30,0x08,0x88,0x88,0x48,0x30,0x00,0x00,0x18,0x20,0x20,0x20,0x11,0x0E,0x00,
"4",0x00,0x00,0xC0,0x20,0x10,0xF8,0x00,0x00,0x00,0x07,0x04,0x24,0x24,0x3F,0x24,0x00,
"5",0x00,0xF8,0x08,0x88,0x88,0x08,0x08,0x00,0x00,0x19,0x21,0x20,0x20,0x11,0x0E,0x00,
"6",0x00,0xE0,0x10,0x88,0x88,0x18,0x00,0x00,0x00,0x0F,0x11,0x20,0x20,0x11,0x0E,0x00,
"7",0x00,0x38,0x08,0x08,0xC8,0x38,0x08,0x00,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x00,
"8",0x00,0x70,0x88,0x08,0x08,0x88,0x70,0x00,0x00,0x1C,0x22,0x21,0x21,0x22,0x1C,0x00,
"9",0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x00,0x31,0x22,0x22,0x11,0x0F,0x00,
"A",0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00,0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20,
"B",0x08,0xF8,0x88,0x88,0x88,0x70,0x00,0x00,0x20,0x3F,0x20,0x20,0x20,0x11,0x0E,0x00,
"C",0xC0,0x30,0x08,0x08,0x08,0x08,0x38,0x00,0x07,0x18,0x20,0x20,0x20,0x10,0x08,0x00,
"D",0x08,0xF8,0x08,0x08,0x08,0x10,0xE0,0x00,0x20,0x3F,0x20,0x20,0x20,0x10,0x0F,0x00,
"E",0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,0x20,0x3F,0x20,0x20,0x23,0x20,0x18,0x00,
"F",0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,0x20,0x3F,0x20,0x00,0x03,0x00,0x00,0x00,
"G",0xC0,0x30,0x08,0x08,0x08,0x38,0x00,0x00,0x07,0x18,0x20,0x20,0x22,0x1E,0x02,0x00,
"H",0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x20,0x3F,0x21,0x01,0x01,0x21,0x3F,0x20,
"I",0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,
"J",0x00,0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00,0x00,
"K",0x08,0xF8,0x88,0xC0,0x28,0x18,0x08,0x00,0x20,0x3F,0x20,0x01,0x26,0x38,0x20,0x00,
"L",0x08,0xF8,0x08,0x00,0x00,0x00,0x00,0x00,0x20,0x3F,0x20,0x20,0x20,0x20,0x30,0x00,
"M",0x08,0xF8,0xF8,0x00,0xF8,0xF8,0x08,0x00,0x20,0x3F,0x00,0x3F,0x00,0x3F,0x20,0x00,
"N",0x08,0xF8,0x30,0xC0,0x00,0x08,0xF8,0x08,0x20,0x3F,0x20,0x00,0x07,0x18,0x3F,0x00,
"O",0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,0x0F,0x10,0x20,0x20,0x20,0x10,0x0F,0x00,
"P",0x08,0xF8,0x08,0x08,0x08,0x08,0xF0,0x00,0x20,0x3F,0x21,0x01,0x01,0x01,0x00,0x00,
"Q",0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,0x0F,0x18,0x24,0x24,0x38,0x50,0x4F,0x00,
"R",0x08,0xF8,0x88,0x88,0x88,0x88,0x70,0x00,0x20,0x3F,0x20,0x00,0x03,0x0C,0x30,0x20,
"S",0x00,0x70,0x88,0x08,0x08,0x08,0x38,0x00,0x00,0x38,0x20,0x21,0x21,0x22,0x1C,0x00,
"T",0x18,0x08,0x08,0xF8,0x08,0x08,0x18,0x00,0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00,
"U",0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00,
"V",0x08,0x78,0x88,0x00,0x00,0xC8,0x38,0x08,0x00,0x00,0x07,0x38,0x0E,0x01,0x00,0x00,
"W",0xF8,0x08,0x00,0xF8,0x00,0x08,0xF8,0x00,0x03,0x3C,0x07,0x00,0x07,0x3C,0x03,0x00,
"X",0x08,0x18,0x68,0x80,0x80,0x68,0x18,0x08,0x20,0x30,0x2C,0x03,0x03,0x2C,0x30,0x20,
"Y",0x08,0x38,0xC8,0x00,0xC8,0x38,0x08,0x00,0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00,
"Z",0x10,0x08,0x08,0x08,0xC8,0x38,0x08,0x00,0x20,0x38,0x26,0x21,0x20,0x20,0x18,0x00,
" ",0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};

 

ASC_16数组的每一行的第一位都是字符,编译是会转变为对应的ASCII码,如果现在要显示的字符是“A”,程序会到ASC_16[] 数组中去找第一位的ASCII代码与"A"相等的。找到后显示后面的字模。下面是disp_char()函数

void disp_char(uchar x, uchar y, uchar c)
{

    uchar i,j,len;
    uchar Char_Index; //字符所在的行
   
    //x <<= 1;
    //y <<= 3;
    if (x & 0x40)
    {
        LCD_CS1 = 0;
        LCD_CS2 = 1;
    }
    else
    {
        LCD_CS1 = 1;
        LCD_CS2 = 0;
    }
   
   
  
    //先找到字符是在数据表里面的哪一行Index=?
   len=sizeof(ASC_16)/sizeof(ASC_16[0]);  //字符表的行数
   for(i=0;i<len;i++)
   {
      if(c == ASC_16.Index[0] ) break;   //找到字符是在哪一行
     
   }
   
   Char_Index=i;

   if (Char_Index<len){
   
   
      //显示上半部分
      Lcd_WriteCmd(PAGE_ADD|y);
      Lcd_WriteCmd(LIST_ADD|x);
      for (j = 0; j <  ASC_CHR_WIDTH ; j++)
      {
   
        Lcd_WriteData(ASC_16[Char_Index].Msk[j]);
           
      }
       //显示下半部分
      Lcd_WriteCmd((PAGE_ADD+1)|y);
      Lcd_WriteCmd(LIST_ADD|x);
      for (j =  ASC_CHR_WIDTH ; j <  ASC_CHR_WIDTH+ ASC_CHR_WIDTH ; j++)
       {
   
         Lcd_WriteData(ASC_16[Char_Index].Msk[j]);
          
       }
   }

}

汉字显示的代码也与之相似,只是需要判断两个uchar变量是否与字库数组中相等。

汉字字库:

#define ASC_HZ_WIDTH   12
#define ASC_HZ_HEIGHT   16

typedef struct typFNT_GB16   /*12*16 汉字字模显示数据结构 */
{
   char Index[2];
   char Msk[24];
};
struct typFNT_GB16 const GB_16[] = {   /* 宋体 9小五 显示为12*16 */
"液",0x19,0xE2,0x14,0x42,0xF2,0x2E,0x72,0x8F,0xAA,0x7A,0x02,0x00,0x01,0x07,0x00,0x00,0x07,0x04,0x04,0x02,0x01,0x02,0x04,0x00,
"晶",0x00,0xC0,0x40,0x5F,0xD5,0x15,0xD5,0x55,0x5F,0x40,0xC0,0x00,0x00,0x07,0x05,0x05,0x07,0x00,0x07,0x05,0x05,0x05,0x07,0x00,
"显",0x00,0x40,0x9F,0x15,0xD5,0x15,0xD5,0x15,0x1F,0xC0,0x00,0x00,0x04,0x04,0x05,0x04,0x07,0x04,0x07,0x06,0x05,0x04,0x04,0x00,
"示",0x10,0x12,0x92,0x52,0x12,0xF2,0x12,0x12,0x53,0x92,0x10,0x00,0x02,0x01,0x00,0x04,0x04,0x07,0x00,0x00,0x00,0x00,0x03,0x00,
"的",0xFC,0x44,0x46,0x45,0xFC,0x10,0x2C,0xC7,0x04,0x04,0xFC,0x00,0x07,0x02,0x02,0x02,0x07,0x00,0x00,0x04,0x04,0x04,0x03,0x00,
"第",0x04,0xEA,0xAB,0xAE,0xAA,0xFC,0xAA,0xAB,0xAE,0xBA,0x82,0x00,0x04,0x04,0x02,0x01,0x00,0x07,0x00,0x02,0x02,0x02,0x01,0x00,
"一",0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x30,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
"行",0x48,0x24,0xF3,0x08,0x09,0x09,0x09,0x09,0xF9,0x09,0x08,0x00,0x00,0x00,0x07,0x00,0x00,0x04,0x04,0x04,0x07,0x00,0x00,0x00,
"二",0x00,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x06,0x04,0x00,0x00,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x00,
"三",0x00,0x02,0x22,0x22,0x22,0x22,0x22,0x22,0x23,0x02,0x00,0x00,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x06,0x04,0x00,
"四",0x00,0xFF,0x81,0x41,0x3F,0x01,0x01,0xFF,0x81,0x81,0xFF,0x00,0x00,0x07,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x07,0x00,
"五",0x00,0x11,0x11,0x91,0x7F,0x11,0x11,0x11,0xF1,0x01,0x00,0x00,0x04,0x04,0x04,0x07,0x04,0x04,0x04,0x04,0x07,0x04,0x04,0x00,

};

 

汉字显示函数:

void disp_hz(uchar x, uchar y, uchar hz0,uchar hz1)
{

    uchar i,j,len;
    uchar Char_Index; //字符所在的行

   
    //x <<= 1;
    //y <<= 3;
    if (x & 0x40)
    {
        LCD_CS1 = 0;
        LCD_CS2 = 1;
    }
    else
    {
        LCD_CS1 = 1;
        LCD_CS2 = 0;
    }
   
  len=sizeof(GB_16)/sizeof(GB_16[0]);
 
 
  
    //先找到汉字是在数据表里面的哪一行Index=?问题是找不到汉字
   for(i=0;i<len;i++)
   {
      if(hz0 ==(256+GB_16.Index[0]) && hz1 ==(256+GB_16.Index[1]))  //发现变成了hz0+GB16=256
         break;
   }

   Char_Index=i;


   if (Char_Index<len){
   
   
      //显示上半部分
      Lcd_WriteCmd(PAGE_ADD|y);
      Lcd_WriteCmd(LIST_ADD|x);
      for (j = 0; j < ASC_HZ_WIDTH; j++)
      {
   
        Lcd_WriteData(GB_16[Char_Index].Msk[j]);
           
      }
       //显示下半部分
      Lcd_WriteCmd((PAGE_ADD+1)|y);
      Lcd_WriteCmd(LIST_ADD|x);
      for (j = ASC_HZ_WIDTH; j < ASC_HZ_WIDTH+ASC_HZ_WIDTH; j++)
       {
   
         Lcd_WriteData(GB_16[Char_Index].Msk[j]);
          
       }
   }

}

很奇怪的是,飞思卡尔编译器系统把字库数组中的汉字编译成ASCII码,编成了负数。所以判断条件和非汉字的不一样。if(hz0 ==(256+GB_16.Index[0]) && hz1 ==(256+GB_16.Index[1]))  也许别的编译系统又不一样。

以上就是图形点阵编程的技巧。

 

 

 

 

 

文章评论2条评论)

登录后参与讨论

用户377235 2013-5-17 23:09

赞一个

用户403664 2012-6-21 10:04

好东西,谢谢!

ash_riple_768180695 2007-5-23 16:11

USB接口取电确实是一个很好的发明:我们的电脑变成了小电动玩具扩展器。
相关推荐阅读
用户1276620 2017-12-30 21:01
压电石英传感器(一)水晶的秘密
压电石英传感器(一)水晶的秘密从2012年底开始设计公路动态衡(WIM)传感器,一直为压电石英传感器的灵敏度一致性和线性度困扰,直到制造了一台高速高精度的D33测试机,许多过去的未解之谜才一一揭开。通...
用户1276620 2013-02-27 09:13
用表面振动替代噪声测量的拾振器
    我们公司生产的汽车仪表用微型步进电机,生产测试中要求检查噪声,但是电机的噪声本来就非常小,用噪声计距离40mm测量也只有40dB的噪声,因此必须在非常安静的隔音室或者隔音箱中测量。  ...
用户1276620 2012-10-28 11:50
5V供电下驱动MOSFET的低成本方案
当需要简化电源系统,使用5V 驱动MOSFET管时,使用图腾柱的方式往往让人感觉到不够完美,因为三极管的PN节要消耗0.7V的电压,实际作用到MOSFET管上的开关电压只有4.3V。最近我就为了做...
用户1276620 2012-08-17 10:11
[博客大赛]运放不只是用于信号放大-模拟运放电阻器
通常人们都以为运放只是用来放大电压信号,其实运放可以实现的信号处理功能是非常多的,只是因为数字电路出现后,因为其灵活性而替代了模拟运放的信号运算处理。模拟运放信号处理的设计渐渐被工程师们淡忘。 ...
用户1276620 2012-08-02 08:38
[博客大赛]超级电荷放大器用于准静态压电测量
压电传感器有非常好的稳定特性用于力学测量,但是高质量的电荷放大器却非常难设计。我最初设计电荷放大器是为了用于振动测试,只要线性度高就可以了。后来北大物理系的老师委托我做的是要求高灵敏度,超低噪声的...
我要评论
2
22
关闭 站长推荐上一条 /2 下一条