带中文字库的12864驱动笔记

1、先确定屏幕的点阵结构点。

2、再确定汉字和acsII字符的数据

实物平面图

带中文字库的12864驱动笔记_第1张图片

 点阵结构图

带中文字库的12864驱动笔记_第2张图片

 显示汉字和符号时候,地址累加是按照1号框大小计算和显示;

 画图的时候,地址累加是按照2号框大小计算和显示;

汉字显示坐标

X 坐标

Line1

80H

81H

82H

83H

84H

85H

86H

87H

Line2

90H

91H

92H

93H

94H

95H

96H

97H

Line3

88H

89H

8AH

8BH

8CH

8DH

8EH

8FH

Line4

98H

99H

9AH

9BH

9CH

9DH

9EH

9FH

 从表格可以看出屏幕的连续地址line1-->line3-->line2-->line4

画图时的地址是:写入行地址后写入列地址,每八个横点为一个列地址,最小单位是每8个横点,并且是字节控制8个点。此时有64行每行128个横点即每行16个列

地址定位:  比如i < 64先写命令0x80--0x80+i定位行再写列0x80+列。然后写数据一个字节。(记得是这样写的,不太确定,因为没用到画图做项目,但实践过一次

显示的字符或者汉字

首先汉字是两个字节一个汉字,ACSII字符是一个字节一个字符。并且编译器中的ACSII符号和中文的数据都是一样的。

比如字符‘1’的和显示屏的字库显示字符1的数据都是整数49。

汉字的数据也是和显示屏的数据一样。都是占用两个字节,而且两个字节数据一样。

 使用原理:使用一个字符串,使用char类型指针一个写进屏幕地址。可以看汉字显示坐标。写两个字符显示一个汉字。0--127是ACSII,0xA0开头的是中文数据。程序下面会体现。

 验证中文数据:

首先:看出爬字的两个字节为0xc5c0

带中文字库的12864驱动笔记_第3张图片

 其次:看C中的爬字的也是0xc5c0,运行vs2019观察得知是一样的数据

#include 

int main()
{   
	 char p1;
	const char* p = { "爬狗" };
	printf_s("%s",p);
	p1 = (char)p;
	printf_s("\r\n   ");
	for (;*p != '\0' ; p++)
	{
		printf_s("\r\n %x  ",*p);
	}
	
	return 0;
}

得出结论:直接使用字符串进行按照字节打印输出。

 屏幕的驱动引脚表:

引脚号

引脚名称

方向

功能说明

1

GND

-

模块的电源地

2

VCC

-

模块的电源正端

3

V0

-

LCD 驱动电压输入端

4

RS(CS)

H/L

并行的指令/数据选择信号;串行的片选信号

5

R/W(SID)

H/L

并行的读写选择信号;串行的数据口

6

E(CLK)

H/L

并行的使能信号;串行的同步时钟

7

DB0

H/L

数据 0       

8

DB1

H/L

数据 1

9

DB2

H/L

数据 2

10

DB3

H/L

数据 3

11

DB4

H/L

数据 4

12

DB5

H/L

数据 5

13

DB6

H/L

数据 6

14

DB7

H/L

数据 7

15

PSB

H/L

并/串行接口选择:H-并行;L-串行

16

NC

空脚

17

/RST

H/L

复位 低电平有效

18

VOUT

倍压输出脚 (VDD=+3.3V 有效)

19

LED_A

-

背光源正极(LED+5V)

20

LED_K

-

背光源负极(LED-OV)

并行和串行时序图:

并行写

带中文字库的12864驱动笔记_第4张图片

 并行读:用来读取繁忙与否和当前游标地址

一般屏幕显示,需要进行刷新时间,需要等待不繁忙后才能继续写

带中文字库的12864驱动笔记_第5张图片

串行写:但是没有读

串口需要等待屏幕操作时间,然后再写,接下来的命令表可以体现。每个命令大概需要72us

带中文字库的12864驱动笔记_第6张图片

串行数据传送共分三个字节完成: 第一字节:串口控制—格式 11111ABC

A 为数据传送方向控制:H 表示数据从 LCD 到 MCU,L 表示数据从 MCU 到 LCD B 为数据类型选择:H 表示数据是显示数据,L 表示数据是控制指令

C 固定为 0

第二字节:(并行)8 位数据的高 4 位—格式 DDDD0000 第三字节:(并行)8 位数据的低 4 位—格式 0000DDDD 串行接口时序参数:(测试条件:T=25℃  VDD=4.5V)

 命令表

可以从最后一列表格看出每个命令需要的操作时间。

1、指令表 1:(RE=0:基本指令集):写入命令0x30就是这个表

指令

指令码

说明

执行时 间

( 540

KHZ)

R S

R W

DB 7

DB 6

DB 5

DB 4

DB 3

DB 2

DB 1

DB 0

清除显 示

0

0

0

0

0

0

0

0

0

1

将 DDRAM 填满“20H”,并且 设定 DDRAM 的地址计数器

(AC)到“00H”

4.6ms

地址归 位

0

0

0

0

0

0

0

0

1

X

设定 DDRAM 的地址计数器

(AC)到“00H”,并且将游 标移到开头原点位置;这个指 令并不改变 DDRAM 的内容

4.6ms

进入点 设定

0

0

0

0

0

0

0

1

I/D

S

指定在资料的读取与写入时, 设定游标移动方向及指定显示 的移位

72us

显示状 态 开/关

0

0

0

0

0

0

1

D

C

B

D=1:整体显示 ON C=1:游标 ON B=1:游标位置 ON

72us

游标或 显示移 位控制

0

0

0

0

0

1

S/ C

R/ L

X

X

设定游标的移动与显示的移位 控制位元;这个指令并不改变 DDRAM 的内容

72us

功能设 定

0

0

0

0

1

DL

X

0

RE

X

X

DL=1 (必须设为 1) RE=1: 扩充指令集动作 RE=0: 基本指令集动作

72us

设 定

CGRA

M 地 址

0

0

0

1

AC 5

AC 4

AC 3

AC 2

AC 1

AC 0

设定 CGRAM 地址到地址计数 器(AC)

72us

设 定 DDRA M

地址

0

0

1

AC 6

AC 5

AC 4

AC 3

AC 2

AC 1

AC 0

设定 DDRAM 地址到地址计数 器(AC)

72us

读取忙 碌标志

(BF) 和地址

0

1

BF

AC 6

AC 5

AC 4

AC 3

AC 2

AC 1

AC 0

读取忙碌标志(BF)可以确认 内部动作是否完成,同时可以 读出地址计数器(AC)的值

0us

写资料 到 RAM

1

0

D7

D6

D5

D4

D3

D2

D1

D0

写 入 资 料 到 内 部 的 RAM

( DDRAM/CGRAM/IRAM/G DRAM)

72us

读 出

RAM

1

1

D7

D6

D5

D4

D3

D2

D1

D0

从 内 部 RAM   读 取 资 料

( DDRAM/CGRAM/IRAM/G

72us

的值

DRAM)

指令表—2:(RE=1:扩充指令集)写入命令0x34就是这个表

指令

指令码

说明

执 行 时 间

(540KHZ)

RS

R

W

DB

7

DB

6

DB

5

DB

4

DB

3

DB

2

DB

1

DB

0

待 命 模 式

0

0

0

0

0

0

0

0

0

1

将  DDRAM  填 满 “ 20H ”, 并 且 设 定 DDRAM 的地址计数 器(AC)到“00H”

72us

卷 动 地 址 或 IRAM 地

址选择

0

0

0

0

0

0

0

0

1

SR

SR=1:允许输入垂直 卷动地址 SR=0:允许输入 IRAM 地址

72us

反 白 选 择

0

0

0

0

0

0

0

1

R1

R0

选择 4 行中的任一行 作反白显示,并可决定 反白与否

72us

睡 眠 模 式

0

0

0

0

0

0

1

SL

X

X

SL=1:脱离睡眠模式 SL=0:进入睡眠模式

72us

扩 充 功 能设定

0

0

0

0

1

1

X

1

RE

G

0

RE=1: 扩充指令集动 作

RE=0: 基本指令集动 作

G=1 :绘图显示 ON G=0  :绘图显示 OFF

72us

设 定

IRAM 地

址 或 卷 动地址

0

0

0

1

AC 5

AC 4

AC 3

AC 2

AC 1

AC0

SR=1:AC5—AC0 为

垂直卷动地址 SR=0:AC3—AC0 为 ICON IRAM 地址

72us

设 定 绘 图 RAM

地址

0

0

1

AC 6

AC 5

AC 4

AC 3

AC 2

AC 1

AC0

设定 CGRAM 地址到 地址计数器(AC)

72us

这个表的命令不够详细有些命令没有具体说明,官方手册也是如此。详细使用需要自己验证。一般几个命令够用了如下:

  1. 清除显 示
  2. 地址归 位
  3. 显示状 态 开/关
  4. 游标或 显示移 位控制
  5. 设 定 DDRA M地址
  6. 读取忙 碌标志(BF) 和地址
  7. 扩 充 功 能设定
  8. 设 定 绘 图 RAM地址

驱动程序如下:

/******************************   以下为12864 管脚的定义 ***************************************/
/*******************************                         ***************************************/
#define SERIAL 1
#define PARALLEL  2
#define MODE  PARALLEL

#define   PSB(n)   GPIO_WriteBit(GPIOA, GPIO_Pin_8,(BitAction) n);//H并行   L串行

#define   RS(n)    GPIO_WriteBit(GPIOA, GPIO_Pin_10,(BitAction) n); //并行H数据 L命令
#define   CS(n)    GPIO_WriteBit(GPIOA, GPIO_Pin_10, (BitAction) n) //串行片选1有效

#define   LED_K(n)    GPIO_WriteBit(GPIOA, GPIO_Pin_11,(BitAction) n);// 背光负,0 接通地
#define   LCD_RST(n)  GPIO_WriteBit(GPIOA, GPIO_Pin_12,(BitAction) n); //LCD复位 0有效


#define   RW(n)    GPIO_WriteBit(GPIOF, GPIO_Pin_0,(BitAction) n);//并行 0写 1读
#define   EN(n)    GPIO_WriteBit(GPIOF, GPIO_Pin_1,(BitAction) n);//并行 0 1 0 边沿信号
#define   SID(n)    GPIO_WriteBit(GPIOF, GPIO_Pin_0, (BitAction) n) //串行 信号线
#define   CLK(n)    GPIO_WriteBit(GPIOF, GPIO_Pin_1, (BitAction) n) //串行 时钟线

#define   LD0(n)    GPIO_WriteBit(GPIOC, GPIO_Pin_0,(BitAction) n);//并行数据位
#define   LD1(n)    GPIO_WriteBit(GPIOC, GPIO_Pin_1,(BitAction) n);
#define   LD2(n)    GPIO_WriteBit(GPIOC, GPIO_Pin_2,(BitAction) n);
#define   LD3(n)    GPIO_WriteBit(GPIOC, GPIO_Pin_3,(BitAction) n);
#define   LD4(n)    GPIO_WriteBit(GPIOC, GPIO_Pin_4,(BitAction) n);
#define   LD5(n)    GPIO_WriteBit(GPIOC, GPIO_Pin_5,(BitAction) n);
#define   LD6(n)    GPIO_WriteBit(GPIOC, GPIO_Pin_6,(BitAction) n);
#define   LD7(n)    GPIO_WriteBit(GPIOC, GPIO_Pin_7,(BitAction) n);
#include "main.h"

#if MODE==PARALLEL

void bus_check()  //芯片繁忙检测
{
  GPIO_InitTypeDef GPIO_InitStruct;
  RS(0);   
  RW(1);    
  LD0(1);  //并行数据位7位拉高
  LD1(1);
  LD2(1);
  LD3(1);
  LD4(1);
  LD5(1);
  LD6(1);
  LD7(1);
  EN(1);

  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 |GPIO_Pin_2 |GPIO_Pin_3 | GPIO_Pin_4 |GPIO_Pin_5 |GPIO_Pin_6 |GPIO_Pin_7;
  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN; //并行数据引脚改为输入
  GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_DOWN;
  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_Level_3;  
  GPIO_Init(GPIOC, &GPIO_InitStruct);
//  delay_ms(2);
  while(GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_7)==1);//读取最高位,为0就是不繁忙
  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;         //改为输出
  GPIO_Init(GPIOC, &GPIO_InitStruct);
  EN(0);
  

}


void led12864_init()
{
  GPIO_InitTypeDef GPIO_InitStruct;

  RCC_AHBPeriphClockCmd( RCC_AHBPeriph_GPIOC, ENABLE);
  RCC_AHBPeriphClockCmd( RCC_AHBPeriph_GPIOA, ENABLE);
  RCC_AHBPeriphClockCmd( RCC_AHBPeriph_GPIOF, ENABLE);
  
  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 |GPIO_Pin_2 |GPIO_Pin_3 | GPIO_Pin_4 |GPIO_Pin_5 |GPIO_Pin_6 |GPIO_Pin_7;
  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;  //并行数据脚初始化
  GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_Level_3;  
  GPIO_Init(GPIOC, &GPIO_InitStruct);
  
  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_10 |GPIO_Pin_11 | GPIO_Pin_12 ;//PSB RS LED_K  LCD_RST
  GPIO_Init(GPIOA, &GPIO_InitStruct);
  
  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;   // RW EN
  GPIO_Init(GPIOF, &GPIO_InitStruct);

  PSB(1);//并行选择
  LED_K(0);  
  LCD_RST(0);//复位
  LCD_RST(1);//
  delay_ms(5);
  
  write_commoned(0x30);//启动基本命令
  write_commoned(0x04);//游标移动方向
  write_commoned(0x0e);//游标显示或者反显
  write_commoned(0x01);//清屏
  write_commoned(0x02);//地址和游标回归首行首列
  write_commoned(0x80);//显示地址回归首行首列

//  write_data(0xbb);
//  write_data(0xb6);

}


void write_commoned(unsigned char data)
{
   bus_check();
   RS(0);//命令 
   RW(0);//写
   EN(0);

   LD0(((data>>0)&0x01));
   LD1(((data>>1)&0x01));
   LD2(((data>>2)&0x01));
   LD3(((data>>3)&0x01));
   LD4(((data>>4)&0x01));
   LD5(((data>>5)&0x01));
   LD6(((data>>6)&0x01));
   LD7(((data>>7)&0x01));
   EN(1);
   EN(0);

}

void write_data(unsigned char data)
{
   bus_check();
   RS(1);//s数据
   RW(0);//写
   EN(0);
   LD0(((data>>0)&0x01));
   LD1(((data>>1)&0x01));
   LD2(((data>>2)&0x01));
   LD3(((data>>3)&0x01));
   LD4(((data>>4)&0x01));
   LD5(((data>>5)&0x01));
   LD6(((data>>6)&0x01));
   LD7(((data>>7)&0x01));   
   EN(1);
   EN(0);

}



void test(unsigned char data)
{
  unsigned char i,j;
  write_commoned(0x01);
  write_commoned(0x34);
  for(i=0;i<32;i++)
  {
    
    write_commoned(0x80+i);
    write_commoned(0x80);

      for(j=0;j<32;j++)
      {


       write_data(data);
      }
  }
  write_commoned(0x36);
  write_commoned(0x30);

}

#endif

/*  
    
    hang 显示在屏幕第几行                     value should 1-4
    lie  第几个格子,一个格子可容纳两个字符  value should 0-7
    *a   指向的字符指针,可以是字符串
    chang  显示字符长度
*/
void display(unsigned char hang , unsigned char lie ,char *a , unsigned char chang)
{
  static unsigned char temp_data;
  switch( hang)//每一行的起始坐标是不一样的
  {
          case 1:temp_data = 0x80;break;  //第一行
          case 2:temp_data = 0x90;break;  //第二行
          case 3:temp_data = 0x88;break;  //第三行
          case 4:temp_data = 0x98;break;  //第四行
          default :return;
  }
  if(lie > 8){ return; }//一个列可以容纳两个acsII字符;
  write_commoned( temp_data + lie); //显示定位行列
  for(;*a != '\0'&&chang>0; a++,chang--)
  {
    write_data( (unsigned char)*a);
  }
  
}

串行程序代码:跟上面的并行共用宏定义


#if MODE==SERIAL

void led12864_init( void )
{
  GPIO_InitTypeDef GPIO_InitStruct;
  RCC_AHBPeriphClockCmd( RCC_AHBPeriph_GPIOA, ENABLE);
  RCC_AHBPeriphClockCmd( RCC_AHBPeriph_GPIOF, ENABLE);
  
  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
  GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_Level_3;  

  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_10 |GPIO_Pin_11 | GPIO_Pin_12 ;//PSB RS LED_K  LCD_RST
  GPIO_Init(GPIOA, &GPIO_InitStruct);
  
  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;   // RW EN 
  GPIO_Init(GPIOF, &GPIO_InitStruct);
  CLK(0);
  SID(0);
  CS(1) ;
  PSB(0);
  LCD_RST(1);
  
  
  delay_us( 1000);//物理复位,需要等待点时间
  write_commoned( 0x30); /*功能设置:一次送8位数据,基本指令集*/ 
  write_commoned( 0x04); /*点设定:显示字符/光标从左到右移位,DDRAM地址加1*/
  write_commoned( 0x0e);  /*显示设定:开显示,显示光标,当前显示位反白闪动*/ 
  write_commoned( 0x01);  /*清DDRAM*/ 
  delay_ms( 5);               /* 等待4.6ms清屏完毕*/
  write_commoned( 0x02); /*DDRAM地址归位*/ 
  write_commoned( 0x80); /*把显示地址设为0X80,即为第一行的首位*/ 	
//  write_data( 0x7f);    //显示一个字符
//  write_commoned( 0x90); //第二行
//  write_data( 0xbb); //显示一个汉字
//  write_data( 0xb6);  

}



void write_byte( unsigned char data)
{	
        static unsigned char i = 0;
	CS(1) ;
	CLK(0);
	delay_us(72); //串行每个写都基本需要72us反应,代替了并行的繁忙等待
	for(i = 0; i < 8; i++)
        {
	   if((data&0x80) == 0x80){ SID(1);}
           else  { SID(0);}
	
	   data = data<<1;
	   CLK(1);
           
           CLK(0);	
	}
	CS(0) ;
}


void write_commoned( unsigned char data)
{
  
  write_byte( 0xf8);   //写命令固定格式
  write_byte( 0xf0&data); //命令的高四
  write_byte( ((0x0f&data)<<4) ); //命令的低四
}

void write_data( unsigned char data)
{
  
  write_byte( 0xfa);//写数据的固定格式
  write_byte( 0xf0&data); //写数据的高四
  write_byte( ((0x0f&data)<<4) );//写数据的低四
}

void test( unsigned char data)
{
  static unsigned char i = 0,j = 0;
  write_commoned( 0x01);//清屏
  delay_us( 46000);//清屏固定等待4.6ms
  write_commoned( 0x34);

  for(i = 0;i < 64;i++)
  {  
            write_commoned( 0x80+i); 
            write_commoned( 0x80); 
            for(j = 0; j < 32; j++)
            { 
              write_data( data);
            }
  }
  write_commoned( 0x36);
  write_commoned( 0x30);

}


#endif



/*画图函数*/

void write_picture(unsigned char *p)
{
   static unsigned char i = 0,j = 0;
//  write_commoned( 0x01);
    write_commoned( 0x34);//关闭绘画
    for(i = 0;i < 32; i++) //打印第一第三行
    {  
              write_commoned( 0x80+i); //点的行定位          
              write_commoned( 0x80);   //点的列定位  行的每八个点为下一个列 
              for(j = 0; j < 16; j++)
              {
                       write_data( *p);p++;
                      // write_commoned( 0x36);
              }
    }
    for(i = 0;i < 32; i++)//打印第二第四行
    {  
              write_commoned( 0x80+i); 
              write_commoned( 0x88); 
              for(j = 0; j < 16; j++)
              {
                       write_data( *p);p++;
                      // write_commoned( 0x36);
              }
    }
    write_commoned( 0x36);//开启绘画
    write_commoned( 0x30);//开启基本命令
}

在使用屏幕的时候的总结:无法单独定位到某个字节的位置,比如:0x80首行首列,0x81是首行第二列。但是首行首列和首行第二列之间存在两个字节的空间。所以地址不能定位到空间中的第二个字节的位置 。

或者这样说:地址定位只能按照两个字节大小来定位。

你可能感兴趣的:(STM32F4的学习,单片机)