Chinaunix首页 | 论坛 | 博客
  • 博客访问: 132877
  • 博文数量: 55
  • 博客积分: 1870
  • 博客等级: 上尉
  • 技术积分: 540
  • 用 户 组: 普通用户
  • 注册时间: 2008-03-21 20:51
文章分类

全部博文(55)

文章存档

2011年(27)

2009年(3)

2008年(25)

我的朋友

分类: C/C++

2011-05-04 14:04:35

【FPGA黑金开发板】NIOSII那些事儿--LCD驱动及图片显示(二十二)
声明:本文转载于http://www.cnblogs.com/kingst,版权归黑金动力社区(http://www.heijin.org)所有。

2

简介     

      这一节,我们来讲解与点阵LCD相关的内容,主要介绍硬件结构及其驱动编写。

      做我们这行的,我相信很少有人不知道点阵LCD的,就算没用过也应该见过(俗话说,没吃过猪肉还没见过猪跑啊,呵呵)。不用我多说,大家也知道这东西是做什么用的。不过可能很多人对他的结构还不是很熟悉。通常我们所见到的LCD模块具有LCM(玻璃)、背光、PCB板。其实这三样只有一样是必须要有的,那就是LCM(玻璃)。大家就有点疑问了,没有背光无所谓了,那没有PCB板,就一块玻璃有啥用啊。大家接着听我说,点阵的LCD模块按驱动控制器的集成方式,分为两种:COB和COG,COG就是将驱动控制芯片集成到了玻璃里面,我们只需要在电路板上加上无法集成的电容电阻就可以了;而COB是那种需要将驱动芯片焊接在LCD模块后面的PCB板上的。这回大家应该明白为什么只有一块玻璃就能显示了吧。

      在我们黑金开发板上使用的LCD就是128*64的COG液晶,它将驱动控制IC集成到了LCM上,这样就省去了PCB底板,给我们节省了很大的空间。下面,我简单介绍一下这款液晶的一些参数,如下图所示,它的驱动芯片为ST7565P。支持三种接口方式,我们采用的是串行时序方式,接口简单,使用方便,相比其他两种,也节省了很多的管脚。

clip_image002

      下图为LCD的串行接口原理图,大家可以看到,仅四根线就可以搞定了。

clip_image004

      对于LCD而言,需要清楚的了解驱动控制IC的显存与LCD上的点的对应关系,这一点非常重要。通过下图,我们可以了解到,LCD的显存中存在8(page)*8+1行,即65行,s0-s131,即132列,而液晶只有64*128个点。因此显存上的一些数据是不能显示的。通过实验测试得知,最后一行(page8中的D0)和最后三列(ADC为正常时,s129、s130、s131;ADC为反向时,s0、s1、s2)是不能显示的,而显存上其他数据与LCD上的点一一对应。如下图的红圈处所示的区域。

clip_image006

      显示屏上的每一个点都对应有控制器片内的显示缓存RAM中的一个位, 显示屏上64*128个点分别对应着显示RAM的8个Page, 每一个Page有128个byte的空间对应,如下表所示

clip_image008

      大家如要点亮 LCD 屏上的某一个点时,实际上就是对该点所对应的显示 RAM 区中的某一个位进行置 1 操作;所以就要确定该点所处的行地址、列地址。从上图中可以看出,液晶的行地址实际上就是 Page 的信息,每一个 Page应有 8 行;而列地址则表示该点的横坐标,在屏上为从左到右排列,Page 中的一个 Byte 对应的是一列(8行,即 8 个点) ,达 128列。 可以根据这样的关系在程序中控制 LCD显示屏的显示。

硬件设置     

      下面我们来看,如果如何在NIOS下驱动液晶屏。首先,我们需要在软核中四个PIO口,分别对应LCD的四个引脚。如下图所示,四个PIO模块全部为输出。

clip_image010

      建好以后,自动分配地址,中断,编译…

      然后,我们回到Quartus中,通过TCL脚本分配好管脚疑惑,LCD部分如下图示是

clip_image012

      接着又是编译,完成以后,硬件部分的设置就结束了。

软件开发

      接下来,我们打开NIOS IDE软件。

      第一步做的还是需要进行一次完全编译,Ctrl+b,漫长的等待…

      编译完成后,我们进入system.h,查看是否有我们想要得到的LCD部分,如下表所示

01/*
02 * LCD_SI configuration
03 *
04 */
05 
06#define LCD_SI_NAME "/dev/LCD_SI"
07#define LCD_SI_TYPE "altera_avalon_pio"
08#define LCD_SI_BASE 0x000018b0
09
10/*
11 * LCD_A0 configuration
12 *
13 */
14 
15#define LCD_A0_NAME "/dev/LCD_A0"
16#define LCD_A0_TYPE "altera_avalon_pio"
17#define LCD_A0_BASE 0x000018c0
18
19/*
20 * LCD_SCL configuration
21 *
22 */
23 
24#define LCD_SCL_NAME "/dev/LCD_SCL"
25#define LCD_SCL_TYPE "altera_avalon_pio"
26#define LCD_SCL_BASE 0x000018d0
27
28/*
29 * LCD_CS configuration
30 *
31 */
32 
33#define LCD_CS_NAME "/dev/LCD_CS"
34#define LCD_CS_TYPE "altera_avalon_pio"
35#define LCD_CS_BASE 0x000018e0
36

接下来,我们需要在sopc.h中添加LCD部分的代码

01typedef struct
02{
03    unsigned long int DATA;
04    unsigned long int DIRECTION;
05    unsigned long int INTERRUPT_MASK;
06    unsigned long int EDGE_CAPTURE;
07     
08}PIO_STR;
09 
10#define _LCD
11 
12#ifdef _LCD
13#define LCD_CS            ((PIO_STR *) LCD_CS_BASE)
14#define LCD_SCL           ((PIO_STR *) LCD_SCL_BASE)
15#define LCD_A0            ((PIO_STR *) LCD_A0_BASE)
16#define LCD_SI            ((PIO_STR *) LCD_SI_BASE)
17#endif /* _LCD */

接下来,我们根据LCD串行方式的时序图来编写LCD的驱动,时序图如下图所示

clip_image014

      我们需要在工程目录中的driver下建立lcd.c文件,代码如下

001/*
002 * =================================================================
003 *       Filename:  lcd.c
004 *   Description:  LCD驱动
005 *        Version:  1.0.0
006 *        Created:  2010.4.16
007 *       Revision:  none
008 *       Compiler:  Nios II 9.0 IDE
009 *         Author:  马瑞 (AVIC)
010 *          Email:  avic633@gmail.com 
011 * ==================================================================
012 */
013 
014/*-------------------------------------------------------------------
015 *  Include
016 *-----------------------------------------------------------------*/
017#include "system.h"
018#include
019#include "../inc/sopc.h"
020 
021/*------------------------------------------------------------------
022 *  Variable
023 *-----------------------------------------------------------------*/
024unsigned char buf[]={
025//这是一张128*64的图片转换而成
026/*--  宽度x高度=128x64  --*/
0270x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0280x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0290x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0300x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0310x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0320x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0330x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0340x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0350x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xE0,0xF0,
0360xF0,0x78,0x38,0x38,0x38,0x78,0xF0,0xF0,0xE0,0x80,0x00,0x00,0xE0,0xF0,0xF8,0x38,
0370x38,0x38,0x38,0x78,0xF0,0xF0,0xC0,0x00,0x00,0xF8,0xF8,0xF8,0x00,0x00,0x00,0x00,
0380x00,0xF8,0xF8,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF8,0xF8,0xF8,0x38,
0390x38,0x38,0x38,0x38,0x38,0x38,0x00,0x00,0xF8,0xF8,0xF8,0x38,0x38,0x38,0x38,0x78,
0400xF8,0xF0,0xE0,0x00,0x00,0x80,0xE0,0xF0,0xF0,0x78,0x38,0x38,0x38,0x38,0x78,0xF0,
0410xF0,0x40,0x00,0x00,0x00,0x00,0xC0,0xF8,0xF8,0x38,0xF8,0xF8,0xC0,0x00,0x00,0x00,
0420x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0430x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x7F,0xFF,
0440xF0,0xE0,0xC0,0xC0,0xC0,0xE0,0xF0,0xFF,0x7F,0x1F,0x00,0x00,0x31,0xF3,0xF7,0xE7,
0450xC7,0xC6,0xCE,0xCE,0xFC,0xFC,0x78,0x00,0x00,0xFF,0xFF,0xFF,0x07,0x07,0x07,0x07,
0460x07,0xFF,0xFF,0xFF,0x00,0x00,0x1C,0x1C,0x1C,0x1C,0x1C,0x00,0xFF,0xFF,0xFF,0x07,
0470x07,0x07,0x07,0x07,0x07,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x0E,0x0E,0x0E,0x0E,0x0F,
0480x0F,0x07,0x03,0x00,0x00,0x1F,0x7F,0xFF,0xF0,0xE0,0xC0,0xC0,0xDC,0xDC,0xDC,0xFC,
0490xFC,0x7C,0x00,0x80,0xF0,0xFE,0x7F,0x3F,0x39,0x38,0x39,0x3F,0x7F,0xFE,0xF0,0x80,
0500x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0510x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0520x00,0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,
0530x01,0x01,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x00,0x00,0x00,0x00,
0540x00,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x00,
0550x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,
0560x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x00,
0570x00,0x00,0x00,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,
0580x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0590x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0600x00,0x00,0x00,0x00,0x00,0xF8,0xF8,0x28,0xE8,0xC8,0xF8,0xF8,0xC8,0x68,0x28,0xF8,
0610xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xC0,0x60,0x38,0x38,0x60,0xC0,0x80,
0620x80,0x00,0x00,0x00,0x00,0x00,0x10,0x10,0x10,0x10,0xF0,0xF0,0x10,0x10,0x10,0xF0,
0630xF0,0x10,0x10,0x10,0x10,0x00,0x00,0x80,0xF0,0xF0,0x80,0x80,0xF8,0xF8,0x80,0x90,
0640xB0,0xE0,0xE0,0x80,0x80,0x80,0x00,0x80,0x80,0x80,0xF8,0xF8,0x80,0x80,0xF0,0xF0,
0650x10,0x10,0x18,0x08,0x08,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0660x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0670x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0680x00,0x00,0x00,0x20,0xA0,0xA9,0x29,0xA9,0xA9,0x29,0x3F,0xBF,0xA9,0x29,0x29,0x29,
0690xA9,0xA0,0x20,0x00,0x02,0x02,0x13,0x11,0x53,0xD2,0x92,0xFE,0xFE,0x12,0xD2,0xD2,
0700x13,0x13,0x03,0x01,0x00,0x02,0x02,0x02,0x82,0xE2,0x7F,0x1F,0x02,0x02,0x02,0xFF,
0710xFF,0x02,0x02,0x02,0x02,0x00,0x00,0x00,0x81,0xC1,0x78,0x3F,0x3F,0x74,0xC4,0xC4,
0720x7C,0x3C,0x00,0x00,0x00,0x00,0x00,0x20,0x38,0x1E,0xFF,0xFF,0x8E,0xFC,0x7F,0x0F,
0730x8F,0xDD,0x71,0xFD,0x8F,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0740x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0750x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0760x00,0x00,0x00,0x02,0x03,0x01,0x00,0x00,0x03,0x03,0x00,0x00,0x03,0x03,0x00,0x00,
0770x00,0x03,0x03,0x00,0x02,0x02,0x02,0x02,0x02,0x03,0x03,0x03,0x03,0x03,0x03,0x02,
0780x02,0x02,0x02,0x02,0x00,0x00,0x02,0x03,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x03,
0790x03,0x00,0x00,0x00,0x00,0x00,0x02,0x03,0x01,0x04,0x04,0x06,0x02,0x03,0x01,0x01,
0800x01,0x03,0x06,0x06,0x02,0x00,0x00,0x00,0x00,0x00,0x03,0x03,0x00,0x00,0x02,0x03,
0810x01,0x00,0x00,0x01,0x03,0x03,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0820x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0830x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0840x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0850x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0860x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0870x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0880x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0890x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0900x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
091};
092//-----------------Function Prototype--------------------//
093void initialize_lcd(void);
094void draw_screen(unsigned char *p);
095void clear(void);
096void write_data(unsigned char dat);
097void data_send(unsigned char dat);
098void write_command(unsigned char com);
099 
100/*
101 * ===  FUNCTION  ==================================================
102 *         Name:  data_send
103 *  Description:  发送一个字节数据
104 * =================================================================
105 */
106void data_send(unsigned char dat)
107{
108    unsigned char i;
109 
110    LCD_CS->DATA=0;
111    LCD_SCL->DATA=0;
112 
113    for(i=0;i<8;i++){
114        if(dat&0x80)LCD_SI->DATA=1;
115        else LCD_SI->DATA=0;
116         
117        dat<<=1;
118 
119        LCD_SCL->DATA=0;
120 
121        LCD_SCL->DATA=1;
122    }
123 
124    LCD_CS->DATA=1;
125}
126 
127/*
128 * ===  FUNCTION  ===================================================
129 *         Name:  write_command
130 *  Description: A0为低时,写命令字,
131 * =================================================================
132 */
133void write_command(unsigned char com)
134{
135    LCD_A0->DATA=0;
136 
137    data_send(com);
138}
139 
140/*
141 * ===  FUNCTION  ===================================================
142 *         Name:  write_data
143 *  Description:  A0为高时,写数据
144 * =================================================================
145 */
146void write_data(unsigned char dat)
147{
148    LCD_A0->DATA = 1;
149 
150    data_send(dat);
151}
152/*
153 * ===  FUNCTION  ===================================================
154 *         Name:  set_x
155 *  Description:  设置列地址
156 * =================================================================
157 */
158void set_x(unsigned char x)
159{
160    write_command(x>>4|0x10);
161    write_command(x&0xf);
162}
163/*
164 * ===  FUNCTION  ===================================================
165 *         Name:  set_y
166 * Description:  设置页地址 一共8页
167 * =================================================================
168 */
169void set_y(unsigned char y)
170{
171    write_command(y|0xb0);
172}
173/*
174 * ===  FUNCTION  ===================================================
175 *         Name:  clear
176 * Description:  清屏
177 * =================================================================
178 */
179void clear(void)
180{
181    int seg;
182    int page;
183 
184    for(page=0;page<8;page++) {
185        set_y(page);//设置页地址,一共8页
186        set_x(0x00);//设置列地址为0
187 
188        for(seg=0;seg<128;seg++){
189            write_data(0);
190        }
191    }
192}
193/*
194 * ===  FUNCTION  ===================================================
195 *         Name:  draw_screen
196 * Description:  显示一张128*64的图片
197 * =================================================================
198 */
199void draw_screen(unsigned char *p)
200{
201    int seg;
202    int page;
203 
204    for(page=0;page<8;page++) {
205        set_y(page);//设置页地址,一共8页
206        set_x(0x00);//设置列地址为0
207 
208        for(seg=0;seg<128;seg++){
209            write_data(*p++);
210        }
211    }
212}
213/*
214 * ===  FUNCTION  ===================================================
215 *         Name:  initialize_lcd
216 * Description:  LCD初始化,初始化函数由厂商提供,相关设置请查询datasheet
217 * =================================================================
218 */
219void initialize_lcd(void)
220{
221    write_command(0xaf); //ON DISPLAY
222    write_command(0x40); //STAR DISPLAY
223    write_command(0xa0); //ADC NORMAL
224    write_command(0xa6); //Display Normal
225    write_command(0xa4); //CLEAR
226    write_command(0xa2); //1/9BIAS
227    write_command(0xc8); //COMMON OUTPUT DIRECTION
228    write_command(0x2f); //POWER CONTROL
229    write_command(0x24); //RESISTER RATIO
230    write_command(0x81); //VOLUM MODE SET
231    write_command(0x24); //RESISTER RATIO
232}

写好驱动以后,我们还需要在工程目录的inc下建立lcd.h函数,代码如下:

01/*
02 * =================================================================
03 *       Filename:  lcd.h
04 *   Description: 
05 *        Version:  1.0.0
06 *        Created:  2010.4.16
07 *       Revision:  none
08 *       Compiler:  Nios II 9.0 IDE
09 *         Author:  马瑞 (AVIC)
10 *          Email:  avic633@gmail.com 
11 * =================================================================
12 */
13 
14#ifndef _lcd_h_
15#define _lcd_h_
16 
17extern void initialize_lcd(void);
18extern void draw_screen(unsigned char *p);
19extern void clear(void);
20extern unsigned char buf[];
21 
22#endif //_lcd_h_

接下来,需要做的就是写个测试函数,在main.c中添加如下代码

01/*
02 * =================================================================
03 *       Filename:  main.c
04 *   Description:  LCD试验,在LCD上打印128*64图片
05 *        Version:  1.0.0
06 *        Created:  2010.4.16
07 *       Revision:  none
08 *       Compiler:  Nios II 9.0 IDE
09 *         Author:  马瑞 (AVIC)
10 *          Email:  avic633@gmail.com 
11 * =================================================================
12 */
13 
14/*------------------------------------------------------------------
15 *  Include
16 *-----------------------------------------------------------------*/
17#include "../inc/lcd.h"
18 
19/*
20 * ===  FUNCTION  ===================================================
21 *         Name:  main
22 *  Description: 
23 * =================================================================
24 */
25int main(void)
26{
27    initialize_lcd();
28    clear();
29    draw_screen(buf);
30     
31}

      OK,代码就全部写好了。编译之后将程序下载进去,我们就可以看出效果了,如下图所示,显示的效果还是很不错的。

image

      上面涉及到了一个放置图片的数组,这个数组是通过点阵液晶取模软件生成的。我下面简单介绍一下它的使用方法。

      首先打开点阵液晶取模软件,如下图所示

clip_image016

如果想显示128*64的图片,首先就要有一个分辨率为128*64的图片,然后按打开图像图标,如下图所示红圈处,将图片载入

clip_image018

然后按下图所示红圈1,点击取模方式。然后点击红圈2,这时在红圈3处就会生成我们需要的16进制的代码了。操作很简单,软件里面还有很多功能,大家可以自行研究一下。

clip_image020

好了,这一节我们就讲完了。下一节,我们将在这一节的基础上,研究有关中文显示和英文变宽字体显示的方法,敬请期待…

阅读(1380) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~